XMMS2

src/xmms/ipc.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 #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