1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 //#include <stdio_ext.h>
7 #include "alloc-util.h"
8 #include "bus-internal.h"
10 #include "bus-message.h"
14 #include "hexdecoct.h"
15 #include "string-util.h"
20 * A: type=signal,sender=foo,interface=bar
21 * B: type=signal,sender=quux,interface=fips
22 * C: type=signal,sender=quux,interface=waldo
23 * D: type=signal,member=test
28 * results in this tree:
31 * + BUS_MATCH_MESSAGE_TYPE
32 * | ` BUS_MATCH_VALUE: value == signal
33 * | + DBUS_MATCH_SENDER
34 * | | + BUS_MATCH_VALUE: value == foo
35 * | | | ` DBUS_MATCH_INTERFACE
36 * | | | ` BUS_MATCH_VALUE: value == bar
37 * | | | ` BUS_MATCH_LEAF: A
38 * | | ` BUS_MATCH_VALUE: value == quux
39 * | | ` DBUS_MATCH_INTERFACE
40 * | | | BUS_MATCH_VALUE: value == fips
41 * | | | ` BUS_MATCH_LEAF: B
42 * | | ` BUS_MATCH_VALUE: value == waldo
43 * | | ` BUS_MATCH_LEAF: C
44 * | + DBUS_MATCH_MEMBER
45 * | | ` BUS_MATCH_VALUE: value == test
46 * | | ` BUS_MATCH_LEAF: D
47 * | + BUS_MATCH_LEAF: F
48 * | ` BUS_MATCH_LEAF: G
50 * ` BUS_MATCH_VALUE: value == miau
54 static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
55 return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_HAS_LAST;
58 static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
59 return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
60 (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST) ||
61 (t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_LAST);
64 static void bus_match_node_free(struct bus_match_node *node) {
68 assert(node->type != BUS_MATCH_ROOT);
69 assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
71 if (node->parent->child) {
72 /* We are apparently linked into the parent's child
73 * list. Let's remove us from there. */
75 assert(node->prev->next == node);
76 node->prev->next = node->next;
78 assert(node->parent->child == node);
79 node->parent->child = node->next;
83 node->next->prev = node->prev;
86 if (node->type == BUS_MATCH_VALUE) {
87 /* We might be in the parent's hash table, so clean
90 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
91 hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
92 else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
93 hashmap_remove(node->parent->compare.children, node->value.str);
95 free(node->value.str);
98 if (BUS_MATCH_IS_COMPARE(node->type)) {
99 assert(hashmap_isempty(node->compare.children));
100 hashmap_free(node->compare.children);
106 static bool bus_match_node_maybe_free(struct bus_match_node *node) {
109 if (node->type == BUS_MATCH_ROOT)
115 if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
118 bus_match_node_free(node);
122 static bool value_node_test(
123 struct bus_match_node *node,
124 enum bus_match_node_type parent_type,
126 const char *value_str,
131 assert(node->type == BUS_MATCH_VALUE);
133 /* Tests parameters against this value node, doing prefix
134 * magic and stuff. */
136 switch (parent_type) {
138 case BUS_MATCH_MESSAGE_TYPE:
139 return node->value.u8 == value_u8;
141 case BUS_MATCH_SENDER:
142 if (streq_ptr(node->value.str, value_str))
145 if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
148 /* on kdbus we have the well known names list
149 * in the credentials, let's make use of that
150 * for an accurate match */
152 STRV_FOREACH(i, m->creds.well_known_names)
153 if (streq_ptr(node->value.str, *i))
158 /* If we don't have kdbus, we don't know the
159 * well-known names of the senders. In that,
160 * let's just hope that dbus-daemon doesn't
161 * send us stuff we didn't want. */
163 if (node->value.str[0] != ':' && value_str && value_str[0] == ':')
169 case BUS_MATCH_DESTINATION:
170 case BUS_MATCH_INTERFACE:
171 case BUS_MATCH_MEMBER:
173 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
176 return streq_ptr(node->value.str, value_str);
180 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: {
183 STRV_FOREACH(i, value_strv)
184 if (streq_ptr(node->value.str, *i))
190 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
192 return namespace_simple_pattern(node->value.str, value_str);
196 case BUS_MATCH_PATH_NAMESPACE:
197 return path_simple_pattern(node->value.str, value_str);
199 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
201 return path_complex_pattern(node->value.str, value_str);
206 assert_not_reached("Invalid node type");
210 static bool value_node_same(
211 struct bus_match_node *node,
212 enum bus_match_node_type parent_type,
214 const char *value_str) {
216 /* Tests parameters against this value node, not doing prefix
217 * magic and stuff, i.e. this one actually compares the match
221 assert(node->type == BUS_MATCH_VALUE);
223 switch (parent_type) {
225 case BUS_MATCH_MESSAGE_TYPE:
226 return node->value.u8 == value_u8;
228 case BUS_MATCH_SENDER:
229 case BUS_MATCH_DESTINATION:
230 case BUS_MATCH_INTERFACE:
231 case BUS_MATCH_MEMBER:
233 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
234 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
235 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
236 case BUS_MATCH_PATH_NAMESPACE:
237 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
238 return streq(node->value.str, value_str);
241 assert_not_reached("Invalid node type");
247 struct bus_match_node *node,
250 _cleanup_strv_free_ char **test_strv = NULL;
251 const char *test_str = NULL;
260 if (bus && bus->match_callbacks_modified)
263 /* Not these special semantics: when traversing the tree we
264 * usually let bus_match_run() when called for a node
265 * recursively invoke bus_match_run(). There's are two
266 * exceptions here though, which are BUS_NODE_ROOT (which
267 * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
268 * are invoked anyway by its parent. */
270 switch (node->type) {
274 /* Run all children. Since we cannot have any siblings
275 * we won't call any. The children of the root node
276 * are compares or leaves, they will automatically
277 * call their siblings. */
278 return bus_match_run(bus, node->child, m);
280 case BUS_MATCH_VALUE:
282 /* Run all children. We don't execute any siblings, we
283 * assume our caller does that. The children of value
284 * nodes are compares or leaves, they will
285 * automatically call their siblings */
288 return bus_match_run(bus, node->child, m);
293 if (node->leaf.callback->last_iteration == bus->iteration_counter)
296 node->leaf.callback->last_iteration = bus->iteration_counter;
299 r = sd_bus_message_rewind(m, true);
303 /* Run the callback. And then invoke siblings. */
304 if (node->leaf.callback->callback) {
305 _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
308 slot = container_of(node->leaf.callback, sd_bus_slot, match_callback);
310 bus->current_slot = sd_bus_slot_ref(slot);
311 bus->current_handler = node->leaf.callback->callback;
312 bus->current_userdata = slot->userdata;
314 r = node->leaf.callback->callback(m, slot->userdata, &error_buffer);
316 bus->current_userdata = NULL;
317 bus->current_handler = NULL;
318 bus->current_slot = sd_bus_slot_unref(slot);
321 r = bus_maybe_reply_error(m, r, &error_buffer);
325 if (bus && bus->match_callbacks_modified)
329 return bus_match_run(bus, node->next, m);
331 case BUS_MATCH_MESSAGE_TYPE:
332 test_u8 = m->header->type;
335 case BUS_MATCH_SENDER:
336 test_str = m->sender;
337 /* FIXME: resolve test_str from a well-known to a unique name first */
340 case BUS_MATCH_DESTINATION:
341 test_str = m->destination;
344 case BUS_MATCH_INTERFACE:
345 test_str = m->interface;
348 case BUS_MATCH_MEMBER:
349 test_str = m->member;
353 case BUS_MATCH_PATH_NAMESPACE:
357 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
358 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str);
361 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
362 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str);
365 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
366 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str);
369 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
370 (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv);
374 assert_not_reached("Unknown match type.");
377 if (BUS_MATCH_CAN_HASH(node->type)) {
378 struct bus_match_node *found;
380 /* Lookup via hash table, nice! So let's jump directly. */
383 found = hashmap_get(node->compare.children, test_str);
384 else if (test_strv) {
387 STRV_FOREACH(i, test_strv) {
388 found = hashmap_get(node->compare.children, *i);
390 r = bus_match_run(bus, found, m);
397 } else if (node->type == BUS_MATCH_MESSAGE_TYPE)
398 found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
403 r = bus_match_run(bus, found, m);
408 struct bus_match_node *c;
410 /* No hash table, so let's iterate manually... */
412 for (c = node->child; c; c = c->next) {
413 if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m))
416 r = bus_match_run(bus, c, m);
420 if (bus && bus->match_callbacks_modified)
425 if (bus && bus->match_callbacks_modified)
428 /* And now, let's invoke our siblings */
429 return bus_match_run(bus, node->next, m);
432 static int bus_match_add_compare_value(
433 struct bus_match_node *where,
434 enum bus_match_node_type t,
436 const char *value_str,
437 struct bus_match_node **ret) {
439 struct bus_match_node *c = NULL, *n = NULL;
443 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
444 assert(BUS_MATCH_IS_COMPARE(t));
447 for (c = where->child; c && c->type != t; c = c->next)
451 /* Comparison node already exists? Then let's see if
452 * the value node exists too. */
454 if (t == BUS_MATCH_MESSAGE_TYPE)
455 n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
456 else if (BUS_MATCH_CAN_HASH(t))
457 n = hashmap_get(c->compare.children, value_str);
459 for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
468 /* Comparison node, doesn't exist yet? Then let's
471 c = new0(struct bus_match_node, 1);
479 c->next = where->child;
484 if (t == BUS_MATCH_MESSAGE_TYPE) {
485 c->compare.children = hashmap_new(NULL);
486 if (!c->compare.children) {
490 } else if (BUS_MATCH_CAN_HASH(t)) {
491 c->compare.children = hashmap_new(&string_hash_ops);
492 if (!c->compare.children) {
499 n = new0(struct bus_match_node, 1);
505 n->type = BUS_MATCH_VALUE;
506 n->value.u8 = value_u8;
508 n->value.str = strdup(value_str);
516 if (c->compare.children) {
518 if (t == BUS_MATCH_MESSAGE_TYPE)
519 r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
521 r = hashmap_put(c->compare.children, n->value.str, n);
537 bus_match_node_maybe_free(c);
547 static int bus_match_find_compare_value(
548 struct bus_match_node *where,
549 enum bus_match_node_type t,
551 const char *value_str,
552 struct bus_match_node **ret) {
554 struct bus_match_node *c, *n;
557 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
558 assert(BUS_MATCH_IS_COMPARE(t));
561 for (c = where->child; c && c->type != t; c = c->next)
567 if (t == BUS_MATCH_MESSAGE_TYPE)
568 n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
569 else if (BUS_MATCH_CAN_HASH(t))
570 n = hashmap_get(c->compare.children, value_str);
572 for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
584 static int bus_match_add_leaf(
585 struct bus_match_node *where,
586 struct match_callback *callback) {
588 struct bus_match_node *n;
591 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
594 n = new0(struct bus_match_node, 1);
598 n->type = BUS_MATCH_LEAF;
600 n->next = where->child;
604 n->leaf.callback = callback;
605 callback->match_node = n;
612 static int bus_match_find_leaf(
613 struct bus_match_node *where,
614 sd_bus_message_handler_t callback,
616 struct bus_match_node **ret) {
618 struct bus_match_node *c;
621 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
624 for (c = where->child; c; c = c->next) {
627 s = container_of(c->leaf.callback, sd_bus_slot, match_callback);
629 if (c->type == BUS_MATCH_LEAF &&
630 c->leaf.callback->callback == callback &&
631 s->userdata == userdata) {
640 enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
643 if (n == 4 && startswith(k, "type"))
644 return BUS_MATCH_MESSAGE_TYPE;
645 if (n == 6 && startswith(k, "sender"))
646 return BUS_MATCH_SENDER;
647 if (n == 11 && startswith(k, "destination"))
648 return BUS_MATCH_DESTINATION;
649 if (n == 9 && startswith(k, "interface"))
650 return BUS_MATCH_INTERFACE;
651 if (n == 6 && startswith(k, "member"))
652 return BUS_MATCH_MEMBER;
653 if (n == 4 && startswith(k, "path"))
654 return BUS_MATCH_PATH;
655 if (n == 14 && startswith(k, "path_namespace"))
656 return BUS_MATCH_PATH_NAMESPACE;
658 if (n == 4 && startswith(k, "arg")) {
665 return BUS_MATCH_ARG + j;
668 if (n == 5 && startswith(k, "arg")) {
670 enum bus_match_node_type t;
677 t = BUS_MATCH_ARG + a * 10 + b;
678 if (t > BUS_MATCH_ARG_LAST)
684 if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) {
691 return BUS_MATCH_ARG_PATH + j;
694 if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) {
695 enum bus_match_node_type t;
703 t = BUS_MATCH_ARG_PATH + a * 10 + b;
704 if (t > BUS_MATCH_ARG_PATH_LAST)
710 if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) {
717 return BUS_MATCH_ARG_NAMESPACE + j;
720 if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) {
721 enum bus_match_node_type t;
729 t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
730 if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
736 if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) {
743 return BUS_MATCH_ARG_HAS + j;
746 if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) {
747 enum bus_match_node_type t;
755 t = BUS_MATCH_ARG_HAS + a * 10 + b;
756 if (t > BUS_MATCH_ARG_HAS_LAST)
765 static int match_component_compare(const void *a, const void *b) {
766 const struct bus_match_component *x = a, *y = b;
768 if (x->type < y->type)
770 if (x->type > y->type)
776 void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
779 for (i = 0; i < n_components; i++)
780 free(components[i].value_str);
787 struct bus_match_component **_components,
788 unsigned *_n_components) {
790 const char *p = match;
791 struct bus_match_component *components = NULL;
792 size_t components_allocated = 0;
793 unsigned n_components = 0, i;
794 _cleanup_free_ char *value = NULL;
799 assert(_n_components);
803 enum bus_match_node_type t;
805 size_t value_allocated = 0;
806 bool escaped = false, quoted;
809 /* Avahi's match rules appear to include whitespace, skip over it */
816 t = bus_match_node_type_from_string(p, eq - p);
820 quoted = eq[1] == '\'';
822 for (q = eq + 1 + quoted;; q++) {
858 if (!GREEDY_REALLOC(value, value_allocated, j + 2)) {
875 if (t == BUS_MATCH_MESSAGE_TYPE) {
876 r = bus_message_type_from_string(value, &u);
880 value = mfree(value);
884 if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) {
889 components[n_components].type = t;
890 components[n_components].value_str = TAKE_PTR(value);
891 components[n_components].value_u8 = u;
897 if (q[quoted] != ',') {
905 /* Order the whole thing, so that we always generate the same tree */
906 qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare);
908 /* Check for duplicates */
909 for (i = 0; i+1 < n_components; i++)
910 if (components[i].type == components[i+1].type) {
915 *_components = components;
916 *_n_components = n_components;
921 bus_match_parse_free(components, n_components);
925 #if 0 /// UNNEEDED by elogind
926 char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
927 _cleanup_fclose_ FILE *f = NULL;
933 if (n_components <= 0)
938 f = open_memstream(&buffer, &size);
942 __fsetlocking(f, FSETLOCKING_BYCALLER);
944 for (i = 0; i < n_components; i++) {
950 fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
954 if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
955 fputs(bus_message_type_to_string(components[i].value_u8), f);
957 fputs(components[i].value_str, f);
962 r = fflush_and_check(f);
971 struct bus_match_node *root,
972 struct bus_match_component *components,
973 unsigned n_components,
974 struct match_callback *callback) {
977 struct bus_match_node *n;
984 for (i = 0; i < n_components; i++) {
985 r = bus_match_add_compare_value(
986 n, components[i].type,
987 components[i].value_u8, components[i].value_str, &n);
992 return bus_match_add_leaf(n, callback);
995 int bus_match_remove(
996 struct bus_match_node *root,
997 struct match_callback *callback) {
999 struct bus_match_node *node, *pp;
1004 node = callback->match_node;
1008 assert(node->type == BUS_MATCH_LEAF);
1010 callback->match_node = NULL;
1014 bus_match_node_free(node);
1016 /* Prune the tree above */
1021 if (!bus_match_node_maybe_free(node))
1029 struct bus_match_node *root,
1030 struct bus_match_component *components,
1031 unsigned n_components,
1032 sd_bus_message_handler_t callback,
1034 struct match_callback **ret) {
1036 struct bus_match_node *n, **gc;
1043 gc = newa(struct bus_match_node*, n_components);
1046 for (i = 0; i < n_components; i++) {
1047 r = bus_match_find_compare_value(
1048 n, components[i].type,
1049 components[i].value_u8, components[i].value_str,
1057 r = bus_match_find_leaf(n, callback, userdata, &n);
1061 *ret = n->leaf.callback;
1065 void bus_match_free(struct bus_match_node *node) {
1066 struct bus_match_node *c;
1071 if (BUS_MATCH_CAN_HASH(node->type)) {
1074 HASHMAP_FOREACH(c, node->compare.children, i)
1077 assert(hashmap_isempty(node->compare.children));
1080 while ((c = node->child))
1083 if (node->type != BUS_MATCH_ROOT)
1084 bus_match_node_free(node);
1087 const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
1090 case BUS_MATCH_ROOT:
1093 case BUS_MATCH_VALUE:
1096 case BUS_MATCH_LEAF:
1099 case BUS_MATCH_MESSAGE_TYPE:
1102 case BUS_MATCH_SENDER:
1105 case BUS_MATCH_DESTINATION:
1106 return "destination";
1108 case BUS_MATCH_INTERFACE:
1111 case BUS_MATCH_MEMBER:
1114 case BUS_MATCH_PATH:
1117 case BUS_MATCH_PATH_NAMESPACE:
1118 return "path_namespace";
1120 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
1121 snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
1124 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
1125 snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
1128 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
1129 snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
1132 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
1133 snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
1141 void bus_match_dump(struct bus_match_node *node, unsigned level) {
1142 struct bus_match_node *c;
1143 _cleanup_free_ char *pfx = NULL;
1149 pfx = strrep(" ", level);
1150 printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
1152 if (node->type == BUS_MATCH_VALUE) {
1153 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
1154 printf(" <%u>\n", node->value.u8);
1156 printf(" <%s>\n", node->value.str);
1157 } else if (node->type == BUS_MATCH_ROOT)
1159 else if (node->type == BUS_MATCH_LEAF)
1160 printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
1164 if (BUS_MATCH_CAN_HASH(node->type)) {
1167 HASHMAP_FOREACH(c, node->compare.children, i)
1168 bus_match_dump(c, level + 1);
1171 for (c = node->child; c; c = c->next)
1172 bus_match_dump(c, level + 1);
1175 enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) {
1176 bool found_driver = false;
1179 if (n_components <= 0)
1180 return BUS_MATCH_GENERIC;
1184 /* Checks whether the specified match can only match the
1185 * pseudo-service for local messages, which we detect by
1186 * sender, interface or path. If a match is not restricted to
1187 * local messages, then we check if it only matches on the
1190 for (i = 0; i < n_components; i++) {
1191 const struct bus_match_component *c = components + i;
1193 if (c->type == BUS_MATCH_SENDER) {
1194 if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1195 return BUS_MATCH_LOCAL;
1197 if (streq_ptr(c->value_str, "org.freedesktop.DBus"))
1198 found_driver = true;
1201 if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1202 return BUS_MATCH_LOCAL;
1204 if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local"))
1205 return BUS_MATCH_LOCAL;
1208 return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC;