]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-match.c
network: fix typo in log message
[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;
906 components[n_components].value_str = value;
907 components[n_components].value_u8 = u;
908 n_components++;
909
910 value = NULL;
911
a3e648cc 912 if (q[quoted] == 0)
392d5b37
LP
913 break;
914
bc6422cb 915 if (q[quoted] != ',') {
392d5b37
LP
916 r = -EINVAL;
917 goto fail;
918 }
919
bc6422cb 920 p = q + 1 + quoted;
392d5b37
LP
921 }
922
923 /* Order the whole thing, so that we always generate the same tree */
7ff7394d 924 qsort_safe(components, n_components, sizeof(struct bus_match_component), match_component_compare);
392d5b37
LP
925
926 /* Check for duplicates */
927 for (i = 0; i+1 < n_components; i++)
928 if (components[i].type == components[i+1].type) {
929 r = -EINVAL;
930 goto fail;
931 }
932
933 *_components = components;
934 *_n_components = n_components;
935
936 return 0;
937
938fail:
c7819669 939 bus_match_parse_free(components, n_components);
392d5b37
LP
940 return r;
941}
942
53461b74 943char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) {
0ba793fb 944 _cleanup_fclose_ FILE *f = NULL;
53461b74
LP
945 char *buffer = NULL;
946 size_t size = 0;
947 unsigned i;
dacd6cee 948 int r;
53461b74
LP
949
950 if (n_components <= 0)
951 return strdup("");
952
953 assert(components);
954
955 f = open_memstream(&buffer, &size);
956 if (!f)
957 return NULL;
958
0d536673
LP
959 __fsetlocking(f, FSETLOCKING_BYCALLER);
960
53461b74
LP
961 for (i = 0; i < n_components; i++) {
962 char buf[32];
963
964 if (i != 0)
0d536673 965 fputc(',', f);
53461b74 966
0d536673
LP
967 fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f);
968 fputc('=', f);
969 fputc('\'', f);
53461b74
LP
970
971 if (components[i].type == BUS_MATCH_MESSAGE_TYPE)
0d536673 972 fputs(bus_message_type_to_string(components[i].value_u8), f);
53461b74 973 else
0d536673 974 fputs(components[i].value_str, f);
53461b74 975
0d536673 976 fputc('\'', f);
53461b74
LP
977 }
978
dacd6cee
LP
979 r = fflush_and_check(f);
980 if (r < 0)
53461b74
LP
981 return NULL;
982
983 return buffer;
984}
985
392d5b37
LP
986int bus_match_add(
987 struct bus_match_node *root,
c7819669
LP
988 struct bus_match_component *components,
989 unsigned n_components,
19befb2d 990 struct match_callback *callback) {
392d5b37 991
c7819669 992 unsigned i;
392d5b37
LP
993 struct bus_match_node *n;
994 int r;
995
996 assert(root);
19befb2d 997 assert(callback);
392d5b37
LP
998
999 n = root;
1000 for (i = 0; i < n_components; i++) {
1001 r = bus_match_add_compare_value(
1002 n, components[i].type,
1003 components[i].value_u8, components[i].value_str, &n);
1004 if (r < 0)
c7819669 1005 return r;
392d5b37
LP
1006 }
1007
19befb2d
LP
1008 return bus_match_add_leaf(n, callback);
1009}
392d5b37 1010
19befb2d
LP
1011int bus_match_remove(
1012 struct bus_match_node *root,
1013 struct match_callback *callback) {
392d5b37 1014
19befb2d
LP
1015 struct bus_match_node *node, *pp;
1016
1017 assert(root);
1018 assert(callback);
1019
1020 node = callback->match_node;
1021 if (!node)
1022 return 0;
1023
1024 assert(node->type == BUS_MATCH_LEAF);
1025
1026 callback->match_node = NULL;
1027
1028 /* Free the leaf */
1029 pp = node->parent;
1030 bus_match_node_free(node);
1031
1032 /* Prune the tree above */
1033 while (pp) {
1034 node = pp;
1035 pp = node->parent;
1036
1037 if (!bus_match_node_maybe_free(node))
1038 break;
1039 }
1040
1041 return 1;
392d5b37
LP
1042}
1043
19befb2d 1044int bus_match_find(
392d5b37 1045 struct bus_match_node *root,
c7819669
LP
1046 struct bus_match_component *components,
1047 unsigned n_components,
52f3ba91 1048 sd_bus_message_handler_t callback,
c7819669 1049 void *userdata,
19befb2d 1050 struct match_callback **ret) {
392d5b37 1051
392d5b37 1052 struct bus_match_node *n, **gc;
19befb2d 1053 unsigned i;
392d5b37
LP
1054 int r;
1055
1056 assert(root);
19befb2d 1057 assert(ret);
392d5b37
LP
1058
1059 gc = newa(struct bus_match_node*, n_components);
1060
1061 n = root;
1062 for (i = 0; i < n_components; i++) {
1063 r = bus_match_find_compare_value(
1064 n, components[i].type,
1065 components[i].value_u8, components[i].value_str,
1066 &n);
1067 if (r <= 0)
c7819669 1068 return r;
392d5b37
LP
1069
1070 gc[i] = n;
1071 }
1072
1073 r = bus_match_find_leaf(n, callback, userdata, &n);
1074 if (r <= 0)
c7819669
LP
1075 return r;
1076
19befb2d
LP
1077 *ret = n->leaf.callback;
1078 return 1;
392d5b37
LP
1079}
1080
1081void bus_match_free(struct bus_match_node *node) {
1082 struct bus_match_node *c;
1083
1084 if (!node)
1085 return;
1086
1087 if (BUS_MATCH_CAN_HASH(node->type)) {
1088 Iterator i;
1089
1090 HASHMAP_FOREACH(c, node->compare.children, i)
1091 bus_match_free(c);
1092
1093 assert(hashmap_isempty(node->compare.children));
1094 }
1095
1096 while ((c = node->child))
1097 bus_match_free(c);
1098
1099 if (node->type != BUS_MATCH_ROOT)
1100 bus_match_node_free(node);
1101}
1102
1103const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) {
1104 switch (t) {
1105
1106 case BUS_MATCH_ROOT:
1107 return "root";
1108
1109 case BUS_MATCH_VALUE:
1110 return "value";
1111
1112 case BUS_MATCH_LEAF:
1113 return "leaf";
1114
1115 case BUS_MATCH_MESSAGE_TYPE:
1116 return "type";
1117
1118 case BUS_MATCH_SENDER:
1119 return "sender";
1120
1121 case BUS_MATCH_DESTINATION:
1122 return "destination";
1123
1124 case BUS_MATCH_INTERFACE:
1125 return "interface";
1126
1127 case BUS_MATCH_MEMBER:
1128 return "member";
1129
1130 case BUS_MATCH_PATH:
1131 return "path";
1132
1133 case BUS_MATCH_PATH_NAMESPACE:
1134 return "path_namespace";
1135
1136 case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST:
1137 snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG);
1138 return buf;
1139
1140 case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST:
1141 snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH);
1142 return buf;
1143
1144 case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST:
1145 snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE);
1146 return buf;
1147
eccd47c5
LP
1148 case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST:
1149 snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS);
1150 return buf;
1151
392d5b37
LP
1152 default:
1153 return NULL;
1154 }
1155}
1156
1157void bus_match_dump(struct bus_match_node *node, unsigned level) {
1158 struct bus_match_node *c;
1159 _cleanup_free_ char *pfx = NULL;
1160 char buf[32];
1161
1162 if (!node)
1163 return;
1164
1165 pfx = strrep(" ", level);
1166 printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf)));
1167
1168 if (node->type == BUS_MATCH_VALUE) {
1169 if (node->parent->type == BUS_MATCH_MESSAGE_TYPE)
1170 printf(" <%u>\n", node->value.u8);
1171 else
1172 printf(" <%s>\n", node->value.str);
1173 } else if (node->type == BUS_MATCH_ROOT)
1174 puts(" root");
1175 else if (node->type == BUS_MATCH_LEAF)
19befb2d 1176 printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata);
392d5b37
LP
1177 else
1178 putchar('\n');
1179
1180 if (BUS_MATCH_CAN_HASH(node->type)) {
1181 Iterator i;
1182
1183 HASHMAP_FOREACH(c, node->compare.children, i)
1184 bus_match_dump(c, level + 1);
1185 }
1186
1187 for (c = node->child; c; c = c->next)
1188 bus_match_dump(c, level + 1);
1189}
cc65fe5e
LP
1190
1191enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) {
1192 bool found_driver = false;
1193 unsigned i;
1194
1195 if (n_components <= 0)
1196 return BUS_MATCH_GENERIC;
1197
1198 assert(components);
1199
1200 /* Checks whether the specified match can only match the
1201 * pseudo-service for local messages, which we detect by
1202 * sender, interface or path. If a match is not restricted to
1203 * local messages, then we check if it only matches on the
1204 * driver. */
1205
1206 for (i = 0; i < n_components; i++) {
1207 const struct bus_match_component *c = components + i;
1208
1209 if (c->type == BUS_MATCH_SENDER) {
1210 if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1211 return BUS_MATCH_LOCAL;
1212
1213 if (streq_ptr(c->value_str, "org.freedesktop.DBus"))
1214 found_driver = true;
1215 }
1216
1217 if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local"))
1218 return BUS_MATCH_LOCAL;
1219
1220 if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local"))
1221 return BUS_MATCH_LOCAL;
1222 }
1223
1224 return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC;
1225
1226}