From fb2d3aacb5998397586ce4523f987dff60a9ca85 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 26 Oct 2012 10:30:29 +0200 Subject: [PATCH] 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 | 72 ++--- gio/tests/.gitignore | 1 + gio/tests/Makefile.am | 3 + gio/tests/gdbus-peer-object-manager.c | 364 ++++++++++++++++++++++++++ 4 files changed, 407 insertions(+), 33 deletions(-) create mode 100644 gio/tests/gdbus-peer-object-manager.c diff --git a/gio/gdbusobjectmanagerclient.c b/gio/gdbusobjectmanagerclient.c index 799940caa..486819df9 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) { - /* yay, bus daemon supports path_namespace */ - g_variant_unref (ret); + /* 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 */ + 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 101685174..1ef962bca 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 5a1fdc5f1..2eed18762 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 000000000..3689b9304 --- /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 + */ + +#include "config.h" + +#include + +#include + +#include +#include +#include + +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(); +}