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