1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "alloc-util.h"
23 #include "bus-internal.h"
24 #include "bus-match.h"
25 #include "bus-message.h"
29 #include "hexdecoct.h"
30 #include "string-util.h"
35 * A: type=signal,sender=foo,interface=bar
36 * B: type=signal,sender=quux,interface=fips
37 * C: type=signal,sender=quux,interface=waldo
38 * D: type=signal,member=test
43 * results in this tree:
46 * + BUS_MATCH_MESSAGE_TYPE
47 * | ` BUS_MATCH_VALUE: value == signal
48 * | + DBUS_MATCH_SENDER
49 * | | + BUS_MATCH_VALUE: value == foo
50 * | | | ` DBUS_MATCH_INTERFACE
51 * | | | ` BUS_MATCH_VALUE: value == bar
52 * | | | ` BUS_MATCH_LEAF: A
53 * | | ` BUS_MATCH_VALUE: value == quux
54 * | | ` DBUS_MATCH_INTERFACE
55 * | | | BUS_MATCH_VALUE: value == fips
56 * | | | ` BUS_MATCH_LEAF: B
57 * | | ` BUS_MATCH_VALUE: value == waldo
58 * | | ` BUS_MATCH_LEAF: C
59 * | + DBUS_MATCH_MEMBER
60 * | | ` BUS_MATCH_VALUE: value == test
61 * | | ` BUS_MATCH_LEAF: D
62 * | + BUS_MATCH_LEAF: F
63 * | ` BUS_MATCH_LEAF: G
65 * ` BUS_MATCH_VALUE: value == miau
69 static inline bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
70 return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_HAS_LAST;
73 static inline bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
74 return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
75 (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST) ||
76 (t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_LAST);
79 static void bus_match_node_free(struct bus_match_node *node) {
83 assert(node->type != BUS_MATCH_ROOT);
84 assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
86 if (node->parent->child) {
87 /* We are apparently linked into the parent's child
88 * list. Let's remove us from there. */
90 assert(node->prev->next == node);
91 node->prev->next = node->next;
93 assert(node->parent->child == node);
94 node->parent->child = node->next;
98 node->next->prev = node->prev;
101 if (node->type == BUS_MATCH_VALUE) {
102 /* We might be in the parent's hash table, so clean
105 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
106 hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
107 else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
108 hashmap_remove(node->parent->compare.children, node->value.str);
110 free(node->value.str);
113 if (BUS_MATCH_IS_COMPARE(node->type)) {
114 assert(hashmap_isempty(node->compare.children));
115 hashmap_free(node->compare.children);
121 static bool bus_match_node_maybe_free(struct bus_match_node *node) {
124 if (node->type == BUS_MATCH_ROOT)
130 if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
133 bus_match_node_free(node);
137 static bool value_node_test(
138 struct bus_match_node *node,
139 enum bus_match_node_type parent_type,
141 const char *value_str,
146 assert(node->type == BUS_MATCH_VALUE);
148 /* Tests parameters against this value node, doing prefix
149 * magic and stuff. */
151 switch (parent_type) {
153 case BUS_MATCH_MESSAGE_TYPE:
154 return node->value.u8 == value_u8;
156 case BUS_MATCH_SENDER:
157 if (streq_ptr(node->value.str, value_str))
160 if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
163 /* on kdbus we have the well known names list
164 * in the credentials, let's make use of that
165 * for an accurate match */
167 STRV_FOREACH(i, m->creds.well_known_names)
168 if (streq_ptr(node->value.str, *i))
173 /* If we don't have kdbus, we don't know the
174 * well-known names of the senders. In that,
175 * let's just hope that dbus-daemon doesn't
176 * send us stuff we didn't want. */
178 if (node->value.str[0] != ':' && value_str && value_str[0] == ':')
184 case BUS_MATCH_DESTINATION:
185 case BUS_MATCH_INTERFACE:
186 case BUS_MATCH_MEMBER:
188 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
191 return streq_ptr(node->value.str, value_str);
195 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: {
198 STRV_FOREACH(i, value_strv)
199 if (streq_ptr(node->value.str, *i))
205 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
207 return namespace_simple_pattern(node->value.str, value_str);
211 case BUS_MATCH_PATH_NAMESPACE:
212 return path_simple_pattern(node->value.str, value_str);
214 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
216 return path_complex_pattern(node->value.str, value_str);
221 assert_not_reached("Invalid node type");
225 static bool value_node_same(
226 struct bus_match_node *node,
227 enum bus_match_node_type parent_type,
229 const char *value_str) {
231 /* Tests parameters against this value node, not doing prefix
232 * magic and stuff, i.e. this one actually compares the match
236 assert(node->type == BUS_MATCH_VALUE);
238 switch (parent_type) {
240 case BUS_MATCH_MESSAGE_TYPE:
241 return node->value.u8 == value_u8;
243 case BUS_MATCH_SENDER:
244 case BUS_MATCH_DESTINATION:
245 case BUS_MATCH_INTERFACE:
246 case BUS_MATCH_MEMBER:
248 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
249 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
250 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
251 case BUS_MATCH_PATH_NAMESPACE:
252 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
253 return streq(node->value.str, value_str);
256 assert_not_reached("Invalid node type");
262 struct bus_match_node *node,
265 _cleanup_strv_free_ char **test_strv = NULL;
266 const char *test_str = NULL;
275 if (bus && bus->match_callbacks_modified)
278 /* Not these special semantics: when traversing the tree we
279 * usually let bus_match_run() when called for a node
280 * recursively invoke bus_match_run(). There's are two
281 * exceptions here though, which are BUS_NODE_ROOT (which
282 * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
283 * are invoked anyway by its parent. */
285 switch (node->type) {
289 /* Run all children. Since we cannot have any siblings
290 * we won't call any. The children of the root node
291 * are compares or leaves, they will automatically
292 * call their siblings. */
293 return bus_match_run(bus, node->child, m);
295 case BUS_MATCH_VALUE:
297 /* Run all children. We don't execute any siblings, we
298 * assume our caller does that. The children of value
299 * nodes are compares or leaves, they will
300 * automatically call their siblings */
303 return bus_match_run(bus, node->child, m);
308 if (node->leaf.callback->last_iteration == bus->iteration_counter)
311 node->leaf.callback->last_iteration = bus->iteration_counter;
314 r = sd_bus_message_rewind(m, true);
318 /* Run the callback. And then invoke siblings. */
319 if (node->leaf.callback->callback) {
320 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
323 slot = container_of(node->leaf.callback, sd_bus_slot, match_callback);
325 bus->current_slot = sd_bus_slot_ref(slot);
326 bus->current_handler = node->leaf.callback->callback;
327 bus->current_userdata = slot->userdata;
329 r = node->leaf.callback->callback(m, slot->userdata, &error_buffer);
331 bus->current_userdata = NULL;
332 bus->current_handler = NULL;
333 bus->current_slot = sd_bus_slot_unref(slot);
336 r = bus_maybe_reply_error(m, r, &error_buffer);
340 if (bus && bus->match_callbacks_modified)
344 return bus_match_run(bus, node->next, m);
346 case BUS_MATCH_MESSAGE_TYPE:
347 test_u8 = m->header->type;
350 case BUS_MATCH_SENDER:
351 test_str = m->sender;
352 /* FIXME: resolve test_str from a well-known to a unique name first */
355 case BUS_MATCH_DESTINATION:
356 test_str = m->destination;
359 case BUS_MATCH_INTERFACE:
360 test_str = m->interface;
363 case BUS_MATCH_MEMBER:
364 test_str = m->member;
368 case BUS_MATCH_PATH_NAMESPACE:
372 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
373 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str);
376 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
377 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str);
380 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
381 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str);
384 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
385 (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv);
389 assert_not_reached("Unknown match type.");
392 if (BUS_MATCH_CAN_HASH(node->type)) {
393 struct bus_match_node *found;
395 /* Lookup via hash table, nice! So let's jump directly. */
398 found = hashmap_get(node->compare.children, test_str);
399 else if (test_strv) {
402 STRV_FOREACH(i, test_strv) {
403 found = hashmap_get(node->compare.children, *i);
405 r = bus_match_run(bus, found, m);
412 } else if (node->type == BUS_MATCH_MESSAGE_TYPE)
413 found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
418 r = bus_match_run(bus, found, m);
423 struct bus_match_node *c;
425 /* No hash table, so let's iterate manually... */
427 for (c = node->child; c; c = c->next) {
428 if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m))
431 r = bus_match_run(bus, c, m);
437 if (bus && bus->match_callbacks_modified)
440 /* And now, let's invoke our siblings */
441 return bus_match_run(bus, node->next, m);
444 static int bus_match_add_compare_value(
445 struct bus_match_node *where,
446 enum bus_match_node_type t,
448 const char *value_str,
449 struct bus_match_node **ret) {
451 struct bus_match_node *c = NULL, *n = NULL;
455 assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
456 assert(BUS_MATCH_IS_COMPARE(t));
459 for (c = where->child; c && c->type != t; c = c->next)
463 /* Comparison node already exists? Then let's see if
464 * the value node exists too. */
466 if (t == BUS_MATCH_MESSAGE_TYPE)
467 n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
468 else if (BUS_MATCH_CAN_HASH(t))
469 n = hashmap_get(c->compare.children, value_str);
471 for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
480 /* Comparison node, doesn't exist yet? Then let's
483 c = new0(struct bus_match_node, 1);
491 c->next = where->child;
496 if (t == BUS_MATCH_MESSAGE_TYPE) {
497 c->compare.children = hashmap_new(NULL);
498 if (!c->compare.children) {
502 } else if (BUS_MATCH_CAN_HASH(t)) {
503 c->compare.children = hashmap_new(&string_hash_ops);
504 if (!c->compare.children) {
511 n = new0(struct bus_match_node, 1);
517 n->type = BUS_MATCH_VALUE;
518 n->value.u8 = value_u8;
520 n->value.str = strdup(value_str);
528 if (c->compare.children) {
530 if (t == BUS_MATCH_MESSAGE_TYPE)
531 r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
533 r = hashmap_put(c->compare.children, n->value.str, n);
549 bus_match_node_maybe_free(c);
559 static int bus_match_find_compare_value(
560 struct bus_match_node *where,
561 enum bus_match_node_type t,
563 const char *value_str,
564 struct bus_match_node **ret) {
566 struct bus_match_node *c, *n;
569 assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
570 assert(BUS_MATCH_IS_COMPARE(t));
573 for (c = where->child; c && c->type != t; c = c->next)
579 if (t == BUS_MATCH_MESSAGE_TYPE)
580 n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
581 else if (BUS_MATCH_CAN_HASH(t))
582 n = hashmap_get(c->compare.children, value_str);
584 for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
596 static int bus_match_add_leaf(
597 struct bus_match_node *where,
598 struct match_callback *callback) {
600 struct bus_match_node *n;
603 assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
606 n = new0(struct bus_match_node, 1);
610 n->type = BUS_MATCH_LEAF;
612 n->next = where->child;
616 n->leaf.callback = callback;
617 callback->match_node = n;
624 static int bus_match_find_leaf(
625 struct bus_match_node *where,
626 sd_bus_message_handler_t callback,
628 struct bus_match_node **ret) {
630 struct bus_match_node *c;
633 assert(where->type == BUS_MATCH_ROOT || where->type == BUS_MATCH_VALUE);
636 for (c = where->child; c; c = c->next) {
639 s = container_of(c->leaf.callback, sd_bus_slot, match_callback);
641 if (c->type == BUS_MATCH_LEAF &&
642 c->leaf.callback->callback == callback &&
643 s->userdata == userdata) {
652 enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
655 if (n == 4 && startswith(k, "type"))
656 return BUS_MATCH_MESSAGE_TYPE;
657 if (n == 6 && startswith(k, "sender"))
658 return BUS_MATCH_SENDER;
659 if (n == 11 && startswith(k, "destination"))
660 return BUS_MATCH_DESTINATION;
661 if (n == 9 && startswith(k, "interface"))
662 return BUS_MATCH_INTERFACE;
663 if (n == 6 && startswith(k, "member"))
664 return BUS_MATCH_MEMBER;
665 if (n == 4 && startswith(k, "path"))
666 return BUS_MATCH_PATH;
667 if (n == 14 && startswith(k, "path_namespace"))
668 return BUS_MATCH_PATH_NAMESPACE;
670 if (n == 4 && startswith(k, "arg")) {
677 return BUS_MATCH_ARG + j;
680 if (n == 5 && startswith(k, "arg")) {
682 enum bus_match_node_type t;
689 t = BUS_MATCH_ARG + a * 10 + b;
690 if (t > BUS_MATCH_ARG_LAST)
696 if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) {
703 return BUS_MATCH_ARG_PATH + j;
706 if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) {
707 enum bus_match_node_type t;
715 t = BUS_MATCH_ARG_PATH + a * 10 + b;
716 if (t > BUS_MATCH_ARG_PATH_LAST)
722 if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) {
729 return BUS_MATCH_ARG_NAMESPACE + j;
732 if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) {
733 enum bus_match_node_type t;
741 t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
742 if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
748 if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) {
755 return BUS_MATCH_ARG_HAS + j;
758 if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) {
759 enum bus_match_node_type t;
767 t = BUS_MATCH_ARG_HAS + a * 10 + b;
768 if (t > BUS_MATCH_ARG_HAS_LAST)
777 static int match_component_compare(const void *a, const void *b) {
778 const struct bus_match_component *x = a, *y = b;
780 if (x->type < y->type)
782 if (x->type > y->type)
788 void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
791 for (i = 0; i < n_components; i++)
792 free(components[i].value_str);
799 struct bus_match_component **_components,
800 unsigned *_n_components) {
802 const char *p = match;
803 struct bus_match_component *components = NULL;
804 size_t components_allocated = 0;
805 unsigned n_components = 0, i;
806 _cleanup_free_ char *value = NULL;
811 assert(_n_components);
815 enum bus_match_node_type t;
817 size_t value_allocated = 0;
818 bool escaped = false, quoted;
821 /* Avahi's match rules appear to include whitespace, skip over it */
828 t = bus_match_node_type_from_string(p, eq - p);
832 quoted = eq[1] == '\'';
834 for (q = eq + 1 + quoted;; q++) {
870 if (!GREEDY_REALLOC(value, value_allocated, j + 2)) {
887 if (t == BUS_MATCH_MESSAGE_TYPE) {
888 r = bus_message_type_from_string(value, &u);
892 value = mfree(value);
896 if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) {
901 components[n_components].type = t;
902 components[n_components].value_str = value;
903 components[n_components].value_u8 = u;
911 if (q[quoted] != ',') {
919 /* Order the whole thing, so that we always generate the same tree */
920 qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare);
922 /* Check for duplicates */
923 for (i = 0; i+1 < n_components; i++)
924 if (components[i].type == components[i+1].type) {
929 *_components = components;
930 *_n_components = n_components;
935 bus_match_parse_free(components, n_components);
939 /// UNNEEDED by elogind
941 char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
942 _cleanup_fclose_ FILE *f = NULL;
948 if (n_components <= 0)
953 f = open_memstream(&buffer, &size);
957 for (i = 0; i < n_components; i++) {
963 fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
967 if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
968 fputs(bus_message_type_to_string(components[i].value_u8), f);
970 fputs(components[i].value_str, f);
975 r = fflush_and_check(f);
984 struct bus_match_node *root,
985 struct bus_match_component *components,
986 unsigned n_components,
987 struct match_callback *callback) {
990 struct bus_match_node *n;
997 for (i = 0; i < n_components; i++) {
998 r = bus_match_add_compare_value(
999 n, components[i].type,
1000 components[i].value_u8, components[i].value_str, &n);
1005 return bus_match_add_leaf(n, callback);
1008 int bus_match_remove(
1009 struct bus_match_node *root,
1010 struct match_callback *callback) {
1012 struct bus_match_node *node, *pp;
1017 node = callback->match_node;
1021 assert(node->type == BUS_MATCH_LEAF);
1023 callback->match_node = NULL;
1027 bus_match_node_free(node);
1029 /* Prune the tree above */
1034 if (!bus_match_node_maybe_free(node))
1042 struct bus_match_node *root,
1043 struct bus_match_component *components,
1044 unsigned n_components,
1045 sd_bus_message_handler_t callback,
1047 struct match_callback **ret) {
1049 struct bus_match_node *n, **gc;
1056 gc = newa(struct bus_match_node*, n_components);
1059 for (i = 0; i < n_components; i++) {
1060 r = bus_match_find_compare_value(
1061 n, components[i].type,
1062 components[i].value_u8, components[i].value_str,
1070 r = bus_match_find_leaf(n, callback, userdata, &n);
1074 *ret = n->leaf.callback;
1078 void bus_match_free(struct bus_match_node *node) {
1079 struct bus_match_node *c;
1084 if (BUS_MATCH_CAN_HASH(node->type)) {
1087 HASHMAP_FOREACH(c, node->compare.children, i)
1090 assert(hashmap_isempty(node->compare.children));
1093 while ((c = node->child))
1096 if (node->type != BUS_MATCH_ROOT)
1097 bus_match_node_free(node);
1100 const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
1103 case BUS_MATCH_ROOT:
1106 case BUS_MATCH_VALUE:
1109 case BUS_MATCH_LEAF:
1112 case BUS_MATCH_MESSAGE_TYPE:
1115 case BUS_MATCH_SENDER:
1118 case BUS_MATCH_DESTINATION:
1119 return "destination";
1121 case BUS_MATCH_INTERFACE:
1124 case BUS_MATCH_MEMBER:
1127 case BUS_MATCH_PATH:
1130 case BUS_MATCH_PATH_NAMESPACE:
1131 return "path_namespace";
1133 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
1134 snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
1137 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
1138 snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
1141 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
1142 snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
1145 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
1146 snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
1154 void bus_match_dump(struct bus_match_node *node, unsigned level) {
1155 struct bus_match_node *c;
1156 _cleanup_free_ char *pfx = NULL;
1162 pfx = strrep(" ", level);
1163 printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
1165 if (node->type == BUS_MATCH_VALUE) {
1166 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
1167 printf(" <%u>\n", node->value.u8);
1169 printf(" <%s>\n", node->value.str);
1170 } else if (node->type == BUS_MATCH_ROOT)
1172 else if (node->type == BUS_MATCH_LEAF)
1173 printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
1177 if (BUS_MATCH_CAN_HASH(node->type)) {
1180 HASHMAP_FOREACH(c, node->compare.children, i)
1181 bus_match_dump(c, level + 1);
1184 for (c = node->child; c; c = c->next)
1185 bus_match_dump(c, level + 1);
1188 enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) {
1189 bool found_driver = false;
1192 if (n_components <= 0)
1193 return BUS_MATCH_GENERIC;
1197 /* Checks whether the specified match can only match the
1198 * pseudo-service for local messages, which we detect by
1199 * sender, interface or path. If a match is not restricted to
1200 * local messages, then we check if it only matches on the
1203 for (i = 0; i < n_components; i++) {
1204 const struct bus_match_component *c = components + i;
1206 if (c->type == BUS_MATCH_SENDER) {
1207 if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1208 return BUS_MATCH_LOCAL;
1210 if (streq_ptr(c->value_str, "org.freedesktop.DBus"))
1211 found_driver = true;
1214 if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1215 return BUS_MATCH_LOCAL;
1217 if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local"))
1218 return BUS_MATCH_LOCAL;
1221 return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC;