]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
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) { |
6b39223c LP |
290 | /* Don't run this match as long as the AddMatch() call is not complete yet. |
291 | * | |
292 | * Don't run this match unless the 'after' counter has been reached. | |
293 | * | |
294 | * Don't run this match more than once per iteration */ | |
295 | ||
296 | if (node->leaf.callback->install_slot || | |
297 | m->read_counter <= node->leaf.callback->after || | |
298 | node->leaf.callback->last_iteration == bus->iteration_counter) | |
299 | return bus_match_run(bus, node->next, m); | |
7286037f | 300 | |
19befb2d | 301 | node->leaf.callback->last_iteration = bus->iteration_counter; |
7286037f LP |
302 | } |
303 | ||
88fe224c LP |
304 | r = sd_bus_message_rewind(m, true); |
305 | if (r < 0) | |
306 | return r; | |
307 | ||
392d5b37 | 308 | /* Run the callback. And then invoke siblings. */ |
7e28adeb | 309 | if (node->leaf.callback->callback) { |
4afd3348 | 310 | _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; |
1b64f838 | 311 | sd_bus_slot *slot; |
ebcf1f97 | 312 | |
19befb2d | 313 | slot = container_of(node->leaf.callback, sd_bus_slot, match_callback); |
caa82984 | 314 | if (bus) { |
1b64f838 | 315 | bus->current_slot = sd_bus_slot_ref(slot); |
caa82984 LP |
316 | bus->current_handler = node->leaf.callback->callback; |
317 | bus->current_userdata = slot->userdata; | |
318 | } | |
19070062 | 319 | r = node->leaf.callback->callback(m, slot->userdata, &error_buffer); |
caa82984 LP |
320 | if (bus) { |
321 | bus->current_userdata = NULL; | |
322 | bus->current_handler = NULL; | |
1b64f838 | 323 | bus->current_slot = sd_bus_slot_unref(slot); |
caa82984 | 324 | } |
19befb2d | 325 | |
ebcf1f97 | 326 | r = bus_maybe_reply_error(m, r, &error_buffer); |
c7819669 LP |
327 | if (r != 0) |
328 | return r; | |
bbb6ff02 LP |
329 | |
330 | if (bus && bus->match_callbacks_modified) | |
331 | return 0; | |
c7819669 | 332 | } |
392d5b37 | 333 | |
eb01ba5d | 334 | return bus_match_run(bus, node->next, m); |
392d5b37 LP |
335 | |
336 | case BUS_MATCH_MESSAGE_TYPE: | |
337 | test_u8 = m->header->type; | |
338 | break; | |
339 | ||
340 | case BUS_MATCH_SENDER: | |
341 | test_str = m->sender; | |
342 | /* FIXME: resolve test_str from a well-known to a unique name first */ | |
343 | break; | |
344 | ||
345 | case BUS_MATCH_DESTINATION: | |
346 | test_str = m->destination; | |
347 | break; | |
348 | ||
349 | case BUS_MATCH_INTERFACE: | |
350 | test_str = m->interface; | |
351 | break; | |
352 | ||
353 | case BUS_MATCH_MEMBER: | |
354 | test_str = m->member; | |
355 | break; | |
356 | ||
357 | case BUS_MATCH_PATH: | |
358 | case BUS_MATCH_PATH_NAMESPACE: | |
359 | test_str = m->path; | |
360 | break; | |
361 | ||
362 | case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: | |
eccd47c5 | 363 | (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG, &test_str); |
392d5b37 LP |
364 | break; |
365 | ||
366 | case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: | |
eccd47c5 | 367 | (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_PATH, &test_str); |
392d5b37 LP |
368 | break; |
369 | ||
370 | case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: | |
eccd47c5 LP |
371 | (void) bus_message_get_arg(m, node->type - BUS_MATCH_ARG_NAMESPACE, &test_str); |
372 | break; | |
373 | ||
374 | case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: | |
375 | (void) bus_message_get_arg_strv(m, node->type - BUS_MATCH_ARG_HAS, &test_strv); | |
392d5b37 LP |
376 | break; |
377 | ||
378 | default: | |
379 | assert_not_reached("Unknown match type."); | |
380 | } | |
381 | ||
382 | if (BUS_MATCH_CAN_HASH(node->type)) { | |
383 | struct bus_match_node *found; | |
384 | ||
385 | /* Lookup via hash table, nice! So let's jump directly. */ | |
386 | ||
387 | if (test_str) | |
388 | found = hashmap_get(node->compare.children, test_str); | |
198b158f LP |
389 | else if (test_strv) { |
390 | char **i; | |
391 | ||
392 | STRV_FOREACH(i, test_strv) { | |
393 | found = hashmap_get(node->compare.children, *i); | |
394 | if (found) { | |
395 | r = bus_match_run(bus, found, m); | |
396 | if (r != 0) | |
397 | return r; | |
398 | } | |
399 | } | |
400 | ||
401 | found = NULL; | |
402 | } else if (node->type == BUS_MATCH_MESSAGE_TYPE) | |
392d5b37 LP |
403 | found = hashmap_get(node->compare.children, UINT_TO_PTR(test_u8)); |
404 | else | |
405 | found = NULL; | |
406 | ||
407 | if (found) { | |
eb01ba5d | 408 | r = bus_match_run(bus, found, m); |
392d5b37 LP |
409 | if (r != 0) |
410 | return r; | |
411 | } | |
412 | } else { | |
413 | struct bus_match_node *c; | |
414 | ||
415 | /* No hash table, so let's iterate manually... */ | |
416 | ||
417 | for (c = node->child; c; c = c->next) { | |
198b158f | 418 | if (!value_node_test(c, node->type, test_u8, test_str, test_strv, m)) |
392d5b37 LP |
419 | continue; |
420 | ||
eb01ba5d | 421 | r = bus_match_run(bus, c, m); |
392d5b37 LP |
422 | if (r != 0) |
423 | return r; | |
f0d5c5be WX |
424 | |
425 | if (bus && bus->match_callbacks_modified) | |
426 | return 0; | |
392d5b37 LP |
427 | } |
428 | } | |
429 | ||
7286037f LP |
430 | if (bus && bus->match_callbacks_modified) |
431 | return 0; | |
432 | ||
392d5b37 | 433 | /* And now, let's invoke our siblings */ |
eb01ba5d | 434 | return bus_match_run(bus, node->next, m); |
392d5b37 LP |
435 | } |
436 | ||
437 | static int bus_match_add_compare_value( | |
438 | struct bus_match_node *where, | |
439 | enum bus_match_node_type t, | |
440 | uint8_t value_u8, | |
441 | const char *value_str, | |
442 | struct bus_match_node **ret) { | |
443 | ||
444 | struct bus_match_node *c = NULL, *n = NULL; | |
445 | int r; | |
446 | ||
447 | assert(where); | |
945c2931 | 448 | assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE)); |
392d5b37 LP |
449 | assert(BUS_MATCH_IS_COMPARE(t)); |
450 | assert(ret); | |
451 | ||
452 | for (c = where->child; c && c->type != t; c = c->next) | |
453 | ; | |
454 | ||
455 | if (c) { | |
456 | /* Comparison node already exists? Then let's see if | |
457 | * the value node exists too. */ | |
458 | ||
459 | if (t == BUS_MATCH_MESSAGE_TYPE) | |
460 | n = hashmap_get(c->compare.children, UINT_TO_PTR(value_u8)); | |
461 | else if (BUS_MATCH_CAN_HASH(t)) | |
462 | n = hashmap_get(c->compare.children, value_str); | |
463 | else { | |
464 | for (n = c->child; n && !value_node_same(n, t, value_u8, value_str); n = n->next) | |
465 | ; | |
466 | } | |
467 | ||
468 | if (n) { | |
469 | *ret = n; | |
470 | return 0; | |
471 | } | |
472 | } else { | |
473 | /* Comparison node, doesn't exist yet? Then let's | |
474 | * create it. */ | |
475 | ||
476 | c = new0(struct bus_match_node, 1); | |
477 | if (!c) { | |
478 | r = -ENOMEM; | |
479 | goto fail; | |
480 | } | |
481 | ||
482 | c->type = t; | |
483 | c->parent = where; | |
484 | c->next = where->child; | |
485 | if (c->next) | |
486 | c->next->prev = c; | |
487 | where->child = c; | |
488 | ||
489 | if (t == BUS_MATCH_MESSAGE_TYPE) { | |
d5099efc | 490 | c->compare.children = hashmap_new(NULL); |
392d5b37 LP |
491 | if (!c->compare.children) { |
492 | r = -ENOMEM; | |
493 | goto fail; | |
494 | } | |
495 | } else if (BUS_MATCH_CAN_HASH(t)) { | |
d5099efc | 496 | c->compare.children = hashmap_new(&string_hash_ops); |
392d5b37 LP |
497 | if (!c->compare.children) { |
498 | r = -ENOMEM; | |
499 | goto fail; | |
500 | } | |
501 | } | |
502 | } | |
503 | ||
504 | n = new0(struct bus_match_node, 1); | |
505 | if (!n) { | |
506 | r = -ENOMEM; | |
507 | goto fail; | |
508 | } | |
509 | ||
510 | n->type = BUS_MATCH_VALUE; | |
511 | n->value.u8 = value_u8; | |
512 | if (value_str) { | |
513 | n->value.str = strdup(value_str); | |
514 | if (!n->value.str) { | |
515 | r = -ENOMEM; | |
516 | goto fail; | |
517 | } | |
518 | } | |
519 | ||
520 | n->parent = c; | |
521 | if (c->compare.children) { | |
522 | ||
523 | if (t == BUS_MATCH_MESSAGE_TYPE) | |
524 | r = hashmap_put(c->compare.children, UINT_TO_PTR(value_u8), n); | |
525 | else | |
526 | r = hashmap_put(c->compare.children, n->value.str, n); | |
527 | ||
528 | if (r < 0) | |
529 | goto fail; | |
530 | } else { | |
531 | n->next = c->child; | |
532 | if (n->next) | |
533 | n->next->prev = n; | |
534 | c->child = n; | |
535 | } | |
536 | ||
537 | *ret = n; | |
538 | return 1; | |
539 | ||
540 | fail: | |
541 | if (c) | |
542 | bus_match_node_maybe_free(c); | |
543 | ||
544 | if (n) { | |
545 | free(n->value.str); | |
546 | free(n); | |
547 | } | |
548 | ||
549 | return r; | |
550 | } | |
551 | ||
392d5b37 LP |
552 | static int bus_match_add_leaf( |
553 | struct bus_match_node *where, | |
19befb2d | 554 | struct match_callback *callback) { |
392d5b37 LP |
555 | |
556 | struct bus_match_node *n; | |
557 | ||
558 | assert(where); | |
945c2931 | 559 | assert(IN_SET(where->type, BUS_MATCH_ROOT, BUS_MATCH_VALUE)); |
19befb2d | 560 | assert(callback); |
392d5b37 LP |
561 | |
562 | n = new0(struct bus_match_node, 1); | |
563 | if (!n) | |
564 | return -ENOMEM; | |
565 | ||
566 | n->type = BUS_MATCH_LEAF; | |
567 | n->parent = where; | |
568 | n->next = where->child; | |
569 | if (n->next) | |
570 | n->next->prev = n; | |
19befb2d | 571 | |
392d5b37 | 572 | n->leaf.callback = callback; |
19befb2d | 573 | callback->match_node = n; |
392d5b37 LP |
574 | |
575 | where->child = n; | |
576 | ||
392d5b37 LP |
577 | return 1; |
578 | } | |
579 | ||
392d5b37 LP |
580 | enum bus_match_node_type bus_match_node_type_from_string(const char *k, size_t n) { |
581 | assert(k); | |
582 | ||
2a0e0692 | 583 | if (n == 4 && startswith(k, "type")) |
392d5b37 | 584 | return BUS_MATCH_MESSAGE_TYPE; |
2a0e0692 | 585 | if (n == 6 && startswith(k, "sender")) |
392d5b37 | 586 | return BUS_MATCH_SENDER; |
2a0e0692 | 587 | if (n == 11 && startswith(k, "destination")) |
392d5b37 | 588 | return BUS_MATCH_DESTINATION; |
2a0e0692 | 589 | if (n == 9 && startswith(k, "interface")) |
392d5b37 | 590 | return BUS_MATCH_INTERFACE; |
2a0e0692 | 591 | if (n == 6 && startswith(k, "member")) |
392d5b37 | 592 | return BUS_MATCH_MEMBER; |
2a0e0692 | 593 | if (n == 4 && startswith(k, "path")) |
392d5b37 | 594 | return BUS_MATCH_PATH; |
2a0e0692 | 595 | if (n == 14 && startswith(k, "path_namespace")) |
392d5b37 LP |
596 | return BUS_MATCH_PATH_NAMESPACE; |
597 | ||
2a0e0692 | 598 | if (n == 4 && startswith(k, "arg")) { |
392d5b37 LP |
599 | int j; |
600 | ||
601 | j = undecchar(k[3]); | |
602 | if (j < 0) | |
603 | return -EINVAL; | |
604 | ||
605 | return BUS_MATCH_ARG + j; | |
606 | } | |
607 | ||
2a0e0692 | 608 | if (n == 5 && startswith(k, "arg")) { |
392d5b37 LP |
609 | int a, b; |
610 | enum bus_match_node_type t; | |
611 | ||
612 | a = undecchar(k[3]); | |
613 | b = undecchar(k[4]); | |
614 | if (a <= 0 || b < 0) | |
615 | return -EINVAL; | |
616 | ||
617 | t = BUS_MATCH_ARG + a * 10 + b; | |
618 | if (t > BUS_MATCH_ARG_LAST) | |
619 | return -EINVAL; | |
620 | ||
621 | return t; | |
622 | } | |
623 | ||
2a0e0692 | 624 | if (n == 8 && startswith(k, "arg") && startswith(k + 4, "path")) { |
392d5b37 LP |
625 | int j; |
626 | ||
627 | j = undecchar(k[3]); | |
628 | if (j < 0) | |
629 | return -EINVAL; | |
630 | ||
631 | return BUS_MATCH_ARG_PATH + j; | |
632 | } | |
633 | ||
2a0e0692 | 634 | if (n == 9 && startswith(k, "arg") && startswith(k + 5, "path")) { |
392d5b37 LP |
635 | enum bus_match_node_type t; |
636 | int a, b; | |
637 | ||
638 | a = undecchar(k[3]); | |
639 | b = undecchar(k[4]); | |
640 | if (a <= 0 || b < 0) | |
641 | return -EINVAL; | |
642 | ||
643 | t = BUS_MATCH_ARG_PATH + a * 10 + b; | |
644 | if (t > BUS_MATCH_ARG_PATH_LAST) | |
645 | return -EINVAL; | |
646 | ||
647 | return t; | |
648 | } | |
649 | ||
2a0e0692 | 650 | if (n == 13 && startswith(k, "arg") && startswith(k + 4, "namespace")) { |
392d5b37 LP |
651 | int j; |
652 | ||
653 | j = undecchar(k[3]); | |
654 | if (j < 0) | |
655 | return -EINVAL; | |
656 | ||
657 | return BUS_MATCH_ARG_NAMESPACE + j; | |
658 | } | |
659 | ||
2a0e0692 | 660 | if (n == 14 && startswith(k, "arg") && startswith(k + 5, "namespace")) { |
392d5b37 LP |
661 | enum bus_match_node_type t; |
662 | int a, b; | |
663 | ||
664 | a = undecchar(k[3]); | |
665 | b = undecchar(k[4]); | |
666 | if (a <= 0 || b < 0) | |
667 | return -EINVAL; | |
668 | ||
669 | t = BUS_MATCH_ARG_NAMESPACE + a * 10 + b; | |
670 | if (t > BUS_MATCH_ARG_NAMESPACE_LAST) | |
671 | return -EINVAL; | |
672 | ||
673 | return t; | |
674 | } | |
675 | ||
eccd47c5 LP |
676 | if (n == 7 && startswith(k, "arg") && startswith(k + 4, "has")) { |
677 | int j; | |
678 | ||
679 | j = undecchar(k[3]); | |
680 | if (j < 0) | |
681 | return -EINVAL; | |
682 | ||
683 | return BUS_MATCH_ARG_HAS + j; | |
684 | } | |
685 | ||
686 | if (n == 8 && startswith(k, "arg") && startswith(k + 5, "has")) { | |
687 | enum bus_match_node_type t; | |
688 | int a, b; | |
689 | ||
690 | a = undecchar(k[3]); | |
691 | b = undecchar(k[4]); | |
692 | if (a <= 0 || b < 0) | |
693 | return -EINVAL; | |
694 | ||
695 | t = BUS_MATCH_ARG_HAS + a * 10 + b; | |
696 | if (t > BUS_MATCH_ARG_HAS_LAST) | |
697 | return -EINVAL; | |
698 | ||
699 | return t; | |
700 | } | |
701 | ||
392d5b37 LP |
702 | return -EINVAL; |
703 | } | |
704 | ||
93bab288 YW |
705 | static int match_component_compare(const struct bus_match_component *a, const struct bus_match_component *b) { |
706 | return CMP(a->type, b->type); | |
392d5b37 LP |
707 | } |
708 | ||
c7819669 | 709 | void bus_match_parse_free(struct bus_match_component *components, unsigned n_components) { |
392d5b37 LP |
710 | unsigned i; |
711 | ||
712 | for (i = 0; i < n_components; i++) | |
713 | free(components[i].value_str); | |
714 | ||
715 | free(components); | |
716 | } | |
717 | ||
c7819669 | 718 | int bus_match_parse( |
392d5b37 | 719 | const char *match, |
c7819669 | 720 | struct bus_match_component **_components, |
392d5b37 LP |
721 | unsigned *_n_components) { |
722 | ||
723 | const char *p = match; | |
c7819669 | 724 | struct bus_match_component *components = NULL; |
392d5b37 LP |
725 | size_t components_allocated = 0; |
726 | unsigned n_components = 0, i; | |
727 | _cleanup_free_ char *value = NULL; | |
728 | int r; | |
729 | ||
730 | assert(match); | |
731 | assert(_components); | |
732 | assert(_n_components); | |
733 | ||
734 | while (*p != 0) { | |
735 | const char *eq, *q; | |
736 | enum bus_match_node_type t; | |
737 | unsigned j = 0; | |
738 | size_t value_allocated = 0; | |
bc6422cb | 739 | bool escaped = false, quoted; |
392d5b37 LP |
740 | uint8_t u; |
741 | ||
771b2724 LP |
742 | /* Avahi's match rules appear to include whitespace, skip over it */ |
743 | p += strspn(p, " "); | |
744 | ||
392d5b37 LP |
745 | eq = strchr(p, '='); |
746 | if (!eq) | |
747 | return -EINVAL; | |
748 | ||
392d5b37 LP |
749 | t = bus_match_node_type_from_string(p, eq - p); |
750 | if (t < 0) | |
751 | return -EINVAL; | |
752 | ||
bc6422cb LP |
753 | quoted = eq[1] == '\''; |
754 | ||
755 | for (q = eq + 1 + quoted;; q++) { | |
392d5b37 LP |
756 | |
757 | if (*q == 0) { | |
bc6422cb LP |
758 | |
759 | if (quoted) { | |
760 | r = -EINVAL; | |
761 | goto fail; | |
762 | } else { | |
763 | if (value) | |
764 | value[j] = 0; | |
765 | break; | |
766 | } | |
392d5b37 LP |
767 | } |
768 | ||
769 | if (!escaped) { | |
770 | if (*q == '\\') { | |
771 | escaped = true; | |
772 | continue; | |
773 | } | |
bc6422cb LP |
774 | |
775 | if (quoted) { | |
776 | if (*q == '\'') { | |
777 | if (value) | |
778 | value[j] = 0; | |
779 | break; | |
780 | } | |
781 | } else { | |
782 | if (*q == ',') { | |
783 | if (value) | |
784 | value[j] = 0; | |
785 | ||
786 | break; | |
787 | } | |
392d5b37 LP |
788 | } |
789 | } | |
790 | ||
2244a6fb | 791 | if (!GREEDY_REALLOC(value, value_allocated, j + 2)) { |
392d5b37 LP |
792 | r = -ENOMEM; |
793 | goto fail; | |
794 | } | |
795 | ||
796 | value[j++] = *q; | |
797 | escaped = false; | |
798 | } | |
799 | ||
2c8d477a LP |
800 | if (!value) { |
801 | value = strdup(""); | |
802 | if (!value) { | |
803 | r = -ENOMEM; | |
804 | goto fail; | |
805 | } | |
806 | } | |
807 | ||
392d5b37 LP |
808 | if (t == BUS_MATCH_MESSAGE_TYPE) { |
809 | r = bus_message_type_from_string(value, &u); | |
810 | if (r < 0) | |
811 | goto fail; | |
812 | ||
97b11eed | 813 | value = mfree(value); |
392d5b37 LP |
814 | } else |
815 | u = 0; | |
816 | ||
2244a6fb | 817 | if (!GREEDY_REALLOC(components, components_allocated, n_components + 1)) { |
392d5b37 LP |
818 | r = -ENOMEM; |
819 | goto fail; | |
820 | } | |
821 | ||
822 | components[n_components].type = t; | |
1cc6c93a | 823 | components[n_components].value_str = TAKE_PTR(value); |
392d5b37 LP |
824 | components[n_components].value_u8 = u; |
825 | n_components++; | |
826 | ||
a3e648cc | 827 | if (q[quoted] == 0) |
392d5b37 LP |
828 | break; |
829 | ||
bc6422cb | 830 | if (q[quoted] != ',') { |
392d5b37 LP |
831 | r = -EINVAL; |
832 | goto fail; | |
833 | } | |
834 | ||
bc6422cb | 835 | p = q + 1 + quoted; |
392d5b37 LP |
836 | } |
837 | ||
838 | /* Order the whole thing, so that we always generate the same tree */ | |
93bab288 | 839 | typesafe_qsort(components, n_components, match_component_compare); |
392d5b37 LP |
840 | |
841 | /* Check for duplicates */ | |
842 | for (i = 0; i+1 < n_components; i++) | |
843 | if (components[i].type == components[i+1].type) { | |
844 | r = -EINVAL; | |
845 | goto fail; | |
846 | } | |
847 | ||
848 | *_components = components; | |
849 | *_n_components = n_components; | |
850 | ||
851 | return 0; | |
852 | ||
853 | fail: | |
c7819669 | 854 | bus_match_parse_free(components, n_components); |
392d5b37 LP |
855 | return r; |
856 | } | |
857 | ||
53461b74 | 858 | char *bus_match_to_string(struct bus_match_component *components, unsigned n_components) { |
0ba793fb | 859 | _cleanup_fclose_ FILE *f = NULL; |
53461b74 LP |
860 | char *buffer = NULL; |
861 | size_t size = 0; | |
862 | unsigned i; | |
dacd6cee | 863 | int r; |
53461b74 LP |
864 | |
865 | if (n_components <= 0) | |
866 | return strdup(""); | |
867 | ||
868 | assert(components); | |
869 | ||
2fe21124 | 870 | f = open_memstream_unlocked(&buffer, &size); |
53461b74 LP |
871 | if (!f) |
872 | return NULL; | |
873 | ||
874 | for (i = 0; i < n_components; i++) { | |
875 | char buf[32]; | |
876 | ||
877 | if (i != 0) | |
0d536673 | 878 | fputc(',', f); |
53461b74 | 879 | |
0d536673 LP |
880 | fputs(bus_match_node_type_to_string(components[i].type, buf, sizeof(buf)), f); |
881 | fputc('=', f); | |
882 | fputc('\'', f); | |
53461b74 LP |
883 | |
884 | if (components[i].type == BUS_MATCH_MESSAGE_TYPE) | |
0d536673 | 885 | fputs(bus_message_type_to_string(components[i].value_u8), f); |
53461b74 | 886 | else |
0d536673 | 887 | fputs(components[i].value_str, f); |
53461b74 | 888 | |
0d536673 | 889 | fputc('\'', f); |
53461b74 LP |
890 | } |
891 | ||
dacd6cee LP |
892 | r = fflush_and_check(f); |
893 | if (r < 0) | |
53461b74 LP |
894 | return NULL; |
895 | ||
896 | return buffer; | |
897 | } | |
898 | ||
392d5b37 LP |
899 | int bus_match_add( |
900 | struct bus_match_node *root, | |
c7819669 LP |
901 | struct bus_match_component *components, |
902 | unsigned n_components, | |
19befb2d | 903 | struct match_callback *callback) { |
392d5b37 | 904 | |
c7819669 | 905 | unsigned i; |
392d5b37 LP |
906 | struct bus_match_node *n; |
907 | int r; | |
908 | ||
909 | assert(root); | |
19befb2d | 910 | assert(callback); |
392d5b37 LP |
911 | |
912 | n = root; | |
913 | for (i = 0; i < n_components; i++) { | |
914 | r = bus_match_add_compare_value( | |
915 | n, components[i].type, | |
916 | components[i].value_u8, components[i].value_str, &n); | |
917 | if (r < 0) | |
c7819669 | 918 | return r; |
392d5b37 LP |
919 | } |
920 | ||
19befb2d LP |
921 | return bus_match_add_leaf(n, callback); |
922 | } | |
392d5b37 | 923 | |
19befb2d LP |
924 | int bus_match_remove( |
925 | struct bus_match_node *root, | |
926 | struct match_callback *callback) { | |
392d5b37 | 927 | |
19befb2d LP |
928 | struct bus_match_node *node, *pp; |
929 | ||
930 | assert(root); | |
931 | assert(callback); | |
932 | ||
933 | node = callback->match_node; | |
934 | if (!node) | |
935 | return 0; | |
936 | ||
937 | assert(node->type == BUS_MATCH_LEAF); | |
938 | ||
939 | callback->match_node = NULL; | |
940 | ||
941 | /* Free the leaf */ | |
942 | pp = node->parent; | |
943 | bus_match_node_free(node); | |
944 | ||
945 | /* Prune the tree above */ | |
946 | while (pp) { | |
947 | node = pp; | |
948 | pp = node->parent; | |
949 | ||
950 | if (!bus_match_node_maybe_free(node)) | |
951 | break; | |
952 | } | |
953 | ||
954 | return 1; | |
392d5b37 LP |
955 | } |
956 | ||
392d5b37 LP |
957 | void bus_match_free(struct bus_match_node *node) { |
958 | struct bus_match_node *c; | |
959 | ||
960 | if (!node) | |
961 | return; | |
962 | ||
963 | if (BUS_MATCH_CAN_HASH(node->type)) { | |
392d5b37 | 964 | |
90e74a66 | 965 | HASHMAP_FOREACH(c, node->compare.children) |
392d5b37 LP |
966 | bus_match_free(c); |
967 | ||
968 | assert(hashmap_isempty(node->compare.children)); | |
969 | } | |
970 | ||
971 | while ((c = node->child)) | |
972 | bus_match_free(c); | |
973 | ||
974 | if (node->type != BUS_MATCH_ROOT) | |
975 | bus_match_node_free(node); | |
976 | } | |
977 | ||
978 | const char* bus_match_node_type_to_string(enum bus_match_node_type t, char buf[], size_t l) { | |
979 | switch (t) { | |
980 | ||
981 | case BUS_MATCH_ROOT: | |
982 | return "root"; | |
983 | ||
984 | case BUS_MATCH_VALUE: | |
985 | return "value"; | |
986 | ||
987 | case BUS_MATCH_LEAF: | |
988 | return "leaf"; | |
989 | ||
990 | case BUS_MATCH_MESSAGE_TYPE: | |
991 | return "type"; | |
992 | ||
993 | case BUS_MATCH_SENDER: | |
994 | return "sender"; | |
995 | ||
996 | case BUS_MATCH_DESTINATION: | |
997 | return "destination"; | |
998 | ||
999 | case BUS_MATCH_INTERFACE: | |
1000 | return "interface"; | |
1001 | ||
1002 | case BUS_MATCH_MEMBER: | |
1003 | return "member"; | |
1004 | ||
1005 | case BUS_MATCH_PATH: | |
1006 | return "path"; | |
1007 | ||
1008 | case BUS_MATCH_PATH_NAMESPACE: | |
1009 | return "path_namespace"; | |
1010 | ||
1011 | case BUS_MATCH_ARG ... BUS_MATCH_ARG_LAST: | |
1012 | snprintf(buf, l, "arg%i", t - BUS_MATCH_ARG); | |
1013 | return buf; | |
1014 | ||
1015 | case BUS_MATCH_ARG_PATH ... BUS_MATCH_ARG_PATH_LAST: | |
1016 | snprintf(buf, l, "arg%ipath", t - BUS_MATCH_ARG_PATH); | |
1017 | return buf; | |
1018 | ||
1019 | case BUS_MATCH_ARG_NAMESPACE ... BUS_MATCH_ARG_NAMESPACE_LAST: | |
1020 | snprintf(buf, l, "arg%inamespace", t - BUS_MATCH_ARG_NAMESPACE); | |
1021 | return buf; | |
1022 | ||
eccd47c5 LP |
1023 | case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: |
1024 | snprintf(buf, l, "arg%ihas", t - BUS_MATCH_ARG_HAS); | |
1025 | return buf; | |
1026 | ||
392d5b37 LP |
1027 | default: |
1028 | return NULL; | |
1029 | } | |
1030 | } | |
1031 | ||
1032 | void bus_match_dump(struct bus_match_node *node, unsigned level) { | |
1033 | struct bus_match_node *c; | |
1034 | _cleanup_free_ char *pfx = NULL; | |
1035 | char buf[32]; | |
1036 | ||
1037 | if (!node) | |
1038 | return; | |
1039 | ||
1040 | pfx = strrep(" ", level); | |
1041 | printf("%s[%s]", strempty(pfx), bus_match_node_type_to_string(node->type, buf, sizeof(buf))); | |
1042 | ||
1043 | if (node->type == BUS_MATCH_VALUE) { | |
1044 | if (node->parent->type == BUS_MATCH_MESSAGE_TYPE) | |
1045 | printf(" <%u>\n", node->value.u8); | |
1046 | else | |
1047 | printf(" <%s>\n", node->value.str); | |
1048 | } else if (node->type == BUS_MATCH_ROOT) | |
1049 | puts(" root"); | |
1050 | else if (node->type == BUS_MATCH_LEAF) | |
19befb2d | 1051 | printf(" %p/%p\n", node->leaf.callback->callback, container_of(node->leaf.callback, sd_bus_slot, match_callback)->userdata); |
392d5b37 LP |
1052 | else |
1053 | putchar('\n'); | |
1054 | ||
1055 | if (BUS_MATCH_CAN_HASH(node->type)) { | |
392d5b37 | 1056 | |
90e74a66 | 1057 | HASHMAP_FOREACH(c, node->compare.children) |
392d5b37 LP |
1058 | bus_match_dump(c, level + 1); |
1059 | } | |
1060 | ||
1061 | for (c = node->child; c; c = c->next) | |
1062 | bus_match_dump(c, level + 1); | |
1063 | } | |
cc65fe5e LP |
1064 | |
1065 | enum bus_match_scope bus_match_get_scope(const struct bus_match_component *components, unsigned n_components) { | |
1066 | bool found_driver = false; | |
1067 | unsigned i; | |
1068 | ||
1069 | if (n_components <= 0) | |
1070 | return BUS_MATCH_GENERIC; | |
1071 | ||
1072 | assert(components); | |
1073 | ||
1074 | /* Checks whether the specified match can only match the | |
1075 | * pseudo-service for local messages, which we detect by | |
1076 | * sender, interface or path. If a match is not restricted to | |
1077 | * local messages, then we check if it only matches on the | |
1078 | * driver. */ | |
1079 | ||
1080 | for (i = 0; i < n_components; i++) { | |
1081 | const struct bus_match_component *c = components + i; | |
1082 | ||
1083 | if (c->type == BUS_MATCH_SENDER) { | |
1084 | if (streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) | |
1085 | return BUS_MATCH_LOCAL; | |
1086 | ||
1087 | if (streq_ptr(c->value_str, "org.freedesktop.DBus")) | |
1088 | found_driver = true; | |
1089 | } | |
1090 | ||
1091 | if (c->type == BUS_MATCH_INTERFACE && streq_ptr(c->value_str, "org.freedesktop.DBus.Local")) | |
1092 | return BUS_MATCH_LOCAL; | |
1093 | ||
1094 | if (c->type == BUS_MATCH_PATH && streq_ptr(c->value_str, "/org/freedesktop/DBus/Local")) | |
1095 | return BUS_MATCH_LOCAL; | |
1096 | } | |
1097 | ||
1098 | return found_driver ? BUS_MATCH_DRIVER : BUS_MATCH_GENERIC; | |
1099 | ||
1100 | } |