(root)/
glib-2.79.0/
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   * 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  /* ---------------------------------------------------------------------------------------------------- */