]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-bus: introduce new match type "arg0has=" for matching arrays of strings 1036/head
authorLennart Poettering <lennart@poettering.net>
Tue, 25 Aug 2015 17:28:30 +0000 (19:28 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 25 Aug 2015 17:28:30 +0000 (19:28 +0200)
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
src/libsystemd/sd-bus/bus-kernel.c
src/libsystemd/sd-bus/bus-match.c
src/libsystemd/sd-bus/bus-match.h
src/libsystemd/sd-bus/bus-message.c
src/libsystemd/sd-bus/bus-message.h
src/libsystemd/sd-bus/test-bus-kernel-bloom.c
src/libsystemd/sd-bus/test-bus-match.c

index 95c7d4ebe4bcf8dbd94d42d98830daf7904597cd..ad83446254cb37af63bafd299cf3c0e7365efe56 100644 (file)
@@ -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:
index 21f37001289e37c13be341caea156abe9641f546..a217cf1a2392804fa3fd5b4fb671b9df6b499ba6 100644 (file)
@@ -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;
 
index f3b49c0c9058a10b05df24db7527aca18563edf5..7175cbe8ed9f7a73ddf920df82481b168e4df5b5 100644 (file)
  */
 
 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;
         }
index 56516be9faa0fcd5df16fb66429d02ea628a5883..53ee0463ca2527801c43ff5c43a512bb689b573b 100644 (file)
@@ -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
 };
index a212f0b398bd9de9e581ea73e1ae094ef61f7b84..970aaabdc2b988b2bfeefb87753657d35fa06a78 100644 (file)
@@ -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;
+static int bus_message_get_arg_skip(
+                sd_bus_message *m,
+                unsigned i,
+                char *_type,
+                const char **_contents) {
+
         unsigned j;
-        char type;
         int r;
 
-        assert(m);
-        assert(str);
-        assert(strv);
-
         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) {
index 088d5b110936b77bdb6b0ca57b29eb27d9aab5af..ff250034618d754fd65646e5ba6f67d4aa187306 100644 (file)
@@ -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);
 
index 90eb1f2a3350d98ea714034077e6c8c8bdc1bc11..f3d1099dd2d9a011815c090a1643af1e4132899f 100644 (file)
@@ -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);
 
index f659ba3124d1f4945603b020ba31ac96db465646..e29975db44e45c322081f8666c8a7a3217ed1d0c 100644 (file)
@@ -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);