summaryrefslogtreecommitdiffstats
path: root/dbus/dbus-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'dbus/dbus-connection.c')
-rw-r--r--dbus/dbus-connection.c413
1 files changed, 411 insertions, 2 deletions
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
index 3c6df57f..4a40f0b0 100644
--- a/dbus/dbus-connection.c
+++ b/dbus/dbus-connection.c
@@ -27,6 +27,7 @@
#include "dbus-watch.h"
#include "dbus-connection-internal.h"
#include "dbus-list.h"
+#include "dbus-hash.h"
#include "dbus-message-internal.h"
/**
@@ -75,6 +76,10 @@ struct DBusConnection
DBusConnectionErrorFunction error_function; /**< Callback for errors. */
void *error_data; /**< Data for error callback. */
DBusFreeFunction error_free_data_function; /**< Free function for error callback data. */
+ DBusHashTable *handler_table; /**< Table of registered DBusMessageHandler */
+ DBusList *filter_list; /**< List of filters. */
+ int filters_serial; /**< Increments when the list of filters is changed. */
+ int handlers_serial; /**< Increments when the handler table is changed. */
};
/**
@@ -270,13 +275,21 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
{
DBusConnection *connection;
DBusWatchList *watch_list;
+ DBusHashTable *handler_table;
watch_list = NULL;
connection = NULL;
+ handler_table = NULL;
watch_list = _dbus_watch_list_new ();
if (watch_list == NULL)
goto error;
+
+ handler_table =
+ _dbus_hash_table_new (DBUS_HASH_STRING,
+ dbus_free, NULL);
+ if (handler_table == NULL)
+ goto error;
connection = dbus_new0 (DBusConnection, 1);
if (connection == NULL)
@@ -285,7 +298,9 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
connection->refcount = 1;
connection->transport = transport;
connection->watches = watch_list;
-
+ connection->handler_table = handler_table;
+ connection->filter_list = NULL;
+
_dbus_transport_ref (transport);
_dbus_transport_set_connection (transport, connection);
@@ -293,14 +308,56 @@ _dbus_connection_new_for_transport (DBusTransport *transport)
error:
- _dbus_assert (connection == NULL);
+ if (connection != NULL)
+ dbus_free (connection);
+ if (handler_table)
+ _dbus_hash_table_unref (handler_table);
+
if (watch_list)
_dbus_watch_list_free (watch_list);
return NULL;
}
+/**
+ * Used to notify a connection when a DBusMessageHandler is
+ * destroyed, so the connection can drop any reference
+ * to the handler.
+ *
+ * @param connection the connection
+ * @param handler the handler
+ */
+void
+_dbus_connection_handler_destroyed (DBusConnection *connection,
+ DBusMessageHandler *handler)
+{
+ DBusHashIter iter;
+ DBusList *link;
+
+ _dbus_hash_iter_init (connection->handler_table, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ DBusMessageHandler *h = _dbus_hash_iter_get_value (&iter);
+
+ if (h == handler)
+ _dbus_hash_iter_remove_entry (&iter);
+ }
+
+ link = _dbus_list_get_first_link (&connection->filter_list);
+ while (link != NULL)
+ {
+ DBusMessageHandler *h = link->data;
+ DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link);
+
+ if (h == handler)
+ _dbus_list_remove_link (&connection->filter_list,
+ link);
+
+ link = next;
+ }
+}
+
/** @} */
/**
@@ -377,11 +434,38 @@ dbus_connection_unref (DBusConnection *connection)
connection->refcount -= 1;
if (connection->refcount == 0)
{
+ DBusHashIter iter;
+ DBusList *link;
+
/* free error data as a side effect */
dbus_connection_set_error_function (connection,
NULL, NULL, NULL);
_dbus_watch_list_free (connection->watches);
+
+ _dbus_hash_iter_init (connection->handler_table, &iter);
+ while (_dbus_hash_iter_next (&iter))
+ {
+ DBusMessageHandler *h = _dbus_hash_iter_get_value (&iter);
+
+ _dbus_message_handler_remove_connection (h, connection);
+ }
+
+ link = _dbus_list_get_first_link (&connection->filter_list);
+ while (link != NULL)
+ {
+ DBusMessageHandler *h = link->data;
+ DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link);
+
+ _dbus_message_handler_remove_connection (h, connection);
+
+ link = next;
+ }
+
+ _dbus_hash_table_unref (connection->handler_table);
+ connection->handler_table = NULL;
+
+ _dbus_list_clear (&connection->filter_list);
_dbus_list_foreach (&connection->outgoing_messages,
(DBusForeachFunction) dbus_message_unref,
@@ -471,6 +555,66 @@ dbus_connection_send_message (DBusConnection *connection,
}
/**
+ * Queues a message to send, as with dbus_connection_send_message(),
+ * but also sets up a DBusMessageHandler to receive a reply to the
+ * message. If no reply is received in the given timeout_milliseconds,
+ * expires the pending reply and sends the DBusMessageHandler a
+ * synthetic error reply (generated in-process, not by the remote
+ * application) indicating that a timeout occurred.
+ *
+ * Reply handlers see their replies after message filters see them,
+ * but before message handlers added with
+ * dbus_connection_register_handler() see them, regardless of the
+ * reply message's name. Reply handlers are only handed a single
+ * message as a reply, after a reply has been seen the handler is
+ * removed. If a filter filters out the reply before the handler sees
+ * it, the handler is not removed but the timeout will immediately
+ * fire again. If a filter was dumb and kept removing the timeout
+ * reply then we'd get in an infinite loop.
+ *
+ * If #NULL is passed for the reply_handler, the timeout reply will
+ * still be generated and placed into the message queue, but no
+ * specific message handler will receive the reply.
+ *
+ * If -1 is passed for the timeout, a sane default timeout is used. -1
+ * is typically the best value for the timeout for this reason, unless
+ * you want a very short or very long timeout. There is no way to
+ * avoid a timeout entirely, other than passing INT_MAX for the
+ * timeout to postpone it indefinitely.
+ *
+ * @param connection the connection
+ * @param message the message to send
+ * @param reply_handler message handler expecting the reply, or #NULL
+ * @param timeout_milliseconds timeout in milliseconds or -1 for default
+ * @param result return location for result code
+ * @returns #TRUE if the message is successfully queued, #FALSE if no memory.
+ *
+ * @todo this function isn't implemented because we need message serials
+ * and other slightly more rich DBusMessage implementation in order to
+ * implement it. The basic idea will be to keep a hash of serials we're
+ * expecting a reply to, and also to add a way to tell GLib or Qt to
+ * install a timeout. Then install a timeout which is the shortest
+ * timeout of any pending reply.
+ *
+ * @todo implement non-reentrant "block for reply" variant. i.e. send
+ * a message, block until we get a reply, then pull reply out of
+ * message queue and return it, *without dispatching any handlers for
+ * any other messages* - used for non-reentrant "method calls" We can
+ * block properly for this using _dbus_connection_do_iteration().
+ *
+ */
+dbus_bool_t
+dbus_connection_send_message_with_reply (DBusConnection *connection,
+ DBusMessage *message,
+ DBusMessageHandler *reply_handler,
+ int timeout_milliseconds,
+ DBusResultCode *result)
+{
+ /* FIXME */
+ return dbus_connection_send_message (connection, message, result);
+}
+
+/**
* Blocks until the outgoing message queue is empty.
*
* @param connection the connection.
@@ -532,6 +676,90 @@ dbus_connection_pop_message (DBusConnection *connection)
}
/**
+ * Pops the first-received message from the current incoming message
+ * queue, runs any handlers for it, then unrefs the message.
+ *
+ * @param connection the connection
+ * @returns #TRUE if the queue is not empty after dispatch
+ *
+ * @todo this function is not properly robust against reentrancy,
+ * that is, if handlers are added/removed while dispatching
+ * a message, things will get messed up.
+ */
+dbus_bool_t
+dbus_connection_dispatch_message (DBusConnection *connection)
+{
+ DBusMessage *message;
+ int filter_serial;
+ int handler_serial;
+ DBusList *link;
+ DBusHandlerResult result;
+ const char *name;
+
+ dbus_connection_ref (connection);
+
+ message = dbus_connection_pop_message (connection);
+ if (message == NULL)
+ return FALSE;
+
+ filter_serial = connection->filters_serial;
+ handler_serial = connection->handlers_serial;
+
+ result = DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
+
+ link = _dbus_list_get_first_link (&connection->filter_list);
+ while (link != NULL)
+ {
+ DBusMessageHandler *handler = link->data;
+ DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link);
+
+ result = _dbus_message_handler_handle_message (handler, connection,
+ message);
+
+ if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
+ goto out;
+
+ if (filter_serial != connection->filters_serial)
+ {
+ _dbus_warn ("Message filters added or removed while dispatching filters - not currently supported!\n");
+ goto out;
+ }
+
+ link = next;
+ }
+
+ name = dbus_message_get_name (message);
+ if (name != NULL)
+ {
+ DBusMessageHandler *handler;
+
+ handler = _dbus_hash_table_lookup_string (connection->handler_table,
+ name);
+ if (handler != NULL)
+ {
+
+ result = _dbus_message_handler_handle_message (handler, connection,
+ message);
+
+ if (result == DBUS_HANDLER_RESULT_REMOVE_MESSAGE)
+ goto out;
+
+ if (handler_serial != connection->handlers_serial)
+ {
+ _dbus_warn ("Message handlers added or removed while dispatching handlers - not currently supported!\n");
+ goto out;
+ }
+ }
+ }
+
+ out:
+ dbus_connection_unref (connection);
+ dbus_message_unref (message);
+
+ return connection->n_incoming > 0;
+}
+
+/**
* Sets the error handler function for the connection.
*
* @param connection the connection.
@@ -623,4 +851,185 @@ dbus_connection_handle_watch (DBusConnection *connection,
watch, condition);
}
+/**
+ * Adds a message filter. Filters are handlers that are run on
+ * all incoming messages, prior to the normal handlers
+ * registered with dbus_connection_register_handler().
+ * Filters are run in the order that they were added.
+ * The same handler can be added as a filter more than once, in
+ * which case it will be run more than once.
+ *
+ * @param connection the connection
+ * @param handler the handler
+ * @returns #TRUE on success, #FALSE if not enough memory.
+ */
+dbus_bool_t
+dbus_connection_add_filter (DBusConnection *connection,
+ DBusMessageHandler *handler)
+{
+ if (!_dbus_message_handler_add_connection (handler, connection))
+ return FALSE;
+
+ if (!_dbus_list_append (&connection->filter_list,
+ handler))
+ {
+ _dbus_message_handler_remove_connection (handler, connection);
+ return FALSE;
+ }
+
+ connection->filters_serial += 1;
+
+ return TRUE;
+}
+
+/**
+ * Removes a previously-added message filter. It is a programming
+ * error to call this function for a handler that has not
+ * been added as a filter. If the given handler was added
+ * more than once, only one instance of it will be removed
+ * (the most recently-added instance).
+ *
+ * @param connection the connection
+ * @param handler the handler to remove
+ *
+ */
+void
+dbus_connection_remove_filter (DBusConnection *connection,
+ DBusMessageHandler *handler)
+{
+ if (!_dbus_list_remove_last (&connection->filter_list, handler))
+ {
+ _dbus_warn ("Tried to remove a DBusConnection filter that had not been added\n");
+ return;
+ }
+
+ _dbus_message_handler_remove_connection (handler, connection);
+
+ connection->filters_serial += 1;
+}
+
+/**
+ * Registers a handler for a list of message names. A single handler
+ * can be registered for any number of message names, but each message
+ * name can only have one handler at a time. It's not allowed to call
+ * this function with the name of a message that already has a
+ * handler. If the function returns #FALSE, the handlers were not
+ * registered due to lack of memory.
+ *
+ * @param connection the connection
+ * @param handler the handler
+ * @param messages_to_handle the messages to handle
+ * @param n_messages the number of message names in messages_to_handle
+ * @returns #TRUE on success, #FALSE if no memory or another handler already exists
+ *
+ **/
+dbus_bool_t
+dbus_connection_register_handler (DBusConnection *connection,
+ DBusMessageHandler *handler,
+ const char **messages_to_handle,
+ int n_messages)
+{
+ int i;
+
+ i = 0;
+ while (i < n_messages)
+ {
+ DBusHashIter iter;
+ char *key;
+
+ key = _dbus_strdup (messages_to_handle[i]);
+ if (key == NULL)
+ goto failed;
+
+ if (!_dbus_hash_iter_lookup (connection->handler_table,
+ key, TRUE,
+ &iter))
+ {
+ dbus_free (key);
+ goto failed;
+ }
+
+ if (_dbus_hash_iter_get_value (&iter) != NULL)
+ {
+ _dbus_warn ("Bug in application: attempted to register a second handler for %s\n",
+ messages_to_handle[i]);
+ dbus_free (key); /* won't have replaced the old key with the new one */
+ goto failed;
+ }
+
+ if (!_dbus_message_handler_add_connection (handler, connection))
+ {
+ _dbus_hash_iter_remove_entry (&iter);
+ /* key has freed on nuking the entry */
+ goto failed;
+ }
+
+ _dbus_hash_iter_set_value (&iter, handler);
+
+ connection->handlers_serial += 1;
+
+ ++i;
+ }
+
+ return TRUE;
+
+ failed:
+ /* unregister everything registered so far,
+ * so we don't fail partially
+ */
+ dbus_connection_unregister_handler (connection,
+ handler,
+ messages_to_handle,
+ i);
+
+ return FALSE;
+}
+
+/**
+ * Unregisters a handler for a list of message names. The handlers
+ * must have been previously registered.
+ *
+ * @param connection the connection
+ * @param handler the handler
+ * @param messages_to_handle the messages to handle
+ * @param n_messages the number of message names in messages_to_handle
+ *
+ **/
+void
+dbus_connection_unregister_handler (DBusConnection *connection,
+ DBusMessageHandler *handler,
+ const char **messages_to_handle,
+ int n_messages)
+{
+ int i;
+
+ i = 0;
+ while (i < n_messages)
+ {
+ DBusHashIter iter;
+
+ if (!_dbus_hash_iter_lookup (connection->handler_table,
+ (char*) messages_to_handle[i], FALSE,
+ &iter))
+ {
+ _dbus_warn ("Bug in application: attempted to unregister handler for %s which was not registered\n",
+ messages_to_handle[i]);
+ }
+ else if (_dbus_hash_iter_get_value (&iter) != handler)
+ {
+ _dbus_warn ("Bug in application: attempted to unregister handler for %s which was registered by a different handler\n",
+ messages_to_handle[i]);
+ }
+ else
+ {
+ _dbus_hash_iter_remove_entry (&iter);
+ _dbus_message_handler_remove_connection (handler, connection);
+ }
+
+ ++i;
+ }
+
+ connection->handlers_serial += 1;
+}
+
/** @} */