1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
8 //#include <stdio_ext.h>
10 #include "alloc-util.h"
11 #include "bus-internal.h"
12 #include "bus-match.h"
13 #include "bus-message.h"
17 #include "hexdecoct.h"
18 #include "string-util.h"
23 * A: type=signal,sender=foo,interface=bar
24 * B: type=signal,sender=quux,interface=fips
25 * C: type=signal,sender=quux,interface=waldo
26 * D: type=signal,member=test
31 * results in this tree:
34 * + BUS_MATCH_MESSAGE_TYPE
35 * | ` BUS_MATCH_VALUE: value == signal
36 * | + DBUS_MATCH_SENDER
37 * | | + BUS_MATCH_VALUE: value == foo
38 * | | | ` DBUS_MATCH_INTERFACE
39 * | | | ` BUS_MATCH_VALUE: value == bar
40 * | | | ` BUS_MATCH_LEAF: A
41 * | | ` BUS_MATCH_VALUE: value == quux
42 * | | ` DBUS_MATCH_INTERFACE
43 * | | | BUS_MATCH_VALUE: value == fips
44 * | | | ` BUS_MATCH_LEAF: B
45 * | | ` BUS_MATCH_VALUE: value == waldo
46 * | | ` BUS_MATCH_LEAF: C
47 * | + DBUS_MATCH_MEMBER
48 * | | ` BUS_MATCH_VALUE: value == test
49 * | | ` BUS_MATCH_LEAF: D
50 * | + BUS_MATCH_LEAF: F
51 * | ` BUS_MATCH_LEAF: G
53 * ` BUS_MATCH_VALUE: value == miau
57 static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
58 return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_HAS_LAST;
61 static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
62 return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
63 (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST) ||
64 (t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_LAST);
67 static void bus_match_node_free(struct bus_match_node *node) {
71 assert(node->type != BUS_MATCH_ROOT);
72 assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
74 if (node->parent->child) {
75 /* We are apparently linked into the parent's child
76 * list. Let's remove us from there. */
78 assert(node->prev->next == node);
79 node->prev->next = node->next;
81 assert(node->parent->child == node);
82 node->parent->child = node->next;
86 node->next->prev = node->prev;
89 if (node->type == BUS_MATCH_VALUE) {
90 /* We might be in the parent's hash table, so clean
93 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
94 hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
95 else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
96 hashmap_remove(node->parent->compare.children, node->value.str);
98 free(node->value.str);
101 if (BUS_MATCH_IS_COMPARE(node->type)) {
102 assert(hashmap_isempty(node->compare.children));
103 hashmap_free(node->compare.children);
109 static bool bus_match_node_maybe_free(struct bus_match_node *node) {
112 if (node->type == BUS_MATCH_ROOT)
118 if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
121 bus_match_node_free(node);
125 static bool value_node_test(
126 struct bus_match_node *node,
127 enum bus_match_node_type parent_type,
129 const char *value_str,
134 assert(node->type == BUS_MATCH_VALUE);
136 /* Tests parameters against this value node, doing prefix
137 * magic and stuff. */
139 switch (parent_type) {
141 case BUS_MATCH_MESSAGE_TYPE:
142 return node->value.u8 == value_u8;
144 case BUS_MATCH_SENDER:
145 if (streq_ptr(node->value.str, value_str))
148 if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
151 /* on kdbus we have the well known names list
152 * in the credentials, let's make use of that
153 * for an accurate match */
155 STRV_FOREACH(i, m->creds.well_known_names)
156 if (streq_ptr(node->value.str, *i))
161 /* If we don't have kdbus, we don't know the
162 * well-known names of the senders. In that,
163 * let's just hope that dbus-daemon doesn't
164 * send us stuff we didn't want. */
166 if (node->value.str[0] != ':' && value_str && value_str[0] == ':')
172 case BUS_MATCH_DESTINATION:
173 case BUS_MATCH_INTERFACE:
174 case BUS_MATCH_MEMBER:
176 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
179 return streq_ptr(node->value.str, value_str);
183 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: {
186 STRV_FOREACH(i, value_strv)
187 if (streq_ptr(node->value.str, *i))
193 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
195 return namespace_simple_pattern(node->value.str, value_str);
199 case BUS_MATCH_PATH_NAMESPACE:
200 return path_simple_pattern(node->value.str, value_str);
202 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
204 return path_complex_pattern(node->value.str, value_str);
209 assert_not_reached("Invalid node type");
213 static bool value_node_same(
214 struct bus_match_node *node,
215 enum bus_match_node_type parent_type,
217 const char *value_str) {
219 /* Tests parameters against this value node, not doing prefix
220 * magic and stuff, i.e. this one actually compares the match
224 assert(node->type == BUS_MATCH_VALUE);
226 switch (parent_type) {
228 case BUS_MATCH_MESSAGE_TYPE:
229 return node->value.u8 == value_u8;
231 case BUS_MATCH_SENDER:
232 case BUS_MATCH_DESTINATION:
233 case BUS_MATCH_INTERFACE:
234 case BUS_MATCH_MEMBER:
236 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
237 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
238 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
239 case BUS_MATCH_PATH_NAMESPACE:
240 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
241 return streq(node->value.str, value_str);
244 assert_not_reached("Invalid node type");
250 struct bus_match_node *node,
253 _cleanup_strv_free_ char **test_strv = NULL;
254 const char *test_str = NULL;
263 if (bus && bus->match_callbacks_modified)
266 /* Not these special semantics: when traversing the tree we
267 * usually let bus_match_run() when called for a node
268 * recursively invoke bus_match_run(). There's are two
269 * exceptions here though, which are BUS_NODE_ROOT (which
270 * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
271 * are invoked anyway by its parent. */
273 switch (node->type) {
277 /* Run all children. Since we cannot have any siblings
278 * we won't call any. The children of the root node
279 * are compares or leaves, they will automatically
280 * call their siblings. */
281 return bus_match_run(bus, node->child, m);
283 case BUS_MATCH_VALUE:
285 /* Run all children. We don't execute any siblings, we
286 * assume our caller does that. The children of value
287 * nodes are compares or leaves, they will
288 * automatically call their siblings */
291 return bus_match_run(bus, node->child, m);
296 if (node->leaf.callback->last_iteration == bus->iteration_counter)
299 node->leaf.callback->last_iteration = bus->iteration_counter;
302 r = sd_bus_message_rewind(m, true);
306 /* Run the callback. And then invoke siblings. */
307 if (node->leaf.callback->callback) {
308 _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
311 slot = container_of(node->leaf.callback, sd_bus_slot, match_callback);
313 bus->current_slot = sd_bus_slot_ref(slot);
314 bus->current_handler = node->leaf.callback->callback;
315 bus->current_userdata = slot->userdata;
317 r = node->leaf.callback->callback(m, slot->userdata, &error_buffer);
319 bus->current_userdata = NULL;
320 bus->current_handler = NULL;
321 bus->current_slot = sd_bus_slot_unref(slot);
324 r = bus_maybe_reply_error(m, r, &error_buffer);
328 if (bus && bus->match_callbacks_modified)
332 return bus_match_run(bus, node->next, m);
334 case BUS_MATCH_MESSAGE_TYPE:
335 test_u8 = m->header->type;
338 case BUS_MATCH_SENDER:
339 test_str = m->sender;
340 /* FIXME: resolve test_str from a well-known to a unique name first */
343 case BUS_MATCH_DESTINATION:
344 test_str = m->destination;
347 case BUS_MATCH_INTERFACE:
348 test_str = m->interface;
351 case BUS_MATCH_MEMBER:
352 test_str = m->member;
356 case BUS_MATCH_PATH_NAMESPACE:
360 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
361 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str);
364 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
365 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str);
368 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
369 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str);
372 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
373 (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv);
377 assert_not_reached("Unknown match type.");
380 if (BUS_MATCH_CAN_HASH(node->type)) {
381 struct bus_match_node *found;
383 /* Lookup via hash table, nice! So let's jump directly. */
386 found = hashmap_get(node->compare.children, test_str);
387 else if (test_strv) {
390 STRV_FOREACH(i, test_strv) {
391 found = hashmap_get(node->compare.children, *i);
393 r = bus_match_run(bus, found, m);
400 } else if (node->type == BUS_MATCH_MESSAGE_TYPE)
401 found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
406 r = bus_match_run(bus, found, m);
411 struct bus_match_node *c;
413 /* No hash table, so let's iterate manually... */
415 for (c = node->child; c; c = c->next) {
416 if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m))
419 r = bus_match_run(bus, c, m);
423 if (bus && bus->match_callbacks_modified)
428 if (bus && bus->match_callbacks_modified)
431 /* And now, let's invoke our siblings */
432 return bus_match_run(bus, node->next, m);
435 static int bus_match_add_compare_value(
436 struct bus_match_node *where,
437 enum bus_match_node_type t,
439 const char *value_str,
440 struct bus_match_node **ret) {
442 struct bus_match_node *c = NULL, *n = NULL;
446 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
447 assert(BUS_MATCH_IS_COMPARE(t));
450 for (c = where->child; c && c->type != t; c = c->next)
454 /* Comparison node already exists? Then let's see if
455 * the value node exists too. */
457 if (t == BUS_MATCH_MESSAGE_TYPE)
458 n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
459 else if (BUS_MATCH_CAN_HASH(t))
460 n = hashmap_get(c->compare.children, value_str);
462 for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
471 /* Comparison node, doesn't exist yet? Then let's
474 c = new0(struct bus_match_node, 1);
482 c->next = where->child;
487 if (t == BUS_MATCH_MESSAGE_TYPE) {
488 c->compare.children = hashmap_new(NULL);
489 if (!c->compare.children) {
493 } else if (BUS_MATCH_CAN_HASH(t)) {
494 c->compare.children = hashmap_new(&string_hash_ops);
495 if (!c->compare.children) {
502 n = new0(struct bus_match_node, 1);
508 n->type = BUS_MATCH_VALUE;
509 n->value.u8 = value_u8;
511 n->value.str = strdup(value_str);
519 if (c->compare.children) {
521 if (t == BUS_MATCH_MESSAGE_TYPE)
522 r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
524 r = hashmap_put(c->compare.children, n->value.str, n);
540 bus_match_node_maybe_free(c);
550 static int bus_match_find_compare_value(
551 struct bus_match_node *where,
552 enum bus_match_node_type t,
554 const char *value_str,
555 struct bus_match_node **ret) {
557 struct bus_match_node *c, *n;
560 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
561 assert(BUS_MATCH_IS_COMPARE(t));
564 for (c = where->child; c && c->type != t; c = c->next)
570 if (t == BUS_MATCH_MESSAGE_TYPE)
571 n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
572 else if (BUS_MATCH_CAN_HASH(t))
573 n = hashmap_get(c->compare.children, value_str);
575 for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
587 static int bus_match_add_leaf(
588 struct bus_match_node *where,
589 struct match_callback *callback) {
591 struct bus_match_node *n;
594 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
597 n = new0(struct bus_match_node, 1);
601 n->type = BUS_MATCH_LEAF;
603 n->next = where->child;
607 n->leaf.callback = callback;
608 callback->match_node = n;
615 static int bus_match_find_leaf(
616 struct bus_match_node *where,
617 sd_bus_message_handler_t callback,
619 struct bus_match_node **ret) {
621 struct bus_match_node *c;
624 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
627 for (c = where->child; c; c = c->next) {
630 s = container_of(c->leaf.callback, sd_bus_slot, match_callback);
632 if (c->type == BUS_MATCH_LEAF &&
633 c->leaf.callback->callback == callback &&
634 s->userdata == userdata) {
643 enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
646 if (n == 4 && startswith(k, "type"))
647 return BUS_MATCH_MESSAGE_TYPE;
648 if (n == 6 && startswith(k, "sender"))
649 return BUS_MATCH_SENDER;
650 if (n == 11 && startswith(k, "destination"))
651 return BUS_MATCH_DESTINATION;
652 if (n == 9 && startswith(k, "interface"))
653 return BUS_MATCH_INTERFACE;
654 if (n == 6 && startswith(k, "member"))
655 return BUS_MATCH_MEMBER;
656 if (n == 4 && startswith(k, "path"))
657 return BUS_MATCH_PATH;
658 if (n == 14 && startswith(k, "path_namespace"))
659 return BUS_MATCH_PATH_NAMESPACE;
661 if (n == 4 && startswith(k, "arg")) {
668 return BUS_MATCH_ARG + j;
671 if (n == 5 && startswith(k, "arg")) {
673 enum bus_match_node_type t;
680 t = BUS_MATCH_ARG + a * 10 + b;
681 if (t > BUS_MATCH_ARG_LAST)
687 if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) {
694 return BUS_MATCH_ARG_PATH + j;
697 if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) {
698 enum bus_match_node_type t;
706 t = BUS_MATCH_ARG_PATH + a * 10 + b;
707 if (t > BUS_MATCH_ARG_PATH_LAST)
713 if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) {
720 return BUS_MATCH_ARG_NAMESPACE + j;
723 if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) {
724 enum bus_match_node_type t;
732 t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
733 if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
739 if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) {
746 return BUS_MATCH_ARG_HAS + j;
749 if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) {
750 enum bus_match_node_type t;
758 t = BUS_MATCH_ARG_HAS + a * 10 + b;
759 if (t > BUS_MATCH_ARG_HAS_LAST)
768 static int match_component_compare(const void *a, const void *b) {
769 const struct bus_match_component *x = a, *y = b;
771 if (x->type < y->type)
773 if (x->type > y->type)
779 void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
782 for (i = 0; i < n_components; i++)
783 free(components[i].value_str);
790 struct bus_match_component **_components,
791 unsigned *_n_components) {
793 const char *p = match;
794 struct bus_match_component *components = NULL;
795 size_t components_allocated = 0;
796 unsigned n_components = 0, i;
797 _cleanup_free_ char *value = NULL;
802 assert(_n_components);
806 enum bus_match_node_type t;
808 size_t value_allocated = 0;
809 bool escaped = false, quoted;
812 /* Avahi's match rules appear to include whitespace, skip over it */
819 t = bus_match_node_type_from_string(p, eq - p);
823 quoted = eq[1] == '\'';
825 for (q = eq + 1 + quoted;; q++) {
861 if (!GREEDY_REALLOC(value, value_allocated, j + 2)) {
878 if (t == BUS_MATCH_MESSAGE_TYPE) {
879 r = bus_message_type_from_string(value, &u);
883 value = mfree(value);
887 if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) {
892 components[n_components].type = t;
893 components[n_components].value_str = TAKE_PTR(value);
894 components[n_components].value_u8 = u;
900 if (q[quoted] != ',') {
908 /* Order the whole thing, so that we always generate the same tree */
909 qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare);
911 /* Check for duplicates */
912 for (i = 0; i+1 < n_components; i++)
913 if (components[i].type == components[i+1].type) {
918 *_components = components;
919 *_n_components = n_components;
924 bus_match_parse_free(components, n_components);
928 #if 0 /// UNNEEDED by elogind
929 char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
930 _cleanup_fclose_ FILE *f = NULL;
936 if (n_components <= 0)
941 f = open_memstream(&buffer, &size);
945 __fsetlocking(f, FSETLOCKING_BYCALLER);
947 for (i = 0; i < n_components; i++) {
953 fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
957 if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
958 fputs(bus_message_type_to_string(components[i].value_u8), f);
960 fputs(components[i].value_str, f);
965 r = fflush_and_check(f);
974 struct bus_match_node *root,
975 struct bus_match_component *components,
976 unsigned n_components,
977 struct match_callback *callback) {
980 struct bus_match_node *n;
987 for (i = 0; i < n_components; i++) {
988 r = bus_match_add_compare_value(
989 n, components[i].type,
990 components[i].value_u8, components[i].value_str, &n);
995 return bus_match_add_leaf(n, callback);
998 int bus_match_remove(
999 struct bus_match_node *root,
1000 struct match_callback *callback) {
1002 struct bus_match_node *node, *pp;
1007 node = callback->match_node;
1011 assert(node->type == BUS_MATCH_LEAF);
1013 callback->match_node = NULL;
1017 bus_match_node_free(node);
1019 /* Prune the tree above */
1024 if (!bus_match_node_maybe_free(node))
1032 struct bus_match_node *root,
1033 struct bus_match_component *components,
1034 unsigned n_components,
1035 sd_bus_message_handler_t callback,
1037 struct match_callback **ret) {
1039 struct bus_match_node *n, **gc;
1046 gc = newa(struct bus_match_node*, n_components);
1049 for (i = 0; i < n_components; i++) {
1050 r = bus_match_find_compare_value(
1051 n, components[i].type,
1052 components[i].value_u8, components[i].value_str,
1060 r = bus_match_find_leaf(n, callback, userdata, &n);
1064 *ret = n->leaf.callback;
1068 void bus_match_free(struct bus_match_node *node) {
1069 struct bus_match_node *c;
1074 if (BUS_MATCH_CAN_HASH(node->type)) {
1077 HASHMAP_FOREACH(c, node->compare.children, i)
1080 assert(hashmap_isempty(node->compare.children));
1083 while ((c = node->child))
1086 if (node->type != BUS_MATCH_ROOT)
1087 bus_match_node_free(node);
1090 const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
1093 case BUS_MATCH_ROOT:
1096 case BUS_MATCH_VALUE:
1099 case BUS_MATCH_LEAF:
1102 case BUS_MATCH_MESSAGE_TYPE:
1105 case BUS_MATCH_SENDER:
1108 case BUS_MATCH_DESTINATION:
1109 return "destination";
1111 case BUS_MATCH_INTERFACE:
1114 case BUS_MATCH_MEMBER:
1117 case BUS_MATCH_PATH:
1120 case BUS_MATCH_PATH_NAMESPACE:
1121 return "path_namespace";
1123 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
1124 snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
1127 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
1128 snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
1131 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
1132 snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
1135 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
1136 snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
1144 void bus_match_dump(struct bus_match_node *node, unsigned level) {
1145 struct bus_match_node *c;
1146 _cleanup_free_ char *pfx = NULL;
1152 pfx = strrep(" ", level);
1153 printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
1155 if (node->type == BUS_MATCH_VALUE) {
1156 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
1157 printf(" <%u>\n", node->value.u8);
1159 printf(" <%s>\n", node->value.str);
1160 } else if (node->type == BUS_MATCH_ROOT)
1162 else if (node->type == BUS_MATCH_LEAF)
1163 printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
1167 if (BUS_MATCH_CAN_HASH(node->type)) {
1170 HASHMAP_FOREACH(c, node->compare.children, i)
1171 bus_match_dump(c, level + 1);
1174 for (c = node->child; c; c = c->next)
1175 bus_match_dump(c, level + 1);
1178 enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) {
1179 bool found_driver = false;
1182 if (n_components <= 0)
1183 return BUS_MATCH_GENERIC;
1187 /* Checks whether the specified match can only match the
1188 * pseudo-service for local messages, which we detect by
1189 * sender, interface or path. If a match is not restricted to
1190 * local messages, then we check if it only matches on the
1193 for (i = 0; i < n_components; i++) {
1194 const struct bus_match_component *c = components + i;
1196 if (c->type == BUS_MATCH_SENDER) {
1197 if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1198 return BUS_MATCH_LOCAL;
1200 if (streq_ptr(c->value_str, "org.freedesktop.DBus"))
1201 found_driver = true;
1204 if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1205 return BUS_MATCH_LOCAL;
1207 if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local"))
1208 return BUS_MATCH_LOCAL;
1211 return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC;