summaryrefslogtreecommitdiff
path: root/src/libsystemd-bus
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2013-03-31 16:16:37 +0200
committerLennart Poettering <lennart@poettering.net>2013-03-31 16:16:37 +0200
commit392d5b378ceae5e1fd7c91ca545fcf4cd105744a (patch)
treee231fe77155323de76b535cd509ee5677f1bf28f /src/libsystemd-bus
parent11c4c2492083325531aeb3eeb9b041c929677890 (diff)
bus: parse matches locally and allow registration of callbacks for them
This includes code to parse and split up match strings which will also be useful to calculate bloom filter masks when the time comes.
Diffstat (limited to 'src/libsystemd-bus')
-rw-r--r--src/libsystemd-bus/bus-control.c17
-rw-r--r--src/libsystemd-bus/bus-control.h27
-rw-r--r--src/libsystemd-bus/bus-internal.c61
-rw-r--r--src/libsystemd-bus/bus-internal.h10
-rw-r--r--src/libsystemd-bus/bus-match.c978
-rw-r--r--src/libsystemd-bus/bus-match.h81
-rw-r--r--src/libsystemd-bus/bus-message.c35
-rw-r--r--src/libsystemd-bus/bus-message.h2
-rw-r--r--src/libsystemd-bus/sd-bus.c62
-rw-r--r--src/libsystemd-bus/sd-bus.h7
-rw-r--r--src/libsystemd-bus/test-bus-chat.c41
-rw-r--r--src/libsystemd-bus/test-bus-match.c114
-rw-r--r--src/libsystemd-bus/test-bus-signature.c31
13 files changed, 1453 insertions, 13 deletions
diff --git a/src/libsystemd-bus/bus-control.c b/src/libsystemd-bus/bus-control.c
index 06afaf3c76..dd404442cf 100644
--- a/src/libsystemd-bus/bus-control.c
+++ b/src/libsystemd-bus/bus-control.c
@@ -27,6 +27,7 @@
#include "sd-bus.h"
#include "bus-internal.h"
#include "bus-message.h"
+#include "bus-control.h"
int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
int r;
@@ -292,14 +293,12 @@ int sd_bus_get_owner_pid(sd_bus *bus, const char *name, pid_t *pid) {
return 0;
}
-int sd_bus_add_match(sd_bus *bus, const char *match) {
+int bus_add_match_internal(sd_bus *bus, const char *match) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
int r;
- if (!bus)
- return -EINVAL;
- if (!match)
- return -EINVAL;
+ assert(bus);
+ assert(match);
r = sd_bus_message_new_method_call(
bus,
@@ -318,14 +317,12 @@ int sd_bus_add_match(sd_bus *bus, const char *match) {
return sd_bus_send_with_reply_and_block(bus, m, 0, NULL, &reply);
}
-int sd_bus_remove_match(sd_bus *bus, const char *match) {
+int bus_remove_match_internal(sd_bus *bus, const char *match) {
_cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
int r;
- if (!bus)
- return -EINVAL;
- if (!match)
- return -EINVAL;
+ assert(bus);
+ assert(match);
r = sd_bus_message_new_method_call(
bus,
diff --git a/src/libsystemd-bus/bus-control.h b/src/libsystemd-bus/bus-control.h
new file mode 100644
index 0000000000..34ecb260c3
--- /dev/null
+++ b/src/libsystemd-bus/bus-control.h
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd 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.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-bus.h"
+
+int bus_add_match_internal(sd_bus *bus, const char *match);
+int bus_remove_match_internal(sd_bus *bus, const char *match);
diff --git a/src/libsystemd-bus/bus-internal.c b/src/libsystemd-bus/bus-internal.c
index d27b3f466b..317f6a8a9c 100644
--- a/src/libsystemd-bus/bus-internal.c
+++ b/src/libsystemd-bus/bus-internal.c
@@ -168,3 +168,64 @@ bool member_name_is_valid(const char *p) {
return true;
}
+
+static bool complex_pattern_check(char c, const char *a, const char *b) {
+ bool separator = false;
+
+ for (;;) {
+ if (*a != *b)
+ return (separator && (*a == 0 || *b == 0)) ||
+ (*a == 0 && *b == c && b[1] == 0) ||
+ (*b == 0 && *a == c && a[1] == 0);
+
+ if (*a == 0)
+ return true;
+
+ separator = *a == c;
+
+ a++, b++;
+ }
+}
+
+bool namespace_complex_pattern(const char *pattern, const char *value) {
+ return complex_pattern_check('.', pattern, value);
+}
+
+bool path_complex_pattern(const char *pattern, const char *value) {
+ return complex_pattern_check('/', pattern, value);
+}
+
+static bool simple_pattern_check(char c, const char *a, const char *b) {
+ for (;;) {
+ if (*a != *b)
+ return *a == 0 && *b == c;
+
+ if (*a == 0)
+ return true;
+
+ a++, b++;
+ }
+}
+
+bool namespace_simple_pattern(const char *pattern, const char *value) {
+ return simple_pattern_check('.', pattern, value);
+}
+
+bool path_simple_pattern(const char *pattern, const char *value) {
+ return simple_pattern_check('/', pattern, value);
+}
+
+int bus_message_type_from_string(const char *s, uint8_t *u) {
+ if (streq(s, "signal"))
+ *u = SD_BUS_MESSAGE_TYPE_SIGNAL;
+ else if (streq(s, "method_call"))
+ *u = SD_BUS_MESSAGE_TYPE_METHOD_CALL;
+ else if (streq(s, "error"))
+ *u = SD_BUS_MESSAGE_TYPE_METHOD_ERROR;
+ else if (streq(s, "method_return"))
+ *u = SD_BUS_MESSAGE_TYPE_METHOD_RETURN;
+ else
+ return -EINVAL;
+
+ return 0;
+}
diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h
index 76b90ea3a6..1dcb383038 100644
--- a/src/libsystemd-bus/bus-internal.h
+++ b/src/libsystemd-bus/bus-internal.h
@@ -32,6 +32,7 @@
#include "sd-bus.h"
#include "bus-error.h"
+#include "bus-match.h"
struct reply_callback {
sd_message_handler_t callback;
@@ -97,6 +98,7 @@ struct sd_bus {
char *unique_name;
+ struct bus_match_node match_callbacks;
Prioq *reply_callbacks_prioq;
Hashmap *reply_callbacks;
LIST_HEAD(struct filter_callback, filter_callbacks);
@@ -166,6 +168,14 @@ bool interface_name_is_valid(const char *p);
bool service_name_is_valid(const char *p);
bool member_name_is_valid(const char *p);
+bool namespace_complex_pattern(const char *pattern, const char *value);
+bool path_complex_pattern(const char *pattern, const char *value);
+
+bool namespace_simple_pattern(const char *pattern, const char *value);
+bool path_simple_pattern(const char *pattern, const char *value);
+
+int bus_message_type_from_string(const char *s, uint8_t *u);
+
#define error_name_is_valid interface_name_is_valid
int bus_ensure_running(sd_bus *bus);
diff --git a/src/libsystemd-bus/bus-match.c b/src/libsystemd-bus/bus-match.c
new file mode 100644
index 0000000000..ff1edfaaed
--- /dev/null
+++ b/src/libsystemd-bus/bus-match.c
@@ -0,0 +1,978 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd 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.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "bus-internal.h"
+#include "bus-message.h"
+#include "bus-match.h"
+
+/* Example:
+ *
+ * A: type=signal,sender=foo,interface=bar
+ * B: type=signal,sender=quux,interface=fips
+ * C: type=signal,sender=quux,interface=waldo
+ * D: type=signal,member=test
+ * E: sender=miau
+ * F: type=signal
+ * G: type=signal
+ *
+ * results in this tree:
+ *
+ * BUS_MATCH_ROOT
+ * + BUS_MATCH_MESSAGE_TYPE
+ * | ` BUS_MATCH_VALUE: value == signal
+ * | + DBUS_MATCH_SENDER
+ * | | + BUS_MATCH_VALUE: value == foo
+ * | | | ` DBUS_MATCH_INTERFACE
+ * | | | ` BUS_MATCH_VALUE: value == bar
+ * | | | ` BUS_MATCH_LEAF: A
+ * | | ` BUS_MATCH_VALUE: value == quux
+ * | | ` DBUS_MATCH_INTERFACE
+ * | | | BUS_MATCH_VALUE: value == fips
+ * | | | ` BUS_MATCH_LEAF: B
+ * | | ` BUS_MATCH_VALUE: value == waldo
+ * | | ` BUS_MATCH_LEAF: C
+ * | + DBUS_MATCH_MEMBER
+ * | | ` BUS_MATCH_VALUE: value == test
+ * | | ` BUS_MATCH_LEAF: D
+ * | + BUS_MATCH_LEAF: F
+ * | ` BUS_MATCH_LEAF: G
+ * ` BUS_MATCH_SENDER
+ * ` BUS_MATCH_VALUE: value == miau
+ * ` BUS_MATCH_LEAF: E
+ */
+
+static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
+ return t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_ARG_NAMESPACE_LAST;
+}
+
+static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
+ return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
+ (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST);
+}
+
+static void bus_match_node_free(struct bus_match_node *node) {
+ assert(node);
+ assert(node->parent);
+ assert(!node->child);
+ assert(node->type != BUS_MATCH_ROOT);
+ assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
+
+ if (node->parent->child) {
+ /* We are apparently linked into the parent's child
+ * list. Let's remove us from there. */
+ if (node->prev) {
+ assert(node->prev->next == node);
+ node->prev->next = node->next;
+ } else {
+ assert(node->parent->child == node);
+ node->parent->child = node->next;
+ }
+
+ if (node->next)
+ node->next->prev = node->prev;
+ }
+
+ if (node->type == BUS_MATCH_VALUE) {
+ /* We might be in the parent's hash table, so clean
+ * this up */
+
+ if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
+ hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
+ else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
+ hashmap_remove(node->parent->compare.children, node->value.str);
+
+ free(node->value.str);
+ }
+
+ if (BUS_MATCH_IS_COMPARE(node->type)) {
+ assert(hashmap_isempty(node->compare.children));
+ hashmap_free(node->compare.children);
+ }
+
+ free(node);
+}
+
+static bool bus_match_node_maybe_free(struct bus_match_node *node) {
+ assert(node);
+
+ if (node->child)
+ return false;
+
+ if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
+ return true;
+
+ bus_match_node_free(node);
+ return true;
+}
+
+static bool value_node_test(
+ struct bus_match_node *node,
+ enum bus_match_node_type parent_type,
+ uint8_t value_u8,
+ const char *value_str) {
+
+ assert(node);
+ assert(node->type == BUS_MATCH_VALUE);
+
+ /* Tests parameters against this value node, doing prefix
+ * magic and stuff. */
+
+ switch (parent_type) {
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ return node->value.u8 == value_u8;
+
+ case BUS_MATCH_SENDER:
+ case BUS_MATCH_DESTINATION:
+ case BUS_MATCH_INTERFACE:
+ case BUS_MATCH_MEMBER:
+ case BUS_MATCH_PATH:
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ return streq(node->value.str, value_str);
+
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ return namespace_simple_pattern(node->value.str, value_str);
+
+ case BUS_MATCH_PATH_NAMESPACE:
+ return path_simple_pattern(node->value.str, value_str);
+
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ return path_complex_pattern(node->value.str, value_str);
+
+ default:
+ assert_not_reached("Invalid node type");
+ }
+}
+
+static bool value_node_same(
+ struct bus_match_node *node,
+ enum bus_match_node_type parent_type,
+ uint8_t value_u8,
+ const char *value_str) {
+
+ /* Tests parameters against this value node, not doing prefix
+ * magic and stuff, i.e. this one actually compares the match
+ * itself.*/
+
+ assert(node);
+ assert(node->type == BUS_MATCH_VALUE);
+
+ switch (parent_type) {
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ return node->value.u8 == value_u8;
+
+ case BUS_MATCH_SENDER:
+ case BUS_MATCH_DESTINATION:
+ case BUS_MATCH_INTERFACE:
+ case BUS_MATCH_MEMBER:
+ case BUS_MATCH_PATH:
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ case BUS_MATCH_PATH_NAMESPACE:
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ return streq(node->value.str, value_str);
+
+ default:
+ assert_not_reached("Invalid node type");
+ }
+}
+
+int bus_match_run(
+ sd_bus *bus,
+ struct bus_match_node *node,
+ int ret,
+ sd_bus_message *m) {
+
+
+ const char *test_str = NULL;
+ uint8_t test_u8 = 0;
+ int r;
+
+ assert(m);
+
+ if (!node)
+ return 0;
+
+ /* Not these special semantics: when traversing the tree we
+ * usually let bus_match_run() when called for a node
+ * recursively invoke bus_match_run(). There's are two
+ * exceptions here though, which are BUS_NODE_ROOT (which
+ * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
+ * are invoked anyway by its parent. */
+
+ switch (node->type) {
+
+ case BUS_MATCH_ROOT:
+
+ /* Run all children. Since we cannot have any siblings
+ * we won't call any. The children of the root node
+ * are compares or leaves, they will automatically
+ * call their siblings. */
+ return bus_match_run(bus, node->child, ret, m);
+
+ case BUS_MATCH_VALUE:
+
+ /* Run all children. We don't execute any siblings, we
+ * assume our caller does that. The children of value
+ * nodes are compares or leaves, they will
+ * automatically call their siblings */
+
+ assert(node->child);
+ return bus_match_run(bus, node->child, ret, m);
+
+ case BUS_MATCH_LEAF:
+
+ /* Run the callback. And then invoke siblings. */
+ assert(node->leaf.callback);
+ r = node->leaf.callback(bus, ret, m, node->leaf.userdata);
+ if (r != 0)
+ return r;
+
+ return bus_match_run(bus, node->next, ret, m);
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ test_u8 = m->header->type;
+ break;
+
+ case BUS_MATCH_SENDER:
+ test_str = m->sender;
+ /* FIXME: resolve test_str from a well-known to a unique name first */
+ break;
+
+ case BUS_MATCH_DESTINATION:
+ test_str = m->destination;
+ break;
+
+ case BUS_MATCH_INTERFACE:
+ test_str = m->interface;
+ break;
+
+ case BUS_MATCH_MEMBER:
+ test_str = m->member;
+ break;
+
+ case BUS_MATCH_PATH:
+ case BUS_MATCH_PATH_NAMESPACE:
+ test_str = m->path;
+ break;
+
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG);
+ break;
+
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH);
+ break;
+
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE);
+ break;
+
+ default:
+ assert_not_reached("Unknown match type.");
+ }
+
+ if (BUS_MATCH_CAN_HASH(node->type)) {
+ struct bus_match_node *found;
+
+ /* Lookup via hash table, nice! So let's jump directly. */
+
+ if (test_str)
+ found = hashmap_get(node->compare.children, test_str);
+ else if (node->type == BUS_MATCH_MESSAGE_TYPE)
+ found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
+ else
+ found = NULL;
+
+ if (found) {
+ r = bus_match_run(bus, found, ret, m);
+ if (r != 0)
+ return r;
+ }
+ } else {
+ struct bus_match_node *c;
+
+ /* No hash table, so let's iterate manually... */
+
+ for (c = node->child; c; c = c->next) {
+ if (!value_node_test(c, node->type, test_u8, test_str))
+ continue;
+
+ r = bus_match_run(bus, c, ret, m);
+ if (r != 0)
+ return r;
+ }
+ }
+
+ /* And now, let's invoke our siblings */
+ return bus_match_run(bus, node->next, ret, m);
+}
+
+static int bus_match_add_compare_value(
+ struct bus_match_node *where,
+ enum bus_match_node_type t,
+ uint8_t value_u8,
+ const char *value_str,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *c = NULL, *n = NULL;
+ int r;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(BUS_MATCH_IS_COMPARE(t));
+ assert(ret);
+
+ for (c = where->child; c && c->type != t; c = c->next)
+ ;
+
+ if (c) {
+ /* Comparison node already exists? Then let's see if
+ * the value node exists too. */
+
+ if (t == BUS_MATCH_MESSAGE_TYPE)
+ n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
+ else if (BUS_MATCH_CAN_HASH(t))
+ n = hashmap_get(c->compare.children, value_str);
+ else {
+ for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
+ ;
+ }
+
+ if (n) {
+ *ret = n;
+ return 0;
+ }
+ } else {
+ /* Comparison node, doesn't exist yet? Then let's
+ * create it. */
+
+ c = new0(struct bus_match_node, 1);
+ if (!c) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ c->type = t;
+ c->parent = where;
+ c->next = where->child;
+ if (c->next)
+ c->next->prev = c;
+ where->child = c;
+
+ if (t == BUS_MATCH_MESSAGE_TYPE) {
+ c->compare.children = hashmap_new(trivial_hash_func, trivial_compare_func);
+ if (!c->compare.children) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ } else if (BUS_MATCH_CAN_HASH(t)) {
+ c->compare.children = hashmap_new(string_hash_func, string_compare_func);
+ if (!c->compare.children) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+ }
+
+ n = new0(struct bus_match_node, 1);
+ if (!n) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ n->type = BUS_MATCH_VALUE;
+ n->value.u8 = value_u8;
+ if (value_str) {
+ n->value.str = strdup(value_str);
+ if (!n->value.str) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ }
+
+ n->parent = c;
+ if (c->compare.children) {
+
+ if (t == BUS_MATCH_MESSAGE_TYPE)
+ r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
+ else
+ r = hashmap_put(c->compare.children, n->value.str, n);
+
+ if (r < 0)
+ goto fail;
+ } else {
+ n->next = c->child;
+ if (n->next)
+ n->next->prev = n;
+ c->child = n;
+ }
+
+ *ret = n;
+ return 1;
+
+fail:
+ if (c)
+ bus_match_node_maybe_free(c);
+
+ if (n) {
+ free(n->value.str);
+ free(n);
+ }
+
+ return r;
+}
+
+static int bus_match_find_compare_value(
+ struct bus_match_node *where,
+ enum bus_match_node_type t,
+ uint8_t value_u8,
+ const char *value_str,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *c, *n;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(BUS_MATCH_IS_COMPARE(t));
+ assert(ret);
+
+ for (c = where->child; c && c->type != t; c = c->next)
+ ;
+
+ if (!c)
+ return 0;
+
+ if (t == BUS_MATCH_MESSAGE_TYPE)
+ n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
+ else if (BUS_MATCH_CAN_HASH(t))
+ n = hashmap_get(c->compare.children, value_str);
+ else {
+ for (n = c->child; !value_node_same(n, t, value_u8, value_str); n = n->next)
+ ;
+ }
+
+ if (n) {
+ *ret = n;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bus_match_add_leaf(
+ struct bus_match_node *where,
+ sd_message_handler_t callback,
+ void *userdata,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *n;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(ret);
+
+ n = new0(struct bus_match_node, 1);
+ if (!n)
+ return -ENOMEM;
+
+ n->type = BUS_MATCH_LEAF;
+ n->parent = where;
+ n->next = where->child;
+ if (n->next)
+ n->next->prev = n;
+ n->leaf.callback = callback;
+ n->leaf.userdata = userdata;
+
+ where->child = n;
+
+ *ret = n;
+ return 1;
+}
+
+static int bus_match_find_leaf(
+ struct bus_match_node *where,
+ sd_message_handler_t callback,
+ void *userdata,
+ struct bus_match_node **ret) {
+
+ struct bus_match_node *c;
+
+ assert(where);
+ assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
+ assert(ret);
+
+ for (c = where->child; c; c = c->next) {
+ if (c->type == BUS_MATCH_LEAF &&
+ c->leaf.callback == callback &&
+ c->leaf.userdata == userdata) {
+ *ret = c;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
+ assert(k);
+
+ if (n == 4 && memcmp(k, "type", 4) == 0)
+ return BUS_MATCH_MESSAGE_TYPE;
+ if (n == 6 && memcmp(k, "sender", 6) == 0)
+ return BUS_MATCH_SENDER;
+ if (n == 11 && memcmp(k, "destination", 11) == 0)
+ return BUS_MATCH_DESTINATION;
+ if (n == 9 && memcmp(k, "interface", 9) == 0)
+ return BUS_MATCH_INTERFACE;
+ if (n == 6 && memcmp(k, "member", 6) == 0)
+ return BUS_MATCH_MEMBER;
+ if (n == 4 && memcmp(k, "path", 4) == 0)
+ return BUS_MATCH_PATH;
+ if (n == 14 && memcmp(k, "path_namespace", 14) == 0)
+ return BUS_MATCH_PATH_NAMESPACE;
+
+ if (n == 4 && memcmp(k, "arg", 3) == 0) {
+ int j;
+
+ j = undecchar(k[3]);
+ if (j < 0)
+ return -EINVAL;
+
+ return BUS_MATCH_ARG + j;
+ }
+
+ if (n == 5 && memcmp(k, "arg", 3) == 0) {
+ int a, b;
+ enum bus_match_node_type t;
+
+ a = undecchar(k[3]);
+ b = undecchar(k[4]);
+ if (a <= 0 || b < 0)
+ return -EINVAL;
+
+ t = BUS_MATCH_ARG + a * 10 + b;
+ if (t > BUS_MATCH_ARG_LAST)
+ return -EINVAL;
+
+ return t;
+ }
+
+ if (n == 8 && memcmp(k, "arg", 3) == 0 && memcmp(k + 4, "path", 4) == 0) {
+ int j;
+
+ j = undecchar(k[3]);
+ if (j < 0)
+ return -EINVAL;
+
+ return BUS_MATCH_ARG_PATH + j;
+ }
+
+ if (n == 9 && memcmp(k, "arg", 3) == 0 && memcmp(k + 5, "path", 4) == 0) {
+ enum bus_match_node_type t;
+ int a, b;
+
+ a = undecchar(k[3]);
+ b = undecchar(k[4]);
+ if (a <= 0 || b < 0)
+ return -EINVAL;
+
+ t = BUS_MATCH_ARG_PATH + a * 10 + b;
+ if (t > BUS_MATCH_ARG_PATH_LAST)
+ return -EINVAL;
+
+ return t;
+ }
+
+ if (n == 13 && memcmp(k, "arg", 3) == 0 && memcmp(k + 4, "namespace", 9) == 0) {
+ int j;
+
+ j = undecchar(k[3]);
+ if (j < 0)
+ return -EINVAL;
+
+ return BUS_MATCH_ARG_NAMESPACE + j;
+ }
+
+ if (n == 14 && memcmp(k, "arg", 3) == 0 && memcmp(k + 5, "namespace", 9) == 0) {
+ enum bus_match_node_type t;
+ int a, b;
+
+ a = undecchar(k[3]);
+ b = undecchar(k[4]);
+ if (a <= 0 || b < 0)
+ return -EINVAL;
+
+ t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
+ if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
+ return -EINVAL;
+
+ return t;
+ }
+
+ return -EINVAL;
+}
+
+struct match_component {
+ enum bus_match_node_type type;
+ uint8_t value_u8;
+ char *value_str;
+};
+
+static int match_component_compare(const void *a, const void *b) {
+ const struct match_component *x = a, *y = b;
+
+ if (x->type < y->type)
+ return -1;
+ if (x->type > y->type)
+ return 1;
+
+ return 0;
+}
+
+static void free_components(struct match_component *components, unsigned n_components) {
+ unsigned i;
+
+ for (i = 0; i < n_components; i++)
+ free(components[i].value_str);
+
+ free(components);
+}
+
+static int parse_match(
+ const char *match,
+ struct match_component **_components,
+ unsigned *_n_components) {
+
+ const char *p = match;
+ struct match_component *components = NULL;
+ size_t components_allocated = 0;
+ unsigned n_components = 0, i;
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ assert(match);
+ assert(_components);
+ assert(_n_components);
+
+ while (*p != 0) {
+ const char *eq, *q;
+ enum bus_match_node_type t;
+ unsigned j = 0;
+ size_t value_allocated = 0;
+ bool escaped = false;
+ uint8_t u;
+
+ eq = strchr(p, '=');
+ if (!eq)
+ return -EINVAL;
+
+ if (eq[1] != '\'')
+ return -EINVAL;
+
+ t = bus_match_node_type_from_string(p, eq - p);
+ if (t < 0)
+ return -EINVAL;
+
+ for (q = eq + 2;; q++) {
+
+ if (*q == 0) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ if (!escaped) {
+ if (*q == '\\') {
+ escaped = true;
+ continue;
+ }
+ if (*q == '\'') {
+ if (value)
+ value[j] = 0;
+ break;
+ }
+ }
+
+ if (!greedy_realloc((void**) &value, &value_allocated, j + 2)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ value[j++] = *q;
+ escaped = false;
+ }
+
+ if (t == BUS_MATCH_MESSAGE_TYPE) {
+ r = bus_message_type_from_string(value, &u);
+ if (r < 0)
+ goto fail;
+
+ free(value);
+ value = NULL;
+ } else
+ u = 0;
+
+ if (!greedy_realloc((void**) &components, &components_allocated,
+ (n_components + 1) * sizeof(struct match_component))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ components[n_components].type = t;
+ components[n_components].value_str = value;
+ components[n_components].value_u8 = u;
+ n_components++;
+
+ value = NULL;
+
+ if (q[1] == 0)
+ break;
+
+ if (q[1] != ',') {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ p = q + 2;
+ }
+
+ /* Order the whole thing, so that we always generate the same tree */
+ qsort(components, n_components, sizeof(struct match_component), match_component_compare);
+
+ /* Check for duplicates */
+ for (i = 0; i+1 < n_components; i++)
+ if (components[i].type == components[i+1].type) {
+ r = -EINVAL;
+ goto fail;
+ }
+
+ *_components = components;
+ *_n_components = n_components;
+
+ return 0;
+
+fail:
+ free_components(components, n_components);
+ return r;
+}
+
+int bus_match_add(
+ struct bus_match_node *root,
+ const char *match,
+ sd_message_handler_t callback,
+ void *userdata,
+ struct bus_match_node **ret) {
+
+ struct match_component *components = NULL;
+ unsigned n_components = 0, i;
+ struct bus_match_node *n;
+ int r;
+
+ assert(root);
+ assert(match);
+ assert(callback);
+
+ r = parse_match(match, &components, &n_components);
+ if (r < 0)
+ return r;
+
+ n = root;
+ for (i = 0; i < n_components; i++) {
+ r = bus_match_add_compare_value(
+ n, components[i].type,
+ components[i].value_u8, components[i].value_str, &n);
+ if (r < 0)
+ goto finish;
+ }
+
+ r = bus_match_add_leaf(n, callback, userdata, &n);
+ if (r < 0)
+ goto finish;
+
+ if (ret)
+ *ret = n;
+
+finish:
+ free_components(components, n_components);
+ return r;
+}
+
+int bus_match_remove(
+ struct bus_match_node *root,
+ const char *match,
+ sd_message_handler_t callback,
+ void *userdata) {
+
+ struct match_component *components = NULL;
+ unsigned n_components = 0, i;
+ struct bus_match_node *n, **gc;
+ int r;
+
+ assert(root);
+ assert(match);
+
+ r = parse_match(match, &components, &n_components);
+ if (r < 0)
+ return r;
+
+ gc = newa(struct bus_match_node*, n_components);
+
+ n = root;
+ for (i = 0; i < n_components; i++) {
+ r = bus_match_find_compare_value(
+ n, components[i].type,
+ components[i].value_u8, components[i].value_str,
+ &n);
+ if (r <= 0)
+ goto finish;
+
+ gc[i] = n;
+ }
+
+ r = bus_match_find_leaf(n, callback, userdata, &n);
+ if (r <= 0)
+ goto finish;
+
+ /* Free the leaf */
+ bus_match_node_free(n);
+
+ /* Prune the tree above */
+ for (i = n_components; i > 0; i --) {
+ struct bus_match_node *p = gc[i-1]->parent;
+
+ if (!bus_match_node_maybe_free(gc[i-1]))
+ break;
+
+ if (!bus_match_node_maybe_free(p))
+ break;
+ }
+
+finish:
+ free_components(components, n_components);
+ return r;
+}
+
+void bus_match_free(struct bus_match_node *node) {
+ struct bus_match_node *c;
+
+ if (!node)
+ return;
+
+ if (BUS_MATCH_CAN_HASH(node->type)) {
+ Iterator i;
+
+ HASHMAP_FOREACH(c, node->compare.children, i)
+ bus_match_free(c);
+
+ assert(hashmap_isempty(node->compare.children));
+ }
+
+ while ((c = node->child))
+ bus_match_free(c);
+
+ if (node->type != BUS_MATCH_ROOT)
+ bus_match_node_free(node);
+}
+
+const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
+ switch (t) {
+
+ case BUS_MATCH_ROOT:
+ return "root";
+
+ case BUS_MATCH_VALUE:
+ return "value";
+
+ case BUS_MATCH_LEAF:
+ return "leaf";
+
+ case BUS_MATCH_MESSAGE_TYPE:
+ return "type";
+
+ case BUS_MATCH_SENDER:
+ return "sender";
+
+ case BUS_MATCH_DESTINATION:
+ return "destination";
+
+ case BUS_MATCH_INTERFACE:
+ return "interface";
+
+ case BUS_MATCH_MEMBER:
+ return "member";
+
+ case BUS_MATCH_PATH:
+ return "path";
+
+ case BUS_MATCH_PATH_NAMESPACE:
+ return "path_namespace";
+
+ case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
+ snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
+ return buf;
+
+ case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
+ snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
+ return buf;
+
+ case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
+ snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
+ return buf;
+
+ default:
+ return NULL;
+ }
+}
+
+void bus_match_dump(struct bus_match_node *node, unsigned level) {
+ struct bus_match_node *c;
+ _cleanup_free_ char *pfx = NULL;
+ char buf[32];
+
+ if (!node)
+ return;
+
+ pfx = strrep(" ", level);
+ printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
+
+ if (node->type == BUS_MATCH_VALUE) {
+ if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
+ printf(" <%u>\n", node->value.u8);
+ else
+ printf(" <%s>\n", node->value.str);
+ } else if (node->type == BUS_MATCH_ROOT)
+ puts(" root");
+ else if (node->type == BUS_MATCH_LEAF)
+ printf(" %p/%p\n", node->leaf.callback, node->leaf.userdata);
+ else
+ putchar('\n');
+
+ if (BUS_MATCH_CAN_HASH(node->type)) {
+ Iterator i;
+
+ HASHMAP_FOREACH(c, node->compare.children, i)
+ bus_match_dump(c, level + 1);
+ }
+
+ for (c = node->child; c; c = c->next)
+ bus_match_dump(c, level + 1);
+}
diff --git a/src/libsystemd-bus/bus-match.h b/src/libsystemd-bus/bus-match.h
new file mode 100644
index 0000000000..0a63b55403
--- /dev/null
+++ b/src/libsystemd-bus/bus-match.h
@@ -0,0 +1,81 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd 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.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "hashmap.h"
+
+#include "sd-bus.h"
+
+enum bus_match_node_type {
+ BUS_MATCH_ROOT,
+ BUS_MATCH_VALUE,
+ BUS_MATCH_LEAF,
+
+ /* The following are all different kinds of compare nodes */
+ BUS_MATCH_MESSAGE_TYPE,
+ BUS_MATCH_SENDER,
+ BUS_MATCH_DESTINATION,
+ BUS_MATCH_INTERFACE,
+ BUS_MATCH_MEMBER,
+ BUS_MATCH_PATH,
+ BUS_MATCH_PATH_NAMESPACE,
+ BUS_MATCH_ARG,
+ BUS_MATCH_ARG_LAST = BUS_MATCH_ARG + 63,
+ BUS_MATCH_ARG_PATH,
+ BUS_MATCH_ARG_PATH_LAST = BUS_MATCH_ARG_PATH + 63,
+ BUS_MATCH_ARG_NAMESPACE,
+ BUS_MATCH_ARG_NAMESPACE_LAST = BUS_MATCH_ARG_NAMESPACE + 63,
+ _BUS_MATCH_NODE_TYPE_MAX,
+ _BUS_MATCH_NODE_TYPE_INVALID = -1
+};
+
+struct bus_match_node {
+ enum bus_match_node_type type;
+ struct bus_match_node *parent, *next, *prev, *child;
+
+ union {
+ struct {
+ uint8_t u8;
+ char *str;
+ } value;
+ struct {
+ sd_message_handler_t callback;
+ void *userdata;
+ } leaf;
+ struct {
+ /* If this is set, then the child is NULL */
+ Hashmap *children;
+ } compare;
+ };
+};
+
+int bus_match_run(sd_bus *bus, struct bus_match_node *root, int ret, sd_bus_message *m);
+
+int bus_match_add(struct bus_match_node *root, const char *match, sd_message_handler_t callback, void *userdata, struct bus_match_node **ret);
+int bus_match_remove(struct bus_match_node *root, const char *match, sd_message_handler_t callback, void *userdata);
+
+void bus_match_free(struct bus_match_node *node);
+
+void bus_match_dump(struct bus_match_node *node, unsigned level);
+
+const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l);
+enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n);
diff --git a/src/libsystemd-bus/bus-message.c b/src/libsystemd-bus/bus-message.c
index 845e2c0040..85b9cbaae0 100644
--- a/src/libsystemd-bus/bus-message.c
+++ b/src/libsystemd-bus/bus-message.c
@@ -3002,3 +3002,38 @@ int bus_message_read_strv_extend(sd_bus_message *m, char ***l) {
return 0;
}
+
+const char* bus_message_get_arg(sd_bus_message *m, unsigned i) {
+ int r;
+ const char *t;
+ char type;
+
+ assert(m);
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return NULL;
+
+ while (i > 0) {
+ r = sd_bus_message_peek_type(m, &type, NULL);
+ if (r < 0)
+ return NULL;
+
+ if (type != SD_BUS_TYPE_STRING &&
+ type != SD_BUS_TYPE_OBJECT_PATH &&
+ type != SD_BUS_TYPE_SIGNATURE)
+ return NULL;
+
+ r = sd_bus_message_read_basic(m, type, &t);
+ if (r < 0)
+ return NULL;
+
+ i--;
+ }
+
+ r = sd_bus_message_rewind(m, true);
+ if (r < 0)
+ return NULL;
+
+ return t;
+}
diff --git a/src/libsystemd-bus/bus-message.h b/src/libsystemd-bus/bus-message.h
index 3289b378f8..91b1668f19 100644
--- a/src/libsystemd-bus/bus-message.h
+++ b/src/libsystemd-bus/bus-message.h
@@ -142,3 +142,5 @@ int bus_message_from_malloc(
const struct ucred *ucred,
const char *label,
sd_bus_message **ret);
+
+const char* bus_message_get_arg(sd_bus_message *m, unsigned i);
diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c
index 047f286f04..0a263ea68c 100644
--- a/src/libsystemd-bus/sd-bus.c
+++ b/src/libsystemd-bus/sd-bus.c
@@ -37,6 +37,7 @@
#include "bus-message.h"
#include "bus-type.h"
#include "bus-socket.h"
+#include "bus-control.h"
static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec);
@@ -84,6 +85,8 @@ static void bus_free(sd_bus *b) {
hashmap_free(b->object_callbacks);
+ bus_match_free(&b->match_callbacks);
+
free(b);
}
@@ -1493,6 +1496,9 @@ static int process_filter(sd_bus *bus, sd_bus_message *m) {
struct filter_callback *l;
int r;
+ assert(bus);
+ assert(m);
+
LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
r = l->callback(bus, 0, m, l->userdata);
if (r != 0)
@@ -1502,6 +1508,13 @@ static int process_filter(sd_bus *bus, sd_bus_message *m) {
return 0;
}
+static int process_match(sd_bus *bus, sd_bus_message *m) {
+ assert(bus);
+ assert(m);
+
+ return bus_match_run(bus, &bus->match_callbacks, 0, m);
+}
+
static int process_builtin(sd_bus *bus, sd_bus_message *m) {
_cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
int r;
@@ -1731,6 +1744,10 @@ static int process_message(sd_bus *bus, sd_bus_message *m) {
if (r != 0)
return r;
+ r = process_match(bus, m);
+ if (r != 0)
+ return r;
+
r = process_builtin(bus, m);
if (r != 0)
return r;
@@ -2057,3 +2074,48 @@ int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t ca
int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata) {
return bus_remove_object(bus, true, prefix, callback, userdata);
}
+
+int sd_bus_add_match(sd_bus *bus, const char *match, sd_message_handler_t callback, void *userdata) {
+ int r = 0;
+
+ if (!bus)
+ return -EINVAL;
+ if (!match)
+ return -EINVAL;
+
+ if (bus->bus_client) {
+ r = bus_add_match_internal(bus, match);
+ if (r < 0)
+ return r;
+ }
+
+ if (callback) {
+ r = bus_match_add(&bus->match_callbacks, match, callback, userdata, NULL);
+ if (r < 0) {
+
+ if (bus->bus_client)
+ bus_remove_match_internal(bus, match);
+ }
+ }
+
+ return r;
+}
+
+int sd_bus_remove_match(sd_bus *bus, const char *match, sd_message_handler_t callback, void *userdata) {
+ int r = 0, q = 0;
+
+ if (!bus)
+ return -EINVAL;
+ if (!match)
+ return -EINVAL;
+
+ if (bus->bus_client)
+ r = bus_remove_match_internal(bus, match);
+
+ if (callback)
+ q = bus_match_remove(&bus->match_callbacks, match, callback, userdata);
+
+ if (r < 0)
+ return r;
+ return q;
+}
diff --git a/src/libsystemd-bus/sd-bus.h b/src/libsystemd-bus/sd-bus.h
index db3b227907..e0970cdec8 100644
--- a/src/libsystemd-bus/sd-bus.h
+++ b/src/libsystemd-bus/sd-bus.h
@@ -33,7 +33,7 @@ extern "C" {
#endif
/* TODO:
- * - allow installing match callbacks
+ * - sd_message_handler_t needs to be renamed to sd_bus_message_handler_t
*
* Later:
* - add page donation logic
@@ -98,6 +98,9 @@ int sd_bus_remove_object(sd_bus *bus, const char *path, sd_message_handler_t cal
int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata);
int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata);
+int sd_bus_add_match(sd_bus *bus, const char *match, sd_message_handler_t callback, void *userdata);
+int sd_bus_remove_match(sd_bus *bus, const char *match, sd_message_handler_t callback, void *userdata);
+
/* Message object */
int sd_bus_message_new_signal(sd_bus *bus, const char *path, const char *interface, const char *member, sd_bus_message **m);
@@ -154,8 +157,6 @@ int sd_bus_list_names(sd_bus *bus, char ***l);
int sd_bus_get_owner(sd_bus *bus, const char *name, char **owner);
int sd_bus_get_owner_uid(sd_bus *bus, const char *name, uid_t *uid);
int sd_bus_get_owner_pid(sd_bus *bus, const char *name, pid_t *pid);
-int sd_bus_add_match(sd_bus *bus, const char *match);
-int sd_bus_remove_match(sd_bus *bus, const char *match);
/* Error structures */
diff --git a/src/libsystemd-bus/test-bus-chat.c b/src/libsystemd-bus/test-bus-chat.c
index 906a7e646c..2dd30912cf 100644
--- a/src/libsystemd-bus/test-bus-chat.c
+++ b/src/libsystemd-bus/test-bus-chat.c
@@ -32,6 +32,13 @@
#include "sd-bus.h"
#include "bus-message.h"
#include "bus-error.h"
+#include "bus-match.h"
+#include "bus-internal.h"
+
+static int match_callback(sd_bus *bus, int error, sd_bus_message *m, void *userdata) {
+ log_info("Match triggered! interface=%s member=%s", strna(sd_bus_message_get_interface(m)), strna(sd_bus_message_get_member(m)));
+ return 0;
+}
static int object_callback(sd_bus *bus, int error, sd_bus_message *m, void *userdata) {
int r;
@@ -106,6 +113,20 @@ static int server_init(sd_bus **_bus) {
goto fail;
}
+ r = sd_bus_add_match(bus, "type='signal',interface='foo.bar',member='Notify'", match_callback, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ goto fail;
+ }
+
+ r = sd_bus_add_match(bus, "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'", match_callback, NULL);
+ if (r < 0) {
+ log_error("Failed to add match: %s", strerror(-r));
+ goto fail;
+ }
+
+ bus_match_dump(&bus->match_callbacks, 0);
+
*_bus = bus;
return 0;
@@ -424,6 +445,26 @@ static void* client2(void*p) {
sd_bus_message_unref(m);
m = NULL;
+ r = sd_bus_message_new_signal(
+ bus,
+ "/foobar",
+ "foo.bar",
+ "Notify",
+ &m);
+ if (r < 0) {
+ log_error("Failed to allocate signal: %s", strerror(-r));
+ goto finish;
+ }
+
+ r = sd_bus_send(bus, m, NULL);
+ if (r < 0) {
+ log_error("Failed to issue signal: %s", bus_error_message(&error, -r));
+ goto finish;
+ }
+
+ sd_bus_message_unref(m);
+ m = NULL;
+
r = sd_bus_message_new_method_call(
bus,
"org.freedesktop.systemd.test",
diff --git a/src/libsystemd-bus/test-bus-match.c b/src/libsystemd-bus/test-bus-match.c
new file mode 100644
index 0000000000..ccbea815e7
--- /dev/null
+++ b/src/libsystemd-bus/test-bus-match.c
@@ -0,0 +1,114 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2013 Lennart Poettering
+
+ systemd 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.1 of the License, or
+ (at your option) any later version.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+
+#include "bus-match.h"
+#include "bus-message.h"
+
+static bool mask[32];
+
+static int filter(sd_bus *b, int ret, sd_bus_message *m, void *userdata) {
+ log_info("Ran %i", PTR_TO_INT(userdata));
+ mask[PTR_TO_INT(userdata)] = true;
+ return 0;
+}
+
+static bool mask_contains(unsigned a[], unsigned n) {
+ unsigned i, j;
+
+ for (i = 0; i < ELEMENTSOF(mask); i++) {
+ bool found = false;
+
+ for (j = 0; j < n; j++)
+ if (a[j] == i) {
+ found = true;
+ break;
+ }
+
+ if (found != mask[i])
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, char *argv[]) {
+ struct bus_match_node root;
+ _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+ enum bus_match_node_type i;
+
+ zero(root);
+ root.type = BUS_MATCH_ROOT;
+
+ assert_se(bus_match_add(&root, "arg3='wal\\'do',sender='foo',type='signal',interface='bar',", filter, INT_TO_PTR(1), NULL) >= 0);
+ assert_se(bus_match_add(&root, "arg3='wal\\'do2',sender='foo',type='signal',interface='bar',", filter, INT_TO_PTR(2), NULL) >= 0);
+ assert_se(bus_match_add(&root, "arg4='test',sender='foo',type='signal',interface='bar',", filter, INT_TO_PTR(3), NULL) >= 0);
+ assert_se(bus_match_add(&root, "arg4='test',sender='foo',type='method_call',interface='bar',", filter, INT_TO_PTR(4), NULL) >= 0);
+ assert_se(bus_match_add(&root, "", filter, INT_TO_PTR(5), NULL) >= 0);
+ assert_se(bus_match_add(&root, "interface='quux'", filter, INT_TO_PTR(6), NULL) >= 0);
+ assert_se(bus_match_add(&root, "interface='bar'", filter, INT_TO_PTR(7), NULL) >= 0);
+ assert_se(bus_match_add(&root, "member='waldo',path='/foo/bar'", filter, INT_TO_PTR(8), NULL) >= 0);
+ assert_se(bus_match_add(&root, "path='/foo/bar'", filter, INT_TO_PTR(9), NULL) >= 0);
+ assert_se(bus_match_add(&root, "path_namespace='/foo'", filter, INT_TO_PTR(10), NULL) >= 0);
+ assert_se(bus_match_add(&root, "path_namespace='/foo/quux'", filter, INT_TO_PTR(11), NULL) >= 0);
+ assert_se(bus_match_add(&root, "arg2='two'", filter, INT_TO_PTR(12), NULL) >= 0);
+ assert_se(bus_match_add(&root, "member='waldo',arg3path='/prefix/'", filter, INT_TO_PTR(13), NULL) >= 0);
+ assert_se(bus_match_add(&root, "member='waldo',path='/foo/bar',arg4namespace='prefix'", filter, INT_TO_PTR(14), NULL) >= 0);
+
+ bus_match_dump(&root, 0);
+
+ assert_se(sd_bus_message_new_signal(NULL, "/foo/bar", "bar", "waldo", &m) >= 0);
+ assert_se(sd_bus_message_append(m, "ssss", "one", "two", "/prefix/three", "prefix.four") >= 0);
+ assert_se(bus_message_seal(m, 1) >= 0);
+
+ zero(mask);
+ assert_se(bus_match_run(NULL, &root, 0, m) == 0);
+ assert_se(mask_contains((unsigned[]) { 9, 8, 7, 5, 10, 12, 13, 14 }, 8));
+
+ assert_se(bus_match_remove(&root, "member='waldo',path='/foo/bar'", filter, INT_TO_PTR(8)) > 0);
+ assert_se(bus_match_remove(&root, "arg3path='/prefix/',member='waldo'", filter, INT_TO_PTR(13)) > 0);
+ assert_se(bus_match_remove(&root, "interface='barxx'", filter, INT_TO_PTR(7)) == 0);
+
+ bus_match_dump(&root, 0);
+
+ zero(mask);
+ assert_se(bus_match_run(NULL, &root, 0, m) == 0);
+ assert_se(mask_contains((unsigned[]) { 9, 5, 10, 12, 14, 7 }, 6));
+
+ for (i = 0; i < _BUS_MATCH_NODE_TYPE_MAX; i++) {
+ char buf[32];
+ const char *x;
+
+ assert_se(x = bus_match_node_type_to_string(i, buf, sizeof(buf)));
+
+ if (i >= BUS_MATCH_MESSAGE_TYPE)
+ assert_se(bus_match_node_type_from_string(x, strlen(x)) == i);
+ }
+
+ bus_match_free(&root);
+
+ return 0;
+}
diff --git a/src/libsystemd-bus/test-bus-signature.c b/src/libsystemd-bus/test-bus-signature.c
index 5bc4310e7c..cab0daa77e 100644
--- a/src/libsystemd-bus/test-bus-signature.c
+++ b/src/libsystemd-bus/test-bus-signature.c
@@ -24,6 +24,7 @@
#include "log.h"
#include "bus-signature.h"
+#include "bus-internal.h"
int main(int argc, char *argv[]) {
@@ -81,5 +82,35 @@ int main(int argc, char *argv[]) {
assert_se(signature_is_valid("(((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))", false));
assert_se(!signature_is_valid("((((((((((((((((((((((((((((((((()))))))))))))))))))))))))))))))))", false));
+ assert_se(namespace_complex_pattern("", ""));
+ assert_se(namespace_complex_pattern("foobar", "foobar"));
+ assert_se(namespace_complex_pattern("foobar.waldo", "foobar.waldo"));
+ assert_se(namespace_complex_pattern("foobar.", "foobar.waldo"));
+ assert_se(namespace_complex_pattern("foobar.waldo", "foobar."));
+ assert_se(!namespace_complex_pattern("foobar.waldo", "foobar"));
+ assert_se(!namespace_complex_pattern("foobar", "foobar.waldo"));
+ assert_se(!namespace_complex_pattern("", "foo"));
+ assert_se(!namespace_complex_pattern("foo", ""));
+ assert_se(!namespace_complex_pattern("foo.", ""));
+
+ assert_se(path_complex_pattern("", ""));
+ assert_se(path_complex_pattern("", "/"));
+ assert_se(path_complex_pattern("/", ""));
+ assert_se(path_complex_pattern("/", "/"));
+ assert_se(path_complex_pattern("/foobar/", "/"));
+ assert_se(path_complex_pattern("/foobar/", "/foobar"));
+ assert_se(path_complex_pattern("/foobar", "/foobar"));
+ assert_se(path_complex_pattern("/foobar", "/foobar/"));
+ assert_se(!path_complex_pattern("/foobar", "/foobar/waldo"));
+ assert_se(path_complex_pattern("/foobar/", "/foobar/waldo"));
+
+ assert_se(namespace_simple_pattern("", ""));
+ assert_se(namespace_simple_pattern("foobar", "foobar"));
+ assert_se(namespace_simple_pattern("foobar.waldo", "foobar.waldo"));
+ assert_se(namespace_simple_pattern("foobar", "foobar.waldo"));
+ assert_se(!namespace_simple_pattern("foobar.waldo", "foobar"));
+ assert_se(!namespace_simple_pattern("", "foo"));
+ assert_se(!namespace_simple_pattern("foo", ""));
+
return 0;
}