diff options
-rw-r--r-- | ChangeLog | 11 | ||||
-rw-r--r-- | dbus/Makefile.am | 2 | ||||
-rw-r--r-- | dbus/dbus-connection.h | 48 | ||||
-rw-r--r-- | dbus/dbus-message-builder.c | 2 | ||||
-rw-r--r-- | dbus/dbus-object-tree.c | 609 | ||||
-rw-r--r-- | dbus/dbus-object-tree.h | 48 | ||||
-rw-r--r-- | dbus/dbus-string.c | 2 | ||||
-rw-r--r-- | dbus/dbus-test.c | 6 | ||||
-rw-r--r-- | dbus/dbus-test.h | 1 |
9 files changed, 727 insertions, 2 deletions
@@ -1,3 +1,14 @@ +2003-08-25 Havoc Pennington <hp@pobox.com> + + Just noticed that dbus_message_test is hosed, I wonder when I + broke that. I thought make check was passing earlier... + + * dbus/dbus-object-tree.c: add new "object tree" to match DCOP + container tree, will replace most of dbus-object-registry + + * dbus/dbus-string.c (_dbus_string_append_printf_valist): fix C99 + screwup + 2003-08-19 Havoc Pennington <hp@pobox.com> * dbus/dbus-message.c (decode_string_field): support FIELD_SENDER diff --git a/dbus/Makefile.am b/dbus/Makefile.am index 3537b935..e59877e6 100644 --- a/dbus/Makefile.am +++ b/dbus/Makefile.am @@ -49,6 +49,8 @@ DBUS_LIB_SOURCES= \ dbus-objectid.c \ dbus-object-registry.c \ dbus-object-registry.h \ + dbus-object-tree.c \ + dbus-object-tree.h \ dbus-pending-call.c \ dbus-resources.c \ dbus-resources.h \ diff --git a/dbus/dbus-connection.h b/dbus/dbus-connection.h index ef106524..7204c8ed 100644 --- a/dbus/dbus-connection.h +++ b/dbus/dbus-connection.h @@ -198,6 +198,54 @@ void dbus_connection_send_preallocated (DBusConnection dbus_uint32_t *client_serial); +/* Object tree functionality */ + +typedef struct DBusObjectTreeVTable DBusObjectTreeVTable; + +typedef void (* DBusObjectTreeUnregisterFunction) (DBusConnection *connection, + const char **path, + void *user_data); +typedef DBusHandlerResult (* DBusObjectTreeMessageFunction) (DBusConnection *connection, + DBusMessage *message, + void *user_data); +typedef dbus_bool_t (* DBusObjectTreeSubdirsFunction) (DBusConnection *connection, + const char **path, + char ***subdirs, + int *n_subdirs, + void *user_data); +typedef dbus_bool_t (* DBusObjectTreeObjectsFunction) (DBusConnection *connection, + const char **path, + DBusObjectID **object_ids, + int *n_object_ids, + void *user_data); +typedef dbus_bool_t (* DBusObjectTreeMethodsFunction) (DBusConnection *connection, + const char **path, + DBusObjectID **object_ids, + int *n_object_ids, + void *user_data); + +struct DBusObjectTreeVTable +{ + DBusObjectTreeUnregisterFunction unregister_function; + DBusObjectTreeMessageFunction message_function; + DBusObjectTreeSubdirsFunction subdirs_function; + DBusObjectTreeObjectsFunction objects_function; + DBusObjectTreeMethodsFunction methods_function; + + void (* dbus_internal_pad1) (void *); + void (* dbus_internal_pad2) (void *); + void (* dbus_internal_pad3) (void *); + void (* dbus_internal_pad4) (void *); +}; + +dbus_bool_t dbus_connection_register_object_tree (DBusConnection *connection, + const char **path, + const DBusObjectTreeVTable *vtable, + void *user_data); +void dbus_connection_unregister_object_tree (DBusConnection *connection, + const char **path); + + DBUS_END_DECLS; #endif /* DBUS_CONNECTION_H */ diff --git a/dbus/dbus-message-builder.c b/dbus/dbus-message-builder.c index 958e57a0..f779c8c1 100644 --- a/dbus/dbus-message-builder.c +++ b/dbus/dbus-message-builder.c @@ -411,7 +411,7 @@ _dbus_message_data_load (DBusString *dest, DBusString name; int message_type; - if (_dbus_string_get_length (&line) < strlen ("VALID_HEADER ")) + if (_dbus_string_get_length (&line) < (int) strlen ("VALID_HEADER ")) { _dbus_warn ("no args to VALID_HEADER\n"); goto parse_failed; diff --git a/dbus/dbus-object-tree.c b/dbus/dbus-object-tree.c new file mode 100644 index 00000000..7f7e6011 --- /dev/null +++ b/dbus/dbus-object-tree.c @@ -0,0 +1,609 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-object-tree.c DBusObjectTree (internals of DBusConnection) + * + * Copyright (C) 2003 Red Hat Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "dbus-object-tree.h" +#include "dbus-connection-internal.h" +#include "dbus-internals.h" +#include "dbus-hash.h" +#include "dbus-protocol.h" +#include <string.h> +#include <stdlib.h> + +/** + * @defgroup DBusObjectTree A hierarchy of objects with container-contained relationship + * @ingroup DBusInternals + * @brief DBusObjectTree is used by DBusConnection to track the object tree + * + * Types and functions related to DBusObjectTree. These + * are all internal. + * + * @{ + */ + +typedef struct DBusObjectSubtree DBusObjectSubtree; + +DBusObjectSubtree* _dbus_object_subtree_new (const char **path, + const DBusObjectTreeVTable *vtable, + void *user_data); +void _dbus_object_subtree_ref (DBusObjectSubtree *subtree); +void _dbus_object_subtree_unref (DBusObjectSubtree *subtree); + +struct DBusObjectTree +{ + int refcount; + DBusConnection *connection; + + /* Each subtree is a separate malloc block since that + * lets us refcount them and maybe helps with + * reentrancy issues when calling back to application code + */ + DBusObjectSubtree **subtrees; + int n_subtrees; + unsigned int subtrees_sorted : 1; +}; + +struct DBusObjectSubtree +{ + int refcount; + char **path; + int n_path_elements; + DBusObjectTreeVTable vtable; + void *user_data; +}; + +DBusObjectTree* +_dbus_object_tree_new (DBusConnection *connection) +{ + DBusObjectTree *tree; + + /* the connection passed in here isn't fully constructed, + * so don't do anything more than store a pointer to + * it + */ + + tree = dbus_new0 (DBusObjectTree, 1); + if (tree == NULL) + goto oom; + + tree->refcount = 1; + tree->connection = connection; + + return tree; + + oom: + if (tree) + { + dbus_free (tree); + } + + return NULL; +} + +void +_dbus_object_tree_ref (DBusObjectTree *tree) +{ + _dbus_assert (tree->refcount > 0); + + tree->refcount += 1; +} + +void +_dbus_object_tree_unref (DBusObjectTree *tree) +{ + _dbus_assert (tree->refcount > 0); + + tree->refcount -= 1; + + if (tree->refcount == 0) + { + if (tree->subtrees) + { + int i; + i = 0; + while (i < tree->n_subtrees) + { + _dbus_object_subtree_unref (tree->subtrees[i]); + ++i; + } + } + + dbus_free (tree); + } +} + +static int +path_cmp (const char **path_a, + const char **path_b) +{ + /* The comparison is as if the path were flattened + * into a single string. strcmp() considers + * a shorter string less than a longer string + * if the shorter string is the initial part + * of the longer + */ + int i; + + i = 0; + while (path_a[i] != NULL) + { + int v; + + if (path_b[i] == NULL) + return 1; /* a is longer than b */ + + _dbus_assert (path_a[i] != NULL); + _dbus_assert (path_b[i] != NULL); + + v = strcmp (path_a[i], path_b[i]); + + if (v != 0) + return v; + + ++i; + } + + _dbus_assert (path_a[i] == NULL); + if (path_b[i] == NULL) + return 0; + + /* b is longer than a */ + return -1; +} + +static int +subtree_cmp (DBusObjectSubtree *subtree_a, + DBusObjectSubtree *subtree_b) +{ + return path_cmp ((const char**) subtree_a->path, + (const char**) subtree_b->path); +} + +static int +subtree_qsort_cmp (const void *a, + const void *b) +{ + DBusObjectSubtree **subtree_a_p = (void*) a; + DBusObjectSubtree **subtree_b_p = (void*) b; + + return subtree_cmp (*subtree_a_p, *subtree_b_p); +} + +/* Returns TRUE if a is a subdir of b or vice + * versa. This is the case if one is a subpath + * of the other. + */ +static dbus_bool_t +path_overlaps (const char **path_a, + const char **path_b) +{ + int i; + + i = 0; + while (path_a[i] != NULL) + { + int v; + + if (path_b[i] == NULL) + return TRUE; /* b is subpath of a */ + + _dbus_assert (path_a[i] != NULL); + _dbus_assert (path_b[i] != NULL); + + v = strcmp (path_a[i], path_b[i]); + + if (v != 0) + return FALSE; /* they overlap until here and then are different, + * not overlapping + */ + + ++i; + } + + /* b is either the same as or a superset of a */ + _dbus_assert (path_a[i] == NULL); + return TRUE; +} + +static dbus_bool_t +find_subtree (DBusObjectTree *tree, + const char **path, + int *idx_p) +{ + int i; + + if (tree->subtrees == NULL) + return FALSE; + + if (!tree->subtrees_sorted) + { + qsort (tree->subtrees, + tree->n_subtrees, + sizeof (DBusObjectSubtree*), + subtree_qsort_cmp); + tree->subtrees_sorted = TRUE; + } + + /* FIXME this should be a binary search, + * as that's the whole point of the sorting + */ + i = 0; + while (i < tree->n_subtrees) + { + int v; + + v = path_cmp (path, + (const char**) tree->subtrees[i]->path); + if (v == 0) + { + if (idx_p) + *idx_p = i; + return TRUE; + } + else if (v > 0) + return FALSE; + + ++i; + } + + return FALSE; +} + +#ifndef DBUS_DISABLE_CHECKS +static void +check_overlap (DBusObjectTree *tree, + const char **path) +{ + int i; + + i = 0; + while (i < tree->n_subtrees) + { + if (path_overlaps (path, (const char**) tree->subtrees[i]->path)) + { + _dbus_warn ("New path (path[0] = %s) overlaps old path (path[0] = %s)\n", + path[0], tree->subtrees[i]->path[0]); + } + ++i; + } +} +#endif + +/** + * Registers a new subtree in the global object tree. + * + * @param tree the global object tree + * @param path NULL-terminated array of path elements giving path to subtree + * @param vtable the vtable used to traverse this subtree + * @param user_data user data to pass to methods in the vtable + * @returns #FALSE if not enough memory + */ +dbus_bool_t +_dbus_object_tree_register (DBusObjectTree *tree, + const char **path, + const DBusObjectTreeVTable *vtable, + void *user_data) +{ + DBusObjectSubtree *subtree; + DBusObjectSubtree **new_subtrees; + int new_n_subtrees; + + _dbus_assert (path != NULL); +#ifndef DBUS_DISABLE_CHECKS + check_overlap (tree, path); +#endif + _dbus_assert (path[0] != NULL); + + subtree = _dbus_object_subtree_new (path, vtable, user_data); + if (subtree == NULL) + return FALSE; + + /* FIXME we should do the "double alloc each time" standard thing */ + new_n_subtrees = tree->n_subtrees + 1; + new_subtrees = dbus_realloc (tree->subtrees, + new_n_subtrees); + if (new_subtrees == NULL) + { + _DBUS_ZERO (subtree->vtable); /* to avoid assertion in unref() */ + _dbus_object_subtree_unref (subtree); + return FALSE; + } + + tree->subtrees[tree->n_subtrees] = subtree; + tree->subtrees_sorted = FALSE; + tree->n_subtrees = new_n_subtrees; + tree->subtrees = new_subtrees; + + return TRUE; +} + +/** + * Unregisters an object subtree that was registered with the + * same path. + * + * @param tree the global object tree + * @param path path to the subtree (same as the one passed to _dbus_object_tree_register()) + */ +void +_dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree, + const char **path) +{ + int i; + DBusObjectSubtree *subtree; + + _dbus_assert (path != NULL); + _dbus_assert (path[0] != NULL); + + if (!find_subtree (tree, path, &i)) + { + _dbus_warn ("Attempted to unregister subtree (path[0] = %s) which isn't registered\n", + path[0]); + return; + } + + subtree = tree->subtrees[i]; + + /* assumes a 0-byte memmove is OK */ + memmove (&tree->subtrees[i], + &tree->subtrees[i+1], + (tree->n_subtrees - i - 1) * sizeof (tree->subtrees[0])); + tree->n_subtrees -= 1; + + _dbus_object_subtree_ref (subtree); + + /* Unlock and call application code */ + _dbus_connection_unlock (tree->connection); + + if (subtree->vtable.unregister_function) + { + (* subtree->vtable.unregister_function) (tree->connection, + (const char**) subtree->path, + subtree->user_data); + _DBUS_ZERO (subtree->vtable); + } +} + +/** + * Tries to dispatch a message by directing it to the object tree + * node listed in the message header, if any. + * + * @param tree the global object tree + * @param message the message to dispatch + * @returns whether message was handled successfully + */ +DBusHandlerResult +_dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree, + DBusMessage *message) +{ + + +} + +DBusObjectSubtree* +_dbus_object_subtree_new (const char **path, + const DBusObjectTreeVTable *vtable, + void *user_data) +{ + DBusObjectSubtree *subtree; + + subtree = dbus_new0 (DBusObjectSubtree, 1); + if (subtree == NULL) + goto oom; + + _dbus_assert (path != NULL); + _dbus_assert (path[0] != NULL); + + subtree->path = _dbus_dup_string_array (path); + if (subtree->path == NULL) + goto oom; + + subtree->vtable = *vtable; + subtree->user_data = user_data; + + subtree->refcount = 1; + + /* count path elements */ + while (subtree->path[subtree->n_path_elements]) + subtree->n_path_elements += 1; + + return subtree; + + oom: + if (subtree) + { + dbus_free_string_array (subtree->path); + dbus_free (subtree); + } + + return NULL; +} + +void +_dbus_object_subtree_ref (DBusObjectSubtree *subtree) +{ + _dbus_assert (subtree->refcount > 0); + + subtree->refcount += 1; +} + +void +_dbus_object_subtree_unref (DBusObjectSubtree *subtree) +{ + _dbus_assert (subtree->refcount > 0); + + subtree->refcount -= 1; + + if (subtree->refcount == 0) + { + _dbus_assert (subtree->vtable.unregister_function == NULL); + + dbus_free_string_array (subtree->path); + + dbus_free (subtree); + } +} + +/** @} */ + +#ifdef DBUS_BUILD_TESTS +#include "dbus-test.h" +#include <stdio.h> + +static dbus_bool_t +test_subtree_cmp (const char **path1, + const char **path2, + int expected, + dbus_bool_t reverse) +{ + DBusObjectSubtree *subtree1; + DBusObjectSubtree *subtree2; + dbus_bool_t retval; + DBusObjectTreeVTable vtable; + + _DBUS_ZERO (vtable); + + retval = FALSE; + + subtree1 = _dbus_object_subtree_new (path1, &vtable, NULL); + subtree2 = _dbus_object_subtree_new (path2, &vtable, NULL); + if (subtree1 == NULL || subtree2 == NULL) + goto out; + + _dbus_assert (subtree_cmp (subtree1, subtree2) == expected); + + retval = TRUE; + + out: + + if (subtree1) + _dbus_object_subtree_unref (subtree1); + + if (subtree2) + _dbus_object_subtree_unref (subtree2); + + if (retval && reverse) + { + /* Verify that the reverse also holds */ + if (expected > 0) + return test_subtree_cmp (path2, path1, -1, FALSE); + else if (expected < 0) + return test_subtree_cmp (path2, path1, 1, FALSE); + else + return test_subtree_cmp (path2, path1, 0, FALSE); + } + + return retval; +} + +static void +test_path_overlap (const char **path1, + const char **path2, + dbus_bool_t expected) +{ + _dbus_assert (path_overlaps (path1, path2) == expected); + _dbus_assert (path_overlaps (path2, path1) == expected); +} + +static dbus_bool_t +object_tree_test_iteration (void *data) +{ + const char *path1[] = { "foo", NULL }; + const char *path2[] = { "foo", "bar", NULL }; + const char *path3[] = { "foo", "bar", "baz", NULL }; + const char *path4[] = { "foo", "bar", "boo", NULL }; + const char *path5[] = { "blah", NULL }; + DBusObjectSubtree *subtree1; + DBusObjectSubtree *subtree2; + DBusObjectTree *tree; + + tree = NULL; + subtree1 = NULL; + subtree2 = NULL; + + test_path_overlap (path1, path1, TRUE); + test_path_overlap (path1, path2, TRUE); + test_path_overlap (path1, path3, TRUE); + test_path_overlap (path1, path4, TRUE); + test_path_overlap (path1, path5, FALSE); + + test_path_overlap (path2, path2, TRUE); + test_path_overlap (path2, path3, TRUE); + test_path_overlap (path2, path4, TRUE); + test_path_overlap (path2, path5, FALSE); + + test_path_overlap (path3, path3, TRUE); + test_path_overlap (path3, path4, FALSE); + test_path_overlap (path3, path5, FALSE); + + test_path_overlap (path4, path4, TRUE); + test_path_overlap (path4, path5, FALSE); + + test_path_overlap (path5, path5, TRUE); + + if (!test_subtree_cmp (path1, path1, 0, TRUE)) + goto out; + if (!test_subtree_cmp (path3, path3, 0, TRUE)) + goto out; + /* When testing -1, the reverse also gets tested */ + if (!test_subtree_cmp (path1, path2, -1, TRUE)) + goto out; + if (!test_subtree_cmp (path1, path3, -1, TRUE)) + goto out; + if (!test_subtree_cmp (path2, path3, -1, TRUE)) + goto out; + if (!test_subtree_cmp (path2, path4, -1, TRUE)) + goto out; + if (!test_subtree_cmp (path3, path4, -1, TRUE)) + goto out; + if (!test_subtree_cmp (path5, path1, -1, TRUE)) + goto out; + + tree = _dbus_object_tree_new (NULL); + if (tree == NULL) + goto out; + + out: + if (subtree1) + _dbus_object_subtree_unref (subtree1); + if (subtree2) + _dbus_object_subtree_unref (subtree2); + if (tree) + _dbus_object_tree_unref (tree); + + return TRUE; +} + +/** + * @ingroup DBusObjectTree + * Unit test for DBusObjectTree + * @returns #TRUE on success. + */ +dbus_bool_t +_dbus_object_tree_test (void) +{ + _dbus_test_oom_handling ("object tree", + object_tree_test_iteration, + NULL); + + return TRUE; +} + +#endif /* DBUS_BUILD_TESTS */ diff --git a/dbus/dbus-object-tree.h b/dbus/dbus-object-tree.h new file mode 100644 index 00000000..06033333 --- /dev/null +++ b/dbus/dbus-object-tree.h @@ -0,0 +1,48 @@ +/* -*- mode: C; c-file-style: "gnu" -*- */ +/* dbus-object-tree.h DBusObjectTree (internals of DBusConnection) + * + * Copyright (C) 2003 Red Hat Inc. + * + * Licensed under the Academic Free License version 1.2 + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef DBUS_OBJECT_TREE_H +#define DBUS_OBJECT_TREE_H + +#include <dbus/dbus-connection.h> + +DBUS_BEGIN_DECLS; + +typedef struct DBusObjectTree DBusObjectTree; + +DBusObjectTree* _dbus_object_tree_new (DBusConnection *connection); +void _dbus_object_tree_ref (DBusObjectTree *tree); +void _dbus_object_tree_unref (DBusObjectTree *tree); + +dbus_bool_t _dbus_object_tree_register (DBusObjectTree *tree, + const char **path, + const DBusObjectTreeVTable *vtable, + void *user_data); +void _dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree, + const char **path); +DBusHandlerResult _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree, + DBusMessage *message); + + +DBUS_END_DECLS; + +#endif /* DBUS_OBJECT_TREE_H */ diff --git a/dbus/dbus-string.c b/dbus/dbus-string.c index 98b4c60e..75b38b9d 100644 --- a/dbus/dbus-string.c +++ b/dbus/dbus-string.c @@ -1002,9 +1002,9 @@ _dbus_string_append_printf_valist (DBusString *str, const char *format, va_list args) { - DBUS_STRING_PREAMBLE (str); int len; char c; + DBUS_STRING_PREAMBLE (str); /* Measure the message length without terminating nul */ len = vsnprintf (&c, 1, format, args); diff --git a/dbus/dbus-test.c b/dbus/dbus-test.c index 8a99d179..2ab7fc27 100644 --- a/dbus/dbus-test.c +++ b/dbus/dbus-test.c @@ -112,6 +112,12 @@ dbus_internal_do_not_use_run_tests (const char *test_data_dir) check_memleaks (); + printf ("%s: running object tree tests\n", "dbus-test"); + if (!_dbus_object_tree_test ()) + die ("object tree"); + + check_memleaks (); + printf ("%s: running object tests\n", "dbus-test"); if (!_dbus_object_test ()) die ("object"); diff --git a/dbus/dbus-test.h b/dbus/dbus-test.h index 276e8f9e..b6c02669 100644 --- a/dbus/dbus-test.h +++ b/dbus/dbus-test.h @@ -56,6 +56,7 @@ dbus_bool_t _dbus_memory_test (void); dbus_bool_t _dbus_object_test (void); dbus_bool_t _dbus_object_id_test (void); dbus_bool_t _dbus_object_registry_test (void); +dbus_bool_t _dbus_object_tree_test (void); dbus_bool_t _dbus_pending_call_test (const char *test_data_dir); void dbus_internal_do_not_use_run_tests (const char *test_data_dir); |