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