aboutsummaryrefslogtreecommitdiff
path: root/src/lib/common/gnunet_dbus_lib_service.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/common/gnunet_dbus_lib_service.c')
-rw-r--r--src/lib/common/gnunet_dbus_lib_service.c686
1 files changed, 686 insertions, 0 deletions
diff --git a/src/lib/common/gnunet_dbus_lib_service.c b/src/lib/common/gnunet_dbus_lib_service.c
new file mode 100644
index 0000000..2151613
--- /dev/null
+++ b/src/lib/common/gnunet_dbus_lib_service.c
@@ -0,0 +1,686 @@
1#include "config.h"
2
3#include <stdbool.h>
4#include <dbus/dbus.h>
5
6#include <gnunet/platform.h>
7#include <gnunet/gnunet_common.h>
8#include <gnunet/gnunet_scheduler_lib.h>
9#include <gnunet/gnunet_container_lib.h>
10
11#include "gnunet_dbus_lib.h"
12
13#include "watch.h"
14#include "timeout.h"
15
16#define LOG(kind, ...) GNUNET_log_from (kind, "dbus-service", __VA_ARGS__)
17
18struct GNUNET_DBUS_Service
19{
20#if 0
21 struct GNUNET_DBUS_ObjectIterator *objects_front;
22 struct GNUNET_DBUS_ObjectIterator *objects_back;
23#else
24 struct GNUNET_DBUS_Object *root_object;
25#endif
26
27 DBusConnection *dbus_connection;
28 const struct GNUNET_CONFIGURATION_Handle *cfg;
29
30 char *gnunet_name;
31 char *well_known_name;
32
33 /*
34 * Linked list of watches
35 */
36 struct WatchIter *watches_front;
37 struct WatchIter *watches_back;
38
39 /*
40 * Linked list of timeouts
41 */
42 struct TimeoutIter *timeouts_front;
43 struct TimeoutIter *timeouts_back;
44
45 /*
46 * Linked list of clients
47 */
48 struct GNUNET_DBUS_ClientIterator *clients_front;
49 struct GNUNET_DBUS_ClientIterator *clients_back;
50
51 GNUNET_DBUS_ClientConnectsHandler client_connects;
52 GNUNET_DBUS_ClientDisconnectsHandler client_disconnects;
53
54 unsigned ref_count;
55};
56
57static dbus_int32_t service_slot_id ()
58{
59 static dbus_int32_t id = -1;
60 if (-1 == id)
61 {
62 dbus_bool_t succ = dbus_connection_allocate_data_slot (&id);
63 if (! succ || -1 == id)
64 {
65 LOG (GNUNET_ERROR_TYPE_ERROR, "dbus_connection_allocate_data_slot failed. id == %lld\n", (long long)id);
66 GNUNET_abort_ ();
67 };
68 };
69
70 return id;
71};
72
73/*
74 * Called by DBus when it has a new watch that it wants us to watch.
75 *
76 * @param watch The DBus watch, created and passed to us by DBus.
77 * @param data the GNUNET_DBUS_Service we passed to dbus_connection_set_watch_functions
78 * @return true on success, false on an unrecoverable error.
79 */
80static dbus_bool_t
81watch_add (
82 DBusWatch *watch,
83 void *data)
84{
85 struct GNUNET_DBUS_Service *service = (struct GNUNET_DBUS_Service *)data;
86 struct Watch *w = watch_create (watch);
87
88 if (dbus_watch_get_enabled (watch))
89 watch_schedule (w);
90
91 struct WatchIter *wi = GNUNET_new (struct WatchIter);
92 wi->w = w;
93 GNUNET_CONTAINER_DLL_insert (service->watches_front,
94 service->watches_back,
95 wi);
96 return true;
97};
98
99/*
100 * Called by DBus when it want to permanently disable and remove a watch
101 *
102 * @param watch The DBus watch, passed to us by DBus.
103 * @param data the GNUNET_DBUS_Service we passed to dbus_connection_set_watch_functions
104 */
105static void
106watch_remove (
107 DBusWatch *watch,
108 void *data)
109{
110 struct GNUNET_DBUS_Service *service = (struct GNUNET_DBUS_Service *)data;
111 struct WatchIter *wi = watch_find (service->watches_front, watch);
112
113 if (NULL == wi)
114 {
115 LOG (GNUNET_ERROR_TYPE_ERROR, "Asked to remove watch that has not been added\n");
116 GNUNET_abort_ ();
117 };
118
119 struct Watch *w = wi->w;
120 watch_unschedule (w);
121 GNUNET_CONTAINER_DLL_remove (service->watches_front,
122 service->watches_back,
123 wi);
124 watch_unref (w);
125 GNUNET_free (wi);
126};
127
128/*
129 * Called by DBus when it wants to enable or disable a watch.
130 * Schedules or unschedules the scheduler to monitor this watch as appropriate.
131 *
132 * @param watch The DBus watch, passed to us by DBus.
133 * @param data the GNUNET_DBUS_Service we passed to dbus_connection_set_watch_functions
134 */
135static void
136watch_toggle (
137 DBusWatch *watch,
138 void *data)
139{
140 struct GNUNET_DBUS_Service *service = (struct GNUNET_DBUS_Service *)data;
141 struct WatchIter *wi = watch_find (service->watches_front, watch);
142
143 if (NULL == wi)
144 {
145 LOG (GNUNET_ERROR_TYPE_ERROR, "Asked to toggle watch that has not been added\n");
146 GNUNET_abort_ ();
147 };
148
149 struct Watch *w = wi->w;
150 if (dbus_watch_get_enabled (watch))
151 watch_unschedule (w);
152 else
153 watch_schedule (w);
154};
155
156static dbus_bool_t
157timeout_add (
158 DBusTimeout *timeout,
159 void *data)
160{
161 struct GNUNET_DBUS_Service *service = (struct GNUNET_DBUS_Service *)data;
162 struct Timeout *t = timeout_create (timeout);
163
164 if (dbus_timeout_get_enabled (timeout))
165 timeout_schedule (t);
166
167 struct TimeoutIter *ti = GNUNET_new (struct TimeoutIter);
168 ti->t = t;
169 GNUNET_CONTAINER_DLL_insert (service->timeouts_front,
170 service->timeouts_back,
171 ti);
172 return true;
173};
174
175static void
176timeout_remove (
177 DBusTimeout *timeout,
178 void *data)
179{
180 struct GNUNET_DBUS_Service *service = (struct GNUNET_DBUS_Service *)data;
181 struct TimeoutIter *ti = timeout_find (service->timeouts_front, timeout);
182
183 if (NULL == ti)
184 {
185 LOG (GNUNET_ERROR_TYPE_WARNING, "Asked to remove timeout that has not been added\n");
186 return;
187 };
188
189 struct Timeout *t = ti->t;
190 timeout_unschedule (t);
191 GNUNET_CONTAINER_DLL_remove (service->timeouts_front,
192 service->timeouts_back,
193 ti);
194 timeout_unref (t);
195 GNUNET_free (ti);
196};
197
198static void
199timeout_toggle (
200 DBusTimeout *timeout,
201 void *data)
202{
203 struct GNUNET_DBUS_Service *service = (struct GNUNET_DBUS_Service *)data;
204 struct TimeoutIter *ti = timeout_find (service->timeouts_front, timeout);
205
206 if (NULL == ti)
207 {
208 LOG (GNUNET_ERROR_TYPE_WARNING, "asked to toggle timeout that has not been added\n");
209 return;
210 };
211
212 struct Timeout *t = ti->t;
213 if (dbus_timeout_get_enabled (timeout))
214 timeout_unschedule (t);
215 else
216 timeout_schedule (t);
217};
218
219
220static void
221dispatch (
222 void *cls,
223 const struct GNUNET_SCHEDULER_TaskContext *tc);
224
225
226static void
227handle_dispatch (
228 DBusConnection *dbus_connection,
229 DBusDispatchStatus status)
230{
231 switch (status)
232 {
233 case DBUS_DISPATCH_DATA_REMAINS:
234 GNUNET_SCHEDULER_add_now (dispatch,
235 dbus_connection);
236 break;
237 case DBUS_DISPATCH_COMPLETE:
238 break;
239 case DBUS_DISPATCH_NEED_MEMORY:
240 LOG (GNUNET_ERROR_TYPE_ERROR, "Out of memory!\n");
241 GNUNET_abort_ ();
242 break;
243 default:
244 LOG (GNUNET_ERROR_TYPE_ERROR, "Unrecognized dispatch status\n");
245 break;
246 };
247};
248
249
250static void
251dispatch (
252 void *cls,
253 const struct GNUNET_SCHEDULER_TaskContext *tc)
254{
255 struct DBusConnection *dbus_connection = (struct DBusConnection *)cls;
256 dbus_connection_dispatch (dbus_connection);
257 handle_dispatch (dbus_connection, dbus_connection_get_dispatch_status (dbus_connection));
258};
259
260
261static void
262dispatch_status_changed (
263 DBusConnection *dbus_connection,
264 DBusDispatchStatus new_status,
265 void *data)
266{
267 (void)data;
268 handle_dispatch (dbus_connection, new_status);
269};
270
271/*
272 * Called whenever a message arrives from DBus
273 *
274 * @param conn The connection it arrived on.
275 * @param message The message
276 * @param cls The closure passed to dbus_connection_try_register_object_path
277 * in our case this is the GNUNET_DBUS_Object that the message was
278 * sent to.
279 *
280 * @return DBUS_HANDLER_RESULT_NEED_MEMORY if anything failed due to lack of
281 * memory.
282 * DBUS_HANDLER_RESULT_HANDLED if any method returned a reply.
283 * DBUS_HANDLER_RESULT_NOT_YET_HANDLED otherwise.
284 */
285DBusHandlerResult
286handle_object_message (
287 DBusConnection *connection,
288 DBusMessage *dbus_message,
289 void *cls)
290{
291 //struct GNUNET_DBUS_Object *object = (struct GNUNET_DBUS_Object *)cls;
292 (void)cls;
293
294 const char *object_path = dbus_message_get_path (dbus_message);
295 const char *type_string = dbus_message_type_to_string (dbus_message_get_type (dbus_message));
296 const char *interface_name = dbus_message_get_interface (dbus_message);
297 const char *member_name = dbus_message_get_member (dbus_message);
298 LOG (GNUNET_ERROR_TYPE_DEBUG, "Recieved DBus message for %s\n", object_path);
299 LOG (GNUNET_ERROR_TYPE_DEBUG, " type == %s\n", type_string ? type_string : "(none)");
300 LOG (GNUNET_ERROR_TYPE_DEBUG, " interface == %s\n", interface_name ? interface_name : "(none)");
301 LOG (GNUNET_ERROR_TYPE_DEBUG, " member == %s\n", member_name ? member_name : "(none)");
302
303 struct GNUNET_DBUS_Service *service = dbus_connection_get_data (connection, service_slot_id ());
304
305 const char *unique_name = dbus_message_get_sender (dbus_message);
306 struct GNUNET_DBUS_ClientIterator *client_it = service->clients_front;
307 struct GNUNET_DBUS_Client *client = NULL;
308 for (; client_it; client_it = client_it->next)
309 {
310 client = client_it->client;
311 const char *this_unique_name = GNUNET_DBUS_client_get_unique_name (client);
312 if (! strcmp (unique_name, this_unique_name))
313 break;
314 };
315 if (! client_it)
316 {
317 client = GNUNET_DBUS_client_create (unique_name);
318 client_it = GNUNET_new (struct GNUNET_DBUS_ClientIterator);
319 client_it->client = client;
320 GNUNET_CONTAINER_DLL_insert (service->clients_front,
321 service->clients_back,
322 client_it);
323 if (service->client_connects)
324 service->client_connects (service, client);
325 /*
326
327 ** TODO **
328
329 detect when a client is no longer on the bus and
330 destroy the client object. At the moment this will slowly
331 leak memory as clients come and go.
332
333 */
334 };
335
336 /*
337 * TODO:
338 *
339 * The code below could be more efficient. Avoid the allocation with
340 * dbus_message_get_path_decomposed and use tries to do
341 * object/method/interface lookups.
342 */
343 char **path_decomposed = NULL;
344 dbus_bool_t succ = dbus_message_get_path_decomposed (dbus_message, &path_decomposed);
345 if (! succ)
346 {
347 LOG (GNUNET_ERROR_TYPE_ERROR, "Ran out of memory in dbus_message_get_path_decomposed.\n");
348 GNUNET_abort_ ();
349 };
350
351 struct GNUNET_DBUS_Object *object = service->root_object;
352 char **path_element = path_decomposed;
353 while (*path_element)
354 {
355 const struct GNUNET_DBUS_ObjectIterator *start = GNUNET_DBUS_object_iterate_subobjects (object);
356 const struct GNUNET_DBUS_ObjectIterator *found = GNUNET_DBUS_object_find (start, *path_element);
357 if (! found)
358 {
359 const char *object_name = GNUNET_DBUS_object_get_name (object);
360 LOG (GNUNET_ERROR_TYPE_DEBUG, "No such object \"%s\" under \"%s\"\n", *path_element, object_name[0] ? object_name : "(root object)");
361 dbus_free_string_array (path_decomposed);
362 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
363 };
364
365 object = found->object;
366 path_element++;
367 };
368 dbus_free_string_array (path_decomposed);
369
370 if (! member_name)
371 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
372
373 if (! interface_name)
374 interface_name = service->well_known_name;
375
376 int dbus_message_type = dbus_message_get_type (dbus_message);
377 if (dbus_message_type != DBUS_MESSAGE_TYPE_METHOD_CALL)
378 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
379
380 const struct GNUNET_DBUS_InterfaceIterator *int_it = \
381 GNUNET_DBUS_interface_find ( \
382 GNUNET_DBUS_object_iterate_interfaces (object),
383 interface_name);
384 if (! int_it)
385 {
386 LOG (GNUNET_ERROR_TYPE_DEBUG, "No such interface: %s\n", interface_name);
387 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
388 };
389 struct GNUNET_DBUS_Interface *interface = int_it->interface;
390
391 const struct GNUNET_DBUS_MethodIterator *meth_it = \
392 GNUNET_DBUS_method_find ( \
393 GNUNET_DBUS_interface_iterate_methods (interface),
394 member_name);
395 if (! meth_it)
396 {
397 LOG (GNUNET_ERROR_TYPE_DEBUG, "No such method: %s\n", member_name);
398 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
399 };
400 struct GNUNET_DBUS_Method *method = meth_it->method;
401
402 LOG (GNUNET_ERROR_TYPE_DEBUG, "Found method.\n");
403
404 struct GNUNET_DBUS_MethodContext *mc = GNUNET_DBUS_method_context_create (
405 client,
406 service,
407 object,
408 interface,
409 method,
410 dbus_message
411 );
412 GNUNET_DBUS_method_call (method, mc);
413 GNUNET_DBUS_method_context_unref (mc);
414 return DBUS_HANDLER_RESULT_HANDLED;
415};
416
417struct GNUNET_DBUS_Service *
418GNUNET_DBUS_service_create (
419 const struct GNUNET_CONFIGURATION_Handle *cfg,
420 const char *name)
421{
422 struct GNUNET_DBUS_Service *service = GNUNET_new (struct GNUNET_DBUS_Service);
423 service->cfg = cfg;
424 service->gnunet_name = GNUNET_strdup (name);
425 service->watches_front = NULL;
426 service->watches_back = NULL;
427 service->timeouts_front = NULL;
428 service->timeouts_back = NULL;
429 service->client_connects = NULL;
430 service->client_disconnects = NULL;
431 service->ref_count = 1;
432
433 DBusError err;
434 dbus_error_init(&err);
435
436 service->root_object = GNUNET_DBUS_object_create ("", NULL);
437 if (! service->root_object)
438 LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to create root object for service.\n");
439 else
440 {
441 service->dbus_connection = dbus_bus_get_private (DBUS_BUS_SYSTEM, &err);
442 if (! service->dbus_connection)
443 LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to connect to dbus system bus (%s)\n", dbus_error_is_set (&err) ? err.message : "dbus_bus_get_private returned NULL");
444 else
445 {
446 dbus_connection_set_exit_on_disconnect (service->dbus_connection,
447 false);
448 dbus_bool_t succ = dbus_connection_set_data (service->dbus_connection,
449 service_slot_id (),
450 service,
451 NULL);
452 if (! succ)
453 {
454 LOG (GNUNET_ERROR_TYPE_ERROR, "dbus_connection_set_data returned false. Out of memory.\n");
455 GNUNET_abort_ ();
456 };
457
458 succ = dbus_connection_set_watch_functions (service->dbus_connection, watch_add, watch_remove, watch_toggle, service, NULL);
459 if(! succ)
460 {
461 LOG (GNUNET_ERROR_TYPE_ERROR, "dbus_connection_set_watch_functions returned false. Out of memory.\n");
462 GNUNET_abort_ ();
463 };
464
465 dbus_connection_set_dispatch_status_function (service->dbus_connection, dispatch_status_changed, service, NULL);
466 handle_dispatch (service->dbus_connection, dbus_connection_get_dispatch_status (service->dbus_connection));
467
468 succ = dbus_connection_set_timeout_functions (service->dbus_connection, timeout_add, timeout_remove, timeout_toggle, service, NULL);
469 if (! succ)
470 {
471 LOG (GNUNET_ERROR_TYPE_ERROR, "dbus_connection_set_timeout_functions returned false. Out of memory.\n");
472 GNUNET_abort_ ();
473 };
474
475 DBusObjectPathVTable vtable;
476 vtable.message_function = handle_object_message;
477 vtable.unregister_function = NULL;
478 succ = dbus_connection_try_register_fallback (service->dbus_connection, "/", &vtable, service, &err);
479 if (dbus_error_is_set (&err) || ! succ)
480 LOG (GNUNET_ERROR_TYPE_ERROR, "dbus_connection_try_register_fallback failed when registering root object.\n");
481 else
482 {
483 service->well_known_name = NULL;
484 GNUNET_asprintf (&service->well_known_name, "gnu.gnunet.%s", name);
485 int request_result = dbus_bus_request_name (service->dbus_connection, service->well_known_name, DBUS_NAME_FLAG_DO_NOT_QUEUE, &err);
486 if (dbus_error_is_set (&err))
487 LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to request the bus name (%s)\n", err.message);
488 else
489 {
490 switch(request_result)
491 {
492 case DBUS_REQUEST_NAME_REPLY_EXISTS:
493 LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to request the bus name \"%s\"; name is already taken.\n", service->well_known_name);
494 break;
495 default:
496 LOG (GNUNET_ERROR_TYPE_ERROR, "Unable to interpret result of dbus_request_name (%d)\n", request_result);
497 break;
498 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
499
500 /* Success! */
501 return service;
502
503 }
504 }
505 dbus_connection_unregister_object_path (service->dbus_connection, "/");
506 }
507 dbus_connection_close (service->dbus_connection);
508 dbus_connection_unref (service->dbus_connection);
509 }
510 GNUNET_DBUS_object_unref (service->root_object);
511 }
512 GNUNET_free (service->well_known_name);
513 GNUNET_free (service->gnunet_name);
514 GNUNET_free (service);
515 LOG (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DBUS_service create failed.\n");
516 return NULL;
517};
518
519void
520GNUNET_DBUS_service_ref (
521 struct GNUNET_DBUS_Service *service)
522{
523 service->ref_count++;
524}
525
526void
527GNUNET_DBUS_service_unref (
528 struct GNUNET_DBUS_Service *service)
529{
530 if (service->ref_count == 0)
531 {
532 LOG (GNUNET_ERROR_TYPE_ERROR, "Tried to unref service with ref count 0\n");
533 LOG (GNUNET_ERROR_TYPE_ERROR, " well_known_name == %s\n", service->well_known_name);
534 GNUNET_abort_ ();
535 }
536
537 if (0 == --(service->ref_count))
538 {
539 GNUNET_DBUS_object_unref (service->root_object);
540
541 GNUNET_free (service->well_known_name);
542 GNUNET_free (service->gnunet_name);
543
544 struct WatchIter *wi = service->watches_front;
545 while (wi)
546 {
547 struct WatchIter *next = wi->next;
548 watch_unref (wi->w);
549 GNUNET_free (wi);
550 wi = next;
551 }
552
553 struct TimeoutIter *ti = service->timeouts_front;
554 while (ti)
555 {
556 struct TimeoutIter *next = ti->next;
557 timeout_unref (ti->t);
558 GNUNET_free (ti);
559 ti = next;
560 }
561
562 struct GNUNET_DBUS_ClientIterator *ci = service->clients_front;
563 while (ci)
564 {
565 struct GNUNET_DBUS_ClientIterator *next = ci->next;
566 GNUNET_DBUS_client_unref (ci->client);
567 GNUNET_free (ci);
568 ci = next;
569 }
570
571 GNUNET_free (service);
572 }
573};
574
575const struct GNUNET_CONFIGURATION_Handle *
576GNUNET_DBUS_service_get_config (
577 struct GNUNET_DBUS_Service *service)
578{
579 return service->cfg;
580};
581
582#if 0
583int
584GNUNET_DBUS_service_add_object (
585 struct GNUNET_DBUS_Service *service,
586 struct GNUNET_DBUS_Object *object)
587{
588 DBusError err;
589 dbus_error_init(&err);
590
591 const char *path = GNUNET_DBUS_object_get_path (object);
592
593 DBusObjectPathVTable vtable;
594 vtable.message_function = handle_object_message;
595 vtable.unregister_function = NULL;
596 dbus_bool_t succ = dbus_connection_try_register_object_path (
597 service->dbus_connection,
598 path,
599 &vtable,
600 object,
601 &err);
602 if (dbus_error_is_set (&err))
603 {
604 LOG (
605 GNUNET_ERROR_TYPE_ERROR,
606 "dbus_connection_try_register failed to register path \"%s\": %s\n",
607 path,
608 err.message);
609 return GNUNET_SYSERR;
610 };
611 if (! succ)
612 {
613 LOG (
614 GNUNET_ERROR_TYPE_ERROR,
615 "dbus_connection_try_register returned false when registering path \"%s\"\n",
616 path);
617 return GNUNET_SYSERR;
618 };
619
620 struct GNUNET_DBUS_ObjectIterator *object_it = GNUNET_new (struct GNUNET_DBUS_ObjectIterator);
621 object_it->object = object;
622 GNUNET_DBUS_object_ref (object);
623 GNUNET_CONTAINER_DLL_insert (service->objects_front,
624 service->objects_back,
625 object_it);
626
627 return GNUNET_OK;
628};
629
630void
631GNUNET_DBUS_service_remove_object (
632 struct GNUNET_DBUS_Service *service,
633 struct GNUNET_DBUS_Object *object)
634{
635 struct GNUNET_DBUS_ObjectIterator *obj_it;
636 for (obj_it = service->objects_front; obj_it; obj_it = obj_it->next)
637 {
638 if (obj_it->object == object)
639 break;
640 };
641
642 if (! obj_it)
643 {
644 LOG (GNUNET_ERROR_TYPE_ERROR, "Tried to remove object that has not been added.\n");
645 GNUNET_abort_ ();
646 };
647
648 GNUNET_CONTAINER_DLL_remove (
649 service->objects_front,
650 service->objects_back,
651 obj_it);
652
653 GNUNET_free (obj_it);
654 GNUNET_DBUS_object_unref (object);
655}
656#endif
657
658void
659GNUNET_DBUS_service_send (
660 struct GNUNET_DBUS_Service *service,
661 DBusMessage *dbus_message)
662{
663 dbus_bool_t succ = dbus_connection_send (service->dbus_connection, dbus_message, NULL);
664 if (! succ)
665 {
666 LOG (GNUNET_ERROR_TYPE_ERROR, "dbus_connection_send failed. Out of memory.\n");
667 GNUNET_abort_ ();
668 };
669};
670
671void
672GNUNET_DBUS_service_set_client_handlers (
673 struct GNUNET_DBUS_Service *service,
674 GNUNET_DBUS_ClientConnectsHandler client_connects,
675 GNUNET_DBUS_ClientDisconnectsHandler client_disconnects)
676{
677 service->client_connects = client_connects;
678 service->client_disconnects = client_disconnects;
679};
680
681struct GNUNET_DBUS_Object *
682GNUNET_DBUS_service_get_root_object (
683 struct GNUNET_DBUS_Service *service)
684{
685 return service->root_object;
686}