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