From eccd47c5beb72211ce33c9a33a1bb36366d43e22 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 25 Aug 2015 19:28:30 +0200 Subject: [PATCH] sd-bus: introduce new match type "arg0has=" for matching arrays of strings Previously, sd-bus inofficially already supported bus matches that tested a string against an array of strings ("as"). This was done via an enhanced way to interpret "arg0=" matches. This is problematic however, since clients have no way to determine if their respective implementation understood strv matches or not, thus allowing invalid matches to be installed without a way to detect that. This patch changes the logic to only allow such matches with a new "arg0has=" syntax. This has the benefit that non-conforming implementations will return a parse error and a client application may thus efficiently detect support for the match type. Matches of this type are useful for "udev"-like systems that "tag" objects with a number of strings, and clients need to be able to match against any of these "tags". The name "has" takes inspiration from Python's ".has_key()" construct. --- src/libsystemd/sd-bus/bus-control.c | 15 ++-- src/libsystemd/sd-bus/bus-kernel.c | 23 +++++- src/libsystemd/sd-bus/bus-match.c | 71 +++++++++++++------ src/libsystemd/sd-bus/bus-match.h | 2 + src/libsystemd/sd-bus/bus-message.c | 71 +++++++++++++------ src/libsystemd/sd-bus/bus-message.h | 3 +- src/libsystemd/sd-bus/test-bus-kernel-bloom.c | 4 +- src/libsystemd/sd-bus/test-bus-match.c | 8 +-- 8 files changed, 142 insertions(+), 55 deletions(-) diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 95c7d4ebe4b..ad83446254c 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -1308,7 +1308,16 @@ int bus_add_match_internal_kernel( break; } - case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: { + case BUS_MATCH_ARG_HAS...BUS_MATCH_ARG_HAS_LAST: { + char buf[sizeof("arg")-1 + 2 + sizeof("has")]; + + xsprintf(buf, "arg%ihas", c->type - BUS_MATCH_ARG_HAS); + bloom_add_pair(bloom, bus->bloom_size, bus->bloom_n_hash, buf, c->value_str); + using_bloom = true; + break; + } + + case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: /* * XXX: DBus spec defines arg[0..63]path= matching to be * a two-way glob. That is, if either string is a prefix @@ -1322,7 +1331,6 @@ int bus_add_match_internal_kernel( * to properly support multiple-matches here. */ break; - } case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: { char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")]; @@ -1333,7 +1341,7 @@ int bus_add_match_internal_kernel( break; } - case BUS_MATCH_DESTINATION: { + case BUS_MATCH_DESTINATION: /* * Kernel only supports matching on destination IDs, but * not on destination names. So just skip the @@ -1351,7 +1359,6 @@ int bus_add_match_internal_kernel( matches_name_change = false; break; - } case BUS_MATCH_ROOT: case BUS_MATCH_VALUE: diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index 21f37001289..a217cf1a239 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -167,6 +167,27 @@ static void add_bloom_arg(void *data, size_t size, unsigned n_hash, unsigned i, bloom_add_prefixes(data, size, n_hash, buf, t, '/'); } +static void add_bloom_arg_has(void *data, size_t size, unsigned n_hash, unsigned i, const char *t) { + char buf[sizeof("arg")-1 + 2 + sizeof("has")]; + char *e; + + assert(data); + assert(size > 0); + assert(i < 64); + assert(t); + + e = stpcpy(buf, "arg"); + if (i < 10) + *(e++) = '0' + (char) i; + else { + *(e++) = '0' + (char) (i / 10); + *(e++) = '0' + (char) (i % 10); + } + + strcpy(e, "has"); + bloom_add_pair(data, size, n_hash, buf, t); +} + static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter *bloom) { void *data; unsigned i; @@ -221,7 +242,7 @@ static int bus_message_setup_bloom(sd_bus_message *m, struct kdbus_bloom_filter return r; while ((r = sd_bus_message_read_basic(m, contents[0], &t)) > 0) - add_bloom_arg(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); + add_bloom_arg_has(data, m->bus->bloom_size, m->bus->bloom_n_hash, i, t); if (r < 0) return r; diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c index f3b49c0c905..7175cbe8ed9 100644 --- a/src/libsystemd/sd-bus/bus-match.c +++ b/src/libsystemd/sd-bus/bus-match.c @@ -61,12 +61,13 @@ */ static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) { - return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_NAMESPACE_LAST; + return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_HAS_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); + (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST) || + (t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_LAST); } static void bus_match_node_free(struct bus_match_node *node) { @@ -178,12 +179,16 @@ static bool value_node_test( case BUS_MATCH_INTERFACE: case BUS_MATCH_MEMBER: case BUS_MATCH_PATH: - case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: { - char **i; + case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: if (value_str) return streq_ptr(node->value.str, value_str); + return false; + + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: { + char **i; + STRV_FOREACH(i, value_strv) if (streq_ptr(node->value.str, *i)) return true; @@ -191,33 +196,20 @@ static bool value_node_test( return false; } - case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: { - char **i; - + case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: if (value_str) return namespace_simple_pattern(node->value.str, value_str); - STRV_FOREACH(i, value_strv) - if (namespace_simple_pattern(node->value.str, *i)) - return true; return false; - } case BUS_MATCH_PATH_NAMESPACE: return path_simple_pattern(node->value.str, value_str); - case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: { - char **i; - + case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: if (value_str) return path_complex_pattern(node->value.str, value_str); - STRV_FOREACH(i, value_strv) - if (path_complex_pattern(node->value.str, *i)) - return true; - return false; - } default: assert_not_reached("Invalid node type"); @@ -248,6 +240,7 @@ static bool value_node_same( case BUS_MATCH_MEMBER: case BUS_MATCH_PATH: case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_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: @@ -371,15 +364,19 @@ int bus_match_run( break; case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: - (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str, &test_strv); + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str); break; case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: - (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str, &test_strv); + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str); break; case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: - (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str, &test_strv); + (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str); + break; + + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: + (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv); break; default: @@ -742,6 +739,32 @@ enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n return t; } + if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) { + int j; + + j = undecchar(k[3]); + if (j < 0) + return -EINVAL; + + return BUS_MATCH_ARG_HAS + j; + } + + if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) { + 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_HAS + a * 10 + b; + if (t > BUS_MATCH_ARG_HAS_LAST) + return -EINVAL; + + return t; + } + return -EINVAL; } @@ -1110,6 +1133,10 @@ const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[] snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE); return buf; + case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: + snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS); + return buf; + default: return NULL; } diff --git a/src/libsystemd/sd-bus/bus-match.h b/src/libsystemd/sd-bus/bus-match.h index 56516be9faa..53ee0463ca2 100644 --- a/src/libsystemd/sd-bus/bus-match.h +++ b/src/libsystemd/sd-bus/bus-match.h @@ -44,6 +44,8 @@ enum bus_match_node_type { 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_ARG_HAS, + BUS_MATCH_ARG_HAS_LAST = BUS_MATCH_ARG_HAS + 63, _BUS_MATCH_NODE_TYPE_MAX, _BUS_MATCH_NODE_TYPE_INVALID = -1 }; diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index a212f0b398b..970aaabdc2b 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -5611,21 +5611,23 @@ _public_ int sd_bus_message_read_strv(sd_bus_message *m, char ***l) { return 1; } -int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str, char ***strv) { - const char *contents; - unsigned j; - char type; - int r; +static int bus_message_get_arg_skip( + sd_bus_message *m, + unsigned i, + char *_type, + const char **_contents) { - assert(m); - assert(str); - assert(strv); + unsigned j; + int r; r = sd_bus_message_rewind(m, true); if (r < 0) return r; for (j = 0;; j++) { + const char *contents; + char type; + r = sd_bus_message_peek_type(m, &type, &contents); if (r < 0) return r; @@ -5637,31 +5639,56 @@ int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str, char ** !(type == SD_BUS_TYPE_ARRAY && STR_IN_SET(contents, "s", "o", "g"))) return -ENXIO; - if (j >= i) - break; + if (j >= i) { + if (_contents) + *_contents = contents; + if (_type) + *_type = type; + return 0; + } r = sd_bus_message_skip(m, NULL); if (r < 0) return r; } - if (type == SD_BUS_TYPE_ARRAY) { +} - r = sd_bus_message_read_strv(m, strv); - if (r < 0) - return r; +int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str) { + char type; + int r; - *str = NULL; + assert(m); + assert(str); - } else { - r = sd_bus_message_read_basic(m, type, str); - if (r < 0) - return r; + r = bus_message_get_arg_skip(m, i, &type, NULL); + if (r < 0) + return r; - *strv = NULL; - } + if (!IN_SET(type, SD_BUS_TYPE_STRING, SD_BUS_TYPE_OBJECT_PATH, SD_BUS_TYPE_SIGNATURE)) + return -ENXIO; - return 0; + return sd_bus_message_read_basic(m, type, str); +} + +int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv) { + const char *contents; + char type; + int r; + + assert(m); + assert(strv); + + r = bus_message_get_arg_skip(m, i, &type, &contents); + if (r < 0) + return r; + + if (type != SD_BUS_TYPE_ARRAY) + return -ENXIO; + if (!STR_IN_SET(contents, "s", "o", "g")) + return -ENXIO; + + return sd_bus_message_read_strv(m, strv); } _public_ int sd_bus_message_get_errno(sd_bus_message *m) { diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h index 088d5b11093..ff250034618 100644 --- a/src/libsystemd/sd-bus/bus-message.h +++ b/src/libsystemd/sd-bus/bus-message.h @@ -218,7 +218,8 @@ int bus_message_from_malloc( const char *label, sd_bus_message **ret); -int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str, char ***strv); +int bus_message_get_arg(sd_bus_message *m, unsigned i, const char **str); +int bus_message_get_arg_strv(sd_bus_message *m, unsigned i, char ***strv); int bus_message_append_ap(sd_bus_message *m, const char *types, va_list ap); diff --git a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c index 90eb1f2a335..f3d1099dd2d 100644 --- a/src/libsystemd/sd-bus/test-bus-kernel-bloom.c +++ b/src/libsystemd/sd-bus/test-bus-kernel-bloom.c @@ -108,8 +108,10 @@ int main(int argc, char *argv[]) { test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "member='Pi_ep'", false); test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foobar'", true); test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "arg0='foo_bar'", false); - test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foobar'", false); test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0='foo_bar'", false); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foobar'", true); + test_one("/foo/bar/waldo", "waldo.com", "Piep", true, "foobar", "arg0has='foo_bar'", false); test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar'", true); test_one("/foo/bar/waldo", "waldo.com", "Piep", false, "foobar", "path='/foo/bar/waldo',interface='waldo.com',member='Piep',arg0='foobar2'", false); diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c index f659ba3124d..e29975db44e 100644 --- a/src/libsystemd/sd-bus/test-bus-match.c +++ b/src/libsystemd/sd-bus/test-bus-match.c @@ -114,10 +114,10 @@ int main(int argc, char *argv[]) { assert_se(match_add(slots, &root, "arg1='two'", 12) >= 0); assert_se(match_add(slots, &root, "member='waldo',arg2path='/prefix/'", 13) >= 0); assert_se(match_add(slots, &root, "member=waldo,path='/foo/bar',arg3namespace='prefix'", 14) >= 0); - assert_se(match_add(slots, &root, "arg4='pi'", 15) >= 0); - assert_se(match_add(slots, &root, "arg4='pa'", 16) >= 0); - assert_se(match_add(slots, &root, "arg4='po'", 17) >= 0); - assert_se(match_add(slots, &root, "arg4='pu'", 18) >= 0); + assert_se(match_add(slots, &root, "arg4has='pi'", 15) >= 0); + assert_se(match_add(slots, &root, "arg4has='pa'", 16) >= 0); + assert_se(match_add(slots, &root, "arg4has='po'", 17) >= 0); + assert_se(match_add(slots, &root, "arg4='pi'", 18) >= 0); bus_match_dump(&root, 0);