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