gkdbus: Fix underflow and unreachable code bug
[platform/upstream/glib.git] / gio / gdbusobjectmanagerserver.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2010 Red Hat, Inc.
4  *
5  * SPDX-License-Identifier: LGPL-2.1-or-later
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include "gdbusobjectmanager.h"
26 #include "gdbusobjectmanagerserver.h"
27 #include "gdbusobject.h"
28 #include "gdbusobjectskeleton.h"
29 #include "gdbusinterfaceskeleton.h"
30 #include "gdbusconnection.h"
31 #include "gdbusintrospection.h"
32 #include "gdbusmethodinvocation.h"
33 #include "gdbuserror.h"
34
35 #include "gioerror.h"
36
37 #include "glibintl.h"
38
39 /**
40  * SECTION:gdbusobjectmanagerserver
41  * @short_description: Service-side object manager
42  * @include: gio/gio.h
43  *
44  * #GDBusObjectManagerServer is used to export #GDBusObject instances using
45  * the standardized
46  * [org.freedesktop.DBus.ObjectManager](http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager)
47  * interface. For example, remote D-Bus clients can get all objects
48  * and properties in a single call. Additionally, any change in the
49  * object hierarchy is broadcast using signals. This means that D-Bus
50  * clients can keep caches up to date by only listening to D-Bus
51  * signals.
52  *
53  * The recommended path to export an object manager at is the path form of the
54  * well-known name of a D-Bus service, or below. For example, if a D-Bus service
55  * is available at the well-known name `net.example.ExampleService1`, the object
56  * manager should typically be exported at `/net/example/ExampleService1`, or
57  * below (to allow for multiple object managers in a service).
58  *
59  * It is supported, but not recommended, to export an object manager at the root
60  * path, `/`.
61  *
62  * See #GDBusObjectManagerClient for the client-side code that is
63  * intended to be used with #GDBusObjectManagerServer or any D-Bus
64  * object implementing the org.freedesktop.DBus.ObjectManager
65  * interface.
66  */
67
68 typedef struct
69 {
70   GDBusObjectSkeleton *object;
71   GDBusObjectManagerServer *manager;
72   GHashTable *map_iface_name_to_iface;
73   gboolean exported;
74 } RegistrationData;
75
76 static void registration_data_free (RegistrationData *data);
77
78 static void export_all (GDBusObjectManagerServer *manager);
79 static void unexport_all (GDBusObjectManagerServer *manager, gboolean only_manager);
80
81 static void g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *manager,
82                                                          RegistrationData   *data,
83                                                          const gchar *const *interfaces,
84                                                          const gchar *object_path);
85
86 static void g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *manager,
87                                                            RegistrationData   *data,
88                                                            const gchar *const *interfaces);
89
90 static gboolean g_dbus_object_manager_server_unexport_unlocked (GDBusObjectManagerServer  *manager,
91                                                                 const gchar               *object_path);
92
93 struct _GDBusObjectManagerServerPrivate
94 {
95   GMutex lock;
96   GDBusConnection *connection;
97   gchar *object_path;
98   gchar *object_path_ending_in_slash;
99   GHashTable *map_object_path_to_data;
100   guint manager_reg_id;
101 };
102
103 enum
104 {
105   PROP_0,
106   PROP_CONNECTION,
107   PROP_OBJECT_PATH
108 };
109
110 static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface);
111
112 G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerServer, g_dbus_object_manager_server, G_TYPE_OBJECT,
113                          G_ADD_PRIVATE (GDBusObjectManagerServer)
114                          G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init))
115
116 static void g_dbus_object_manager_server_constructed (GObject *object);
117
118 static void
119 g_dbus_object_manager_server_finalize (GObject *object)
120 {
121   GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object);
122
123   if (manager->priv->connection != NULL)
124     {
125       unexport_all (manager, TRUE);
126       g_object_unref (manager->priv->connection);
127     }
128   g_hash_table_unref (manager->priv->map_object_path_to_data);
129   g_free (manager->priv->object_path);
130   g_free (manager->priv->object_path_ending_in_slash);
131
132   g_mutex_clear (&manager->priv->lock);
133
134   if (G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->finalize != NULL)
135     G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->finalize (object);
136 }
137
138 static void
139 g_dbus_object_manager_server_get_property (GObject    *object,
140                                            guint       prop_id,
141                                            GValue     *value,
142                                            GParamSpec *pspec)
143 {
144   GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object);
145
146   switch (prop_id)
147     {
148     case PROP_CONNECTION:
149       g_mutex_lock (&manager->priv->lock);
150       g_value_set_object (value, manager->priv->connection);
151       g_mutex_unlock (&manager->priv->lock);
152       break;
153
154     case PROP_OBJECT_PATH:
155       g_value_set_string (value, g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager)));
156       break;
157
158     default:
159       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
160       break;
161     }
162 }
163
164 static void
165 g_dbus_object_manager_server_set_property (GObject       *object,
166                                            guint          prop_id,
167                                            const GValue  *value,
168                                            GParamSpec    *pspec)
169 {
170   GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object);
171
172   switch (prop_id)
173     {
174     case PROP_CONNECTION:
175       g_dbus_object_manager_server_set_connection (manager, g_value_get_object (value));
176       break;
177
178     case PROP_OBJECT_PATH:
179       g_assert (manager->priv->object_path == NULL);
180       g_assert (g_variant_is_object_path (g_value_get_string (value)));
181       manager->priv->object_path = g_value_dup_string (value);
182       if (g_str_equal (manager->priv->object_path, "/"))
183         manager->priv->object_path_ending_in_slash = g_strdup (manager->priv->object_path);
184       else
185         manager->priv->object_path_ending_in_slash = g_strdup_printf ("%s/", manager->priv->object_path);
186       break;
187
188     default:
189       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
190       break;
191     }
192 }
193
194 static void
195 g_dbus_object_manager_server_class_init (GDBusObjectManagerServerClass *klass)
196 {
197   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
198
199   gobject_class->finalize     = g_dbus_object_manager_server_finalize;
200   gobject_class->constructed  = g_dbus_object_manager_server_constructed;
201   gobject_class->set_property = g_dbus_object_manager_server_set_property;
202   gobject_class->get_property = g_dbus_object_manager_server_get_property;
203
204   /**
205    * GDBusObjectManagerServer:connection:
206    *
207    * The #GDBusConnection to export objects on.
208    *
209    * Since: 2.30
210    */
211   g_object_class_install_property (gobject_class,
212                                    PROP_CONNECTION,
213                                    g_param_spec_object ("connection",
214                                                         "Connection",
215                                                         "The connection to export objects on",
216                                                         G_TYPE_DBUS_CONNECTION,
217                                                         G_PARAM_READABLE |
218                                                         G_PARAM_WRITABLE |
219                                                         G_PARAM_STATIC_STRINGS));
220
221   /**
222    * GDBusObjectManagerServer:object-path:
223    *
224    * The object path to register the manager object at.
225    *
226    * Since: 2.30
227    */
228   g_object_class_install_property (gobject_class,
229                                    PROP_OBJECT_PATH,
230                                    g_param_spec_string ("object-path",
231                                                         "Object Path",
232                                                         "The object path to register the manager object at",
233                                                         NULL,
234                                                         G_PARAM_READABLE |
235                                                         G_PARAM_WRITABLE |
236                                                         G_PARAM_CONSTRUCT_ONLY |
237                                                         G_PARAM_STATIC_STRINGS));
238 }
239
240 static void
241 g_dbus_object_manager_server_init (GDBusObjectManagerServer *manager)
242 {
243   manager->priv = g_dbus_object_manager_server_get_instance_private (manager);
244   g_mutex_init (&manager->priv->lock);
245   manager->priv->map_object_path_to_data = g_hash_table_new_full (g_str_hash,
246                                                                   g_str_equal,
247                                                                   g_free,
248                                                                   (GDestroyNotify) registration_data_free);
249 }
250
251 /**
252  * g_dbus_object_manager_server_new:
253  * @object_path: The object path to export the manager object at.
254  *
255  * Creates a new #GDBusObjectManagerServer object.
256  *
257  * The returned server isn't yet exported on any connection. To do so,
258  * use g_dbus_object_manager_server_set_connection(). Normally you
259  * want to export all of your objects before doing so to avoid
260  * [InterfacesAdded](http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager)
261  * signals being emitted.
262  *
263  * Returns: A #GDBusObjectManagerServer object. Free with g_object_unref().
264  *
265  * Since: 2.30
266  */
267 GDBusObjectManagerServer *
268 g_dbus_object_manager_server_new (const gchar     *object_path)
269 {
270   g_return_val_if_fail (g_variant_is_object_path (object_path), NULL);
271   return G_DBUS_OBJECT_MANAGER_SERVER (g_object_new (G_TYPE_DBUS_OBJECT_MANAGER_SERVER,
272                                                      "object-path", object_path,
273                                                      NULL));
274 }
275
276 /**
277  * g_dbus_object_manager_server_set_connection:
278  * @manager: A #GDBusObjectManagerServer.
279  * @connection: (nullable): A #GDBusConnection or %NULL.
280  *
281  * Exports all objects managed by @manager on @connection. If
282  * @connection is %NULL, stops exporting objects.
283  */
284 void
285 g_dbus_object_manager_server_set_connection (GDBusObjectManagerServer  *manager,
286                                              GDBusConnection           *connection)
287 {
288   g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
289   g_return_if_fail (connection == NULL || G_IS_DBUS_CONNECTION (connection));
290
291   g_mutex_lock (&manager->priv->lock);
292
293   if (manager->priv->connection == connection)
294     {
295       g_mutex_unlock (&manager->priv->lock);
296       goto out;
297     }
298
299   if (manager->priv->connection != NULL)
300     {
301       unexport_all (manager, FALSE);
302       g_object_unref (manager->priv->connection);
303       manager->priv->connection = NULL;
304     }
305
306   manager->priv->connection = connection != NULL ? g_object_ref (connection) : NULL;
307   if (manager->priv->connection != NULL)
308     export_all (manager);
309
310   g_mutex_unlock (&manager->priv->lock);
311
312   g_object_notify (G_OBJECT (manager), "connection");
313  out:
314   ;
315 }
316
317 /**
318  * g_dbus_object_manager_server_get_connection:
319  * @manager: A #GDBusObjectManagerServer
320  *
321  * Gets the #GDBusConnection used by @manager.
322  *
323  * Returns: (transfer full) (nullable): A #GDBusConnection object or %NULL if
324  *   @manager isn't exported on a connection. The returned object should
325  *   be freed with g_object_unref().
326  *
327  * Since: 2.30
328  */
329 GDBusConnection *
330 g_dbus_object_manager_server_get_connection (GDBusObjectManagerServer *manager)
331 {
332   GDBusConnection *ret;
333   g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), NULL);
334   g_mutex_lock (&manager->priv->lock);
335   ret = manager->priv->connection != NULL ? g_object_ref (manager->priv->connection) : NULL;
336   g_mutex_unlock (&manager->priv->lock);
337   return ret;
338 }
339
340 /* ---------------------------------------------------------------------------------------------------- */
341
342 static void
343 registration_data_export_interface (RegistrationData        *data,
344                                     GDBusInterfaceSkeleton  *interface_skeleton,
345                                     const gchar             *object_path)
346 {
347   GDBusInterfaceInfo *info;
348   GError *error;
349
350   info = g_dbus_interface_skeleton_get_info (interface_skeleton);
351   error = NULL;
352   if (data->manager->priv->connection != NULL)
353     {
354       if (!g_dbus_interface_skeleton_export (interface_skeleton,
355                                              data->manager->priv->connection,
356                                              object_path,
357                                              &error))
358         {
359           g_warning ("%s: Error registering object at %s with interface %s: %s",
360                      G_STRLOC,
361                      object_path,
362                      info->name,
363                      error->message);
364           g_error_free (error);
365         }
366     }
367
368   g_assert (g_hash_table_lookup (data->map_iface_name_to_iface, info->name) == NULL);
369   g_hash_table_insert (data->map_iface_name_to_iface,
370                        info->name,
371                        g_object_ref (interface_skeleton));
372
373   /* if we are already exported, then... */
374   if (data->exported)
375     {
376       const gchar *interfaces[2];
377       /* emit InterfacesAdded on the ObjectManager object */
378       interfaces[0] = info->name;
379       interfaces[1] = NULL;
380       g_dbus_object_manager_server_emit_interfaces_added (data->manager, data, interfaces, object_path);
381     }
382 }
383
384 static void
385 registration_data_unexport_interface (RegistrationData       *data,
386                                       GDBusInterfaceSkeleton *interface_skeleton)
387 {
388   GDBusInterfaceInfo *info;
389   GDBusInterfaceSkeleton *iface;
390
391   info = g_dbus_interface_skeleton_get_info (interface_skeleton);
392   iface = g_hash_table_lookup (data->map_iface_name_to_iface, info->name);
393   g_assert (iface != NULL);
394
395   if (data->manager->priv->connection != NULL)
396     g_dbus_interface_skeleton_unexport (iface);
397
398   g_warn_if_fail (g_hash_table_remove (data->map_iface_name_to_iface, info->name));
399
400   /* if we are already exported, then... */
401   if (data->exported)
402     {
403       const gchar *interfaces[2];
404       /* emit InterfacesRemoved on the ObjectManager object */
405       interfaces[0] = info->name;
406       interfaces[1] = NULL;
407       g_dbus_object_manager_server_emit_interfaces_removed (data->manager, data, interfaces);
408     }
409 }
410
411 /* ---------------------------------------------------------------------------------------------------- */
412
413 static void
414 on_interface_added (GDBusObject    *object,
415                     GDBusInterface *interface,
416                     gpointer        user_data)
417 {
418   RegistrationData *data = user_data;
419   const gchar *object_path;
420   g_mutex_lock (&data->manager->priv->lock);
421   object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
422   registration_data_export_interface (data, G_DBUS_INTERFACE_SKELETON (interface), object_path);
423   g_mutex_unlock (&data->manager->priv->lock);
424 }
425
426 static void
427 on_interface_removed (GDBusObject    *object,
428                       GDBusInterface *interface,
429                       gpointer        user_data)
430 {
431   RegistrationData *data = user_data;
432   g_mutex_lock (&data->manager->priv->lock);
433   registration_data_unexport_interface (data, G_DBUS_INTERFACE_SKELETON (interface));
434   g_mutex_unlock (&data->manager->priv->lock);
435 }
436
437 /* ---------------------------------------------------------------------------------------------------- */
438
439
440 static void
441 registration_data_free (RegistrationData *data)
442 {
443   GHashTableIter iter;
444   GDBusInterfaceSkeleton *iface;
445
446   data->exported = FALSE;
447
448   g_hash_table_iter_init (&iter, data->map_iface_name_to_iface);
449   while (g_hash_table_iter_next (&iter, NULL, (gpointer) &iface))
450     {
451       if (data->manager->priv->connection != NULL)
452         g_dbus_interface_skeleton_unexport (iface);
453     }
454
455   g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_added), data);
456   g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_removed), data);
457   g_object_unref (data->object);
458   g_hash_table_destroy (data->map_iface_name_to_iface);
459   g_free (data);
460 }
461
462 /* Validate whether an object path is valid as a child of the manager. According
463  * to the specification:
464  * https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
465  * this means that:
466  * > All returned object paths are children of the object path implementing this
467  * > interface, i.e. their object paths start with the ObjectManager's object
468  * > path plus '/'
469  *
470  * For example, if the manager is at `/org/gnome/Example`, children will be
471  * `/org/gnome/Example/(.+)`.
472  *
473  * It is permissible (but not encouraged) for the manager to be at `/`. If so,
474  * children will be `/(.+)`.
475  */
476 static gboolean
477 is_valid_child_object_path (GDBusObjectManagerServer *manager,
478                             const gchar              *child_object_path)
479 {
480   /* Historically GDBus accepted @child_object_paths at `/` if the @manager
481    * itself is also at `/". This is not spec-compliant, but making GDBus enforce
482    * the spec more strictly would be an incompatible change.
483    *
484    * See https://gitlab.gnome.org/GNOME/glib/-/issues/2500 */
485   g_warn_if_fail (!g_str_equal (child_object_path, manager->priv->object_path_ending_in_slash));
486
487   return g_str_has_prefix (child_object_path, manager->priv->object_path_ending_in_slash);
488 }
489
490 /* ---------------------------------------------------------------------------------------------------- */
491
492 static void
493 g_dbus_object_manager_server_export_unlocked (GDBusObjectManagerServer  *manager,
494                                               GDBusObjectSkeleton       *object,
495                                               const gchar               *object_path)
496 {
497   RegistrationData *data;
498   GList *existing_interfaces;
499   GList *l;
500   GPtrArray *interface_names;
501
502   g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
503   g_return_if_fail (G_IS_DBUS_OBJECT (object));
504   g_return_if_fail (is_valid_child_object_path (manager, object_path));
505
506   interface_names = g_ptr_array_new ();
507
508   data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
509   if (data != NULL)
510     g_dbus_object_manager_server_unexport_unlocked (manager, object_path);
511
512   data = g_new0 (RegistrationData, 1);
513   data->object = g_object_ref (object);
514   data->manager = manager;
515   data->map_iface_name_to_iface = g_hash_table_new_full (g_str_hash,
516                                                          g_str_equal,
517                                                          NULL,
518                                                          (GDestroyNotify) g_object_unref);
519
520   g_signal_connect (object,
521                     "interface-added",
522                     G_CALLBACK (on_interface_added),
523                     data);
524   g_signal_connect (object,
525                     "interface-removed",
526                     G_CALLBACK (on_interface_removed),
527                     data);
528
529   /* Register all known interfaces - note that data->exported is FALSE so
530    * we don't emit any InterfacesAdded signals.
531    */
532   existing_interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (object));
533   for (l = existing_interfaces; l != NULL; l = l->next)
534     {
535       GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (l->data);
536       registration_data_export_interface (data, interface_skeleton, object_path);
537       g_ptr_array_add (interface_names, g_dbus_interface_skeleton_get_info (interface_skeleton)->name);
538     }
539   g_list_free_full (existing_interfaces, g_object_unref);
540   g_ptr_array_add (interface_names, NULL);
541
542   data->exported = TRUE;
543
544   /* now emit InterfacesAdded() for all the interfaces */
545   g_dbus_object_manager_server_emit_interfaces_added (manager, data, (const gchar *const *) interface_names->pdata, object_path);
546   g_ptr_array_unref (interface_names);
547
548   g_hash_table_insert (manager->priv->map_object_path_to_data,
549                        g_strdup (object_path),
550                        data);
551 }
552
553 /**
554  * g_dbus_object_manager_server_export:
555  * @manager: A #GDBusObjectManagerServer.
556  * @object: A #GDBusObjectSkeleton.
557  *
558  * Exports @object on @manager.
559  *
560  * If there is already a #GDBusObject exported at the object path,
561  * then the old object is removed.
562  *
563  * The object path for @object must be in the hierarchy rooted by the
564  * object path for @manager.
565  *
566  * Note that @manager will take a reference on @object for as long as
567  * it is exported.
568  *
569  * Since: 2.30
570  */
571 void
572 g_dbus_object_manager_server_export (GDBusObjectManagerServer  *manager,
573                                      GDBusObjectSkeleton       *object)
574 {
575   g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
576   g_mutex_lock (&manager->priv->lock);
577   g_dbus_object_manager_server_export_unlocked (manager, object,
578                                                 g_dbus_object_get_object_path (G_DBUS_OBJECT (object)));
579   g_mutex_unlock (&manager->priv->lock);
580 }
581
582 /**
583  * g_dbus_object_manager_server_export_uniquely:
584  * @manager: A #GDBusObjectManagerServer.
585  * @object: An object.
586  *
587  * Like g_dbus_object_manager_server_export() but appends a string of
588  * the form _N (with N being a natural number) to @object's object path
589  * if an object with the given path already exists. As such, the
590  * #GDBusObjectProxy:g-object-path property of @object may be modified.
591  *
592  * Since: 2.30
593  */
594 void
595 g_dbus_object_manager_server_export_uniquely (GDBusObjectManagerServer *manager,
596                                               GDBusObjectSkeleton      *object)
597 {
598   const gchar *orig_object_path;
599   gchar *object_path;
600   guint count;
601   gboolean modified;
602
603   orig_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
604
605   g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
606   g_return_if_fail (G_IS_DBUS_OBJECT (object));
607   g_return_if_fail (is_valid_child_object_path (manager, orig_object_path));
608
609   g_mutex_lock (&manager->priv->lock);
610
611   object_path = g_strdup (orig_object_path);
612   count = 1;
613   modified = FALSE;
614   while (TRUE)
615     {
616       RegistrationData *data;
617       data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
618       if (data == NULL)
619         {
620           break;
621         }
622       g_free (object_path);
623       object_path = g_strdup_printf ("%s_%d", orig_object_path, count++);
624       modified = TRUE;
625     }
626
627   g_dbus_object_manager_server_export_unlocked (manager, object, object_path);
628
629   g_mutex_unlock (&manager->priv->lock);
630
631   if (modified)
632     g_dbus_object_skeleton_set_object_path (G_DBUS_OBJECT_SKELETON (object), object_path);
633
634   g_free (object_path);
635
636 }
637
638 /**
639  * g_dbus_object_manager_server_is_exported:
640  * @manager: A #GDBusObjectManagerServer.
641  * @object: An object.
642  *
643  * Returns whether @object is currently exported on @manager.
644  *
645  * Returns: %TRUE if @object is exported
646  *
647  * Since: 2.34
648  **/
649 gboolean
650 g_dbus_object_manager_server_is_exported (GDBusObjectManagerServer *manager,
651                                           GDBusObjectSkeleton      *object)
652 {
653   RegistrationData *data = NULL;
654   const gchar *object_path;
655   gboolean object_is_exported;
656
657   g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), FALSE);
658   g_return_val_if_fail (G_IS_DBUS_OBJECT (object), FALSE);
659
660   g_mutex_lock (&manager->priv->lock);
661
662   object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
663   if (object_path != NULL)
664     data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
665   object_is_exported = (data != NULL);
666
667   g_mutex_unlock (&manager->priv->lock);
668
669   return object_is_exported;
670 }
671
672 /* ---------------------------------------------------------------------------------------------------- */
673
674 static gboolean
675 g_dbus_object_manager_server_unexport_unlocked (GDBusObjectManagerServer  *manager,
676                                                 const gchar               *object_path)
677 {
678   RegistrationData *data;
679   gboolean ret;
680
681   g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), FALSE);
682   g_return_val_if_fail (g_variant_is_object_path (object_path), FALSE);
683   g_return_val_if_fail (is_valid_child_object_path (manager, object_path), FALSE);
684
685   ret = FALSE;
686
687   data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
688   if (data != NULL)
689     {
690       GPtrArray *interface_names;
691       GHashTableIter iter;
692       const gchar *iface_name;
693
694       interface_names = g_ptr_array_new ();
695       g_hash_table_iter_init (&iter, data->map_iface_name_to_iface);
696       while (g_hash_table_iter_next (&iter, (gpointer) &iface_name, NULL))
697         g_ptr_array_add (interface_names, (gpointer) iface_name);
698       g_ptr_array_add (interface_names, NULL);
699       /* now emit InterfacesRemoved() for all the interfaces */
700       g_dbus_object_manager_server_emit_interfaces_removed (manager, data, (const gchar *const *) interface_names->pdata);
701       g_ptr_array_unref (interface_names);
702
703       g_hash_table_remove (manager->priv->map_object_path_to_data, object_path);
704       ret = TRUE;
705     }
706
707   return ret;
708 }
709
710 /**
711  * g_dbus_object_manager_server_unexport:
712  * @manager: A #GDBusObjectManagerServer.
713  * @object_path: An object path.
714  *
715  * If @manager has an object at @path, removes the object. Otherwise
716  * does nothing.
717  *
718  * Note that @object_path must be in the hierarchy rooted by the
719  * object path for @manager.
720  *
721  * Returns: %TRUE if object at @object_path was removed, %FALSE otherwise.
722  *
723  * Since: 2.30
724  */
725 gboolean
726 g_dbus_object_manager_server_unexport (GDBusObjectManagerServer  *manager,
727                                        const gchar         *object_path)
728 {
729   gboolean ret;
730   g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), FALSE);
731   g_mutex_lock (&manager->priv->lock);
732   ret = g_dbus_object_manager_server_unexport_unlocked (manager, object_path);
733   g_mutex_unlock (&manager->priv->lock);
734   return ret;
735 }
736
737
738 /* ---------------------------------------------------------------------------------------------------- */
739
740 static const GDBusArgInfo manager_interfaces_added_signal_info_arg0 =
741 {
742   -1,
743   "object_path",
744   "o",
745   (GDBusAnnotationInfo**) NULL,
746 };
747
748 static const GDBusArgInfo manager_interfaces_added_signal_info_arg1 =
749 {
750   -1,
751   "interfaces_and_properties",
752   "a{sa{sv}}",
753   (GDBusAnnotationInfo**) NULL,
754 };
755
756 static const GDBusArgInfo * const manager_interfaces_added_signal_info_arg_pointers[] =
757 {
758   &manager_interfaces_added_signal_info_arg0,
759   &manager_interfaces_added_signal_info_arg1,
760   NULL
761 };
762
763 static const GDBusSignalInfo manager_interfaces_added_signal_info =
764 {
765   -1,
766   "InterfacesAdded",
767   (GDBusArgInfo**) &manager_interfaces_added_signal_info_arg_pointers,
768   (GDBusAnnotationInfo**) NULL
769 };
770
771 /* ---------- */
772
773 static const GDBusArgInfo manager_interfaces_removed_signal_info_arg0 =
774 {
775   -1,
776   "object_path",
777   "o",
778   (GDBusAnnotationInfo**) NULL,
779 };
780
781 static const GDBusArgInfo manager_interfaces_removed_signal_info_arg1 =
782 {
783   -1,
784   "interfaces",
785   "as",
786   (GDBusAnnotationInfo**) NULL,
787 };
788
789 static const GDBusArgInfo * const manager_interfaces_removed_signal_info_arg_pointers[] =
790 {
791   &manager_interfaces_removed_signal_info_arg0,
792   &manager_interfaces_removed_signal_info_arg1,
793   NULL
794 };
795
796 static const GDBusSignalInfo manager_interfaces_removed_signal_info =
797 {
798   -1,
799   "InterfacesRemoved",
800   (GDBusArgInfo**) &manager_interfaces_removed_signal_info_arg_pointers,
801   (GDBusAnnotationInfo**) NULL
802 };
803
804 /* ---------- */
805
806 static const GDBusSignalInfo * const manager_signal_info_pointers[] =
807 {
808   &manager_interfaces_added_signal_info,
809   &manager_interfaces_removed_signal_info,
810   NULL
811 };
812
813 /* ---------- */
814
815 static const GDBusArgInfo manager_get_all_method_info_out_arg0 =
816 {
817   -1,
818   "object_paths_interfaces_and_properties",
819   "a{oa{sa{sv}}}",
820   (GDBusAnnotationInfo**) NULL,
821 };
822
823 static const GDBusArgInfo * const manager_get_all_method_info_out_arg_pointers[] =
824 {
825   &manager_get_all_method_info_out_arg0,
826   NULL
827 };
828
829 static const GDBusMethodInfo manager_get_all_method_info =
830 {
831   -1,
832   "GetManagedObjects",
833   (GDBusArgInfo**) NULL,
834   (GDBusArgInfo**) &manager_get_all_method_info_out_arg_pointers,
835   (GDBusAnnotationInfo**) NULL
836 };
837
838 static const GDBusMethodInfo * const manager_method_info_pointers[] =
839 {
840   &manager_get_all_method_info,
841   NULL
842 };
843
844 /* ---------- */
845
846 static const GDBusInterfaceInfo manager_interface_info =
847 {
848   -1,
849   "org.freedesktop.DBus.ObjectManager",
850   (GDBusMethodInfo **) manager_method_info_pointers,
851   (GDBusSignalInfo **) manager_signal_info_pointers,
852   (GDBusPropertyInfo **) NULL,
853   (GDBusAnnotationInfo **) NULL
854 };
855
856 static void
857 manager_method_call (GDBusConnection       *connection,
858                      const gchar           *sender,
859                      const gchar           *object_path,
860                      const gchar           *interface_name,
861                      const gchar           *method_name,
862                      GVariant              *parameters,
863                      GDBusMethodInvocation *invocation,
864                      gpointer               user_data)
865 {
866   GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (user_data);
867   GVariantBuilder array_builder;
868   GHashTableIter object_iter;
869   RegistrationData *data;
870
871   g_mutex_lock (&manager->priv->lock);
872
873   if (g_strcmp0 (method_name, "GetManagedObjects") == 0)
874     {
875       g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{oa{sa{sv}}}"));
876       g_hash_table_iter_init (&object_iter, manager->priv->map_object_path_to_data);
877       while (g_hash_table_iter_next (&object_iter, NULL, (gpointer) &data))
878         {
879           GVariantBuilder interfaces_builder;
880           GHashTableIter interface_iter;
881           GDBusInterfaceSkeleton *iface;
882           const gchar *iter_object_path;
883
884           g_variant_builder_init (&interfaces_builder, G_VARIANT_TYPE ("a{sa{sv}}"));
885           g_hash_table_iter_init (&interface_iter, data->map_iface_name_to_iface);
886           while (g_hash_table_iter_next (&interface_iter, NULL, (gpointer) &iface))
887             {
888               GVariant *properties = g_dbus_interface_skeleton_get_properties (iface);
889               g_variant_builder_add (&interfaces_builder, "{s@a{sv}}",
890                                      g_dbus_interface_skeleton_get_info (iface)->name,
891                                      properties);
892               g_variant_unref (properties);
893             }
894           iter_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
895           g_variant_builder_add (&array_builder,
896                                  "{oa{sa{sv}}}",
897                                  iter_object_path,
898                                  &interfaces_builder);
899         }
900
901       g_dbus_method_invocation_return_value (invocation,
902                                              g_variant_new ("(a{oa{sa{sv}}})",
903                                                             &array_builder));
904     }
905   else
906     {
907       g_dbus_method_invocation_return_error (invocation,
908                                              G_DBUS_ERROR,
909                                              G_DBUS_ERROR_UNKNOWN_METHOD,
910                                              "Unknown method %s - only GetManagedObjects() is supported",
911                                              method_name);
912     }
913   g_mutex_unlock (&manager->priv->lock);
914 }
915
916 static const GDBusInterfaceVTable manager_interface_vtable =
917 {
918   manager_method_call, /* handle_method_call */
919   NULL, /* get_property */
920   NULL, /* set_property */
921   { 0 }
922 };
923
924 /* ---------------------------------------------------------------------------------------------------- */
925
926 static void
927 g_dbus_object_manager_server_constructed (GObject *object)
928 {
929   GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object);
930
931   if (manager->priv->connection != NULL)
932     export_all (manager);
933
934   if (G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->constructed != NULL)
935     G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->constructed (object);
936 }
937
938 static void
939 g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *manager,
940                                                     RegistrationData   *data,
941                                                     const gchar *const *interfaces,
942                                                     const gchar *object_path)
943 {
944   GVariantBuilder array_builder;
945   GError *error;
946   guint n;
947
948   if (data->manager->priv->connection == NULL)
949     goto out;
950
951   g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{sa{sv}}"));
952   for (n = 0; interfaces[n] != NULL; n++)
953     {
954       GDBusInterfaceSkeleton *iface;
955       GVariant *properties;
956
957       iface = g_hash_table_lookup (data->map_iface_name_to_iface, interfaces[n]);
958       g_assert (iface != NULL);
959       properties = g_dbus_interface_skeleton_get_properties (iface);
960       g_variant_builder_add (&array_builder, "{s@a{sv}}", interfaces[n], properties);
961       g_variant_unref (properties);
962     }
963
964   error = NULL;
965   g_dbus_connection_emit_signal (data->manager->priv->connection,
966                                  NULL, /* destination_bus_name */
967                                  manager->priv->object_path,
968                                  manager_interface_info.name,
969                                  "InterfacesAdded",
970                                  g_variant_new ("(oa{sa{sv}})",
971                                                 object_path,
972                                                 &array_builder),
973                                  &error);
974   if (error)
975     {
976       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED))
977         g_warning ("Couldn't emit InterfacesAdded signal: %s", error->message);
978       g_error_free (error);
979     }
980  out:
981   ;
982 }
983
984 static void
985 g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *manager,
986                                                       RegistrationData   *data,
987                                                       const gchar *const *interfaces)
988 {
989   GVariantBuilder array_builder;
990   GError *error;
991   guint n;
992   const gchar *object_path;
993
994   if (data->manager->priv->connection == NULL)
995     goto out;
996
997   g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as"));
998   for (n = 0; interfaces[n] != NULL; n++)
999     g_variant_builder_add (&array_builder, "s", interfaces[n]);
1000
1001   error = NULL;
1002   object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
1003   g_dbus_connection_emit_signal (data->manager->priv->connection,
1004                                  NULL, /* destination_bus_name */
1005                                  manager->priv->object_path,
1006                                  manager_interface_info.name,
1007                                  "InterfacesRemoved",
1008                                  g_variant_new ("(oas)",
1009                                                 object_path,
1010                                                 &array_builder),
1011                                  &error);
1012   if (error)
1013     {
1014       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED))
1015         g_warning ("Couldn't emit InterfacesRemoved signal: %s", error->message);
1016       g_error_free (error);
1017     }
1018  out:
1019   ;
1020 }
1021
1022 /* ---------------------------------------------------------------------------------------------------- */
1023
1024 static GList *
1025 g_dbus_object_manager_server_get_objects (GDBusObjectManager  *_manager)
1026 {
1027   GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager);
1028   GList *ret;
1029   GHashTableIter iter;
1030   RegistrationData *data;
1031
1032   g_mutex_lock (&manager->priv->lock);
1033
1034   ret = NULL;
1035   g_hash_table_iter_init (&iter, manager->priv->map_object_path_to_data);
1036   while (g_hash_table_iter_next (&iter, NULL, (gpointer) &data))
1037     {
1038       ret = g_list_prepend (ret, g_object_ref (data->object));
1039     }
1040
1041   g_mutex_unlock (&manager->priv->lock);
1042
1043   return ret;
1044 }
1045
1046 static const gchar *
1047 g_dbus_object_manager_server_get_object_path (GDBusObjectManager *_manager)
1048 {
1049   GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager);
1050   return manager->priv->object_path;
1051 }
1052
1053 static GDBusObject *
1054 g_dbus_object_manager_server_get_object (GDBusObjectManager *_manager,
1055                                          const gchar        *object_path)
1056 {
1057   GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager);
1058   GDBusObject *ret;
1059   RegistrationData *data;
1060
1061   ret = NULL;
1062
1063   g_mutex_lock (&manager->priv->lock);
1064   data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
1065   if (data != NULL)
1066     ret = g_object_ref (G_DBUS_OBJECT (data->object));
1067   g_mutex_unlock (&manager->priv->lock);
1068
1069   return ret;
1070 }
1071
1072 static GDBusInterface *
1073 g_dbus_object_manager_server_get_interface  (GDBusObjectManager  *_manager,
1074                                              const gchar         *object_path,
1075                                              const gchar         *interface_name)
1076 {
1077   GDBusInterface *ret;
1078   GDBusObject *object;
1079
1080   ret = NULL;
1081
1082   object = g_dbus_object_manager_get_object (_manager, object_path);
1083   if (object == NULL)
1084     goto out;
1085
1086   ret = g_dbus_object_get_interface (object, interface_name);
1087   g_object_unref (object);
1088
1089  out:
1090   return ret;
1091 }
1092
1093 static void
1094 dbus_object_manager_interface_init (GDBusObjectManagerIface *iface)
1095 {
1096   iface->get_object_path = g_dbus_object_manager_server_get_object_path;
1097   iface->get_objects     = g_dbus_object_manager_server_get_objects;
1098   iface->get_object      = g_dbus_object_manager_server_get_object;
1099   iface->get_interface   = g_dbus_object_manager_server_get_interface;
1100 }
1101
1102 /* ---------------------------------------------------------------------------------------------------- */
1103
1104 static void
1105 export_all (GDBusObjectManagerServer *manager)
1106 {
1107   GHashTableIter iter;
1108   const gchar *object_path;
1109   RegistrationData *data;
1110   GHashTableIter iface_iter;
1111   GDBusInterfaceSkeleton *iface;
1112   GError *error;
1113
1114   g_return_if_fail (manager->priv->connection != NULL);
1115
1116   error = NULL;
1117   g_warn_if_fail (manager->priv->manager_reg_id == 0);
1118   manager->priv->manager_reg_id = g_dbus_connection_register_object (manager->priv->connection,
1119                                                                      manager->priv->object_path,
1120                                                                      (GDBusInterfaceInfo *) &manager_interface_info,
1121                                                                      &manager_interface_vtable,
1122                                                                      manager,
1123                                                                      NULL, /* user_data_free_func */
1124                                                                      &error);
1125   if (manager->priv->manager_reg_id == 0)
1126     {
1127       g_warning ("%s: Error registering manager at %s: %s",
1128                  G_STRLOC,
1129                  manager->priv->object_path,
1130                  error->message);
1131       g_error_free (error);
1132     }
1133
1134   g_hash_table_iter_init (&iter, manager->priv->map_object_path_to_data);
1135   while (g_hash_table_iter_next (&iter, (gpointer) &object_path, (gpointer) &data))
1136     {
1137       g_hash_table_iter_init (&iface_iter, data->map_iface_name_to_iface);
1138       while (g_hash_table_iter_next (&iface_iter, NULL, (gpointer) &iface))
1139         {
1140           g_warn_if_fail (g_dbus_interface_skeleton_get_connection (iface) == NULL);
1141           error = NULL;
1142           if (!g_dbus_interface_skeleton_export (iface,
1143                                                  manager->priv->connection,
1144                                                  object_path,
1145                                                  &error))
1146             {
1147               g_warning ("%s: Error registering object at %s with interface %s: %s",
1148                          G_STRLOC,
1149                          object_path,
1150                          g_dbus_interface_skeleton_get_info (iface)->name,
1151                          error->message);
1152               g_error_free (error);
1153             }
1154         }
1155     }
1156 }
1157
1158 static void
1159 unexport_all (GDBusObjectManagerServer *manager, gboolean only_manager)
1160 {
1161   GHashTableIter iter;
1162   RegistrationData *data;
1163   GHashTableIter iface_iter;
1164   GDBusInterfaceSkeleton *iface;
1165
1166   g_return_if_fail (manager->priv->connection != NULL);
1167
1168   g_warn_if_fail (manager->priv->manager_reg_id > 0);
1169   if (manager->priv->manager_reg_id > 0)
1170     {
1171       g_warn_if_fail (g_dbus_connection_unregister_object (manager->priv->connection,
1172                                                            manager->priv->manager_reg_id));
1173       manager->priv->manager_reg_id = 0;
1174     }
1175   if (only_manager)
1176     goto out;
1177
1178   g_hash_table_iter_init (&iter, manager->priv->map_object_path_to_data);
1179   while (g_hash_table_iter_next (&iter, NULL, (gpointer) &data))
1180     {
1181       g_hash_table_iter_init (&iface_iter, data->map_iface_name_to_iface);
1182       while (g_hash_table_iter_next (&iface_iter, NULL, (gpointer) &iface))
1183         {
1184           g_warn_if_fail (g_dbus_interface_skeleton_get_connection (iface) != NULL);
1185           g_dbus_interface_skeleton_unexport (iface);
1186         }
1187     }
1188  out:
1189   ;
1190 }
1191
1192 /* ---------------------------------------------------------------------------------------------------- */