]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-match.c
Merge pull request #20303 from andir/sysconfig-example
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-match.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
392d5b37 2
b5efdb8a 3#include "alloc-util.h"
392d5b37 4#include "bus-internal.h"
3ffd4af2 5#include "bus-match.h"
392d5b37 6#include "bus-message.h"
3ffd4af2 7#include "fd-util.h"
0d39fa9c 8#include "fileio.h"
e4e73a63 9#include "hexdecoct.h"
760877e9 10#include "sort-util.h"
07630cea 11#include "string-util.h"
06100c7a 12#include "strv.h"
392d5b37
LP
13
14/* Example:
15 *
16 * A: type=signal,sender=foo,interface=bar
17 * B: type=signal,sender=quux,interface=fips
18 * C: type=signal,sender=quux,interface=waldo
19 * D: type=signal,member=test
20 * E: sender=miau
21 * F: type=signal
22 * G: type=signal
23 *
24 * results in this tree:
25 *
26 * BUS_MATCH_ROOT
27 * + BUS_MATCH_MESSAGE_TYPE
28 * | ` BUS_MATCH_VALUE: value == signal
29 * | + DBUS_MATCH_SENDER
30 * | | + BUS_MATCH_VALUE: value == foo
31 * | | | ` DBUS_MATCH_INTERFACE
32 * | | | ` BUS_MATCH_VALUE: value == bar
33 * | | | ` BUS_MATCH_LEAF: A
34 * | | ` BUS_MATCH_VALUE: value == quux
35 * | | ` DBUS_MATCH_INTERFACE
36 * | | | BUS_MATCH_VALUE: value == fips
37 * | | | ` BUS_MATCH_LEAF: B
38 * | | ` BUS_MATCH_VALUE: value == waldo
39 * | | ` BUS_MATCH_LEAF: C
40 * | + DBUS_MATCH_MEMBER
41 * | | ` BUS_MATCH_VALUE: value == test
42 * | | ` BUS_MATCH_LEAF: D
43 * | + BUS_MATCH_LEAF: F
44 * | ` BUS_MATCH_LEAF: G
45 * ` BUS_MATCH_SENDER
46 * ` BUS_MATCH_VALUE: value == miau
47 * ` BUS_MATCH_LEAF: E
48 */
49
a1e92eee 50static bool BUS_MATCH_IS_COMPARE(enum bus_match_node_type t) {
eccd47c5 51 return t >= BUS_MATCH_SENDER && t <= BUS_MATCH_ARG_HAS_LAST;
392d5b37
LP
52}
53
a1e92eee 54static bool BUS_MATCH_CAN_HASH(enum bus_match_node_type t) {
392d5b37 55 return (t >= BUS_MATCH_MESSAGE_TYPE && t <= BUS_MATCH_PATH) ||
eccd47c5
LP
56 (t >= BUS_MATCH_ARG && t <= BUS_MATCH_ARG_LAST) ||
57 (t >= BUS_MATCH_ARG_HAS && t <= BUS_MATCH_ARG_HAS_LAST);
392d5b37
LP
58}
59
60static void bus_match_node_free(struct bus_match_node *node) {
61 assert(node);
62 assert(node->parent);
63 assert(!node->child);
64 assert(node->type != BUS_MATCH_ROOT);
65 assert(node->type < _BUS_MATCH_NODE_TYPE_MAX);
66
67 if (node->parent->child) {
68 /* We are apparently linked into the parent's child
69 * list. Let's remove us from there. */
70 if (node->prev) {
71 assert(node->prev->next == node);
72 node->prev->next = node->next;
73 } else {
74 assert(node->parent->child == node);
75 node->parent->child = node->next;
76 }
77
78 if (node->next)
79 node->next->prev = node->prev;
80 }
81
82 if (node->type == BUS_MATCH_VALUE) {
83 /* We might be in the parent's hash table, so clean
84 * this up */
85
86 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
87 hashmap_remove(node->parent->compare.children, UINT_TO_PTR(node->value.u8));
88 else if (BUS_MATCH_CAN_HASH(node->parent->type) && node->value.str)
89 hashmap_remove(node->parent->compare.children, node->value.str);
90
91 free(node->value.str);
92 }
93
94 if (BUS_MATCH_IS_COMPARE(node->type)) {
95 assert(hashmap_isempty(node->compare.children));
96 hashmap_free(node->compare.children);
97 }
98
99 free(node);
100}
101
102static bool bus_match_node_maybe_free(struct bus_match_node *node) {
103 assert(node);
104
19befb2d
LP
105 if (node->type == BUS_MATCH_ROOT)
106 return false;
107
392d5b37
LP
108 if (node->child)
109 return false;
110
111 if (BUS_MATCH_IS_COMPARE(node->type) && !hashmap_isempty(node->compare.children))
112 return true;
113
114 bus_match_node_free(node);
115 return true;
116}
117
118static bool value_node_test(
119 struct bus_match_node *node,
120 enum bus_match_node_type parent_type,
121 uint8_t value_u8,
06100c7a 122 const char *value_str,
198b158f 123 char **value_strv,
06100c7a 124 sd_bus_message *m) {
392d5b37
LP
125
126 assert(node);
127 assert(node->type == BUS_MATCH_VALUE);
128
129 /* Tests parameters against this value node, doing prefix
130 * magic and stuff. */
131
132 switch (parent_type) {
133
134 case BUS_MATCH_MESSAGE_TYPE:
135 return node->value.u8 == value_u8;
136
137 case BUS_MATCH_SENDER:
ae7bed3f
LP
138 if (streq_ptr(node->value.str, value_str))
139 return true;
140
06100c7a
LP
141 if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
142 char **i;
ae7bed3f 143
06100c7a
LP
144 /* on kdbus we have the well known names list
145 * in the credentials, let's make use of that
146 * for an accurate match */
147
148 STRV_FOREACH(i, m->creds.well_known_names)
149 if (streq_ptr(node->value.str, *i))
150 return true;
151
152 } else {
153
154 /* If we don't have kdbus, we don't know the
155 * well-known names of the senders. In that,
156 * let's just hope that dbus-daemon doesn't
157 * send us stuff we didn't want. */
158
159 if (node->value.str[0] != ':' && value_str && value_str[0] == ':')
160 return true;
161 }
ae7bed3f
LP
162
163 return false;
164
06100c7a 165 case BUS_MATCH_DESTINATION:
392d5b37
LP
166 case BUS_MATCH_INTERFACE:
167 case BUS_MATCH_MEMBER:
168 case BUS_MATCH_PATH:
eccd47c5 169 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
392d5b37 170
198b158f
LP
171 if (value_str)
172 return streq_ptr(node->value.str, value_str);
173
eccd47c5
LP
174 return false;
175
176 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: {
177 char **i;
178
198b158f
LP
179 STRV_FOREACH(i, value_strv)
180 if (streq_ptr(node->value.str, *i))
181 return true;
182
183 return false;
184 }
185
eccd47c5 186 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
198b158f
LP
187 if (value_str)
188 return namespace_simple_pattern(node->value.str, value_str);
189
198b158f 190 return false;
392d5b37
LP
191
192 case BUS_MATCH_PATH_NAMESPACE:
193 return path_simple_pattern(node->value.str, value_str);
194
eccd47c5 195 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
198b158f
LP
196 if (value_str)
197 return path_complex_pattern(node->value.str, value_str);
198
198b158f 199 return false;
392d5b37
LP
200
201 default:
04499a70 202 assert_not_reached();
392d5b37
LP
203 }
204}
205
206static bool value_node_same(
207 struct bus_match_node *node,
208 enum bus_match_node_type parent_type,
209 uint8_t value_u8,
210 const char *value_str) {
211
212 /* Tests parameters against this value node, not doing prefix
213 * magic and stuff, i.e. this one actually compares the match
ad67ef27 214 * itself. */
392d5b37
LP
215
216 assert(node);
217 assert(node->type == BUS_MATCH_VALUE);
218
219 switch (parent_type) {
220
221 case BUS_MATCH_MESSAGE_TYPE:
222 return node->value.u8 == value_u8;
223
224 case BUS_MATCH_SENDER:
225 case BUS_MATCH_DESTINATION:
226 case BUS_MATCH_INTERFACE:
227 case BUS_MATCH_MEMBER:
228 case BUS_MATCH_PATH:
229 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
eccd47c5 230 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
392d5b37
LP
231 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
232 case BUS_MATCH_PATH_NAMESPACE:
233 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
234 return streq(node->value.str, value_str);
235
236 default:
04499a70 237 assert_not_reached();
392d5b37
LP
238 }
239}
240
241int bus_match_run(
242 sd_bus *bus,
243 struct bus_match_node *node,
392d5b37
LP
244 sd_bus_message *m) {
245
198b158f 246 _cleanup_strv_free_ char **test_strv = NULL;
392d5b37
LP
247 const char *test_str = NULL;
248 uint8_t test_u8 = 0;
249 int r;
250
251 assert(m);
252
253 if (!node)
254 return 0;
255
7286037f
LP
256 if (bus && bus->match_callbacks_modified)
257 return 0;
258
392d5b37
LP
259 /* Not these special semantics: when traversing the tree we
260 * usually let bus_match_run() when called for a node
261 * recursively invoke bus_match_run(). There's are two
262 * exceptions here though, which are BUS_NODE_ROOT (which
263 * cannot have a sibling), and BUS_NODE_VALUE (whose siblings
264 * are invoked anyway by its parent. */
265
266 switch (node->type) {
267
268 case BUS_MATCH_ROOT:
269
270 /* Run all children. Since we cannot have any siblings
271 * we won't call any. The children of the root node
272 * are compares or leaves, they will automatically
273 * call their siblings. */
eb01ba5d 274 return bus_match_run(bus, node->child, m);
392d5b37
LP
275
276 case BUS_MATCH_VALUE:
277
278 /* Run all children. We don't execute any siblings, we
279 * assume our caller does that. The children of value
280 * nodes are compares or leaves, they will
281 * automatically call their siblings */
282
283 assert(node->child);
eb01ba5d 284 return bus_match_run(bus, node->child, m);
392d5b37
LP
285
286 case BUS_MATCH_LEAF:
287
7286037f 288 if (bus) {
6b39223c
LP
289 /* Don't run this match as long as the AddMatch() call is not complete yet.
290 *
291 * Don't run this match unless the 'after' counter has been reached.
292 *
293 * Don't run this match more than once per iteration */
294
295 if (node->leaf.callback->install_slot ||
296 m->read_counter <= node->leaf.callback->after ||
297 node->leaf.callback->last_iteration == bus->iteration_counter)
298 return bus_match_run(bus, node->next, m);
7286037f 299
19befb2d 300 node->leaf.callback->last_iteration = bus->iteration_counter;
7286037f
LP
301 }
302
88fe224c
LP
303 r = sd_bus_message_rewind(m, true);
304 if (r < 0)
305 return r;
306
392d5b37 307 /* Run the callback. And then invoke siblings. */
7e28adeb 308 if (node->leaf.callback->callback) {
4afd3348 309 _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
1b64f838 310 sd_bus_slot *slot;
ebcf1f97 311
19befb2d 312 slot = container_of(node->leaf.callback, sd_bus_slot, match_callback);
caa82984 313 if (bus) {
1b64f838 314 bus->current_slot = sd_bus_slot_ref(slot);
caa82984
LP
315 bus->current_handler = node->leaf.callback->callback;
316 bus->current_userdata = slot->userdata;
317 }
19070062 318 r = node->leaf.callback->callback(m, slot->userdata, &error_buffer);
caa82984
LP
319 if (bus) {
320 bus->current_userdata = NULL;
321 bus->current_handler = NULL;
1b64f838 322 bus->current_slot = sd_bus_slot_unref(slot);
caa82984 323 }
19befb2d 324
ebcf1f97 325 r = bus_maybe_reply_error(m, r, &error_buffer);
c7819669
LP
326 if (r != 0)
327 return r;
bbb6ff02
LP
328
329 if (bus && bus->match_callbacks_modified)
330 return 0;
c7819669 331 }
392d5b37 332
eb01ba5d 333 return bus_match_run(bus, node->next, m);
392d5b37
LP
334
335 case BUS_MATCH_MESSAGE_TYPE:
336 test_u8 = m->header->type;
337 break;
338
339 case BUS_MATCH_SENDER:
340 test_str = m->sender;
341 /* FIXME: resolve test_str from a well-known to a unique name first */
342 break;
343
344 case BUS_MATCH_DESTINATION:
345 test_str = m->destination;
346 break;
347
348 case BUS_MATCH_INTERFACE:
349 test_str = m->interface;
350 break;
351
352 case BUS_MATCH_MEMBER:
353 test_str = m->member;
354 break;
355
356 case BUS_MATCH_PATH:
357 case BUS_MATCH_PATH_NAMESPACE:
358 test_str = m->path;
359 break;
360
361 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
eccd47c5 362 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str);
392d5b37
LP
363 break;
364
365 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
eccd47c5 366 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str);
392d5b37
LP
367 break;
368
369 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
eccd47c5
LP
370 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str);
371 break;
372
373 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
374 (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv);
392d5b37
LP
375 break;
376
377 default:
04499a70 378 assert_not_reached();
392d5b37
LP
379 }
380
381 if (BUS_MATCH_CAN_HASH(node->type)) {
382 struct bus_match_node *found;
383
384 /* Lookup via hash table, nice! So let's jump directly. */
385
386 if (test_str)
387 found = hashmap_get(node->compare.children, test_str);
198b158f
LP
388 else if (test_strv) {
389 char **i;
390
391 STRV_FOREACH(i, test_strv) {
392 found = hashmap_get(node->compare.children, *i);
393 if (found) {
394 r = bus_match_run(bus, found, m);
395 if (r != 0)
396 return r;
397 }
398 }
399
400 found = NULL;
401 } else if (node->type == BUS_MATCH_MESSAGE_TYPE)
392d5b37
LP
402 found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
403 else
404 found = NULL;
405
406 if (found) {
eb01ba5d 407 r = bus_match_run(bus, found, m);
392d5b37
LP
408 if (r != 0)
409 return r;
410 }
fd5b9b84 411 } else
392d5b37 412 /* No hash table, so let's iterate manually... */
fd5b9b84 413 for (struct bus_match_node *c = node->child; c; c = c->next) {
198b158f 414 if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m))
392d5b37
LP
415 continue;
416
eb01ba5d 417 r = bus_match_run(bus, c, m);
392d5b37
LP
418 if (r != 0)
419 return r;
f0d5c5be
WX
420
421 if (bus && bus->match_callbacks_modified)
422 return 0;
392d5b37 423 }
392d5b37 424
7286037f
LP
425 if (bus && bus->match_callbacks_modified)
426 return 0;
427
392d5b37 428 /* And now, let's invoke our siblings */
eb01ba5d 429 return bus_match_run(bus, node->next, m);
392d5b37
LP
430}
431
432static int bus_match_add_compare_value(
433 struct bus_match_node *where,
434 enum bus_match_node_type t,
435 uint8_t value_u8,
436 const char *value_str,
437 struct bus_match_node **ret) {
438
fd5b9b84 439 struct bus_match_node *c, *n = NULL;
392d5b37
LP
440 int r;
441
442 assert(where);
945c2931 443 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
392d5b37
LP
444 assert(BUS_MATCH_IS_COMPARE(t));
445 assert(ret);
446
447 for (c = where->child; c && c->type != t; c = c->next)
448 ;
449
450 if (c) {
fd5b9b84 451 /* Comparison node already exists? Then let's see if the value node exists too. */
392d5b37
LP
452
453 if (t == BUS_MATCH_MESSAGE_TYPE)
454 n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
455 else if (BUS_MATCH_CAN_HASH(t))
456 n = hashmap_get(c->compare.children, value_str);
fd5b9b84 457 else
392d5b37
LP
458 for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
459 ;
392d5b37
LP
460
461 if (n) {
462 *ret = n;
463 return 0;
464 }
465 } else {
fd5b9b84 466 /* Comparison node, doesn't exist yet? Then let's create it. */
392d5b37
LP
467
468 c = new0(struct bus_match_node, 1);
469 if (!c) {
470 r = -ENOMEM;
471 goto fail;
472 }
473
474 c->type = t;
475 c->parent = where;
476 c->next = where->child;
477 if (c->next)
478 c->next->prev = c;
479 where->child = c;
480
481 if (t == BUS_MATCH_MESSAGE_TYPE) {
d5099efc 482 c->compare.children = hashmap_new(NULL);
392d5b37
LP
483 if (!c->compare.children) {
484 r = -ENOMEM;
485 goto fail;
486 }
487 } else if (BUS_MATCH_CAN_HASH(t)) {
d5099efc 488 c->compare.children = hashmap_new(&string_hash_ops);
392d5b37
LP
489 if (!c->compare.children) {
490 r = -ENOMEM;
491 goto fail;
492 }
493 }
494 }
495
496 n = new0(struct bus_match_node, 1);
497 if (!n) {
498 r = -ENOMEM;
499 goto fail;
500 }
501
502 n->type = BUS_MATCH_VALUE;
503 n->value.u8 = value_u8;
504 if (value_str) {
505 n->value.str = strdup(value_str);
506 if (!n->value.str) {
507 r = -ENOMEM;
508 goto fail;
509 }
510 }
511
512 n->parent = c;
513 if (c->compare.children) {
514
515 if (t == BUS_MATCH_MESSAGE_TYPE)
516 r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n);
517 else
518 r = hashmap_put(c->compare.children, n->value.str, n);
519
520 if (r < 0)
521 goto fail;
522 } else {
523 n->next = c->child;
524 if (n->next)
525 n->next->prev = n;
526 c->child = n;
527 }
528
529 *ret = n;
530 return 1;
531
532fail:
533 if (c)
534 bus_match_node_maybe_free(c);
535
536 if (n) {
537 free(n->value.str);
538 free(n);
539 }
540
541 return r;
542}
543
392d5b37
LP
544static int bus_match_add_leaf(
545 struct bus_match_node *where,
19befb2d 546 struct match_callback *callback) {
392d5b37
LP
547
548 struct bus_match_node *n;
549
550 assert(where);
945c2931 551 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
19befb2d 552 assert(callback);
392d5b37
LP
553
554 n = new0(struct bus_match_node, 1);
555 if (!n)
556 return -ENOMEM;
557
558 n->type = BUS_MATCH_LEAF;
559 n->parent = where;
560 n->next = where->child;
561 if (n->next)
562 n->next->prev = n;
19befb2d 563
392d5b37 564 n->leaf.callback = callback;
19befb2d 565 callback->match_node = n;
392d5b37
LP
566
567 where->child = n;
568
392d5b37
LP
569 return 1;
570}
571
392d5b37
LP
572enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) {
573 assert(k);
574
2a0e0692 575 if (n == 4 && startswith(k, "type"))
392d5b37 576 return BUS_MATCH_MESSAGE_TYPE;
2a0e0692 577 if (n == 6 && startswith(k, "sender"))
392d5b37 578 return BUS_MATCH_SENDER;
2a0e0692 579 if (n == 11 && startswith(k, "destination"))
392d5b37 580 return BUS_MATCH_DESTINATION;
2a0e0692 581 if (n == 9 && startswith(k, "interface"))
392d5b37 582 return BUS_MATCH_INTERFACE;
2a0e0692 583 if (n == 6 && startswith(k, "member"))
392d5b37 584 return BUS_MATCH_MEMBER;
2a0e0692 585 if (n == 4 && startswith(k, "path"))
392d5b37 586 return BUS_MATCH_PATH;
2a0e0692 587 if (n == 14 && startswith(k, "path_namespace"))
392d5b37
LP
588 return BUS_MATCH_PATH_NAMESPACE;
589
2a0e0692 590 if (n == 4 && startswith(k, "arg")) {
392d5b37
LP
591 int j;
592
593 j = undecchar(k[3]);
594 if (j < 0)
595 return -EINVAL;
596
597 return BUS_MATCH_ARG + j;
598 }
599
2a0e0692 600 if (n == 5 && startswith(k, "arg")) {
392d5b37
LP
601 int a, b;
602 enum bus_match_node_type t;
603
604 a = undecchar(k[3]);
605 b = undecchar(k[4]);
606 if (a <= 0 || b < 0)
607 return -EINVAL;
608
609 t = BUS_MATCH_ARG + a * 10 + b;
610 if (t > BUS_MATCH_ARG_LAST)
611 return -EINVAL;
612
613 return t;
614 }
615
2a0e0692 616 if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) {
392d5b37
LP
617 int j;
618
619 j = undecchar(k[3]);
620 if (j < 0)
621 return -EINVAL;
622
623 return BUS_MATCH_ARG_PATH + j;
624 }
625
2a0e0692 626 if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) {
392d5b37
LP
627 enum bus_match_node_type t;
628 int a, b;
629
630 a = undecchar(k[3]);
631 b = undecchar(k[4]);
632 if (a <= 0 || b < 0)
633 return -EINVAL;
634
635 t = BUS_MATCH_ARG_PATH + a * 10 + b;
636 if (t > BUS_MATCH_ARG_PATH_LAST)
637 return -EINVAL;
638
639 return t;
640 }
641
2a0e0692 642 if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) {
392d5b37
LP
643 int j;
644
645 j = undecchar(k[3]);
646 if (j < 0)
647 return -EINVAL;
648
649 return BUS_MATCH_ARG_NAMESPACE + j;
650 }
651
2a0e0692 652 if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) {
392d5b37
LP
653 enum bus_match_node_type t;
654 int a, b;
655
656 a = undecchar(k[3]);
657 b = undecchar(k[4]);
658 if (a <= 0 || b < 0)
659 return -EINVAL;
660
661 t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b;
662 if (t > BUS_MATCH_ARG_NAMESPACE_LAST)
663 return -EINVAL;
664
665 return t;
666 }
667
eccd47c5
LP
668 if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) {
669 int j;
670
671 j = undecchar(k[3]);
672 if (j < 0)
673 return -EINVAL;
674
675 return BUS_MATCH_ARG_HAS + j;
676 }
677
678 if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) {
679 enum bus_match_node_type t;
680 int a, b;
681
682 a = undecchar(k[3]);
683 b = undecchar(k[4]);
684 if (a <= 0 || b < 0)
685 return -EINVAL;
686
687 t = BUS_MATCH_ARG_HAS + a * 10 + b;
688 if (t > BUS_MATCH_ARG_HAS_LAST)
689 return -EINVAL;
690
691 return t;
692 }
693
392d5b37
LP
694 return -EINVAL;
695}
696
93bab288
YW
697static int match_component_compare(const struct bus_match_component *a, const struct bus_match_component *b) {
698 return CMP(a->type, b->type);
392d5b37
LP
699}
700
c7819669 701void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) {
fd5b9b84 702 for (unsigned i = 0; i < n_components; i++)
392d5b37
LP
703 free(components[i].value_str);
704
705 free(components);
706}
707
c7819669 708int bus_match_parse(
392d5b37 709 const char *match,
4ff42f83
ZJS
710 struct bus_match_component **ret_components,
711 unsigned *ret_n_components) {
392d5b37 712
c7819669 713 struct bus_match_component *components = NULL;
fd5b9b84 714 unsigned n_components = 0;
392d5b37
LP
715 int r;
716
717 assert(match);
4ff42f83
ZJS
718 assert(ret_components);
719 assert(ret_n_components);
392d5b37 720
7a39ec2e 721 while (*match != '\0') {
392d5b37
LP
722 const char *eq, *q;
723 enum bus_match_node_type t;
724 unsigned j = 0;
7a39ec2e 725 _cleanup_free_ char *value = NULL;
bc6422cb 726 bool escaped = false, quoted;
392d5b37
LP
727 uint8_t u;
728
771b2724 729 /* Avahi's match rules appear to include whitespace, skip over it */
7a39ec2e 730 match += strspn(match, " ");
771b2724 731
7a39ec2e 732 eq = strchr(match, '=');
8df3f44c
ZJS
733 if (!eq) {
734 r = -EINVAL;
735 goto fail;
736 }
392d5b37 737
7a39ec2e 738 t = bus_match_node_type_from_string(match, eq - match);
8df3f44c
ZJS
739 if (t < 0) {
740 r = -EINVAL;
741 goto fail;
742 }
392d5b37 743
bc6422cb
LP
744 quoted = eq[1] == '\'';
745
746 for (q = eq + 1 + quoted;; q++) {
392d5b37 747
7a39ec2e 748 if (*q == '\0') {
bc6422cb
LP
749
750 if (quoted) {
751 r = -EINVAL;
752 goto fail;
753 } else {
754 if (value)
7a39ec2e 755 value[j] = '\0';
bc6422cb
LP
756 break;
757 }
392d5b37
LP
758 }
759
760 if (!escaped) {
761 if (*q == '\\') {
762 escaped = true;
763 continue;
764 }
bc6422cb
LP
765
766 if (quoted) {
767 if (*q == '\'') {
768 if (value)
7a39ec2e 769 value[j] = '\0';
bc6422cb
LP
770 break;
771 }
772 } else {
773 if (*q == ',') {
774 if (value)
7a39ec2e 775 value[j] = '\0';
bc6422cb
LP
776 break;
777 }
392d5b37
LP
778 }
779 }
780
319a4f4b 781 if (!GREEDY_REALLOC(value, j + 2)) {
392d5b37
LP
782 r = -ENOMEM;
783 goto fail;
784 }
785
786 value[j++] = *q;
787 escaped = false;
788 }
789
2c8d477a
LP
790 if (!value) {
791 value = strdup("");
792 if (!value) {
793 r = -ENOMEM;
794 goto fail;
795 }
796 }
797
392d5b37
LP
798 if (t == BUS_MATCH_MESSAGE_TYPE) {
799 r = bus_message_type_from_string(value, &u);
800 if (r < 0)
801 goto fail;
802
97b11eed 803 value = mfree(value);
392d5b37
LP
804 } else
805 u = 0;
806
319a4f4b 807 if (!GREEDY_REALLOC(components, n_components + 1)) {
392d5b37
LP
808 r = -ENOMEM;
809 goto fail;
810 }
811
7a39ec2e
ZJS
812 components[n_components++] = (struct bus_match_component) {
813 .type = t,
814 .value_str = TAKE_PTR(value),
815 .value_u8 = u,
816 };
392d5b37 817
a3e648cc 818 if (q[quoted] == 0)
392d5b37
LP
819 break;
820
bc6422cb 821 if (q[quoted] != ',') {
392d5b37
LP
822 r = -EINVAL;
823 goto fail;
824 }
825
7a39ec2e 826 match = q + 1 + quoted;
392d5b37
LP
827 }
828
829 /* Order the whole thing, so that we always generate the same tree */
93bab288 830 typesafe_qsort(components, n_components, match_component_compare);
392d5b37
LP
831
832 /* Check for duplicates */
fd5b9b84 833 for (unsigned i = 0; i+1 < n_components; i++)
392d5b37
LP
834 if (components[i].type == components[i+1].type) {
835 r = -EINVAL;
836 goto fail;
837 }
838
4ff42f83
ZJS
839 *ret_components = components;
840 *ret_n_components = n_components;
392d5b37
LP
841
842 return 0;
843
844fail:
c7819669 845 bus_match_parse_free(components, n_components);
392d5b37
LP
846 return r;
847}
848
53461b74 849char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
5963e6f4 850 _cleanup_free_ char *buffer = NULL;
53461b74 851 size_t size = 0;
dacd6cee 852 int r;
53461b74
LP
853
854 if (n_components <= 0)
855 return strdup("");
856
857 assert(components);
858
5963e6f4 859 FILE *f = open_memstream_unlocked(&buffer, &size);
53461b74
LP
860 if (!f)
861 return NULL;
862
fd5b9b84 863 for (unsigned i = 0; i < n_components; i++) {
53461b74
LP
864 char buf[32];
865
866 if (i != 0)
0d536673 867 fputc(',', f);
53461b74 868
0d536673
LP
869 fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
870 fputc('=', f);
871 fputc('\'', f);
53461b74
LP
872
873 if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
0d536673 874 fputs(bus_message_type_to_string(components[i].value_u8), f);
53461b74 875 else
0d536673 876 fputs(components[i].value_str, f);
53461b74 877
0d536673 878 fputc('\'', f);
53461b74
LP
879 }
880
dacd6cee 881 r = fflush_and_check(f);
5963e6f4 882 safe_fclose(f);
dacd6cee 883 if (r < 0)
53461b74 884 return NULL;
5963e6f4 885 return TAKE_PTR(buffer);
53461b74
LP
886}
887
392d5b37
LP
888int bus_match_add(
889 struct bus_match_node *root,
c7819669
LP
890 struct bus_match_component *components,
891 unsigned n_components,
19befb2d 892 struct match_callback *callback) {
392d5b37 893
392d5b37
LP
894 int r;
895
896 assert(root);
19befb2d 897 assert(callback);
392d5b37 898
fd5b9b84
ZJS
899 for (unsigned i = 0; i < n_components; i++) {
900 r = bus_match_add_compare_value(root,
901 components[i].type,
902 components[i].value_u8,
903 components[i].value_str,
904 &root);
392d5b37 905 if (r < 0)
c7819669 906 return r;
392d5b37
LP
907 }
908
fd5b9b84 909 return bus_match_add_leaf(root, callback);
19befb2d 910}
392d5b37 911
19befb2d
LP
912int bus_match_remove(
913 struct bus_match_node *root,
914 struct match_callback *callback) {
392d5b37 915
19befb2d
LP
916 struct bus_match_node *node, *pp;
917
918 assert(root);
919 assert(callback);
920
921 node = callback->match_node;
922 if (!node)
923 return 0;
924
925 assert(node->type == BUS_MATCH_LEAF);
926
927 callback->match_node = NULL;
928
929 /* Free the leaf */
930 pp = node->parent;
931 bus_match_node_free(node);
932
933 /* Prune the tree above */
934 while (pp) {
935 node = pp;
936 pp = node->parent;
937
938 if (!bus_match_node_maybe_free(node))
939 break;
940 }
941
942 return 1;
392d5b37
LP
943}
944
392d5b37
LP
945void bus_match_free(struct bus_match_node *node) {
946 struct bus_match_node *c;
947
948 if (!node)
949 return;
950
951 if (BUS_MATCH_CAN_HASH(node->type)) {
392d5b37 952
90e74a66 953 HASHMAP_FOREACH(c, node->compare.children)
392d5b37
LP
954 bus_match_free(c);
955
956 assert(hashmap_isempty(node->compare.children));
957 }
958
959 while ((c = node->child))
960 bus_match_free(c);
961
962 if (node->type != BUS_MATCH_ROOT)
963 bus_match_node_free(node);
964}
965
966const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
967 switch (t) {
968
969 case BUS_MATCH_ROOT:
970 return "root";
971
972 case BUS_MATCH_VALUE:
973 return "value";
974
975 case BUS_MATCH_LEAF:
976 return "leaf";
977
978 case BUS_MATCH_MESSAGE_TYPE:
979 return "type";
980
981 case BUS_MATCH_SENDER:
982 return "sender";
983
984 case BUS_MATCH_DESTINATION:
985 return "destination";
986
987 case BUS_MATCH_INTERFACE:
988 return "interface";
989
990 case BUS_MATCH_MEMBER:
991 return "member";
992
993 case BUS_MATCH_PATH:
994 return "path";
995
996 case BUS_MATCH_PATH_NAMESPACE:
997 return "path_namespace";
998
999 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
272fff7d 1000 return snprintf_ok(buf, l, "arg%i", t - BUS_MATCH_ARG);
392d5b37
LP
1001
1002 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
272fff7d 1003 return snprintf_ok(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
392d5b37
LP
1004
1005 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
272fff7d 1006 return snprintf_ok(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
392d5b37 1007
eccd47c5 1008 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
272fff7d 1009 return snprintf_ok(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
eccd47c5 1010
392d5b37
LP
1011 default:
1012 return NULL;
1013 }
1014}
1015
fc561c8e 1016void bus_match_dump(FILE *out, struct bus_match_node *node, unsigned level) {
392d5b37
LP
1017 char buf[32];
1018
1019 if (!node)
1020 return;
1021
c25eb44a 1022 fprintf(out, "%*s[%s]", 2 * level, "", bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
392d5b37
LP
1023
1024 if (node->type == BUS_MATCH_VALUE) {
1025 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
fc561c8e 1026 fprintf(out, " <%u>\n", node->value.u8);
392d5b37 1027 else
fc561c8e 1028 fprintf(out, " <%s>\n", node->value.str);
392d5b37 1029 } else if (node->type == BUS_MATCH_ROOT)
fc561c8e 1030 fputs(" root\n", out);
392d5b37 1031 else if (node->type == BUS_MATCH_LEAF)
fc561c8e
ZJS
1032 fprintf(out, " %p/%p\n", node->leaf.callback->callback,
1033 container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
392d5b37 1034 else
fc561c8e 1035 putc('\n', out);
392d5b37
LP
1036
1037 if (BUS_MATCH_CAN_HASH(node->type)) {
fd5b9b84 1038 struct bus_match_node *c;
90e74a66 1039 HASHMAP_FOREACH(c, node->compare.children)
fc561c8e 1040 bus_match_dump(out, c, level + 1);
392d5b37
LP
1041 }
1042
fd5b9b84 1043 for (struct bus_match_node *c = node->child; c; c = c->next)
fc561c8e 1044 bus_match_dump(out, c, level + 1);
392d5b37 1045}
cc65fe5e
LP
1046
1047enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) {
1048 bool found_driver = false;
cc65fe5e
LP
1049
1050 if (n_components <= 0)
1051 return BUS_MATCH_GENERIC;
1052
1053 assert(components);
1054
1055 /* Checks whether the specified match can only match the
1056 * pseudo-service for local messages, which we detect by
1057 * sender, interface or path. If a match is not restricted to
1058 * local messages, then we check if it only matches on the
1059 * driver. */
1060
fd5b9b84 1061 for (unsigned i = 0; i < n_components; i++) {
cc65fe5e
LP
1062 const struct bus_match_component *c = components + i;
1063
1064 if (c->type == BUS_MATCH_SENDER) {
1065 if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1066 return BUS_MATCH_LOCAL;
1067
1068 if (streq_ptr(c->value_str, "org.freedesktop.DBus"))
1069 found_driver = true;
1070 }
1071
1072 if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1073 return BUS_MATCH_LOCAL;
1074
1075 if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local"))
1076 return BUS_MATCH_LOCAL;
1077 }
1078
1079 return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC;
cc65fe5e 1080}