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