[kdbus] sync with kdbus (kdbus.h - commit: 5ae1ecac44cb)
[platform/upstream/glib.git] / gio / gdbusobjectmanagerserver.c
index c31b2ba..bb6f8c2 100644 (file)
@@ -13,9 +13,7 @@
  * 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, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Author: David Zeuthen <davidz@redhat.com>
  */
  * @include: gio/gio.h
  *
  * #GDBusObjectManagerServer is used to export #GDBusObject instances using
- * the standardized <ulink
- * url="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">org.freedesktop.DBus.ObjectManager</ulink>
+ * 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.
  *
- * See #GDBusObjectManagerClient for the client-side code that is intended to
- * be used with #GDBusObjectManagerServer.
+ * See #GDBusObjectManagerClient 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.
  */
 
 typedef struct
@@ -62,16 +62,24 @@ typedef struct
 
 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 *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;
@@ -89,7 +97,8 @@ enum
 static void dbus_object_manager_interface_init (GDBusObjectManagerIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (GDBusObjectManagerServer, g_dbus_object_manager_server, G_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (G_TYPE_DBUS_OBJECT_MANAGER, dbus_object_manager_interface_init));
+                         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);
 
@@ -98,29 +107,35 @@ 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);
-  if (manager->priv->manager_reg_id > 0)
-    g_warn_if_fail (g_dbus_connection_unregister_object (manager->priv->connection, manager->priv->manager_reg_id));
-  g_object_unref (manager->priv->connection);
   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)
+g_dbus_object_manager_server_get_property (GObject    *object,
+                                           guint       prop_id,
+                                           GValue     *value,
+                                           GParamSpec *pspec)
 {
-  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (_object);
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object);
 
   switch (prop_id)
     {
     case PROP_CONNECTION:
-      g_value_set_object (value, g_dbus_object_manager_server_get_connection (manager));
+      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:
@@ -128,25 +143,23 @@ g_dbus_object_manager_server_get_property (GObject    *_object,
       break;
 
     default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      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)
+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);
+  GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object);
 
   switch (prop_id)
     {
     case PROP_CONNECTION:
-      g_assert (manager->priv->connection == NULL);
-      g_assert (G_IS_DBUS_CONNECTION (g_value_get_object (value)));
-      manager->priv->connection = g_value_dup_object (value);
+      g_dbus_object_manager_server_set_connection (manager, g_value_get_object (value));
       break;
 
     case PROP_OBJECT_PATH:
@@ -157,7 +170,7 @@ g_dbus_object_manager_server_set_property (GObject       *_object,
       break;
 
     default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (_object, prop_id, pspec);
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
     }
 }
@@ -187,7 +200,6 @@ g_dbus_object_manager_server_class_init (GDBusObjectManagerServerClass *klass)
                                                         G_TYPE_DBUS_CONNECTION,
                                                         G_PARAM_READABLE |
                                                         G_PARAM_WRITABLE |
-                                                        G_PARAM_CONSTRUCT_ONLY |
                                                         G_PARAM_STATIC_STRINGS));
 
   /**
@@ -207,16 +219,13 @@ g_dbus_object_manager_server_class_init (GDBusObjectManagerServerClass *klass)
                                                         G_PARAM_WRITABLE |
                                                         G_PARAM_CONSTRUCT_ONLY |
                                                         G_PARAM_STATIC_STRINGS));
-
-  g_type_class_add_private (klass, sizeof (GDBusObjectManagerServerPrivate));
 }
 
 static void
 g_dbus_object_manager_server_init (GDBusObjectManagerServer *manager)
 {
-  manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
-                                               G_TYPE_DBUS_OBJECT_MANAGER_SERVER,
-                                               GDBusObjectManagerServerPrivate);
+  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,
@@ -225,28 +234,68 @@ g_dbus_object_manager_server_init (GDBusObjectManagerServer *manager)
 
 /**
  * g_dbus_object_manager_server_new:
- * @connection: A #GDBusConnection.
  * @object_path: The object path to export the manager object at.
  *
  * Creates a new #GDBusObjectManagerServer object.
  *
- * TODO: make it so that the objects are not exported yet -
- * e.g. start()/stop() semantics.
+ * 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 <ulink
+ * url="http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager">InterfacesAdded</ulink>
+ * signals being emitted.
  *
  * Returns: A #GDBusObjectManagerServer object. Free with g_object_unref().
  *
  * Since: 2.30
  */
 GDBusObjectManagerServer *
-g_dbus_object_manager_server_new (GDBusConnection *connection,
-                           const gchar     *object_path)
+g_dbus_object_manager_server_new (const gchar     *object_path)
 {
-  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
   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,
-                                              "connection", connection,
-                                              "object-path", object_path,
-                                              NULL));
+                                                     "object-path", object_path,
+                                                     NULL));
+}
+
+/**
+ * g_dbus_object_manager_server_set_connection:
+ * @manager: A #GDBusObjectManagerServer.
+ * @connection: (allow-none): 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:
+  ;
 }
 
 /**
@@ -255,45 +304,49 @@ g_dbus_object_manager_server_new (GDBusConnection *connection,
  *
  * Gets the #GDBusConnection used by @manager.
  *
- * Returns: (transfer none): A #GDBusConnection object. Do not free,
- *   the object belongs to @manager.
+ * Returns: (transfer full): 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);
-  return manager->priv->connection;
+  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)
+                                    GDBusInterfaceSkeleton  *interface_skeleton,
+                                    const gchar             *object_path)
 {
   GDBusInterfaceInfo *info;
   GError *error;
-  const gchar *object_path;
-
-  object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (data->object));
 
   info = g_dbus_interface_skeleton_get_info (interface_skeleton);
   error = NULL;
-  if (!g_dbus_interface_skeleton_export (interface_skeleton,
-                                         data->manager->priv->connection,
-                                         object_path,
-                                         &error))
+  if (data->manager->priv->connection != NULL)
     {
-      /* TODO: probably wrong to complain on stderr */
-      g_warning ("%s: Error registering object at %s with interface %s: %s",
-                 G_STRLOC,
-                 object_path,
-                 info->name,
-                 error->message);
-      g_error_free (error);
-      goto out;
+      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);
@@ -308,11 +361,8 @@ registration_data_export_interface (RegistrationData        *data,
       /* 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);
+      g_dbus_object_manager_server_emit_interfaces_added (data->manager, data, interfaces, object_path);
     }
-
- out:
-  ;
 }
 
 static void
@@ -326,7 +376,8 @@ registration_data_unexport_interface (RegistrationData       *data,
   iface = g_hash_table_lookup (data->map_iface_name_to_iface, info->name);
   g_assert (iface != NULL);
 
-  g_dbus_interface_skeleton_unexport (iface);
+  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));
 
@@ -349,7 +400,11 @@ on_interface_added (GDBusObject    *object,
                     gpointer        user_data)
 {
   RegistrationData *data = user_data;
-  registration_data_export_interface (data, G_DBUS_INTERFACE_SKELETON (interface));
+  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
@@ -358,7 +413,9 @@ on_interface_removed (GDBusObject    *object,
                       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);
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -374,7 +431,10 @@ registration_data_free (RegistrationData *data)
 
   g_hash_table_iter_init (&iter, data->map_iface_name_to_iface);
   while (g_hash_table_iter_next (&iter, NULL, (gpointer) &iface))
-    g_dbus_interface_skeleton_unexport (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);
@@ -385,35 +445,15 @@ registration_data_free (RegistrationData *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)
+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;
-  const gchar *object_path;
-
-  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));
@@ -423,7 +463,7 @@ g_dbus_object_manager_server_export (GDBusObjectManagerServer  *manager,
 
   data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
   if (data != NULL)
-    g_dbus_object_manager_server_unexport (manager, object_path);
+    g_dbus_object_manager_server_unexport_unlocked (manager, object_path);
 
   data = g_new0 (RegistrationData, 1);
   data->object = g_object_ref (object);
@@ -449,17 +489,16 @@ g_dbus_object_manager_server_export (GDBusObjectManagerServer  *manager,
   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);
+      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_foreach (existing_interfaces, (GFunc) g_object_unref, NULL);
-  g_list_free (existing_interfaces);
+  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);
+  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,
@@ -468,15 +507,43 @@ g_dbus_object_manager_server_export (GDBusObjectManagerServer  *manager,
 }
 
 /**
+ * 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 <literal>_N</literal> (with N being a natural number) to
- * @object<!-- -->'s object path if an object with the given path
- * already exists. As such, the #GDBusObjectProxy:object-path property
- * of @object may be modified.
+ * 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
  */
@@ -495,6 +562,8 @@ g_dbus_object_manager_server_export_uniquely (GDBusObjectManagerServer *manager,
   g_return_if_fail (G_IS_DBUS_OBJECT (object));
   g_return_if_fail (g_str_has_prefix (orig_object_path, manager->priv->object_path_ending_in_slash));
 
+  g_mutex_lock (&manager->priv->lock);
+
   object_path = g_strdup (orig_object_path);
   count = 1;
   modified = FALSE;
@@ -511,37 +580,66 @@ g_dbus_object_manager_server_export_uniquely (GDBusObjectManagerServer *manager,
       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_dbus_object_manager_server_export (manager, object);
-
   g_free (object_path);
   g_free (orig_object_path);
+
 }
 
 /**
- * g_dbus_object_manager_server_unexport:
+ * g_dbus_object_manager_server_is_exported:
  * @manager: A #GDBusObjectManagerServer.
- * @object_path: An object path.
+ * @object: An object.
  *
- * If @manager has an object at @path, removes the object. Otherwise
- * does nothing.
+ * Returns whether @object is currently exported on @manager.
  *
- * Note that @object_path must be in the hierarchy rooted by the
- * object path for @manager.
+ * Returns: %TRUE if @object is exported
  *
- * Since: 2.30
- */
-void
-g_dbus_object_manager_server_unexport (GDBusObjectManagerServer  *manager,
-                                const gchar         *object_path)
+ * 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_if_fail (G_IS_DBUS_OBJECT_MANAGER_SERVER (manager));
-  g_return_if_fail (g_variant_is_object_path (object_path));
-  g_return_if_fail (g_str_has_prefix (object_path, manager->priv->object_path_ending_in_slash));
+  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 (g_str_has_prefix (object_path, manager->priv->object_path_ending_in_slash), FALSE);
+
+  ret = FALSE;
 
   data = g_hash_table_lookup (manager->priv->map_object_path_to_data, object_path);
   if (data != NULL)
@@ -560,7 +658,37 @@ g_dbus_object_manager_server_unexport (GDBusObjectManagerServer  *manager,
       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;
 }
 
 
@@ -697,6 +825,8 @@ manager_method_call (GDBusConnection       *connection,
   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}}}"));
@@ -712,10 +842,11 @@ manager_method_call (GDBusConnection       *connection,
           g_hash_table_iter_init (&interface_iter, data->map_iface_name_to_iface);
           while (g_hash_table_iter_next (&interface_iter, NULL, (gpointer) &iface))
             {
-              g_variant_builder_add_value (&interfaces_builder,
-                                           g_variant_new ("{s@a{sv}}",
-                                                          g_dbus_interface_skeleton_get_info (iface)->name,
-                                                          g_dbus_interface_skeleton_get_properties (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,
@@ -736,6 +867,7 @@ manager_method_call (GDBusConnection       *connection,
                                              "Unknown method %s - only GetManagedObjects() is supported",
                                              method_name);
     }
+  g_mutex_unlock (&manager->priv->lock);
 }
 
 static const GDBusInterfaceVTable manager_interface_vtable =
@@ -751,25 +883,9 @@ static void
 g_dbus_object_manager_server_constructed (GObject *object)
 {
   GDBusObjectManagerServer *manager = G_DBUS_OBJECT_MANAGER_SERVER (object);
-  GError *error;
 
-  error = NULL;
-  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)
-    {
-      /* TODO: probably wrong to complain on stderr */
-      g_warning ("%s: Error registering manager at %s: %s",
-                 G_STRLOC,
-                 manager->priv->object_path,
-                 error->message);
-      g_error_free (error);
-    }
+  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);
@@ -777,28 +893,31 @@ g_dbus_object_manager_server_constructed (GObject *object)
 
 static void
 g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *manager,
-                                             RegistrationData   *data,
-                                             const gchar *const *interfaces)
+                                                    RegistrationData   *data,
+                                                    const gchar *const *interfaces,
+                                                    const gchar *object_path)
 {
   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 ("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);
-      g_variant_builder_add_value (&array_builder,
-                                   g_variant_new ("{s@a{sv}}",
-                                                  interfaces[n],
-                                                  g_dbus_interface_skeleton_get_properties (iface)));
+      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;
-  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,
@@ -809,18 +928,23 @@ g_dbus_object_manager_server_emit_interfaces_added (GDBusObjectManagerServer *ma
                                                 &array_builder),
                                  &error);
   g_assert_no_error (error);
+ out:
+  ;
 }
 
 static void
 g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *manager,
-                                               RegistrationData   *data,
-                                               const gchar *const *interfaces)
+                                                      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]);
@@ -837,6 +961,8 @@ g_dbus_object_manager_server_emit_interfaces_removed (GDBusObjectManagerServer *
                                                 &array_builder),
                                  &error);
   g_assert_no_error (error);
+ out:
+  ;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -849,6 +975,8 @@ g_dbus_object_manager_server_get_objects (GDBusObjectManager  *_manager)
   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))
@@ -856,6 +984,8 @@ g_dbus_object_manager_server_get_objects (GDBusObjectManager  *_manager)
       ret = g_list_prepend (ret, g_object_ref (data->object));
     }
 
+  g_mutex_unlock (&manager->priv->lock);
+
   return ret;
 }
 
@@ -875,9 +1005,13 @@ g_dbus_object_manager_server_get_object (GDBusObjectManager *_manager,
   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 (data->object);
+  g_mutex_unlock (&manager->priv->lock);
+
   return ret;
 }
 
@@ -910,3 +1044,95 @@ dbus_object_manager_interface_init (GDBusObjectManagerIface *iface)
   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:
+  ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */