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