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