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