00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
00058
00059 static void xmms_main_client_quit (xmms_object_t *object, xmms_error_t *error);
00060 static GTree *xmms_main_client_stats (xmms_object_t *object, xmms_error_t *error);
00061 static GList *xmms_main_client_plugin_list (xmms_object_t *main, gint32 type, xmms_error_t *err);
00062 static void xmms_main_client_hello (xmms_object_t *object, gint protocolver, const gchar *client, xmms_error_t *error);
00063 static void install_scripts (const gchar *into_dir);
00064 static void spawn_script_setup (gpointer data);
00065 static xmms_xform_object_t *xform_obj;
00066 static xmms_bindata_t *bindata_obj;
00067
00068 XMMS_CMD_DEFINE (quit, xmms_main_client_quit, xmms_object_t*, NONE, NONE, NONE);
00069 XMMS_CMD_DEFINE (hello, xmms_main_client_hello, xmms_object_t *, NONE, INT32, STRING);
00070 XMMS_CMD_DEFINE (stats, xmms_main_client_stats, xmms_object_t *, DICT, NONE, NONE);
00071 XMMS_CMD_DEFINE (plugin_list, xmms_main_client_plugin_list, xmms_object_t *, LIST, INT32, NONE);
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090 struct xmms_main_St {
00091 xmms_object_t object;
00092 xmms_output_t *output;
00093 xmms_visualization_t *vis;
00094 time_t starttime;
00095 };
00096
00097 typedef struct xmms_main_St xmms_main_t;
00098
00099
00100 static GMainLoop *mainloop;
00101
00102
00103 static gchar *conffile = NULL;
00104
00105
00106
00107
00108 static GTree *
00109 xmms_main_client_stats (xmms_object_t *object, xmms_error_t *error)
00110 {
00111 GTree *ret;
00112 gint starttime;
00113
00114 ret = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
00115 NULL, (GDestroyNotify) xmmsv_unref);
00116
00117 starttime = ((xmms_main_t*)object)->starttime;
00118
00119 g_tree_insert (ret, (gpointer) "version",
00120 xmmsv_new_string (XMMS_VERSION));
00121 g_tree_insert (ret, (gpointer) "uptime",
00122 xmmsv_new_int (time (NULL) - starttime));
00123
00124 return ret;
00125 }
00126
00127 static gboolean
00128 xmms_main_client_list_foreach (xmms_plugin_t *plugin, gpointer data)
00129 {
00130 xmmsv_t *dict;
00131 GList **list = data;
00132
00133 dict = xmmsv_build_dict (
00134 XMMSV_DICT_ENTRY_STR ("name", xmms_plugin_name_get (plugin)),
00135 XMMSV_DICT_ENTRY_STR ("shortname", xmms_plugin_shortname_get (plugin)),
00136 XMMSV_DICT_ENTRY_STR ("version", xmms_plugin_version_get (plugin)),
00137 XMMSV_DICT_ENTRY_STR ("description", xmms_plugin_description_get (plugin)),
00138 XMMSV_DICT_ENTRY_INT ("type", xmms_plugin_type_get (plugin)),
00139 XMMSV_DICT_END);
00140
00141 *list = g_list_prepend (*list, dict);
00142
00143 return TRUE;
00144 }
00145
00146 static GList *
00147 xmms_main_client_plugin_list (xmms_object_t *main, gint32 type, xmms_error_t *err)
00148 {
00149 GList *list = NULL;
00150 xmms_plugin_foreach (type, xmms_main_client_list_foreach, &list);
00151 return list;
00152 }
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164 static void
00165 do_scriptdir (const gchar *scriptdir, const gchar *arg1)
00166 {
00167 GError *err = NULL;
00168 GDir *dir;
00169 const gchar *f;
00170 gchar *argv[3] = {NULL, NULL, NULL};
00171
00172 XMMS_DBG ("Running scripts in %s", scriptdir);
00173 if (!g_file_test (scriptdir, G_FILE_TEST_IS_DIR)) {
00174 g_mkdir_with_parents (scriptdir, 0755);
00175 install_scripts (scriptdir);
00176 }
00177
00178 dir = g_dir_open (scriptdir, 0, &err);
00179 if (!dir) {
00180 xmms_log_error ("Could not open script dir '%s' error: %s", scriptdir, err->message);
00181 return;
00182 }
00183
00184 argv[1] = g_strdup (arg1);
00185 while ((f = g_dir_read_name (dir))) {
00186 argv[0] = g_strdup_printf ("%s/%s", scriptdir, f);
00187 if (g_file_test (argv[0], G_FILE_TEST_IS_EXECUTABLE)) {
00188 if (!g_spawn_async (g_get_home_dir (), argv, NULL, 0,
00189 spawn_script_setup, NULL, NULL, &err)) {
00190 xmms_log_error ("Could not run script '%s', error: %s",
00191 argv[0], err->message);
00192 }
00193 }
00194 g_free (argv[0]);
00195 }
00196 g_free (argv[1]);
00197
00198 g_dir_close (dir);
00199
00200 }
00201
00202
00203
00204
00205 static void
00206 spawn_script_setup (gpointer data)
00207 {
00208 xmms_signal_restore ();
00209 }
00210
00211
00212
00213
00214
00215 static void
00216 load_config (void)
00217 {
00218 gchar configdir[XMMS_PATH_MAX];
00219
00220 if (!conffile) {
00221 conffile = XMMS_BUILD_PATH ("xmms2.conf");
00222 }
00223
00224 g_assert (strlen (conffile) <= XMMS_MAX_CONFIGFILE_LEN);
00225
00226 if (!xmms_userconfdir_get (configdir, sizeof (configdir))) {
00227 xmms_log_error ("Could not get path to config dir");
00228 } else if (!g_file_test (configdir, G_FILE_TEST_IS_DIR)) {
00229 g_mkdir_with_parents (configdir, 0755);
00230 }
00231
00232 xmms_config_init (conffile);
00233 }
00234
00235
00236
00237
00238
00239
00240
00241 static void
00242 change_output (xmms_object_t *object, xmmsv_t *_data, gpointer userdata)
00243 {
00244 xmms_output_plugin_t *plugin;
00245 xmms_main_t *mainobj = (xmms_main_t*)userdata;
00246 const gchar *outname;
00247
00248 if (!mainobj->output)
00249 return;
00250
00251 outname = xmms_config_property_get_string ((xmms_config_property_t *) object);
00252
00253 xmms_log_info ("Switching to output %s", outname);
00254
00255 plugin = (xmms_output_plugin_t *)xmms_plugin_find (XMMS_PLUGIN_TYPE_OUTPUT, outname);
00256 if (!plugin) {
00257 xmms_log_error ("Baaaaad output plugin, try to change the output.plugin config variable to something useful");
00258 } else {
00259 if (!xmms_output_plugin_switch (mainobj->output, plugin)) {
00260 xmms_log_error ("Baaaaad output plugin, try to change the output.plugin config variable to something useful");
00261 }
00262 }
00263 }
00264
00265
00266
00267
00268
00269 static void
00270 xmms_main_destroy (xmms_object_t *object)
00271 {
00272 xmms_main_t *mainobj = (xmms_main_t *) object;
00273 xmms_object_cmd_arg_t arg;
00274 xmms_config_property_t *cv;
00275
00276 cv = xmms_config_lookup ("core.shutdownpath");
00277 do_scriptdir (xmms_config_property_get_string (cv), "stop");
00278
00279
00280 xmms_object_cmd_arg_init (&arg);
00281
00282 xmms_object_cmd_call (XMMS_OBJECT (mainobj->output),
00283 XMMS_IPC_CMD_STOP, &arg);
00284
00285 g_usleep (G_USEC_PER_SEC);
00286
00287 xmms_object_unref (mainobj->vis);
00288 xmms_object_unref (mainobj->output);
00289
00290 xmms_object_unref (xform_obj);
00291
00292 xmms_config_save ();
00293
00294 xmms_config_shutdown ();
00295
00296 xmms_plugin_shutdown ();
00297
00298 xmms_ipc_object_unregister (XMMS_IPC_OBJECT_MAIN);
00299 xmms_ipc_shutdown ();
00300
00301 xmms_log_shutdown ();
00302 }
00303
00304
00305
00306
00307 static void
00308 xmms_main_client_hello (xmms_object_t *object, gint protocolver, const gchar *client, xmms_error_t *error)
00309 {
00310 if (protocolver != XMMS_IPC_PROTOCOL_VERSION) {
00311 xmms_log_info ("Client '%s' with bad protocol version (%d, not %d) connected", client, protocolver, XMMS_IPC_PROTOCOL_VERSION);
00312 xmms_error_set (error, XMMS_ERROR_INVAL, "Bad protocol version");
00313 return;
00314 }
00315 XMMS_DBG ("Client '%s' connected", client);
00316 }
00317
00318 static gboolean
00319 kill_server (gpointer object) {
00320 xmms_object_emit_f (XMMS_OBJECT (object),
00321 XMMS_IPC_SIGNAL_QUIT,
00322 XMMSV_TYPE_INT32,
00323 time (NULL)-((xmms_main_t*)object)->starttime);
00324
00325 xmms_object_unref (object);
00326
00327 exit (EXIT_SUCCESS);
00328 }
00329
00330
00331
00332
00333
00334 static void
00335 xmms_main_client_quit (xmms_object_t *object, xmms_error_t *error)
00336 {
00337
00338
00339
00340
00341
00342 g_timeout_add (1, kill_server, object);
00343 }
00344
00345 static void
00346 install_scripts (const gchar *into_dir)
00347 {
00348 GDir *dir;
00349 GError *err = NULL;
00350 gchar path[XMMS_PATH_MAX];
00351 const gchar *f;
00352 gchar *s;
00353
00354 s = strrchr (into_dir, G_DIR_SEPARATOR);
00355 if (!s)
00356 return;
00357
00358 s++;
00359
00360 g_snprintf (path, XMMS_PATH_MAX, "%s/scripts/%s", SHAREDDIR, s);
00361 xmms_log_info ("Installing scripts from %s", path);
00362 dir = g_dir_open (path, 0, &err);
00363 if (!dir) {
00364 xmms_log_error ("Global script directory not found");
00365 return;
00366 }
00367
00368 while ((f = g_dir_read_name (dir))) {
00369 gchar *source = g_strdup_printf ("%s/%s", path, f);
00370 gchar *dest = g_strdup_printf ("%s/%s", into_dir, f);
00371 if (!xmms_symlink_file (source, dest)) {
00372 g_free (source);
00373 g_free (dest);
00374 break;
00375 }
00376 g_free (source);
00377 g_free (dest);
00378 }
00379
00380 g_dir_close (dir);
00381 }
00382
00383
00384
00385
00386 static void
00387 print_version (void)
00388 {
00389 printf ("XMMS2 version " XMMS_VERSION "\n");
00390 printf ("Copyright (C) 2003-2009 XMMS2 Team\n");
00391 printf ("This is free software; see the source for copying conditions.\n");
00392 printf ("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n");
00393 printf ("PARTICULAR PURPOSE.\n");
00394 printf (" Using glib version %d.%d.%d (compiled against "
00395 G_STRINGIFY (GLIB_MAJOR_VERSION) "."
00396 G_STRINGIFY (GLIB_MINOR_VERSION) "."
00397 G_STRINGIFY (GLIB_MICRO_VERSION) ")\n",
00398 glib_major_version,
00399 glib_minor_version,
00400 glib_micro_version);
00401 xmms_sqlite_print_version ();
00402
00403 exit (EXIT_SUCCESS);
00404 }
00405
00406
00407
00408
00409 int
00410 main (int argc, char **argv)
00411 {
00412 xmms_output_plugin_t *o_plugin;
00413 xmms_config_property_t *cv;
00414 xmms_main_t *mainobj;
00415 int loglevel = 1;
00416 xmms_playlist_t *playlist;
00417 gchar default_path[XMMS_PATH_MAX + 16], *tmp;
00418 gboolean verbose = FALSE;
00419 gboolean quiet = FALSE;
00420 gboolean version = FALSE;
00421 gboolean nologging = FALSE;
00422 gboolean runasroot = FALSE;
00423 gboolean showhelp = FALSE;
00424 const gchar *outname = NULL;
00425 const gchar *ipcpath = NULL;
00426 gchar *ppath = NULL;
00427 int status_fd = -1;
00428 GOptionContext *context = NULL;
00429 GError *error = NULL;
00430
00431 setlocale (LC_ALL, "");
00432
00433
00434
00435
00436 GOptionEntry opts[] = {
00437 {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Increase verbosity", NULL},
00438 {"quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Decrease verbosity", NULL},
00439 {"version", 'V', 0, G_OPTION_ARG_NONE, &version, "Print version", NULL},
00440 {"no-logging", 'n', 0, G_OPTION_ARG_NONE, &nologging, "Disable logging", NULL},
00441 {"output", 'o', 0, G_OPTION_ARG_STRING, &outname, "Use 'x' as output plugin", "<x>"},
00442 {"ipc-socket", 'i', 0, G_OPTION_ARG_FILENAME, &ipcpath, "Listen to socket 'url'", "<url>"},
00443 {"plugindir", 'p', 0, G_OPTION_ARG_FILENAME, &ppath, "Search for plugins in directory 'foo'", "<foo>"},
00444 {"conf", 'c', 0, G_OPTION_ARG_FILENAME, &conffile, "Specify alternate configuration file", "<file>"},
00445 {"status-fd", 's', 0, G_OPTION_ARG_INT, &status_fd, "Specify a filedescriptor to write to when started", "fd"},
00446 {"yes-run-as-root", 0, 0, G_OPTION_ARG_NONE, &runasroot, "Give me enough rope to shoot myself in the foot", NULL},
00447 {"show-help", 'h', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &showhelp, "Use --help or -? instead", NULL},
00448 {NULL}
00449 };
00450
00451
00452 if (glib_major_version != GLIB_MAJOR_VERSION ||
00453 glib_minor_version < GLIB_MINOR_VERSION) {
00454 g_print ("xmms2d is build against version %d.%d,\n"
00455 "but is (runtime) linked against %d.%d.\n"
00456 "Refusing to start.\n",
00457 GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION,
00458 glib_major_version, glib_minor_version);
00459 exit (EXIT_FAILURE);
00460 }
00461
00462 xmms_signal_block ();
00463
00464 context = g_option_context_new ("- XMMS2 Daemon");
00465 g_option_context_add_main_entries (context, opts, NULL);
00466 if (!g_option_context_parse (context, &argc, &argv, &error) || error) {
00467 g_print ("Error parsing options: %s\n", error->message);
00468 g_clear_error (&error);
00469 exit (EXIT_FAILURE);
00470 }
00471 if (showhelp) {
00472 #if GLIB_CHECK_VERSION(2,14,0)
00473 g_print ("%s", g_option_context_get_help (context, TRUE, NULL));
00474 exit (EXIT_SUCCESS);
00475 #else
00476 g_print ("Please use --help or -? for help\n");
00477 exit (EXIT_FAILURE);
00478 #endif
00479 }
00480 g_option_context_free (context);
00481
00482 if (argc != 1) {
00483 g_print ("There were unknown options, aborting!\n");
00484 exit (EXIT_FAILURE);
00485 }
00486
00487 if (xmms_checkroot ()) {
00488 if (runasroot) {
00489 g_print ("***************************************\n");
00490 g_print ("Warning! You are running XMMS2D as root, this is a bad idea!\nBut I'll allow it since you asked nicely.\n");
00491 g_print ("***************************************\n\n");
00492 } else {
00493 g_print ("PLEASE DON'T RUN XMMS2D AS ROOT!\n\n(if you really must, read the help)\n");
00494 exit (EXIT_FAILURE);
00495 }
00496 }
00497
00498 if (verbose) {
00499 loglevel++;
00500 } else if (quiet) {
00501 loglevel--;
00502 }
00503
00504 if (version) {
00505 print_version ();
00506 }
00507
00508 g_thread_init (NULL);
00509
00510 g_random_set_seed (time (NULL));
00511
00512 xmms_log_init (loglevel);
00513 xmms_ipc_init ();
00514
00515 load_config ();
00516
00517 cv = xmms_config_property_register ("core.logtsfmt",
00518 "%H:%M:%S ",
00519 NULL, NULL);
00520
00521 xmms_log_set_format (xmms_config_property_get_string (cv));
00522
00523 xmms_fallback_ipcpath_get (default_path, sizeof (default_path));
00524
00525 cv = xmms_config_property_register ("core.ipcsocket",
00526 default_path,
00527 on_config_ipcsocket_change,
00528 NULL);
00529
00530 if (!ipcpath) {
00531
00532
00533
00534
00535 ipcpath = xmms_config_property_get_string (cv);
00536 }
00537
00538 if (!xmms_ipc_setup_server (ipcpath)) {
00539 xmms_ipc_shutdown ();
00540 xmms_log_fatal ("IPC failed to init!");
00541 }
00542
00543 if (!xmms_plugin_init (ppath)) {
00544 return 1;
00545 }
00546
00547 playlist = xmms_playlist_init ();
00548 xform_obj = xmms_xform_object_init ();
00549 bindata_obj = xmms_bindata_init ();
00550
00551 mainobj = xmms_object_new (xmms_main_t, xmms_main_destroy);
00552
00553
00554 cv = xmms_config_property_register ("output.plugin",
00555 XMMS_OUTPUT_DEFAULT,
00556 change_output, mainobj);
00557
00558 if (outname) {
00559 xmms_config_property_set_data (cv, outname);
00560 }
00561
00562 outname = xmms_config_property_get_string (cv);
00563 xmms_log_info ("Using output plugin: %s", outname);
00564 o_plugin = (xmms_output_plugin_t *)xmms_plugin_find (XMMS_PLUGIN_TYPE_OUTPUT, outname);
00565 if (!o_plugin) {
00566 xmms_log_error ("Baaaaad output plugin, try to change the"
00567 "output.plugin config variable to something useful");
00568 }
00569
00570 mainobj->output = xmms_output_new (o_plugin, playlist);
00571 if (!mainobj->output) {
00572 xmms_log_fatal ("Failed to create output object!");
00573 }
00574
00575 mainobj->vis = xmms_visualization_new (mainobj->output);
00576
00577 if (status_fd != -1) {
00578 write (status_fd, "+", 1);
00579 }
00580
00581 xmms_signal_init (XMMS_OBJECT (mainobj));
00582
00583 xmms_ipc_object_register (XMMS_IPC_OBJECT_MAIN,
00584 XMMS_OBJECT (mainobj));
00585
00586 xmms_ipc_broadcast_register (XMMS_OBJECT (mainobj),
00587 XMMS_IPC_SIGNAL_QUIT);
00588
00589 xmms_object_cmd_add (XMMS_OBJECT (mainobj),
00590 XMMS_IPC_CMD_QUIT,
00591 XMMS_CMD_FUNC (quit));
00592 xmms_object_cmd_add (XMMS_OBJECT (mainobj),
00593 XMMS_IPC_CMD_HELLO,
00594 XMMS_CMD_FUNC (hello));
00595 xmms_object_cmd_add (XMMS_OBJECT (mainobj),
00596 XMMS_IPC_CMD_PLUGIN_LIST,
00597 XMMS_CMD_FUNC (plugin_list));
00598 xmms_object_cmd_add (XMMS_OBJECT (mainobj),
00599 XMMS_IPC_CMD_STATS,
00600 XMMS_CMD_FUNC (stats));
00601
00602
00603 mainobj->starttime = time (NULL);
00604
00605
00606 g_strlcpy (default_path, ipcpath, sizeof (default_path));
00607
00608 tmp = strchr (default_path, ';');
00609 if (tmp) {
00610 *tmp = '\0';
00611 }
00612
00613 g_setenv ("XMMS_PATH", default_path, TRUE);
00614
00615
00616 g_setenv("XMMS_PATH_FULL", ipcpath, TRUE);
00617
00618 tmp = XMMS_BUILD_PATH ("shutdown.d");
00619 cv = xmms_config_property_register ("core.shutdownpath",
00620 tmp, NULL, NULL);
00621 g_free (tmp);
00622
00623 tmp = XMMS_BUILD_PATH ("startup.d");
00624 cv = xmms_config_property_register ("core.startuppath",
00625 tmp, NULL, NULL);
00626 g_free (tmp);
00627
00628
00629 do_scriptdir (xmms_config_property_get_string (cv), "start");
00630
00631 mainloop = g_main_loop_new (NULL, FALSE);
00632
00633 g_main_loop_run (mainloop);
00634
00635 return 0;
00636 }
00637
00638