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