*
* Copyright (C) 2002, 2003 Red Hat Inc.
*
- * Licensed under the Academic Free License version 1.2
+ * Licensed under the Academic Free License version 2.0
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
*/
#include <config.h>
+#include "dbus-shared.h"
#include "dbus-connection.h"
#include "dbus-list.h"
#include "dbus-timeout.h"
#include "dbus-list.h"
#include "dbus-hash.h"
#include "dbus-message-internal.h"
-#include "dbus-message-handler.h"
#include "dbus-threads.h"
#include "dbus-protocol.h"
#include "dbus-dataslot.h"
#include "dbus-string.h"
#include "dbus-pending-call.h"
#include "dbus-object-tree.h"
+#include "dbus-marshal.h"
#if 0
#define CONNECTION_LOCK(connection) do { \
* handle the details here for you by setting up watch functions.
*
* When a connection is disconnected, you are guaranteed to get a
- * message with the name #DBUS_MESSAGE_LOCAL_DISCONNECT.
+ * signal "Disconnected" from the interface
+ * #DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL, path
+ * #DBUS_PATH_ORG_FREEDESKTOP_LOCAL.
*
* You may not drop the last reference to a #DBusConnection
* until that connection has been disconnected.
*
* You may dispatch the unprocessed incoming message queue even if the
- * connection is disconnected. However, #DBUS_MESSAGE_LOCAL_DISCONNECT
- * will always be the last message in the queue (obviously no messages
- * are received after disconnection).
+ * connection is disconnected. However, "Disconnected" will always be
+ * the last message in the queue (obviously no messages are received
+ * after disconnection).
*
* #DBusConnection has thread locks and drops them when invoking user
* callbacks, so in general is transparently threadsafe. However,
* @{
*/
+/**
+ * Internal struct representing a message filter function
+ */
+typedef struct DBusMessageFilter DBusMessageFilter;
+
+/**
+ * Internal struct representing a message filter function
+ */
+struct DBusMessageFilter
+{
+ DBusAtomic refcount; /**< Reference count */
+ DBusHandleMessageFunction function; /**< Function to call to filter */
+ void *user_data; /**< User data for the function */
+ DBusFreeFunction free_user_data_function; /**< Function to free the user data */
+};
+
+
+/**
+ * Internals of DBusPreallocatedSend
+ */
+struct DBusPreallocatedSend
+{
+ DBusConnection *connection; /**< Connection we'd send the message to */
+ DBusList *queue_link; /**< Preallocated link in the queue */
+ DBusList *counter_link; /**< Preallocated link in the resource counter */
+};
+
static dbus_bool_t _dbus_modify_sigpipe = TRUE;
/**
* for the global linked list mempool lock
*/
DBusObjectTree *objects; /**< Object path handlers registered with this connection */
+
+ unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */
};
static void _dbus_connection_remove_timeout_locked (DBusConnection *connection,
DBusDispatchStatus new_status);
static void _dbus_connection_last_unref (DBusConnection *connection);
+static DBusMessageFilter *
+_dbus_message_filter_ref (DBusMessageFilter *filter)
+{
+ _dbus_assert (filter->refcount.value > 0);
+ _dbus_atomic_inc (&filter->refcount);
+
+ return filter;
+}
+
+static void
+_dbus_message_filter_unref (DBusMessageFilter *filter)
+{
+ _dbus_assert (filter->refcount.value > 0);
+
+ if (_dbus_atomic_dec (&filter->refcount) == 1)
+ {
+ if (filter->free_user_data_function)
+ (* filter->free_user_data_function) (filter->user_data);
+
+ dbus_free (filter);
+ }
+}
/**
* Acquires the connection lock.
_dbus_connection_wakeup_mainloop (connection);
- _dbus_verbose ("Message %p (%s) added to incoming queue %p, %d incoming\n",
+ _dbus_verbose ("Message %p (%d %s '%s') added to incoming queue %p, %d incoming\n",
message,
+ dbus_message_get_type (message),
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) :
"no interface",
+ dbus_message_get_signature (message),
connection,
connection->n_incoming);
}
connection->n_outgoing -= 1;
- _dbus_verbose ("Message %p (%s) removed from outgoing queue %p, %d left to send\n",
+ _dbus_verbose ("Message %p (%d %s '%s') removed from outgoing queue %p, %d left to send\n",
message,
+ dbus_message_get_type (message),
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) :
"no interface",
+ dbus_message_get_signature (message),
connection, connection->n_outgoing);
/* Save this link in the link cache also */
timeout, enabled);
}
-/**
- * Tells the connection that the transport has been disconnected.
- * Results in posting a disconnect message on the incoming message
- * queue. Only has an effect the first time it's called.
- *
- * @param connection the connection
- */
-void
-_dbus_connection_notify_disconnected (DBusConnection *connection)
-{
- if (connection->disconnect_message_link)
- {
- /* We haven't sent the disconnect message already */
- _dbus_connection_queue_synthesized_message_link (connection,
- connection->disconnect_message_link);
- connection->disconnect_message_link = NULL;
- }
-}
-
static dbus_bool_t
_dbus_connection_attach_pending_call_unlocked (DBusConnection *connection,
DBusPendingCall *pending)
message = pending->timeout_link->data;
_dbus_list_clear (&pending->timeout_link);
}
+ else
+ dbus_message_ref (message);
- _dbus_verbose (" handing message %p to pending call\n", message);
+ _dbus_verbose (" handing message %p (%s) to pending call serial %u\n",
+ message,
+ dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN ?
+ "method return" :
+ dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_ERROR ?
+ "error" : "other type",
+ pending->reply_serial);
_dbus_assert (pending->reply == NULL);
+ _dbus_assert (pending->reply_serial == dbus_message_get_reply_serial (message));
pending->reply = message;
- dbus_message_ref (pending->reply);
dbus_pending_call_ref (pending); /* in case there's no app with a ref held */
_dbus_connection_detach_pending_call_and_unlock (pending->connection, pending);
if (io_path_cond == NULL)
goto error;
- disconnect_message = dbus_message_new_signal (DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
+ disconnect_message = dbus_message_new_signal (DBUS_PATH_ORG_FREEDESKTOP_LOCAL,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
"Disconnected");
if (disconnect_message == NULL)
connection->filter_list = NULL;
connection->last_dispatch_status = DBUS_DISPATCH_COMPLETE; /* so we're notified first time there's data */
connection->objects = objects;
+ connection->exit_on_disconnect = FALSE;
_dbus_data_slot_list_init (&connection->slot_list);
* Requires that the caller already holds the connection lock.
*
* @param connection the connection.
+ * @returns the connection.
*/
-void
+DBusConnection *
_dbus_connection_ref_unlocked (DBusConnection *connection)
{
#ifdef DBUS_HAVE_ATOMIC_INT
_dbus_assert (connection->refcount.value > 0);
connection->refcount.value += 1;
#endif
+
+ return connection;
}
/**
}
/**
- * Used to notify a connection when a DBusMessageHandler is
- * destroyed, so the connection can drop any reference
- * to the handler. This is a private function, but still
- * takes the connection lock. Don't call it with the lock held.
- *
- * @todo needs to check in pending_replies too.
- *
- * @param connection the connection
- * @param handler the handler
- */
-void
-_dbus_connection_handler_destroyed_locked (DBusConnection *connection,
- DBusMessageHandler *handler)
-{
- DBusList *link;
-
- CONNECTION_LOCK (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);
-
- if (h == handler)
- _dbus_list_remove_link (&connection->filter_list,
- link);
-
- link = next;
- }
- CONNECTION_UNLOCK (connection);
-}
-
-/**
* A callback for use with dbus_watch_new() to create a DBusWatch.
*
* @todo This is basically a hack - we could delete _dbus_transport_handle_watch()
* Increments the reference count of a DBusConnection.
*
* @param connection the connection.
+ * @returns the connection.
*/
-void
+DBusConnection *
dbus_connection_ref (DBusConnection *connection)
{
- _dbus_return_if_fail (connection != NULL);
+ _dbus_return_val_if_fail (connection != NULL, NULL);
/* The connection lock is better than the global
* lock in the atomic increment fallback
connection->refcount.value += 1;
CONNECTION_UNLOCK (connection);
#endif
+
+ return connection;
}
static void
connection->timeouts = NULL;
_dbus_data_slot_list_free (&connection->slot_list);
- /* ---- Done with stuff that invokes application callbacks */
link = _dbus_list_get_first_link (&connection->filter_list);
while (link != NULL)
{
- DBusMessageHandler *h = link->data;
+ DBusMessageFilter *filter = link->data;
DBusList *next = _dbus_list_get_next_link (&connection->filter_list, link);
-
- _dbus_message_handler_remove_connection (h, connection);
+
+ filter->function = NULL;
+ _dbus_message_filter_unref (filter); /* calls app callback */
+ link->data = NULL;
link = next;
}
+ _dbus_list_clear (&connection->filter_list);
+
+ /* ---- Done with stuff that invokes application callbacks */
_dbus_object_tree_unref (connection->objects);
* function does not affect the connection's reference count. It's
* safe to disconnect a connection more than once; all calls after the
* first do nothing. It's impossible to "reconnect" a connection, a
- * new connection must be created.
+ * new connection must be created. This function may result in a call
+ * to the DBusDispatchStatusFunction set with
+ * dbus_connection_set_dispatch_status_function(), as the disconnect
+ * message it generates needs to be dispatched.
*
* @param connection the connection.
*/
void
dbus_connection_disconnect (DBusConnection *connection)
{
+ DBusDispatchStatus status;
+
_dbus_return_if_fail (connection != NULL);
CONNECTION_LOCK (connection);
_dbus_transport_disconnect (connection->transport);
- CONNECTION_UNLOCK (connection);
+
+ status = _dbus_connection_get_dispatch_status_unlocked (connection);
+
+ /* this calls out to user code */
+ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
}
static dbus_bool_t
return res;
}
-struct DBusPreallocatedSend
+/**
+ * Set whether _exit() should be called when the connection receives a
+ * disconnect signal. The call to _exit() comes after any handlers for
+ * the disconnect signal run; handlers can cancel the exit by calling
+ * this function.
+ *
+ * By default, exit_on_disconnect is #FALSE; but for message bus
+ * connections returned from dbus_bus_get() it will be toggled on
+ * by default.
+ *
+ * @param connection the connection
+ * @param exit_on_disconnect #TRUE if _exit() should be called after a disconnect signal
+ */
+void
+dbus_connection_set_exit_on_disconnect (DBusConnection *connection,
+ dbus_bool_t exit_on_disconnect)
{
- DBusConnection *connection;
- DBusList *queue_link;
- DBusList *counter_link;
-};
+ _dbus_return_if_fail (connection != NULL);
+
+ CONNECTION_LOCK (connection);
+ connection->exit_on_disconnect = exit_on_disconnect != FALSE;
+ CONNECTION_UNLOCK (connection);
+}
static DBusPreallocatedSend*
_dbus_connection_preallocate_send_unlocked (DBusConnection *connection)
connection->n_outgoing += 1;
- _dbus_verbose ("Message %p (%s) added to outgoing queue %p, %d pending to send\n",
+ _dbus_verbose ("Message %p (%d %s '%s') added to outgoing queue %p, %d pending to send\n",
message,
+ dbus_message_get_type (message),
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) :
"no interface",
+ dbus_message_get_signature (message),
connection,
connection->n_outgoing);
reply = dbus_message_new_error (message, DBUS_ERROR_NO_REPLY,
"No reply within specified time");
- if (!reply)
- {
- CONNECTION_UNLOCK (connection);
- dbus_pending_call_unref (pending);
- return FALSE;
- }
+ if (reply == NULL)
+ goto error;
reply_link = _dbus_list_alloc_link (reply);
- if (!reply)
+ if (reply_link == NULL)
{
CONNECTION_UNLOCK (connection);
dbus_message_unref (reply);
- dbus_pending_call_unref (pending);
- return FALSE;
+ goto error_unlocked;
}
pending->timeout_link = reply_link;
* Also, add the timeout.
*/
if (!_dbus_connection_attach_pending_call_unlocked (connection,
- pending))
- {
- CONNECTION_UNLOCK (connection);
- dbus_pending_call_unref (pending);
- return FALSE;
- }
+ pending))
+ goto error;
if (!_dbus_connection_send_unlocked (connection, message, NULL))
{
_dbus_connection_detach_pending_call_and_unlock (connection,
- pending);
- return FALSE;
+ pending);
+ goto error_unlocked;
}
if (pending_return)
- {
- dbus_pending_call_ref (pending);
- *pending_return = pending;
- }
+ *pending_return = pending;
+ else
+ dbus_pending_call_unref (pending);
CONNECTION_UNLOCK (connection);
return TRUE;
+
+ error:
+ CONNECTION_UNLOCK (connection);
+ error_unlocked:
+ dbus_pending_call_unref (pending);
+ return FALSE;
}
static DBusMessage*
{
_dbus_list_remove_link (&connection->incoming_messages, link);
connection->n_incoming -= 1;
- dbus_message_ref (reply);
return reply;
}
link = _dbus_list_get_next_link (&connection->incoming_messages, link);
* the whole message queue for example) and has thread issues,
* see comments in source
*
+ * Does not re-enter the main loop or run filter/path-registered
+ * callbacks. The reply to the message will not be seen by
+ * filter callbacks.
+ *
* @param connection the connection
* @param client_serial the reply serial to wait for
* @param timeout_milliseconds timeout in milliseconds or -1 for default
_dbus_get_current_time (&tv_sec, &tv_usec);
- if (tv_sec < start_tv_sec)
+ if (!_dbus_connection_get_is_connected_unlocked (connection))
+ return NULL;
+ else if (tv_sec < start_tv_sec)
_dbus_verbose ("dbus_connection_send_with_reply_and_block(): clock set backward\n");
else if (connection->disconnect_message_link == NULL)
_dbus_verbose ("dbus_connection_send_with_reply_and_block(): disconnected\n");
link = _dbus_list_pop_first_link (&connection->incoming_messages);
connection->n_incoming -= 1;
- _dbus_verbose ("Message %p (%s) removed from incoming queue %p, %d incoming\n",
+ _dbus_verbose ("Message %p (%d %s '%s') removed from incoming queue %p, %d incoming\n",
link->data,
+ dbus_message_get_type (link->data),
dbus_message_get_interface (link->data) ?
dbus_message_get_interface (link->data) :
"no interface",
+ dbus_message_get_signature (link->data),
connection, connection->n_incoming);
return link;
message_link);
connection->n_incoming += 1;
- _dbus_verbose ("Message %p (%s) put back into queue %p, %d incoming\n",
+ _dbus_verbose ("Message %p (%d %s '%s') put back into queue %p, %d incoming\n",
message_link->data,
+ dbus_message_get_type (message_link->data),
dbus_message_get_interface (message_link->data) ?
dbus_message_get_interface (message_link->data) :
"no interface",
+ dbus_message_get_signature (message_link->data),
connection, connection->n_incoming);
}
status = _dbus_transport_get_dispatch_status (connection->transport);
+ if (status == DBUS_DISPATCH_COMPLETE &&
+ connection->disconnect_message_link &&
+ !_dbus_transport_get_is_connected (connection->transport))
+ {
+ /* We haven't sent the disconnect message already,
+ * and all real messages have been queued up.
+ */
+ _dbus_connection_queue_synthesized_message_link (connection,
+ connection->disconnect_message_link);
+ connection->disconnect_message_link = NULL;
+ }
+
if (status != DBUS_DISPATCH_COMPLETE)
return status;
else if (connection->n_incoming > 0)
* @todo some FIXME in here about handling DBUS_HANDLER_RESULT_NEED_MEMORY
*
* @todo right now a message filter gets run on replies to a pending
- * call in here, but not in the case where we block without
- * entering the main loop.
+ * call in here, but not in the case where we block without entering
+ * the main loop. Simple solution might be to just have the pending
+ * call stuff run before the filters.
+ *
+ * @todo FIXME what if we call out to application code to handle a
+ * message, holding the dispatch lock, and the application code runs
+ * the main loop and dispatches again? Probably deadlocks at the
+ * moment. Maybe we want a dispatch status of DBUS_DISPATCH_IN_PROGRESS,
+ * and then the GSource etc. could handle the situation?
*
* @param connection the connection
* @returns dispatch status
}
_dbus_list_foreach (&filter_list_copy,
- (DBusForeachFunction)dbus_message_handler_ref,
+ (DBusForeachFunction)_dbus_message_filter_ref,
NULL);
/* We're still protected from dispatch() reentrancy here
link = _dbus_list_get_first_link (&filter_list_copy);
while (link != NULL)
{
- DBusMessageHandler *handler = link->data;
+ DBusMessageFilter *filter = link->data;
DBusList *next = _dbus_list_get_next_link (&filter_list_copy, link);
_dbus_verbose (" running filter on message %p\n", message);
- result = _dbus_message_handler_handle_message (handler, connection,
- message);
+ result = (* filter->function) (connection, message, filter->user_data);
if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
break;
}
_dbus_list_foreach (&filter_list_copy,
- (DBusForeachFunction)dbus_message_handler_unref,
+ (DBusForeachFunction)_dbus_message_filter_unref,
NULL);
_dbus_list_clear (&filter_list_copy);
/* We're still protected from dispatch() reentrancy here
* since we acquired the dispatcher
*/
- _dbus_verbose (" running object handler on message %p (%s)\n",
+ _dbus_verbose (" running object path dispatch on message %p (%d %s '%s')\n",
message,
+ dbus_message_get_type (message),
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) :
- "no interface");
+ "no interface",
+ dbus_message_get_signature (message));
result = _dbus_object_tree_dispatch_and_unlock (connection->objects,
message);
result = DBUS_HANDLER_RESULT_HANDLED;
}
- _dbus_verbose (" done dispatching %p (%s) on connection %p\n", message,
+ _dbus_verbose (" done dispatching %p (%d %s '%s') on connection %p\n", message,
+ dbus_message_get_type (message),
dbus_message_get_interface (message) ?
dbus_message_get_interface (message) :
"no interface",
+ dbus_message_get_signature (message),
connection);
out:
if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
{
+ _dbus_verbose ("out of memory in %s\n", _DBUS_FUNCTION_NAME);
+
/* Put message back, and we'll start over.
* Yes this means handlers must be idempotent if they
* don't return HANDLED; c'est la vie.
}
else
{
+ _dbus_verbose ("Done with message in %s\n", _DBUS_FUNCTION_NAME);
+
+ if (connection->exit_on_disconnect &&
+ dbus_message_is_signal (message,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
+ "Disconnected"))
+ {
+ _dbus_verbose ("Exiting on Disconnected signal\n");
+ CONNECTION_UNLOCK (connection);
+ _dbus_exit (1);
+ _dbus_assert_not_reached ("Call to exit() returned");
+ }
+
_dbus_list_free_link (message_link);
dbus_message_unref (message); /* don't want the message to count in max message limits
* in computing dispatch status below
}
/**
+ * Get the UNIX file descriptor of the connection, if any. This can
+ * be used for SELinux access control checks with getpeercon() for
+ * example. DO NOT read or write to the file descriptor, or try to
+ * select() on it; use DBusWatch for main loop integration. Not all
+ * connections will have a file descriptor. So for adding descriptors
+ * to the main loop, use dbus_watch_get_fd() and so forth.
+ *
+ * @param connection the connection
+ * @param fd return location for the file descriptor.
+ * @returns #TRUE if fd is successfully obtained.
+ */
+dbus_bool_t
+dbus_connection_get_unix_fd (DBusConnection *connection,
+ int *fd)
+{
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (connection->transport != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ retval = _dbus_transport_get_unix_fd (connection->transport,
+ fd);
+
+ CONNECTION_UNLOCK (connection);
+
+ return retval;
+}
+
+/**
* Gets the UNIX user ID of the connection if any.
* Returns #TRUE if the uid is filled in.
* Always returns #FALSE on non-UNIX platforms.
}
/**
+ * Gets the process ID of the connection if any.
+ * Returns #TRUE if the uid is filled in.
+ * Always returns #FALSE prior to authenticating the
+ * connection.
+ *
+ * @param connection the connection
+ * @param pid return location for the process ID
+ * @returns #TRUE if uid is filled in with a valid process ID
+ */
+dbus_bool_t
+dbus_connection_get_unix_process_id (DBusConnection *connection,
+ unsigned long *pid)
+{
+ dbus_bool_t result;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (pid != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ if (!_dbus_transport_get_is_authenticated (connection->transport))
+ result = FALSE;
+ else
+ result = _dbus_transport_get_unix_process_id (connection->transport,
+ pid);
+ CONNECTION_UNLOCK (connection);
+
+ return result;
+}
+
+/**
* Sets a predicate function used to determine whether a given user ID
* is allowed to connect. When an incoming connection has
* authenticated with a particular user ID, this function is called;
}
/**
- * Adds a message filter. Filters are handlers that are run on
- * all incoming messages, prior to the objects
- * registered with dbus_connection_register_object().
- * 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.
- * Filters added during a filter callback won't be run on the
- * message being processed.
- *
- * The connection does NOT add a reference to the message handler;
- * instead, if the message handler is finalized, the connection simply
- * forgets about it. Thus the caller of this function must keep a
- * reference to the message handler.
+ * Adds a message filter. Filters are handlers that are run on all
+ * incoming messages, prior to the objects registered with
+ * dbus_connection_register_object_path(). 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.
+ * Filters added during a filter callback won't be run on the message
+ * being processed.
*
* @todo we don't run filters on messages while blocking without
* entering the main loop, since filters are run as part of
- * dbus_connection_dispatch().
+ * dbus_connection_dispatch(). This is probably a feature, as filters
+ * could create arbitrary reentrancy. But kind of sucks if you're
+ * trying to filter METHOD_RETURN for some reason.
*
* @param connection the connection
- * @param handler the handler
+ * @param function function to handle messages
+ * @param user_data user data to pass to the function
+ * @param free_data_function function to use for freeing user data
* @returns #TRUE on success, #FALSE if not enough memory.
*/
dbus_bool_t
-dbus_connection_add_filter (DBusConnection *connection,
- DBusMessageHandler *handler)
+dbus_connection_add_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data,
+ DBusFreeFunction free_data_function)
{
+ DBusMessageFilter *filter;
+
_dbus_return_val_if_fail (connection != NULL, FALSE);
- _dbus_return_val_if_fail (handler != NULL, FALSE);
+ _dbus_return_val_if_fail (function != NULL, FALSE);
+
+ filter = dbus_new0 (DBusMessageFilter, 1);
+ if (filter == NULL)
+ return FALSE;
+ filter->refcount.value = 1;
+
CONNECTION_LOCK (connection);
- if (!_dbus_message_handler_add_connection (handler, connection))
- {
- CONNECTION_UNLOCK (connection);
- return FALSE;
- }
if (!_dbus_list_append (&connection->filter_list,
- handler))
+ filter))
{
- _dbus_message_handler_remove_connection (handler, connection);
+ _dbus_message_filter_unref (filter);
CONNECTION_UNLOCK (connection);
return FALSE;
}
+ /* Fill in filter after all memory allocated,
+ * so we don't run the free_user_data_function
+ * if the add_filter() fails
+ */
+
+ filter->function = function;
+ filter->user_data = user_data;
+ filter->free_user_data_function = free_data_function;
+
CONNECTION_UNLOCK (connection);
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).
+ * 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
+ * @param function the handler to remove
+ * @param user_data user data for the handler to remove
*
*/
void
-dbus_connection_remove_filter (DBusConnection *connection,
- DBusMessageHandler *handler)
+dbus_connection_remove_filter (DBusConnection *connection,
+ DBusHandleMessageFunction function,
+ void *user_data)
{
+ DBusList *link;
+ DBusMessageFilter *filter;
+
_dbus_return_if_fail (connection != NULL);
- _dbus_return_if_fail (handler != NULL);
+ _dbus_return_if_fail (function != NULL);
CONNECTION_LOCK (connection);
- if (!_dbus_list_remove_last (&connection->filter_list, handler))
+
+ filter = NULL;
+
+ link = _dbus_list_get_last_link (&connection->filter_list);
+ while (link != NULL)
{
- _dbus_warn ("Tried to remove a DBusConnection filter that had not been added\n");
- CONNECTION_UNLOCK (connection);
- return;
+ filter = link->data;
+
+ if (filter->function == function &&
+ filter->user_data == user_data)
+ {
+ _dbus_list_remove_link (&connection->filter_list, link);
+ filter->function = NULL;
+
+ break;
+ }
+
+ link = _dbus_list_get_prev_link (&connection->filter_list, link);
}
+
+ CONNECTION_UNLOCK (connection);
- _dbus_message_handler_remove_connection (handler, connection);
+#ifndef DBUS_DISABLE_CHECKS
+ if (filter == NULL)
+ {
+ _dbus_warn ("Attempt to remove filter function %p user data %p, but no such filter has been added\n",
+ function, user_data);
+ return;
+ }
+#endif
+
+ /* Call application code */
+ if (filter->free_user_data_function)
+ (* filter->free_user_data_function) (filter->user_data);
- CONNECTION_UNLOCK (connection);
+ filter->free_user_data_function = NULL;
+ filter->user_data = NULL;
+
+ _dbus_message_filter_unref (filter);
}
/**
- * Registers a handler for a given subsection of the object hierarchy.
- * The given vtable handles messages at or below the given path.
+ * Registers a handler for a given path in the object hierarchy.
+ * The given vtable handles messages sent to exactly the given path.
*
*
* @param connection the connection
- * @param path #NULL-terminated array of path elements
+ * @param path a '/' delimited string of path elements
* @param vtable the virtual table
* @param user_data data to pass to functions in the vtable
* @returns #FALSE if not enough memory
*/
dbus_bool_t
dbus_connection_register_object_path (DBusConnection *connection,
- const char **path,
+ const char *path,
const DBusObjectPathVTable *vtable,
void *user_data)
{
+ char **decomposed_path;
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
+ _dbus_return_val_if_fail (vtable != NULL, FALSE);
+
+ if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+ return FALSE;
+
+ CONNECTION_LOCK (connection);
+
+ retval = _dbus_object_tree_register (connection->objects,
+ FALSE,
+ (const char **) decomposed_path, vtable,
+ user_data);
+
+ CONNECTION_UNLOCK (connection);
+
+ dbus_free_string_array (decomposed_path);
+
+ return retval;
+}
+
+/**
+ * Registers a fallback handler for a given subsection of the object
+ * hierarchy. The given vtable handles messages at or below the given
+ * path. You can use this to establish a default message handling
+ * policy for a whole "subdirectory."
+ *
+ * @param connection the connection
+ * @param path a '/' delimited string of path elements
+ * @param vtable the virtual table
+ * @param user_data data to pass to functions in the vtable
+ * @returns #FALSE if not enough memory
+ */
+dbus_bool_t
+dbus_connection_register_fallback (DBusConnection *connection,
+ const char *path,
+ const DBusObjectPathVTable *vtable,
+ void *user_data)
+{
+ char **decomposed_path;
dbus_bool_t retval;
_dbus_return_val_if_fail (connection != NULL, FALSE);
_dbus_return_val_if_fail (path != NULL, FALSE);
- _dbus_return_val_if_fail (path[0] != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
_dbus_return_val_if_fail (vtable != NULL, FALSE);
+ if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+ return FALSE;
+
CONNECTION_LOCK (connection);
- retval = _dbus_object_tree_register (connection->objects, path, vtable,
+ retval = _dbus_object_tree_register (connection->objects,
+ TRUE,
+ (const char **) decomposed_path, vtable,
user_data);
CONNECTION_UNLOCK (connection);
+ dbus_free_string_array (decomposed_path);
+
return retval;
}
/**
* Unregisters the handler registered with exactly the given path.
* It's a bug to call this function for a path that isn't registered.
+ * Can unregister both fallback paths and object paths.
*
* @param connection the connection
- * @param path the #NULL-terminated array of path elements
+ * @param path a '/' delimited string of path elements
+ * @returns #FALSE if not enough memory
*/
-void
+dbus_bool_t
dbus_connection_unregister_object_path (DBusConnection *connection,
- const char **path)
+ const char *path)
{
- _dbus_return_if_fail (connection != NULL);
- _dbus_return_if_fail (path != NULL);
- _dbus_return_if_fail (path[0] != NULL);
+ char **decomposed_path;
+
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (path != NULL, FALSE);
+ _dbus_return_val_if_fail (path[0] == '/', FALSE);
+
+ if (!_dbus_decompose_path (path, strlen (path), &decomposed_path, NULL))
+ return FALSE;
CONNECTION_LOCK (connection);
- return _dbus_object_tree_unregister_and_unlock (connection->objects,
- path);
+ _dbus_object_tree_unregister_and_unlock (connection->objects, (const char **) decomposed_path);
+
+ dbus_free_string_array (decomposed_path);
+
+ return TRUE;
+}
+
+/**
+ * Lists the registered fallback handlers and object path handlers at
+ * the given parent_path. The returned array should be freed with
+ * dbus_free_string_array().
+ *
+ * @param connection the connection
+ * @param parent_path the path to list the child handlers of
+ * @param child_entries returns #NULL-terminated array of children
+ * @returns #FALSE if no memory to allocate the child entries
+ */
+dbus_bool_t
+dbus_connection_list_registered (DBusConnection *connection,
+ const char *parent_path,
+ char ***child_entries)
+{
+ char **decomposed_path;
+ dbus_bool_t retval;
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (parent_path != NULL, FALSE);
+ _dbus_return_val_if_fail (parent_path[0] == '/', FALSE);
+ _dbus_return_val_if_fail (child_entries != NULL, FALSE);
+
+ if (!_dbus_decompose_path (parent_path, strlen (parent_path), &decomposed_path, NULL))
+ return FALSE;
+
+ CONNECTION_LOCK (connection);
+
+ retval = _dbus_object_tree_list_registered_and_unlock (connection->objects,
+ (const char **) decomposed_path,
+ child_entries);
+ dbus_free_string_array (decomposed_path);
+
+ return retval;
}
static DBusDataSlotAllocator slot_allocator;