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