/* GDBus - GLib D-Bus Library * * Copyright (C) 2008-2010 Red Hat, Inc. * * SPDX-License-Identifier: LGPL-2.1-or-later * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . * * Author: David Zeuthen */ #include "config.h" #include "gdbusobjectmanager.h" #include "gdbusobjectmanagerserver.h" #include "gdbusobject.h" #include "gdbusobjectskeleton.h" #include "gdbusinterfaceskeleton.h" #include "gdbusconnection.h" #include "gdbusintrospection.h" #include "gdbusmethodinvocation.h" #include "gdbuserror.h" #include "gioerror.h" #include "gdbusprivate.h" #include "glibintl.h" /** * GDBusObjectManagerServer: * * `GDBusObjectManagerServer` is used to export [iface@Gio.DBusObject] instances * using the standardized * [`org.freedesktop.DBus.ObjectManager`](http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) * interface. For example, remote D-Bus clients can get all objects * and properties in a single call. Additionally, any change in the * object hierarchy is broadcast using signals. This means that D-Bus * clients can keep caches up to date by only listening to D-Bus * signals. * * The recommended path to export an object manager at is the path form of the * well-known name of a D-Bus service, or below. For example, if a D-Bus service * is available at the well-known name `net.example.ExampleService1`, the object * manager should typically be exported at `/net/example/ExampleService1`, or * below (to allow for multiple object managers in a service). * * It is supported, but not recommended, to export an object manager at the root * path, `/`. * * See [class@Gio.DBusObjectManagerClient] for the client-side code that is * intended to be used with `GDBusObjectManagerServer` or any D-Bus * object implementing the `org.freedesktop.DBus.ObjectManager` interface. * * Since: 2.30 */ typedef struct { GDBusObjectSkeleton *object; GDBusObjectManagerServer *manager; GHashTable *map_iface_name_to_iface; gboolean exported; } RegistrationData; static void registration_data_free (RegistrationData *data); static void export_all (GDBusObjectManagerServer *manager); static void unexport_all (GDBusObjectManagerServer *manager, gboolean only_manager); static void g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *manager, RegistrationData *data, const gchar *const *interfaces, const gchar *object_path); static void g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *manager, RegistrationData *data, const gchar *const *interfaces); static gboolean g_dbus_object_manager_server_unexport_unlocked (GDBusObjectManagerServer *manager, const gchar *object_path); struct _GDBusObjectManagerServerPrivate { GMutex lock; GDBusConnection *connection; gchar *object_path; gchar *object_path_ending_in_slash; GHashTable *map_object_path_to_data; guint manager_reg_id; }; enum { PROP_0, PROP_CONNECTION, PROP_OBJECT_PATH }; static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface); G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerServer, g_dbus_object_manager_server, G_TYPE_OBJECT, G_ADD_PRIVATE (GDBusObjectManagerServer) G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init)) static void g_dbus_object_manager_server_constructed (GObject *object); static void g_dbus_object_manager_server_finalize (GObject *object) { GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object); if (manager->priv->connection != NULL) { unexport_all (manager, TRUE); g_object_unref (manager->priv->connection); } g_hash_table_unref (manager->priv->map_object_path_to_data); g_free (manager->priv->object_path); g_free (manager->priv->object_path_ending_in_slash); g_mutex_clear (&manager->priv->lock); if (G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->finalize != NULL) G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->finalize (object); } static void g_dbus_object_manager_server_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object); switch (prop_id) { case PROP_CONNECTION: g_mutex_lock (&manager->priv->lock); g_value_set_object (value, manager->priv->connection); g_mutex_unlock (&manager->priv->lock); break; case PROP_OBJECT_PATH: g_value_set_string (value, g_dbus_object_manager_get_object_path (G_DBUS_OBJECT_MANAGER (manager))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void g_dbus_object_manager_server_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object); switch (prop_id) { case PROP_CONNECTION: g_dbus_object_manager_server_set_connection (manager, g_value_get_object (value)); break; case PROP_OBJECT_PATH: g_assert (manager->priv->object_path == NULL); g_assert (g_variant_is_object_path (g_value_get_string (value))); manager->priv->object_path = g_value_dup_string (value); if (g_str_equal (manager->priv->object_path, "/")) manager->priv->object_path_ending_in_slash = g_strdup (manager->priv->object_path); else manager->priv->object_path_ending_in_slash = g_strdup_printf ("%s/", manager->priv->object_path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void g_dbus_object_manager_server_class_init (GDBusObjectManagerServerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = g_dbus_object_manager_server_finalize; gobject_class->constructed = g_dbus_object_manager_server_constructed; gobject_class->set_property = g_dbus_object_manager_server_set_property; gobject_class->get_property = g_dbus_object_manager_server_get_property; /** * GDBusObjectManagerServer:connection: * * The #GDBusConnection to export objects on. * * Since: 2.30 */ g_object_class_install_property (gobject_class, PROP_CONNECTION, g_param_spec_object ("connection", NULL, NULL, G_TYPE_DBUS_CONNECTION, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS)); /** * GDBusObjectManagerServer:object-path: * * The object path to register the manager object at. * * Since: 2.30 */ g_object_class_install_property (gobject_class, PROP_OBJECT_PATH, g_param_spec_string ("object-path", NULL, NULL, NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void g_dbus_object_manager_server_init (GDBusObjectManagerServer *manager) { manager->priv = g_dbus_object_manager_server_get_instance_private (manager); g_mutex_init (&manager->priv->lock); manager->priv->map_object_path_to_data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) registration_data_free); } /** * g_dbus_object_manager_server_new: * @object_path: The object path to export the manager object at. * * Creates a new #GDBusObjectManagerServer object. * * The returned server isn't yet exported on any connection. To do so, * use g_dbus_object_manager_server_set_connection(). Normally you * want to export all of your objects before doing so to avoid * [InterfacesAdded](http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) * signals being emitted. * * Returns: A #GDBusObjectManagerServer object. Free with g_object_unref(). * * Since: 2.30 */ GDBusObjectManagerServer * g_dbus_object_manager_server_new (const gchar *object_path) { g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); return G_DBUS_OBJECT_MANAGER_SERVER (g_object_new (G_TYPE_DBUS_OBJECT_MANAGER_SERVER, "object-path", object_path, NULL)); } /** * g_dbus_object_manager_server_set_connection: * @manager: A #GDBusObjectManagerServer. * @connection: (nullable): A #GDBusConnection or %NULL. * * Exports all objects managed by @manager on @connection. If * @connection is %NULL, stops exporting objects. */ void g_dbus_object_manager_server_set_connection (GDBusObjectManagerServer *manager, GDBusConnection *connection) { g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager)); g_return_if_fail (connection == NULL || G_IS_DBUS_CONNECTION (connection)); g_mutex_lock (&manager->priv->lock); if (manager->priv->connection == connection) { g_mutex_unlock (&manager->priv->lock); goto out; } if (manager->priv->connection != NULL) { unexport_all (manager, FALSE); g_object_unref (manager->priv->connection); manager->priv->connection = NULL; } manager->priv->connection = connection != NULL ? g_object_ref (connection) : NULL; if (manager->priv->connection != NULL) export_all (manager); g_mutex_unlock (&manager->priv->lock); g_object_notify (G_OBJECT (manager), "connection"); out: ; } /** * g_dbus_object_manager_server_get_connection: * @manager: A #GDBusObjectManagerServer * * Gets the #GDBusConnection used by @manager. * * Returns: (transfer full) (nullable): A #GDBusConnection object or %NULL if * @manager isn't exported on a connection. The returned object should * be freed with g_object_unref(). * * Since: 2.30 */ GDBusConnection * g_dbus_object_manager_server_get_connection (GDBusObjectManagerServer *manager) { GDBusConnection *ret; g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), NULL); g_mutex_lock (&manager->priv->lock); ret = manager->priv->connection != NULL ? g_object_ref (manager->priv->connection) : NULL; g_mutex_unlock (&manager->priv->lock); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static void registration_data_export_interface (RegistrationData *data, GDBusInterfaceSkeleton *interface_skeleton, const gchar *object_path) { GDBusInterfaceInfo *info; GError *error; info = g_dbus_interface_skeleton_get_info (interface_skeleton); error = NULL; if (data->manager->priv->connection != NULL) { if (!g_dbus_interface_skeleton_export (interface_skeleton, data->manager->priv->connection, object_path, &error)) { g_warning ("%s: Error registering object at %s with interface %s: %s", G_STRLOC, object_path, info->name, error->message); g_error_free (error); } } g_assert (g_hash_table_lookup (data->map_iface_name_to_iface, info->name) == NULL); g_hash_table_insert (data->map_iface_name_to_iface, info->name, g_object_ref (interface_skeleton)); /* if we are already exported, then... */ if (data->exported) { const gchar *interfaces[2]; /* emit InterfacesAdded on the ObjectManager object */ interfaces[0] = info->name; interfaces[1] = NULL; g_dbus_object_manager_server_emit_interfaces_added (data->manager, data, interfaces, object_path); } } static void registration_data_unexport_interface (RegistrationData *data, GDBusInterfaceSkeleton *interface_skeleton) { GDBusInterfaceInfo *info; GDBusInterfaceSkeleton *iface; info = g_dbus_interface_skeleton_get_info (interface_skeleton); iface = g_hash_table_lookup (data->map_iface_name_to_iface, info->name); g_assert (iface != NULL); if (data->manager->priv->connection != NULL) g_dbus_interface_skeleton_unexport (iface); g_warn_if_fail (g_hash_table_remove (data->map_iface_name_to_iface, info->name)); /* if we are already exported, then... */ if (data->exported) { const gchar *interfaces[2]; /* emit InterfacesRemoved on the ObjectManager object */ interfaces[0] = info->name; interfaces[1] = NULL; g_dbus_object_manager_server_emit_interfaces_removed (data->manager, data, interfaces); } } /* ---------------------------------------------------------------------------------------------------- */ static void on_interface_added (GDBusObject *object, GDBusInterface *interface, gpointer user_data) { RegistrationData *data = user_data; const gchar *object_path; g_mutex_lock (&data->manager->priv->lock); object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object)); registration_data_export_interface (data, G_DBUS_INTERFACE_SKELETON (interface), object_path); g_mutex_unlock (&data->manager->priv->lock); } static void on_interface_removed (GDBusObject *object, GDBusInterface *interface, gpointer user_data) { RegistrationData *data = user_data; g_mutex_lock (&data->manager->priv->lock); registration_data_unexport_interface (data, G_DBUS_INTERFACE_SKELETON (interface)); g_mutex_unlock (&data->manager->priv->lock); } /* ---------------------------------------------------------------------------------------------------- */ static void registration_data_free (RegistrationData *data) { GHashTableIter iter; GDBusInterfaceSkeleton *iface; data->exported = FALSE; g_hash_table_iter_init (&iter, data->map_iface_name_to_iface); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &iface)) { if (data->manager->priv->connection != NULL) g_dbus_interface_skeleton_unexport (iface); } g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_added), data); g_signal_handlers_disconnect_by_func (data->object, G_CALLBACK (on_interface_removed), data); g_object_unref (data->object); g_hash_table_destroy (data->map_iface_name_to_iface); g_free (data); } /* Validate whether an object path is valid as a child of the manager. According * to the specification: * https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager * this means that: * > All returned object paths are children of the object path implementing this * > interface, i.e. their object paths start with the ObjectManager's object * > path plus '/' * * For example, if the manager is at `/org/gnome/Example`, children will be * `/org/gnome/Example/(.+)`. * * It is permissible (but not encouraged) for the manager to be at `/`. If so, * children will be `/(.+)`. */ static gboolean is_valid_child_object_path (GDBusObjectManagerServer *manager, const gchar *child_object_path) { /* Historically GDBus accepted @child_object_paths at `/` if the @manager * itself is also at `/". This is not spec-compliant, but making GDBus enforce * the spec more strictly would be an incompatible change. * * See https://gitlab.gnome.org/GNOME/glib/-/issues/2500 */ g_warn_if_fail (!g_str_equal (child_object_path, manager->priv->object_path_ending_in_slash)); return g_str_has_prefix (child_object_path, manager->priv->object_path_ending_in_slash); } /* ---------------------------------------------------------------------------------------------------- */ static void g_dbus_object_manager_server_export_unlocked (GDBusObjectManagerServer *manager, GDBusObjectSkeleton *object, const gchar *object_path) { RegistrationData *data; GList *existing_interfaces; GList *l; GPtrArray *interface_names; g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager)); g_return_if_fail (G_IS_DBUS_OBJECT (object)); g_return_if_fail (is_valid_child_object_path (manager, object_path)); interface_names = g_ptr_array_new (); data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path); if (data != NULL) g_dbus_object_manager_server_unexport_unlocked (manager, object_path); data = g_new0 (RegistrationData, 1); data->object = g_object_ref (object); data->manager = manager; data->map_iface_name_to_iface = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_object_unref); g_signal_connect (object, "interface-added", G_CALLBACK (on_interface_added), data); g_signal_connect (object, "interface-removed", G_CALLBACK (on_interface_removed), data); /* Register all known interfaces - note that data->exported is FALSE so * we don't emit any InterfacesAdded signals. */ existing_interfaces = g_dbus_object_get_interfaces (G_DBUS_OBJECT (object)); for (l = existing_interfaces; l != NULL; l = l->next) { GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (l->data); registration_data_export_interface (data, interface_skeleton, object_path); g_ptr_array_add (interface_names, g_dbus_interface_skeleton_get_info (interface_skeleton)->name); } g_list_free_full (existing_interfaces, g_object_unref); g_ptr_array_add (interface_names, NULL); data->exported = TRUE; /* now emit InterfacesAdded() for all the interfaces */ g_dbus_object_manager_server_emit_interfaces_added (manager, data, (const gchar *const *) interface_names->pdata, object_path); g_ptr_array_unref (interface_names); g_hash_table_insert (manager->priv->map_object_path_to_data, g_strdup (object_path), data); } /** * g_dbus_object_manager_server_export: * @manager: A #GDBusObjectManagerServer. * @object: A #GDBusObjectSkeleton. * * Exports @object on @manager. * * If there is already a #GDBusObject exported at the object path, * then the old object is removed. * * The object path for @object must be in the hierarchy rooted by the * object path for @manager. * * Note that @manager will take a reference on @object for as long as * it is exported. * * Since: 2.30 */ void g_dbus_object_manager_server_export (GDBusObjectManagerServer *manager, GDBusObjectSkeleton *object) { g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager)); g_mutex_lock (&manager->priv->lock); g_dbus_object_manager_server_export_unlocked (manager, object, g_dbus_object_get_object_path (G_DBUS_OBJECT (object))); g_mutex_unlock (&manager->priv->lock); } /** * g_dbus_object_manager_server_export_uniquely: * @manager: A #GDBusObjectManagerServer. * @object: An object. * * Like g_dbus_object_manager_server_export() but appends a string of * the form _N (with N being a natural number) to @object's object path * if an object with the given path already exists. As such, the * #GDBusObjectProxy:g-object-path property of @object may be modified. * * Since: 2.30 */ void g_dbus_object_manager_server_export_uniquely (GDBusObjectManagerServer *manager, GDBusObjectSkeleton *object) { const gchar *orig_object_path; gchar *object_path; guint count; gboolean modified; orig_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object)); g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager)); g_return_if_fail (G_IS_DBUS_OBJECT (object)); g_return_if_fail (is_valid_child_object_path (manager, orig_object_path)); g_mutex_lock (&manager->priv->lock); object_path = g_strdup (orig_object_path); count = 1; modified = FALSE; while (TRUE) { RegistrationData *data; data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path); if (data == NULL) { break; } g_free (object_path); object_path = g_strdup_printf ("%s_%d", orig_object_path, count++); modified = TRUE; } g_dbus_object_manager_server_export_unlocked (manager, object, object_path); g_mutex_unlock (&manager->priv->lock); if (modified) g_dbus_object_skeleton_set_object_path (G_DBUS_OBJECT_SKELETON (object), object_path); g_free (object_path); } /** * g_dbus_object_manager_server_is_exported: * @manager: A #GDBusObjectManagerServer. * @object: An object. * * Returns whether @object is currently exported on @manager. * * Returns: %TRUE if @object is exported * * Since: 2.34 **/ gboolean g_dbus_object_manager_server_is_exported (GDBusObjectManagerServer *manager, GDBusObjectSkeleton *object) { RegistrationData *data = NULL; const gchar *object_path; gboolean object_is_exported; g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), FALSE); g_return_val_if_fail (G_IS_DBUS_OBJECT (object), FALSE); g_mutex_lock (&manager->priv->lock); object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object)); if (object_path != NULL) data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path); object_is_exported = (data != NULL); g_mutex_unlock (&manager->priv->lock); return object_is_exported; } /* ---------------------------------------------------------------------------------------------------- */ static gboolean g_dbus_object_manager_server_unexport_unlocked (GDBusObjectManagerServer *manager, const gchar *object_path) { RegistrationData *data; gboolean ret; g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), FALSE); g_return_val_if_fail (g_variant_is_object_path (object_path), FALSE); g_return_val_if_fail (is_valid_child_object_path (manager, object_path), FALSE); ret = FALSE; data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path); if (data != NULL) { GPtrArray *interface_names; GHashTableIter iter; const gchar *iface_name; interface_names = g_ptr_array_new (); g_hash_table_iter_init (&iter, data->map_iface_name_to_iface); while (g_hash_table_iter_next (&iter, (gpointer) &iface_name, NULL)) g_ptr_array_add (interface_names, (gpointer) iface_name); g_ptr_array_add (interface_names, NULL); /* now emit InterfacesRemoved() for all the interfaces */ g_dbus_object_manager_server_emit_interfaces_removed (manager, data, (const gchar *const *) interface_names->pdata); g_ptr_array_unref (interface_names); g_hash_table_remove (manager->priv->map_object_path_to_data, object_path); ret = TRUE; } return ret; } /** * g_dbus_object_manager_server_unexport: * @manager: A #GDBusObjectManagerServer. * @object_path: An object path. * * If @manager has an object at @path, removes the object. Otherwise * does nothing. * * Note that @object_path must be in the hierarchy rooted by the * object path for @manager. * * Returns: %TRUE if object at @object_path was removed, %FALSE otherwise. * * Since: 2.30 */ gboolean g_dbus_object_manager_server_unexport (GDBusObjectManagerServer *manager, const gchar *object_path) { gboolean ret; g_return_val_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager), FALSE); g_mutex_lock (&manager->priv->lock); ret = g_dbus_object_manager_server_unexport_unlocked (manager, object_path); g_mutex_unlock (&manager->priv->lock); return ret; } /* ---------------------------------------------------------------------------------------------------- */ static const GDBusArgInfo manager_interfaces_added_signal_info_arg0 = { -1, "object_path", "o", (GDBusAnnotationInfo**) NULL, }; static const GDBusArgInfo manager_interfaces_added_signal_info_arg1 = { -1, "interfaces_and_properties", "a{sa{sv}}", (GDBusAnnotationInfo**) NULL, }; static const GDBusArgInfo * const manager_interfaces_added_signal_info_arg_pointers[] = { &manager_interfaces_added_signal_info_arg0, &manager_interfaces_added_signal_info_arg1, NULL }; static const GDBusSignalInfo manager_interfaces_added_signal_info = { -1, "InterfacesAdded", (GDBusArgInfo**) &manager_interfaces_added_signal_info_arg_pointers, (GDBusAnnotationInfo**) NULL }; /* ---------- */ static const GDBusArgInfo manager_interfaces_removed_signal_info_arg0 = { -1, "object_path", "o", (GDBusAnnotationInfo**) NULL, }; static const GDBusArgInfo manager_interfaces_removed_signal_info_arg1 = { -1, "interfaces", "as", (GDBusAnnotationInfo**) NULL, }; static const GDBusArgInfo * const manager_interfaces_removed_signal_info_arg_pointers[] = { &manager_interfaces_removed_signal_info_arg0, &manager_interfaces_removed_signal_info_arg1, NULL }; static const GDBusSignalInfo manager_interfaces_removed_signal_info = { -1, "InterfacesRemoved", (GDBusArgInfo**) &manager_interfaces_removed_signal_info_arg_pointers, (GDBusAnnotationInfo**) NULL }; /* ---------- */ static const GDBusSignalInfo * const manager_signal_info_pointers[] = { &manager_interfaces_added_signal_info, &manager_interfaces_removed_signal_info, NULL }; /* ---------- */ static const GDBusArgInfo manager_get_all_method_info_out_arg0 = { -1, "object_paths_interfaces_and_properties", "a{oa{sa{sv}}}", (GDBusAnnotationInfo**) NULL, }; static const GDBusArgInfo * const manager_get_all_method_info_out_arg_pointers[] = { &manager_get_all_method_info_out_arg0, NULL }; static const GDBusMethodInfo manager_get_all_method_info = { -1, "GetManagedObjects", (GDBusArgInfo**) NULL, (GDBusArgInfo**) &manager_get_all_method_info_out_arg_pointers, (GDBusAnnotationInfo**) NULL }; static const GDBusMethodInfo * const manager_method_info_pointers[] = { &manager_get_all_method_info, NULL }; /* ---------- */ static const GDBusInterfaceInfo manager_interface_info = { -1, DBUS_INTERFACE_OBJECT_MANAGER, (GDBusMethodInfo **) manager_method_info_pointers, (GDBusSignalInfo **) manager_signal_info_pointers, (GDBusPropertyInfo **) NULL, (GDBusAnnotationInfo **) NULL }; static void manager_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (user_data); GVariantBuilder array_builder; GHashTableIter object_iter; RegistrationData *data; g_mutex_lock (&manager->priv->lock); if (g_strcmp0 (method_name, "GetManagedObjects") == 0) { g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{oa{sa{sv}}}")); g_hash_table_iter_init (&object_iter, manager->priv->map_object_path_to_data); while (g_hash_table_iter_next (&object_iter, NULL, (gpointer) &data)) { GVariantBuilder interfaces_builder; GHashTableIter interface_iter; GDBusInterfaceSkeleton *iface; const gchar *iter_object_path; g_variant_builder_init (&interfaces_builder, G_VARIANT_TYPE ("a{sa{sv}}")); g_hash_table_iter_init (&interface_iter, data->map_iface_name_to_iface); while (g_hash_table_iter_next (&interface_iter, NULL, (gpointer) &iface)) { GVariant *properties = g_dbus_interface_skeleton_get_properties (iface); g_variant_builder_add (&interfaces_builder, "{s@a{sv}}", g_dbus_interface_skeleton_get_info (iface)->name, properties); g_variant_unref (properties); } iter_object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object)); g_variant_builder_add (&array_builder, "{oa{sa{sv}}}", iter_object_path, &interfaces_builder); } g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{oa{sa{sv}}})", &array_builder)); } else { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Unknown method %s - only GetManagedObjects() is supported", method_name); } g_mutex_unlock (&manager->priv->lock); } static const GDBusInterfaceVTable manager_interface_vtable = { manager_method_call, /* handle_method_call */ NULL, /* get_property */ NULL, /* set_property */ { 0 } }; /* ---------------------------------------------------------------------------------------------------- */ static void g_dbus_object_manager_server_constructed (GObject *object) { GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object); if (manager->priv->connection != NULL) export_all (manager); if (G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->constructed != NULL) G_OBJECT_CLASS (g_dbus_object_manager_server_parent_class)->constructed (object); } static void g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *manager, RegistrationData *data, const gchar *const *interfaces, const gchar *object_path) { GVariantBuilder array_builder; GError *error; guint n; if (data->manager->priv->connection == NULL) goto out; g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("a{sa{sv}}")); for (n = 0; interfaces[n] != NULL; n++) { GDBusInterfaceSkeleton *iface; GVariant *properties; iface = g_hash_table_lookup (data->map_iface_name_to_iface, interfaces[n]); g_assert (iface != NULL); properties = g_dbus_interface_skeleton_get_properties (iface); g_variant_builder_add (&array_builder, "{s@a{sv}}", interfaces[n], properties); g_variant_unref (properties); } error = NULL; g_dbus_connection_emit_signal (data->manager->priv->connection, NULL, /* destination_bus_name */ manager->priv->object_path, manager_interface_info.name, "InterfacesAdded", g_variant_new ("(oa{sa{sv}})", object_path, &array_builder), &error); if (error) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) g_warning ("Couldn't emit InterfacesAdded signal: %s", error->message); g_error_free (error); } out: ; } static void g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *manager, RegistrationData *data, const gchar *const *interfaces) { GVariantBuilder array_builder; GError *error; guint n; const gchar *object_path; if (data->manager->priv->connection == NULL) goto out; g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as")); for (n = 0; interfaces[n] != NULL; n++) g_variant_builder_add (&array_builder, "s", interfaces[n]); error = NULL; object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object)); g_dbus_connection_emit_signal (data->manager->priv->connection, NULL, /* destination_bus_name */ manager->priv->object_path, manager_interface_info.name, "InterfacesRemoved", g_variant_new ("(oas)", object_path, &array_builder), &error); if (error) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CLOSED)) g_warning ("Couldn't emit InterfacesRemoved signal: %s", error->message); g_error_free (error); } out: ; } /* ---------------------------------------------------------------------------------------------------- */ static GList * g_dbus_object_manager_server_get_objects (GDBusObjectManager *_manager) { GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager); GList *ret; GHashTableIter iter; RegistrationData *data; g_mutex_lock (&manager->priv->lock); ret = NULL; g_hash_table_iter_init (&iter, manager->priv->map_object_path_to_data); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &data)) { ret = g_list_prepend (ret, g_object_ref (data->object)); } g_mutex_unlock (&manager->priv->lock); return ret; } static const gchar * g_dbus_object_manager_server_get_object_path (GDBusObjectManager *_manager) { GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager); return manager->priv->object_path; } static GDBusObject * g_dbus_object_manager_server_get_object (GDBusObjectManager *_manager, const gchar *object_path) { GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_manager); GDBusObject *ret; RegistrationData *data; ret = NULL; g_mutex_lock (&manager->priv->lock); data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path); if (data != NULL) ret = g_object_ref (G_DBUS_OBJECT (data->object)); g_mutex_unlock (&manager->priv->lock); return ret; } static GDBusInterface * g_dbus_object_manager_server_get_interface (GDBusObjectManager *_manager, const gchar *object_path, const gchar *interface_name) { GDBusInterface *ret; GDBusObject *object; ret = NULL; object = g_dbus_object_manager_get_object (_manager, object_path); if (object == NULL) goto out; ret = g_dbus_object_get_interface (object, interface_name); g_object_unref (object); out: return ret; } static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface) { iface->get_object_path = g_dbus_object_manager_server_get_object_path; iface->get_objects = g_dbus_object_manager_server_get_objects; iface->get_object = g_dbus_object_manager_server_get_object; iface->get_interface = g_dbus_object_manager_server_get_interface; } /* ---------------------------------------------------------------------------------------------------- */ static void export_all (GDBusObjectManagerServer *manager) { GHashTableIter iter; const gchar *object_path; RegistrationData *data; GHashTableIter iface_iter; GDBusInterfaceSkeleton *iface; GError *error; g_return_if_fail (manager->priv->connection != NULL); error = NULL; g_warn_if_fail (manager->priv->manager_reg_id == 0); manager->priv->manager_reg_id = g_dbus_connection_register_object (manager->priv->connection, manager->priv->object_path, (GDBusInterfaceInfo *) &manager_interface_info, &manager_interface_vtable, manager, NULL, /* user_data_free_func */ &error); if (manager->priv->manager_reg_id == 0) { g_warning ("%s: Error registering manager at %s: %s", G_STRLOC, manager->priv->object_path, error->message); g_error_free (error); } g_hash_table_iter_init (&iter, manager->priv->map_object_path_to_data); while (g_hash_table_iter_next (&iter, (gpointer) &object_path, (gpointer) &data)) { g_hash_table_iter_init (&iface_iter, data->map_iface_name_to_iface); while (g_hash_table_iter_next (&iface_iter, NULL, (gpointer) &iface)) { g_warn_if_fail (g_dbus_interface_skeleton_get_connection (iface) == NULL); error = NULL; if (!g_dbus_interface_skeleton_export (iface, manager->priv->connection, object_path, &error)) { g_warning ("%s: Error registering object at %s with interface %s: %s", G_STRLOC, object_path, g_dbus_interface_skeleton_get_info (iface)->name, error->message); g_error_free (error); } } } } static void unexport_all (GDBusObjectManagerServer *manager, gboolean only_manager) { GHashTableIter iter; RegistrationData *data; GHashTableIter iface_iter; GDBusInterfaceSkeleton *iface; g_return_if_fail (manager->priv->connection != NULL); g_warn_if_fail (manager->priv->manager_reg_id > 0); if (manager->priv->manager_reg_id > 0) { g_warn_if_fail (g_dbus_connection_unregister_object (manager->priv->connection, manager->priv->manager_reg_id)); manager->priv->manager_reg_id = 0; } if (only_manager) goto out; g_hash_table_iter_init (&iter, manager->priv->map_object_path_to_data); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &data)) { g_hash_table_iter_init (&iface_iter, data->map_iface_name_to_iface); while (g_hash_table_iter_next (&iface_iter, NULL, (gpointer) &iface)) { g_warn_if_fail (g_dbus_interface_skeleton_get_connection (iface) != NULL); g_dbus_interface_skeleton_unexport (iface); } } out: ; } /* ---------------------------------------------------------------------------------------------------- */