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 #include <glib.h> 00018 #include <string.h> 00019 00020 #include "xmms/xmms_log.h" 00021 #include "xmms/xmms_config.h" 00022 #include "xmmspriv/xmms_ipc.h" 00023 #include "xmmsc/xmmsc_ipc_msg.h" 00024 00025 00026 /** 00027 * @defgroup IPC IPC 00028 * @ingroup XMMSServer 00029 * @brief IPC functions for XMMS2 Daemon 00030 * @{ 00031 */ 00032 00033 00034 00035 /** 00036 * The IPC object list 00037 */ 00038 typedef struct xmms_ipc_object_pool_t { 00039 xmms_object_t *objects[XMMS_IPC_OBJECT_END]; 00040 xmms_object_t *signals[XMMS_IPC_SIGNAL_END]; 00041 xmms_object_t *broadcasts[XMMS_IPC_SIGNAL_END]; 00042 } xmms_ipc_object_pool_t; 00043 00044 00045 /** 00046 * The server IPC object 00047 */ 00048 struct xmms_ipc_St { 00049 xmms_ipc_transport_t *transport; 00050 GList *clients; 00051 GIOChannel *chan; 00052 GMutex *mutex_lock; 00053 xmms_object_t **objects; 00054 xmms_object_t **signals; 00055 xmms_object_t **broadcasts; 00056 }; 00057 00058 00059 /** 00060 * A IPC client representation. 00061 */ 00062 typedef struct xmms_ipc_client_St { 00063 GMainLoop *ml; 00064 GIOChannel *iochan; 00065 00066 xmms_ipc_transport_t *transport; 00067 xmms_ipc_msg_t *read_msg; 00068 xmms_ipc_t *ipc; 00069 00070 /* this lock protects out_msg, pendingsignals and broadcasts, 00071 which can be accessed from other threads than the 00072 client-thread */ 00073 GMutex *lock; 00074 00075 /** Messages waiting to be written */ 00076 GQueue *out_msg; 00077 00078 guint pendingsignals[XMMS_IPC_SIGNAL_END]; 00079 GList *broadcasts[XMMS_IPC_SIGNAL_END]; 00080 } xmms_ipc_client_t; 00081 00082 static GMutex *ipc_servers_lock; 00083 static GList *ipc_servers = NULL; 00084 00085 static GMutex *ipc_object_pool_lock; 00086 static struct xmms_ipc_object_pool_t *ipc_object_pool = NULL; 00087 00088 static void xmms_ipc_client_destroy (xmms_ipc_client_t *client); 00089 00090 static void xmms_ipc_register_signal (xmms_ipc_client_t *client, xmms_ipc_msg_t *msg, xmmsv_t *arguments); 00091 static void xmms_ipc_register_broadcast (xmms_ipc_client_t *client, xmms_ipc_msg_t *msg, xmmsv_t *arguments); 00092 static gboolean xmms_ipc_client_msg_write (xmms_ipc_client_t *client, xmms_ipc_msg_t *msg); 00093 00094 static gboolean 00095 type_and_msg_to_arg (xmmsv_type_t expected_type, xmmsv_t *argument_list, 00096 xmms_object_cmd_arg_t *arg, gint i) 00097 { 00098 xmmsv_t *arg_value; 00099 xmmsv_type_t actual_type; 00100 00101 if (argument_list && xmmsv_list_get (argument_list, i, &arg_value)) { 00102 xmmsv_ref (arg_value); 00103 } else { 00104 arg_value = xmmsv_new_none (); 00105 } 00106 00107 actual_type = xmmsv_get_type (arg_value); 00108 00109 if (actual_type != expected_type) { 00110 XMMS_DBG ("Expected type %i, but got type %i", 00111 expected_type, actual_type); 00112 00113 xmmsv_unref (arg_value); 00114 00115 return FALSE; 00116 } else { 00117 arg->values[i] = arg_value; 00118 00119 return TRUE; 00120 } 00121 } 00122 00123 static void 00124 xmms_ipc_handle_cmd_value (xmms_ipc_msg_t *msg, xmmsv_t *val) 00125 { 00126 if (xmms_ipc_msg_put_value (msg, val) == (uint32_t) -1) { 00127 xmms_log_error ("Failed to serialize the return value into the IPC message!"); 00128 } 00129 } 00130 00131 static void 00132 xmms_ipc_register_signal (xmms_ipc_client_t *client, 00133 xmms_ipc_msg_t *msg, xmmsv_t *arguments) 00134 { 00135 xmmsv_t *arg; 00136 gint32 signalid; 00137 int r; 00138 00139 if (!arguments || !xmmsv_list_get (arguments, 0, &arg)) { 00140 xmms_log_error ("No signalid in this msg?!"); 00141 return; 00142 } 00143 00144 r = xmmsv_get_int (arg, &signalid); 00145 00146 if (!r) { 00147 xmms_log_error ("Cannot extract signal id from value"); 00148 return; 00149 } 00150 00151 if (signalid < 0 || signalid >= XMMS_IPC_SIGNAL_END) { 00152 xmms_log_error ("Bad signal id (%d)", signalid); 00153 return; 00154 } 00155 00156 g_mutex_lock (client->lock); 00157 client->pendingsignals[signalid] = xmms_ipc_msg_get_cookie (msg); 00158 g_mutex_unlock (client->lock); 00159 } 00160 00161 static void 00162 xmms_ipc_register_broadcast (xmms_ipc_client_t *client, 00163 xmms_ipc_msg_t *msg, xmmsv_t *arguments) 00164 { 00165 xmmsv_t *arg; 00166 gint32 broadcastid; 00167 int r; 00168 00169 if (!arguments || !xmmsv_list_get (arguments, 0, &arg)) { 00170 xmms_log_error ("No broadcastid in this msg?!"); 00171 return; 00172 } 00173 00174 r = xmmsv_get_int (arg, &broadcastid); 00175 00176 if (!r) { 00177 xmms_log_error ("Cannot extract broadcast id from value"); 00178 return; 00179 } 00180 00181 if (broadcastid < 0 || broadcastid >= XMMS_IPC_SIGNAL_END) { 00182 xmms_log_error ("Bad broadcast id (%d)", broadcastid); 00183 return; 00184 } 00185 00186 g_mutex_lock (client->lock); 00187 client->broadcasts[broadcastid] = 00188 g_list_append (client->broadcasts[broadcastid], 00189 GUINT_TO_POINTER (xmms_ipc_msg_get_cookie (msg))); 00190 00191 g_mutex_unlock (client->lock); 00192 } 00193 00194 static void 00195 process_msg (xmms_ipc_client_t *client, xmms_ipc_msg_t *msg) 00196 { 00197 xmms_object_t *object; 00198 xmms_object_cmd_desc_t *cmd = NULL; 00199 xmms_object_cmd_arg_t arg; 00200 xmms_ipc_msg_t *retmsg; 00201 xmmsv_t *error, *arguments; 00202 uint32_t objid, cmdid; 00203 gint i; 00204 00205 g_return_if_fail (msg); 00206 00207 objid = xmms_ipc_msg_get_object (msg); 00208 cmdid = xmms_ipc_msg_get_cmd (msg); 00209 00210 if (!xmms_ipc_msg_get_value (msg, &arguments)) { 00211 xmms_log_error ("Cannot read command arguments. " 00212 "Ignoring command."); 00213 00214 return; 00215 } 00216 00217 if (objid == XMMS_IPC_OBJECT_SIGNAL) { 00218 if (cmdid == XMMS_IPC_CMD_SIGNAL) { 00219 xmms_ipc_register_signal (client, msg, arguments); 00220 } else if (cmdid == XMMS_IPC_CMD_BROADCAST) { 00221 xmms_ipc_register_broadcast (client, msg, arguments); 00222 } else { 00223 xmms_log_error ("Bad command id (%d) for signal object", cmdid); 00224 } 00225 00226 goto out; 00227 } 00228 00229 if (objid >= XMMS_IPC_OBJECT_END) { 00230 xmms_log_error ("Bad object id (%d)", objid); 00231 goto out; 00232 } 00233 00234 g_mutex_lock (ipc_object_pool_lock); 00235 object = ipc_object_pool->objects[objid]; 00236 g_mutex_unlock (ipc_object_pool_lock); 00237 if (!object) { 00238 xmms_log_error ("Object %d was not found!", objid); 00239 goto out; 00240 } 00241 00242 if (object->cmds) 00243 cmd = g_tree_lookup (object->cmds, GUINT_TO_POINTER (cmdid)); 00244 00245 if (!cmd) { 00246 xmms_log_error ("No such cmd %d on object %d", cmdid, objid); 00247 goto out; 00248 } 00249 00250 xmms_object_cmd_arg_init (&arg); 00251 00252 for (i = 0; i < XMMS_OBJECT_CMD_MAX_ARGS; i++) { 00253 if (!type_and_msg_to_arg (cmd->args[i], arguments, &arg, i)) { 00254 xmms_log_error ("Error parsing args"); 00255 00256 if (objid == XMMS_IPC_OBJECT_MAIN && 00257 cmdid == XMMS_IPC_CMD_HELLO) { 00258 xmms_log_error ("Couldn't parse hello message. " 00259 "Maybe the client or libxmmsclient " 00260 "needs to be updated."); 00261 } 00262 00263 retmsg = xmms_ipc_msg_new (objid, XMMS_IPC_CMD_ERROR); 00264 00265 error = xmmsv_new_error ("Corrupt msg"); 00266 xmms_ipc_msg_put_value (retmsg, error); 00267 xmmsv_unref (error); 00268 00269 goto err; 00270 } 00271 00272 } 00273 00274 xmms_object_cmd_call (object, cmdid, &arg); 00275 if (xmms_error_isok (&arg.error)) { 00276 retmsg = xmms_ipc_msg_new (objid, XMMS_IPC_CMD_REPLY); 00277 xmms_ipc_handle_cmd_value (retmsg, arg.retval); 00278 } else { 00279 /* FIXME: or we could omit setting the command to _CMD_ERROR 00280 * and let the client check whether the value it got is an 00281 * error xmmsv_t. If so, don't forget to 00282 * update the client-side of IPC too. */ 00283 retmsg = xmms_ipc_msg_new (objid, XMMS_IPC_CMD_ERROR); 00284 00285 error = xmmsv_new_error (xmms_error_message_get (&arg.error)); 00286 xmms_ipc_msg_put_value (retmsg, error); 00287 xmmsv_unref (error); 00288 00289 /* 00290 retmsg = xmms_ipc_msg_new (objid, XMMS_IPC_CMD_REPLY); 00291 xmms_ipc_handle_cmd_value (retmsg, arg.retval); 00292 */ 00293 } 00294 00295 if (arg.retval) 00296 xmmsv_unref (arg.retval); 00297 00298 err: 00299 for (i = 0; i < XMMS_OBJECT_CMD_MAX_ARGS; i++) { 00300 if (arg.values[i]) 00301 xmmsv_unref (arg.values[i]); 00302 } 00303 xmms_ipc_msg_set_cookie (retmsg, xmms_ipc_msg_get_cookie (msg)); 00304 g_mutex_lock (client->lock); 00305 xmms_ipc_client_msg_write (client, retmsg); 00306 g_mutex_unlock (client->lock); 00307 00308 out: 00309 if (arguments) { 00310 xmmsv_unref (arguments); 00311 } 00312 } 00313 00314 00315 static gboolean 00316 xmms_ipc_client_read_cb (GIOChannel *iochan, 00317 GIOCondition cond, 00318 gpointer data) 00319 { 00320 xmms_ipc_client_t *client = data; 00321 bool disconnect = FALSE; 00322 00323 g_return_val_if_fail (client, FALSE); 00324 00325 if (cond & G_IO_IN) { 00326 while (TRUE) { 00327 if (!client->read_msg) { 00328 client->read_msg = xmms_ipc_msg_alloc (); 00329 } 00330 00331 if (xmms_ipc_msg_read_transport (client->read_msg, client->transport, &disconnect)) { 00332 xmms_ipc_msg_t *msg = client->read_msg; 00333 client->read_msg = NULL; 00334 process_msg (client, msg); 00335 xmms_ipc_msg_destroy (msg); 00336 } else { 00337 break; 00338 } 00339 } 00340 } 00341 00342 if (disconnect || (cond & G_IO_HUP)) { 00343 if (client->read_msg) { 00344 xmms_ipc_msg_destroy (client->read_msg); 00345 client->read_msg = NULL; 00346 } 00347 XMMS_DBG ("disconnect was true!"); 00348 g_main_loop_quit (client->ml); 00349 return FALSE; 00350 } 00351 00352 if (cond & G_IO_ERR) { 00353 xmms_log_error ("Client got error, maybe connection died?"); 00354 g_main_loop_quit (client->ml); 00355 return FALSE; 00356 } 00357 00358 return TRUE; 00359 } 00360 00361 static gboolean 00362 xmms_ipc_client_write_cb (GIOChannel *iochan, 00363 GIOCondition cond, 00364 gpointer data) 00365 { 00366 xmms_ipc_client_t *client = data; 00367 bool disconnect = FALSE; 00368 00369 g_return_val_if_fail (client, FALSE); 00370 00371 while (TRUE) { 00372 xmms_ipc_msg_t *msg; 00373 00374 g_mutex_lock (client->lock); 00375 msg = g_queue_peek_head (client->out_msg); 00376 g_mutex_unlock (client->lock); 00377 00378 if (!msg) 00379 break; 00380 00381 if (!xmms_ipc_msg_write_transport (msg, 00382 client->transport, 00383 &disconnect)) { 00384 if (disconnect) { 00385 break; 00386 } else { 00387 /* try sending again later */ 00388 return TRUE; 00389 } 00390 } 00391 00392 g_mutex_lock (client->lock); 00393 g_queue_pop_head (client->out_msg); 00394 g_mutex_unlock (client->lock); 00395 00396 xmms_ipc_msg_destroy (msg); 00397 } 00398 00399 return FALSE; 00400 } 00401 00402 static gpointer 00403 xmms_ipc_client_thread (gpointer data) 00404 { 00405 xmms_ipc_client_t *client = data; 00406 GSource *source; 00407 00408 source = g_io_create_watch (client->iochan, G_IO_IN | G_IO_ERR | G_IO_HUP); 00409 g_source_set_callback (source, 00410 (GSourceFunc) xmms_ipc_client_read_cb, 00411 (gpointer) client, 00412 NULL); 00413 g_source_attach (source, g_main_loop_get_context (client->ml)); 00414 g_source_unref (source); 00415 00416 g_main_loop_run (client->ml); 00417 00418 xmms_ipc_client_destroy (client); 00419 00420 return NULL; 00421 } 00422 00423 static xmms_ipc_client_t * 00424 xmms_ipc_client_new (xmms_ipc_t *ipc, xmms_ipc_transport_t *transport) 00425 { 00426 xmms_ipc_client_t *client; 00427 GMainContext *context; 00428 int fd; 00429 00430 g_return_val_if_fail (transport, NULL); 00431 00432 client = g_new0 (xmms_ipc_client_t, 1); 00433 00434 context = g_main_context_new (); 00435 client->ml = g_main_loop_new (context, FALSE); 00436 g_main_context_unref (context); 00437 00438 fd = xmms_ipc_transport_fd_get (transport); 00439 client->iochan = g_io_channel_unix_new (fd); 00440 g_return_val_if_fail (client->iochan, NULL); 00441 00442 /* We don't set the close_on_unref flag here, because 00443 * the transport will close the fd for us. No need to close it twice. 00444 */ 00445 g_io_channel_set_encoding (client->iochan, NULL, NULL); 00446 g_io_channel_set_buffered (client->iochan, FALSE); 00447 00448 client->transport = transport; 00449 client->ipc = ipc; 00450 client->out_msg = g_queue_new (); 00451 client->lock = g_mutex_new (); 00452 00453 return client; 00454 } 00455 00456 static void 00457 xmms_ipc_client_destroy (xmms_ipc_client_t *client) 00458 { 00459 guint i; 00460 00461 XMMS_DBG ("Destroying client!"); 00462 00463 if (client->ipc) { 00464 g_mutex_lock (client->ipc->mutex_lock); 00465 client->ipc->clients = g_list_remove (client->ipc->clients, client); 00466 g_mutex_unlock (client->ipc->mutex_lock); 00467 } 00468 00469 g_main_loop_unref (client->ml); 00470 g_io_channel_unref (client->iochan); 00471 00472 xmms_ipc_transport_destroy (client->transport); 00473 00474 g_mutex_lock (client->lock); 00475 while (!g_queue_is_empty (client->out_msg)) { 00476 xmms_ipc_msg_t *msg = g_queue_pop_head (client->out_msg); 00477 xmms_ipc_msg_destroy (msg); 00478 } 00479 00480 g_queue_free (client->out_msg); 00481 00482 for (i = 0; i < XMMS_IPC_SIGNAL_END; i++) { 00483 g_list_free (client->broadcasts[i]); 00484 } 00485 00486 g_mutex_unlock (client->lock); 00487 g_mutex_free (client->lock); 00488 g_free (client); 00489 } 00490 00491 /** 00492 * Gets called when the config property "core.ipcsocket" has changed. 00493 */ 00494 void 00495 on_config_ipcsocket_change (xmms_object_t *object, xmmsv_t *_data, gpointer udata) 00496 { 00497 const gchar *value; 00498 00499 XMMS_DBG ("Shutting down ipc server threads through config property \"core.ipcsocket\" change."); 00500 00501 xmms_ipc_shutdown (); 00502 value = xmms_config_property_get_string ((xmms_config_property_t *) object); 00503 xmms_ipc_setup_server (value); 00504 } 00505 00506 /** 00507 * Put a message in the queue awaiting to be sent to the client. 00508 * Should hold client->lock. 00509 */ 00510 static gboolean 00511 xmms_ipc_client_msg_write (xmms_ipc_client_t *client, xmms_ipc_msg_t *msg) 00512 { 00513 gboolean queue_empty; 00514 00515 g_return_val_if_fail (client, FALSE); 00516 g_return_val_if_fail (msg, FALSE); 00517 00518 queue_empty = g_queue_is_empty (client->out_msg); 00519 g_queue_push_tail (client->out_msg, msg); 00520 00521 /* If there's no write in progress, add a new callback */ 00522 if (queue_empty) { 00523 GMainContext *context = g_main_loop_get_context (client->ml); 00524 GSource *source = g_io_create_watch (client->iochan, G_IO_OUT); 00525 00526 g_source_set_callback (source, 00527 (GSourceFunc) xmms_ipc_client_write_cb, 00528 (gpointer) client, 00529 NULL); 00530 g_source_attach (source, context); 00531 g_source_unref (source); 00532 00533 g_main_context_wakeup (context); 00534 } 00535 00536 return TRUE; 00537 } 00538 00539 static gboolean 00540 xmms_ipc_source_accept (GIOChannel *chan, GIOCondition cond, gpointer data) 00541 { 00542 xmms_ipc_t *ipc = (xmms_ipc_t *) data; 00543 xmms_ipc_transport_t *transport; 00544 xmms_ipc_client_t *client; 00545 00546 if (!(cond & G_IO_IN)) { 00547 xmms_log_error ("IPC listener got error/hup"); 00548 return FALSE; 00549 } 00550 00551 XMMS_DBG ("Client connected"); 00552 transport = xmms_ipc_server_accept (ipc->transport); 00553 if (!transport) { 00554 xmms_log_error ("accept returned null!"); 00555 return TRUE; 00556 } 00557 00558 client = xmms_ipc_client_new (ipc, transport); 00559 if (!client) { 00560 xmms_ipc_transport_destroy (transport); 00561 return TRUE; 00562 } 00563 00564 g_mutex_lock (ipc->mutex_lock); 00565 ipc->clients = g_list_append (ipc->clients, client); 00566 g_mutex_unlock (ipc->mutex_lock); 00567 00568 /* Now that the client has been registered in the ipc->clients list 00569 * we may safely start its thread. 00570 */ 00571 g_thread_create (xmms_ipc_client_thread, client, FALSE, NULL); 00572 00573 return TRUE; 00574 } 00575 00576 /** 00577 * Enable IPC 00578 */ 00579 static gboolean 00580 xmms_ipc_setup_server_internaly (xmms_ipc_t *ipc) 00581 { 00582 g_mutex_lock (ipc->mutex_lock); 00583 ipc->chan = g_io_channel_unix_new (xmms_ipc_transport_fd_get (ipc->transport)); 00584 00585 g_io_channel_set_close_on_unref (ipc->chan, TRUE); 00586 g_io_channel_set_encoding (ipc->chan, NULL, NULL); 00587 g_io_channel_set_buffered (ipc->chan, FALSE); 00588 00589 g_io_add_watch (ipc->chan, G_IO_IN | G_IO_HUP | G_IO_ERR, 00590 xmms_ipc_source_accept, ipc); 00591 g_mutex_unlock (ipc->mutex_lock); 00592 return TRUE; 00593 } 00594 00595 /** 00596 * Checks if someone is waiting for signalid 00597 */ 00598 gboolean 00599 xmms_ipc_has_pending (guint signalid) 00600 { 00601 GList *c, *s; 00602 xmms_ipc_t *ipc; 00603 00604 g_mutex_lock (ipc_servers_lock); 00605 00606 for (s = ipc_servers; s; s = g_list_next (s)) { 00607 ipc = s->data; 00608 g_mutex_lock (ipc->mutex_lock); 00609 for (c = ipc->clients; c; c = g_list_next (c)) { 00610 xmms_ipc_client_t *cli = c->data; 00611 g_mutex_lock (cli->lock); 00612 if (cli->pendingsignals[signalid]) { 00613 g_mutex_unlock (cli->lock); 00614 g_mutex_unlock (ipc->mutex_lock); 00615 g_mutex_unlock (ipc_servers_lock); 00616 return TRUE; 00617 } 00618 g_mutex_unlock (cli->lock); 00619 } 00620 g_mutex_unlock (ipc->mutex_lock); 00621 } 00622 00623 g_mutex_unlock (ipc_servers_lock); 00624 return FALSE; 00625 } 00626 00627 static void 00628 xmms_ipc_signal_cb (xmms_object_t *object, xmmsv_t *arg, gpointer userdata) 00629 { 00630 GList *c, *s; 00631 guint signalid = GPOINTER_TO_UINT (userdata); 00632 xmms_ipc_t *ipc; 00633 xmms_ipc_msg_t *msg; 00634 00635 g_mutex_lock (ipc_servers_lock); 00636 00637 for (s = ipc_servers; s && s->data; s = g_list_next (s)) { 00638 ipc = s->data; 00639 g_mutex_lock (ipc->mutex_lock); 00640 for (c = ipc->clients; c; c = g_list_next (c)) { 00641 xmms_ipc_client_t *cli = c->data; 00642 g_mutex_lock (cli->lock); 00643 if (cli->pendingsignals[signalid]) { 00644 msg = xmms_ipc_msg_new (XMMS_IPC_OBJECT_SIGNAL, XMMS_IPC_CMD_SIGNAL); 00645 xmms_ipc_msg_set_cookie (msg, cli->pendingsignals[signalid]); 00646 xmms_ipc_handle_cmd_value (msg, arg); 00647 xmms_ipc_client_msg_write (cli, msg); 00648 cli->pendingsignals[signalid] = 0; 00649 } 00650 g_mutex_unlock (cli->lock); 00651 } 00652 g_mutex_unlock (ipc->mutex_lock); 00653 } 00654 00655 g_mutex_unlock (ipc_servers_lock); 00656 00657 } 00658 00659 static void 00660 xmms_ipc_broadcast_cb (xmms_object_t *object, xmmsv_t *arg, gpointer userdata) 00661 { 00662 GList *c, *s; 00663 guint broadcastid = GPOINTER_TO_UINT (userdata); 00664 xmms_ipc_t *ipc; 00665 xmms_ipc_msg_t *msg = NULL; 00666 GList *l; 00667 00668 g_mutex_lock (ipc_servers_lock); 00669 00670 for (s = ipc_servers; s && s->data; s = g_list_next (s)) { 00671 ipc = s->data; 00672 g_mutex_lock (ipc->mutex_lock); 00673 for (c = ipc->clients; c; c = g_list_next (c)) { 00674 xmms_ipc_client_t *cli = c->data; 00675 00676 g_mutex_lock (cli->lock); 00677 for (l = cli->broadcasts[broadcastid]; l; l = g_list_next (l)) { 00678 msg = xmms_ipc_msg_new (XMMS_IPC_OBJECT_SIGNAL, XMMS_IPC_CMD_BROADCAST); 00679 xmms_ipc_msg_set_cookie (msg, GPOINTER_TO_UINT (l->data)); 00680 xmms_ipc_handle_cmd_value (msg, arg); 00681 xmms_ipc_client_msg_write (cli, msg); 00682 } 00683 g_mutex_unlock (cli->lock); 00684 } 00685 g_mutex_unlock (ipc->mutex_lock); 00686 } 00687 g_mutex_unlock (ipc_servers_lock); 00688 } 00689 00690 /** 00691 * Register a broadcast signal. 00692 */ 00693 void 00694 xmms_ipc_broadcast_register (xmms_object_t *object, xmms_ipc_signals_t signalid) 00695 { 00696 g_return_if_fail (object); 00697 g_mutex_lock (ipc_object_pool_lock); 00698 00699 ipc_object_pool->broadcasts[signalid] = object; 00700 xmms_object_connect (object, signalid, xmms_ipc_broadcast_cb, GUINT_TO_POINTER (signalid)); 00701 00702 g_mutex_unlock (ipc_object_pool_lock); 00703 } 00704 00705 /** 00706 * Unregister a broadcast signal. 00707 */ 00708 void 00709 xmms_ipc_broadcast_unregister (xmms_ipc_signals_t signalid) 00710 { 00711 xmms_object_t *obj; 00712 00713 g_mutex_lock (ipc_object_pool_lock); 00714 obj = ipc_object_pool->broadcasts[signalid]; 00715 if (obj) { 00716 xmms_object_disconnect (obj, signalid, xmms_ipc_broadcast_cb, GUINT_TO_POINTER (signalid)); 00717 ipc_object_pool->broadcasts[signalid] = NULL; 00718 } 00719 g_mutex_unlock (ipc_object_pool_lock); 00720 } 00721 00722 /** 00723 * Register a signal 00724 */ 00725 void 00726 xmms_ipc_signal_register (xmms_object_t *object, xmms_ipc_signals_t signalid) 00727 { 00728 g_return_if_fail (object); 00729 00730 g_mutex_lock (ipc_object_pool_lock); 00731 ipc_object_pool->signals[signalid] = object; 00732 xmms_object_connect (object, signalid, xmms_ipc_signal_cb, GUINT_TO_POINTER (signalid)); 00733 g_mutex_unlock (ipc_object_pool_lock); 00734 } 00735 00736 /** 00737 * Unregister a signal 00738 */ 00739 void 00740 xmms_ipc_signal_unregister (xmms_ipc_signals_t signalid) 00741 { 00742 xmms_object_t *obj; 00743 00744 g_mutex_lock (ipc_object_pool_lock); 00745 obj = ipc_object_pool->signals[signalid]; 00746 if (obj) { 00747 xmms_object_disconnect (obj, signalid, xmms_ipc_signal_cb, GUINT_TO_POINTER (signalid)); 00748 ipc_object_pool->signals[signalid] = NULL; 00749 } 00750 g_mutex_unlock (ipc_object_pool_lock); 00751 } 00752 00753 /** 00754 * Register a object to the IPC core. This needs to be done if you 00755 * want to send commands to that object from the client. 00756 */ 00757 void 00758 xmms_ipc_object_register (xmms_ipc_objects_t objectid, xmms_object_t *object) 00759 { 00760 g_mutex_lock (ipc_object_pool_lock); 00761 ipc_object_pool->objects[objectid] = object; 00762 g_mutex_unlock (ipc_object_pool_lock); 00763 } 00764 00765 /** 00766 * Remove a object from the IPC core. 00767 */ 00768 void 00769 xmms_ipc_object_unregister (xmms_ipc_objects_t objectid) 00770 { 00771 g_mutex_lock (ipc_object_pool_lock); 00772 ipc_object_pool->objects[objectid] = NULL; 00773 g_mutex_unlock (ipc_object_pool_lock); 00774 } 00775 00776 /** 00777 * Initialize IPC 00778 */ 00779 xmms_ipc_t * 00780 xmms_ipc_init (void) 00781 { 00782 ipc_servers_lock = g_mutex_new (); 00783 ipc_object_pool_lock = g_mutex_new (); 00784 ipc_object_pool = g_new0 (xmms_ipc_object_pool_t, 1); 00785 return NULL; 00786 } 00787 00788 /** 00789 * Shutdown a IPC Server 00790 */ 00791 static void 00792 xmms_ipc_shutdown_server (xmms_ipc_t *ipc) 00793 { 00794 GList *c; 00795 xmms_ipc_client_t *co; 00796 if (!ipc) return; 00797 00798 g_mutex_lock (ipc->mutex_lock); 00799 g_source_remove_by_user_data (ipc); 00800 g_io_channel_unref (ipc->chan); 00801 xmms_ipc_transport_destroy (ipc->transport); 00802 00803 for (c = ipc->clients; c; c = g_list_next (c)) { 00804 co = c->data; 00805 if (!co) continue; 00806 co->ipc = NULL; 00807 } 00808 00809 g_list_free (ipc->clients); 00810 g_mutex_unlock (ipc->mutex_lock); 00811 g_mutex_free (ipc->mutex_lock); 00812 00813 g_free (ipc); 00814 00815 } 00816 00817 00818 /** 00819 * Disable IPC 00820 */ 00821 void 00822 xmms_ipc_shutdown (void) 00823 { 00824 GList *s = ipc_servers; 00825 xmms_ipc_t *ipc; 00826 00827 g_mutex_lock (ipc_servers_lock); 00828 while (s) { 00829 ipc = s->data; 00830 s = g_list_next (s); 00831 ipc_servers = g_list_remove (ipc_servers, ipc); 00832 xmms_ipc_shutdown_server (ipc); 00833 } 00834 g_mutex_unlock (ipc_servers_lock); 00835 00836 } 00837 00838 /** 00839 * Start the server 00840 */ 00841 gboolean 00842 xmms_ipc_setup_server (const gchar *path) 00843 { 00844 xmms_ipc_transport_t *transport; 00845 xmms_ipc_t *ipc; 00846 gchar **split; 00847 gint i = 0, num_init = 0; 00848 g_return_val_if_fail (path, FALSE); 00849 00850 split = g_strsplit (path, ";", 0); 00851 00852 for (i = 0; split && split[i]; i++) { 00853 ipc = g_new0 (xmms_ipc_t, 1); 00854 if (!ipc) { 00855 XMMS_DBG ("No IPC server initialized."); 00856 continue; 00857 } 00858 00859 transport = xmms_ipc_server_init (split[i]); 00860 if (!transport) { 00861 g_free (ipc); 00862 xmms_log_error ("Couldn't setup IPC listening on '%s'.", split[i]); 00863 continue; 00864 } 00865 00866 00867 ipc->mutex_lock = g_mutex_new (); 00868 ipc->transport = transport; 00869 ipc->signals = ipc_object_pool->signals; 00870 ipc->broadcasts = ipc_object_pool->broadcasts; 00871 ipc->objects = ipc_object_pool->objects; 00872 00873 xmms_ipc_setup_server_internaly (ipc); 00874 xmms_log_info ("IPC listening on '%s'.", split[i]); 00875 00876 g_mutex_lock (ipc_servers_lock); 00877 ipc_servers = g_list_prepend (ipc_servers, ipc); 00878 g_mutex_unlock (ipc_servers_lock); 00879 00880 num_init++; 00881 } 00882 00883 g_strfreev (split); 00884 00885 00886 /* If there is less than one socket, there is sth. wrong. */ 00887 if (num_init < 1) 00888 return FALSE; 00889 00890 XMMS_DBG ("IPC setup done."); 00891 return TRUE; 00892 } 00893 00894 /** @} */ 00895