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