]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-match.c
Add open_memstream_unlocked() wrapper
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-match.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
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) {
19befb2d 290 if (node->leaf.callback->last_iteration == bus->iteration_counter)
7286037f
LP
291 return 0;
292
19befb2d 293 node->leaf.callback->last_iteration = bus->iteration_counter;
7286037f
LP
294 }
295
88fe224c
LP
296 r = sd_bus_message_rewind(m, true);
297 if (r < 0)
298 return r;
299
392d5b37 300 /* Run the callback. And then invoke siblings. */
7e28adeb 301 if (node->leaf.callback->callback) {
4afd3348 302 _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
1b64f838 303 sd_bus_slot *slot;
ebcf1f97 304
19befb2d 305 slot = container_of(node->leaf.callback, sd_bus_slot, match_callback);
caa82984 306 if (bus) {
1b64f838 307 bus->current_slot = sd_bus_slot_ref(slot);
caa82984
LP
308 bus->current_handler = node->leaf.callback->callback;
309 bus->current_userdata = slot->userdata;
310 }
19070062 311 r = node->leaf.callback->callback(m, slot->userdata, &error_buffer);
caa82984
LP
312 if (bus) {
313 bus->current_userdata = NULL;
314 bus->current_handler = NULL;
1b64f838 315 bus->current_slot = sd_bus_slot_unref(slot);
caa82984 316 }
19befb2d 317
ebcf1f97 318 r = bus_maybe_reply_error(m, r, &error_buffer);
c7819669
LP
319 if (r != 0)
320 return r;
bbb6ff02
LP
321
322 if (bus && bus->match_callbacks_modified)
323 return 0;
c7819669 324 }
392d5b37 325
eb01ba5d 326 return bus_match_run(bus, node->next, m);
392d5b37
LP
327
328 case BUS_MATCH_MESSAGE_TYPE:
329 test_u8 = m->header->type;
330 break;
331
332 case BUS_MATCH_SENDER:
333 test_str = m->sender;
334 /* FIXME: resolve test_str from a well-known to a unique name first */
335 break;
336
337 case BUS_MATCH_DESTINATION:
338 test_str = m->destination;
339 break;
340
341 case BUS_MATCH_INTERFACE:
342 test_str = m->interface;
343 break;
344
345 case BUS_MATCH_MEMBER:
346 test_str = m->member;
347 break;
348
349 case BUS_MATCH_PATH:
350 case BUS_MATCH_PATH_NAMESPACE:
351 test_str = m->path;
352 break;
353
354 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
eccd47c5 355 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str);
392d5b37
LP
356 break;
357
358 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
eccd47c5 359 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str);
392d5b37
LP
360 break;
361
362 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
eccd47c5
LP
363 (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str);
364 break;
365
366 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
367 (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv);
392d5b37
LP
368 break;
369
370 default:
371 assert_not_reached("Unknown match type.");
372 }
373
374 if (BUS_MATCH_CAN_HASH(node->type)) {
375 struct bus_match_node *found;
376
377 /* Lookup via hash table, nice! So let's jump directly. */
378
379 if (test_str)
380 found = hashmap_get(node->compare.children, test_str);
198b158f
LP
381 else if (test_strv) {
382 char **i;
383
384 STRV_FOREACH(i, test_strv) {
385 found = hashmap_get(node->compare.children, *i);
386 if (found) {
387 r = bus_match_run(bus, found, m);
388 if (r != 0)
389 return r;
390 }
391 }
392
393 found = NULL;
394 } else if (node->type == BUS_MATCH_MESSAGE_TYPE)
392d5b37
LP
395 found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8));
396 else
397 found = NULL;
398
399 if (found) {
eb01ba5d 400 r = bus_match_run(bus, found, m);
392d5b37
LP
401 if (r != 0)
402 return r;
403 }
404 } else {
405 struct bus_match_node *c;
406
407 /* No hash table, so let's iterate manually... */
408
409 for (c = node->child; c; c = c->next) {
198b158f 410 if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m))
392d5b37
LP
411 continue;
412
eb01ba5d 413 r = bus_match_run(bus, c, m);
392d5b37
LP
414 if (r != 0)
415 return r;
f0d5c5be
WX
416
417 if (bus && bus->match_callbacks_modified)
418 return 0;
392d5b37
LP
419 }
420 }
421
7286037f
LP
422 if (bus && bus->match_callbacks_modified)
423 return 0;
424
392d5b37 425 /* And now, let's invoke our siblings */
eb01ba5d 426 return bus_match_run(bus, node->next, m);
392d5b37
LP
427}
428
429static int bus_match_add_compare_value(
430 struct bus_match_node *where,
431 enum bus_match_node_type t,
432 uint8_t value_u8,
433 const char *value_str,
434 struct bus_match_node **ret) {
435
436 struct bus_match_node *c = NULL, *n = NULL;
437 int r;
438
439 assert(where);
945c2931 440 assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE));
392d5b37
LP
441 assert(BUS_MATCH_IS_COMPARE(t));
442 assert(ret);
443
444 for (c = where->child; c && c->type != t; c = c->next)
445 ;
446
447 if (c) {
448 /* Comparison node already exists? Then let's see if
449 * the value node exists too. */
450
451 if (t == BUS_MATCH_MESSAGE_TYPE)
452 n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8));
453 else if (BUS_MATCH_CAN_HASH(t))
454 n = hashmap_get(c->compare.children, value_str);
455 else {
456 for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next)
457 ;
458 }
459
460 if (n) {
461 *ret = n;
462 return 0;
463 }
464 } else {
465 /* Comparison node, doesn't exist yet? Then let's
466 * create it. */
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) {
392d5b37
LP
702 unsigned i;
703
704 for (i = 0; i < n_components; i++)
705 free(components[i].value_str);
706
707 free(components);
708}
709
c7819669 710int bus_match_parse(
392d5b37 711 const char *match,
c7819669 712 struct bus_match_component **_components,
392d5b37
LP
713 unsigned *_n_components) {
714
715 const char *p = match;
c7819669 716 struct bus_match_component *components = NULL;
392d5b37
LP
717 size_t components_allocated = 0;
718 unsigned n_components = 0, i;
719 _cleanup_free_ char *value = NULL;
720 int r;
721
722 assert(match);
723 assert(_components);
724 assert(_n_components);
725
726 while (*p != 0) {
727 const char *eq, *q;
728 enum bus_match_node_type t;
729 unsigned j = 0;
730 size_t value_allocated = 0;
bc6422cb 731 bool escaped = false, quoted;
392d5b37
LP
732 uint8_t u;
733
771b2724
LP
734 /* Avahi's match rules appear to include whitespace, skip over it */
735 p += strspn(p, " ");
736
392d5b37
LP
737 eq = strchr(p, '=');
738 if (!eq)
739 return -EINVAL;
740
392d5b37
LP
741 t = bus_match_node_type_from_string(p, eq - p);
742 if (t < 0)
743 return -EINVAL;
744
bc6422cb
LP
745 quoted = eq[1] == '\'';
746
747 for (q = eq + 1 + quoted;; q++) {
392d5b37
LP
748
749 if (*q == 0) {
bc6422cb
LP
750
751 if (quoted) {
752 r = -EINVAL;
753 goto fail;
754 } else {
755 if (value)
756 value[j] = 0;
757 break;
758 }
392d5b37
LP
759 }
760
761 if (!escaped) {
762 if (*q == '\\') {
763 escaped = true;
764 continue;
765 }
bc6422cb
LP
766
767 if (quoted) {
768 if (*q == '\'') {
769 if (value)
770 value[j] = 0;
771 break;
772 }
773 } else {
774 if (*q == ',') {
775 if (value)
776 value[j] = 0;
777
778 break;
779 }
392d5b37
LP
780 }
781 }
782
2244a6fb 783 if (!GREEDY_REALLOC(value, value_allocated, j + 2)) {
392d5b37
LP
784 r = -ENOMEM;
785 goto fail;
786 }
787
788 value[j++] = *q;
789 escaped = false;
790 }
791
2c8d477a
LP
792 if (!value) {
793 value = strdup("");
794 if (!value) {
795 r = -ENOMEM;
796 goto fail;
797 }
798 }
799
392d5b37
LP
800 if (t == BUS_MATCH_MESSAGE_TYPE) {
801 r = bus_message_type_from_string(value, &u);
802 if (r < 0)
803 goto fail;
804
97b11eed 805 value = mfree(value);
392d5b37
LP
806 } else
807 u = 0;
808
2244a6fb 809 if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) {
392d5b37
LP
810 r = -ENOMEM;
811 goto fail;
812 }
813
814 components[n_components].type = t;
1cc6c93a 815 components[n_components].value_str = TAKE_PTR(value);
392d5b37
LP
816 components[n_components].value_u8 = u;
817 n_components++;
818
a3e648cc 819 if (q[quoted] == 0)
392d5b37
LP
820 break;
821
bc6422cb 822 if (q[quoted] != ',') {
392d5b37
LP
823 r = -EINVAL;
824 goto fail;
825 }
826
bc6422cb 827 p = q + 1 + quoted;
392d5b37
LP
828 }
829
830 /* Order the whole thing, so that we always generate the same tree */
93bab288 831 typesafe_qsort(components, n_components, match_component_compare);
392d5b37
LP
832
833 /* Check for duplicates */
834 for (i = 0; i+1 < n_components; i++)
835 if (components[i].type == components[i+1].type) {
836 r = -EINVAL;
837 goto fail;
838 }
839
840 *_components = components;
841 *_n_components = n_components;
842
843 return 0;
844
845fail:
c7819669 846 bus_match_parse_free(components, n_components);
392d5b37
LP
847 return r;
848}
849
53461b74 850char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
0ba793fb 851 _cleanup_fclose_ FILE *f = NULL;
53461b74
LP
852 char *buffer = NULL;
853 size_t size = 0;
854 unsigned i;
dacd6cee 855 int r;
53461b74
LP
856
857 if (n_components <= 0)
858 return strdup("");
859
860 assert(components);
861
2fe21124 862 f = open_memstream_unlocked(&buffer, &size);
53461b74
LP
863 if (!f)
864 return NULL;
865
866 for (i = 0; i < n_components; i++) {
867 char buf[32];
868
869 if (i != 0)
0d536673 870 fputc(',', f);
53461b74 871
0d536673
LP
872 fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
873 fputc('=', f);
874 fputc('\'', f);
53461b74
LP
875
876 if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
0d536673 877 fputs(bus_message_type_to_string(components[i].value_u8), f);
53461b74 878 else
0d536673 879 fputs(components[i].value_str, f);
53461b74 880
0d536673 881 fputc('\'', f);
53461b74
LP
882 }
883
dacd6cee
LP
884 r = fflush_and_check(f);
885 if (r < 0)
53461b74
LP
886 return NULL;
887
888 return buffer;
889}
890
392d5b37
LP
891int bus_match_add(
892 struct bus_match_node *root,
c7819669
LP
893 struct bus_match_component *components,
894 unsigned n_components,
19befb2d 895 struct match_callback *callback) {
392d5b37 896
c7819669 897 unsigned i;
392d5b37
LP
898 struct bus_match_node *n;
899 int r;
900
901 assert(root);
19befb2d 902 assert(callback);
392d5b37
LP
903
904 n = root;
905 for (i = 0; i < n_components; i++) {
906 r = bus_match_add_compare_value(
907 n, components[i].type,
908 components[i].value_u8, components[i].value_str, &n);
909 if (r < 0)
c7819669 910 return r;
392d5b37
LP
911 }
912
19befb2d
LP
913 return bus_match_add_leaf(n, callback);
914}
392d5b37 915
19befb2d
LP
916int bus_match_remove(
917 struct bus_match_node *root,
918 struct match_callback *callback) {
392d5b37 919
19befb2d
LP
920 struct bus_match_node *node, *pp;
921
922 assert(root);
923 assert(callback);
924
925 node = callback->match_node;
926 if (!node)
927 return 0;
928
929 assert(node->type == BUS_MATCH_LEAF);
930
931 callback->match_node = NULL;
932
933 /* Free the leaf */
934 pp = node->parent;
935 bus_match_node_free(node);
936
937 /* Prune the tree above */
938 while (pp) {
939 node = pp;
940 pp = node->parent;
941
942 if (!bus_match_node_maybe_free(node))
943 break;
944 }
945
946 return 1;
392d5b37
LP
947}
948
392d5b37
LP
949void bus_match_free(struct bus_match_node *node) {
950 struct bus_match_node *c;
951
952 if (!node)
953 return;
954
955 if (BUS_MATCH_CAN_HASH(node->type)) {
956 Iterator i;
957
958 HASHMAP_FOREACH(c, node->compare.children, i)
959 bus_match_free(c);
960
961 assert(hashmap_isempty(node->compare.children));
962 }
963
964 while ((c = node->child))
965 bus_match_free(c);
966
967 if (node->type != BUS_MATCH_ROOT)
968 bus_match_node_free(node);
969}
970
971const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
972 switch (t) {
973
974 case BUS_MATCH_ROOT:
975 return "root";
976
977 case BUS_MATCH_VALUE:
978 return "value";
979
980 case BUS_MATCH_LEAF:
981 return "leaf";
982
983 case BUS_MATCH_MESSAGE_TYPE:
984 return "type";
985
986 case BUS_MATCH_SENDER:
987 return "sender";
988
989 case BUS_MATCH_DESTINATION:
990 return "destination";
991
992 case BUS_MATCH_INTERFACE:
993 return "interface";
994
995 case BUS_MATCH_MEMBER:
996 return "member";
997
998 case BUS_MATCH_PATH:
999 return "path";
1000
1001 case BUS_MATCH_PATH_NAMESPACE:
1002 return "path_namespace";
1003
1004 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
1005 snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
1006 return buf;
1007
1008 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
1009 snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
1010 return buf;
1011
1012 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
1013 snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
1014 return buf;
1015
eccd47c5
LP
1016 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
1017 snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
1018 return buf;
1019
392d5b37
LP
1020 default:
1021 return NULL;
1022 }
1023}
1024
1025void bus_match_dump(struct bus_match_node *node, unsigned level) {
1026 struct bus_match_node *c;
1027 _cleanup_free_ char *pfx = NULL;
1028 char buf[32];
1029
1030 if (!node)
1031 return;
1032
1033 pfx = strrep(" ", level);
1034 printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
1035
1036 if (node->type == BUS_MATCH_VALUE) {
1037 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
1038 printf(" <%u>\n", node->value.u8);
1039 else
1040 printf(" <%s>\n", node->value.str);
1041 } else if (node->type == BUS_MATCH_ROOT)
1042 puts(" root");
1043 else if (node->type == BUS_MATCH_LEAF)
19befb2d 1044 printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
392d5b37
LP
1045 else
1046 putchar('\n');
1047
1048 if (BUS_MATCH_CAN_HASH(node->type)) {
1049 Iterator i;
1050
1051 HASHMAP_FOREACH(c, node->compare.children, i)
1052 bus_match_dump(c, level + 1);
1053 }
1054
1055 for (c = node->child; c; c = c->next)
1056 bus_match_dump(c, level + 1);
1057}
cc65fe5e
LP
1058
1059enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) {
1060 bool found_driver = false;
1061 unsigned i;
1062
1063 if (n_components <= 0)
1064 return BUS_MATCH_GENERIC;
1065
1066 assert(components);
1067
1068 /* Checks whether the specified match can only match the
1069 * pseudo-service for local messages, which we detect by
1070 * sender, interface or path. If a match is not restricted to
1071 * local messages, then we check if it only matches on the
1072 * driver. */
1073
1074 for (i = 0; i < n_components; i++) {
1075 const struct bus_match_component *c = components + i;
1076
1077 if (c->type == BUS_MATCH_SENDER) {
1078 if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1079 return BUS_MATCH_LOCAL;
1080
1081 if (streq_ptr(c->value_str, "org.freedesktop.DBus"))
1082 found_driver = true;
1083 }
1084
1085 if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1086 return BUS_MATCH_LOCAL;
1087
1088 if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local"))
1089 return BUS_MATCH_LOCAL;
1090 }
1091
1092 return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC;
1093
1094}