Commit 00f6d781 authored by Lars Uebernickel's avatar Lars Uebernickel

g_dbus_connection_signal_subscribe: add path and namespace matching

https://bugzilla.gnome.org/show_bug.cgi?id=695156
parent 690d6b97
......@@ -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++)
{
......
......@@ -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;
/**
......
......@@ -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();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment