Home | History | Annotate | Download | only in dbus
      1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
      2 /* dbus-object-tree.c  DBusObjectTree (internals of DBusConnection)
      3  *
      4  * Copyright (C) 2003, 2005  Red Hat Inc.
      5  *
      6  * Licensed under the Academic Free License version 2.1
      7  *
      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.
     12  *
     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.
     17  *
     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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #include <config.h>
     25 #include "dbus-object-tree.h"
     26 #include "dbus-connection-internal.h"
     27 #include "dbus-internals.h"
     28 #include "dbus-hash.h"
     29 #include "dbus-protocol.h"
     30 #include "dbus-string.h"
     31 #include <string.h>
     32 #include <stdlib.h>
     33 
     34 /**
     35  * @defgroup DBusObjectTree A hierarchy of objects with container-contained relationship
     36  * @ingroup  DBusInternals
     37  * @brief DBusObjectTree is used by DBusConnection to track the object tree
     38  *
     39  * Types and functions related to DBusObjectTree. These
     40  * are all library-internal.
     41  *
     42  * @{
     43  */
     44 
     45 /** Subnode of the object hierarchy */
     46 typedef struct DBusObjectSubtree DBusObjectSubtree;
     47 
     48 static DBusObjectSubtree* _dbus_object_subtree_new   (const char                  *name,
     49                                                       const DBusObjectPathVTable  *vtable,
     50                                                       void                        *user_data);
     51 static DBusObjectSubtree* _dbus_object_subtree_ref   (DBusObjectSubtree           *subtree);
     52 static void               _dbus_object_subtree_unref (DBusObjectSubtree           *subtree);
     53 
     54 /**
     55  * Internals of DBusObjectTree
     56  */
     57 struct DBusObjectTree
     58 {
     59   int                 refcount;   /**< Reference count */
     60   DBusConnection     *connection; /**< Connection this tree belongs to */
     61 
     62   DBusObjectSubtree  *root;       /**< Root of the tree ("/" node) */
     63 };
     64 
     65 /**
     66  * Struct representing a single registered subtree handler, or node
     67  * that's a parent of a registered subtree handler. If
     68  * message_function != NULL there's actually a handler at this node.
     69  */
     70 struct DBusObjectSubtree
     71 {
     72   DBusAtomic                         refcount;            /**< Reference count */
     73   DBusObjectSubtree                 *parent;              /**< Parent node */
     74   DBusObjectPathUnregisterFunction   unregister_function; /**< Function to call on unregister */
     75   DBusObjectPathMessageFunction      message_function;    /**< Function to handle messages */
     76   void                              *user_data;           /**< Data for functions */
     77   DBusObjectSubtree                **subtrees;            /**< Child nodes */
     78   int                                n_subtrees;          /**< Number of child nodes */
     79   int                                max_subtrees;        /**< Number of allocated entries in subtrees */
     80   unsigned int                       invoke_as_fallback : 1; /**< Whether to invoke message_function when child nodes don't handle the message */
     81   char                               name[1]; /**< Allocated as large as necessary */
     82 };
     83 
     84 /**
     85  * Creates a new object tree, representing a mapping from paths
     86  * to handler vtables.
     87  *
     88  * @param connection the connection this tree belongs to
     89  * @returns the new tree or #NULL if no memory
     90  */
     91 DBusObjectTree*
     92 _dbus_object_tree_new (DBusConnection *connection)
     93 {
     94   DBusObjectTree *tree;
     95 
     96   /* the connection passed in here isn't fully constructed,
     97    * so don't do anything more than store a pointer to
     98    * it
     99    */
    100 
    101   tree = dbus_new0 (DBusObjectTree, 1);
    102   if (tree == NULL)
    103     goto oom;
    104 
    105   tree->refcount = 1;
    106   tree->connection = connection;
    107   tree->root = _dbus_object_subtree_new ("/", NULL, NULL);
    108   if (tree->root == NULL)
    109     goto oom;
    110   tree->root->invoke_as_fallback = TRUE;
    111 
    112   return tree;
    113 
    114  oom:
    115   if (tree)
    116     {
    117       dbus_free (tree);
    118     }
    119 
    120   return NULL;
    121 }
    122 
    123 /**
    124  * Increment the reference count
    125  * @param tree the object tree
    126  * @returns the object tree
    127  */
    128 DBusObjectTree *
    129 _dbus_object_tree_ref (DBusObjectTree *tree)
    130 {
    131   _dbus_assert (tree->refcount > 0);
    132 
    133   tree->refcount += 1;
    134 
    135   return tree;
    136 }
    137 
    138 /**
    139  * Decrement the reference count
    140  * @param tree the object tree
    141  */
    142 void
    143 _dbus_object_tree_unref (DBusObjectTree *tree)
    144 {
    145   _dbus_assert (tree->refcount > 0);
    146 
    147   tree->refcount -= 1;
    148 
    149   if (tree->refcount == 0)
    150     {
    151       _dbus_object_tree_free_all_unlocked (tree);
    152 
    153       dbus_free (tree);
    154     }
    155 }
    156 
    157 /** Set to 1 to get a bunch of debug spew about finding the
    158  * subtree nodes
    159  */
    160 #define VERBOSE_FIND 0
    161 
    162 static DBusObjectSubtree*
    163 find_subtree_recurse (DBusObjectSubtree  *subtree,
    164                       const char        **path,
    165                       dbus_bool_t         create_if_not_found,
    166                       int                *index_in_parent,
    167                       dbus_bool_t        *exact_match)
    168 {
    169   int i, j;
    170   dbus_bool_t return_deepest_match;
    171 
    172   return_deepest_match = exact_match != NULL;
    173 
    174   _dbus_assert (!(return_deepest_match && create_if_not_found));
    175 
    176   if (path[0] == NULL)
    177     {
    178 #if VERBOSE_FIND
    179       _dbus_verbose ("  path exhausted, returning %s\n",
    180                      subtree->name);
    181 #endif
    182       if (exact_match != NULL)
    183 	*exact_match = TRUE;
    184       return subtree;
    185     }
    186 
    187 #if VERBOSE_FIND
    188   _dbus_verbose ("  searching children of %s for %s\n",
    189                  subtree->name, path[0]);
    190 #endif
    191 
    192   i = 0;
    193   j = subtree->n_subtrees;
    194   while (i < j)
    195     {
    196       int k, v;
    197 
    198       k = (i + j) / 2;
    199       v = strcmp (path[0], subtree->subtrees[k]->name);
    200 
    201 #if VERBOSE_FIND
    202       _dbus_verbose ("  %s cmp %s = %d\n",
    203                      path[0], subtree->subtrees[k]->name,
    204                      v);
    205 #endif
    206 
    207       if (v == 0)
    208         {
    209           if (index_in_parent)
    210             {
    211 #if VERBOSE_FIND
    212               _dbus_verbose ("  storing parent index %d\n", k);
    213 #endif
    214               *index_in_parent = k;
    215             }
    216 
    217           if (return_deepest_match)
    218             {
    219               DBusObjectSubtree *next;
    220 
    221               next = find_subtree_recurse (subtree->subtrees[k],
    222                                            &path[1], create_if_not_found,
    223                                            index_in_parent, exact_match);
    224               if (next == NULL &&
    225                   subtree->invoke_as_fallback)
    226                 {
    227 #if VERBOSE_FIND
    228                   _dbus_verbose ("  no deeper match found, returning %s\n",
    229                                  subtree->name);
    230 #endif
    231 		  if (exact_match != NULL)
    232 		    *exact_match = FALSE;
    233                   return subtree;
    234                 }
    235               else
    236                 return next;
    237             }
    238           else
    239             return find_subtree_recurse (subtree->subtrees[k],
    240                                          &path[1], create_if_not_found,
    241                                          index_in_parent, exact_match);
    242         }
    243       else if (v < 0)
    244         {
    245           j = k;
    246         }
    247       else
    248         {
    249           i = k + 1;
    250         }
    251     }
    252 
    253 #if VERBOSE_FIND
    254   _dbus_verbose ("  no match found, current tree %s, create_if_not_found = %d\n",
    255                  subtree->name, create_if_not_found);
    256 #endif
    257 
    258   if (create_if_not_found)
    259     {
    260       DBusObjectSubtree* child;
    261       int child_pos, new_n_subtrees;
    262 
    263 #if VERBOSE_FIND
    264       _dbus_verbose ("  creating subtree %s\n",
    265                      path[0]);
    266 #endif
    267 
    268       child = _dbus_object_subtree_new (path[0],
    269                                         NULL, NULL);
    270       if (child == NULL)
    271         return NULL;
    272 
    273       new_n_subtrees = subtree->n_subtrees + 1;
    274       if (new_n_subtrees > subtree->max_subtrees)
    275         {
    276           int new_max_subtrees;
    277           DBusObjectSubtree **new_subtrees;
    278 
    279           new_max_subtrees = subtree->max_subtrees == 0 ? 1 : 2 * subtree->max_subtrees;
    280           new_subtrees = dbus_realloc (subtree->subtrees,
    281                                        new_max_subtrees * sizeof (DBusObjectSubtree*));
    282           if (new_subtrees == NULL)
    283             {
    284               _dbus_object_subtree_unref (child);
    285               return NULL;
    286             }
    287           subtree->subtrees = new_subtrees;
    288           subtree->max_subtrees = new_max_subtrees;
    289         }
    290 
    291       /* The binary search failed, so i == j points to the
    292          place the child should be inserted. */
    293       child_pos = i;
    294       _dbus_assert (child_pos < new_n_subtrees &&
    295                     new_n_subtrees <= subtree->max_subtrees);
    296       if (child_pos + 1 < new_n_subtrees)
    297 	{
    298 	  memmove (&subtree->subtrees[child_pos+1],
    299 		   &subtree->subtrees[child_pos],
    300 		   (new_n_subtrees - child_pos - 1) *
    301 		   sizeof subtree->subtrees[0]);
    302 	}
    303       subtree->subtrees[child_pos] = child;
    304 
    305       if (index_in_parent)
    306         *index_in_parent = child_pos;
    307       subtree->n_subtrees = new_n_subtrees;
    308       child->parent = subtree;
    309 
    310       return find_subtree_recurse (child,
    311                                    &path[1], create_if_not_found,
    312                                    index_in_parent, exact_match);
    313     }
    314   else
    315     {
    316       if (exact_match != NULL)
    317 	*exact_match = FALSE;
    318       return (return_deepest_match && subtree->invoke_as_fallback) ? subtree : NULL;
    319     }
    320 }
    321 
    322 static DBusObjectSubtree*
    323 find_subtree (DBusObjectTree *tree,
    324               const char    **path,
    325               int            *index_in_parent)
    326 {
    327   DBusObjectSubtree *subtree;
    328 
    329 #if VERBOSE_FIND
    330   _dbus_verbose ("Looking for exact registered subtree\n");
    331 #endif
    332 
    333   subtree = find_subtree_recurse (tree->root, path, FALSE, index_in_parent, NULL);
    334 
    335   if (subtree && subtree->message_function == NULL)
    336     return NULL;
    337   else
    338     return subtree;
    339 }
    340 
    341 static DBusObjectSubtree*
    342 lookup_subtree (DBusObjectTree *tree,
    343                 const char    **path)
    344 {
    345 #if VERBOSE_FIND
    346   _dbus_verbose ("Looking for subtree\n");
    347 #endif
    348   return find_subtree_recurse (tree->root, path, FALSE, NULL, NULL);
    349 }
    350 
    351 static DBusObjectSubtree*
    352 find_handler (DBusObjectTree *tree,
    353               const char    **path,
    354               dbus_bool_t    *exact_match)
    355 {
    356 #if VERBOSE_FIND
    357   _dbus_verbose ("Looking for deepest handler\n");
    358 #endif
    359   _dbus_assert (exact_match != NULL);
    360 
    361   *exact_match = FALSE; /* ensure always initialized */
    362 
    363   return find_subtree_recurse (tree->root, path, FALSE, NULL, exact_match);
    364 }
    365 
    366 static DBusObjectSubtree*
    367 ensure_subtree (DBusObjectTree *tree,
    368                 const char    **path)
    369 {
    370 #if VERBOSE_FIND
    371   _dbus_verbose ("Ensuring subtree\n");
    372 #endif
    373   return find_subtree_recurse (tree->root, path, TRUE, NULL, NULL);
    374 }
    375 
    376 static char *flatten_path (const char **path);
    377 
    378 /**
    379  * Registers a new subtree in the global object tree.
    380  *
    381  * @param tree the global object tree
    382  * @param fallback #TRUE to handle messages to children of this path
    383  * @param path NULL-terminated array of path elements giving path to subtree
    384  * @param vtable the vtable used to traverse this subtree
    385  * @param user_data user data to pass to methods in the vtable
    386  * @param error address where an error can be returned
    387  * @returns #FALSE if an error (#DBUS_ERROR_NO_MEMORY or
    388  *    #DBUS_ERROR_OBJECT_PATH_IN_USE) is reported
    389  */
    390 dbus_bool_t
    391 _dbus_object_tree_register (DBusObjectTree              *tree,
    392                             dbus_bool_t                  fallback,
    393                             const char                 **path,
    394                             const DBusObjectPathVTable  *vtable,
    395                             void                        *user_data,
    396                             DBusError                   *error)
    397 {
    398   DBusObjectSubtree  *subtree;
    399 
    400   _dbus_assert (tree != NULL);
    401   _dbus_assert (vtable->message_function != NULL);
    402   _dbus_assert (path != NULL);
    403 
    404   subtree = ensure_subtree (tree, path);
    405   if (subtree == NULL)
    406     {
    407       _DBUS_SET_OOM (error);
    408       return FALSE;
    409     }
    410 
    411   if (subtree->message_function != NULL)
    412     {
    413       if (error != NULL)
    414         {
    415           char *complete_path = flatten_path (path);
    416 
    417           dbus_set_error (error, DBUS_ERROR_OBJECT_PATH_IN_USE,
    418                           "A handler is already registered for %s",
    419                           complete_path ? complete_path
    420                                         : "(cannot represent path: out of memory!)");
    421 
    422           dbus_free (complete_path);
    423         }
    424 
    425       return FALSE;
    426     }
    427 
    428   subtree->message_function = vtable->message_function;
    429   subtree->unregister_function = vtable->unregister_function;
    430   subtree->user_data = user_data;
    431   subtree->invoke_as_fallback = fallback != FALSE;
    432 
    433   return TRUE;
    434 }
    435 
    436 /**
    437  * Unregisters an object subtree that was registered with the
    438  * same path.
    439  *
    440  * @param tree the global object tree
    441  * @param path path to the subtree (same as the one passed to _dbus_object_tree_register())
    442  */
    443 void
    444 _dbus_object_tree_unregister_and_unlock (DBusObjectTree          *tree,
    445                                          const char             **path)
    446 {
    447   int i;
    448   DBusObjectSubtree *subtree;
    449   DBusObjectPathUnregisterFunction unregister_function;
    450   void *user_data;
    451   DBusConnection *connection;
    452 
    453   _dbus_assert (path != NULL);
    454 
    455   unregister_function = NULL;
    456   user_data = NULL;
    457 
    458   subtree = find_subtree (tree, path, &i);
    459 
    460 #ifndef DBUS_DISABLE_CHECKS
    461   if (subtree == NULL)
    462     {
    463       _dbus_warn ("Attempted to unregister path (path[0] = %s path[1] = %s) which isn't registered\n",
    464                   path[0] ? path[0] : "null",
    465                   path[1] ? path[1] : "null");
    466       goto unlock;
    467     }
    468 #else
    469   _dbus_assert (subtree != NULL);
    470 #endif
    471 
    472   _dbus_assert (subtree->parent == NULL ||
    473                 (i >= 0 && subtree->parent->subtrees[i] == subtree));
    474 
    475   subtree->message_function = NULL;
    476 
    477   unregister_function = subtree->unregister_function;
    478   user_data = subtree->user_data;
    479 
    480   subtree->unregister_function = NULL;
    481   subtree->user_data = NULL;
    482 
    483   /* If we have no subtrees of our own, remove from
    484    * our parent (FIXME could also be more aggressive
    485    * and remove our parent if it becomes empty)
    486    */
    487   if (subtree->parent && subtree->n_subtrees == 0)
    488     {
    489       /* assumes a 0-byte memmove is OK */
    490       memmove (&subtree->parent->subtrees[i],
    491                &subtree->parent->subtrees[i+1],
    492                (subtree->parent->n_subtrees - i - 1) *
    493                sizeof (subtree->parent->subtrees[0]));
    494       subtree->parent->n_subtrees -= 1;
    495 
    496       subtree->parent = NULL;
    497 
    498       _dbus_object_subtree_unref (subtree);
    499     }
    500   subtree = NULL;
    501 
    502 unlock:
    503   connection = tree->connection;
    504 
    505   /* Unlock and call application code */
    506 #ifdef DBUS_BUILD_TESTS
    507   if (connection)
    508 #endif
    509     {
    510       _dbus_connection_ref_unlocked (connection);
    511       _dbus_verbose ("unlock\n");
    512       _dbus_connection_unlock (connection);
    513     }
    514 
    515   if (unregister_function)
    516     (* unregister_function) (connection, user_data);
    517 
    518 #ifdef DBUS_BUILD_TESTS
    519   if (connection)
    520 #endif
    521     dbus_connection_unref (connection);
    522 }
    523 
    524 static void
    525 free_subtree_recurse (DBusConnection    *connection,
    526                       DBusObjectSubtree *subtree)
    527 {
    528   /* Delete them from the end, for slightly
    529    * more robustness against odd reentrancy.
    530    */
    531   while (subtree->n_subtrees > 0)
    532     {
    533       DBusObjectSubtree *child;
    534 
    535       child = subtree->subtrees[subtree->n_subtrees - 1];
    536       subtree->subtrees[subtree->n_subtrees - 1] = NULL;
    537       subtree->n_subtrees -= 1;
    538       child->parent = NULL;
    539 
    540       free_subtree_recurse (connection, child);
    541     }
    542 
    543   /* Call application code */
    544   if (subtree->unregister_function)
    545     (* subtree->unregister_function) (connection,
    546 				      subtree->user_data);
    547 
    548   subtree->message_function = NULL;
    549   subtree->unregister_function = NULL;
    550   subtree->user_data = NULL;
    551 
    552   /* Now free ourselves */
    553   _dbus_object_subtree_unref (subtree);
    554 }
    555 
    556 /**
    557  * Free all the handlers in the tree. Lock on tree's connection
    558  * must not be held.
    559  *
    560  * @param tree the object tree
    561  */
    562 void
    563 _dbus_object_tree_free_all_unlocked (DBusObjectTree *tree)
    564 {
    565   if (tree->root)
    566     free_subtree_recurse (tree->connection,
    567                           tree->root);
    568   tree->root = NULL;
    569 }
    570 
    571 static dbus_bool_t
    572 _dbus_object_tree_list_registered_unlocked (DBusObjectTree *tree,
    573                                             const char    **parent_path,
    574                                             char         ***child_entries)
    575 {
    576   DBusObjectSubtree *subtree;
    577   char **retval;
    578 
    579   _dbus_assert (parent_path != NULL);
    580   _dbus_assert (child_entries != NULL);
    581 
    582   *child_entries = NULL;
    583 
    584   subtree = lookup_subtree (tree, parent_path);
    585   if (subtree == NULL)
    586     {
    587       retval = dbus_new0 (char *, 1);
    588     }
    589   else
    590     {
    591       int i;
    592       retval = dbus_new0 (char*, subtree->n_subtrees + 1);
    593       if (retval == NULL)
    594         goto out;
    595       i = 0;
    596       while (i < subtree->n_subtrees)
    597         {
    598           retval[i] = _dbus_strdup (subtree->subtrees[i]->name);
    599           if (retval[i] == NULL)
    600             {
    601               dbus_free_string_array (retval);
    602               retval = NULL;
    603               goto out;
    604             }
    605           ++i;
    606         }
    607     }
    608 
    609  out:
    610 
    611   *child_entries = retval;
    612   return retval != NULL;
    613 }
    614 
    615 static DBusHandlerResult
    616 handle_default_introspect_and_unlock (DBusObjectTree          *tree,
    617                                       DBusMessage             *message,
    618                                       const char             **path)
    619 {
    620   DBusString xml;
    621   DBusHandlerResult result;
    622   char **children;
    623   int i;
    624   DBusMessage *reply;
    625   DBusMessageIter iter;
    626   const char *v_STRING;
    627   dbus_bool_t already_unlocked;
    628 
    629   /* We have the connection lock here */
    630 
    631   already_unlocked = FALSE;
    632 
    633   _dbus_verbose (" considering default Introspect() handler...\n");
    634 
    635   reply = NULL;
    636 
    637   if (!dbus_message_is_method_call (message,
    638                                     DBUS_INTERFACE_INTROSPECTABLE,
    639                                     "Introspect"))
    640     {
    641 #ifdef DBUS_BUILD_TESTS
    642       if (tree->connection)
    643 #endif
    644         {
    645           _dbus_verbose ("unlock\n");
    646           _dbus_connection_unlock (tree->connection);
    647         }
    648 
    649       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    650     }
    651 
    652   _dbus_verbose (" using default Introspect() handler!\n");
    653 
    654   if (!_dbus_string_init (&xml))
    655     {
    656 #ifdef DBUS_BUILD_TESTS
    657       if (tree->connection)
    658 #endif
    659         {
    660           _dbus_verbose ("unlock\n");
    661           _dbus_connection_unlock (tree->connection);
    662         }
    663 
    664       return DBUS_HANDLER_RESULT_NEED_MEMORY;
    665     }
    666 
    667   result = DBUS_HANDLER_RESULT_NEED_MEMORY;
    668 
    669   children = NULL;
    670   if (!_dbus_object_tree_list_registered_unlocked (tree, path, &children))
    671     goto out;
    672 
    673   if (!_dbus_string_append (&xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE))
    674     goto out;
    675 
    676   if (!_dbus_string_append (&xml, "<node>\n"))
    677     goto out;
    678 
    679   i = 0;
    680   while (children[i] != NULL)
    681     {
    682       if (!_dbus_string_append_printf (&xml, "  <node name=\"%s\"/>\n",
    683                                        children[i]))
    684         goto out;
    685 
    686       ++i;
    687     }
    688 
    689   if (!_dbus_string_append (&xml, "</node>\n"))
    690     goto out;
    691 
    692   reply = dbus_message_new_method_return (message);
    693   if (reply == NULL)
    694     goto out;
    695 
    696   dbus_message_iter_init_append (reply, &iter);
    697   v_STRING = _dbus_string_get_const_data (&xml);
    698   if (!dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &v_STRING))
    699     goto out;
    700 
    701 #ifdef DBUS_BUILD_TESTS
    702   if (tree->connection)
    703 #endif
    704     {
    705       already_unlocked = TRUE;
    706 
    707       if (!_dbus_connection_send_and_unlock (tree->connection, reply, NULL))
    708         goto out;
    709     }
    710 
    711   result = DBUS_HANDLER_RESULT_HANDLED;
    712 
    713  out:
    714 #ifdef DBUS_BUILD_TESTS
    715   if (tree->connection)
    716 #endif
    717     {
    718       if (!already_unlocked)
    719         {
    720           _dbus_verbose ("unlock\n");
    721           _dbus_connection_unlock (tree->connection);
    722         }
    723     }
    724 
    725   _dbus_string_free (&xml);
    726   dbus_free_string_array (children);
    727   if (reply)
    728     dbus_message_unref (reply);
    729 
    730   return result;
    731 }
    732 
    733 /**
    734  * Tries to dispatch a message by directing it to handler for the
    735  * object path listed in the message header, if any. Messages are
    736  * dispatched first to the registered handler that matches the largest
    737  * number of path elements; that is, message to /foo/bar/baz would go
    738  * to the handler for /foo/bar before the one for /foo.
    739  *
    740  * @todo thread problems
    741  *
    742  * @param tree the global object tree
    743  * @param message the message to dispatch
    744  * @returns whether message was handled successfully
    745  */
    746 DBusHandlerResult
    747 _dbus_object_tree_dispatch_and_unlock (DBusObjectTree          *tree,
    748                                        DBusMessage             *message,
    749                                        dbus_bool_t             *found_object)
    750 {
    751   char **path;
    752   dbus_bool_t exact_match;
    753   DBusList *list;
    754   DBusList *link;
    755   DBusHandlerResult result;
    756   DBusObjectSubtree *subtree;
    757 
    758 #if 0
    759   _dbus_verbose ("Dispatch of message by object path\n");
    760 #endif
    761 
    762   path = NULL;
    763   if (!dbus_message_get_path_decomposed (message, &path))
    764     {
    765 #ifdef DBUS_BUILD_TESTS
    766       if (tree->connection)
    767 #endif
    768         {
    769           _dbus_verbose ("unlock\n");
    770           _dbus_connection_unlock (tree->connection);
    771         }
    772 
    773       _dbus_verbose ("No memory to get decomposed path\n");
    774 
    775       return DBUS_HANDLER_RESULT_NEED_MEMORY;
    776     }
    777 
    778   if (path == NULL)
    779     {
    780 #ifdef DBUS_BUILD_TESTS
    781       if (tree->connection)
    782 #endif
    783         {
    784           _dbus_verbose ("unlock\n");
    785           _dbus_connection_unlock (tree->connection);
    786         }
    787 
    788       _dbus_verbose ("No path field in message\n");
    789       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    790     }
    791 
    792   /* Find the deepest path that covers the path in the message */
    793   subtree = find_handler (tree, (const char**) path, &exact_match);
    794 
    795   if (found_object)
    796     *found_object = !!subtree;
    797 
    798   /* Build a list of all paths that cover the path in the message */
    799 
    800   list = NULL;
    801 
    802   while (subtree != NULL)
    803     {
    804       if (subtree->message_function != NULL && (exact_match || subtree->invoke_as_fallback))
    805         {
    806           _dbus_object_subtree_ref (subtree);
    807 
    808           /* run deepest paths first */
    809           if (!_dbus_list_append (&list, subtree))
    810             {
    811               result = DBUS_HANDLER_RESULT_NEED_MEMORY;
    812               _dbus_object_subtree_unref (subtree);
    813               goto free_and_return;
    814             }
    815         }
    816 
    817       exact_match = FALSE;
    818       subtree = subtree->parent;
    819     }
    820 
    821   _dbus_verbose ("%d handlers in the path tree for this message\n",
    822                  _dbus_list_get_length (&list));
    823 
    824   /* Invoke each handler in the list */
    825 
    826   result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    827 
    828   link = _dbus_list_get_first_link (&list);
    829   while (link != NULL)
    830     {
    831       DBusList *next = _dbus_list_get_next_link (&list, link);
    832       subtree = link->data;
    833 
    834       /* message_function is NULL if we're unregistered
    835        * due to reentrancy
    836        */
    837       if (subtree->message_function)
    838         {
    839           DBusObjectPathMessageFunction message_function;
    840           void *user_data;
    841 
    842           message_function = subtree->message_function;
    843           user_data = subtree->user_data;
    844 
    845 #if 0
    846           _dbus_verbose ("  (invoking a handler)\n");
    847 #endif
    848 
    849 #ifdef DBUS_BUILD_TESTS
    850           if (tree->connection)
    851 #endif
    852             {
    853               _dbus_verbose ("unlock\n");
    854               _dbus_connection_unlock (tree->connection);
    855             }
    856 
    857           /* FIXME you could unregister the subtree in another thread
    858            * before we invoke the callback, and I can't figure out a
    859            * good way to solve this.
    860            */
    861 
    862           result = (* message_function) (tree->connection,
    863                                          message,
    864                                          user_data);
    865 
    866 #ifdef DBUS_BUILD_TESTS
    867           if (tree->connection)
    868 #endif
    869             _dbus_connection_lock (tree->connection);
    870 
    871           if (result != DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
    872             goto free_and_return;
    873         }
    874 
    875       link = next;
    876     }
    877 
    878  free_and_return:
    879 
    880   if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
    881     {
    882       /* This hardcoded default handler does a minimal Introspect()
    883        */
    884       result = handle_default_introspect_and_unlock (tree, message,
    885                                                      (const char**) path);
    886     }
    887   else
    888     {
    889 #ifdef DBUS_BUILD_TESTS
    890       if (tree->connection)
    891 #endif
    892         {
    893           _dbus_verbose ("unlock\n");
    894           _dbus_connection_unlock (tree->connection);
    895         }
    896     }
    897 
    898   while (list != NULL)
    899     {
    900       link = _dbus_list_get_first_link (&list);
    901       _dbus_object_subtree_unref (link->data);
    902       _dbus_list_remove_link (&list, link);
    903     }
    904 
    905   dbus_free_string_array (path);
    906 
    907   return result;
    908 }
    909 
    910 /**
    911  * Looks up the data passed to _dbus_object_tree_register() for a
    912  * handler at the given path.
    913  *
    914  * @param tree the global object tree
    915  * @param path NULL-terminated array of path elements giving path to subtree
    916  * @returns the object's user_data or #NULL if none found
    917  */
    918 void*
    919 _dbus_object_tree_get_user_data_unlocked (DBusObjectTree *tree,
    920                                           const char    **path)
    921 {
    922   dbus_bool_t exact_match;
    923   DBusObjectSubtree *subtree;
    924 
    925   _dbus_assert (tree != NULL);
    926   _dbus_assert (path != NULL);
    927 
    928   /* Find the deepest path that covers the path in the message */
    929   subtree = find_handler (tree, (const char**) path, &exact_match);
    930 
    931   if ((subtree == NULL) || !exact_match)
    932     {
    933       _dbus_verbose ("No object at specified path found\n");
    934       return NULL;
    935     }
    936 
    937   return subtree->user_data;
    938 }
    939 
    940 /**
    941  * Allocates a subtree object.
    942  *
    943  * @param name name to duplicate.
    944  * @returns newly-allocated subtree
    945  */
    946 static DBusObjectSubtree*
    947 allocate_subtree_object (const char *name)
    948 {
    949   int len;
    950   DBusObjectSubtree *subtree;
    951   const size_t front_padding = _DBUS_STRUCT_OFFSET (DBusObjectSubtree, name);
    952 
    953   _dbus_assert (name != NULL);
    954 
    955   len = strlen (name);
    956 
    957   subtree = dbus_malloc0 (MAX (front_padding + (len + 1), sizeof (DBusObjectSubtree)));
    958 
    959   if (subtree == NULL)
    960     return NULL;
    961 
    962   memcpy (subtree->name, name, len + 1);
    963 
    964   return subtree;
    965 }
    966 
    967 static DBusObjectSubtree*
    968 _dbus_object_subtree_new (const char                  *name,
    969                           const DBusObjectPathVTable  *vtable,
    970                           void                        *user_data)
    971 {
    972   DBusObjectSubtree *subtree;
    973 
    974   subtree = allocate_subtree_object (name);
    975   if (subtree == NULL)
    976     goto oom;
    977 
    978   _dbus_assert (name != NULL);
    979 
    980   subtree->parent = NULL;
    981 
    982   if (vtable)
    983     {
    984       subtree->message_function = vtable->message_function;
    985       subtree->unregister_function = vtable->unregister_function;
    986     }
    987   else
    988     {
    989       subtree->message_function = NULL;
    990       subtree->unregister_function = NULL;
    991     }
    992 
    993   subtree->user_data = user_data;
    994   _dbus_atomic_inc (&subtree->refcount);
    995   subtree->subtrees = NULL;
    996   subtree->n_subtrees = 0;
    997   subtree->max_subtrees = 0;
    998   subtree->invoke_as_fallback = FALSE;
    999 
   1000   return subtree;
   1001 
   1002  oom:
   1003   return NULL;
   1004 }
   1005 
   1006 static DBusObjectSubtree *
   1007 _dbus_object_subtree_ref (DBusObjectSubtree *subtree)
   1008 {
   1009 #ifdef DBUS_DISABLE_ASSERT
   1010   _dbus_atomic_inc (&subtree->refcount);
   1011 #else
   1012   dbus_int32_t old_value;
   1013 
   1014   old_value = _dbus_atomic_inc (&subtree->refcount);
   1015   _dbus_assert (old_value > 0);
   1016 #endif
   1017 
   1018   return subtree;
   1019 }
   1020 
   1021 static void
   1022 _dbus_object_subtree_unref (DBusObjectSubtree *subtree)
   1023 {
   1024   dbus_int32_t old_value;
   1025 
   1026   old_value = _dbus_atomic_dec (&subtree->refcount);
   1027   _dbus_assert (old_value > 0);
   1028 
   1029   if (old_value == 1)
   1030     {
   1031       _dbus_assert (subtree->unregister_function == NULL);
   1032       _dbus_assert (subtree->message_function == NULL);
   1033 
   1034       dbus_free (subtree->subtrees);
   1035       dbus_free (subtree);
   1036     }
   1037 }
   1038 
   1039 /**
   1040  * Lists the registered fallback handlers and object path handlers at
   1041  * the given parent_path. The returned array should be freed with
   1042  * dbus_free_string_array().
   1043  *
   1044  * @param tree the object tree
   1045  * @param parent_path the path to list the child handlers of
   1046  * @param child_entries returns #NULL-terminated array of children
   1047  * @returns #FALSE if no memory to allocate the child entries
   1048  */
   1049 dbus_bool_t
   1050 _dbus_object_tree_list_registered_and_unlock (DBusObjectTree *tree,
   1051                                               const char    **parent_path,
   1052                                               char         ***child_entries)
   1053 {
   1054   dbus_bool_t result;
   1055 
   1056   result = _dbus_object_tree_list_registered_unlocked (tree,
   1057                                                        parent_path,
   1058                                                        child_entries);
   1059 
   1060 #ifdef DBUS_BUILD_TESTS
   1061   if (tree->connection)
   1062 #endif
   1063     {
   1064       _dbus_verbose ("unlock\n");
   1065       _dbus_connection_unlock (tree->connection);
   1066     }
   1067 
   1068   return result;
   1069 }
   1070 
   1071 
   1072 /** Set to 1 to get a bunch of spew about disassembling the path string */
   1073 #define VERBOSE_DECOMPOSE 0
   1074 
   1075 /**
   1076  * Decompose an object path.  A path of just "/" is
   1077  * represented as an empty vector of strings.
   1078  * The path need not be nul terminated.
   1079  *
   1080  * @param data the path data
   1081  * @param len  the length of the path string
   1082  * @param path address to store new object path
   1083  * @param path_len length of stored path
   1084  */
   1085 dbus_bool_t
   1086 _dbus_decompose_path (const char*     data,
   1087                       int             len,
   1088                       char         ***path,
   1089                       int            *path_len)
   1090 {
   1091   char **retval;
   1092   int n_components;
   1093   int i, j, comp;
   1094 
   1095   _dbus_assert (data != NULL);
   1096   _dbus_assert (path != NULL);
   1097 
   1098 #if VERBOSE_DECOMPOSE
   1099   _dbus_verbose ("Decomposing path \"%s\"\n",
   1100                  data);
   1101 #endif
   1102 
   1103   n_components = 0;
   1104   if (len > 1) /* if path is not just "/" */
   1105     {
   1106       i = 0;
   1107       while (i < len)
   1108         {
   1109           _dbus_assert (data[i] != '\0');
   1110           if (data[i] == '/')
   1111             n_components += 1;
   1112           ++i;
   1113         }
   1114     }
   1115 
   1116   retval = dbus_new0 (char*, n_components + 1);
   1117 
   1118   if (retval == NULL)
   1119     return FALSE;
   1120 
   1121   comp = 0;
   1122   if (n_components == 0)
   1123     i = 1;
   1124   else
   1125     i = 0;
   1126   while (comp < n_components)
   1127     {
   1128       _dbus_assert (i < len);
   1129 
   1130       if (data[i] == '/')
   1131         ++i;
   1132       j = i;
   1133 
   1134       while (j < len && data[j] != '/')
   1135         ++j;
   1136 
   1137       /* Now [i, j) is the path component */
   1138       _dbus_assert (i < j);
   1139       _dbus_assert (data[i] != '/');
   1140       _dbus_assert (j == len || data[j] == '/');
   1141 
   1142 #if VERBOSE_DECOMPOSE
   1143       _dbus_verbose ("  (component in [%d,%d))\n",
   1144                      i, j);
   1145 #endif
   1146 
   1147       retval[comp] = _dbus_memdup (&data[i], j - i + 1);
   1148       if (retval[comp] == NULL)
   1149         {
   1150           dbus_free_string_array (retval);
   1151           return FALSE;
   1152         }
   1153       retval[comp][j-i] = '\0';
   1154 #if VERBOSE_DECOMPOSE
   1155       _dbus_verbose ("  (component %d = \"%s\")\n",
   1156                      comp, retval[comp]);
   1157 #endif
   1158 
   1159       ++comp;
   1160       i = j;
   1161     }
   1162   _dbus_assert (i == len);
   1163 
   1164   *path = retval;
   1165   if (path_len)
   1166     *path_len = n_components;
   1167 
   1168   return TRUE;
   1169 }
   1170 
   1171 /** @} */
   1172 
   1173 static char*
   1174 flatten_path (const char **path)
   1175 {
   1176   DBusString str;
   1177   char *s;
   1178 
   1179   if (!_dbus_string_init (&str))
   1180     return NULL;
   1181 
   1182   if (path[0] == NULL)
   1183     {
   1184       if (!_dbus_string_append_byte (&str, '/'))
   1185         goto nomem;
   1186     }
   1187   else
   1188     {
   1189       int i;
   1190 
   1191       i = 0;
   1192       while (path[i])
   1193         {
   1194           if (!_dbus_string_append_byte (&str, '/'))
   1195             goto nomem;
   1196 
   1197           if (!_dbus_string_append (&str, path[i]))
   1198             goto nomem;
   1199 
   1200           ++i;
   1201         }
   1202     }
   1203 
   1204   if (!_dbus_string_steal_data (&str, &s))
   1205     goto nomem;
   1206 
   1207   _dbus_string_free (&str);
   1208 
   1209   return s;
   1210 
   1211  nomem:
   1212   _dbus_string_free (&str);
   1213   return NULL;
   1214 }
   1215 
   1216 
   1217 #ifdef DBUS_BUILD_TESTS
   1218 
   1219 #ifndef DOXYGEN_SHOULD_SKIP_THIS
   1220 
   1221 #include "dbus-test.h"
   1222 #include <stdio.h>
   1223 
   1224 typedef enum
   1225 {
   1226   STR_EQUAL,
   1227   STR_PREFIX,
   1228   STR_DIFFERENT
   1229 } StrComparison;
   1230 
   1231 /* Returns TRUE if container is a parent of child
   1232  */
   1233 static StrComparison
   1234 path_contains (const char **container,
   1235                const char **child)
   1236 {
   1237   int i;
   1238 
   1239   i = 0;
   1240   while (child[i] != NULL)
   1241     {
   1242       int v;
   1243 
   1244       if (container[i] == NULL)
   1245         return STR_PREFIX; /* container ran out, child continues;
   1246                             * thus the container is a parent of the
   1247                             * child.
   1248                             */
   1249 
   1250       _dbus_assert (container[i] != NULL);
   1251       _dbus_assert (child[i] != NULL);
   1252 
   1253       v = strcmp (container[i], child[i]);
   1254 
   1255       if (v != 0)
   1256         return STR_DIFFERENT; /* they overlap until here and then are different,
   1257                                * not overlapping
   1258                                */
   1259 
   1260       ++i;
   1261     }
   1262 
   1263   /* Child ran out; if container also did, they are equal;
   1264    * otherwise, the child is a parent of the container.
   1265    */
   1266   if (container[i] == NULL)
   1267     return STR_EQUAL;
   1268   else
   1269     return STR_DIFFERENT;
   1270 }
   1271 
   1272 #if 0
   1273 static void
   1274 spew_subtree_recurse (DBusObjectSubtree *subtree,
   1275                       int                indent)
   1276 {
   1277   int i;
   1278 
   1279   i = 0;
   1280   while (i < indent)
   1281     {
   1282       _dbus_verbose (" ");
   1283       ++i;
   1284     }
   1285 
   1286   _dbus_verbose ("%s (%d children)\n",
   1287                  subtree->name, subtree->n_subtrees);
   1288 
   1289   i = 0;
   1290   while (i < subtree->n_subtrees)
   1291     {
   1292       spew_subtree_recurse (subtree->subtrees[i], indent + 2);
   1293 
   1294       ++i;
   1295     }
   1296 }
   1297 
   1298 static void
   1299 spew_tree (DBusObjectTree *tree)
   1300 {
   1301   spew_subtree_recurse (tree->root, 0);
   1302 }
   1303 #endif
   1304 
   1305 /**
   1306  * Callback data used in tests
   1307  */
   1308 typedef struct
   1309 {
   1310   const char **path; /**< Path */
   1311   dbus_bool_t handler_fallback; /**< true if the handler may be called as fallback */
   1312   dbus_bool_t message_handled; /**< Gets set to true if message handler called */
   1313   dbus_bool_t handler_unregistered; /**< gets set to true if handler is unregistered */
   1314 } TreeTestData;
   1315 
   1316 
   1317 static void
   1318 test_unregister_function (DBusConnection  *connection,
   1319                           void            *user_data)
   1320 {
   1321   TreeTestData *ttd = user_data;
   1322 
   1323   ttd->handler_unregistered = TRUE;
   1324 }
   1325 
   1326 static DBusHandlerResult
   1327 test_message_function (DBusConnection  *connection,
   1328                        DBusMessage     *message,
   1329                        void            *user_data)
   1330 {
   1331   TreeTestData *ttd = user_data;
   1332 
   1333   ttd->message_handled = TRUE;
   1334 
   1335   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
   1336 }
   1337 
   1338 static dbus_bool_t
   1339 do_register (DBusObjectTree *tree,
   1340              const char    **path,
   1341              dbus_bool_t     fallback,
   1342              int             i,
   1343              TreeTestData   *tree_test_data)
   1344 {
   1345   DBusObjectPathVTable vtable = { test_unregister_function,
   1346                                   test_message_function, NULL };
   1347 
   1348   tree_test_data[i].message_handled = FALSE;
   1349   tree_test_data[i].handler_unregistered = FALSE;
   1350   tree_test_data[i].handler_fallback = fallback;
   1351   tree_test_data[i].path = path;
   1352 
   1353   if (!_dbus_object_tree_register (tree, fallback, path,
   1354                                    &vtable,
   1355                                    &tree_test_data[i],
   1356                                    NULL))
   1357     return FALSE;
   1358 
   1359   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path) ==
   1360                 &tree_test_data[i]);
   1361 
   1362   return TRUE;
   1363 }
   1364 
   1365 static dbus_bool_t
   1366 do_test_dispatch (DBusObjectTree *tree,
   1367                   const char    **path,
   1368                   int             i,
   1369                   TreeTestData   *tree_test_data,
   1370                   int             n_test_data)
   1371 {
   1372   DBusMessage *message;
   1373   int j;
   1374   DBusHandlerResult result;
   1375   char *flat;
   1376 
   1377   message = NULL;
   1378 
   1379   flat = flatten_path (path);
   1380   if (flat == NULL)
   1381     goto oom;
   1382 
   1383   message = dbus_message_new_method_call (NULL,
   1384                                           flat,
   1385                                           "org.freedesktop.TestInterface",
   1386                                           "Foo");
   1387   dbus_free (flat);
   1388   if (message == NULL)
   1389     goto oom;
   1390 
   1391   j = 0;
   1392   while (j < n_test_data)
   1393     {
   1394       tree_test_data[j].message_handled = FALSE;
   1395       ++j;
   1396     }
   1397 
   1398   result = _dbus_object_tree_dispatch_and_unlock (tree, message, NULL);
   1399   if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
   1400     goto oom;
   1401 
   1402   _dbus_assert (tree_test_data[i].message_handled);
   1403 
   1404   j = 0;
   1405   while (j < n_test_data)
   1406     {
   1407       if (tree_test_data[j].message_handled)
   1408 	{
   1409 	  if (tree_test_data[j].handler_fallback)
   1410 	    _dbus_assert (path_contains (tree_test_data[j].path,
   1411 					 path) != STR_DIFFERENT);
   1412 	  else
   1413 	    _dbus_assert (path_contains (tree_test_data[j].path, path) == STR_EQUAL);
   1414 	}
   1415       else
   1416 	{
   1417 	  if (tree_test_data[j].handler_fallback)
   1418 	    _dbus_assert (path_contains (tree_test_data[j].path,
   1419 					 path) == STR_DIFFERENT);
   1420 	  else
   1421 	    _dbus_assert (path_contains (tree_test_data[j].path, path) != STR_EQUAL);
   1422 	}
   1423 
   1424       ++j;
   1425     }
   1426 
   1427   dbus_message_unref (message);
   1428 
   1429   return TRUE;
   1430 
   1431  oom:
   1432   if (message)
   1433     dbus_message_unref (message);
   1434   return FALSE;
   1435 }
   1436 
   1437 static size_t
   1438 string_array_length (const char **array)
   1439 {
   1440   size_t i;
   1441   for (i = 0; array[i]; i++) ;
   1442   return i;
   1443 }
   1444 
   1445 typedef struct
   1446 {
   1447   const char *path;
   1448   const char *result[20];
   1449 } DecomposePathTest;
   1450 
   1451 static DecomposePathTest decompose_tests[] = {
   1452   { "/foo", { "foo", NULL } },
   1453   { "/foo/bar", { "foo", "bar", NULL } },
   1454   { "/", { NULL } },
   1455   { "/a/b", { "a", "b", NULL } },
   1456   { "/a/b/c", { "a", "b", "c", NULL } },
   1457   { "/a/b/c/d", { "a", "b", "c", "d", NULL } },
   1458   { "/foo/bar/q", { "foo", "bar", "q", NULL } },
   1459   { "/foo/bar/this/is/longer", { "foo", "bar", "this", "is", "longer", NULL } }
   1460 };
   1461 
   1462 static dbus_bool_t
   1463 run_decompose_tests (void)
   1464 {
   1465   int i;
   1466 
   1467   i = 0;
   1468   while (i < _DBUS_N_ELEMENTS (decompose_tests))
   1469     {
   1470       char **result;
   1471       int    result_len;
   1472       int    expected_len;
   1473 
   1474       if (!_dbus_decompose_path (decompose_tests[i].path,
   1475                                  strlen (decompose_tests[i].path),
   1476                                  &result, &result_len))
   1477         return FALSE;
   1478 
   1479       expected_len = string_array_length (decompose_tests[i].result);
   1480 
   1481       if (result_len != (int) string_array_length ((const char**)result) ||
   1482           expected_len != result_len ||
   1483           path_contains (decompose_tests[i].result,
   1484                          (const char**) result) != STR_EQUAL)
   1485         {
   1486           int real_len = string_array_length ((const char**)result);
   1487           _dbus_warn ("Expected decompose of %s to have len %d, returned %d, appears to have %d\n",
   1488                       decompose_tests[i].path, expected_len, result_len,
   1489                       real_len);
   1490           _dbus_warn ("Decompose resulted in elements: { ");
   1491           i = 0;
   1492           while (i < real_len)
   1493             {
   1494               _dbus_warn ("\"%s\"%s", result[i],
   1495                           (i + 1) == real_len ? "" : ", ");
   1496               ++i;
   1497             }
   1498           _dbus_warn ("}\n");
   1499           _dbus_assert_not_reached ("path decompose failed\n");
   1500         }
   1501 
   1502       dbus_free_string_array (result);
   1503 
   1504       ++i;
   1505     }
   1506 
   1507   return TRUE;
   1508 }
   1509 
   1510 static dbus_bool_t
   1511 object_tree_test_iteration (void *data)
   1512 {
   1513   const char *path0[] = { NULL };
   1514   const char *path1[] = { "foo", NULL };
   1515   const char *path2[] = { "foo", "bar", NULL };
   1516   const char *path3[] = { "foo", "bar", "baz", NULL };
   1517   const char *path4[] = { "foo", "bar", "boo", NULL };
   1518   const char *path5[] = { "blah", NULL };
   1519   const char *path6[] = { "blah", "boof", NULL };
   1520   const char *path7[] = { "blah", "boof", "this", "is", "really", "long", NULL };
   1521   const char *path8[] = { "childless", NULL };
   1522   DBusObjectTree *tree;
   1523   TreeTestData tree_test_data[9];
   1524   int i;
   1525   dbus_bool_t exact_match;
   1526 
   1527   if (!run_decompose_tests ())
   1528     return FALSE;
   1529 
   1530   tree = NULL;
   1531 
   1532   tree = _dbus_object_tree_new (NULL);
   1533   if (tree == NULL)
   1534     goto out;
   1535 
   1536   if (!do_register (tree, path0, TRUE, 0, tree_test_data))
   1537     goto out;
   1538 
   1539   _dbus_assert (find_subtree (tree, path0, NULL));
   1540   _dbus_assert (!find_subtree (tree, path1, NULL));
   1541   _dbus_assert (!find_subtree (tree, path2, NULL));
   1542   _dbus_assert (!find_subtree (tree, path3, NULL));
   1543   _dbus_assert (!find_subtree (tree, path4, NULL));
   1544   _dbus_assert (!find_subtree (tree, path5, NULL));
   1545   _dbus_assert (!find_subtree (tree, path6, NULL));
   1546   _dbus_assert (!find_subtree (tree, path7, NULL));
   1547   _dbus_assert (!find_subtree (tree, path8, NULL));
   1548 
   1549   _dbus_assert (find_handler (tree, path0, &exact_match) && exact_match);
   1550   _dbus_assert (find_handler (tree, path1, &exact_match) == tree->root && !exact_match);
   1551   _dbus_assert (find_handler (tree, path2, &exact_match) == tree->root && !exact_match);
   1552   _dbus_assert (find_handler (tree, path3, &exact_match) == tree->root && !exact_match);
   1553   _dbus_assert (find_handler (tree, path4, &exact_match) == tree->root && !exact_match);
   1554   _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
   1555   _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
   1556   _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
   1557   _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
   1558 
   1559   if (!do_register (tree, path1, TRUE, 1, tree_test_data))
   1560     goto out;
   1561 
   1562   _dbus_assert (find_subtree (tree, path0, NULL));
   1563   _dbus_assert (find_subtree (tree, path1, NULL));
   1564   _dbus_assert (!find_subtree (tree, path2, NULL));
   1565   _dbus_assert (!find_subtree (tree, path3, NULL));
   1566   _dbus_assert (!find_subtree (tree, path4, NULL));
   1567   _dbus_assert (!find_subtree (tree, path5, NULL));
   1568   _dbus_assert (!find_subtree (tree, path6, NULL));
   1569   _dbus_assert (!find_subtree (tree, path7, NULL));
   1570   _dbus_assert (!find_subtree (tree, path8, NULL));
   1571 
   1572   _dbus_assert (find_handler (tree, path0, &exact_match) &&  exact_match);
   1573   _dbus_assert (find_handler (tree, path1, &exact_match) &&  exact_match);
   1574   _dbus_assert (find_handler (tree, path2, &exact_match) && !exact_match);
   1575   _dbus_assert (find_handler (tree, path3, &exact_match) && !exact_match);
   1576   _dbus_assert (find_handler (tree, path4, &exact_match) && !exact_match);
   1577   _dbus_assert (find_handler (tree, path5, &exact_match) == tree->root && !exact_match);
   1578   _dbus_assert (find_handler (tree, path6, &exact_match) == tree->root && !exact_match);
   1579   _dbus_assert (find_handler (tree, path7, &exact_match) == tree->root && !exact_match);
   1580   _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
   1581 
   1582   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
   1583     goto out;
   1584 
   1585   _dbus_assert (find_subtree (tree, path1, NULL));
   1586   _dbus_assert (find_subtree (tree, path2, NULL));
   1587   _dbus_assert (!find_subtree (tree, path3, NULL));
   1588   _dbus_assert (!find_subtree (tree, path4, NULL));
   1589   _dbus_assert (!find_subtree (tree, path5, NULL));
   1590   _dbus_assert (!find_subtree (tree, path6, NULL));
   1591   _dbus_assert (!find_subtree (tree, path7, NULL));
   1592   _dbus_assert (!find_subtree (tree, path8, NULL));
   1593 
   1594   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
   1595     goto out;
   1596 
   1597   _dbus_assert (find_subtree (tree, path0, NULL));
   1598   _dbus_assert (find_subtree (tree, path1, NULL));
   1599   _dbus_assert (find_subtree (tree, path2, NULL));
   1600   _dbus_assert (find_subtree (tree, path3, NULL));
   1601   _dbus_assert (!find_subtree (tree, path4, NULL));
   1602   _dbus_assert (!find_subtree (tree, path5, NULL));
   1603   _dbus_assert (!find_subtree (tree, path6, NULL));
   1604   _dbus_assert (!find_subtree (tree, path7, NULL));
   1605   _dbus_assert (!find_subtree (tree, path8, NULL));
   1606 
   1607   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
   1608     goto out;
   1609 
   1610   _dbus_assert (find_subtree (tree, path0, NULL));
   1611   _dbus_assert (find_subtree (tree, path1, NULL));
   1612   _dbus_assert (find_subtree (tree, path2, NULL));
   1613   _dbus_assert (find_subtree (tree, path3, NULL));
   1614   _dbus_assert (find_subtree (tree, path4, NULL));
   1615   _dbus_assert (!find_subtree (tree, path5, NULL));
   1616   _dbus_assert (!find_subtree (tree, path6, NULL));
   1617   _dbus_assert (!find_subtree (tree, path7, NULL));
   1618   _dbus_assert (!find_subtree (tree, path8, NULL));
   1619 
   1620   if (!do_register (tree, path5, TRUE, 5, tree_test_data))
   1621     goto out;
   1622 
   1623   _dbus_assert (find_subtree (tree, path0, NULL));
   1624   _dbus_assert (find_subtree (tree, path1, NULL));
   1625   _dbus_assert (find_subtree (tree, path2, NULL));
   1626   _dbus_assert (find_subtree (tree, path3, NULL));
   1627   _dbus_assert (find_subtree (tree, path4, NULL));
   1628   _dbus_assert (find_subtree (tree, path5, NULL));
   1629   _dbus_assert (!find_subtree (tree, path6, NULL));
   1630   _dbus_assert (!find_subtree (tree, path7, NULL));
   1631   _dbus_assert (!find_subtree (tree, path8, NULL));
   1632 
   1633   _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root &&  exact_match);
   1634   _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root &&  exact_match);
   1635   _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root &&  exact_match);
   1636   _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root &&  exact_match);
   1637   _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root &&  exact_match);
   1638   _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root &&  exact_match);
   1639   _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && !exact_match);
   1640   _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && !exact_match);
   1641   _dbus_assert (find_handler (tree, path8, &exact_match) == tree->root && !exact_match);
   1642 
   1643   if (!do_register (tree, path6, TRUE, 6, tree_test_data))
   1644     goto out;
   1645 
   1646   _dbus_assert (find_subtree (tree, path0, NULL));
   1647   _dbus_assert (find_subtree (tree, path1, NULL));
   1648   _dbus_assert (find_subtree (tree, path2, NULL));
   1649   _dbus_assert (find_subtree (tree, path3, NULL));
   1650   _dbus_assert (find_subtree (tree, path4, NULL));
   1651   _dbus_assert (find_subtree (tree, path5, NULL));
   1652   _dbus_assert (find_subtree (tree, path6, NULL));
   1653   _dbus_assert (!find_subtree (tree, path7, NULL));
   1654   _dbus_assert (!find_subtree (tree, path8, NULL));
   1655 
   1656   if (!do_register (tree, path7, TRUE, 7, tree_test_data))
   1657     goto out;
   1658 
   1659   _dbus_assert (find_subtree (tree, path0, NULL));
   1660   _dbus_assert (find_subtree (tree, path1, NULL));
   1661   _dbus_assert (find_subtree (tree, path2, NULL));
   1662   _dbus_assert (find_subtree (tree, path3, NULL));
   1663   _dbus_assert (find_subtree (tree, path4, NULL));
   1664   _dbus_assert (find_subtree (tree, path5, NULL));
   1665   _dbus_assert (find_subtree (tree, path6, NULL));
   1666   _dbus_assert (find_subtree (tree, path7, NULL));
   1667   _dbus_assert (!find_subtree (tree, path8, NULL));
   1668 
   1669   if (!do_register (tree, path8, TRUE, 8, tree_test_data))
   1670     goto out;
   1671 
   1672   _dbus_assert (find_subtree (tree, path0, NULL));
   1673   _dbus_assert (find_subtree (tree, path1, NULL));
   1674   _dbus_assert (find_subtree (tree, path2, NULL));
   1675   _dbus_assert (find_subtree (tree, path3, NULL));
   1676   _dbus_assert (find_subtree (tree, path4, NULL));
   1677   _dbus_assert (find_subtree (tree, path5, NULL));
   1678   _dbus_assert (find_subtree (tree, path6, NULL));
   1679   _dbus_assert (find_subtree (tree, path7, NULL));
   1680   _dbus_assert (find_subtree (tree, path8, NULL));
   1681 
   1682   _dbus_assert (find_handler (tree, path0, &exact_match) == tree->root &&  exact_match);
   1683   _dbus_assert (find_handler (tree, path1, &exact_match) != tree->root && exact_match);
   1684   _dbus_assert (find_handler (tree, path2, &exact_match) != tree->root && exact_match);
   1685   _dbus_assert (find_handler (tree, path3, &exact_match) != tree->root && exact_match);
   1686   _dbus_assert (find_handler (tree, path4, &exact_match) != tree->root && exact_match);
   1687   _dbus_assert (find_handler (tree, path5, &exact_match) != tree->root && exact_match);
   1688   _dbus_assert (find_handler (tree, path6, &exact_match) != tree->root && exact_match);
   1689   _dbus_assert (find_handler (tree, path7, &exact_match) != tree->root && exact_match);
   1690   _dbus_assert (find_handler (tree, path8, &exact_match) != tree->root && exact_match);
   1691 
   1692   /* test the list_registered function */
   1693 
   1694   {
   1695     const char *root[] = { NULL };
   1696     char **child_entries;
   1697     int nb;
   1698 
   1699     _dbus_object_tree_list_registered_unlocked (tree, path1, &child_entries);
   1700     if (child_entries != NULL)
   1701       {
   1702 	nb = string_array_length ((const char**)child_entries);
   1703 	_dbus_assert (nb == 1);
   1704 	dbus_free_string_array (child_entries);
   1705       }
   1706 
   1707     _dbus_object_tree_list_registered_unlocked (tree, path2, &child_entries);
   1708     if (child_entries != NULL)
   1709       {
   1710 	nb = string_array_length ((const char**)child_entries);
   1711 	_dbus_assert (nb == 2);
   1712 	dbus_free_string_array (child_entries);
   1713       }
   1714 
   1715     _dbus_object_tree_list_registered_unlocked (tree, path8, &child_entries);
   1716     if (child_entries != NULL)
   1717       {
   1718 	nb = string_array_length ((const char**)child_entries);
   1719 	_dbus_assert (nb == 0);
   1720 	dbus_free_string_array (child_entries);
   1721       }
   1722 
   1723     _dbus_object_tree_list_registered_unlocked (tree, root, &child_entries);
   1724     if (child_entries != NULL)
   1725       {
   1726 	nb = string_array_length ((const char**)child_entries);
   1727 	_dbus_assert (nb == 3);
   1728 	dbus_free_string_array (child_entries);
   1729       }
   1730   }
   1731 
   1732   /* Check that destroying tree calls unregister funcs */
   1733   _dbus_object_tree_unref (tree);
   1734 
   1735   i = 0;
   1736   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
   1737     {
   1738       _dbus_assert (tree_test_data[i].handler_unregistered);
   1739       _dbus_assert (!tree_test_data[i].message_handled);
   1740       ++i;
   1741     }
   1742 
   1743   /* Now start again and try the individual unregister function */
   1744   tree = _dbus_object_tree_new (NULL);
   1745   if (tree == NULL)
   1746     goto out;
   1747 
   1748   if (!do_register (tree, path0, TRUE, 0, tree_test_data))
   1749     goto out;
   1750   if (!do_register (tree, path1, TRUE, 1, tree_test_data))
   1751     goto out;
   1752   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
   1753     goto out;
   1754   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
   1755     goto out;
   1756   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
   1757     goto out;
   1758   if (!do_register (tree, path5, TRUE, 5, tree_test_data))
   1759     goto out;
   1760   if (!do_register (tree, path6, TRUE, 6, tree_test_data))
   1761     goto out;
   1762   if (!do_register (tree, path7, TRUE, 7, tree_test_data))
   1763     goto out;
   1764   if (!do_register (tree, path8, TRUE, 8, tree_test_data))
   1765     goto out;
   1766 
   1767   _dbus_object_tree_unregister_and_unlock (tree, path0);
   1768   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path0) == NULL);
   1769 
   1770   _dbus_assert (!find_subtree (tree, path0, NULL));
   1771   _dbus_assert (find_subtree (tree, path1, NULL));
   1772   _dbus_assert (find_subtree (tree, path2, NULL));
   1773   _dbus_assert (find_subtree (tree, path3, NULL));
   1774   _dbus_assert (find_subtree (tree, path4, NULL));
   1775   _dbus_assert (find_subtree (tree, path5, NULL));
   1776   _dbus_assert (find_subtree (tree, path6, NULL));
   1777   _dbus_assert (find_subtree (tree, path7, NULL));
   1778   _dbus_assert (find_subtree (tree, path8, NULL));
   1779 
   1780   _dbus_object_tree_unregister_and_unlock (tree, path1);
   1781   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path1) == NULL);
   1782 
   1783   _dbus_assert (!find_subtree (tree, path0, NULL));
   1784   _dbus_assert (!find_subtree (tree, path1, NULL));
   1785   _dbus_assert (find_subtree (tree, path2, NULL));
   1786   _dbus_assert (find_subtree (tree, path3, NULL));
   1787   _dbus_assert (find_subtree (tree, path4, NULL));
   1788   _dbus_assert (find_subtree (tree, path5, NULL));
   1789   _dbus_assert (find_subtree (tree, path6, NULL));
   1790   _dbus_assert (find_subtree (tree, path7, NULL));
   1791   _dbus_assert (find_subtree (tree, path8, NULL));
   1792 
   1793   _dbus_object_tree_unregister_and_unlock (tree, path2);
   1794   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path2) == NULL);
   1795 
   1796   _dbus_assert (!find_subtree (tree, path0, NULL));
   1797   _dbus_assert (!find_subtree (tree, path1, NULL));
   1798   _dbus_assert (!find_subtree (tree, path2, NULL));
   1799   _dbus_assert (find_subtree (tree, path3, NULL));
   1800   _dbus_assert (find_subtree (tree, path4, NULL));
   1801   _dbus_assert (find_subtree (tree, path5, NULL));
   1802   _dbus_assert (find_subtree (tree, path6, NULL));
   1803   _dbus_assert (find_subtree (tree, path7, NULL));
   1804   _dbus_assert (find_subtree (tree, path8, NULL));
   1805 
   1806   _dbus_object_tree_unregister_and_unlock (tree, path3);
   1807   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path3) == NULL);
   1808 
   1809   _dbus_assert (!find_subtree (tree, path0, NULL));
   1810   _dbus_assert (!find_subtree (tree, path1, NULL));
   1811   _dbus_assert (!find_subtree (tree, path2, NULL));
   1812   _dbus_assert (!find_subtree (tree, path3, NULL));
   1813   _dbus_assert (find_subtree (tree, path4, NULL));
   1814   _dbus_assert (find_subtree (tree, path5, NULL));
   1815   _dbus_assert (find_subtree (tree, path6, NULL));
   1816   _dbus_assert (find_subtree (tree, path7, NULL));
   1817   _dbus_assert (find_subtree (tree, path8, NULL));
   1818 
   1819   _dbus_object_tree_unregister_and_unlock (tree, path4);
   1820   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path4) == NULL);
   1821 
   1822   _dbus_assert (!find_subtree (tree, path0, NULL));
   1823   _dbus_assert (!find_subtree (tree, path1, NULL));
   1824   _dbus_assert (!find_subtree (tree, path2, NULL));
   1825   _dbus_assert (!find_subtree (tree, path3, NULL));
   1826   _dbus_assert (!find_subtree (tree, path4, NULL));
   1827   _dbus_assert (find_subtree (tree, path5, NULL));
   1828   _dbus_assert (find_subtree (tree, path6, NULL));
   1829   _dbus_assert (find_subtree (tree, path7, NULL));
   1830   _dbus_assert (find_subtree (tree, path8, NULL));
   1831 
   1832   _dbus_object_tree_unregister_and_unlock (tree, path5);
   1833   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path5) == NULL);
   1834 
   1835   _dbus_assert (!find_subtree (tree, path0, NULL));
   1836   _dbus_assert (!find_subtree (tree, path1, NULL));
   1837   _dbus_assert (!find_subtree (tree, path2, NULL));
   1838   _dbus_assert (!find_subtree (tree, path3, NULL));
   1839   _dbus_assert (!find_subtree (tree, path4, NULL));
   1840   _dbus_assert (!find_subtree (tree, path5, NULL));
   1841   _dbus_assert (find_subtree (tree, path6, NULL));
   1842   _dbus_assert (find_subtree (tree, path7, NULL));
   1843   _dbus_assert (find_subtree (tree, path8, NULL));
   1844 
   1845   _dbus_object_tree_unregister_and_unlock (tree, path6);
   1846   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path6) == NULL);
   1847 
   1848   _dbus_assert (!find_subtree (tree, path0, NULL));
   1849   _dbus_assert (!find_subtree (tree, path1, NULL));
   1850   _dbus_assert (!find_subtree (tree, path2, NULL));
   1851   _dbus_assert (!find_subtree (tree, path3, NULL));
   1852   _dbus_assert (!find_subtree (tree, path4, NULL));
   1853   _dbus_assert (!find_subtree (tree, path5, NULL));
   1854   _dbus_assert (!find_subtree (tree, path6, NULL));
   1855   _dbus_assert (find_subtree (tree, path7, NULL));
   1856   _dbus_assert (find_subtree (tree, path8, NULL));
   1857 
   1858   _dbus_object_tree_unregister_and_unlock (tree, path7);
   1859   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path7) == NULL);
   1860 
   1861   _dbus_assert (!find_subtree (tree, path0, NULL));
   1862   _dbus_assert (!find_subtree (tree, path1, NULL));
   1863   _dbus_assert (!find_subtree (tree, path2, NULL));
   1864   _dbus_assert (!find_subtree (tree, path3, NULL));
   1865   _dbus_assert (!find_subtree (tree, path4, NULL));
   1866   _dbus_assert (!find_subtree (tree, path5, NULL));
   1867   _dbus_assert (!find_subtree (tree, path6, NULL));
   1868   _dbus_assert (!find_subtree (tree, path7, NULL));
   1869   _dbus_assert (find_subtree (tree, path8, NULL));
   1870 
   1871   _dbus_object_tree_unregister_and_unlock (tree, path8);
   1872   _dbus_assert (_dbus_object_tree_get_user_data_unlocked (tree, path8) == NULL);
   1873 
   1874   _dbus_assert (!find_subtree (tree, path0, NULL));
   1875   _dbus_assert (!find_subtree (tree, path1, NULL));
   1876   _dbus_assert (!find_subtree (tree, path2, NULL));
   1877   _dbus_assert (!find_subtree (tree, path3, NULL));
   1878   _dbus_assert (!find_subtree (tree, path4, NULL));
   1879   _dbus_assert (!find_subtree (tree, path5, NULL));
   1880   _dbus_assert (!find_subtree (tree, path6, NULL));
   1881   _dbus_assert (!find_subtree (tree, path7, NULL));
   1882   _dbus_assert (!find_subtree (tree, path8, NULL));
   1883 
   1884   i = 0;
   1885   while (i < (int) _DBUS_N_ELEMENTS (tree_test_data))
   1886     {
   1887       _dbus_assert (tree_test_data[i].handler_unregistered);
   1888       _dbus_assert (!tree_test_data[i].message_handled);
   1889       ++i;
   1890     }
   1891 
   1892   /* Register it all again, and test dispatch */
   1893 
   1894   if (!do_register (tree, path0, TRUE, 0, tree_test_data))
   1895     goto out;
   1896   if (!do_register (tree, path1, FALSE, 1, tree_test_data))
   1897     goto out;
   1898   if (!do_register (tree, path2, TRUE, 2, tree_test_data))
   1899     goto out;
   1900   if (!do_register (tree, path3, TRUE, 3, tree_test_data))
   1901     goto out;
   1902   if (!do_register (tree, path4, TRUE, 4, tree_test_data))
   1903     goto out;
   1904   if (!do_register (tree, path5, TRUE, 5, tree_test_data))
   1905     goto out;
   1906   if (!do_register (tree, path6, FALSE, 6, tree_test_data))
   1907     goto out;
   1908   if (!do_register (tree, path7, TRUE, 7, tree_test_data))
   1909     goto out;
   1910   if (!do_register (tree, path8, TRUE, 8, tree_test_data))
   1911     goto out;
   1912 
   1913 #if 0
   1914   spew_tree (tree);
   1915 #endif
   1916 
   1917   if (!do_test_dispatch (tree, path0, 0, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1918     goto out;
   1919   if (!do_test_dispatch (tree, path1, 1, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1920     goto out;
   1921   if (!do_test_dispatch (tree, path2, 2, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1922     goto out;
   1923   if (!do_test_dispatch (tree, path3, 3, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1924     goto out;
   1925   if (!do_test_dispatch (tree, path4, 4, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1926     goto out;
   1927   if (!do_test_dispatch (tree, path5, 5, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1928     goto out;
   1929   if (!do_test_dispatch (tree, path6, 6, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1930     goto out;
   1931   if (!do_test_dispatch (tree, path7, 7, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1932     goto out;
   1933   if (!do_test_dispatch (tree, path8, 8, tree_test_data, _DBUS_N_ELEMENTS (tree_test_data)))
   1934     goto out;
   1935 
   1936  out:
   1937   if (tree)
   1938     {
   1939       /* test ref */
   1940       _dbus_object_tree_ref (tree);
   1941       _dbus_object_tree_unref (tree);
   1942       _dbus_object_tree_unref (tree);
   1943     }
   1944 
   1945   return TRUE;
   1946 }
   1947 
   1948 /**
   1949  * @ingroup DBusObjectTree
   1950  * Unit test for DBusObjectTree
   1951  * @returns #TRUE on success.
   1952  */
   1953 dbus_bool_t
   1954 _dbus_object_tree_test (void)
   1955 {
   1956   _dbus_test_oom_handling ("object tree",
   1957                            object_tree_test_iteration,
   1958                            NULL);
   1959 
   1960   return TRUE;
   1961 }
   1962 
   1963 #endif /* !DOXYGEN_SHOULD_SKIP_THIS */
   1964 
   1965 #endif /* DBUS_BUILD_TESTS */
   1966