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