Main Page | Modules | Data Structures | File List | Data Fields | Related Pages

dbus-object-tree.c

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

Generated on Tue Feb 10 18:14:06 2004 for D-BUS by doxygen 1.3.5