00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "dbus-object-tree.h"
00024 #include "dbus-connection-internal.h"
00025 #include "dbus-internals.h"
00026 #include "dbus-hash.h"
00027 #include "dbus-protocol.h"
00028 #include "dbus-string.h"
00029 #include <string.h>
00030 #include <stdlib.h>
00031
00044 typedef struct DBusObjectSubtree DBusObjectSubtree;
00045
00046 static DBusObjectSubtree* _dbus_object_subtree_new (const char *name,
00047 const DBusObjectPathVTable *vtable,
00048 void *user_data);
00049 static DBusObjectSubtree* _dbus_object_subtree_ref (DBusObjectSubtree *subtree);
00050 static void _dbus_object_subtree_unref (DBusObjectSubtree *subtree);
00051
00055 struct DBusObjectTree
00056 {
00057 int refcount;
00058 DBusConnection *connection;
00060 DBusObjectSubtree *root;
00061 };
00062
00068 struct DBusObjectSubtree
00069 {
00070 DBusAtomic refcount;
00071 DBusObjectSubtree *parent;
00072 DBusObjectPathUnregisterFunction unregister_function;
00073 DBusObjectPathMessageFunction message_function;
00074 void *user_data;
00075 DBusObjectSubtree **subtrees;
00076 int n_subtrees;
00077 unsigned int subtrees_sorted : 1;
00078 unsigned int invoke_as_fallback : 1;
00079 char name[1];
00080 };
00081
00089 DBusObjectTree*
00090 _dbus_object_tree_new (DBusConnection *connection)
00091 {
00092 DBusObjectTree *tree;
00093
00094
00095
00096
00097
00098
00099 tree = dbus_new0 (DBusObjectTree, 1);
00100 if (tree == NULL)
00101 goto oom;
00102
00103 tree->refcount = 1;
00104 tree->connection = connection;
00105 tree->root = _dbus_object_subtree_new ("/", NULL, NULL);
00106 if (tree->root == NULL)
00107 goto oom;
00108 tree->root->invoke_as_fallback = TRUE;
00109
00110 return tree;
00111
00112 oom:
00113 if (tree)
00114 {
00115 dbus_free (tree);
00116 }
00117
00118 return NULL;
00119 }
00120
00126 DBusObjectTree *
00127 _dbus_object_tree_ref (DBusObjectTree *tree)
00128 {
00129 _dbus_assert (tree->refcount > 0);
00130
00131 tree->refcount += 1;
00132
00133 return tree;
00134 }
00135
00140 void
00141 _dbus_object_tree_unref (DBusObjectTree *tree)
00142 {
00143 _dbus_assert (tree->refcount > 0);
00144
00145 tree->refcount -= 1;
00146
00147 if (tree->refcount == 0)
00148 {
00149 _dbus_object_tree_free_all_unlocked (tree);
00150
00151 dbus_free (tree);
00152 }
00153 }
00154
00155 static int
00156 subtree_cmp (DBusObjectSubtree *subtree_a,
00157 DBusObjectSubtree *subtree_b)
00158 {
00159 return strcmp (subtree_a->name, subtree_b->name);
00160 }
00161
00162 static int
00163 subtree_qsort_cmp (const void *a,
00164 const void *b)
00165 {
00166 DBusObjectSubtree **subtree_a_p = (void*) a;
00167 DBusObjectSubtree **subtree_b_p = (void*) b;
00168
00169 return subtree_cmp (*subtree_a_p, *subtree_b_p);
00170 }
00171
00172 static void
00173 ensure_sorted (DBusObjectSubtree *subtree)
00174 {
00175 if (subtree->subtrees && !subtree->subtrees_sorted)
00176 {
00177 qsort (subtree->subtrees,
00178 subtree->n_subtrees,
00179 sizeof (DBusObjectSubtree*),
00180 subtree_qsort_cmp);
00181 subtree->subtrees_sorted = TRUE;
00182 }
00183 }
00184
00188 #define VERBOSE_FIND 0
00189
00190 static DBusObjectSubtree*
00191 find_subtree_recurse (DBusObjectSubtree *subtree,
00192 const char **path,
00193 dbus_bool_t return_deepest_match,
00194 dbus_bool_t create_if_not_found,
00195 int *index_in_parent)
00196 {
00197 int i;
00198
00199 _dbus_assert (!(return_deepest_match && create_if_not_found));
00200
00201 if (path[0] == NULL)
00202 {
00203 #if VERBOSE_FIND
00204 _dbus_verbose (" path exhausted, returning %s\n",
00205 subtree->name);
00206 #endif
00207 return subtree;
00208 }
00209
00210 #if VERBOSE_FIND
00211 _dbus_verbose (" searching children of %s for %s\n",
00212 subtree->name, path[0]);
00213 #endif
00214
00215 ensure_sorted (subtree);
00216
00217
00218
00219
00220
00221 i = 0;
00222 while (i < subtree->n_subtrees)
00223 {
00224 int v;
00225
00226 v = strcmp (path[0], subtree->subtrees[i]->name);
00227
00228 #if VERBOSE_FIND
00229 _dbus_verbose (" %s cmp %s = %d\n",
00230 path[0], subtree->subtrees[i]->name,
00231 v);
00232 #endif
00233
00234 if (v == 0)
00235 {
00236 if (index_in_parent)
00237 {
00238 #if VERBOSE_FIND
00239 _dbus_verbose (" storing parent index %d\n", i);
00240 #endif
00241 *index_in_parent = i;
00242 }
00243
00244 if (return_deepest_match)
00245 {
00246 DBusObjectSubtree *next;
00247
00248 next = find_subtree_recurse (subtree->subtrees[i],
00249 &path[1], return_deepest_match,
00250 create_if_not_found, index_in_parent);
00251 if (next == NULL &&
00252 subtree->invoke_as_fallback)
00253 {
00254 #if VERBOSE_FIND
00255 _dbus_verbose (" no deeper match found, returning %s\n",
00256 subtree->name);
00257 #endif
00258 return subtree;
00259 }
00260 else
00261 return next;
00262 }
00263 else
00264 return find_subtree_recurse (subtree->subtrees[i],
00265 &path[1], return_deepest_match,
00266 create_if_not_found, index_in_parent);
00267 }
00268 else if (v < 0)
00269 {
00270 goto not_found;
00271 }
00272
00273 ++i;
00274 }
00275
00276 not_found:
00277 #if VERBOSE_FIND
00278 _dbus_verbose (" no match found, current tree %s, create_if_not_found = %d\n",
00279 subtree->name, create_if_not_found);
00280 #endif
00281
00282 if (create_if_not_found)
00283 {
00284 DBusObjectSubtree* child;
00285 DBusObjectSubtree **new_subtrees;
00286 int new_n_subtrees;
00287
00288 #if VERBOSE_FIND
00289 _dbus_verbose (" creating subtree %s\n",
00290 path[0]);
00291 #endif
00292
00293 child = _dbus_object_subtree_new (path[0],
00294 NULL, NULL);
00295 if (child == NULL)
00296 return NULL;
00297
00298
00299 new_n_subtrees = subtree->n_subtrees + 1;
00300 new_subtrees = dbus_realloc (subtree->subtrees,
00301 new_n_subtrees * sizeof (DBusObjectSubtree*));
00302 if (new_subtrees == NULL)
00303 {
00304 child->unregister_function = NULL;
00305 child->message_function = NULL;
00306 _dbus_object_subtree_unref (child);
00307 return FALSE;
00308 }
00309
00310 new_subtrees[subtree->n_subtrees] = child;
00311 if (index_in_parent)
00312 *index_in_parent = subtree->n_subtrees;
00313 subtree->subtrees_sorted = FALSE;
00314 subtree->n_subtrees = new_n_subtrees;
00315 subtree->subtrees = new_subtrees;
00316
00317 child->parent = subtree;
00318
00319 return find_subtree_recurse (child,
00320 &path[1], return_deepest_match,
00321 create_if_not_found, index_in_parent);
00322 }
00323 else
00324 return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL;
00325 }
00326
00327 static DBusObjectSubtree*
00328 find_subtree (DBusObjectTree *tree,
00329 const char **path,
00330 int *index_in_parent)
00331 {
00332 DBusObjectSubtree *subtree;
00333
00334 #if VERBOSE_FIND
00335 _dbus_verbose ("Looking for exact registered subtree\n");
00336 #endif
00337
00338 subtree = find_subtree_recurse (tree->root, path, FALSE, FALSE, index_in_parent);
00339
00340 if (subtree && subtree->message_function == NULL)
00341 return NULL;
00342 else
00343 return subtree;
00344 }
00345
00346 static DBusObjectSubtree*
00347 find_handler (DBusObjectTree *tree,
00348 const char **path)
00349 {
00350 #if VERBOSE_FIND
00351 _dbus_verbose ("Looking for deepest handler\n");
00352 #endif
00353 return find_subtree_recurse (tree->root, path, TRUE, FALSE, NULL);
00354 }
00355
00356 static DBusObjectSubtree*
00357 ensure_subtree (DBusObjectTree *tree,
00358 const char **path)
00359 {
00360 #if VERBOSE_FIND
00361 _dbus_verbose ("Ensuring subtree\n");
00362 #endif
00363 return find_subtree_recurse (tree->root, path, FALSE, TRUE, NULL);
00364 }
00365
00376 dbus_bool_t
00377 _dbus_object_tree_register (DBusObjectTree *tree,
00378 dbus_bool_t fallback,
00379 const char **path,
00380 const DBusObjectPathVTable *vtable,
00381 void *user_data)
00382 {
00383 DBusObjectSubtree *subtree;
00384
00385 _dbus_assert (tree != NULL);
00386 _dbus_assert (vtable->message_function != NULL);
00387 _dbus_assert (path != NULL);
00388
00389 subtree = ensure_subtree (tree, path);
00390 if (subtree == NULL)
00391 return FALSE;
00392
00393 #ifndef DBUS_DISABLE_CHECKS
00394 if (subtree->message_function != NULL)
00395 {
00396 _dbus_warn ("A handler is already registered for the path starting with path[0] = \"%s\"\n",
00397 path[0] ? path[0] : "null");
00398 return FALSE;
00399 }
00400 #else
00401 _dbus_assert (subtree->message_function == NULL);
00402 #endif
00403
00404 subtree->message_function = vtable->message_function;
00405 subtree->unregister_function = vtable->unregister_function;
00406 subtree->user_data = user_data;
00407 subtree->invoke_as_fallback = fallback != FALSE;
00408
00409 return TRUE;
00410 }
00411
00419 void
00420 _dbus_object_tree_unregister_and_unlock (DBusObjectTree *tree,
00421 const char **path)
00422 {
00423 int i;
00424 DBusObjectSubtree *subtree;
00425 DBusObjectPathUnregisterFunction unregister_function;
00426 void *user_data;
00427 DBusConnection *connection;
00428
00429 _dbus_assert (path != NULL);
00430
00431 subtree = find_subtree (tree, path, &i);
00432
00433 #ifndef DBUS_DISABLE_CHECKS
00434 if (subtree == NULL)
00435 {
00436 _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n",
00437 path[0] ? path[0] : "null",
00438 path[1] ? path[1] : "null");
00439 return;
00440 }
00441 #else
00442 _dbus_assert (subtree != NULL);
00443 #endif
00444
00445 _dbus_assert (subtree->parent == NULL ||
00446 (i >= 0 && subtree->parent->subtrees[i] == subtree));
00447
00448 subtree->message_function = NULL;
00449
00450 unregister_function = subtree->unregister_function;
00451 user_data = subtree->user_data;
00452
00453 subtree->unregister_function = NULL;
00454 subtree->user_data = NULL;
00455
00456
00457
00458
00459
00460 if (subtree->parent && subtree->n_subtrees == 0)
00461 {
00462
00463 memmove (&subtree->parent->subtrees[i],
00464 &subtree->parent->subtrees[i+1],
00465 (subtree->parent->n_subtrees - i - 1) *
00466 sizeof (subtree->parent->subtrees[0]));
00467 subtree->parent->n_subtrees -= 1;
00468
00469 subtree->parent = NULL;
00470
00471 _dbus_object_subtree_unref (subtree);
00472 }
00473 subtree = NULL;
00474
00475 connection = tree->connection;
00476
00477
00478 #ifdef DBUS_BUILD_TESTS
00479 if (connection)
00480 #endif
00481 {
00482 _dbus_connection_ref_unlocked (connection);
00483 _dbus_connection_unlock (connection);
00484 }
00485
00486 if (unregister_function)
00487 (* unregister_function) (connection, user_data);
00488
00489 #ifdef DBUS_BUILD_TESTS
00490 if (connection)
00491 #endif
00492 dbus_connection_unref (connection);
00493 }
00494
00495 static void
00496 free_subtree_recurse (DBusConnection *connection,
00497 DBusObjectSubtree *subtree)
00498 {
00499
00500
00501
00502 while (subtree->n_subtrees > 0)
00503 {
00504 DBusObjectSubtree *child;
00505
00506 child = subtree->subtrees[subtree->n_subtrees - 1];
00507 subtree->subtrees[subtree->n_subtrees - 1] = NULL;
00508 subtree->n_subtrees -= 1;
00509 child->parent = NULL;
00510
00511 free_subtree_recurse (connection, child);
00512 }
00513
00514
00515 if (subtree->unregister_function)
00516 {
00517 (* subtree->unregister_function) (connection,
00518 subtree->user_data);
00519 subtree->message_function = NULL;
00520 subtree->unregister_function = NULL;
00521 subtree->user_data = NULL;
00522 }
00523
00524
00525 _dbus_object_subtree_unref (subtree);
00526 }
00527
00534 void
00535 _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
00536 {
00537 if (tree->root)
00538 free_subtree_recurse (tree->connection,
00539 tree->root);
00540 tree->root = NULL;
00541 }
00542
00543 static dbus_bool_t
00544 _dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
00545 const char **parent_path,
00546 char ***child_entries)
00547 {
00548 DBusObjectSubtree *subtree;
00549 char **retval;
00550
00551 _dbus_assert (parent_path != NULL);
00552 _dbus_assert (child_entries != NULL);
00553
00554 *child_entries = NULL;
00555
00556 subtree = find_subtree (tree, parent_path, NULL);
00557 if (subtree == NULL)
00558 {
00559 retval = dbus_new0 (char *, 1);
00560 if (retval == NULL)
00561 goto out;
00562 }
00563 else
00564 {
00565 int i;
00566 retval = dbus_new0 (char*, subtree->n_subtrees + 1);
00567 if (retval == NULL)
00568 goto out;
00569 i = 0;
00570 while (i < subtree->n_subtrees)
00571 {
00572 retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
00573 if (retval[i] == NULL)
00574 {
00575 dbus_free_string_array (retval);
00576 retval = NULL;
00577 goto out;
00578 }
00579 ++i;
00580 }
00581 }
00582
00583 out:
00584
00585 *child_entries = retval;
00586 return retval != NULL;
00587 }
00588
00589 static DBusHandlerResult
00590 handle_default_introspect_unlocked (DBusObjectTree *tree,
00591 DBusMessage *message,
00592 const char **path)
00593 {
00594 DBusString xml;
00595 DBusHandlerResult result;
00596 char **children;
00597 int i;
00598
00599 if (!dbus_message_is_method_call (message,
00600 DBUS_INTERFACE_ORG_FREEDESKTOP_INTROSPECTABLE,
00601 "Introspect"))
00602 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00603
00604 if (!_dbus_string_init (&xml))
00605 return DBUS_HANDLER_RESULT_NEED_MEMORY;
00606
00607 result = DBUS_HANDLER_RESULT_NEED_MEMORY;
00608
00609 children = NULL;
00610 if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
00611 goto out;
00612
00613 if (!_dbus_string_append (&xml, "<node>\n"))
00614 goto out;
00615
00616 i = 0;
00617 while (children[i] != NULL)
00618 {
00619 if (!_dbus_string_append_printf (&xml, " <node name=\"%s\"/>\n",
00620 children[i]))
00621 goto out;
00622
00623 ++i;
00624 }
00625
00626 if (!_dbus_string_append (&xml, "</node>\n"))
00627 goto out;
00628
00629 result = DBUS_HANDLER_RESULT_HANDLED;
00630
00631 out:
00632 _dbus_string_free (&xml);
00633 dbus_free_string_array (children);
00634
00635 return result;
00636 }
00637
00651 DBusHandlerResult
00652 _dbus_object_tree_dispatch_and_unlock (DBusObjectTree *tree,
00653 DBusMessage *message)
00654 {
00655 char **path;
00656 DBusList *list;
00657 DBusList *link;
00658 DBusHandlerResult result;
00659 DBusObjectSubtree *subtree;
00660
00661 #if 0
00662 _dbus_verbose ("Dispatch of message by object path\n");
00663 #endif
00664
00665 path = NULL;
00666 if (!dbus_message_get_path_decomposed (message, &path))
00667 {
00668 #ifdef DBUS_BUILD_TESTS
00669 if (tree->connection)
00670 #endif
00671 _dbus_connection_unlock (tree->connection);
00672
00673 _dbus_verbose ("No memory to get decomposed path\n");
00674
00675 return DBUS_HANDLER_RESULT_NEED_MEMORY;
00676 }
00677
00678 if (path == NULL)
00679 {
00680 #ifdef DBUS_BUILD_TESTS
00681 if (tree->connection)
00682 #endif
00683 _dbus_connection_unlock (tree->connection);
00684
00685 _dbus_verbose ("No path field in message\n");
00686 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00687 }
00688
00689
00690 subtree = find_handler (tree, (const char**) path);
00691
00692
00693
00694 list = NULL;
00695
00696 while (subtree != NULL)
00697 {
00698 if (subtree->message_function != NULL)
00699 {
00700 _dbus_object_subtree_ref (subtree);
00701
00702
00703 if (!_dbus_list_append (&list, subtree))
00704 {
00705 result = DBUS_HANDLER_RESULT_NEED_MEMORY;
00706 _dbus_object_subtree_unref (subtree);
00707 goto free_and_return;
00708 }
00709 }
00710
00711 subtree = subtree->parent;
00712 }
00713
00714 _dbus_verbose ("%d handlers in the path tree for this message\n",
00715 _dbus_list_get_length (&list));
00716
00717
00718
00719 result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
00720
00721 link = _dbus_list_get_first_link (&list);
00722 while (link != NULL)
00723 {
00724 DBusList *next = _dbus_list_get_next_link (&list, link);
00725 subtree = link->data;
00726
00727
00728
00729
00730 if (subtree->message_function)
00731 {
00732 DBusObjectPathMessageFunction message_function;
00733 void *user_data;
00734
00735 message_function = subtree->message_function;
00736 user_data = subtree->user_data;
00737
00738 #if 0
00739 _dbus_verbose (" (invoking a handler)\n");
00740 #endif
00741
00742 #ifdef DBUS_BUILD_TESTS
00743 if (tree->connection)
00744 #endif
00745 _dbus_connection_unlock (tree->connection);
00746
00747
00748
00749
00750
00751
00752 result = (* message_function) (tree->connection,
00753 message,
00754 user_data);
00755
00756 #ifdef DBUS_BUILD_TESTS
00757 if (tree->connection)
00758 #endif
00759 _dbus_connection_lock (tree->connection);
00760
00761 if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
00762 goto free_and_return;
00763 }
00764
00765 link = next;
00766 }
00767
00768 free_and_return:
00769
00770 if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
00771 {
00772
00773
00774 result = handle_default_introspect_unlocked (tree, message,
00775 (const char**) path);
00776 }
00777
00778 #ifdef DBUS_BUILD_TESTS
00779 if (tree->connection)
00780 #endif
00781 _dbus_connection_unlock (tree->connection);
00782
00783 while (list != NULL)
00784 {
00785 link = _dbus_list_get_first_link (&list);
00786 _dbus_object_subtree_unref (link->data);
00787 _dbus_list_remove_link (&list, link);
00788 }
00789
00790 dbus_free_string_array (path);
00791
00792 return result;
00793 }
00794
00801 static DBusObjectSubtree*
00802 allocate_subtree_object (const char *name)
00803 {
00804 int len;
00805 DBusObjectSubtree *subtree;
00806 const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name);
00807
00808 _dbus_assert (name != NULL);
00809
00810 len = strlen (name);
00811
00812 subtree = dbus_malloc (front_padding + (len + 1));
00813
00814 if (subtree == NULL)
00815 return NULL;
00816
00817 memcpy (subtree->name, name, len + 1);
00818
00819 return subtree;
00820 }
00821
00822 static DBusObjectSubtree*
00823 _dbus_object_subtree_new (const char *name,
00824 const DBusObjectPathVTable *vtable,
00825 void *user_data)
00826 {
00827 DBusObjectSubtree *subtree;
00828
00829 subtree = allocate_subtree_object (name);
00830 if (subtree == NULL)
00831 goto oom;
00832
00833 _dbus_assert (name != NULL);
00834
00835 subtree->parent = NULL;
00836
00837 if (vtable)
00838 {
00839 subtree->message_function = vtable->message_function;
00840 subtree->unregister_function = vtable->unregister_function;
00841 }
00842 else
00843 {
00844 subtree->message_function = NULL;
00845 subtree->unregister_function = NULL;
00846 }
00847
00848 subtree->user_data = user_data;
00849 subtree->refcount.value = 1;
00850 subtree->subtrees = NULL;
00851 subtree->n_subtrees = 0;
00852 subtree->subtrees_sorted = TRUE;
00853
00854 return subtree;
00855
00856 oom:
00857 if (subtree)
00858 {
00859 dbus_free (subtree);
00860 }
00861
00862 return NULL;
00863 }
00864
00865 static DBusObjectSubtree *
00866 _dbus_object_subtree_ref (DBusObjectSubtree *subtree)
00867 {
00868 _dbus_assert (subtree->refcount.value > 0);
00869 _dbus_atomic_inc (&subtree->refcount);
00870
00871 return subtree;
00872 }
00873
00874 static void
00875 _dbus_object_subtree_unref (DBusObjectSubtree *subtree)
00876 {
00877 _dbus_assert (subtree->refcount.value > 0);
00878
00879 if (_dbus_atomic_dec (&subtree->refcount) == 1)
00880 {
00881 _dbus_assert (subtree->unregister_function == NULL);
00882 _dbus_assert (subtree->message_function == NULL);
00883
00884 dbus_free (subtree->subtrees);
00885 dbus_free (subtree);
00886 }
00887 }
00888
00899 dbus_bool_t
00900 _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
00901 const char **parent_path,
00902 char ***child_entries)
00903 {
00904 dbus_bool_t result;
00905
00906 result = _dbus_object_tree_list_registered_unlocked (tree,
00907 parent_path,
00908 child_entries);
00909
00910 #ifdef DBUS_BUILD_TESTS
00911 if (tree->connection)
00912 #endif
00913 _dbus_connection_unlock (tree->connection);
00914
00915 return result;
00916 }
00917
00920 #ifdef DBUS_BUILD_TESTS
00921 #include "dbus-test.h"
00922 #include <stdio.h>
00923
00924 static char*
00925 flatten_path (const char **path)
00926 {
00927 DBusString str;
00928 int i;
00929 char *s;
00930
00931 if (!_dbus_string_init (&str))
00932 return NULL;
00933
00934 i = 0;
00935 while (path[i])
00936 {
00937 if (!_dbus_string_append_byte (&str, '/'))
00938 goto nomem;
00939
00940 if (!_dbus_string_append (&str, path[i]))
00941 goto nomem;
00942
00943 ++i;
00944 }
00945
00946 if (!_dbus_string_steal_data (&str, &s))
00947 goto nomem;
00948
00949 _dbus_string_free (&str);
00950
00951 return s;
00952
00953 nomem:
00954 _dbus_string_free (&str);
00955 return NULL;
00956 }
00957
00958
00959
00960 static dbus_bool_t
00961 path_contains (const char **container,
00962 const char **child)
00963 {
00964 int i;
00965
00966 i = 0;
00967 while (child[i] != NULL)
00968 {
00969 int v;
00970
00971 if (container[i] == NULL)
00972 return TRUE;
00973
00974
00975
00976
00977 _dbus_assert (container[i] != NULL);
00978 _dbus_assert (child[i] != NULL);
00979
00980 v = strcmp (container[i], child[i]);
00981
00982 if (v != 0)
00983 return FALSE;
00984
00985
00986
00987 ++i;
00988 }
00989
00990
00991
00992
00993 if (container[i] == NULL)
00994 return TRUE;
00995 else
00996 return FALSE;
00997 }
00998
00999 static void
01000 spew_subtree_recurse (DBusObjectSubtree *subtree,
01001 int indent)
01002 {
01003 int i;
01004
01005 i = 0;
01006 while (i < indent)
01007 {
01008 _dbus_verbose (" ");
01009 ++i;
01010 }
01011
01012 _dbus_verbose ("%s (%d children)\n",
01013 subtree->name, subtree->n_subtrees);
01014
01015 i = 0;
01016 while (i < subtree->n_subtrees)
01017 {
01018 spew_subtree_recurse (subtree->subtrees[i], indent + 2);
01019
01020 ++i;
01021 }
01022 }
01023
01024 static void
01025 spew_tree (DBusObjectTree *tree)
01026 {
01027 spew_subtree_recurse (tree->root, 0);
01028 }
01029
01033 typedef struct
01034 {
01035 const char **path;
01036 dbus_bool_t message_handled;
01037 dbus_bool_t handler_unregistered;
01039 } TreeTestData;
01040
01041
01042 static void
01043 test_unregister_function (DBusConnection *connection,
01044 void *user_data)
01045 {
01046 TreeTestData *ttd = user_data;
01047
01048 ttd->handler_unregistered = TRUE;
01049 }
01050
01051 static DBusHandlerResult
01052 test_message_function (DBusConnection *connection,
01053 DBusMessage *message,
01054 void *user_data)
01055 {
01056 TreeTestData *ttd = user_data;
01057
01058 ttd->message_handled = TRUE;
01059
01060 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
01061 }
01062
01063 static dbus_bool_t
01064 do_register (DBusObjectTree *tree,
01065 const char **path,
01066 int i,
01067 TreeTestData *tree_test_data)
01068 {
01069 DBusObjectPathVTable vtable = { test_unregister_function,
01070 test_message_function, NULL };
01071
01072 tree_test_data[i].message_handled = FALSE;
01073 tree_test_data[i].handler_unregistered = FALSE;
01074 tree_test_data[i].path = path;
01075
01076 if (!_dbus_object_tree_register (tree, TRUE, path,
01077 &vtable,
01078 &tree_test_data[i]))
01079 return FALSE;
01080
01081 return TRUE;
01082 }
01083
01084 static dbus_bool_t
01085 do_test_dispatch (DBusObjectTree *tree,
01086 const char **path,
01087 int i,
01088 TreeTestData *tree_test_data,
01089 int n_test_data)
01090 {
01091 DBusMessage *message;
01092 int j;
01093 DBusHandlerResult result;
01094 char *flat;
01095
01096 message = NULL;
01097
01098 flat = flatten_path (path);
01099 if (flat == NULL)
01100 goto oom;
01101
01102 message = dbus_message_new_method_call (NULL,
01103 flat,
01104 "org.freedesktop.TestInterface",
01105 "Foo");
01106 dbus_free (flat);
01107 if (message == NULL)
01108 goto oom;
01109
01110 j = 0;
01111 while (j < n_test_data)
01112 {
01113 tree_test_data[j].message_handled = FALSE;
01114 ++j;
01115 }
01116
01117 result = _dbus_object_tree_dispatch_and_unlock (tree, message);
01118 if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
01119 goto oom;
01120
01121 _dbus_assert (tree_test_data[i].message_handled);
01122
01123 j = 0;
01124 while (j < n_test_data)
01125 {
01126 if (tree_test_data[j].message_handled)
01127 _dbus_assert (path_contains (tree_test_data[j].path,
01128 path));
01129 else
01130 _dbus_assert (!path_contains (tree_test_data[j].path,
01131 path));
01132
01133 ++j;
01134 }
01135
01136 dbus_message_unref (message);
01137
01138 return TRUE;
01139
01140 oom:
01141 if (message)
01142 dbus_message_unref (message);
01143 return FALSE;
01144 }
01145
01146 static dbus_bool_t
01147 object_tree_test_iteration (void *data)
01148 {
01149 const char *path1[] = { "foo", NULL };
01150 const char *path2[] = { "foo", "bar", NULL };
01151 const char *path3[] = { "foo", "bar", "baz", NULL };
01152 const char *path4[] = { "foo", "bar", "boo", NULL };
01153 const char *path5[] = { "blah", NULL };
01154 const char *path6[] = { "blah", "boof", NULL };
01155 const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL };
01156 const char *path8[] = { "childless", NULL };
01157 DBusObjectTree *tree;
01158 TreeTestData tree_test_data[8];
01159 int i;
01160
01161 tree = NULL;
01162
01163 tree = _dbus_object_tree_new (NULL);
01164 if (tree == NULL)
01165 goto out;
01166
01167 if (!do_register (tree, path1, 0, tree_test_data))
01168 goto out;
01169
01170 _dbus_assert (find_subtree (tree, path1, NULL));
01171 _dbus_assert (!find_subtree (tree, path2, NULL));
01172 _dbus_assert (!find_subtree (tree, path3, NULL));
01173 _dbus_assert (!find_subtree (tree, path4, NULL));
01174 _dbus_assert (!find_subtree (tree, path5, NULL));
01175 _dbus_assert (!find_subtree (tree, path6, NULL));
01176 _dbus_assert (!find_subtree (tree, path7, NULL));
01177 _dbus_assert (!find_subtree (tree, path8, NULL));
01178
01179 _dbus_assert (find_handler (tree, path1));
01180 _dbus_assert (find_handler (tree, path2));
01181 _dbus_assert (find_handler (tree, path3));
01182 _dbus_assert (find_handler (tree, path4));
01183 _dbus_assert (find_handler (tree, path5) == tree->root);
01184 _dbus_assert (find_handler (tree, path6) == tree->root);
01185 _dbus_assert (find_handler (tree, path7) == tree->root);
01186 _dbus_assert (find_handler (tree, path8) == tree->root);
01187
01188 if (!do_register (tree, path2, 1, tree_test_data))
01189 goto out;
01190
01191 _dbus_assert (find_subtree (tree, path1, NULL));
01192 _dbus_assert (find_subtree (tree, path2, NULL));
01193 _dbus_assert (!find_subtree (tree, path3, NULL));
01194 _dbus_assert (!find_subtree (tree, path4, NULL));
01195 _dbus_assert (!find_subtree (tree, path5, NULL));
01196 _dbus_assert (!find_subtree (tree, path6, NULL));
01197 _dbus_assert (!find_subtree (tree, path7, NULL));
01198 _dbus_assert (!find_subtree (tree, path8, NULL));
01199
01200 if (!do_register (tree, path3, 2, tree_test_data))
01201 goto out;
01202
01203 _dbus_assert (find_subtree (tree, path1, NULL));
01204 _dbus_assert (find_subtree (tree, path2, NULL));
01205 _dbus_assert (find_subtree (tree, path3, NULL));
01206 _dbus_assert (!find_subtree (tree, path4, NULL));
01207 _dbus_assert (!find_subtree (tree, path5, NULL));
01208 _dbus_assert (!find_subtree (tree, path6, NULL));
01209 _dbus_assert (!find_subtree (tree, path7, NULL));
01210 _dbus_assert (!find_subtree (tree, path8, NULL));
01211
01212 if (!do_register (tree, path4, 3, tree_test_data))
01213 goto out;
01214
01215 _dbus_assert (find_subtree (tree, path1, NULL));
01216 _dbus_assert (find_subtree (tree, path2, NULL));
01217 _dbus_assert (find_subtree (tree, path3, NULL));
01218 _dbus_assert (find_subtree (tree, path4, NULL));
01219 _dbus_assert (!find_subtree (tree, path5, NULL));
01220 _dbus_assert (!find_subtree (tree, path6, NULL));
01221 _dbus_assert (!find_subtree (tree, path7, NULL));
01222 _dbus_assert (!find_subtree (tree, path8, NULL));
01223
01224 if (!do_register (tree, path5, 4, tree_test_data))
01225 goto out;
01226
01227 _dbus_assert (find_subtree (tree, path1, NULL));
01228 _dbus_assert (find_subtree (tree, path2, NULL));
01229 _dbus_assert (find_subtree (tree, path3, NULL));
01230 _dbus_assert (find_subtree (tree, path4, NULL));
01231 _dbus_assert (find_subtree (tree, path5, NULL));
01232 _dbus_assert (!find_subtree (tree, path6, NULL));
01233 _dbus_assert (!find_subtree (tree, path7, NULL));
01234 _dbus_assert (!find_subtree (tree, path8, NULL));
01235
01236 _dbus_assert (find_handler (tree, path1) != tree->root);
01237 _dbus_assert (find_handler (tree, path2) != tree->root);
01238 _dbus_assert (find_handler (tree, path3) != tree->root);
01239 _dbus_assert (find_handler (tree, path4) != tree->root);
01240 _dbus_assert (find_handler (tree, path5) != tree->root);
01241 _dbus_assert (find_handler (tree, path6) != tree->root);
01242 _dbus_assert (find_handler (tree, path7) != tree->root);
01243 _dbus_assert (find_handler (tree, path8) == tree->root);
01244
01245 if (!do_register (tree, path6, 5, tree_test_data))
01246 goto out;
01247
01248 _dbus_assert (find_subtree (tree, path1, NULL));
01249 _dbus_assert (find_subtree (tree, path2, NULL));
01250 _dbus_assert (find_subtree (tree, path3, NULL));
01251 _dbus_assert (find_subtree (tree, path4, NULL));
01252 _dbus_assert (find_subtree (tree, path5, NULL));
01253 _dbus_assert (find_subtree (tree, path6, NULL));
01254 _dbus_assert (!find_subtree (tree, path7, NULL));
01255 _dbus_assert (!find_subtree (tree, path8, NULL));
01256
01257 if (!do_register (tree, path7, 6, tree_test_data))
01258 goto out;
01259
01260 _dbus_assert (find_subtree (tree, path1, NULL));
01261 _dbus_assert (find_subtree (tree, path2, NULL));
01262 _dbus_assert (find_subtree (tree, path3, NULL));
01263 _dbus_assert (find_subtree (tree, path4, NULL));
01264 _dbus_assert (find_subtree (tree, path5, NULL));
01265 _dbus_assert (find_subtree (tree, path6, NULL));
01266 _dbus_assert (find_subtree (tree, path7, NULL));
01267 _dbus_assert (!find_subtree (tree, path8, NULL));
01268
01269 if (!do_register (tree, path8, 7, tree_test_data))
01270 goto out;
01271
01272 _dbus_assert (find_subtree (tree, path1, NULL));
01273 _dbus_assert (find_subtree (tree, path2, NULL));
01274 _dbus_assert (find_subtree (tree, path3, NULL));
01275 _dbus_assert (find_subtree (tree, path4, NULL));
01276 _dbus_assert (find_subtree (tree, path5, NULL));
01277 _dbus_assert (find_subtree (tree, path6, NULL));
01278 _dbus_assert (find_subtree (tree, path7, NULL));
01279 _dbus_assert (find_subtree (tree, path8, NULL));
01280
01281 _dbus_assert (find_handler (tree, path1) != tree->root);
01282 _dbus_assert (find_handler (tree, path2) != tree->root);
01283 _dbus_assert (find_handler (tree, path3) != tree->root);
01284 _dbus_assert (find_handler (tree, path4) != tree->root);
01285 _dbus_assert (find_handler (tree, path5) != tree->root);
01286 _dbus_assert (find_handler (tree, path6) != tree->root);
01287 _dbus_assert (find_handler (tree, path7) != tree->root);
01288 _dbus_assert (find_handler (tree, path8) != tree->root);
01289
01290
01291 _dbus_object_tree_unref (tree);
01292
01293 i = 0;
01294 while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
01295 {
01296 _dbus_assert (tree_test_data[i].handler_unregistered);
01297 _dbus_assert (!tree_test_data[i].message_handled);
01298 ++i;
01299 }
01300
01301
01302 tree = _dbus_object_tree_new (NULL);
01303 if (tree == NULL)
01304 goto out;
01305
01306 if (!do_register (tree, path1, 0, tree_test_data))
01307 goto out;
01308 if (!do_register (tree, path2, 1, tree_test_data))
01309 goto out;
01310 if (!do_register (tree, path3, 2, tree_test_data))
01311 goto out;
01312 if (!do_register (tree, path4, 3, tree_test_data))
01313 goto out;
01314 if (!do_register (tree, path5, 4, tree_test_data))
01315 goto out;
01316 if (!do_register (tree, path6, 5, tree_test_data))
01317 goto out;
01318 if (!do_register (tree, path7, 6, tree_test_data))
01319 goto out;
01320 if (!do_register (tree, path8, 7, tree_test_data))
01321 goto out;
01322
01323 _dbus_object_tree_unregister_and_unlock (tree, path1);
01324
01325 _dbus_assert (!find_subtree (tree, path1, NULL));
01326 _dbus_assert (find_subtree (tree, path2, NULL));
01327 _dbus_assert (find_subtree (tree, path3, NULL));
01328 _dbus_assert (find_subtree (tree, path4, NULL));
01329 _dbus_assert (find_subtree (tree, path5, NULL));
01330 _dbus_assert (find_subtree (tree, path6, NULL));
01331 _dbus_assert (find_subtree (tree, path7, NULL));
01332 _dbus_assert (find_subtree (tree, path8, NULL));
01333
01334 _dbus_object_tree_unregister_and_unlock (tree, path2);
01335
01336 _dbus_assert (!find_subtree (tree, path1, NULL));
01337 _dbus_assert (!find_subtree (tree, path2, NULL));
01338 _dbus_assert (find_subtree (tree, path3, NULL));
01339 _dbus_assert (find_subtree (tree, path4, NULL));
01340 _dbus_assert (find_subtree (tree, path5, NULL));
01341 _dbus_assert (find_subtree (tree, path6, NULL));
01342 _dbus_assert (find_subtree (tree, path7, NULL));
01343 _dbus_assert (find_subtree (tree, path8, NULL));
01344
01345 _dbus_object_tree_unregister_and_unlock (tree, path3);
01346
01347 _dbus_assert (!find_subtree (tree, path1, NULL));
01348 _dbus_assert (!find_subtree (tree, path2, NULL));
01349 _dbus_assert (!find_subtree (tree, path3, NULL));
01350 _dbus_assert (find_subtree (tree, path4, NULL));
01351 _dbus_assert (find_subtree (tree, path5, NULL));
01352 _dbus_assert (find_subtree (tree, path6, NULL));
01353 _dbus_assert (find_subtree (tree, path7, NULL));
01354 _dbus_assert (find_subtree (tree, path8, NULL));
01355
01356 _dbus_object_tree_unregister_and_unlock (tree, path4);
01357
01358 _dbus_assert (!find_subtree (tree, path1, NULL));
01359 _dbus_assert (!find_subtree (tree, path2, NULL));
01360 _dbus_assert (!find_subtree (tree, path3, NULL));
01361 _dbus_assert (!find_subtree (tree, path4, NULL));
01362 _dbus_assert (find_subtree (tree, path5, NULL));
01363 _dbus_assert (find_subtree (tree, path6, NULL));
01364 _dbus_assert (find_subtree (tree, path7, NULL));
01365 _dbus_assert (find_subtree (tree, path8, NULL));
01366
01367 _dbus_object_tree_unregister_and_unlock (tree, path5);
01368
01369 _dbus_assert (!find_subtree (tree, path1, NULL));
01370 _dbus_assert (!find_subtree (tree, path2, NULL));
01371 _dbus_assert (!find_subtree (tree, path3, NULL));
01372 _dbus_assert (!find_subtree (tree, path4, NULL));
01373 _dbus_assert (!find_subtree (tree, path5, NULL));
01374 _dbus_assert (find_subtree (tree, path6, NULL));
01375 _dbus_assert (find_subtree (tree, path7, NULL));
01376 _dbus_assert (find_subtree (tree, path8, NULL));
01377
01378 _dbus_object_tree_unregister_and_unlock (tree, path6);
01379
01380 _dbus_assert (!find_subtree (tree, path1, NULL));
01381 _dbus_assert (!find_subtree (tree, path2, NULL));
01382 _dbus_assert (!find_subtree (tree, path3, NULL));
01383 _dbus_assert (!find_subtree (tree, path4, NULL));
01384 _dbus_assert (!find_subtree (tree, path5, NULL));
01385 _dbus_assert (!find_subtree (tree, path6, NULL));
01386 _dbus_assert (find_subtree (tree, path7, NULL));
01387 _dbus_assert (find_subtree (tree, path8, NULL));
01388
01389 _dbus_object_tree_unregister_and_unlock (tree, path7);
01390
01391 _dbus_assert (!find_subtree (tree, path1, NULL));
01392 _dbus_assert (!find_subtree (tree, path2, NULL));
01393 _dbus_assert (!find_subtree (tree, path3, NULL));
01394 _dbus_assert (!find_subtree (tree, path4, NULL));
01395 _dbus_assert (!find_subtree (tree, path5, NULL));
01396 _dbus_assert (!find_subtree (tree, path6, NULL));
01397 _dbus_assert (!find_subtree (tree, path7, NULL));
01398 _dbus_assert (find_subtree (tree, path8, NULL));
01399
01400 _dbus_object_tree_unregister_and_unlock (tree, path8);
01401
01402 _dbus_assert (!find_subtree (tree, path1, NULL));
01403 _dbus_assert (!find_subtree (tree, path2, NULL));
01404 _dbus_assert (!find_subtree (tree, path3, NULL));
01405 _dbus_assert (!find_subtree (tree, path4, NULL));
01406 _dbus_assert (!find_subtree (tree, path5, NULL));
01407 _dbus_assert (!find_subtree (tree, path6, NULL));
01408 _dbus_assert (!find_subtree (tree, path7, NULL));
01409 _dbus_assert (!find_subtree (tree, path8, NULL));
01410
01411 i = 0;
01412 while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
01413 {
01414 _dbus_assert (tree_test_data[i].handler_unregistered);
01415 _dbus_assert (!tree_test_data[i].message_handled);
01416 ++i;
01417 }
01418
01419
01420
01421 if (!do_register (tree, path1, 0, tree_test_data))
01422 goto out;
01423 if (!do_register (tree, path2, 1, tree_test_data))
01424 goto out;
01425 if (!do_register (tree, path3, 2, tree_test_data))
01426 goto out;
01427 if (!do_register (tree, path4, 3, tree_test_data))
01428 goto out;
01429 if (!do_register (tree, path5, 4, tree_test_data))
01430 goto out;
01431 if (!do_register (tree, path6, 5, tree_test_data))
01432 goto out;
01433 if (!do_register (tree, path7, 6, tree_test_data))
01434 goto out;
01435 if (!do_register (tree, path8, 7, tree_test_data))
01436 goto out;
01437
01438 #if 0
01439 spew_tree (tree);
01440 #endif
01441
01442 if (!do_test_dispatch (tree, path1, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01443 goto out;
01444 if (!do_test_dispatch (tree, path2, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01445 goto out;
01446 if (!do_test_dispatch (tree, path3, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01447 goto out;
01448 if (!do_test_dispatch (tree, path4, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01449 goto out;
01450 if (!do_test_dispatch (tree, path5, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01451 goto out;
01452 if (!do_test_dispatch (tree, path6, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01453 goto out;
01454 if (!do_test_dispatch (tree, path7, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01455 goto out;
01456 if (!do_test_dispatch (tree, path8, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
01457 goto out;
01458
01459 out:
01460 if (tree)
01461 {
01462
01463 _dbus_object_tree_ref (tree);
01464 _dbus_object_tree_unref (tree);
01465 _dbus_object_tree_unref (tree);
01466 }
01467
01468 return TRUE;
01469 }
01470
01476 dbus_bool_t
01477 _dbus_object_tree_test (void)
01478 {
01479 _dbus_test_oom_handling ("object tree",
01480 object_tree_test_iteration,
01481 NULL);
01482
01483 return TRUE;
01484 }
01485
01486 #endif