Add path_prefix match rule

Add a new path_prefix match rule that can be used for efficient
implementations of the org.freedesktop.DBus.ObjectManager interface
(see bug 34869).

https://bugs.freedesktop.org/show_bug.cgi?id=34870

Signed-off-by: David Zeuthen <davidz@redhat.com>
This commit is contained in:
David Zeuthen 2011-03-08 10:32:34 -05:00 committed by Simon McVittie
parent 36cc4c547c
commit 0980e63aac
3 changed files with 192 additions and 1 deletions

View file

@ -41,6 +41,7 @@ struct BusMatchRule
char *sender;
char *destination;
char *path;
char *path_prefix;
unsigned int *arg_lens;
char **args;
@ -94,6 +95,7 @@ bus_match_rule_unref (BusMatchRule *rule)
dbus_free (rule->sender);
dbus_free (rule->destination);
dbus_free (rule->path);
dbus_free (rule->path_prefix);
dbus_free (rule->arg_lens);
/* can't use dbus_free_string_array() since there
@ -206,6 +208,18 @@ match_rule_to_string (BusMatchRule *rule)
goto nomem;
}
if (rule->flags & BUS_MATCH_PATH_PREFIX)
{
if (_dbus_string_get_length (&str) > 0)
{
if (!_dbus_string_append (&str, ","))
goto nomem;
}
if (!_dbus_string_append_printf (&str, "path_prefix='%s'", rule->path_prefix))
goto nomem;
}
if (rule->flags & BUS_MATCH_SENDER)
{
if (_dbus_string_get_length (&str) > 0)
@ -388,6 +402,25 @@ bus_match_rule_set_path (BusMatchRule *rule,
return TRUE;
}
dbus_bool_t
bus_match_rule_set_path_prefix (BusMatchRule *rule,
const char *path_prefix)
{
char *new;
_dbus_assert (path_prefix != NULL);
new = _dbus_strdup (path_prefix);
if (new == NULL)
return FALSE;
rule->flags |= BUS_MATCH_PATH_PREFIX;
dbus_free (rule->path_prefix);
rule->path_prefix = new;
return TRUE;
}
dbus_bool_t
bus_match_rule_set_arg (BusMatchRule *rule,
int arg,
@ -1018,6 +1051,34 @@ bus_match_rule_parse (DBusConnection *matches_go_to,
goto failed;
}
}
else if (strcmp (key, "path_prefix") == 0)
{
int path_prefix_len;
if (rule->flags & BUS_MATCH_PATH_PREFIX)
{
dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
"Key %s specified twice in match rule\n", key);
goto failed;
}
path_prefix_len = len;
if (_dbus_string_ends_with_c_str (&tmp_str, "/"))
path_prefix_len--;
if (!_dbus_validate_path (&tmp_str, 0, path_prefix_len))
{
dbus_set_error (error, DBUS_ERROR_MATCH_RULE_INVALID,
"Path prefix '%s' is invalid\n", value);
goto failed;
}
if (!bus_match_rule_set_path_prefix (rule, value))
{
BUS_SET_OOM (error);
goto failed;
}
}
else if (strcmp (key, "destination") == 0)
{
if (rule->flags & BUS_MATCH_DESTINATION)
@ -1349,6 +1410,10 @@ match_rule_equal (BusMatchRule *a,
if ((a->flags & BUS_MATCH_PATH) &&
strcmp (a->path, b->path) != 0)
return FALSE;
if ((a->flags & BUS_MATCH_PATH_PREFIX) &&
strcmp (a->path_prefix, b->path_prefix) != 0)
return FALSE;
if ((a->flags & BUS_MATCH_INTERFACE) &&
strcmp (a->interface, b->interface) != 0)
@ -1611,6 +1676,17 @@ connection_is_primary_owner (DBusConnection *connection,
return bus_service_get_primary_owners_connection (service) == connection;
}
static dbus_bool_t
str_has_prefix (const char *str, const char *prefix)
{
size_t prefix_len;
prefix_len = strlen (prefix);
if (strncmp (str, prefix, prefix_len) == 0)
return TRUE;
else
return FALSE;
}
static dbus_bool_t
match_rule_matches (BusMatchRule *rule,
DBusConnection *sender,
@ -1722,6 +1798,20 @@ match_rule_matches (BusMatchRule *rule,
return FALSE;
}
if (flags & BUS_MATCH_PATH_PREFIX)
{
const char *path;
_dbus_assert (rule->path_prefix != NULL);
path = dbus_message_get_path (message);
if (path == NULL)
return FALSE;
if (!str_has_prefix (path, rule->path_prefix))
return FALSE;
}
if (flags & BUS_MATCH_ARGS)
{
int i;
@ -2616,6 +2706,83 @@ test_path_matching (void)
bus_match_rule_unref (rule);
}
static const char*
path_prefix_should_match_message_1[] = {
"type='signal',path_prefix='/foo'",
"type='signal',path_prefix='/foo/'",
"type='signal',path_prefix='/foo/TheObjectManager'",
NULL
};
static const char*
path_prefix_should_not_match_message_1[] = {
"type='signal',path_prefix='/bar'",
"type='signal',path_prefix='/bar/'",
"type='signal',path_prefix='/bar/TheObjectManager'",
NULL
};
static const char*
path_prefix_should_match_message_2[] = {
"type='signal',path_prefix='/foo/TheObjectManager'",
"type='signal',path_prefix='/foo/TheObjectManager/'",
NULL
};
static const char*
path_prefix_should_not_match_message_2[] = {
NULL
};
static const char*
path_prefix_should_match_message_3[] = {
"type='signal',path_prefix='/foo/TheObjectManager'",
NULL
};
static const char*
path_prefix_should_not_match_message_3[] = {
"type='signal',path_prefix='/foo/TheObjectManager/'",
NULL
};
static void
test_matching_path_prefix (void)
{
DBusMessage *message1;
DBusMessage *message2;
DBusMessage *message3;
message1 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
_dbus_assert (message1 != NULL);
if (!dbus_message_set_path (message1, "/foo/TheObjectManager"))
_dbus_assert_not_reached ("oom");
message2 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
_dbus_assert (message2 != NULL);
if (!dbus_message_set_path (message2, "/foo/TheObjectManager/child_object"))
_dbus_assert_not_reached ("oom");
message3 = dbus_message_new (DBUS_MESSAGE_TYPE_SIGNAL);
_dbus_assert (message3 != NULL);
if (!dbus_message_set_path (message3, "/foo/TheObjectManagerOther"))
_dbus_assert_not_reached ("oom");
check_matching (message1, 1,
path_prefix_should_match_message_1,
path_prefix_should_not_match_message_1);
check_matching (message2, 2,
path_prefix_should_match_message_2,
path_prefix_should_not_match_message_2);
check_matching (message3, 3,
path_prefix_should_match_message_3,
path_prefix_should_not_match_message_3);
dbus_message_unref (message3);
dbus_message_unref (message2);
dbus_message_unref (message1);
}
dbus_bool_t
bus_signals_test (const DBusString *test_data_dir)
{
@ -2632,6 +2799,7 @@ bus_signals_test (const DBusString *test_data_dir)
test_equality ();
test_matching ();
test_path_matching ();
test_matching_path_prefix ();
return TRUE;
}

View file

@ -37,7 +37,8 @@ typedef enum
BUS_MATCH_SENDER = 1 << 3,
BUS_MATCH_DESTINATION = 1 << 4,
BUS_MATCH_PATH = 1 << 5,
BUS_MATCH_ARGS = 1 << 6
BUS_MATCH_ARGS = 1 << 6,
BUS_MATCH_PATH_PREFIX = 1 << 7
} BusMatchFlags;
BusMatchRule* bus_match_rule_new (DBusConnection *matches_go_to);
@ -56,6 +57,8 @@ dbus_bool_t bus_match_rule_set_destination (BusMatchRule *rule,
const char *destination);
dbus_bool_t bus_match_rule_set_path (BusMatchRule *rule,
const char *path);
dbus_bool_t bus_match_rule_set_path_prefix (BusMatchRule *rule,
const char *path_prefix);
dbus_bool_t bus_match_rule_set_arg (BusMatchRule *rule,
int arg,
const DBusString *value,

View file

@ -3721,6 +3721,26 @@
<entry>Matches messages which are sent from or to the given object. An example of a
path match is path='/org/freedesktop/Hal/Manager'</entry>
</row>
<row>
<entry><literal>path_prefix</literal></entry>
<entry>An object path optionally ending in a slash</entry>
<entry>
<para>
Matches messages which are sent from or to an
object for which the object path is a prefix of
the given value. Examples of matches are
path_prefix='/org/Application/ObjectManager' or
path_prefix='/org/Application/ContactObjects/'.
</para>
<para>
<emphasis>
This match key was added in version 0.15 of the
D-Bus specification and implemented by the bus
daemon in dbus 1.4.7 and later.
</emphasis>
</para>
</entry>
</row>
<row>
<entry><literal>destination</literal></entry>
<entry>A unique name (see <xref linkend="term-unique-name"/>)</entry>