• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

src/xmms/main.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 /**
00018  * @mainpage
00019  * @image html pixmaps/xmms2-128.png
00020  */
00021 
00022 /** @file
00023  * This file controls the XMMS2 main loop.
00024  */
00025 
00026 #include <locale.h>
00027 #include <glib.h>
00028 
00029 #include "xmms_configuration.h"
00030 #include "xmmsc/xmmsc_util.h"
00031 #include "xmmspriv/xmms_plugin.h"
00032 #include "xmmspriv/xmms_config.h"
00033 #include "xmmspriv/xmms_playlist.h"
00034 #include "xmmspriv/xmms_collection.h"
00035 #include "xmmspriv/xmms_signal.h"
00036 #include "xmmspriv/xmms_symlink.h"
00037 #include "xmmspriv/xmms_checkroot.h"
00038 #include "xmmspriv/xmms_medialib.h"
00039 #include "xmmspriv/xmms_output.h"
00040 #include "xmmspriv/xmms_ipc.h"
00041 #include "xmmspriv/xmms_log.h"
00042 #include "xmmspriv/xmms_sqlite.h"
00043 #include "xmmspriv/xmms_xform.h"
00044 #include "xmmspriv/xmms_bindata.h"
00045 #include "xmmspriv/xmms_utils.h"
00046 #include "xmmspriv/xmms_visualization.h"
00047 
00048 #include <stdio.h>
00049 #include <stdlib.h>
00050 #include <string.h>
00051 #include <unistd.h>
00052 #include <signal.h>
00053 #include <sys/stat.h>
00054 #include <fcntl.h>
00055 
00056 /*
00057  * Forward declarations of the methods in the main object
00058  */
00059 static void quit (xmms_object_t *object, xmms_error_t *error);
00060 static GTree *stats (xmms_object_t *object, xmms_error_t *error);
00061 static void hello (xmms_object_t *object, gint protocolver, const gchar *client, xmms_error_t *error);
00062 static void install_scripts (const gchar *into_dir);
00063 static xmms_xform_object_t *xform_obj;
00064 static xmms_bindata_t *bindata_obj;
00065 
00066 XMMS_CMD_DEFINE (quit, quit, xmms_object_t*, NONE, NONE, NONE);
00067 XMMS_CMD_DEFINE (hello, hello, xmms_object_t *, NONE, INT32, STRING);
00068 XMMS_CMD_DEFINE (stats, stats, xmms_object_t *, DICT, NONE, NONE);
00069 XMMS_CMD_DEFINE (plugin_list, xmms_plugin_client_list, xmms_object_t *, LIST, INT32, NONE);
00070 
00071 /** @defgroup XMMSServer XMMSServer
00072   * @brief look at this if you want to code inside the server.
00073   * The XMMS2 project is split into a server and a multiple clients.
00074   * This documents the server part.
00075   */
00076 
00077 /**
00078   * @defgroup Main Main
00079   * @ingroup XMMSServer
00080   * @brief main object
00081   * @{
00082   */
00083 
00084 
00085 /**
00086  * Main object, when this is unreffed, XMMS2 is quiting.
00087  */
00088 struct xmms_main_St {
00089     xmms_object_t object;
00090     xmms_output_t *output;
00091     time_t starttime;
00092 };
00093 
00094 typedef struct xmms_main_St xmms_main_t;
00095 
00096 /** This is the mainloop of the xmms2 server */
00097 static GMainLoop *mainloop;
00098 
00099 /** The path of the configfile */
00100 static gchar *conffile = NULL;
00101 
00102 /**
00103  * This returns the main stats for the server
00104  */
00105 static GTree *
00106 stats (xmms_object_t *object, xmms_error_t *error)
00107 {
00108     GTree *ret;
00109     gint starttime;
00110 
00111     ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
00112                            NULL, (GDestroyNotify) xmmsv_unref);
00113 
00114     starttime = ((xmms_main_t*)object)->starttime;
00115 
00116     g_tree_insert (ret, (gpointer) "version",
00117                    xmmsv_new_string (XMMS_VERSION));
00118     g_tree_insert (ret, (gpointer) "uptime",
00119                    xmmsv_new_int (time (NULL) - starttime));
00120 
00121     return ret;
00122 }
00123 
00124 /**
00125  * @internal Execute all programs or scripts in a directory. Used when starting
00126  * up and shutting down the daemon.
00127  *
00128  * @param[in] scriptdir Directory to search for executable programs/scripts.
00129  * started.
00130  */
00131 static void
00132 do_scriptdir (const gchar *scriptdir)
00133 {
00134     GError *err = NULL;
00135     GDir *dir;
00136     const gchar *f;
00137     gchar *argv[2] = {NULL, NULL};
00138 
00139     XMMS_DBG ("Running scripts in %s", scriptdir);
00140     if (!g_file_test (scriptdir, G_FILE_TEST_IS_DIR)) {
00141         g_mkdir_with_parents (scriptdir, 0755);
00142         install_scripts (scriptdir);
00143     }
00144 
00145     dir = g_dir_open (scriptdir, 0, &err);
00146     if (!dir) {
00147         xmms_log_error ("Could not open script dir '%s' error: %s", scriptdir, err->message);
00148         return;
00149     }
00150 
00151     while ((f = g_dir_read_name (dir))) {
00152         argv[0] = g_strdup_printf ("%s/%s", scriptdir, f);
00153         if (g_file_test (argv[0], G_FILE_TEST_IS_EXECUTABLE)) {
00154             if (!g_spawn_async (g_get_home_dir (),
00155                                 argv, NULL, 0, NULL, NULL, NULL, &err)) {
00156                 xmms_log_error ("Could not run script '%s', error: %s",
00157                                 argv[0], err->message);
00158             }
00159         }
00160         g_free (argv[0]);
00161     }
00162 
00163     g_dir_close (dir);
00164 
00165 }
00166 
00167 /**
00168  * @internal Load the xmms2d configuration file. Creates the config directory
00169  * if needed.
00170  */
00171 static void
00172 load_config ()
00173 {
00174     gchar configdir[PATH_MAX];
00175 
00176     if (!conffile) {
00177         conffile = XMMS_BUILD_PATH ("xmms2.conf");
00178     }
00179 
00180     g_assert (strlen (conffile) <= XMMS_MAX_CONFIGFILE_LEN);
00181 
00182     if (!xmms_userconfdir_get (configdir, sizeof (configdir))) {
00183         xmms_log_error ("Could not get path to config dir");
00184     } else if (!g_file_test (configdir, G_FILE_TEST_IS_DIR)) {
00185         g_mkdir_with_parents (configdir, 0755);
00186     }
00187 
00188     xmms_config_init (conffile);
00189 }
00190 
00191 /**
00192  * @internal Switch to using another output plugin
00193  * @param object An object
00194  * @param data The name of the output plugin to switch to
00195  * @param userdata The #xmms_main_t object
00196  */
00197 static void
00198 change_output (xmms_object_t *object, xmmsv_t *_data, gpointer userdata)
00199 {
00200     xmms_output_plugin_t *plugin;
00201     xmms_main_t *mainobj = (xmms_main_t*)userdata;
00202     const gchar *outname;
00203 
00204     if (!mainobj->output)
00205         return;
00206 
00207     outname = xmms_config_property_get_string ((xmms_config_property_t *) object);
00208 
00209     xmms_log_info ("Switching to output %s", outname);
00210 
00211     plugin = (xmms_output_plugin_t *)xmms_plugin_find (XMMS_PLUGIN_TYPE_OUTPUT, outname);
00212     if (!plugin) {
00213         xmms_log_error ("Baaaaad output plugin, try to change the output.plugin config variable to something usefull");
00214     } else {
00215         if (!xmms_output_plugin_switch (mainobj->output, plugin)) {
00216             xmms_log_error ("Baaaaad output plugin, try to change the output.plugin config variable to something usefull");
00217         }
00218     }
00219 }
00220 
00221 /**
00222  * @internal Destroy the main object
00223  * @param[in] object The object to destroy
00224  */
00225 static void
00226 xmms_main_destroy (xmms_object_t *object)
00227 {
00228     xmms_main_t *mainobj = (xmms_main_t *) object;
00229     xmms_object_cmd_arg_t arg;
00230     xmms_config_property_t *cv;
00231 
00232     cv = xmms_config_lookup ("core.shutdownpath");
00233     do_scriptdir (xmms_config_property_get_string (cv));
00234 
00235     /* stop output */
00236     xmms_object_cmd_arg_init (&arg);
00237 
00238     xmms_object_cmd_call (XMMS_OBJECT (mainobj->output),
00239                           XMMS_IPC_CMD_STOP, &arg);
00240 
00241     g_usleep (G_USEC_PER_SEC); /* wait for the output thread to end */
00242 
00243     xmms_visualization_destroy ();
00244     xmms_object_unref (mainobj->output);
00245 
00246     xmms_object_unref (xform_obj);
00247 
00248     xmms_config_save ();
00249 
00250     xmms_config_shutdown ();
00251 
00252     xmms_plugin_shutdown ();
00253 
00254     xmms_ipc_object_unregister (XMMS_IPC_OBJECT_MAIN);
00255     xmms_ipc_shutdown ();
00256 
00257     xmms_log_shutdown ();
00258 }
00259 
00260 /**
00261  * @internal Function to respond to the 'hello' sent from clients on connect
00262  */
00263 static void
00264 hello (xmms_object_t *object, gint protocolver, const gchar *client, xmms_error_t *error)
00265 {
00266     if (protocolver != XMMS_IPC_PROTOCOL_VERSION) {
00267         xmms_log_info ("Client '%s' with bad protocol version (%d, not %d) connected", client, protocolver, XMMS_IPC_PROTOCOL_VERSION);
00268         xmms_error_set (error, XMMS_ERROR_INVAL, "Bad protocol version");
00269         return;
00270     }
00271     XMMS_DBG ("Client '%s' connected", client);
00272 }
00273 
00274 static gboolean
00275 kill_server (gpointer object) {
00276     xmms_object_emit_f (XMMS_OBJECT (object),
00277                         XMMS_IPC_SIGNAL_QUIT,
00278                         XMMSV_TYPE_INT32,
00279                         time (NULL)-((xmms_main_t*)object)->starttime);
00280 
00281     xmms_object_unref (object);
00282 
00283     exit (EXIT_SUCCESS);
00284 }
00285 
00286 
00287 /**
00288  * @internal Function to respond to the 'quit' command sent from a client
00289  */
00290 static void
00291 quit (xmms_object_t *object, xmms_error_t *error)
00292 {
00293     /*
00294      * to be able to return from this method
00295      * we add a timeout that will kill the server
00296      * very "ugly"
00297      */
00298     g_timeout_add (1, kill_server, object);
00299 }
00300 
00301 static void
00302 install_scripts (const gchar *into_dir)
00303 {
00304     GDir *dir;
00305     GError *err = NULL;
00306     gchar path[PATH_MAX];
00307     const gchar *f;
00308     gchar *s;
00309 
00310     s = strrchr (into_dir, G_DIR_SEPARATOR);
00311     if (!s)
00312         return;
00313 
00314     s++;
00315 
00316     g_snprintf (path, PATH_MAX, "%s/scripts/%s", SHAREDDIR, s);
00317     xmms_log_info ("Installing scripts from %s", path);
00318     dir = g_dir_open (path, 0, &err);
00319     if (!dir) {
00320         xmms_log_error ("Global script directory not found");
00321         return;
00322     }
00323 
00324     while ((f = g_dir_read_name (dir))) {
00325         gchar *source = g_strdup_printf ("%s/%s", path, f);
00326         gchar *dest = g_strdup_printf ("%s/%s", into_dir, f);
00327         if (!xmms_symlink_file (source, dest)) {
00328             break;
00329         }
00330         g_free (source);
00331         g_free (dest);
00332     }
00333 
00334     g_dir_close (dir);
00335 }
00336 
00337 /**
00338  * Just print version and quit
00339  */
00340 void
00341 print_version ()
00342 {
00343     printf ("XMMS2 version " XMMS_VERSION "\n");
00344     printf ("Copyright (C) 2003-2009 XMMS2 Team\n");
00345     printf ("This is free software; see the source for copying conditions.\n");
00346     printf ("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
00347     printf ("PARTICULAR PURPOSE.\n");
00348     printf (" Using glib version %d.%d.%d (compiled against "
00349             G_STRINGIFY (GLIB_MAJOR_VERSION) "."
00350             G_STRINGIFY (GLIB_MINOR_VERSION) "."
00351             G_STRINGIFY (GLIB_MICRO_VERSION) ")\n",
00352             glib_major_version,
00353             glib_minor_version,
00354             glib_micro_version);
00355     xmms_sqlite_print_version ();
00356 
00357     exit (EXIT_SUCCESS);
00358 }
00359 
00360 /**
00361  * The xmms2 daemon main initialisation function
00362  */
00363 int
00364 main (int argc, char **argv)
00365 {
00366     xmms_output_plugin_t *o_plugin;
00367     xmms_config_property_t *cv;
00368     xmms_main_t *mainobj;
00369     int loglevel = 1;
00370     xmms_playlist_t *playlist;
00371     gchar default_path[XMMS_PATH_MAX + 16], *tmp;
00372     gboolean verbose = FALSE;
00373     gboolean quiet = FALSE;
00374     gboolean version = FALSE;
00375     gboolean nologging = FALSE;
00376     gboolean runasroot = FALSE;
00377     gboolean showhelp = FALSE;
00378     const gchar *outname = NULL;
00379     const gchar *ipcpath = NULL;
00380     gchar *ppath = NULL;
00381     int status_fd = -1;
00382     GOptionContext *context = NULL;
00383     GError *error = NULL;
00384 
00385     setlocale (LC_ALL, "");
00386 
00387     /**
00388      * The options that the server accepts.
00389      */
00390     GOptionEntry opts[] = {
00391         {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Increase verbosity", NULL},
00392         {"quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Decrease verbosity", NULL},
00393         {"version", 'V', 0, G_OPTION_ARG_NONE, &version, "Print version", NULL},
00394         {"no-logging", 'n', 0, G_OPTION_ARG_NONE, &nologging, "Disable logging", NULL},
00395         {"output", 'o', 0, G_OPTION_ARG_STRING, &outname, "Use 'x' as output plugin", "<x>"},
00396         {"ipc-socket", 'i', 0, G_OPTION_ARG_FILENAME, &ipcpath, "Listen to socket 'url'", "<url>"},
00397         {"plugindir", 'p', 0, G_OPTION_ARG_FILENAME, &ppath, "Search for plugins in directory 'foo'", "<foo>"},
00398         {"conf", 'c', 0, G_OPTION_ARG_FILENAME, &conffile, "Specify alternate configuration file", "<file>"},
00399         {"status-fd", 's', 0, G_OPTION_ARG_INT, &status_fd, "Specify a filedescriptor to write to when started", "fd"},
00400         {"yes-run-as-root", 0, 0, G_OPTION_ARG_NONE, &runasroot, "Give me enough rope to shoot myself in the foot", NULL},
00401         {"show-help", 'h', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &showhelp, "Use --help or -? instead", NULL},
00402         {NULL}
00403     };
00404 
00405     /** Check that we are running against the correct glib version */
00406     if (glib_major_version != GLIB_MAJOR_VERSION ||
00407         glib_minor_version < GLIB_MINOR_VERSION) {
00408         g_print ("xmms2d is build against version %d.%d,\n"
00409                  "but is (runtime) linked against %d.%d.\n"
00410                  "Refusing to start.\n",
00411                  GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION,
00412                  glib_major_version, glib_minor_version);
00413         exit (EXIT_FAILURE);
00414     }
00415 
00416     xmms_signal_block ();
00417 
00418     context = g_option_context_new ("- XMMS2 Daemon");
00419     g_option_context_add_main_entries (context, opts, NULL);
00420     if (!g_option_context_parse (context, &argc, &argv, &error) || error) {
00421         g_print ("Error parsing options: %s\n", error->message);
00422         g_clear_error (&error);
00423         exit (EXIT_FAILURE);
00424     }
00425     if (showhelp) {
00426 #if GLIB_CHECK_VERSION(2,14,0)
00427         g_print (g_option_context_get_help (context, TRUE, NULL));
00428         exit (EXIT_SUCCESS);
00429 #else
00430         g_print ("Please use --help or -? for help\n");
00431         exit (EXIT_FAILURE);
00432 #endif
00433     }
00434     g_option_context_free (context);
00435 
00436     if (argc != 1) {
00437         g_print ("There were unknown options, aborting!\n");
00438         exit (EXIT_FAILURE);
00439     }
00440 
00441     if (xmms_checkroot ()) {
00442         if (runasroot) {
00443             g_print ("***************************************\n");
00444             g_print ("Warning! You are running XMMS2D as root, this is a bad idea!\nBut I'll allow it since you asked nicely.\n");
00445             g_print ("***************************************\n\n");
00446         } else {
00447             g_print ("PLEASE DON'T RUN XMMS2D AS ROOT!\n\n(if you really must, read the help)\n");
00448             exit (EXIT_FAILURE);
00449         }
00450     }
00451 
00452     if (verbose) {
00453         loglevel++;
00454     } else if (quiet) {
00455         loglevel--;
00456     }
00457 
00458     if (version) {
00459         print_version ();
00460     }
00461 
00462     g_thread_init (NULL);
00463 
00464     g_random_set_seed (time (NULL));
00465 
00466     xmms_log_init (loglevel);
00467     xmms_ipc_init ();
00468 
00469     load_config ();
00470 
00471     cv = xmms_config_property_register ("core.logtsfmt",
00472                                         "%H:%M:%S ",
00473                                         NULL, NULL);
00474 
00475     xmms_log_set_format (xmms_config_property_get_string (cv));
00476 
00477     xmms_fallback_ipcpath_get (default_path, sizeof (default_path));
00478 
00479     cv = xmms_config_property_register ("core.ipcsocket",
00480                                         default_path,
00481                                         on_config_ipcsocket_change,
00482                                         NULL);
00483 
00484     if (!ipcpath) {
00485         /*
00486          * if not ipcpath is specifed on the cmd line we
00487          * grab it from the config
00488          */
00489         ipcpath = xmms_config_property_get_string (cv);
00490     }
00491 
00492     if (!xmms_ipc_setup_server (ipcpath)) {
00493         xmms_ipc_shutdown ();
00494         xmms_log_fatal ("IPC failed to init!");
00495     }
00496 
00497     if (!xmms_plugin_init (ppath)) {
00498         return 1;
00499     }
00500 
00501     playlist = xmms_playlist_init ();
00502     xform_obj = xmms_xform_object_init ();
00503     bindata_obj = xmms_bindata_init ();
00504 
00505     mainobj = xmms_object_new (xmms_main_t, xmms_main_destroy);
00506 
00507     /* find output plugin. */
00508     cv = xmms_config_property_register ("output.plugin",
00509                                         XMMS_OUTPUT_DEFAULT,
00510                                         change_output, mainobj);
00511 
00512     if (outname) {
00513         xmms_config_setvalue (NULL, "output.plugin", outname, NULL);
00514     }
00515 
00516     outname = xmms_config_property_get_string (cv);
00517     xmms_log_info ("Using output plugin: %s", outname);
00518     o_plugin = (xmms_output_plugin_t *)xmms_plugin_find (XMMS_PLUGIN_TYPE_OUTPUT, outname);
00519     if (!o_plugin) {
00520         xmms_log_error ("Baaaaad output plugin, try to change the"
00521                         "output.plugin config variable to something usefull");
00522     }
00523 
00524     mainobj->output = xmms_output_new (o_plugin, playlist);
00525     if (!mainobj->output) {
00526         xmms_log_fatal ("Failed to create output object!");
00527     }
00528     xmms_visualization_init (mainobj->output);
00529 
00530     if (status_fd != -1) {
00531         write (status_fd, "+", 1);
00532     }
00533 
00534     xmms_signal_init (XMMS_OBJECT (mainobj));
00535 
00536     xmms_ipc_object_register (XMMS_IPC_OBJECT_MAIN,
00537                               XMMS_OBJECT (mainobj));
00538 
00539     xmms_ipc_broadcast_register (XMMS_OBJECT (mainobj),
00540                                  XMMS_IPC_SIGNAL_QUIT);
00541 
00542     xmms_object_cmd_add (XMMS_OBJECT (mainobj),
00543                          XMMS_IPC_CMD_QUIT,
00544                          XMMS_CMD_FUNC (quit));
00545     xmms_object_cmd_add (XMMS_OBJECT (mainobj),
00546                          XMMS_IPC_CMD_HELLO,
00547                          XMMS_CMD_FUNC (hello));
00548     xmms_object_cmd_add (XMMS_OBJECT (mainobj),
00549                          XMMS_IPC_CMD_PLUGIN_LIST,
00550                          XMMS_CMD_FUNC (plugin_list));
00551     xmms_object_cmd_add (XMMS_OBJECT (mainobj),
00552                          XMMS_IPC_CMD_STATS,
00553                          XMMS_CMD_FUNC (stats));
00554 
00555     /* Save the time we started in order to count uptime */
00556     mainobj->starttime = time (NULL);
00557 
00558     /* Dirty hack to tell XMMS_PATH a valid path */
00559     g_strlcpy (default_path, ipcpath, sizeof (default_path));
00560 
00561     tmp = strchr (default_path, ';');
00562     if (tmp) {
00563         *tmp = '\0';
00564     }
00565 
00566     putenv (g_strdup_printf ("XMMS_PATH=%s", default_path));
00567 
00568     /* Also put the full path for clients that understands */
00569     putenv (g_strdup_printf ("XMMS_PATH_FULL=%s", ipcpath));
00570 
00571     tmp = XMMS_BUILD_PATH ("shutdown.d");
00572     cv = xmms_config_property_register ("core.shutdownpath",
00573                                         tmp, NULL, NULL);
00574     g_free (tmp);
00575 
00576     tmp = XMMS_BUILD_PATH ("startup.d");
00577     cv = xmms_config_property_register ("core.startuppath",
00578                                         tmp, NULL, NULL);
00579     g_free (tmp);
00580 
00581     /* Startup dir */
00582     do_scriptdir (xmms_config_property_get_string (cv));
00583 
00584     mainloop = g_main_loop_new (NULL, FALSE);
00585 
00586     g_main_loop_run (mainloop);
00587 
00588     return 0;
00589 }
00590 
00591 /** @} */

Generated on Wed Feb 9 2011 for XMMS2 by  doxygen 1.7.1