[glib] g_dbus_connection_signal_subscribe: add path and namespace matching
- From: Lars Uebernickel <larsu src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] g_dbus_connection_signal_subscribe: add path and namespace matching
- Date: Tue, 9 Apr 2013 07:12:46 +0000 (UTC)
commit 00f6d78125a02c014bcfb31e47b082ff30ede1d6
Author: Lars Uebernickel <lars uebernickel canonical com>
Date: Mon Apr 8 08:13:10 2013 +0200
g_dbus_connection_signal_subscribe: add path and namespace matching
https://bugzilla.gnome.org/show_bug.cgi?id=695156
gio/gdbusconnection.c | 93 ++++++++++++++++++++++++++++++++++++-----
gio/gioenums.h | 9 ++++-
gio/tests/gdbus-connection.c | 78 +++++++++++++++++++++++++++++++++++
3 files changed, 167 insertions(+), 13 deletions(-)
---
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index f4c63a9..0efd2c7 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -3246,6 +3246,7 @@ typedef struct
gchar *member;
gchar *object_path;
gchar *arg0;
+ GDBusSignalFlags flags;
GArray *subscribers;
} SignalData;
@@ -3273,17 +3274,17 @@ signal_data_free (SignalData *signal_data)
}
static gchar *
-args_to_rule (const gchar *sender,
- const gchar *interface_name,
- const gchar *member,
- const gchar *object_path,
- const gchar *arg0,
- gboolean negate)
+args_to_rule (const gchar *sender,
+ const gchar *interface_name,
+ const gchar *member,
+ const gchar *object_path,
+ const gchar *arg0,
+ GDBusSignalFlags flags)
{
GString *rule;
rule = g_string_new ("type='signal'");
- if (negate)
+ if (flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE)
g_string_prepend_c (rule, '-');
if (sender != NULL)
g_string_append_printf (rule, ",sender='%s'", sender);
@@ -3293,8 +3294,16 @@ args_to_rule (const gchar *sender,
g_string_append_printf (rule, ",member='%s'", member);
if (object_path != NULL)
g_string_append_printf (rule, ",path='%s'", object_path);
+
if (arg0 != NULL)
- g_string_append_printf (rule, ",arg0='%s'", arg0);
+ {
+ if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH)
+ g_string_append_printf (rule, ",arg0path='%s'", arg0);
+ else if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)
+ g_string_append_printf (rule, ",arg0namespace='%s'", arg0);
+ else
+ g_string_append_printf (rule, ",arg0='%s'", arg0);
+ }
return g_string_free (rule, FALSE);
}
@@ -3417,6 +3426,11 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data)
* tracking the name owner of the well-known name and use that when
* processing the received signal.
*
+ * If one of %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE or
+ * %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH are given, @arg0 is
+ * interpreted as part of a namespace or path. The first argument
+ * of a signal is matched against that part as specified by D-Bus.
+ *
* Returns: A subscription identifier that can be used with g_dbus_connection_signal_unsubscribe().
*
* Since: 2.26
@@ -3456,6 +3470,8 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), 0);
g_return_val_if_fail (callback != NULL, 0);
g_return_val_if_fail (check_initialized (connection), 0);
+ g_return_val_if_fail (!((flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH) && (flags &
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)), 0);
+ g_return_val_if_fail (!(arg0 == NULL && (flags & (G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH |
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE))), 0);
CONNECTION_LOCK (connection);
@@ -3467,8 +3483,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
* the usual way, but the '-' prevents the match rule from ever
* actually being send to the bus (either for add or remove).
*/
- rule = args_to_rule (sender, interface_name, member, object_path, arg0,
- (flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE) != 0);
+ rule = args_to_rule (sender, interface_name, member, object_path, arg0, flags);
if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0))
sender_unique_name = sender;
@@ -3498,6 +3513,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
signal_data->member = g_strdup (member);
signal_data->object_path = g_strdup (object_path);
signal_data->arg0 = g_strdup (arg0);
+ signal_data->flags = flags;
signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber));
g_array_append_val (signal_data->subscribers, subscriber);
@@ -3733,6 +3749,43 @@ signal_instance_free (SignalInstance *signal_instance)
g_free (signal_instance);
}
+static gboolean
+namespace_rule_matches (const gchar *namespace,
+ const gchar *name)
+{
+ gint len_namespace;
+ gint len_name;
+
+ len_namespace = strlen (namespace);
+ len_name = strlen (name);
+
+ if (len_name < len_namespace)
+ return FALSE;
+
+ if (memcmp (namespace, name, len_namespace) != 0)
+ return FALSE;
+
+ return len_namespace == len_name || name[len_namespace] == '.';
+}
+
+static gboolean
+path_rule_matches (const gchar *path_a,
+ const gchar *path_b)
+{
+ gint len_a, len_b;
+
+ len_a = strlen (path_a);
+ len_b = strlen (path_b);
+
+ if (len_a < len_b && path_a[len_a - 1] != '/')
+ return FALSE;
+
+ if (len_b < len_a && path_b[len_b - 1] != '/')
+ return FALSE;
+
+ return memcmp (path_a, path_b, MIN (len_a, len_b)) == 0;
+}
+
/* called in GDBusWorker thread WITH lock held */
static void
schedule_callbacks (GDBusConnection *connection,
@@ -3786,8 +3839,24 @@ schedule_callbacks (GDBusConnection *connection,
if (signal_data->object_path != NULL && g_strcmp0 (signal_data->object_path, path) != 0)
continue;
- if (signal_data->arg0 != NULL && g_strcmp0 (signal_data->arg0, arg0) != 0)
- continue;
+ if (signal_data->arg0 != NULL)
+ {
+ if (arg0 == NULL)
+ continue;
+
+ if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)
+ {
+ if (!namespace_rule_matches (signal_data->arg0, arg0))
+ continue;
+ }
+ else if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH)
+ {
+ if (!path_rule_matches (signal_data->arg0, arg0))
+ continue;
+ }
+ else if (!g_str_equal (signal_data->arg0, arg0))
+ continue;
+ }
for (m = 0; m < signal_data->subscribers->len; m++)
{
diff --git a/gio/gioenums.h b/gio/gioenums.h
index 3831ec9..9767226 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -1272,6 +1272,11 @@ typedef enum
* @G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE: Don't actually send the AddMatch
* D-Bus call for this signal subscription. This gives you more control
* over which match rules you add (but you must add them manually).
+ * @G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE: Match first arguments that
+ * contain a bus or interface name with the given namespace.
+ * @G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH: Match first arguments that
+ * contain an object path that is either equivalent to the given path,
+ * or one of the paths is a subpath of the other.
*
* Flags used when subscribing to signals via g_dbus_connection_signal_subscribe().
*
@@ -1280,7 +1285,9 @@ typedef enum
typedef enum /*< flags >*/
{
G_DBUS_SIGNAL_FLAGS_NONE = 0,
- G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE = (1<<0)
+ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE = (1<<0),
+ G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE = (1<<1),
+ G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH = (1<<2)
} GDBusSignalFlags;
/**
diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c
index e48d61f..f57c776 100644
--- a/gio/tests/gdbus-connection.c
+++ b/gio/tests/gdbus-connection.c
@@ -713,6 +713,83 @@ test_connection_signals (void)
session_bus_down ();
}
+static void
+test_match_rule (GDBusConnection *connection,
+ GDBusSignalFlags flags,
+ gchar *arg0_rule,
+ gchar *arg0,
+ gboolean should_match)
+{
+ guint subscription_ids[2];
+ gint emissions = 0;
+ gint matches = 0;
+ GError *error = NULL;
+
+ subscription_ids[0] = g_dbus_connection_signal_subscribe (connection,
+ NULL, "org.gtk.ExampleInterface", "Foo", "/",
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ test_connection_signal_handler,
+ &emissions, NULL);
+ subscription_ids[1] = g_dbus_connection_signal_subscribe (connection,
+ NULL, "org.gtk.ExampleInterface", "Foo", "/",
+ arg0_rule,
+ flags,
+ test_connection_signal_handler,
+ &matches, NULL);
+ g_assert_cmpint (subscription_ids[0], !=, 0);
+ g_assert_cmpint (subscription_ids[1], !=, 0);
+
+ g_dbus_connection_emit_signal (connection,
+ NULL, "/", "org.gtk.ExampleInterface",
+ "Foo", g_variant_new ("(s)", arg0),
+ &error);
+ g_assert_no_error (error);
+
+ /* synchronously ping a non-existent method to make sure the signals are dispatched */
+ g_dbus_connection_call_sync (connection, "org.gtk.ExampleInterface", "/", "org.gtk.ExampleInterface",
+ "Bar", g_variant_new ("()"), G_VARIANT_TYPE_UNIT, G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, NULL);
+
+ while (g_main_context_iteration (NULL, FALSE))
+ ;
+
+ g_assert_cmpint (emissions, ==, 1);
+ g_assert_cmpint (matches, ==, should_match ? 1 : 0);
+
+ g_dbus_connection_signal_unsubscribe (connection, subscription_ids[0]);
+ g_dbus_connection_signal_unsubscribe (connection, subscription_ids[1]);
+}
+
+static void
+test_connection_signal_match_rules (void)
+{
+ GDBusConnection *con;
+
+ session_bus_up ();
+ con = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_NONE, "foo", "foo", TRUE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_NONE, "foo", "bar", FALSE);
+
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "", FALSE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org", FALSE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org.gtk", TRUE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org.gtk.Example", TRUE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org.gtk+", FALSE);
+
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/", "/", TRUE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/", "", FALSE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/Example", "/org/gtk/Example", TRUE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/", "/org/gtk/Example", TRUE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/Example", "/org/gtk/", TRUE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/Example", "/org/gtk", FALSE);
+ test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk+", "/org/gtk", FALSE);
+
+ g_object_unref (con);
+ session_bus_down ();
+}
+
/* ---------------------------------------------------------------------------------------------------- */
typedef struct
@@ -1153,6 +1230,7 @@ main (int argc,
g_test_add_func ("/gdbus/connection/life-cycle", test_connection_life_cycle);
g_test_add_func ("/gdbus/connection/send", test_connection_send);
g_test_add_func ("/gdbus/connection/signals", test_connection_signals);
+ g_test_add_func ("/gdbus/connection/signal-match-rules", test_connection_signal_match_rules);
g_test_add_func ("/gdbus/connection/filter", test_connection_filter);
g_test_add_func ("/gdbus/connection/serials", test_connection_serials);
return g_test_run();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]