chiark / gitweb /
bus: don't rely on static IDs in tests
[elogind.git] / src / libsystemd / sd-bus / bus-match.c
index ffc97562fd8e286d12db6f6deeabf82f3c440f57..162f0ab608b1b6066e9792ed65db1d539a0768ca 100644 (file)
@@ -116,6 +116,9 @@ static void bus_match_node_free(struct bus_match_node *node) {
 static bool bus_match_node_maybe_free(struct bus_match_node *node) {
         assert(node);
 
+        if (node->type == BUS_MATCH_ROOT)
+                return false;
+
         if (node->child)
                 return false;
 
@@ -131,6 +134,7 @@ static bool value_node_test(
                 enum bus_match_node_type parent_type,
                 uint8_t value_u8,
                 const char *value_str,
+                char **value_strv,
                 sd_bus_message *m) {
 
         assert(node);
@@ -176,17 +180,46 @@ 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:
-                return streq_ptr(node->value.str, value_str);
+        case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: {
+                char **i;
 
-        case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
-                return namespace_simple_pattern(node->value.str, value_str);
+                if (value_str)
+                        return streq_ptr(node->value.str, value_str);
+
+                STRV_FOREACH(i, value_strv)
+                        if (streq_ptr(node->value.str, *i))
+                                return true;
+
+                return false;
+        }
+
+        case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: {
+                char **i;
+
+                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:
-                return path_complex_pattern(node->value.str, value_str);
+        case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: {
+                char **i;
+
+                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");
@@ -201,7 +234,7 @@ static bool value_node_same(
 
         /* Tests parameters against this value node, not doing prefix
          * magic and stuff, i.e. this one actually compares the match
-         * itself.*/
+         * itself. */
 
         assert(node);
         assert(node->type == BUS_MATCH_VALUE);
@@ -232,7 +265,7 @@ int bus_match_run(
                 struct bus_match_node *node,
                 sd_bus_message *m) {
 
-
+        _cleanup_strv_free_ char **test_strv = NULL;
         const char *test_str = NULL;
         uint8_t test_u8 = 0;
         int r;
@@ -275,10 +308,10 @@ int bus_match_run(
         case BUS_MATCH_LEAF:
 
                 if (bus) {
-                        if (node->leaf.last_iteration == bus->iteration_counter)
+                        if (node->leaf.callback->last_iteration == bus->iteration_counter)
                                 return 0;
 
-                        node->leaf.last_iteration = bus->iteration_counter;
+                        node->leaf.callback->last_iteration = bus->iteration_counter;
                 }
 
                 r = sd_bus_message_rewind(m, true);
@@ -286,13 +319,29 @@ int bus_match_run(
                         return r;
 
                 /* Run the callback. And then invoke siblings. */
-                if (node->leaf.callback) {
+                if (node->leaf.callback->callback) {
                         _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
+                        sd_bus_slot *slot;
+
+                        slot = container_of(node->leaf.callback, sd_bus_slot, match_callback);
+                        if (bus) {
+                                bus->current_slot = sd_bus_slot_ref(slot);
+                                bus->current_handler = node->leaf.callback->callback;
+                                bus->current_userdata = slot->userdata;
+                        }
+                        r = node->leaf.callback->callback(bus, m, slot->userdata, &error_buffer);
+                        if (bus) {
+                                bus->current_userdata = NULL;
+                                bus->current_handler = NULL;
+                                bus->current_slot = sd_bus_slot_unref(slot);
+                        }
 
-                        r = node->leaf.callback(bus, m, node->leaf.userdata, &error_buffer);
                         r = bus_maybe_reply_error(m, r, &error_buffer);
                         if (r != 0)
                                 return r;
+
+                        if (bus && bus->match_callbacks_modified)
+                                return 0;
                 }
 
                 return bus_match_run(bus, node->next, m);
@@ -324,15 +373,15 @@ int bus_match_run(
                 break;
 
         case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
-                test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG);
+                (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str, &test_strv);
                 break;
 
         case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
-                test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH);
+                (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str, &test_strv);
                 break;
 
         case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
-                test_str = bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE);
+                (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str, &test_strv);
                 break;
 
         default:
@@ -346,7 +395,20 @@ int bus_match_run(
 
                 if (test_str)
                         found = hashmap_get(node->compare.children, test_str);
-                else if (node->type == BUS_MATCH_MESSAGE_TYPE)
+                else if (test_strv) {
+                        char **i;
+
+                        STRV_FOREACH(i, test_strv) {
+                                found = hashmap_get(node->compare.children, *i);
+                                if (found) {
+                                        r = bus_match_run(bus, found, m);
+                                        if (r != 0)
+                                                return r;
+                                }
+                        }
+
+                        found = NULL;
+                } else if (node->type == BUS_MATCH_MESSAGE_TYPE)
                         found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
                 else
                         found = NULL;
@@ -362,7 +424,7 @@ int bus_match_run(
                 /* 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, m))
+                        if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m))
                                 continue;
 
                         r = bus_match_run(bus, c, m);
@@ -431,13 +493,13 @@ static int bus_match_add_compare_value(
                 where->child = c;
 
                 if (t == BUS_MATCH_MESSAGE_TYPE) {
-                        c->compare.children = hashmap_new(trivial_hash_func, trivial_compare_func);
+                        c->compare.children = hashmap_new(NULL);
                         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);
+                        c->compare.children = hashmap_new(&string_hash_ops);
                         if (!c->compare.children) {
                                 r = -ENOMEM;
                                 goto fail;
@@ -518,7 +580,7 @@ static int bus_match_find_compare_value(
         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)
+                for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
                         ;
         }
 
@@ -532,16 +594,13 @@ static int bus_match_find_compare_value(
 
 static int bus_match_add_leaf(
                 struct bus_match_node *where,
-                sd_bus_message_handler_t callback,
-                void *userdata,
-                uint64_t cookie,
-                struct bus_match_node **ret) {
+                struct match_callback *callback) {
 
         struct bus_match_node *n;
 
         assert(where);
         assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
-        assert(ret);
+        assert(callback);
 
         n = new0(struct bus_match_node, 1);
         if (!n)
@@ -552,13 +611,12 @@ static int bus_match_add_leaf(
         n->next = where->child;
         if (n->next)
                 n->next->prev = n;
+
         n->leaf.callback = callback;
-        n->leaf.userdata = userdata;
-        n->leaf.cookie = cookie;
+        callback->match_node = n;
 
         where->child = n;
 
-        *ret = n;
         return 1;
 }
 
@@ -575,9 +633,13 @@ static int bus_match_find_leaf(
         assert(ret);
 
         for (c = where->child; c; c = c->next) {
+                sd_bus_slot *s;
+
+                s = container_of(c->leaf.callback, sd_bus_slot, match_callback);
+
                 if (c->type == BUS_MATCH_LEAF &&
-                    c->leaf.callback == callback &&
-                    c->leaf.userdata == userdata) {
+                    c->leaf.callback->callback == callback &&
+                    s->userdata == userdata) {
                         *ret = c;
                         return 1;
                 }
@@ -729,6 +791,9 @@ int bus_match_parse(
                 bool escaped = false, quoted;
                 uint8_t u;
 
+                /* Avahi's match rules appear to include whitespace, skip over it */
+                p += strspn(p, " ");
+
                 eq = strchr(p, '=');
                 if (!eq)
                         return -EINVAL;
@@ -814,7 +879,7 @@ int bus_match_parse(
 
                 value = NULL;
 
-                if (q[1] == 0)
+                if (q[quoted] == 0)
                         break;
 
                 if (q[quoted] != ',') {
@@ -889,16 +954,14 @@ int bus_match_add(
                 struct bus_match_node *root,
                 struct bus_match_component *components,
                 unsigned n_components,
-                sd_bus_message_handler_t callback,
-                void *userdata,
-                uint64_t cookie,
-                struct bus_match_node **ret) {
+                struct match_callback *callback) {
 
         unsigned i;
         struct bus_match_node *n;
         int r;
 
         assert(root);
+        assert(callback);
 
         n = root;
         for (i = 0; i < n_components; i++) {
@@ -909,29 +972,56 @@ int bus_match_add(
                         return r;
         }
 
-        r = bus_match_add_leaf(n, callback, userdata, cookie, &n);
-        if (r < 0)
-                return r;
+        return bus_match_add_leaf(n, callback);
+}
 
-        if (ret)
-                *ret = n;
+int bus_match_remove(
+                struct bus_match_node *root,
+                struct match_callback *callback) {
 
-        return 0;
+        struct bus_match_node *node, *pp;
+
+        assert(root);
+        assert(callback);
+
+        node = callback->match_node;
+        if (!node)
+                return 0;
+
+        assert(node->type == BUS_MATCH_LEAF);
+
+        callback->match_node = NULL;
+
+        /* Free the leaf */
+        pp = node->parent;
+        bus_match_node_free(node);
+
+        /* Prune the tree above */
+        while (pp) {
+                node = pp;
+                pp = node->parent;
+
+                if (!bus_match_node_maybe_free(node))
+                        break;
+        }
+
+        return 1;
 }
 
-int bus_match_remove(
+int bus_match_find(
                 struct bus_match_node *root,
                 struct bus_match_component *components,
                 unsigned n_components,
                 sd_bus_message_handler_t callback,
                 void *userdata,
-                uint64_t *cookie) {
+                struct match_callback **ret) {
 
-        unsigned i;
         struct bus_match_node *n, **gc;
+        unsigned i;
         int r;
 
         assert(root);
+        assert(ret);
 
         gc = newa(struct bus_match_node*, n_components);
 
@@ -951,24 +1041,8 @@ int bus_match_remove(
         if (r <= 0)
                 return r;
 
-        if (cookie)
-                *cookie = n->leaf.cookie;
-
-        /* 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;
-        }
-
-        return r;
+        *ret = n->leaf.callback;
+        return 1;
 }
 
 void bus_match_free(struct bus_match_node *node) {
@@ -1062,7 +1136,7 @@ void bus_match_dump(struct bus_match_node *node, unsigned level) {
         } 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);
+                printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
         else
                 putchar('\n');