1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-object-tree.c DBusObjectTree (internals of DBusConnection)
4 * Copyright (C) 2003 Red Hat Inc.
6 * Licensed under the Academic Free License version 1.2
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "dbus-object-tree.h"
24 #include "dbus-connection-internal.h"
25 #include "dbus-internals.h"
26 #include "dbus-hash.h"
27 #include "dbus-protocol.h"
32 * @defgroup DBusObjectTree A hierarchy of objects with container-contained relationship
33 * @ingroup DBusInternals
34 * @brief DBusObjectTree is used by DBusConnection to track the object tree
36 * Types and functions related to DBusObjectTree. These
42 typedef struct DBusObjectSubtree DBusObjectSubtree;
44 DBusObjectSubtree* _dbus_object_subtree_new (const char **path,
45 const DBusObjectTreeVTable *vtable,
47 void _dbus_object_subtree_ref (DBusObjectSubtree *subtree);
48 void _dbus_object_subtree_unref (DBusObjectSubtree *subtree);
53 DBusConnection *connection;
55 /* Each subtree is a separate malloc block since that
56 * lets us refcount them and maybe helps with
57 * reentrancy issues when calling back to application code
59 DBusObjectSubtree **subtrees;
61 unsigned int subtrees_sorted : 1;
64 struct DBusObjectSubtree
69 DBusObjectTreeVTable vtable;
74 _dbus_object_tree_new (DBusConnection *connection)
78 /* the connection passed in here isn't fully constructed,
79 * so don't do anything more than store a pointer to
83 tree = dbus_new0 (DBusObjectTree, 1);
88 tree->connection = connection;
102 _dbus_object_tree_ref (DBusObjectTree *tree)
104 _dbus_assert (tree->refcount > 0);
110 _dbus_object_tree_unref (DBusObjectTree *tree)
112 _dbus_assert (tree->refcount > 0);
116 if (tree->refcount == 0)
122 while (i < tree->n_subtrees)
124 _dbus_object_subtree_unref (tree->subtrees[i]);
134 path_cmp (const char **path_a,
137 /* The comparison is as if the path were flattened
138 * into a single string. strcmp() considers
139 * a shorter string less than a longer string
140 * if the shorter string is the initial part
146 while (path_a[i] != NULL)
150 if (path_b[i] == NULL)
151 return 1; /* a is longer than b */
153 _dbus_assert (path_a[i] != NULL);
154 _dbus_assert (path_b[i] != NULL);
156 v = strcmp (path_a[i], path_b[i]);
164 _dbus_assert (path_a[i] == NULL);
165 if (path_b[i] == NULL)
168 /* b is longer than a */
173 subtree_cmp (DBusObjectSubtree *subtree_a,
174 DBusObjectSubtree *subtree_b)
176 return path_cmp ((const char**) subtree_a->path,
177 (const char**) subtree_b->path);
181 subtree_qsort_cmp (const void *a,
184 DBusObjectSubtree **subtree_a_p = (void*) a;
185 DBusObjectSubtree **subtree_b_p = (void*) b;
187 return subtree_cmp (*subtree_a_p, *subtree_b_p);
190 /* Returns TRUE if a is a subdir of b or vice
191 * versa. This is the case if one is a subpath
195 path_overlaps (const char **path_a,
201 while (path_a[i] != NULL)
205 if (path_b[i] == NULL)
206 return TRUE; /* b is subpath of a */
208 _dbus_assert (path_a[i] != NULL);
209 _dbus_assert (path_b[i] != NULL);
211 v = strcmp (path_a[i], path_b[i]);
214 return FALSE; /* they overlap until here and then are different,
221 /* b is either the same as or a superset of a */
222 _dbus_assert (path_a[i] == NULL);
227 find_subtree (DBusObjectTree *tree,
233 if (tree->subtrees == NULL)
236 if (!tree->subtrees_sorted)
238 qsort (tree->subtrees,
240 sizeof (DBusObjectSubtree*),
242 tree->subtrees_sorted = TRUE;
245 /* FIXME this should be a binary search,
246 * as that's the whole point of the sorting
249 while (i < tree->n_subtrees)
254 (const char**) tree->subtrees[i]->path);
270 #ifndef DBUS_DISABLE_CHECKS
272 check_overlap (DBusObjectTree *tree,
278 while (i < tree->n_subtrees)
280 if (path_overlaps (path, (const char**) tree->subtrees[i]->path))
282 _dbus_warn ("New path (path[0] = %s) overlaps old path (path[0] = %s)\n",
283 path[0], tree->subtrees[i]->path[0]);
291 * Registers a new subtree in the global object tree.
293 * @param tree the global object tree
294 * @param path NULL-terminated array of path elements giving path to subtree
295 * @param vtable the vtable used to traverse this subtree
296 * @param user_data user data to pass to methods in the vtable
297 * @returns #FALSE if not enough memory
300 _dbus_object_tree_register (DBusObjectTree *tree,
302 const DBusObjectTreeVTable *vtable,
305 DBusObjectSubtree *subtree;
306 DBusObjectSubtree **new_subtrees;
309 _dbus_assert (path != NULL);
310 #ifndef DBUS_DISABLE_CHECKS
311 check_overlap (tree, path);
313 _dbus_assert (path[0] != NULL);
315 subtree = _dbus_object_subtree_new (path, vtable, user_data);
319 /* FIXME we should do the "double alloc each time" standard thing */
320 new_n_subtrees = tree->n_subtrees + 1;
321 new_subtrees = dbus_realloc (tree->subtrees,
323 if (new_subtrees == NULL)
325 _DBUS_ZERO (subtree->vtable); /* to avoid assertion in unref() */
326 _dbus_object_subtree_unref (subtree);
330 tree->subtrees[tree->n_subtrees] = subtree;
331 tree->subtrees_sorted = FALSE;
332 tree->n_subtrees = new_n_subtrees;
333 tree->subtrees = new_subtrees;
339 * Unregisters an object subtree that was registered with the
342 * @param tree the global object tree
343 * @param path path to the subtree (same as the one passed to _dbus_object_tree_register())
346 _dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree,
350 DBusObjectSubtree *subtree;
352 _dbus_assert (path != NULL);
353 _dbus_assert (path[0] != NULL);
355 if (!find_subtree (tree, path, &i))
357 _dbus_warn ("Attempted to unregister subtree (path[0] = %s) which isn't registered\n",
362 subtree = tree->subtrees[i];
364 /* assumes a 0-byte memmove is OK */
365 memmove (&tree->subtrees[i],
366 &tree->subtrees[i+1],
367 (tree->n_subtrees - i - 1) * sizeof (tree->subtrees[0]));
368 tree->n_subtrees -= 1;
370 _dbus_object_subtree_ref (subtree);
372 /* Unlock and call application code */
373 _dbus_connection_unlock (tree->connection);
375 if (subtree->vtable.unregister_function)
377 (* subtree->vtable.unregister_function) (tree->connection,
378 (const char**) subtree->path,
380 _DBUS_ZERO (subtree->vtable);
385 * Tries to dispatch a message by directing it to the object tree
386 * node listed in the message header, if any.
388 * @param tree the global object tree
389 * @param message the message to dispatch
390 * @returns whether message was handled successfully
393 _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
394 DBusMessage *message)
401 _dbus_object_subtree_new (const char **path,
402 const DBusObjectTreeVTable *vtable,
405 DBusObjectSubtree *subtree;
407 subtree = dbus_new0 (DBusObjectSubtree, 1);
411 _dbus_assert (path != NULL);
412 _dbus_assert (path[0] != NULL);
414 subtree->path = _dbus_dup_string_array (path);
415 if (subtree->path == NULL)
418 subtree->vtable = *vtable;
419 subtree->user_data = user_data;
421 subtree->refcount = 1;
423 /* count path elements */
424 while (subtree->path[subtree->n_path_elements])
425 subtree->n_path_elements += 1;
432 dbus_free_string_array (subtree->path);
440 _dbus_object_subtree_ref (DBusObjectSubtree *subtree)
442 _dbus_assert (subtree->refcount > 0);
444 subtree->refcount += 1;
448 _dbus_object_subtree_unref (DBusObjectSubtree *subtree)
450 _dbus_assert (subtree->refcount > 0);
452 subtree->refcount -= 1;
454 if (subtree->refcount == 0)
456 _dbus_assert (subtree->vtable.unregister_function == NULL);
458 dbus_free_string_array (subtree->path);
466 #ifdef DBUS_BUILD_TESTS
467 #include "dbus-test.h"
471 test_subtree_cmp (const char **path1,
476 DBusObjectSubtree *subtree1;
477 DBusObjectSubtree *subtree2;
479 DBusObjectTreeVTable vtable;
485 subtree1 = _dbus_object_subtree_new (path1, &vtable, NULL);
486 subtree2 = _dbus_object_subtree_new (path2, &vtable, NULL);
487 if (subtree1 == NULL || subtree2 == NULL)
490 _dbus_assert (subtree_cmp (subtree1, subtree2) == expected);
497 _dbus_object_subtree_unref (subtree1);
500 _dbus_object_subtree_unref (subtree2);
502 if (retval && reverse)
504 /* Verify that the reverse also holds */
506 return test_subtree_cmp (path2, path1, -1, FALSE);
507 else if (expected < 0)
508 return test_subtree_cmp (path2, path1, 1, FALSE);
510 return test_subtree_cmp (path2, path1, 0, FALSE);
517 test_path_overlap (const char **path1,
519 dbus_bool_t expected)
521 _dbus_assert (path_overlaps (path1, path2) == expected);
522 _dbus_assert (path_overlaps (path2, path1) == expected);
526 object_tree_test_iteration (void *data)
528 const char *path1[] = { "foo", NULL };
529 const char *path2[] = { "foo", "bar", NULL };
530 const char *path3[] = { "foo", "bar", "baz", NULL };
531 const char *path4[] = { "foo", "bar", "boo", NULL };
532 const char *path5[] = { "blah", NULL };
533 DBusObjectSubtree *subtree1;
534 DBusObjectSubtree *subtree2;
535 DBusObjectTree *tree;
541 test_path_overlap (path1, path1, TRUE);
542 test_path_overlap (path1, path2, TRUE);
543 test_path_overlap (path1, path3, TRUE);
544 test_path_overlap (path1, path4, TRUE);
545 test_path_overlap (path1, path5, FALSE);
547 test_path_overlap (path2, path2, TRUE);
548 test_path_overlap (path2, path3, TRUE);
549 test_path_overlap (path2, path4, TRUE);
550 test_path_overlap (path2, path5, FALSE);
552 test_path_overlap (path3, path3, TRUE);
553 test_path_overlap (path3, path4, FALSE);
554 test_path_overlap (path3, path5, FALSE);
556 test_path_overlap (path4, path4, TRUE);
557 test_path_overlap (path4, path5, FALSE);
559 test_path_overlap (path5, path5, TRUE);
561 if (!test_subtree_cmp (path1, path1, 0, TRUE))
563 if (!test_subtree_cmp (path3, path3, 0, TRUE))
565 /* When testing -1, the reverse also gets tested */
566 if (!test_subtree_cmp (path1, path2, -1, TRUE))
568 if (!test_subtree_cmp (path1, path3, -1, TRUE))
570 if (!test_subtree_cmp (path2, path3, -1, TRUE))
572 if (!test_subtree_cmp (path2, path4, -1, TRUE))
574 if (!test_subtree_cmp (path3, path4, -1, TRUE))
576 if (!test_subtree_cmp (path5, path1, -1, TRUE))
579 tree = _dbus_object_tree_new (NULL);
585 _dbus_object_subtree_unref (subtree1);
587 _dbus_object_subtree_unref (subtree2);
589 _dbus_object_tree_unref (tree);
595 * @ingroup DBusObjectTree
596 * Unit test for DBusObjectTree
597 * @returns #TRUE on success.
600 _dbus_object_tree_test (void)
602 _dbus_test_oom_handling ("object tree",
603 object_tree_test_iteration,
609 #endif /* DBUS_BUILD_TESTS */