[glib] gdbus: Allow GDBusObjectManagerClient to work on peer connections



commit fb2d3aacb5998397586ce4523f987dff60a9ca85
Author: Stef Walter <stefw gnome org>
Date:   Fri Oct 26 10:30:29 2012 +0200

    gdbus: Allow GDBusObjectManagerClient to work on peer connections
    
    Allow GDBusObjectManagerClient to work on peer to peer DBus
    connections. Don't require that a unique bus name is available
    for the object manager, if the owned bus name is NULL.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=686920

 gio/gdbusobjectmanagerclient.c        |   70 ++++---
 gio/tests/.gitignore                  |    1 +
 gio/tests/Makefile.am                 |    3 +
 gio/tests/gdbus-peer-object-manager.c |  364 +++++++++++++++++++++++++++++++++
 4 files changed, 406 insertions(+), 32 deletions(-)
---
diff --git a/gio/gdbusobjectmanagerclient.c b/gio/gdbusobjectmanagerclient.c
index 799940c..486819d 100644
--- a/gio/gdbusobjectmanagerclient.c
+++ b/gio/gdbusobjectmanagerclient.c
@@ -263,6 +263,7 @@ g_dbus_object_manager_client_set_property (GObject       *_object,
                                            GParamSpec    *pspec)
 {
   GDBusObjectManagerClient *manager = G_DBUS_OBJECT_MANAGER_CLIENT (_object);
+  const gchar *name;
 
   switch (prop_id)
     {
@@ -287,8 +288,9 @@ g_dbus_object_manager_client_set_property (GObject       *_object,
 
     case PROP_NAME:
       g_assert (manager->priv->name == NULL);
-      g_assert (g_dbus_is_name (g_value_get_string (value)));
-      manager->priv->name = g_value_dup_string (value);
+      name = g_value_get_string (value);
+      g_assert (name == NULL || g_dbus_is_name (name));
+      manager->priv->name = g_strdup (name);
       break;
 
     case PROP_FLAGS:
@@ -585,7 +587,7 @@ g_dbus_object_manager_client_init (GDBusObjectManagerClient *manager)
  * g_dbus_object_manager_client_new_sync:
  * @connection: A #GDBusConnection.
  * @flags: Zero or more flags from the #GDBusObjectManagerClientFlags enumeration.
- * @name: The owner of the control object (unique or well-known name).
+ * @name: (allow-none): The owner of the control object (unique or well-known name), or %NULL when not using a message bus connection.
  * @object_path: The object path of the control object.
  * @get_proxy_type_func: (allow-none): A #GDBusProxyTypeFunc function or %NULL to always construct #GDBusProxy proxies.
  * @get_proxy_type_user_data: User data to pass to @get_proxy_type_func.
@@ -911,7 +913,8 @@ g_dbus_object_manager_client_get_connection (GDBusObjectManagerClient *manager)
  * g_dbus_object_manager_client_get_name:
  * @manager: A #GDBusObjectManagerClient
  *
- * Gets the name that @manager is for.
+ * Gets the name that @manager is for, or %NULL if not a message bus
+ * connection.
  *
  * Returns: A unique or well-known name. Do not free, the string
  * belongs to @manager.
@@ -1096,38 +1099,41 @@ static void
 subscribe_signals (GDBusObjectManagerClient *manager,
                    const gchar *name_owner)
 {
-  GError *error;
+  GError *error = NULL;
   GVariant *ret;
 
   g_return_if_fail (G_IS_DBUS_OBJECT_MANAGER_CLIENT (manager));
   g_return_if_fail (manager->priv->signal_subscription_id == 0);
-  g_return_if_fail (g_dbus_is_unique_name (name_owner));
+  g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner));
 
-  /* the bus daemon may not implement path_prefix so gracefully
-   * handle this by using a fallback
-   */
-  manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s',path_namespace='%s'",
-                                               name_owner,
-                                               manager->priv->object_path);
-
-  error = NULL;
-  ret = g_dbus_connection_call_sync (manager->priv->connection,
-                                     "org.freedesktop.DBus",
-                                     "/org/freedeskop/DBus",
-                                     "org.freedesktop.DBus",
-                                     "AddMatch",
-                                     g_variant_new ("(s)",
-                                                    manager->priv->match_rule),
-                                     NULL, /* reply_type */
-                                     G_DBUS_CALL_FLAGS_NONE,
-                                     -1, /* default timeout */
-                                     NULL, /* TODO: Cancellable */
-                                     &error);
-  if (ret != NULL)
+  if (name_owner != NULL)
     {
+      /* the bus daemon may not implement path_prefix so gracefully
+       * handle this by using a fallback
+       */
+      manager->priv->match_rule = g_strdup_printf ("type='signal',sender='%s',path_namespace='%s'",
+                                                   name_owner, manager->priv->object_path);
+
+      ret = g_dbus_connection_call_sync (manager->priv->connection,
+                                         "org.freedesktop.DBus",
+                                         "/org/freedeskop/DBus",
+                                         "org.freedesktop.DBus",
+                                         "AddMatch",
+                                         g_variant_new ("(s)",
+                                                        manager->priv->match_rule),
+                                         NULL, /* reply_type */
+                                         G_DBUS_CALL_FLAGS_NONE,
+                                         -1, /* default timeout */
+                                         NULL, /* TODO: Cancellable */
+                                         &error);
+
       /* yay, bus daemon supports path_namespace */
-      g_variant_unref (ret);
+      if (ret != NULL)
+        g_variant_unref (ret);
+    }
 
+  if (error == NULL)
+    {
       /* still need to ask GDBusConnection for the callbacks */
       manager->priv->signal_subscription_id =
         g_dbus_connection_signal_subscribe (manager->priv->connection,
@@ -1347,7 +1353,7 @@ initable_init (GInitable     *initable,
                     manager);
 
   manager->priv->name_owner = g_dbus_proxy_get_name_owner (manager->priv->control_proxy);
-  if (manager->priv->name_owner == NULL)
+  if (manager->priv->name_owner == NULL && manager->priv->name != NULL)
     {
       /* it's perfectly fine if there's no name owner.. we're just going to
        * wait until one is ready
@@ -1355,7 +1361,7 @@ initable_init (GInitable     *initable,
     }
   else
     {
-      /* yay, we have a name owner */
+      /* yay, we can get the objects */
       g_signal_connect (manager->priv->control_proxy,
                         "g-signal",
                         G_CALLBACK (on_control_proxy_g_signal),
@@ -1418,7 +1424,7 @@ add_interfaces (GDBusObjectManagerClient *manager,
   GList *interface_added_signals, *l;
   GDBusProxy *interface_proxy;
 
-  g_return_if_fail (g_dbus_is_unique_name (name_owner));
+  g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner));
 
   g_mutex_lock (&manager->priv->lock);
 
@@ -1615,7 +1621,7 @@ process_get_all_result (GDBusObjectManagerClient *manager,
   GVariant *ifaces_and_properties;
   GVariantIter iter;
 
-  g_return_if_fail (g_dbus_is_unique_name (name_owner));
+  g_return_if_fail (name_owner == NULL || g_dbus_is_unique_name (name_owner));
 
   arg0 = g_variant_get_child_value (value, 0);
   g_variant_iter_init (&iter, arg0);
diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore
index 1016851..1ef962b 100644
--- a/gio/tests/.gitignore
+++ b/gio/tests/.gitignore
@@ -56,6 +56,7 @@ gdbus-message
 gdbus-names
 gdbus-non-socket
 gdbus-peer
+gdbus-peer-object-manager
 gdbus-proxy
 gdbus-proxy-threads
 gdbus-proxy-well-known-name
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 5a1fdc5..2eed187 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -92,6 +92,7 @@ TEST_PROGS +=			\
 	gdbus-peer		\
 	gdbus-exit-on-close	\
 	gdbus-non-socket	\
+	gdbus-peer-object-manager \
 	appinfo			\
 	contenttype		\
 	mimeapps		\
@@ -322,6 +323,8 @@ BUILT_SOURCES += gdbus-test-codegen-generated.c gdbus-test-codegen-generated.h
 gdbus_test_codegen_SOURCES  = gdbus-test-codegen.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
 gdbus_test_codegen_SOURCES += gdbus-test-codegen-generated.c gdbus-test-codegen-generated.h
 gdbus_test_codegen_LDADD = $(progs_ldadd)
+
+gdbus_peer_object_manager_LDADD = $(progs_ldadd)
 endif # OS_UNIX
 
 gdbus_connection_SOURCES = gdbus-connection.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c
diff --git a/gio/tests/gdbus-peer-object-manager.c b/gio/tests/gdbus-peer-object-manager.c
new file mode 100644
index 0000000..3689b93
--- /dev/null
+++ b/gio/tests/gdbus-peer-object-manager.c
@@ -0,0 +1,364 @@
+/* GLib testing framework examples and tests
+ *
+ * Copyright (C) 2012 Red Hat Inc.
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Stef Walter <stefw gnome org>
+ */
+
+#include "config.h"
+
+#include <gio/gio.h>
+
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+typedef struct {
+  GDBusInterfaceSkeleton parent;
+  gint number;
+} MockInterface;
+
+typedef struct {
+  GDBusInterfaceSkeletonClass parent_class;
+} MockInterfaceClass;
+
+G_DEFINE_TYPE (MockInterface, mock_interface, G_TYPE_DBUS_INTERFACE_SKELETON);
+
+static void
+mock_interface_init (MockInterface *self)
+{
+
+}
+
+static GDBusInterfaceInfo *
+mock_interface_get_info (GDBusInterfaceSkeleton *skeleton)
+{
+  static GDBusPropertyInfo path_info = {
+    -1,
+    "Path",
+    "o",
+    G_DBUS_PROPERTY_INFO_FLAGS_READABLE,
+    NULL,
+  };
+
+  static GDBusPropertyInfo number_info = {
+    -1,
+    "Number",
+    "i",
+    G_DBUS_PROPERTY_INFO_FLAGS_READABLE,
+    NULL,
+  };
+
+  static GDBusPropertyInfo *property_info[] = {
+    &path_info,
+    &number_info,
+    NULL
+  };
+
+  static GDBusInterfaceInfo interface_info = {
+    -1,
+    (gchar *) "org.mock.Interface",
+    NULL,
+    NULL,
+    (GDBusPropertyInfo **) &property_info,
+    NULL
+  };
+
+  return &interface_info;
+}
+
+GVariant *
+mock_interface_get_property (GDBusConnection *connection,
+                             const gchar *sender,
+                             const gchar *object_path,
+                             const gchar *interface_name,
+                             const gchar *property_name,
+                             GError **error,
+                             gpointer user_data)
+{
+  MockInterface *self = user_data;
+  if (g_str_equal (property_name, "Path"))
+    return g_variant_new_object_path (object_path);
+  else if (g_str_equal (property_name, "Number"))
+    return g_variant_new_int32 (self->number);
+  else
+    return NULL;
+}
+
+static GDBusInterfaceVTable *
+mock_interface_get_vtable (GDBusInterfaceSkeleton *interface)
+{
+  static GDBusInterfaceVTable vtable = {
+    NULL,
+    mock_interface_get_property,
+    NULL,
+  };
+
+  return &vtable;
+}
+
+static GVariant *
+mock_interface_get_properties (GDBusInterfaceSkeleton *interface)
+{
+  GVariantBuilder builder;
+  GDBusInterfaceInfo *info;
+  GDBusInterfaceVTable *vtable;
+  guint n;
+
+  /* Groan, this is completely generic code and should be in gdbus */
+
+  info = g_dbus_interface_skeleton_get_info (interface);
+  vtable = g_dbus_interface_skeleton_get_vtable (interface);
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+  for (n = 0; info->properties[n] != NULL; n++)
+    {
+      if (info->properties[n]->flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE)
+        {
+          GVariant *value;
+          g_return_val_if_fail (vtable->get_property != NULL, NULL);
+          value = (vtable->get_property) (g_dbus_interface_skeleton_get_connection (interface), NULL,
+                                          g_dbus_interface_skeleton_get_object_path (interface),
+                                          info->name, info->properties[n]->name,
+                                          NULL, interface);
+          if (value != NULL)
+            {
+              g_variant_take_ref (value);
+              g_variant_builder_add (&builder, "{sv}", info->properties[n]->name, value);
+              g_variant_unref (value);
+            }
+        }
+    }
+
+  return g_variant_builder_end (&builder);
+}
+
+static void
+mock_interface_flush (GDBusInterfaceSkeleton *skeleton)
+{
+
+}
+
+static void
+mock_interface_class_init (MockInterfaceClass *klass)
+{
+  GDBusInterfaceSkeletonClass *skeleton_class = G_DBUS_INTERFACE_SKELETON_CLASS (klass);
+  skeleton_class->get_info = mock_interface_get_info;
+  skeleton_class->get_properties = mock_interface_get_properties;
+  skeleton_class->flush = mock_interface_flush;
+  skeleton_class->get_vtable = mock_interface_get_vtable;
+}
+typedef struct {
+  GDBusConnection *server;
+  GDBusConnection *client;
+  GMainLoop *loop;
+  GAsyncResult *result;
+} Test;
+
+static void
+on_server_connection (GObject *source,
+                      GAsyncResult *result,
+                      gpointer user_data)
+{
+  Test *test = user_data;
+  GError *error = NULL;
+
+  g_assert (test->server == NULL);
+  test->server = g_dbus_connection_new_finish (result, &error);
+  g_assert_no_error (error);
+  g_assert (test->server != NULL);
+
+  if (test->server && test->client)
+    g_main_loop_quit (test->loop);
+}
+
+static void
+on_client_connection (GObject *source,
+                      GAsyncResult *result,
+                      gpointer user_data)
+{
+  Test *test = user_data;
+  GError *error = NULL;
+
+  g_assert (test->client == NULL);
+  test->client = g_dbus_connection_new_finish (result, &error);
+  g_assert_no_error (error);
+  g_assert (test->client != NULL);
+
+  if (test->server && test->client)
+    g_main_loop_quit (test->loop);
+}
+
+static void
+setup (Test *test,
+       gconstpointer unused)
+{
+  GError *error = NULL;
+  GSocket *socket;
+  GSocketConnection *stream;
+  gchar *guid;
+  int pair[2];
+
+  test->loop = g_main_loop_new (NULL, FALSE);
+
+  if (socketpair (AF_UNIX, SOCK_STREAM, 0, pair) < 0)
+    {
+      g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (errno),
+                   g_strerror (errno));
+      g_assert_no_error (error);
+    }
+
+  /* Build up the server stuff */
+  socket = g_socket_new_from_fd (pair[1], &error);
+  g_assert_no_error (error);
+
+  stream = g_socket_connection_factory_create_connection (socket);
+  g_assert (stream != NULL);
+  g_object_unref (socket);
+
+  guid = g_dbus_generate_guid ();
+  g_dbus_connection_new (G_IO_STREAM (stream), guid,
+                         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER |
+                         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS,
+                         NULL, NULL, on_server_connection, test);
+  g_object_unref (stream);
+  g_free (guid);
+
+  /* Build up the client stuff */
+  socket = g_socket_new_from_fd (pair[0], &error);
+  g_assert_no_error (error);
+
+  stream = g_socket_connection_factory_create_connection (socket);
+  g_assert (stream != NULL);
+  g_object_unref (socket);
+
+  g_dbus_connection_new (G_IO_STREAM (stream), NULL,
+                         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
+                         G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_ALLOW_ANONYMOUS,
+                         NULL, NULL, on_client_connection, test);
+
+  g_main_loop_run (test->loop);
+
+  g_assert (test->server);
+  g_assert (test->client);
+}
+
+static void
+teardown (Test *test,
+          gconstpointer unused)
+{
+  g_clear_object (&test->client);
+  g_clear_object (&test->server);
+  g_main_loop_unref (test->loop);
+}
+
+static void
+on_result (GObject *source,
+           GAsyncResult *result,
+           gpointer user_data)
+{
+  Test *test = user_data;
+  g_assert (test->result == NULL);
+  test->result = g_object_ref (result);
+  g_main_loop_quit (test->loop);
+
+}
+
+static void
+test_object_manager (Test *test,
+                     gconstpointer unused)
+{
+  GDBusObjectManager *client;
+  GDBusObjectManagerServer *server;
+  MockInterface *mock;
+  GDBusObjectSkeleton *skeleton;
+  const gchar *dbus_name;
+  GError *error = NULL;
+  GDBusInterface *proxy;
+  GVariant *prop;
+
+  server = g_dbus_object_manager_server_new ("/objects");
+
+  mock = g_object_new (mock_interface_get_type (), NULL);
+  mock->number = 1;
+  skeleton = g_dbus_object_skeleton_new ("/objects/number_1");
+  g_dbus_object_skeleton_add_interface (skeleton, G_DBUS_INTERFACE_SKELETON (mock));
+  g_dbus_object_manager_server_export (server, skeleton);
+
+  mock = g_object_new (mock_interface_get_type (), NULL);
+  mock->number = 2;
+  skeleton = g_dbus_object_skeleton_new ("/objects/number_2");
+  g_dbus_object_skeleton_add_interface (skeleton, G_DBUS_INTERFACE_SKELETON (mock));
+  g_dbus_object_manager_server_export (server, skeleton);
+
+  g_dbus_object_manager_server_set_connection (server, test->server);
+
+  dbus_name = NULL;
+
+  g_dbus_object_manager_client_new (test->client, G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
+                                    dbus_name, "/objects", NULL, NULL, NULL, NULL, on_result, test);
+
+  g_main_loop_run (test->loop);
+  client = g_dbus_object_manager_client_new_finish (test->result, &error);
+  g_assert_no_error (error);
+  g_clear_object (&test->result);
+
+  proxy = g_dbus_object_manager_get_interface (client, "/objects/number_1", "org.mock.Interface");
+  g_assert (proxy != NULL);
+  prop = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Path");
+  g_assert (prop != NULL);
+  g_assert_cmpstr ((gchar *)g_variant_get_type (prop), ==, (gchar *)G_VARIANT_TYPE_OBJECT_PATH);
+  g_assert_cmpstr (g_variant_get_string (prop, NULL), ==, "/objects/number_1");
+  g_variant_unref (prop);
+  prop = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Number");
+  g_assert (prop != NULL);
+  g_assert_cmpstr ((gchar *)g_variant_get_type (prop), ==, (gchar *)G_VARIANT_TYPE_INT32);
+  g_assert_cmpint (g_variant_get_int32 (prop), ==, 1);
+  g_variant_unref (prop);
+  g_object_unref (proxy);
+
+  proxy = g_dbus_object_manager_get_interface (client, "/objects/number_2", "org.mock.Interface");
+  g_assert (proxy != NULL);
+  prop = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Path");
+  g_assert (prop != NULL);
+  g_assert_cmpstr ((gchar *)g_variant_get_type (prop), ==, (gchar *)G_VARIANT_TYPE_OBJECT_PATH);
+  g_assert_cmpstr (g_variant_get_string (prop, NULL), ==, "/objects/number_2");
+  g_variant_unref (prop);
+  prop = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), "Number");
+  g_assert (prop != NULL);
+  g_assert_cmpstr ((gchar *)g_variant_get_type (prop), ==, (gchar *)G_VARIANT_TYPE_INT32);
+  g_assert_cmpint (g_variant_get_int32 (prop), ==, 2);
+  g_variant_unref (prop);
+  g_object_unref (proxy);
+
+  g_object_unref (server);
+  g_object_unref (client);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+
+  g_test_add ("/gdbus/peer-object-manager", Test, NULL, setup, test_object_manager, teardown);
+
+  return g_test_run();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]