summaryrefslogtreecommitdiffstats
path: root/dbus
diff options
context:
space:
mode:
Diffstat (limited to 'dbus')
-rw-r--r--dbus/dbus-connection-internal.h5
-rw-r--r--dbus/dbus-connection.c28
-rw-r--r--dbus/dbus-connection.h3
-rw-r--r--dbus/dbus-internals.h3
-rw-r--r--dbus/dbus-object-tree.c164
-rw-r--r--dbus/dbus-object-tree.h4
-rw-r--r--dbus/dbus-pending-call.c171
-rw-r--r--dbus/dbus-pending-call.h11
-rw-r--r--dbus/dbus-threads.c1
9 files changed, 352 insertions, 38 deletions
diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
index 5a04dece..b19ab636 100644
--- a/dbus/dbus-connection-internal.h
+++ b/dbus/dbus-connection-internal.h
@@ -30,6 +30,7 @@
#include <dbus/dbus-resources.h>
#include <dbus/dbus-list.h>
#include <dbus/dbus-timeout.h>
+#include <dbus/dbus-dataslot.h>
DBUS_BEGIN_DECLS;
@@ -104,9 +105,9 @@ struct DBusPendingCall
{
DBusAtomic refcount; /**< reference count */
+ DBusDataSlotList slot_list; /**< Data stored by allocated integer ID */
+
DBusPendingCallNotifyFunction function; /**< Notifier when reply arrives. */
- void *user_data; /**< user data for function */
- DBusFreeFunction free_user_data; /**< free the user data */
DBusConnection *connection; /**< Connections we're associated with */
DBusMessage *reply; /**< Reply (after we've received it) */
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
index bab6ffd8..b55f270c 100644
--- a/dbus/dbus-connection.c
+++ b/dbus/dbus-connection.c
@@ -3101,7 +3101,6 @@ dbus_connection_register_object_path (DBusConnection *connection,
* path. You can use this to establish a default message handling
* policy for a whole "subdirectory."
*
- *
* @param connection the connection
* @param path #NULL-terminated array of path elements
* @param vtable the virtual table
@@ -3136,6 +3135,7 @@ dbus_connection_register_fallback (DBusConnection *connection,
/**
* 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
@@ -3154,6 +3154,32 @@ dbus_connection_unregister_object_path (DBusConnection *connection,
path);
}
+/**
+ * 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)
+{
+ _dbus_return_val_if_fail (connection != NULL, FALSE);
+ _dbus_return_val_if_fail (parent_path != NULL, FALSE);
+ _dbus_return_val_if_fail (child_entries != NULL, FALSE);
+
+ CONNECTION_LOCK (connection);
+
+ return _dbus_object_tree_list_registered_and_unlock (connection->objects,
+ parent_path,
+ child_entries);
+}
+
static DBusDataSlotAllocator slot_allocator;
_DBUS_DEFINE_GLOBAL_LOCK (connection_slots);
diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h
index abc88056..a4212c74 100644
--- a/dbus/dbus-connection.h
+++ b/dbus/dbus-connection.h
@@ -240,6 +240,9 @@ dbus_bool_t dbus_connection_register_fallback (DBusConnection
void dbus_connection_unregister_object_path (DBusConnection *connection,
const char **path);
+dbus_bool_t dbus_connection_list_registered (DBusConnection *connection,
+ const char **parent_path,
+ char ***child_entries);
DBUS_END_DECLS;
diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h
index 06a011e3..b3010355 100644
--- a/dbus/dbus-internals.h
+++ b/dbus/dbus-internals.h
@@ -230,13 +230,14 @@ extern int _dbus_current_generation;
_DBUS_DECLARE_GLOBAL_LOCK (list);
_DBUS_DECLARE_GLOBAL_LOCK (connection_slots);
+_DBUS_DECLARE_GLOBAL_LOCK (pending_call_slots);
_DBUS_DECLARE_GLOBAL_LOCK (server_slots);
_DBUS_DECLARE_GLOBAL_LOCK (message_slots);
_DBUS_DECLARE_GLOBAL_LOCK (atomic);
_DBUS_DECLARE_GLOBAL_LOCK (bus);
_DBUS_DECLARE_GLOBAL_LOCK (shutdown_funcs);
_DBUS_DECLARE_GLOBAL_LOCK (system_users);
-#define _DBUS_N_GLOBAL_LOCKS (8)
+#define _DBUS_N_GLOBAL_LOCKS (9)
dbus_bool_t _dbus_threads_init_debug (void);
diff --git a/dbus/dbus-object-tree.c b/dbus/dbus-object-tree.c
index 9922dec4..07d3ae59 100644
--- a/dbus/dbus-object-tree.c
+++ b/dbus/dbus-object-tree.c
@@ -25,6 +25,7 @@
#include "dbus-internals.h"
#include "dbus-hash.h"
#include "dbus-protocol.h"
+#include "dbus-string.h"
#include <string.h>
#include <stdlib.h>
@@ -34,13 +35,7 @@
* @brief DBusObjectTree is used by DBusConnection to track the object tree
*
* Types and functions related to DBusObjectTree. These
- * are all internal.
- *
- * @todo this is totally broken, because of the following case:
- * /foo, /foo/bar, /foo/baz
- * if we then receive a message to /foo/baz we need to hand it
- * to /foo/baz and /foo but not /foo/bar. So we should be
- * using a real tree structure as with GConfListeners.
+ * are all library-internal.
*
* @{
*/
@@ -542,6 +537,101 @@ _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
tree->root = NULL;
}
+static dbus_bool_t
+_dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
+ const char **parent_path,
+ char ***child_entries)
+{
+ DBusObjectSubtree *subtree;
+ char **retval;
+
+ _dbus_assert (parent_path != NULL);
+ _dbus_assert (child_entries != NULL);
+
+ *child_entries = NULL;
+
+ subtree = find_subtree (tree, parent_path, NULL);
+ if (subtree == NULL)
+ {
+ retval = dbus_new0 (char *, 1);
+ if (retval == NULL)
+ goto out;
+ }
+ else
+ {
+ int i;
+ retval = dbus_new0 (char*, subtree->n_subtrees + 1);
+ if (retval == NULL)
+ goto out;
+ i = 0;
+ while (i < subtree->n_subtrees)
+ {
+ retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
+ if (retval[i] == NULL)
+ {
+ dbus_free_string_array (retval);
+ retval = NULL;
+ goto out;
+ }
+ ++i;
+ }
+ }
+
+ out:
+
+ *child_entries = retval;
+ return retval != NULL;
+}
+
+static DBusHandlerResult
+handle_default_introspect_unlocked (DBusObjectTree *tree,
+ DBusMessage *message,
+ const char **path)
+{
+ DBusString xml;
+ DBusHandlerResult result;
+ char **children;
+ int i;
+
+ if (!dbus_message_is_method_call (message,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
+ "Introspect"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!_dbus_string_init (&xml))
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ result = DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+ children = NULL;
+ if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
+ goto out;
+
+ if (!_dbus_string_append (&xml, "<node>\n"))
+ goto out;
+
+ i = 0;
+ while (children[i] != NULL)
+ {
+ if (!_dbus_string_append_printf (&xml, " <node name=\"%s\"/>\n",
+ children[i]))
+ goto out;
+
+ ++i;
+ }
+
+ if (!_dbus_string_append (&xml, "</node>\n"))
+ goto out;
+
+ result = DBUS_HANDLER_RESULT_HANDLED;
+
+ out:
+ _dbus_string_free (&xml);
+ dbus_free_string_array (children);
+
+ return result;
+}
+
/**
* Tries to dispatch a message by directing it to handler for the
* object path listed in the message header, if any. Messages are
@@ -572,12 +662,23 @@ _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
path = NULL;
if (!dbus_message_get_path_decomposed (message, &path))
{
+#ifdef DBUS_BUILD_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_unlock (tree->connection);
+
_dbus_verbose ("No memory to get decomposed path\n");
+
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
if (path == NULL)
{
+#ifdef DBUS_BUILD_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_unlock (tree->connection);
+
_dbus_verbose ("No path field in message\n");
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -649,30 +750,40 @@ _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
message,
user_data);
- if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
- goto free_and_return;
-
#ifdef DBUS_BUILD_TESTS
if (tree->connection)
#endif
_dbus_connection_lock (tree->connection);
+
+ if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ goto free_and_return;
}
link = next;
}
+ free_and_return:
+
+ if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ {
+ /* This hardcoded default handler does a minimal Introspect()
+ */
+ result = handle_default_introspect_unlocked (tree, message,
+ (const char**) path);
+ }
+
#ifdef DBUS_BUILD_TESTS
if (tree->connection)
#endif
_dbus_connection_unlock (tree->connection);
-
- free_and_return:
+
while (list != NULL)
{
link = _dbus_list_get_first_link (&list);
_dbus_object_subtree_unref (link->data);
_dbus_list_remove_link (&list, link);
}
+
dbus_free_string_array (path);
return result;
@@ -770,6 +881,35 @@ _dbus_object_subtree_unref (DBusObjectSubtree *subtree)
}
}
+/**
+ * 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_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
+ const char **parent_path,
+ char ***child_entries)
+{
+ dbus_bool_t result;
+
+ result = _dbus_object_tree_list_registered_unlocked (tree,
+ parent_path,
+ child_entries);
+
+#ifdef DBUS_BUILD_TESTS
+ if (tree->connection)
+#endif
+ _dbus_connection_unlock (tree->connection);
+
+ return result;
+}
+
/** @} */
#ifdef DBUS_BUILD_TESTS
diff --git a/dbus/dbus-object-tree.h b/dbus/dbus-object-tree.h
index 21d8b5f1..bf34d972 100644
--- a/dbus/dbus-object-tree.h
+++ b/dbus/dbus-object-tree.h
@@ -44,7 +44,9 @@ DBusHandlerResult _dbus_object_tree_dispatch_and_unlock (DBusObjectTree
DBusMessage *message);
void _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree);
-
+dbus_bool_t _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
+ const char **parent_path,
+ char ***child_entries);
DBUS_END_DECLS;
#endif /* DBUS_OBJECT_TREE_H */
diff --git a/dbus/dbus-pending-call.c b/dbus/dbus-pending-call.c
index 2b6021e9..dad444e3 100644
--- a/dbus/dbus-pending-call.c
+++ b/dbus/dbus-pending-call.c
@@ -38,6 +38,8 @@
* @{
*/
+static dbus_int32_t notify_user_data_slot = -1;
+
/**
* Creates a new pending reply object.
*
@@ -58,11 +60,17 @@ _dbus_pending_call_new (DBusConnection *connection,
if (timeout_milliseconds == -1)
timeout_milliseconds = _DBUS_DEFAULT_TIMEOUT_VALUE;
+
+ if (!dbus_pending_call_allocate_data_slot (&notify_user_data_slot))
+ return NULL;
pending = dbus_new (DBusPendingCall, 1);
if (pending == NULL)
- return NULL;
+ {
+ dbus_pending_call_free_data_slot (&notify_user_data_slot);
+ return NULL;
+ }
timeout = _dbus_timeout_new (timeout_milliseconds,
timeout_handler,
@@ -70,6 +78,7 @@ _dbus_pending_call_new (DBusConnection *connection,
if (timeout == NULL)
{
+ dbus_pending_call_free_data_slot (&notify_user_data_slot);
dbus_free (pending);
return NULL;
}
@@ -77,6 +86,8 @@ _dbus_pending_call_new (DBusConnection *connection,
pending->refcount.value = 1;
pending->connection = connection;
pending->timeout = timeout;
+
+ _dbus_data_slot_list_init (&pending->slot_list);
return pending;
}
@@ -94,7 +105,13 @@ _dbus_pending_call_notify (DBusPendingCall *pending)
pending->completed = TRUE;
if (pending->function)
- (* pending->function) (pending, pending->user_data);
+ {
+ void *user_data;
+ user_data = dbus_pending_call_get_data (pending,
+ notify_user_data_slot);
+
+ (* pending->function) (pending, user_data);
+ }
}
/** @} */
@@ -154,9 +171,8 @@ dbus_pending_call_unref (DBusPendingCall *pending)
_dbus_assert (!pending->timeout_added);
/* this assumes we aren't holding connection lock... */
- if (pending->free_user_data)
- (* pending->free_user_data) (pending->user_data);
-
+ _dbus_data_slot_list_free (&pending->slot_list);
+
if (pending->timeout != NULL)
_dbus_timeout_unref (pending->timeout);
@@ -174,6 +190,8 @@ dbus_pending_call_unref (DBusPendingCall *pending)
}
dbus_free (pending);
+
+ dbus_pending_call_free_data_slot (&notify_user_data_slot);
}
}
@@ -185,28 +203,24 @@ dbus_pending_call_unref (DBusPendingCall *pending)
* @param function notifier function
* @param user_data data to pass to notifier function
* @param free_user_data function to free the user data
- *
+ * @returns #FALSE if not enough memory
*/
-void
+dbus_bool_t
dbus_pending_call_set_notify (DBusPendingCall *pending,
DBusPendingCallNotifyFunction function,
void *user_data,
DBusFreeFunction free_user_data)
{
- DBusFreeFunction old_free_func;
- void *old_user_data;
-
- _dbus_return_if_fail (pending != NULL);
+ _dbus_return_val_if_fail (pending != NULL, FALSE);
- old_free_func = pending->free_user_data;
- old_user_data = pending->user_data;
-
- pending->user_data = user_data;
- pending->free_user_data = free_user_data;
+ /* could invoke application code! */
+ if (!dbus_pending_call_set_data (pending, notify_user_data_slot,
+ user_data, free_user_data))
+ return FALSE;
+
pending->function = function;
- if (old_free_func)
- (* old_free_func) (old_user_data);
+ return TRUE;
}
/**
@@ -230,9 +244,10 @@ dbus_pending_call_cancel (DBusPendingCall *pending)
* Checks whether the pending call has received a reply
* yet, or not.
*
+ * @todo not thread safe? I guess it has to lock though it sucks
+ *
* @param pending the pending call
- * @returns #TRUE if a reply has been received
- */
+ * @returns #TRUE if a reply has been received */
dbus_bool_t
dbus_pending_call_get_completed (DBusPendingCall *pending)
{
@@ -245,6 +260,9 @@ dbus_pending_call_get_completed (DBusPendingCall *pending)
* have to keep a reference count on the pending call (or add one
* to the message).
*
+ * @todo not thread safe? I guess it has to lock though it sucks
+ * @todo maybe to make this threadsafe, it should be steal_reply(), i.e. only one thread can ever get the message
+ *
* @param pending the pending call
* @returns the reply message or #NULL.
*/
@@ -260,6 +278,9 @@ dbus_pending_call_get_reply (DBusPendingCall *pending)
* main loop or process other messages, it simply waits for the reply
* in question.
*
+ * If the pending call is already completed, this function returns
+ * immediately.
+ *
* @todo when you start blocking, the timeout is reset, but it should
* really only use time remaining since the pending call was created.
*
@@ -269,6 +290,9 @@ void
dbus_pending_call_block (DBusPendingCall *pending)
{
DBusMessage *message;
+
+ if (dbus_pending_call_get_completed (pending))
+ return;
message = _dbus_connection_block_for_reply (pending->connection,
pending->reply_serial,
@@ -279,6 +303,113 @@ dbus_pending_call_block (DBusPendingCall *pending)
dbus_message_unref (message);
}
+static DBusDataSlotAllocator slot_allocator;
+_DBUS_DEFINE_GLOBAL_LOCK (pending_call_slots);
+
+/**
+ * Allocates an integer ID to be used for storing application-specific
+ * data on any DBusPendingCall. The allocated ID may then be used
+ * with dbus_pending_call_set_data() and dbus_pending_call_get_data().
+ * The passed-in slot must be initialized to -1, and is filled in
+ * with the slot ID. If the passed-in slot is not -1, it's assumed
+ * to be already allocated, and its refcount is incremented.
+ *
+ * The allocated slot is global, i.e. all DBusPendingCall objects will
+ * have a slot with the given integer ID reserved.
+ *
+ * @param slot_p address of a global variable storing the slot
+ * @returns #FALSE on failure (no memory)
+ */
+dbus_bool_t
+dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p)
+{
+ return _dbus_data_slot_allocator_alloc (&slot_allocator,
+ _DBUS_LOCK_NAME (pending_call_slots),
+ slot_p);
+}
+
+/**
+ * Deallocates a global ID for #DBusPendingCall data slots.
+ * dbus_pending_call_get_data() and dbus_pending_call_set_data() may
+ * no longer be used with this slot. Existing data stored on existing
+ * DBusPendingCall objects will be freed when the #DBusPendingCall is
+ * finalized, but may not be retrieved (and may only be replaced if
+ * someone else reallocates the slot). When the refcount on the
+ * passed-in slot reaches 0, it is set to -1.
+ *
+ * @param slot_p address storing the slot to deallocate
+ */
+void
+dbus_pending_call_free_data_slot (dbus_int32_t *slot_p)
+{
+ _dbus_return_if_fail (*slot_p >= 0);
+
+ _dbus_data_slot_allocator_free (&slot_allocator, slot_p);
+}
+
+/**
+ * Stores a pointer on a #DBusPendingCall, along
+ * with an optional function to be used for freeing
+ * the data when the data is set again, or when
+ * the pending call is finalized. The slot number
+ * must have been allocated with dbus_pending_call_allocate_data_slot().
+ *
+ * @param pending the pending_call
+ * @param slot the slot number
+ * @param data the data to store
+ * @param free_data_func finalizer function for the data
+ * @returns #TRUE if there was enough memory to store the data
+ */
+dbus_bool_t
+dbus_pending_call_set_data (DBusPendingCall *pending,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func)
+{
+ DBusFreeFunction old_free_func;
+ void *old_data;
+ dbus_bool_t retval;
+
+ _dbus_return_val_if_fail (pending != NULL, FALSE);
+ _dbus_return_val_if_fail (slot >= 0, FALSE);
+
+ retval = _dbus_data_slot_list_set (&slot_allocator,
+ &pending->slot_list,
+ slot, data, free_data_func,
+ &old_free_func, &old_data);
+
+ if (retval)
+ {
+ if (old_free_func)
+ (* old_free_func) (old_data);
+ }
+
+ return retval;
+}
+
+/**
+ * Retrieves data previously set with dbus_pending_call_set_data().
+ * The slot must still be allocated (must not have been freed).
+ *
+ * @param pending the pending_call
+ * @param slot the slot to get data from
+ * @returns the data, or #NULL if not found
+ */
+void*
+dbus_pending_call_get_data (DBusPendingCall *pending,
+ dbus_int32_t slot)
+{
+ void *res;
+
+ _dbus_return_val_if_fail (pending != NULL, NULL);
+
+ res = _dbus_data_slot_list_get (&slot_allocator,
+ &pending->slot_list,
+ slot);
+
+ return res;
+}
+
/** @} */
#ifdef DBUS_BUILD_TESTS
diff --git a/dbus/dbus-pending-call.h b/dbus/dbus-pending-call.h
index 81af872f..4f1e92c0 100644
--- a/dbus/dbus-pending-call.h
+++ b/dbus/dbus-pending-call.h
@@ -35,7 +35,7 @@ DBUS_BEGIN_DECLS;
void dbus_pending_call_ref (DBusPendingCall *pending);
void dbus_pending_call_unref (DBusPendingCall *pending);
-void dbus_pending_call_set_notify (DBusPendingCall *pending,
+dbus_bool_t dbus_pending_call_set_notify (DBusPendingCall *pending,
DBusPendingCallNotifyFunction function,
void *user_data,
DBusFreeFunction free_user_data);
@@ -44,6 +44,15 @@ dbus_bool_t dbus_pending_call_get_completed (DBusPendingCall *pen
DBusMessage* dbus_pending_call_get_reply (DBusPendingCall *pending);
void dbus_pending_call_block (DBusPendingCall *pending);
+dbus_bool_t dbus_pending_call_allocate_data_slot (dbus_int32_t *slot_p);
+void dbus_pending_call_free_data_slot (dbus_int32_t *slot_p);
+dbus_bool_t dbus_pending_call_set_data (DBusPendingCall *pending,
+ dbus_int32_t slot,
+ void *data,
+ DBusFreeFunction free_data_func);
+void* dbus_pending_call_get_data (DBusPendingCall *pending,
+ dbus_int32_t slot);
+
DBUS_END_DECLS;
#endif /* DBUS_PENDING_CALL_H */
diff --git a/dbus/dbus-threads.c b/dbus/dbus-threads.c
index c5ce638f..2170c465 100644
--- a/dbus/dbus-threads.c
+++ b/dbus/dbus-threads.c
@@ -223,6 +223,7 @@ init_global_locks (void)
#define LOCK_ADDR(name) (& _dbus_lock_##name)
LOCK_ADDR (list),
LOCK_ADDR (connection_slots),
+ LOCK_ADDR (pending_call_slots),
LOCK_ADDR (server_slots),
LOCK_ADDR (message_slots),
LOCK_ADDR (atomic),