XMMS2
|
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 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 /** @defgroup XMMSServer XMMSServer 00074 * @brief look at this if you want to code inside the server. 00075 * The XMMS2 project is split into a server and a multiple clients. 00076 * This documents the server part. 00077 */ 00078 00079 /** 00080 * @defgroup Main Main 00081 * @ingroup XMMSServer 00082 * @brief main object 00083 * @{ 00084 */ 00085 00086 00087 /** 00088 * Main object, when this is unreffed, XMMS2 is quiting. 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 /** This is the mainloop of the xmms2 server */ 00100 static GMainLoop *mainloop; 00101 00102 /** The path of the configfile */ 00103 static gchar *conffile = NULL; 00104 00105 /** 00106 * This returns the main stats for the server 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 * @internal Execute all programs or scripts in a directory. Used when starting 00157 * up and shutting down the daemon. 00158 * 00159 * @param[in] scriptdir Directory to search for executable programs/scripts. 00160 * started. 00161 * @param arg1 value passed to executed scripts as argument 1. This makes 00162 * it possible to handle start and stop in one script 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 * @internal Setup function for processes spawned by do_scriptdir 00204 */ 00205 static void 00206 spawn_script_setup (gpointer data) 00207 { 00208 xmms_signal_restore (); 00209 } 00210 00211 /** 00212 * @internal Load the xmms2d configuration file. Creates the config directory 00213 * if needed. 00214 */ 00215 static void 00216 load_config (void) 00217 { 00218 gchar configdir[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 * @internal Switch to using another output plugin 00237 * @param object An object 00238 * @param data The name of the output plugin to switch to 00239 * @param userdata The #xmms_main_t object 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 usefull"); 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 usefull"); 00261 } 00262 } 00263 } 00264 00265 /** 00266 * @internal Destroy the main object 00267 * @param[in] object The object to destroy 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 /* stop output */ 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); /* wait for the output thread to end */ 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 * @internal Function to respond to the 'hello' sent from clients on connect 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 * @internal Function to respond to the 'quit' command sent from a client 00333 */ 00334 static void 00335 xmms_main_client_quit (xmms_object_t *object, xmms_error_t *error) 00336 { 00337 /* 00338 * to be able to return from this method 00339 * we add a timeout that will kill the server 00340 * very "ugly" 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[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, 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 * Just print version and quit 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 * The xmms2 daemon main initialisation function 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 * The options that the server accepts. 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 /** Check that we are running against the correct glib version */ 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 * if not ipcpath is specifed on the cmd line we 00533 * grab it from the config 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 /* find output plugin. */ 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 usefull"); 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 /* Save the time we started in order to count uptime */ 00603 mainobj->starttime = time (NULL); 00604 00605 /* Dirty hack to tell XMMS_PATH a valid path */ 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 /* Also put the full path for clients that understands */ 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 /* Startup dir */ 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 /** @} */