1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "alloc-util.h"
4 #include "bus-internal.h"
6 #include "bus-message.h"
10 #include "hexdecoct.h"
11 #include "sort-util.h"
12 #include "string-util.h"
17 * A: type=signal,sender=foo,interface=bar
18 * B: type=signal,sender=quux,interface=fips
19 * C: type=signal,sender=quux,interface=waldo
20 * D: type=signal,member=test
25 * results in this tree:
28 * + BUS_MATCH_MESSAGE_TYPE
29 * | ` BUS_MATCH_VALUE: value == signal
30 * | + DBUS_MATCH_SENDER
31 * | | + BUS_MATCH_VALUE: value == foo
32 * | | | ` DBUS_MATCH_INTERFACE
33 * | | | ` BUS_MATCH_VALUE: value == bar
34 * | | | ` BUS_MATCH_LEAF: A
35 * | | ` BUS_MATCH_VALUE: value == quux
36 * | | ` DBUS_MATCH_INTERFACE
37 * | | | BUS_MATCH_VALUE: value == fips
38 * | | | ` BUS_MATCH_LEAF: B
39 * | | ` BUS_MATCH_VALUE: value == waldo
40 * | | ` BUS_MATCH_LEAF: C
41 * | + DBUS_MATCH_MEMBER
42 * | | ` BUS_MATCH_VALUE: value == test
43 * | | ` BUS_MATCH_LEAF: D
44 * | + BUS_MATCH_LEAF: F
45 * | ` BUS_MATCH_LEAF: G
47 * ` BUS_MATCH_VALUE: value == miau
51 static bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t
) {
52 return t
>= BUS_MATCH_SENDER
&& t
<= BUS_MATCH_ARG_HAS_LAST
;
55 static bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t
) {
56 return (t
>= BUS_MATCH_MESSAGE_TYPE
&& t
<= BUS_MATCH_PATH
) ||
57 (t
>= BUS_MATCH_ARG
&& t
<= BUS_MATCH_ARG_LAST
) ||
58 (t
>= BUS_MATCH_ARG_HAS
&& t
<= BUS_MATCH_ARG_HAS_LAST
);
61 static void bus_match_node_free(struct bus_match_node
*node
) {
65 assert(node
->type
!= BUS_MATCH_ROOT
);
66 assert(node
->type
< _BUS_MATCH_NODE_TYPE_MAX
);
68 if (node
->parent
->child
) {
69 /* We are apparently linked into the parent's child
70 * list. Let's remove us from there. */
72 assert(node
->prev
->next
== node
);
73 node
->prev
->next
= node
->next
;
75 assert(node
->parent
->child
== node
);
76 node
->parent
->child
= node
->next
;
80 node
->next
->prev
= node
->prev
;
83 if (node
->type
== BUS_MATCH_VALUE
) {
84 /* We might be in the parent's hash table, so clean
87 if (node
->parent
->type
== BUS_MATCH_MESSAGE_TYPE
)
88 hashmap_remove(node
->parent
->compare
.children
, UINT_TO_PTR(node
->value
.u8
));
89 else if (BUS_MATCH_CAN_HASH(node
->parent
->type
) && node
->value
.str
)
90 hashmap_remove(node
->parent
->compare
.children
, node
->value
.str
);
92 free(node
->value
.str
);
95 if (BUS_MATCH_IS_COMPARE(node
->type
)) {
96 assert(hashmap_isempty(node
->compare
.children
));
97 hashmap_free(node
->compare
.children
);
103 static bool bus_match_node_maybe_free(struct bus_match_node
*node
) {
106 if (node
->type
== BUS_MATCH_ROOT
)
112 if (BUS_MATCH_IS_COMPARE(node
->type
) && !hashmap_isempty(node
->compare
.children
))
115 bus_match_node_free(node
);
119 static bool value_node_test(
120 struct bus_match_node
*node
,
121 enum bus_match_node_type parent_type
,
123 const char *value_str
,
128 assert(node
->type
== BUS_MATCH_VALUE
);
130 /* Tests parameters against this value node, doing prefix
131 * magic and stuff. */
133 switch (parent_type
) {
135 case BUS_MATCH_MESSAGE_TYPE
:
136 return node
->value
.u8
== value_u8
;
138 case BUS_MATCH_SENDER
:
139 if (streq_ptr(node
->value
.str
, value_str
))
142 if (m
->creds
.mask
& SD_BUS_CREDS_WELL_KNOWN_NAMES
) {
145 /* on kdbus we have the well known names list
146 * in the credentials, let's make use of that
147 * for an accurate match */
149 STRV_FOREACH(i
, m
->creds
.well_known_names
)
150 if (streq_ptr(node
->value
.str
, *i
))
155 /* If we don't have kdbus, we don't know the
156 * well-known names of the senders. In that,
157 * let's just hope that dbus-daemon doesn't
158 * send us stuff we didn't want. */
160 if (node
->value
.str
[0] != ':' && value_str
&& value_str
[0] == ':')
166 case BUS_MATCH_DESTINATION
:
167 case BUS_MATCH_INTERFACE
:
168 case BUS_MATCH_MEMBER
:
170 case BUS_MATCH_ARG
... BUS_MATCH_ARG_LAST
:
173 return streq_ptr(node
->value
.str
, value_str
);
177 case BUS_MATCH_ARG_HAS
... BUS_MATCH_ARG_HAS_LAST
: {
180 STRV_FOREACH(i
, value_strv
)
181 if (streq_ptr(node
->value
.str
, *i
))
187 case BUS_MATCH_ARG_NAMESPACE
... BUS_MATCH_ARG_NAMESPACE_LAST
:
189 return namespace_simple_pattern(node
->value
.str
, value_str
);
193 case BUS_MATCH_PATH_NAMESPACE
:
194 return path_simple_pattern(node
->value
.str
, value_str
);
196 case BUS_MATCH_ARG_PATH
... BUS_MATCH_ARG_PATH_LAST
:
198 return path_complex_pattern(node
->value
.str
, value_str
);
203 assert_not_reached("Invalid node type");
207 static bool value_node_same(
208 struct bus_match_node
*node
,
209 enum bus_match_node_type parent_type
,
211 const char *value_str
) {
213 /* Tests parameters against this value node, not doing prefix
214 * magic and stuff, i.e. this one actually compares the match
218 assert(node
->type
== BUS_MATCH_VALUE
);
220 switch (parent_type
) {
222 case BUS_MATCH_MESSAGE_TYPE
:
223 return node
->value
.u8
== value_u8
;
225 case BUS_MATCH_SENDER
:
226 case BUS_MATCH_DESTINATION
:
227 case BUS_MATCH_INTERFACE
:
228 case BUS_MATCH_MEMBER
:
230 case BUS_MATCH_ARG
... BUS_MATCH_ARG_LAST
:
231 case BUS_MATCH_ARG_HAS
... BUS_MATCH_ARG_HAS_LAST
:
232 case BUS_MATCH_ARG_NAMESPACE
... BUS_MATCH_ARG_NAMESPACE_LAST
:
233 case BUS_MATCH_PATH_NAMESPACE
:
234 case BUS_MATCH_ARG_PATH
... BUS_MATCH_ARG_PATH_LAST
:
235 return streq(node
->value
.str
, value_str
);
238 assert_not_reached("Invalid node type");
244 struct bus_match_node
*node
,
247 _cleanup_strv_free_
char **test_strv
= NULL
;
248 const char *test_str
= NULL
;
257 if (bus
&& bus
->match_callbacks_modified
)
260 /* Not these special semantics: when traversing the tree we
261 * usually let bus_match_run() when called for a node
262 * recursively invoke bus_match_run(). There's are two
263 * exceptions here though, which are BUS_NODE_ROOT (which
264 * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
265 * are invoked anyway by its parent. */
267 switch (node
->type
) {
271 /* Run all children. Since we cannot have any siblings
272 * we won't call any. The children of the root node
273 * are compares or leaves, they will automatically
274 * call their siblings. */
275 return bus_match_run(bus
, node
->child
, m
);
277 case BUS_MATCH_VALUE
:
279 /* Run all children. We don't execute any siblings, we
280 * assume our caller does that. The children of value
281 * nodes are compares or leaves, they will
282 * automatically call their siblings */
285 return bus_match_run(bus
, node
->child
, m
);
290 /* Don't run this match as long as the AddMatch() call is not complete yet.
292 * Don't run this match unless the 'after' counter has been reached.
294 * Don't run this match more than once per iteration */
296 if (node
->leaf
.callback
->install_slot
||
297 m
->read_counter
<= node
->leaf
.callback
->after
||
298 node
->leaf
.callback
->last_iteration
== bus
->iteration_counter
)
299 return bus_match_run(bus
, node
->next
, m
);
301 node
->leaf
.callback
->last_iteration
= bus
->iteration_counter
;
304 r
= sd_bus_message_rewind(m
, true);
308 /* Run the callback. And then invoke siblings. */
309 if (node
->leaf
.callback
->callback
) {
310 _cleanup_(sd_bus_error_free
) sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
313 slot
= container_of(node
->leaf
.callback
, sd_bus_slot
, match_callback
);
315 bus
->current_slot
= sd_bus_slot_ref(slot
);
316 bus
->current_handler
= node
->leaf
.callback
->callback
;
317 bus
->current_userdata
= slot
->userdata
;
319 r
= node
->leaf
.callback
->callback(m
, slot
->userdata
, &error_buffer
);
321 bus
->current_userdata
= NULL
;
322 bus
->current_handler
= NULL
;
323 bus
->current_slot
= sd_bus_slot_unref(slot
);
326 r
= bus_maybe_reply_error(m
, r
, &error_buffer
);
330 if (bus
&& bus
->match_callbacks_modified
)
334 return bus_match_run(bus
, node
->next
, m
);
336 case BUS_MATCH_MESSAGE_TYPE
:
337 test_u8
= m
->header
->type
;
340 case BUS_MATCH_SENDER
:
341 test_str
= m
->sender
;
342 /* FIXME: resolve test_str from a well-known to a unique name first */
345 case BUS_MATCH_DESTINATION
:
346 test_str
= m
->destination
;
349 case BUS_MATCH_INTERFACE
:
350 test_str
= m
->interface
;
353 case BUS_MATCH_MEMBER
:
354 test_str
= m
->member
;
358 case BUS_MATCH_PATH_NAMESPACE
:
362 case BUS_MATCH_ARG
... BUS_MATCH_ARG_LAST
:
363 (void) bus_message_get_arg(m
, node
->type
- BUS_MATCH_ARG
, &test_str
);
366 case BUS_MATCH_ARG_PATH
... BUS_MATCH_ARG_PATH_LAST
:
367 (void) bus_message_get_arg(m
, node
->type
- BUS_MATCH_ARG_PATH
, &test_str
);
370 case BUS_MATCH_ARG_NAMESPACE
... BUS_MATCH_ARG_NAMESPACE_LAST
:
371 (void) bus_message_get_arg(m
, node
->type
- BUS_MATCH_ARG_NAMESPACE
, &test_str
);
374 case BUS_MATCH_ARG_HAS
... BUS_MATCH_ARG_HAS_LAST
:
375 (void) bus_message_get_arg_strv(m
, node
->type
- BUS_MATCH_ARG_HAS
, &test_strv
);
379 assert_not_reached("Unknown match type.");
382 if (BUS_MATCH_CAN_HASH(node
->type
)) {
383 struct bus_match_node
*found
;
385 /* Lookup via hash table, nice! So let's jump directly. */
388 found
= hashmap_get(node
->compare
.children
, test_str
);
389 else if (test_strv
) {
392 STRV_FOREACH(i
, test_strv
) {
393 found
= hashmap_get(node
->compare
.children
, *i
);
395 r
= bus_match_run(bus
, found
, m
);
402 } else if (node
->type
== BUS_MATCH_MESSAGE_TYPE
)
403 found
= hashmap_get(node
->compare
.children
, UINT_TO_PTR(test_u8
));
408 r
= bus_match_run(bus
, found
, m
);
413 struct bus_match_node
*c
;
415 /* No hash table, so let's iterate manually... */
417 for (c
= node
->child
; c
; c
= c
->next
) {
418 if (!value_node_test(c
, node
->type
, test_u8
, test_str
, test_strv
, m
))
421 r
= bus_match_run(bus
, c
, m
);
425 if (bus
&& bus
->match_callbacks_modified
)
430 if (bus
&& bus
->match_callbacks_modified
)
433 /* And now, let's invoke our siblings */
434 return bus_match_run(bus
, node
->next
, m
);
437 static int bus_match_add_compare_value(
438 struct bus_match_node
*where
,
439 enum bus_match_node_type t
,
441 const char *value_str
,
442 struct bus_match_node
**ret
) {
444 struct bus_match_node
*c
= NULL
, *n
= NULL
;
448 assert(IN_SET(where
->type
, BUS_MATCH_ROOT
, BUS_MATCH_VALUE
));
449 assert(BUS_MATCH_IS_COMPARE(t
));
452 for (c
= where
->child
; c
&& c
->type
!= t
; c
= c
->next
)
456 /* Comparison node already exists? Then let's see if
457 * the value node exists too. */
459 if (t
== BUS_MATCH_MESSAGE_TYPE
)
460 n
= hashmap_get(c
->compare
.children
, UINT_TO_PTR(value_u8
));
461 else if (BUS_MATCH_CAN_HASH(t
))
462 n
= hashmap_get(c
->compare
.children
, value_str
);
464 for (n
= c
->child
; n
&& !value_node_same(n
, t
, value_u8
, value_str
); n
= n
->next
)
473 /* Comparison node, doesn't exist yet? Then let's
476 c
= new0(struct bus_match_node
, 1);
484 c
->next
= where
->child
;
489 if (t
== BUS_MATCH_MESSAGE_TYPE
) {
490 c
->compare
.children
= hashmap_new(NULL
);
491 if (!c
->compare
.children
) {
495 } else if (BUS_MATCH_CAN_HASH(t
)) {
496 c
->compare
.children
= hashmap_new(&string_hash_ops
);
497 if (!c
->compare
.children
) {
504 n
= new0(struct bus_match_node
, 1);
510 n
->type
= BUS_MATCH_VALUE
;
511 n
->value
.u8
= value_u8
;
513 n
->value
.str
= strdup(value_str
);
521 if (c
->compare
.children
) {
523 if (t
== BUS_MATCH_MESSAGE_TYPE
)
524 r
= hashmap_put(c
->compare
.children
, UINT_TO_PTR(value_u8
), n
);
526 r
= hashmap_put(c
->compare
.children
, n
->value
.str
, n
);
542 bus_match_node_maybe_free(c
);
552 static int bus_match_add_leaf(
553 struct bus_match_node
*where
,
554 struct match_callback
*callback
) {
556 struct bus_match_node
*n
;
559 assert(IN_SET(where
->type
, BUS_MATCH_ROOT
, BUS_MATCH_VALUE
));
562 n
= new0(struct bus_match_node
, 1);
566 n
->type
= BUS_MATCH_LEAF
;
568 n
->next
= where
->child
;
572 n
->leaf
.callback
= callback
;
573 callback
->match_node
= n
;
580 enum bus_match_node_type
bus_match_node_type_from_string(const char *k
, size_t n
) {
583 if (n
== 4 && startswith(k
, "type"))
584 return BUS_MATCH_MESSAGE_TYPE
;
585 if (n
== 6 && startswith(k
, "sender"))
586 return BUS_MATCH_SENDER
;
587 if (n
== 11 && startswith(k
, "destination"))
588 return BUS_MATCH_DESTINATION
;
589 if (n
== 9 && startswith(k
, "interface"))
590 return BUS_MATCH_INTERFACE
;
591 if (n
== 6 && startswith(k
, "member"))
592 return BUS_MATCH_MEMBER
;
593 if (n
== 4 && startswith(k
, "path"))
594 return BUS_MATCH_PATH
;
595 if (n
== 14 && startswith(k
, "path_namespace"))
596 return BUS_MATCH_PATH_NAMESPACE
;
598 if (n
== 4 && startswith(k
, "arg")) {
605 return BUS_MATCH_ARG
+ j
;
608 if (n
== 5 && startswith(k
, "arg")) {
610 enum bus_match_node_type t
;
617 t
= BUS_MATCH_ARG
+ a
* 10 + b
;
618 if (t
> BUS_MATCH_ARG_LAST
)
624 if (n
== 8 && startswith(k
, "arg") && startswith(k
+ 4, "path")) {
631 return BUS_MATCH_ARG_PATH
+ j
;
634 if (n
== 9 && startswith(k
, "arg") && startswith(k
+ 5, "path")) {
635 enum bus_match_node_type t
;
643 t
= BUS_MATCH_ARG_PATH
+ a
* 10 + b
;
644 if (t
> BUS_MATCH_ARG_PATH_LAST
)
650 if (n
== 13 && startswith(k
, "arg") && startswith(k
+ 4, "namespace")) {
657 return BUS_MATCH_ARG_NAMESPACE
+ j
;
660 if (n
== 14 && startswith(k
, "arg") && startswith(k
+ 5, "namespace")) {
661 enum bus_match_node_type t
;
669 t
= BUS_MATCH_ARG_NAMESPACE
+ a
* 10 + b
;
670 if (t
> BUS_MATCH_ARG_NAMESPACE_LAST
)
676 if (n
== 7 && startswith(k
, "arg") && startswith(k
+ 4, "has")) {
683 return BUS_MATCH_ARG_HAS
+ j
;
686 if (n
== 8 && startswith(k
, "arg") && startswith(k
+ 5, "has")) {
687 enum bus_match_node_type t
;
695 t
= BUS_MATCH_ARG_HAS
+ a
* 10 + b
;
696 if (t
> BUS_MATCH_ARG_HAS_LAST
)
705 static int match_component_compare(const struct bus_match_component
*a
, const struct bus_match_component
*b
) {
706 return CMP(a
->type
, b
->type
);
709 void bus_match_parse_free(struct bus_match_component
*components
, unsigned n_components
) {
712 for (i
= 0; i
< n_components
; i
++)
713 free(components
[i
].value_str
);
720 struct bus_match_component
**_components
,
721 unsigned *_n_components
) {
723 const char *p
= match
;
724 struct bus_match_component
*components
= NULL
;
725 size_t components_allocated
= 0;
726 unsigned n_components
= 0, i
;
727 _cleanup_free_
char *value
= NULL
;
732 assert(_n_components
);
736 enum bus_match_node_type t
;
738 size_t value_allocated
= 0;
739 bool escaped
= false, quoted
;
742 /* Avahi's match rules appear to include whitespace, skip over it */
749 t
= bus_match_node_type_from_string(p
, eq
- p
);
753 quoted
= eq
[1] == '\'';
755 for (q
= eq
+ 1 + quoted
;; q
++) {
791 if (!GREEDY_REALLOC(value
, value_allocated
, j
+ 2)) {
808 if (t
== BUS_MATCH_MESSAGE_TYPE
) {
809 r
= bus_message_type_from_string(value
, &u
);
813 value
= mfree(value
);
817 if (!GREEDY_REALLOC(components
, components_allocated
, n_components
+ 1)) {
822 components
[n_components
].type
= t
;
823 components
[n_components
].value_str
= TAKE_PTR(value
);
824 components
[n_components
].value_u8
= u
;
830 if (q
[quoted
] != ',') {
838 /* Order the whole thing, so that we always generate the same tree */
839 typesafe_qsort(components
, n_components
, match_component_compare
);
841 /* Check for duplicates */
842 for (i
= 0; i
+1 < n_components
; i
++)
843 if (components
[i
].type
== components
[i
+1].type
) {
848 *_components
= components
;
849 *_n_components
= n_components
;
854 bus_match_parse_free(components
, n_components
);
858 char *bus_match_to_string(struct bus_match_component
*components
, unsigned n_components
) {
859 _cleanup_fclose_
FILE *f
= NULL
;
865 if (n_components
<= 0)
870 f
= open_memstream_unlocked(&buffer
, &size
);
874 for (i
= 0; i
< n_components
; i
++) {
880 fputs(bus_match_node_type_to_string(components
[i
].type
, buf
, sizeof(buf
)), f
);
884 if (components
[i
].type
== BUS_MATCH_MESSAGE_TYPE
)
885 fputs(bus_message_type_to_string(components
[i
].value_u8
), f
);
887 fputs(components
[i
].value_str
, f
);
892 r
= fflush_and_check(f
);
900 struct bus_match_node
*root
,
901 struct bus_match_component
*components
,
902 unsigned n_components
,
903 struct match_callback
*callback
) {
906 struct bus_match_node
*n
;
913 for (i
= 0; i
< n_components
; i
++) {
914 r
= bus_match_add_compare_value(
915 n
, components
[i
].type
,
916 components
[i
].value_u8
, components
[i
].value_str
, &n
);
921 return bus_match_add_leaf(n
, callback
);
924 int bus_match_remove(
925 struct bus_match_node
*root
,
926 struct match_callback
*callback
) {
928 struct bus_match_node
*node
, *pp
;
933 node
= callback
->match_node
;
937 assert(node
->type
== BUS_MATCH_LEAF
);
939 callback
->match_node
= NULL
;
943 bus_match_node_free(node
);
945 /* Prune the tree above */
950 if (!bus_match_node_maybe_free(node
))
957 void bus_match_free(struct bus_match_node
*node
) {
958 struct bus_match_node
*c
;
963 if (BUS_MATCH_CAN_HASH(node
->type
)) {
965 HASHMAP_FOREACH(c
, node
->compare
.children
)
968 assert(hashmap_isempty(node
->compare
.children
));
971 while ((c
= node
->child
))
974 if (node
->type
!= BUS_MATCH_ROOT
)
975 bus_match_node_free(node
);
978 const char* bus_match_node_type_to_string(enum bus_match_node_type t
, char buf
[], size_t l
) {
984 case BUS_MATCH_VALUE
:
990 case BUS_MATCH_MESSAGE_TYPE
:
993 case BUS_MATCH_SENDER
:
996 case BUS_MATCH_DESTINATION
:
997 return "destination";
999 case BUS_MATCH_INTERFACE
:
1002 case BUS_MATCH_MEMBER
:
1005 case BUS_MATCH_PATH
:
1008 case BUS_MATCH_PATH_NAMESPACE
:
1009 return "path_namespace";
1011 case BUS_MATCH_ARG
... BUS_MATCH_ARG_LAST
:
1012 snprintf(buf
, l
, "arg%i", t
- BUS_MATCH_ARG
);
1015 case BUS_MATCH_ARG_PATH
... BUS_MATCH_ARG_PATH_LAST
:
1016 snprintf(buf
, l
, "arg%ipath", t
- BUS_MATCH_ARG_PATH
);
1019 case BUS_MATCH_ARG_NAMESPACE
... BUS_MATCH_ARG_NAMESPACE_LAST
:
1020 snprintf(buf
, l
, "arg%inamespace", t
- BUS_MATCH_ARG_NAMESPACE
);
1023 case BUS_MATCH_ARG_HAS
... BUS_MATCH_ARG_HAS_LAST
:
1024 snprintf(buf
, l
, "arg%ihas", t
- BUS_MATCH_ARG_HAS
);
1032 void bus_match_dump(struct bus_match_node
*node
, unsigned level
) {
1033 struct bus_match_node
*c
;
1034 _cleanup_free_
char *pfx
= NULL
;
1040 pfx
= strrep(" ", level
);
1041 printf("%s[%s]", strempty(pfx
), bus_match_node_type_to_string(node
->type
, buf
, sizeof(buf
)));
1043 if (node
->type
== BUS_MATCH_VALUE
) {
1044 if (node
->parent
->type
== BUS_MATCH_MESSAGE_TYPE
)
1045 printf(" <%u>\n", node
->value
.u8
);
1047 printf(" <%s>\n", node
->value
.str
);
1048 } else if (node
->type
== BUS_MATCH_ROOT
)
1050 else if (node
->type
== BUS_MATCH_LEAF
)
1051 printf(" %p/%p\n", node
->leaf
.callback
->callback
, container_of(node
->leaf
.callback
, sd_bus_slot
, match_callback
)->userdata
);
1055 if (BUS_MATCH_CAN_HASH(node
->type
)) {
1057 HASHMAP_FOREACH(c
, node
->compare
.children
)
1058 bus_match_dump(c
, level
+ 1);
1061 for (c
= node
->child
; c
; c
= c
->next
)
1062 bus_match_dump(c
, level
+ 1);
1065 enum bus_match_scope
bus_match_get_scope(const struct bus_match_component
*components
, unsigned n_components
) {
1066 bool found_driver
= false;
1069 if (n_components
<= 0)
1070 return BUS_MATCH_GENERIC
;
1074 /* Checks whether the specified match can only match the
1075 * pseudo-service for local messages, which we detect by
1076 * sender, interface or path. If a match is not restricted to
1077 * local messages, then we check if it only matches on the
1080 for (i
= 0; i
< n_components
; i
++) {
1081 const struct bus_match_component
*c
= components
+ i
;
1083 if (c
->type
== BUS_MATCH_SENDER
) {
1084 if (streq_ptr(c
->value_str
, "org.freedesktop.DBus.Local"))
1085 return BUS_MATCH_LOCAL
;
1087 if (streq_ptr(c
->value_str
, "org.freedesktop.DBus"))
1088 found_driver
= true;
1091 if (c
->type
== BUS_MATCH_INTERFACE
&& streq_ptr(c
->value_str
, "org.freedesktop.DBus.Local"))
1092 return BUS_MATCH_LOCAL
;
1094 if (c
->type
== BUS_MATCH_PATH
&& streq_ptr(c
->value_str
, "/org/freedesktop/DBus/Local"))
1095 return BUS_MATCH_LOCAL
;
1098 return found_driver
? BUS_MATCH_DRIVER
: BUS_MATCH_GENERIC
;