Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include "alloc-util.h"
4 : #include "bus-internal.h"
5 : #include "bus-introspect.h"
6 : #include "bus-message.h"
7 : #include "bus-objects.h"
8 : #include "bus-signature.h"
9 : #include "bus-slot.h"
10 : #include "bus-type.h"
11 : #include "bus-util.h"
12 : #include "missing_capability.h"
13 : #include "set.h"
14 : #include "string-util.h"
15 : #include "strv.h"
16 :
17 39 : static int node_vtable_get_userdata(
18 : sd_bus *bus,
19 : const char *path,
20 : struct node_vtable *c,
21 : void **userdata,
22 : sd_bus_error *error) {
23 :
24 : sd_bus_slot *s;
25 39 : void *u, *found_u = NULL;
26 : int r;
27 :
28 39 : assert(bus);
29 39 : assert(path);
30 39 : assert(c);
31 :
32 39 : s = container_of(c, sd_bus_slot, node_vtable);
33 39 : u = s->userdata;
34 39 : if (c->find) {
35 0 : bus->current_slot = sd_bus_slot_ref(s);
36 0 : bus->current_userdata = u;
37 0 : r = c->find(bus, path, c->interface, u, &found_u, error);
38 0 : bus->current_userdata = NULL;
39 0 : bus->current_slot = sd_bus_slot_unref(s);
40 :
41 0 : if (r < 0)
42 0 : return r;
43 0 : if (sd_bus_error_is_set(error))
44 0 : return -sd_bus_error_get_errno(error);
45 0 : if (r == 0)
46 0 : return r;
47 : } else
48 39 : found_u = u;
49 :
50 39 : if (userdata)
51 31 : *userdata = found_u;
52 :
53 39 : return 1;
54 : }
55 :
56 13 : static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
57 13 : assert(p);
58 :
59 13 : return (uint8_t*) u + p->x.method.offset;
60 : }
61 :
62 34 : static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
63 34 : assert(p);
64 :
65 34 : return (uint8_t*) u + p->x.property.offset;
66 : }
67 :
68 6 : static int vtable_property_get_userdata(
69 : sd_bus *bus,
70 : const char *path,
71 : struct vtable_member *p,
72 : void **userdata,
73 : sd_bus_error *error) {
74 :
75 : void *u;
76 : int r;
77 :
78 6 : assert(bus);
79 6 : assert(path);
80 6 : assert(p);
81 6 : assert(userdata);
82 :
83 6 : r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
84 6 : if (r <= 0)
85 0 : return r;
86 6 : if (bus->nodes_modified)
87 0 : return 0;
88 :
89 6 : *userdata = vtable_property_convert_userdata(p->vtable, u);
90 6 : return 1;
91 : }
92 :
93 6 : static int add_enumerated_to_set(
94 : sd_bus *bus,
95 : const char *prefix,
96 : struct node_enumerator *first,
97 : Set *s,
98 : sd_bus_error *error) {
99 :
100 : struct node_enumerator *c;
101 : int r;
102 :
103 6 : assert(bus);
104 6 : assert(prefix);
105 6 : assert(s);
106 :
107 9 : LIST_FOREACH(enumerators, c, first) {
108 3 : char **children = NULL, **k;
109 : sd_bus_slot *slot;
110 :
111 3 : if (bus->nodes_modified)
112 0 : return 0;
113 :
114 3 : slot = container_of(c, sd_bus_slot, node_enumerator);
115 :
116 3 : bus->current_slot = sd_bus_slot_ref(slot);
117 3 : bus->current_userdata = slot->userdata;
118 3 : r = c->callback(bus, prefix, slot->userdata, &children, error);
119 3 : bus->current_userdata = NULL;
120 3 : bus->current_slot = sd_bus_slot_unref(slot);
121 :
122 3 : if (r < 0)
123 0 : return r;
124 3 : if (sd_bus_error_is_set(error))
125 0 : return -sd_bus_error_get_errno(error);
126 :
127 12 : STRV_FOREACH(k, children) {
128 9 : if (r < 0) {
129 0 : free(*k);
130 0 : continue;
131 : }
132 :
133 9 : if (!object_path_is_valid(*k)) {
134 0 : free(*k);
135 0 : r = -EINVAL;
136 0 : continue;
137 : }
138 :
139 9 : if (!object_path_startswith(*k, prefix)) {
140 0 : free(*k);
141 0 : continue;
142 : }
143 :
144 9 : r = set_consume(s, *k);
145 9 : if (r == -EEXIST)
146 0 : r = 0;
147 : }
148 :
149 3 : free(children);
150 3 : if (r < 0)
151 0 : return r;
152 : }
153 :
154 6 : return 0;
155 : }
156 :
157 : enum {
158 : /* if set, add_subtree() works recursively */
159 : CHILDREN_RECURSIVE = 1 << 0,
160 : /* if set, add_subtree() scans object-manager hierarchies recursively */
161 : CHILDREN_SUBHIERARCHIES = 1 << 1,
162 : };
163 :
164 6 : static int add_subtree_to_set(
165 : sd_bus *bus,
166 : const char *prefix,
167 : struct node *n,
168 : unsigned flags,
169 : Set *s,
170 : sd_bus_error *error) {
171 :
172 : struct node *i;
173 : int r;
174 :
175 6 : assert(bus);
176 6 : assert(prefix);
177 6 : assert(n);
178 6 : assert(s);
179 :
180 6 : r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
181 6 : if (r < 0)
182 0 : return r;
183 6 : if (bus->nodes_modified)
184 1 : return 0;
185 :
186 9 : LIST_FOREACH(siblings, i, n->child) {
187 : char *t;
188 :
189 4 : if (!object_path_startswith(i->path, prefix))
190 0 : continue;
191 :
192 4 : t = strdup(i->path);
193 4 : if (!t)
194 0 : return -ENOMEM;
195 :
196 4 : r = set_consume(s, t);
197 4 : if (r < 0 && r != -EEXIST)
198 0 : return r;
199 :
200 4 : if ((flags & CHILDREN_RECURSIVE) &&
201 1 : ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) {
202 0 : r = add_subtree_to_set(bus, prefix, i, flags, s, error);
203 0 : if (r < 0)
204 0 : return r;
205 0 : if (bus->nodes_modified)
206 0 : return 0;
207 : }
208 : }
209 :
210 5 : return 0;
211 : }
212 :
213 6 : static int get_child_nodes(
214 : sd_bus *bus,
215 : const char *prefix,
216 : struct node *n,
217 : unsigned flags,
218 : Set **_s,
219 : sd_bus_error *error) {
220 :
221 6 : Set *s = NULL;
222 : int r;
223 :
224 6 : assert(bus);
225 6 : assert(prefix);
226 6 : assert(n);
227 6 : assert(_s);
228 :
229 6 : s = set_new(&string_hash_ops);
230 6 : if (!s)
231 0 : return -ENOMEM;
232 :
233 6 : r = add_subtree_to_set(bus, prefix, n, flags, s, error);
234 6 : if (r < 0) {
235 0 : set_free_free(s);
236 0 : return r;
237 : }
238 :
239 6 : *_s = s;
240 6 : return 0;
241 : }
242 :
243 40 : static int node_callbacks_run(
244 : sd_bus *bus,
245 : sd_bus_message *m,
246 : struct node_callback *first,
247 : bool require_fallback,
248 : bool *found_object) {
249 :
250 : struct node_callback *c;
251 : int r;
252 :
253 40 : assert(bus);
254 40 : assert(m);
255 40 : assert(found_object);
256 :
257 40 : LIST_FOREACH(callbacks, c, first) {
258 1 : _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
259 : sd_bus_slot *slot;
260 :
261 1 : if (bus->nodes_modified)
262 0 : return 0;
263 :
264 1 : if (require_fallback && !c->is_fallback)
265 0 : continue;
266 :
267 1 : *found_object = true;
268 :
269 1 : if (c->last_iteration == bus->iteration_counter)
270 0 : continue;
271 :
272 1 : c->last_iteration = bus->iteration_counter;
273 :
274 1 : r = sd_bus_message_rewind(m, true);
275 1 : if (r < 0)
276 0 : return r;
277 :
278 1 : slot = container_of(c, sd_bus_slot, node_callback);
279 :
280 1 : bus->current_slot = sd_bus_slot_ref(slot);
281 1 : bus->current_handler = c->callback;
282 1 : bus->current_userdata = slot->userdata;
283 1 : r = c->callback(m, slot->userdata, &error_buffer);
284 1 : bus->current_userdata = NULL;
285 1 : bus->current_handler = NULL;
286 1 : bus->current_slot = sd_bus_slot_unref(slot);
287 :
288 1 : r = bus_maybe_reply_error(m, r, &error_buffer);
289 1 : if (r != 0)
290 1 : return r;
291 : }
292 :
293 39 : return 0;
294 : }
295 :
296 : #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
297 :
298 16 : static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
299 : uint64_t cap;
300 : int r;
301 :
302 16 : assert(bus);
303 16 : assert(m);
304 16 : assert(c);
305 :
306 : /* If the entire bus is trusted let's grant access */
307 16 : if (bus->trusted)
308 0 : return 0;
309 :
310 : /* If the member is marked UNPRIVILEGED let's grant access */
311 16 : if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
312 3 : return 0;
313 :
314 : /* Check have the caller has the requested capability
315 : * set. Note that the flags value contains the capability
316 : * number plus one, which we need to subtract here. We do this
317 : * so that we have 0 as special value for "default
318 : * capability". */
319 13 : cap = CAPABILITY_SHIFT(c->vtable->flags);
320 13 : if (cap == 0)
321 13 : cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
322 13 : if (cap == 0)
323 13 : cap = CAP_SYS_ADMIN;
324 : else
325 0 : cap--;
326 :
327 13 : r = sd_bus_query_sender_privilege(m, cap);
328 13 : if (r < 0)
329 0 : return r;
330 13 : if (r > 0)
331 13 : return 0;
332 :
333 0 : return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
334 : }
335 :
336 13 : static int method_callbacks_run(
337 : sd_bus *bus,
338 : sd_bus_message *m,
339 : struct vtable_member *c,
340 : bool require_fallback,
341 : bool *found_object) {
342 :
343 13 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
344 : const char *signature;
345 : void *u;
346 : int r;
347 :
348 13 : assert(bus);
349 13 : assert(m);
350 13 : assert(c);
351 13 : assert(found_object);
352 :
353 13 : if (require_fallback && !c->parent->is_fallback)
354 0 : return 0;
355 :
356 13 : r = check_access(bus, m, c, &error);
357 13 : if (r < 0)
358 0 : return bus_maybe_reply_error(m, r, &error);
359 :
360 13 : r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
361 13 : if (r <= 0)
362 0 : return bus_maybe_reply_error(m, r, &error);
363 13 : if (bus->nodes_modified)
364 0 : return 0;
365 :
366 13 : u = vtable_method_convert_userdata(c->vtable, u);
367 :
368 13 : *found_object = true;
369 :
370 13 : if (c->last_iteration == bus->iteration_counter)
371 0 : return 0;
372 :
373 13 : c->last_iteration = bus->iteration_counter;
374 :
375 13 : r = sd_bus_message_rewind(m, true);
376 13 : if (r < 0)
377 0 : return r;
378 :
379 13 : signature = sd_bus_message_get_signature(m, true);
380 13 : if (!signature)
381 0 : return -EINVAL;
382 :
383 13 : if (!streq(strempty(c->vtable->x.method.signature), signature))
384 1 : return sd_bus_reply_method_errorf(
385 : m,
386 : SD_BUS_ERROR_INVALID_ARGS,
387 : "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
388 1 : signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
389 :
390 : /* Keep track what the signature of the reply to this message
391 : * should be, so that this can be enforced when sealing the
392 : * reply. */
393 12 : m->enforced_reply_signature = strempty(c->vtable->x.method.result);
394 :
395 12 : if (c->vtable->x.method.handler) {
396 : sd_bus_slot *slot;
397 :
398 11 : slot = container_of(c->parent, sd_bus_slot, node_vtable);
399 :
400 11 : bus->current_slot = sd_bus_slot_ref(slot);
401 11 : bus->current_handler = c->vtable->x.method.handler;
402 11 : bus->current_userdata = u;
403 11 : r = c->vtable->x.method.handler(m, u, &error);
404 11 : bus->current_userdata = NULL;
405 11 : bus->current_handler = NULL;
406 11 : bus->current_slot = sd_bus_slot_unref(slot);
407 :
408 11 : return bus_maybe_reply_error(m, r, &error);
409 : }
410 :
411 : /* If the method callback is NULL, make this a successful NOP */
412 1 : r = sd_bus_reply_method_return(m, NULL);
413 1 : if (r < 0)
414 0 : return r;
415 :
416 1 : return 1;
417 : }
418 :
419 31 : static int invoke_property_get(
420 : sd_bus *bus,
421 : sd_bus_slot *slot,
422 : const sd_bus_vtable *v,
423 : const char *path,
424 : const char *interface,
425 : const char *property,
426 : sd_bus_message *reply,
427 : void *userdata,
428 : sd_bus_error *error) {
429 :
430 : const void *p;
431 : int r;
432 :
433 31 : assert(bus);
434 31 : assert(slot);
435 31 : assert(v);
436 31 : assert(path);
437 31 : assert(interface);
438 31 : assert(property);
439 31 : assert(reply);
440 :
441 31 : if (v->x.property.get) {
442 :
443 27 : bus->current_slot = sd_bus_slot_ref(slot);
444 27 : bus->current_userdata = userdata;
445 27 : r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
446 27 : bus->current_userdata = NULL;
447 27 : bus->current_slot = sd_bus_slot_unref(slot);
448 :
449 27 : if (r < 0)
450 0 : return r;
451 27 : if (sd_bus_error_is_set(error))
452 0 : return -sd_bus_error_get_errno(error);
453 27 : return r;
454 : }
455 :
456 : /* Automatic handling if no callback is defined. */
457 :
458 4 : if (streq(v->x.property.signature, "as"))
459 0 : return sd_bus_message_append_strv(reply, *(char***) userdata);
460 :
461 4 : assert(signature_is_single(v->x.property.signature, false));
462 4 : assert(bus_type_is_basic(v->x.property.signature[0]));
463 :
464 4 : switch (v->x.property.signature[0]) {
465 :
466 2 : case SD_BUS_TYPE_STRING:
467 : case SD_BUS_TYPE_SIGNATURE:
468 2 : p = strempty(*(char**) userdata);
469 2 : break;
470 :
471 0 : case SD_BUS_TYPE_OBJECT_PATH:
472 0 : p = *(char**) userdata;
473 0 : assert(p);
474 0 : break;
475 :
476 2 : default:
477 2 : p = userdata;
478 2 : break;
479 : }
480 :
481 4 : return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
482 : }
483 :
484 3 : static int invoke_property_set(
485 : sd_bus *bus,
486 : sd_bus_slot *slot,
487 : const sd_bus_vtable *v,
488 : const char *path,
489 : const char *interface,
490 : const char *property,
491 : sd_bus_message *value,
492 : void *userdata,
493 : sd_bus_error *error) {
494 :
495 : int r;
496 :
497 3 : assert(bus);
498 3 : assert(slot);
499 3 : assert(v);
500 3 : assert(path);
501 3 : assert(interface);
502 3 : assert(property);
503 3 : assert(value);
504 :
505 3 : if (v->x.property.set) {
506 :
507 1 : bus->current_slot = sd_bus_slot_ref(slot);
508 1 : bus->current_userdata = userdata;
509 1 : r = v->x.property.set(bus, path, interface, property, value, userdata, error);
510 1 : bus->current_userdata = NULL;
511 1 : bus->current_slot = sd_bus_slot_unref(slot);
512 :
513 1 : if (r < 0)
514 0 : return r;
515 1 : if (sd_bus_error_is_set(error))
516 0 : return -sd_bus_error_get_errno(error);
517 1 : return r;
518 : }
519 :
520 : /* Automatic handling if no callback is defined. */
521 :
522 2 : assert(signature_is_single(v->x.property.signature, false));
523 2 : assert(bus_type_is_basic(v->x.property.signature[0]));
524 :
525 2 : switch (v->x.property.signature[0]) {
526 :
527 1 : case SD_BUS_TYPE_STRING:
528 : case SD_BUS_TYPE_OBJECT_PATH:
529 : case SD_BUS_TYPE_SIGNATURE: {
530 : const char *p;
531 : char *n;
532 :
533 1 : r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
534 1 : if (r < 0)
535 0 : return r;
536 :
537 1 : n = strdup(p);
538 1 : if (!n)
539 0 : return -ENOMEM;
540 :
541 1 : free(*(char**) userdata);
542 1 : *(char**) userdata = n;
543 :
544 1 : break;
545 : }
546 :
547 1 : default:
548 1 : r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
549 1 : if (r < 0)
550 0 : return r;
551 :
552 1 : break;
553 : }
554 :
555 2 : return 1;
556 : }
557 :
558 6 : static int property_get_set_callbacks_run(
559 : sd_bus *bus,
560 : sd_bus_message *m,
561 : struct vtable_member *c,
562 : bool require_fallback,
563 : bool is_get,
564 : bool *found_object) {
565 :
566 6 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
567 6 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
568 : sd_bus_slot *slot;
569 6 : void *u = NULL;
570 : int r;
571 :
572 6 : assert(bus);
573 6 : assert(m);
574 6 : assert(c);
575 6 : assert(found_object);
576 :
577 6 : if (require_fallback && !c->parent->is_fallback)
578 0 : return 0;
579 :
580 6 : r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
581 6 : if (r <= 0)
582 0 : return bus_maybe_reply_error(m, r, &error);
583 6 : if (bus->nodes_modified)
584 0 : return 0;
585 :
586 6 : slot = container_of(c->parent, sd_bus_slot, node_vtable);
587 :
588 6 : *found_object = true;
589 :
590 6 : r = sd_bus_message_new_method_return(m, &reply);
591 6 : if (r < 0)
592 0 : return r;
593 :
594 6 : if (is_get) {
595 : /* Note that we do not protect against reexecution
596 : * here (using the last_iteration check, see below),
597 : * should the node tree have changed and we got called
598 : * again. We assume that property Get() calls are
599 : * ultimately without side-effects or if they aren't
600 : * then at least idempotent. */
601 :
602 3 : r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
603 3 : if (r < 0)
604 0 : return r;
605 :
606 : /* Note that we do not do an access check here. Read
607 : * access to properties is always unrestricted, since
608 : * PropertiesChanged signals broadcast contents
609 : * anyway. */
610 :
611 3 : r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
612 3 : if (r < 0)
613 0 : return bus_maybe_reply_error(m, r, &error);
614 :
615 3 : if (bus->nodes_modified)
616 0 : return 0;
617 :
618 3 : r = sd_bus_message_close_container(reply);
619 3 : if (r < 0)
620 0 : return r;
621 :
622 : } else {
623 3 : const char *signature = NULL;
624 3 : char type = 0;
625 :
626 3 : if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
627 0 : return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
628 :
629 : /* Avoid that we call the set routine more than once
630 : * if the processing of this message got restarted
631 : * because the node tree changed. */
632 3 : if (c->last_iteration == bus->iteration_counter)
633 0 : return 0;
634 :
635 3 : c->last_iteration = bus->iteration_counter;
636 :
637 3 : r = sd_bus_message_peek_type(m, &type, &signature);
638 3 : if (r < 0)
639 0 : return r;
640 :
641 3 : if (type != 'v')
642 0 : return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_SIGNATURE,
643 : "Incorrect signature when setting property '%s', expected 'v', got '%c'.",
644 : c->member, type);
645 3 : if (!streq(strempty(signature), strempty(c->vtable->x.property.signature)))
646 0 : return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS,
647 : "Incorrect parameters for property '%s', expected '%s', got '%s'.",
648 0 : c->member, strempty(c->vtable->x.property.signature), strempty(signature));
649 :
650 3 : r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
651 3 : if (r < 0)
652 0 : return r;
653 :
654 3 : r = check_access(bus, m, c, &error);
655 3 : if (r < 0)
656 0 : return bus_maybe_reply_error(m, r, &error);
657 :
658 3 : r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
659 3 : if (r < 0)
660 0 : return bus_maybe_reply_error(m, r, &error);
661 :
662 3 : if (bus->nodes_modified)
663 0 : return 0;
664 :
665 3 : r = sd_bus_message_exit_container(m);
666 3 : if (r < 0)
667 0 : return r;
668 : }
669 :
670 6 : r = sd_bus_send(bus, reply, NULL);
671 6 : if (r < 0)
672 0 : return r;
673 :
674 6 : return 1;
675 : }
676 :
677 28 : static int vtable_append_one_property(
678 : sd_bus *bus,
679 : sd_bus_message *reply,
680 : const char *path,
681 : struct node_vtable *c,
682 : const sd_bus_vtable *v,
683 : void *userdata,
684 : sd_bus_error *error) {
685 :
686 : sd_bus_slot *slot;
687 : int r;
688 :
689 28 : assert(bus);
690 28 : assert(reply);
691 28 : assert(path);
692 28 : assert(c);
693 28 : assert(v);
694 :
695 28 : r = sd_bus_message_open_container(reply, 'e', "sv");
696 28 : if (r < 0)
697 0 : return r;
698 :
699 28 : r = sd_bus_message_append(reply, "s", v->x.property.member);
700 28 : if (r < 0)
701 0 : return r;
702 :
703 28 : r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
704 28 : if (r < 0)
705 0 : return r;
706 :
707 28 : slot = container_of(c, sd_bus_slot, node_vtable);
708 :
709 28 : r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
710 28 : if (r < 0)
711 0 : return r;
712 28 : if (bus->nodes_modified)
713 0 : return 0;
714 :
715 28 : r = sd_bus_message_close_container(reply);
716 28 : if (r < 0)
717 0 : return r;
718 :
719 28 : r = sd_bus_message_close_container(reply);
720 28 : if (r < 0)
721 0 : return r;
722 :
723 28 : return 0;
724 : }
725 :
726 7 : static int vtable_append_all_properties(
727 : sd_bus *bus,
728 : sd_bus_message *reply,
729 : const char *path,
730 : struct node_vtable *c,
731 : void *userdata,
732 : sd_bus_error *error) {
733 :
734 : const sd_bus_vtable *v;
735 : int r;
736 :
737 7 : assert(bus);
738 7 : assert(reply);
739 7 : assert(path);
740 7 : assert(c);
741 :
742 7 : if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
743 0 : return 1;
744 :
745 7 : v = c->vtable;
746 62 : for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
747 55 : if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
748 24 : continue;
749 :
750 31 : if (v->flags & SD_BUS_VTABLE_HIDDEN)
751 0 : continue;
752 :
753 31 : if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
754 5 : continue;
755 :
756 26 : r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
757 26 : if (r < 0)
758 0 : return r;
759 26 : if (bus->nodes_modified)
760 0 : return 0;
761 : }
762 :
763 7 : return 1;
764 : }
765 :
766 3 : static int property_get_all_callbacks_run(
767 : sd_bus *bus,
768 : sd_bus_message *m,
769 : struct node_vtable *first,
770 : bool require_fallback,
771 : const char *iface,
772 : bool *found_object) {
773 :
774 3 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
775 : struct node_vtable *c;
776 : bool found_interface;
777 : int r;
778 :
779 3 : assert(bus);
780 3 : assert(m);
781 3 : assert(found_object);
782 :
783 3 : r = sd_bus_message_new_method_return(m, &reply);
784 3 : if (r < 0)
785 0 : return r;
786 :
787 3 : r = sd_bus_message_open_container(reply, 'a', "{sv}");
788 3 : if (r < 0)
789 0 : return r;
790 :
791 5 : found_interface = !iface ||
792 2 : streq(iface, "org.freedesktop.DBus.Properties") ||
793 7 : streq(iface, "org.freedesktop.DBus.Peer") ||
794 2 : streq(iface, "org.freedesktop.DBus.Introspectable");
795 :
796 6 : LIST_FOREACH(vtables, c, first) {
797 3 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
798 : void *u;
799 :
800 3 : if (require_fallback && !c->is_fallback)
801 0 : continue;
802 :
803 3 : r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
804 3 : if (r < 0)
805 0 : return bus_maybe_reply_error(m, r, &error);
806 3 : if (bus->nodes_modified)
807 0 : return 0;
808 3 : if (r == 0)
809 0 : continue;
810 :
811 3 : *found_object = true;
812 :
813 3 : if (iface && !streq(c->interface, iface))
814 1 : continue;
815 2 : found_interface = true;
816 :
817 2 : r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
818 2 : if (r < 0)
819 0 : return bus_maybe_reply_error(m, r, &error);
820 2 : if (bus->nodes_modified)
821 0 : return 0;
822 : }
823 :
824 3 : if (!*found_object)
825 1 : return 0;
826 :
827 2 : if (!found_interface) {
828 1 : r = sd_bus_reply_method_errorf(
829 : m,
830 : SD_BUS_ERROR_UNKNOWN_INTERFACE,
831 : "Unknown interface '%s'.", iface);
832 1 : if (r < 0)
833 0 : return r;
834 :
835 1 : return 1;
836 : }
837 :
838 1 : r = sd_bus_message_close_container(reply);
839 1 : if (r < 0)
840 0 : return r;
841 :
842 1 : r = sd_bus_send(bus, reply, NULL);
843 1 : if (r < 0)
844 0 : return r;
845 :
846 1 : return 1;
847 : }
848 :
849 11 : static int bus_node_exists(
850 : sd_bus *bus,
851 : struct node *n,
852 : const char *path,
853 : bool require_fallback) {
854 :
855 : struct node_vtable *c;
856 : struct node_callback *k;
857 : int r;
858 :
859 11 : assert(bus);
860 11 : assert(n);
861 11 : assert(path);
862 :
863 : /* Tests if there's anything attached directly to this node
864 : * for the specified path */
865 :
866 11 : if (!require_fallback && (n->enumerators || n->object_managers))
867 3 : return true;
868 :
869 8 : LIST_FOREACH(callbacks, k, n->callbacks) {
870 0 : if (require_fallback && !k->is_fallback)
871 0 : continue;
872 :
873 0 : return 1;
874 : }
875 :
876 8 : LIST_FOREACH(vtables, c, n->vtables) {
877 2 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
878 :
879 2 : if (require_fallback && !c->is_fallback)
880 0 : continue;
881 :
882 2 : r = node_vtable_get_userdata(bus, path, c, NULL, &error);
883 2 : if (r != 0)
884 2 : return r;
885 0 : if (bus->nodes_modified)
886 0 : return 0;
887 : }
888 :
889 6 : return 0;
890 : }
891 :
892 5 : int introspect_path(
893 : sd_bus *bus,
894 : const char *path,
895 : struct node *n,
896 : bool require_fallback,
897 : bool ignore_nodes_modified,
898 : bool *found_object,
899 : char **ret,
900 : sd_bus_error *error) {
901 :
902 5 : _cleanup_set_free_free_ Set *s = NULL;
903 5 : const char *previous_interface = NULL;
904 5 : _cleanup_(introspect_free) struct introspect intro = {};
905 : struct node_vtable *c;
906 : bool empty;
907 : int r;
908 :
909 5 : if (!n) {
910 1 : n = hashmap_get(bus->nodes, path);
911 1 : if (!n)
912 0 : return -ENOENT;
913 : }
914 :
915 5 : r = get_child_nodes(bus, path, n, 0, &s, error);
916 5 : if (r < 0)
917 0 : return r;
918 5 : if (bus->nodes_modified && !ignore_nodes_modified)
919 0 : return 0;
920 :
921 5 : r = introspect_begin(&intro, bus->trusted);
922 5 : if (r < 0)
923 0 : return r;
924 :
925 5 : r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
926 5 : if (r < 0)
927 0 : return r;
928 :
929 5 : empty = set_isempty(s);
930 :
931 11 : LIST_FOREACH(vtables, c, n->vtables) {
932 6 : if (require_fallback && !c->is_fallback)
933 0 : continue;
934 :
935 6 : r = node_vtable_get_userdata(bus, path, c, NULL, error);
936 6 : if (r < 0)
937 0 : return r;
938 6 : if (bus->nodes_modified && !ignore_nodes_modified)
939 0 : return 0;
940 6 : if (r == 0)
941 0 : continue;
942 :
943 6 : empty = false;
944 :
945 6 : if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
946 0 : continue;
947 :
948 6 : if (!streq_ptr(previous_interface, c->interface)) {
949 6 : if (previous_interface)
950 3 : fputs(" </interface>\n", intro.f);
951 :
952 6 : fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
953 : }
954 :
955 6 : r = introspect_write_interface(&intro, c->vtable);
956 6 : if (r < 0)
957 0 : return r;
958 :
959 6 : previous_interface = c->interface;
960 : }
961 :
962 5 : if (previous_interface)
963 3 : fputs(" </interface>\n", intro.f);
964 :
965 5 : if (empty) {
966 : /* Nothing?, let's see if we exist at all, and if not
967 : * refuse to do anything */
968 0 : r = bus_node_exists(bus, n, path, require_fallback);
969 0 : if (r <= 0)
970 0 : return r;
971 0 : if (bus->nodes_modified && !ignore_nodes_modified)
972 0 : return 0;
973 : }
974 :
975 5 : if (found_object)
976 4 : *found_object = true;
977 :
978 5 : r = introspect_write_child_nodes(&intro, s, path);
979 5 : if (r < 0)
980 0 : return r;
981 :
982 5 : r = introspect_finish(&intro, ret);
983 5 : if (r < 0)
984 0 : return r;
985 :
986 5 : return 1;
987 : }
988 :
989 4 : static int process_introspect(
990 : sd_bus *bus,
991 : sd_bus_message *m,
992 : struct node *n,
993 : bool require_fallback,
994 : bool *found_object) {
995 :
996 4 : _cleanup_free_ char *s = NULL;
997 4 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
998 4 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
999 : int r;
1000 :
1001 4 : assert(bus);
1002 4 : assert(m);
1003 4 : assert(n);
1004 4 : assert(found_object);
1005 :
1006 4 : r = introspect_path(bus, m->path, n, require_fallback, false, found_object, &s, &error);
1007 4 : if (r < 0)
1008 0 : return bus_maybe_reply_error(m, r, &error);
1009 4 : if (r == 0)
1010 : /* nodes_modified == true */
1011 0 : return 0;
1012 :
1013 4 : r = sd_bus_message_new_method_return(m, &reply);
1014 4 : if (r < 0)
1015 0 : return r;
1016 :
1017 4 : r = sd_bus_message_append(reply, "s", s);
1018 4 : if (r < 0)
1019 0 : return r;
1020 :
1021 4 : r = sd_bus_send(bus, reply, NULL);
1022 4 : if (r < 0)
1023 0 : return r;
1024 :
1025 4 : return 1;
1026 : }
1027 :
1028 9 : static int object_manager_serialize_path(
1029 : sd_bus *bus,
1030 : sd_bus_message *reply,
1031 : const char *prefix,
1032 : const char *path,
1033 : bool require_fallback,
1034 : sd_bus_error *error) {
1035 :
1036 9 : const char *previous_interface = NULL;
1037 9 : bool found_something = false;
1038 : struct node_vtable *i;
1039 : struct node *n;
1040 : int r;
1041 :
1042 9 : assert(bus);
1043 9 : assert(reply);
1044 9 : assert(prefix);
1045 9 : assert(path);
1046 9 : assert(error);
1047 :
1048 9 : n = hashmap_get(bus->nodes, prefix);
1049 9 : if (!n)
1050 2 : return 0;
1051 :
1052 10 : LIST_FOREACH(vtables, i, n->vtables) {
1053 : void *u;
1054 :
1055 3 : if (require_fallback && !i->is_fallback)
1056 0 : continue;
1057 :
1058 3 : r = node_vtable_get_userdata(bus, path, i, &u, error);
1059 3 : if (r < 0)
1060 0 : return r;
1061 3 : if (bus->nodes_modified)
1062 0 : return 0;
1063 3 : if (r == 0)
1064 0 : continue;
1065 :
1066 3 : if (!found_something) {
1067 :
1068 : /* Open the object part */
1069 :
1070 3 : r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1071 3 : if (r < 0)
1072 0 : return r;
1073 :
1074 3 : r = sd_bus_message_append(reply, "o", path);
1075 3 : if (r < 0)
1076 0 : return r;
1077 :
1078 3 : r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1079 3 : if (r < 0)
1080 0 : return r;
1081 :
1082 3 : r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
1083 3 : if (r < 0)
1084 0 : return r;
1085 :
1086 3 : r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
1087 3 : if (r < 0)
1088 0 : return r;
1089 :
1090 3 : r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
1091 3 : if (r < 0)
1092 0 : return r;
1093 :
1094 3 : r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1095 3 : if (r < 0)
1096 0 : return r;
1097 :
1098 3 : found_something = true;
1099 : }
1100 :
1101 3 : if (!streq_ptr(previous_interface, i->interface)) {
1102 :
1103 : /* Maybe close the previous interface part */
1104 :
1105 3 : if (previous_interface) {
1106 0 : r = sd_bus_message_close_container(reply);
1107 0 : if (r < 0)
1108 0 : return r;
1109 :
1110 0 : r = sd_bus_message_close_container(reply);
1111 0 : if (r < 0)
1112 0 : return r;
1113 : }
1114 :
1115 : /* Open the new interface part */
1116 :
1117 3 : r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1118 3 : if (r < 0)
1119 0 : return r;
1120 :
1121 3 : r = sd_bus_message_append(reply, "s", i->interface);
1122 3 : if (r < 0)
1123 0 : return r;
1124 :
1125 3 : r = sd_bus_message_open_container(reply, 'a', "{sv}");
1126 3 : if (r < 0)
1127 0 : return r;
1128 : }
1129 :
1130 3 : r = vtable_append_all_properties(bus, reply, path, i, u, error);
1131 3 : if (r < 0)
1132 0 : return r;
1133 3 : if (bus->nodes_modified)
1134 0 : return 0;
1135 :
1136 3 : previous_interface = i->interface;
1137 : }
1138 :
1139 7 : if (previous_interface) {
1140 3 : r = sd_bus_message_close_container(reply);
1141 3 : if (r < 0)
1142 0 : return r;
1143 :
1144 3 : r = sd_bus_message_close_container(reply);
1145 3 : if (r < 0)
1146 0 : return r;
1147 : }
1148 :
1149 7 : if (found_something) {
1150 3 : r = sd_bus_message_close_container(reply);
1151 3 : if (r < 0)
1152 0 : return r;
1153 :
1154 3 : r = sd_bus_message_close_container(reply);
1155 3 : if (r < 0)
1156 0 : return r;
1157 : }
1158 :
1159 7 : return 1;
1160 : }
1161 :
1162 3 : static int object_manager_serialize_path_and_fallbacks(
1163 : sd_bus *bus,
1164 : sd_bus_message *reply,
1165 : const char *path,
1166 : sd_bus_error *error) {
1167 :
1168 3 : _cleanup_free_ char *prefix = NULL;
1169 : size_t pl;
1170 : int r;
1171 :
1172 3 : assert(bus);
1173 3 : assert(reply);
1174 3 : assert(path);
1175 3 : assert(error);
1176 :
1177 : /* First, add all vtables registered for this path */
1178 3 : r = object_manager_serialize_path(bus, reply, path, path, false, error);
1179 3 : if (r < 0)
1180 0 : return r;
1181 3 : if (bus->nodes_modified)
1182 0 : return 0;
1183 :
1184 : /* Second, add fallback vtables registered for any of the prefixes */
1185 3 : pl = strlen(path);
1186 3 : assert(pl <= BUS_PATH_SIZE_MAX);
1187 3 : prefix = new(char, pl + 1);
1188 3 : if (!prefix)
1189 0 : return -ENOMEM;
1190 :
1191 9 : OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1192 6 : r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1193 6 : if (r < 0)
1194 0 : return r;
1195 6 : if (bus->nodes_modified)
1196 0 : return 0;
1197 : }
1198 :
1199 3 : return 0;
1200 : }
1201 :
1202 3 : static int process_get_managed_objects(
1203 : sd_bus *bus,
1204 : sd_bus_message *m,
1205 : struct node *n,
1206 : bool require_fallback,
1207 : bool *found_object) {
1208 :
1209 3 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1210 3 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1211 3 : _cleanup_set_free_free_ Set *s = NULL;
1212 : Iterator i;
1213 : char *path;
1214 : int r;
1215 :
1216 3 : assert(bus);
1217 3 : assert(m);
1218 3 : assert(n);
1219 3 : assert(found_object);
1220 :
1221 : /* Spec says, GetManagedObjects() is only implemented on the root of a
1222 : * sub-tree. Therefore, we require a registered object-manager on
1223 : * exactly the queried path, otherwise, we refuse to respond. */
1224 :
1225 3 : if (require_fallback || !n->object_managers)
1226 2 : return 0;
1227 :
1228 1 : r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
1229 1 : if (r < 0)
1230 0 : return bus_maybe_reply_error(m, r, &error);
1231 1 : if (bus->nodes_modified)
1232 0 : return 0;
1233 :
1234 1 : r = sd_bus_message_new_method_return(m, &reply);
1235 1 : if (r < 0)
1236 0 : return r;
1237 :
1238 1 : r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1239 1 : if (r < 0)
1240 0 : return r;
1241 :
1242 4 : SET_FOREACH(path, s, i) {
1243 3 : r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1244 3 : if (r < 0)
1245 0 : return bus_maybe_reply_error(m, r, &error);
1246 :
1247 3 : if (bus->nodes_modified)
1248 0 : return 0;
1249 : }
1250 :
1251 1 : r = sd_bus_message_close_container(reply);
1252 1 : if (r < 0)
1253 0 : return r;
1254 :
1255 1 : r = sd_bus_send(bus, reply, NULL);
1256 1 : if (r < 0)
1257 0 : return r;
1258 :
1259 1 : return 1;
1260 : }
1261 :
1262 43 : static int object_find_and_run(
1263 : sd_bus *bus,
1264 : sd_bus_message *m,
1265 : const char *p,
1266 : bool require_fallback,
1267 : bool *found_object) {
1268 :
1269 : struct node *n;
1270 : struct vtable_member vtable_key, *v;
1271 : int r;
1272 :
1273 43 : assert(bus);
1274 43 : assert(m);
1275 43 : assert(p);
1276 43 : assert(found_object);
1277 :
1278 43 : n = hashmap_get(bus->nodes, p);
1279 43 : if (!n)
1280 3 : return 0;
1281 :
1282 : /* First, try object callbacks */
1283 40 : r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1284 40 : if (r != 0)
1285 1 : return r;
1286 39 : if (bus->nodes_modified)
1287 0 : return 0;
1288 :
1289 39 : if (!m->interface || !m->member)
1290 0 : return 0;
1291 :
1292 : /* Then, look for a known method */
1293 39 : vtable_key.path = (char*) p;
1294 39 : vtable_key.interface = m->interface;
1295 39 : vtable_key.member = m->member;
1296 :
1297 39 : v = hashmap_get(bus->vtable_methods, &vtable_key);
1298 39 : if (v) {
1299 13 : r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1300 13 : if (r != 0)
1301 13 : return r;
1302 0 : if (bus->nodes_modified)
1303 0 : return 0;
1304 : }
1305 :
1306 : /* Then, look for a known property */
1307 26 : if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1308 9 : bool get = false;
1309 :
1310 9 : get = streq(m->member, "Get");
1311 :
1312 9 : if (get || streq(m->member, "Set")) {
1313 :
1314 6 : r = sd_bus_message_rewind(m, true);
1315 6 : if (r < 0)
1316 0 : return r;
1317 :
1318 6 : vtable_key.path = (char*) p;
1319 :
1320 6 : r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1321 6 : if (r < 0)
1322 0 : return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1323 :
1324 6 : v = hashmap_get(bus->vtable_properties, &vtable_key);
1325 6 : if (v) {
1326 6 : r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1327 6 : if (r != 0)
1328 6 : return r;
1329 : }
1330 :
1331 3 : } else if (streq(m->member, "GetAll")) {
1332 : const char *iface;
1333 :
1334 3 : r = sd_bus_message_rewind(m, true);
1335 3 : if (r < 0)
1336 2 : return r;
1337 :
1338 3 : r = sd_bus_message_read(m, "s", &iface);
1339 3 : if (r < 0)
1340 0 : return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1341 :
1342 3 : if (iface[0] == 0)
1343 1 : iface = NULL;
1344 :
1345 3 : r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1346 3 : if (r != 0)
1347 2 : return r;
1348 : }
1349 :
1350 17 : } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1351 :
1352 4 : if (!isempty(sd_bus_message_get_signature(m, true)))
1353 0 : return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1354 :
1355 4 : r = process_introspect(bus, m, n, require_fallback, found_object);
1356 4 : if (r != 0)
1357 4 : return r;
1358 :
1359 13 : } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1360 :
1361 3 : if (!isempty(sd_bus_message_get_signature(m, true)))
1362 0 : return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1363 :
1364 3 : r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1365 3 : if (r != 0)
1366 1 : return r;
1367 : }
1368 :
1369 13 : if (bus->nodes_modified)
1370 0 : return 0;
1371 :
1372 13 : if (!*found_object) {
1373 11 : r = bus_node_exists(bus, n, m->path, require_fallback);
1374 11 : if (r < 0)
1375 0 : return bus_maybe_reply_error(m, r, NULL);
1376 11 : if (bus->nodes_modified)
1377 0 : return 0;
1378 11 : if (r > 0)
1379 5 : *found_object = true;
1380 : }
1381 :
1382 13 : return 0;
1383 : }
1384 :
1385 72 : int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1386 72 : _cleanup_free_ char *prefix = NULL;
1387 : int r;
1388 : size_t pl;
1389 72 : bool found_object = false;
1390 :
1391 72 : assert(bus);
1392 72 : assert(m);
1393 :
1394 72 : if (bus->is_monitor)
1395 0 : return 0;
1396 :
1397 72 : if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1398 31 : return 0;
1399 :
1400 41 : if (hashmap_isempty(bus->nodes))
1401 6 : return 0;
1402 :
1403 : /* Never respond to broadcast messages */
1404 35 : if (bus->bus_client && !m->destination)
1405 0 : return 0;
1406 :
1407 35 : assert(m->path);
1408 35 : assert(m->member);
1409 :
1410 35 : pl = strlen(m->path);
1411 35 : assert(pl <= BUS_PATH_SIZE_MAX);
1412 35 : prefix = new(char, pl + 1);
1413 35 : if (!prefix)
1414 0 : return -ENOMEM;
1415 :
1416 : do {
1417 35 : bus->nodes_modified = false;
1418 :
1419 35 : r = object_find_and_run(bus, m, m->path, false, &found_object);
1420 35 : if (r != 0)
1421 22 : return r;
1422 :
1423 : /* Look for fallback prefixes */
1424 16 : OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1425 :
1426 8 : if (bus->nodes_modified)
1427 0 : break;
1428 :
1429 8 : r = object_find_and_run(bus, m, prefix, true, &found_object);
1430 8 : if (r != 0)
1431 5 : return r;
1432 : }
1433 :
1434 8 : } while (bus->nodes_modified);
1435 :
1436 8 : if (!found_object)
1437 6 : return 0;
1438 :
1439 4 : if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1440 2 : sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set")) {
1441 0 : const char *interface = NULL, *property = NULL;
1442 :
1443 0 : (void) sd_bus_message_rewind(m, true);
1444 0 : (void) sd_bus_message_read_basic(m, 's', &interface);
1445 0 : (void) sd_bus_message_read_basic(m, 's', &property);
1446 :
1447 0 : r = sd_bus_reply_method_errorf(
1448 : m,
1449 : SD_BUS_ERROR_UNKNOWN_PROPERTY,
1450 : "Unknown interface %s or property %s.", strnull(interface), strnull(property));
1451 : } else
1452 2 : r = sd_bus_reply_method_errorf(
1453 : m,
1454 : SD_BUS_ERROR_UNKNOWN_METHOD,
1455 : "Unknown method %s or interface %s.", m->member, m->interface);
1456 :
1457 2 : if (r < 0)
1458 0 : return r;
1459 :
1460 2 : return 1;
1461 : }
1462 :
1463 27 : static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1464 : struct node *n, *parent;
1465 : const char *e;
1466 27 : _cleanup_free_ char *s = NULL;
1467 : char *p;
1468 : int r;
1469 :
1470 27 : assert(bus);
1471 27 : assert(path);
1472 27 : assert(path[0] == '/');
1473 :
1474 27 : n = hashmap_get(bus->nodes, path);
1475 27 : if (n)
1476 10 : return n;
1477 :
1478 17 : r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1479 17 : if (r < 0)
1480 0 : return NULL;
1481 :
1482 17 : s = strdup(path);
1483 17 : if (!s)
1484 0 : return NULL;
1485 :
1486 17 : if (streq(path, "/"))
1487 7 : parent = NULL;
1488 : else {
1489 10 : e = strrchr(path, '/');
1490 10 : assert(e);
1491 :
1492 10 : p = strndupa(path, MAX(1, e - path));
1493 :
1494 10 : parent = bus_node_allocate(bus, p);
1495 10 : if (!parent)
1496 0 : return NULL;
1497 : }
1498 :
1499 17 : n = new0(struct node, 1);
1500 17 : if (!n)
1501 0 : return NULL;
1502 :
1503 17 : n->parent = parent;
1504 17 : n->path = TAKE_PTR(s);
1505 :
1506 17 : r = hashmap_put(bus->nodes, n->path, n);
1507 17 : if (r < 0) {
1508 0 : free(n->path);
1509 0 : return mfree(n);
1510 : }
1511 :
1512 17 : if (parent)
1513 10 : LIST_PREPEND(siblings, parent->child, n);
1514 :
1515 17 : return n;
1516 : }
1517 :
1518 34 : void bus_node_gc(sd_bus *b, struct node *n) {
1519 34 : assert(b);
1520 :
1521 34 : if (!n)
1522 7 : return;
1523 :
1524 27 : if (n->child ||
1525 25 : n->callbacks ||
1526 25 : n->vtables ||
1527 18 : n->enumerators ||
1528 17 : n->object_managers)
1529 10 : return;
1530 :
1531 17 : assert_se(hashmap_remove(b->nodes, n->path) == n);
1532 :
1533 17 : if (n->parent)
1534 10 : LIST_REMOVE(siblings, n->parent->child, n);
1535 :
1536 17 : free(n->path);
1537 17 : bus_node_gc(b, n->parent);
1538 17 : free(n);
1539 : }
1540 :
1541 4 : static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1542 : struct node *n;
1543 :
1544 4 : assert(bus);
1545 4 : assert(path);
1546 :
1547 4 : n = hashmap_get(bus->nodes, path);
1548 4 : if (!n) {
1549 4 : _cleanup_free_ char *prefix = NULL;
1550 : size_t pl;
1551 :
1552 4 : pl = strlen(path);
1553 4 : assert(pl <= BUS_PATH_SIZE_MAX);
1554 4 : prefix = new(char, pl + 1);
1555 4 : if (!prefix)
1556 0 : return -ENOMEM;
1557 :
1558 4 : OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1559 4 : n = hashmap_get(bus->nodes, prefix);
1560 4 : if (n)
1561 4 : break;
1562 : }
1563 : }
1564 :
1565 4 : while (n && !n->object_managers)
1566 0 : n = n->parent;
1567 :
1568 4 : if (out)
1569 4 : *out = n;
1570 4 : return !!n;
1571 : }
1572 :
1573 1 : static int bus_add_object(
1574 : sd_bus *bus,
1575 : sd_bus_slot **slot,
1576 : bool fallback,
1577 : const char *path,
1578 : sd_bus_message_handler_t callback,
1579 : void *userdata) {
1580 :
1581 : sd_bus_slot *s;
1582 : struct node *n;
1583 : int r;
1584 :
1585 1 : assert_return(bus, -EINVAL);
1586 1 : assert_return(bus = bus_resolve(bus), -ENOPKG);
1587 1 : assert_return(object_path_is_valid(path), -EINVAL);
1588 1 : assert_return(callback, -EINVAL);
1589 1 : assert_return(!bus_pid_changed(bus), -ECHILD);
1590 :
1591 1 : n = bus_node_allocate(bus, path);
1592 1 : if (!n)
1593 0 : return -ENOMEM;
1594 :
1595 1 : s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1596 1 : if (!s) {
1597 0 : r = -ENOMEM;
1598 0 : goto fail;
1599 : }
1600 :
1601 1 : s->node_callback.callback = callback;
1602 1 : s->node_callback.is_fallback = fallback;
1603 :
1604 1 : s->node_callback.node = n;
1605 1 : LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1606 1 : bus->nodes_modified = true;
1607 :
1608 1 : if (slot)
1609 0 : *slot = s;
1610 :
1611 1 : return 0;
1612 :
1613 0 : fail:
1614 0 : sd_bus_slot_unref(s);
1615 0 : bus_node_gc(bus, n);
1616 :
1617 0 : return r;
1618 : }
1619 :
1620 0 : _public_ int sd_bus_add_object(
1621 : sd_bus *bus,
1622 : sd_bus_slot **slot,
1623 : const char *path,
1624 : sd_bus_message_handler_t callback,
1625 : void *userdata) {
1626 :
1627 0 : return bus_add_object(bus, slot, false, path, callback, userdata);
1628 : }
1629 :
1630 1 : _public_ int sd_bus_add_fallback(
1631 : sd_bus *bus,
1632 : sd_bus_slot **slot,
1633 : const char *prefix,
1634 : sd_bus_message_handler_t callback,
1635 : void *userdata) {
1636 :
1637 1 : return bus_add_object(bus, slot, true, prefix, callback, userdata);
1638 : }
1639 :
1640 355 : static void vtable_member_hash_func(const struct vtable_member *m, struct siphash *state) {
1641 355 : assert(m);
1642 :
1643 355 : string_hash_func(m->path, state);
1644 355 : string_hash_func(m->interface, state);
1645 355 : string_hash_func(m->member, state);
1646 355 : }
1647 :
1648 200 : static int vtable_member_compare_func(const struct vtable_member *x, const struct vtable_member *y) {
1649 : int r;
1650 :
1651 200 : assert(x);
1652 200 : assert(y);
1653 :
1654 200 : r = strcmp(x->path, y->path);
1655 200 : if (r != 0)
1656 16 : return r;
1657 :
1658 184 : r = strcmp(x->interface, y->interface);
1659 184 : if (r != 0)
1660 32 : return r;
1661 :
1662 152 : return strcmp(x->member, y->member);
1663 : }
1664 :
1665 : DEFINE_PRIVATE_HASH_OPS(vtable_member_hash_ops, struct vtable_member, vtable_member_hash_func, vtable_member_compare_func);
1666 :
1667 : typedef enum {
1668 : NAMES_FIRST_PART = 1 << 0, /* first part of argument name list (input names). It is reset by names_are_valid() */
1669 : NAMES_PRESENT = 1 << 1, /* at least one argument name is present, so the names will checked.
1670 : This flag is set and used internally by names_are_valid(), but needs to be stored across calls for 2-parts list */
1671 : NAMES_SINGLE_PART = 1 << 2, /* argument name list consisting of a single part */
1672 : } names_flags;
1673 :
1674 116 : static bool names_are_valid(const char *signature, const char **names, names_flags *flags) {
1675 : int r;
1676 :
1677 116 : if ((*flags & NAMES_FIRST_PART || *flags & NAMES_SINGLE_PART) && **names != '\0')
1678 12 : *flags |= NAMES_PRESENT;
1679 :
1680 152 : for (;*flags & NAMES_PRESENT;) {
1681 : size_t l;
1682 :
1683 56 : if (!*signature)
1684 20 : break;
1685 :
1686 36 : r = signature_element_length(signature, &l);
1687 36 : if (r < 0)
1688 0 : return false;
1689 :
1690 36 : if (**names != '\0') {
1691 36 : if (!member_name_is_valid(*names))
1692 0 : return false;
1693 36 : *names += strlen(*names) + 1;
1694 0 : } else if (*flags & NAMES_PRESENT)
1695 0 : return false;
1696 :
1697 36 : signature += l;
1698 : }
1699 : /* let's check if there are more argument names specified than the signature allows */
1700 116 : if (*flags & NAMES_PRESENT && **names != '\0' && !(*flags & NAMES_FIRST_PART))
1701 0 : return false;
1702 116 : *flags &= ~NAMES_FIRST_PART;
1703 116 : return true;
1704 : }
1705 :
1706 : /* the current version of this struct is defined in sd-bus-vtable.h, but we need to list here the historical versions
1707 : to make sure the calling code is compatible with one of these */
1708 : struct sd_bus_vtable_221 {
1709 : uint8_t type:8;
1710 : uint64_t flags:56;
1711 : union {
1712 : struct {
1713 : size_t element_size;
1714 : } start;
1715 : struct {
1716 : const char *member;
1717 : const char *signature;
1718 : const char *result;
1719 : sd_bus_message_handler_t handler;
1720 : size_t offset;
1721 : } method;
1722 : struct {
1723 : const char *member;
1724 : const char *signature;
1725 : } signal;
1726 : struct {
1727 : const char *member;
1728 : const char *signature;
1729 : sd_bus_property_get_t get;
1730 : sd_bus_property_set_t set;
1731 : size_t offset;
1732 : } property;
1733 : } x;
1734 : };
1735 : /* Structure size up to v241 */
1736 : #define VTABLE_ELEMENT_SIZE_221 sizeof(struct sd_bus_vtable_221)
1737 :
1738 : /* Size of the structure when "features" field was added. If the structure definition is augmented, a copy of
1739 : * the structure definition will need to be made (similarly to the sd_bus_vtable_221 above), and this
1740 : * definition updated to refer to it. */
1741 : #define VTABLE_ELEMENT_SIZE_242 sizeof(struct sd_bus_vtable)
1742 :
1743 117 : static int vtable_features(const sd_bus_vtable *vtable) {
1744 117 : if (vtable[0].x.start.element_size < VTABLE_ELEMENT_SIZE_242 ||
1745 113 : !vtable[0].x.start.vtable_format_reference)
1746 4 : return 0;
1747 113 : return vtable[0].x.start.features;
1748 : }
1749 :
1750 117 : bool bus_vtable_has_names(const sd_bus_vtable *vtable) {
1751 117 : return vtable_features(vtable) & _SD_BUS_VTABLE_PARAM_NAMES;
1752 : }
1753 :
1754 419 : const sd_bus_vtable* bus_vtable_next(const sd_bus_vtable *vtable, const sd_bus_vtable *v) {
1755 419 : return (const sd_bus_vtable*) ((char*) v + vtable[0].x.start.element_size);
1756 : }
1757 :
1758 12 : static int add_object_vtable_internal(
1759 : sd_bus *bus,
1760 : sd_bus_slot **slot,
1761 : const char *path,
1762 : const char *interface,
1763 : const sd_bus_vtable *vtable,
1764 : bool fallback,
1765 : sd_bus_object_find_t find,
1766 : void *userdata) {
1767 :
1768 12 : sd_bus_slot *s = NULL;
1769 12 : struct node_vtable *i, *existing = NULL;
1770 : const sd_bus_vtable *v;
1771 : struct node *n;
1772 : int r;
1773 12 : const char *names = "";
1774 : names_flags nf;
1775 :
1776 12 : assert_return(bus, -EINVAL);
1777 12 : assert_return(bus = bus_resolve(bus), -ENOPKG);
1778 12 : assert_return(object_path_is_valid(path), -EINVAL);
1779 12 : assert_return(interface_name_is_valid(interface), -EINVAL);
1780 12 : assert_return(vtable, -EINVAL);
1781 12 : assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1782 12 : assert_return(vtable[0].x.start.element_size == VTABLE_ELEMENT_SIZE_221 ||
1783 : vtable[0].x.start.element_size >= VTABLE_ELEMENT_SIZE_242,
1784 : -EINVAL);
1785 12 : assert_return(!bus_pid_changed(bus), -ECHILD);
1786 12 : assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1787 : !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1788 : !streq(interface, "org.freedesktop.DBus.Peer") &&
1789 : !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1790 :
1791 12 : r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1792 12 : if (r < 0)
1793 0 : return r;
1794 :
1795 12 : r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1796 12 : if (r < 0)
1797 0 : return r;
1798 :
1799 12 : n = bus_node_allocate(bus, path);
1800 12 : if (!n)
1801 0 : return -ENOMEM;
1802 :
1803 19 : LIST_FOREACH(vtables, i, n->vtables) {
1804 7 : if (i->is_fallback != fallback) {
1805 0 : r = -EPROTOTYPE;
1806 0 : goto fail;
1807 : }
1808 :
1809 7 : if (streq(i->interface, interface)) {
1810 :
1811 0 : if (i->vtable == vtable) {
1812 0 : r = -EEXIST;
1813 0 : goto fail;
1814 : }
1815 :
1816 0 : existing = i;
1817 : }
1818 : }
1819 :
1820 12 : s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1821 12 : if (!s) {
1822 0 : r = -ENOMEM;
1823 0 : goto fail;
1824 : }
1825 :
1826 12 : s->node_vtable.is_fallback = fallback;
1827 12 : s->node_vtable.vtable = vtable;
1828 12 : s->node_vtable.find = find;
1829 :
1830 12 : s->node_vtable.interface = strdup(interface);
1831 12 : if (!s->node_vtable.interface) {
1832 0 : r = -ENOMEM;
1833 0 : goto fail;
1834 : }
1835 :
1836 12 : v = s->node_vtable.vtable;
1837 119 : for (v = bus_vtable_next(vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) {
1838 :
1839 107 : switch (v->type) {
1840 :
1841 52 : case _SD_BUS_VTABLE_METHOD: {
1842 : struct vtable_member *m;
1843 52 : nf = NAMES_FIRST_PART;
1844 :
1845 52 : if (bus_vtable_has_names(vtable))
1846 50 : names = strempty(v->x.method.names);
1847 :
1848 52 : if (!member_name_is_valid(v->x.method.member) ||
1849 52 : !signature_is_valid(strempty(v->x.method.signature), false) ||
1850 52 : !signature_is_valid(strempty(v->x.method.result), false) ||
1851 52 : !names_are_valid(strempty(v->x.method.signature), &names, &nf) ||
1852 52 : !names_are_valid(strempty(v->x.method.result), &names, &nf) ||
1853 52 : !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1854 52 : v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1855 0 : r = -EINVAL;
1856 0 : goto fail;
1857 : }
1858 :
1859 52 : m = new0(struct vtable_member, 1);
1860 52 : if (!m) {
1861 0 : r = -ENOMEM;
1862 0 : goto fail;
1863 : }
1864 :
1865 52 : m->parent = &s->node_vtable;
1866 52 : m->path = n->path;
1867 52 : m->interface = s->node_vtable.interface;
1868 52 : m->member = v->x.method.member;
1869 52 : m->vtable = v;
1870 :
1871 52 : r = hashmap_put(bus->vtable_methods, m, m);
1872 52 : if (r < 0) {
1873 0 : free(m);
1874 0 : goto fail;
1875 : }
1876 :
1877 52 : break;
1878 : }
1879 :
1880 18 : case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1881 :
1882 18 : if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1883 0 : r = -EINVAL;
1884 0 : goto fail;
1885 : }
1886 :
1887 18 : if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1888 0 : r = -EINVAL;
1889 0 : goto fail;
1890 : }
1891 :
1892 : _fallthrough_;
1893 : case _SD_BUS_VTABLE_PROPERTY: {
1894 : struct vtable_member *m;
1895 :
1896 43 : if (!member_name_is_valid(v->x.property.member) ||
1897 43 : !signature_is_single(v->x.property.signature, false) ||
1898 43 : !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1899 43 : (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1900 43 : (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1901 43 : ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1902 43 : (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1903 0 : r = -EINVAL;
1904 0 : goto fail;
1905 : }
1906 :
1907 43 : m = new0(struct vtable_member, 1);
1908 43 : if (!m) {
1909 0 : r = -ENOMEM;
1910 0 : goto fail;
1911 : }
1912 :
1913 43 : m->parent = &s->node_vtable;
1914 43 : m->path = n->path;
1915 43 : m->interface = s->node_vtable.interface;
1916 43 : m->member = v->x.property.member;
1917 43 : m->vtable = v;
1918 :
1919 43 : r = hashmap_put(bus->vtable_properties, m, m);
1920 43 : if (r < 0) {
1921 0 : free(m);
1922 0 : goto fail;
1923 : }
1924 :
1925 43 : break;
1926 : }
1927 :
1928 12 : case _SD_BUS_VTABLE_SIGNAL:
1929 12 : nf = NAMES_SINGLE_PART;
1930 :
1931 12 : if (bus_vtable_has_names(vtable))
1932 12 : names = strempty(v->x.signal.names);
1933 :
1934 12 : if (!member_name_is_valid(v->x.signal.member) ||
1935 12 : !signature_is_valid(strempty(v->x.signal.signature), false) ||
1936 12 : !names_are_valid(strempty(v->x.signal.signature), &names, &nf) ||
1937 12 : v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1938 0 : r = -EINVAL;
1939 0 : goto fail;
1940 : }
1941 :
1942 12 : break;
1943 :
1944 0 : default:
1945 0 : r = -EINVAL;
1946 0 : goto fail;
1947 : }
1948 : }
1949 :
1950 12 : s->node_vtable.node = n;
1951 12 : LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1952 12 : bus->nodes_modified = true;
1953 :
1954 12 : if (slot)
1955 0 : *slot = s;
1956 :
1957 12 : return 0;
1958 :
1959 0 : fail:
1960 0 : sd_bus_slot_unref(s);
1961 0 : bus_node_gc(bus, n);
1962 :
1963 0 : return r;
1964 : }
1965 :
1966 : /* This symbol exists solely to tell the linker that the "new" vtable format is used. */
1967 : _public_ const unsigned sd_bus_object_vtable_format = 242;
1968 :
1969 11 : _public_ int sd_bus_add_object_vtable(
1970 : sd_bus *bus,
1971 : sd_bus_slot **slot,
1972 : const char *path,
1973 : const char *interface,
1974 : const sd_bus_vtable *vtable,
1975 : void *userdata) {
1976 :
1977 11 : return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1978 : }
1979 :
1980 1 : _public_ int sd_bus_add_fallback_vtable(
1981 : sd_bus *bus,
1982 : sd_bus_slot **slot,
1983 : const char *prefix,
1984 : const char *interface,
1985 : const sd_bus_vtable *vtable,
1986 : sd_bus_object_find_t find,
1987 : void *userdata) {
1988 :
1989 1 : return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1990 : }
1991 :
1992 2 : _public_ int sd_bus_add_node_enumerator(
1993 : sd_bus *bus,
1994 : sd_bus_slot **slot,
1995 : const char *path,
1996 : sd_bus_node_enumerator_t callback,
1997 : void *userdata) {
1998 :
1999 : sd_bus_slot *s;
2000 : struct node *n;
2001 : int r;
2002 :
2003 2 : assert_return(bus, -EINVAL);
2004 2 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2005 2 : assert_return(object_path_is_valid(path), -EINVAL);
2006 2 : assert_return(callback, -EINVAL);
2007 2 : assert_return(!bus_pid_changed(bus), -ECHILD);
2008 :
2009 2 : n = bus_node_allocate(bus, path);
2010 2 : if (!n)
2011 0 : return -ENOMEM;
2012 :
2013 2 : s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
2014 2 : if (!s) {
2015 0 : r = -ENOMEM;
2016 0 : goto fail;
2017 : }
2018 :
2019 2 : s->node_enumerator.callback = callback;
2020 :
2021 2 : s->node_enumerator.node = n;
2022 2 : LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
2023 2 : bus->nodes_modified = true;
2024 :
2025 2 : if (slot)
2026 0 : *slot = s;
2027 :
2028 2 : return 0;
2029 :
2030 0 : fail:
2031 0 : sd_bus_slot_unref(s);
2032 0 : bus_node_gc(bus, n);
2033 :
2034 0 : return r;
2035 : }
2036 :
2037 4 : static int emit_properties_changed_on_interface(
2038 : sd_bus *bus,
2039 : const char *prefix,
2040 : const char *path,
2041 : const char *interface,
2042 : bool require_fallback,
2043 : bool *found_interface,
2044 : char **names) {
2045 :
2046 4 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2047 4 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2048 4 : bool has_invalidating = false, has_changing = false;
2049 4 : struct vtable_member key = {};
2050 : struct node_vtable *c;
2051 : struct node *n;
2052 : char **property;
2053 4 : void *u = NULL;
2054 : int r;
2055 :
2056 4 : assert(bus);
2057 4 : assert(prefix);
2058 4 : assert(path);
2059 4 : assert(interface);
2060 4 : assert(found_interface);
2061 :
2062 4 : n = hashmap_get(bus->nodes, prefix);
2063 4 : if (!n)
2064 0 : return 0;
2065 :
2066 4 : r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
2067 4 : if (r < 0)
2068 0 : return r;
2069 :
2070 4 : r = sd_bus_message_append(m, "s", interface);
2071 4 : if (r < 0)
2072 0 : return r;
2073 :
2074 4 : r = sd_bus_message_open_container(m, 'a', "{sv}");
2075 4 : if (r < 0)
2076 0 : return r;
2077 :
2078 4 : key.path = prefix;
2079 4 : key.interface = interface;
2080 :
2081 6 : LIST_FOREACH(vtables, c, n->vtables) {
2082 2 : if (require_fallback && !c->is_fallback)
2083 0 : continue;
2084 :
2085 2 : if (!streq(c->interface, interface))
2086 0 : continue;
2087 :
2088 2 : r = node_vtable_get_userdata(bus, path, c, &u, &error);
2089 2 : if (r < 0)
2090 0 : return r;
2091 2 : if (bus->nodes_modified)
2092 0 : return 0;
2093 2 : if (r == 0)
2094 0 : continue;
2095 :
2096 2 : *found_interface = true;
2097 :
2098 2 : if (names) {
2099 : /* If the caller specified a list of
2100 : * properties we include exactly those in the
2101 : * PropertiesChanged message */
2102 :
2103 2 : STRV_FOREACH(property, names) {
2104 : struct vtable_member *v;
2105 :
2106 1 : assert_return(member_name_is_valid(*property), -EINVAL);
2107 :
2108 1 : key.member = *property;
2109 1 : v = hashmap_get(bus->vtable_properties, &key);
2110 1 : if (!v)
2111 0 : return -ENOENT;
2112 :
2113 : /* If there are two vtables for the same
2114 : * interface, let's handle this property when
2115 : * we come to that vtable. */
2116 1 : if (c != v->parent)
2117 0 : continue;
2118 :
2119 1 : assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
2120 : v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
2121 :
2122 1 : assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
2123 :
2124 1 : if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2125 0 : has_invalidating = true;
2126 0 : continue;
2127 : }
2128 :
2129 1 : has_changing = true;
2130 :
2131 1 : r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
2132 1 : if (r < 0)
2133 0 : return r;
2134 1 : if (bus->nodes_modified)
2135 0 : return 0;
2136 : }
2137 : } else {
2138 : const sd_bus_vtable *v;
2139 :
2140 : /* If the caller specified no properties list
2141 : * we include all properties that are marked
2142 : * as changing in the message. */
2143 :
2144 1 : v = c->vtable;
2145 8 : for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
2146 7 : if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
2147 2 : continue;
2148 :
2149 5 : if (v->flags & SD_BUS_VTABLE_HIDDEN)
2150 0 : continue;
2151 :
2152 5 : if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2153 2 : has_invalidating = true;
2154 2 : continue;
2155 : }
2156 :
2157 3 : if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2158 2 : continue;
2159 :
2160 1 : has_changing = true;
2161 :
2162 1 : r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2163 1 : if (r < 0)
2164 0 : return r;
2165 1 : if (bus->nodes_modified)
2166 0 : return 0;
2167 : }
2168 : }
2169 : }
2170 :
2171 4 : if (!has_invalidating && !has_changing)
2172 2 : return 0;
2173 :
2174 2 : r = sd_bus_message_close_container(m);
2175 2 : if (r < 0)
2176 0 : return r;
2177 :
2178 2 : r = sd_bus_message_open_container(m, 'a', "s");
2179 2 : if (r < 0)
2180 0 : return r;
2181 :
2182 2 : if (has_invalidating) {
2183 2 : LIST_FOREACH(vtables, c, n->vtables) {
2184 1 : if (require_fallback && !c->is_fallback)
2185 0 : continue;
2186 :
2187 1 : if (!streq(c->interface, interface))
2188 0 : continue;
2189 :
2190 1 : r = node_vtable_get_userdata(bus, path, c, &u, &error);
2191 1 : if (r < 0)
2192 0 : return r;
2193 1 : if (bus->nodes_modified)
2194 0 : return 0;
2195 1 : if (r == 0)
2196 0 : continue;
2197 :
2198 1 : if (names) {
2199 0 : STRV_FOREACH(property, names) {
2200 : struct vtable_member *v;
2201 :
2202 0 : key.member = *property;
2203 0 : assert_se(v = hashmap_get(bus->vtable_properties, &key));
2204 0 : assert(c == v->parent);
2205 :
2206 0 : if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2207 0 : continue;
2208 :
2209 0 : r = sd_bus_message_append(m, "s", *property);
2210 0 : if (r < 0)
2211 0 : return r;
2212 : }
2213 : } else {
2214 : const sd_bus_vtable *v;
2215 :
2216 1 : v = c->vtable;
2217 8 : for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) {
2218 7 : if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
2219 2 : continue;
2220 :
2221 5 : if (v->flags & SD_BUS_VTABLE_HIDDEN)
2222 0 : continue;
2223 :
2224 5 : if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2225 3 : continue;
2226 :
2227 2 : r = sd_bus_message_append(m, "s", v->x.property.member);
2228 2 : if (r < 0)
2229 0 : return r;
2230 : }
2231 : }
2232 : }
2233 : }
2234 :
2235 2 : r = sd_bus_message_close_container(m);
2236 2 : if (r < 0)
2237 0 : return r;
2238 :
2239 2 : r = sd_bus_send(bus, m, NULL);
2240 2 : if (r < 0)
2241 0 : return r;
2242 :
2243 2 : return 1;
2244 : }
2245 :
2246 2 : _public_ int sd_bus_emit_properties_changed_strv(
2247 : sd_bus *bus,
2248 : const char *path,
2249 : const char *interface,
2250 : char **names) {
2251 :
2252 2 : _cleanup_free_ char *prefix = NULL;
2253 2 : bool found_interface = false;
2254 : size_t pl;
2255 : int r;
2256 :
2257 2 : assert_return(bus, -EINVAL);
2258 2 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2259 2 : assert_return(object_path_is_valid(path), -EINVAL);
2260 2 : assert_return(interface_name_is_valid(interface), -EINVAL);
2261 2 : assert_return(!bus_pid_changed(bus), -ECHILD);
2262 :
2263 2 : if (!BUS_IS_OPEN(bus->state))
2264 0 : return -ENOTCONN;
2265 :
2266 : /* A non-NULL but empty names list means nothing needs to be
2267 : generated. A NULL list OTOH indicates that all properties
2268 : that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2269 : included in the PropertiesChanged message. */
2270 2 : if (names && names[0] == NULL)
2271 0 : return 0;
2272 :
2273 4 : BUS_DONT_DESTROY(bus);
2274 :
2275 2 : pl = strlen(path);
2276 2 : assert(pl <= BUS_PATH_SIZE_MAX);
2277 2 : prefix = new(char, pl + 1);
2278 2 : if (!prefix)
2279 0 : return -ENOMEM;
2280 :
2281 : do {
2282 2 : bus->nodes_modified = false;
2283 :
2284 2 : r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2285 2 : if (r != 0)
2286 0 : return r;
2287 2 : if (bus->nodes_modified)
2288 0 : continue;
2289 :
2290 2 : OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2291 2 : r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2292 2 : if (r != 0)
2293 2 : return r;
2294 0 : if (bus->nodes_modified)
2295 0 : break;
2296 : }
2297 :
2298 0 : } while (bus->nodes_modified);
2299 :
2300 0 : return found_interface ? 0 : -ENOENT;
2301 : }
2302 :
2303 1 : _public_ int sd_bus_emit_properties_changed(
2304 : sd_bus *bus,
2305 : const char *path,
2306 : const char *interface,
2307 : const char *name, ...) {
2308 :
2309 : char **names;
2310 :
2311 1 : assert_return(bus, -EINVAL);
2312 1 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2313 1 : assert_return(object_path_is_valid(path), -EINVAL);
2314 1 : assert_return(interface_name_is_valid(interface), -EINVAL);
2315 1 : assert_return(!bus_pid_changed(bus), -ECHILD);
2316 :
2317 1 : if (!BUS_IS_OPEN(bus->state))
2318 0 : return -ENOTCONN;
2319 :
2320 1 : if (!name)
2321 0 : return 0;
2322 :
2323 1 : names = strv_from_stdarg_alloca(name);
2324 :
2325 1 : return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2326 : }
2327 :
2328 4 : static int object_added_append_all_prefix(
2329 : sd_bus *bus,
2330 : sd_bus_message *m,
2331 : Set *s,
2332 : const char *prefix,
2333 : const char *path,
2334 : bool require_fallback) {
2335 :
2336 4 : const char *previous_interface = NULL;
2337 : struct node_vtable *c;
2338 : struct node *n;
2339 : int r;
2340 :
2341 4 : assert(bus);
2342 4 : assert(m);
2343 4 : assert(s);
2344 4 : assert(prefix);
2345 4 : assert(path);
2346 :
2347 4 : n = hashmap_get(bus->nodes, prefix);
2348 4 : if (!n)
2349 1 : return 0;
2350 :
2351 4 : LIST_FOREACH(vtables, c, n->vtables) {
2352 1 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2353 1 : void *u = NULL;
2354 :
2355 1 : if (require_fallback && !c->is_fallback)
2356 0 : continue;
2357 :
2358 1 : r = node_vtable_get_userdata(bus, path, c, &u, &error);
2359 1 : if (r < 0)
2360 0 : return r;
2361 1 : if (bus->nodes_modified)
2362 0 : return 0;
2363 1 : if (r == 0)
2364 0 : continue;
2365 :
2366 1 : if (!streq_ptr(c->interface, previous_interface)) {
2367 : /* If a child-node already handled this interface, we
2368 : * skip it on any of its parents. The child vtables
2369 : * always fully override any conflicting vtables of
2370 : * any parent node. */
2371 1 : if (set_get(s, c->interface))
2372 0 : continue;
2373 :
2374 1 : r = set_put(s, c->interface);
2375 1 : if (r < 0)
2376 0 : return r;
2377 :
2378 1 : if (previous_interface) {
2379 0 : r = sd_bus_message_close_container(m);
2380 0 : if (r < 0)
2381 0 : return r;
2382 0 : r = sd_bus_message_close_container(m);
2383 0 : if (r < 0)
2384 0 : return r;
2385 : }
2386 :
2387 1 : r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2388 1 : if (r < 0)
2389 0 : return r;
2390 1 : r = sd_bus_message_append(m, "s", c->interface);
2391 1 : if (r < 0)
2392 0 : return r;
2393 1 : r = sd_bus_message_open_container(m, 'a', "{sv}");
2394 1 : if (r < 0)
2395 0 : return r;
2396 :
2397 1 : previous_interface = c->interface;
2398 : }
2399 :
2400 1 : r = vtable_append_all_properties(bus, m, path, c, u, &error);
2401 1 : if (r < 0)
2402 0 : return r;
2403 1 : if (bus->nodes_modified)
2404 0 : return 0;
2405 : }
2406 :
2407 3 : if (previous_interface) {
2408 1 : r = sd_bus_message_close_container(m);
2409 1 : if (r < 0)
2410 0 : return r;
2411 1 : r = sd_bus_message_close_container(m);
2412 1 : if (r < 0)
2413 0 : return r;
2414 : }
2415 :
2416 3 : return 0;
2417 : }
2418 :
2419 1 : static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2420 1 : _cleanup_set_free_ Set *s = NULL;
2421 1 : _cleanup_free_ char *prefix = NULL;
2422 : size_t pl;
2423 : int r;
2424 :
2425 1 : assert(bus);
2426 1 : assert(m);
2427 1 : assert(path);
2428 :
2429 : /*
2430 : * This appends all interfaces registered on path @path. We first add
2431 : * the builtin interfaces, which are always available and handled by
2432 : * sd-bus. Then, we add all interfaces registered on the exact node,
2433 : * followed by all fallback interfaces registered on any parent prefix.
2434 : *
2435 : * If an interface is registered multiple times on the same node with
2436 : * different vtables, we merge all the properties across all vtables.
2437 : * However, if a child node has the same interface registered as one of
2438 : * its parent nodes has as fallback, we make the child overwrite the
2439 : * parent instead of extending it. Therefore, we keep a "Set" of all
2440 : * handled interfaces during parent traversal, so we skip interfaces on
2441 : * a parent that were overwritten by a child.
2442 : */
2443 :
2444 1 : s = set_new(&string_hash_ops);
2445 1 : if (!s)
2446 0 : return -ENOMEM;
2447 :
2448 1 : r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2449 1 : if (r < 0)
2450 0 : return r;
2451 1 : r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2452 1 : if (r < 0)
2453 0 : return r;
2454 1 : r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2455 1 : if (r < 0)
2456 0 : return r;
2457 1 : r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2458 1 : if (r < 0)
2459 0 : return r;
2460 :
2461 1 : r = object_added_append_all_prefix(bus, m, s, path, path, false);
2462 1 : if (r < 0)
2463 0 : return r;
2464 1 : if (bus->nodes_modified)
2465 0 : return 0;
2466 :
2467 1 : pl = strlen(path);
2468 1 : assert(pl <= BUS_PATH_SIZE_MAX);
2469 1 : prefix = new(char, pl + 1);
2470 1 : if (!prefix)
2471 0 : return -ENOMEM;
2472 :
2473 4 : OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2474 3 : r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2475 3 : if (r < 0)
2476 0 : return r;
2477 3 : if (bus->nodes_modified)
2478 0 : return 0;
2479 : }
2480 :
2481 1 : return 0;
2482 : }
2483 :
2484 1 : _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2485 1 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2486 : struct node *object_manager;
2487 : int r;
2488 :
2489 : /*
2490 : * This emits an InterfacesAdded signal on the given path, by iterating
2491 : * all registered vtables and fallback vtables on the path. All
2492 : * properties are queried and included in the signal.
2493 : * This call is equivalent to sd_bus_emit_interfaces_added() with an
2494 : * explicit list of registered interfaces. However, unlike
2495 : * interfaces_added(), this call can figure out the list of supported
2496 : * interfaces itself. Furthermore, it properly adds the builtin
2497 : * org.freedesktop.DBus.* interfaces.
2498 : */
2499 :
2500 1 : assert_return(bus, -EINVAL);
2501 1 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2502 1 : assert_return(object_path_is_valid(path), -EINVAL);
2503 1 : assert_return(!bus_pid_changed(bus), -ECHILD);
2504 :
2505 1 : if (!BUS_IS_OPEN(bus->state))
2506 0 : return -ENOTCONN;
2507 :
2508 1 : r = bus_find_parent_object_manager(bus, &object_manager, path);
2509 1 : if (r < 0)
2510 0 : return r;
2511 1 : if (r == 0)
2512 0 : return -ESRCH;
2513 :
2514 2 : BUS_DONT_DESTROY(bus);
2515 :
2516 : do {
2517 1 : bus->nodes_modified = false;
2518 1 : m = sd_bus_message_unref(m);
2519 :
2520 1 : r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2521 1 : if (r < 0)
2522 0 : return r;
2523 :
2524 1 : r = sd_bus_message_append_basic(m, 'o', path);
2525 1 : if (r < 0)
2526 0 : return r;
2527 :
2528 1 : r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2529 1 : if (r < 0)
2530 0 : return r;
2531 :
2532 1 : r = object_added_append_all(bus, m, path);
2533 1 : if (r < 0)
2534 0 : return r;
2535 :
2536 1 : if (bus->nodes_modified)
2537 0 : continue;
2538 :
2539 1 : r = sd_bus_message_close_container(m);
2540 1 : if (r < 0)
2541 0 : return r;
2542 :
2543 1 : } while (bus->nodes_modified);
2544 :
2545 1 : return sd_bus_send(bus, m, NULL);
2546 : }
2547 :
2548 4 : static int object_removed_append_all_prefix(
2549 : sd_bus *bus,
2550 : sd_bus_message *m,
2551 : Set *s,
2552 : const char *prefix,
2553 : const char *path,
2554 : bool require_fallback) {
2555 :
2556 4 : const char *previous_interface = NULL;
2557 : struct node_vtable *c;
2558 : struct node *n;
2559 : int r;
2560 :
2561 4 : assert(bus);
2562 4 : assert(m);
2563 4 : assert(s);
2564 4 : assert(prefix);
2565 4 : assert(path);
2566 :
2567 4 : n = hashmap_get(bus->nodes, prefix);
2568 4 : if (!n)
2569 1 : return 0;
2570 :
2571 4 : LIST_FOREACH(vtables, c, n->vtables) {
2572 1 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2573 1 : void *u = NULL;
2574 :
2575 1 : if (require_fallback && !c->is_fallback)
2576 0 : continue;
2577 1 : if (streq_ptr(c->interface, previous_interface))
2578 0 : continue;
2579 :
2580 : /* If a child-node already handled this interface, we
2581 : * skip it on any of its parents. The child vtables
2582 : * always fully override any conflicting vtables of
2583 : * any parent node. */
2584 1 : if (set_get(s, c->interface))
2585 0 : continue;
2586 :
2587 1 : r = node_vtable_get_userdata(bus, path, c, &u, &error);
2588 1 : if (r < 0)
2589 0 : return r;
2590 1 : if (bus->nodes_modified)
2591 0 : return 0;
2592 1 : if (r == 0)
2593 0 : continue;
2594 :
2595 1 : r = set_put(s, c->interface);
2596 1 : if (r < 0)
2597 0 : return r;
2598 :
2599 1 : r = sd_bus_message_append(m, "s", c->interface);
2600 1 : if (r < 0)
2601 0 : return r;
2602 :
2603 1 : previous_interface = c->interface;
2604 : }
2605 :
2606 3 : return 0;
2607 : }
2608 :
2609 1 : static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2610 1 : _cleanup_set_free_ Set *s = NULL;
2611 1 : _cleanup_free_ char *prefix = NULL;
2612 : size_t pl;
2613 : int r;
2614 :
2615 1 : assert(bus);
2616 1 : assert(m);
2617 1 : assert(path);
2618 :
2619 : /* see sd_bus_emit_object_added() for details */
2620 :
2621 1 : s = set_new(&string_hash_ops);
2622 1 : if (!s)
2623 0 : return -ENOMEM;
2624 :
2625 1 : r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2626 1 : if (r < 0)
2627 0 : return r;
2628 1 : r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2629 1 : if (r < 0)
2630 0 : return r;
2631 1 : r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2632 1 : if (r < 0)
2633 0 : return r;
2634 1 : r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2635 1 : if (r < 0)
2636 0 : return r;
2637 :
2638 1 : r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2639 1 : if (r < 0)
2640 0 : return r;
2641 1 : if (bus->nodes_modified)
2642 0 : return 0;
2643 :
2644 1 : pl = strlen(path);
2645 1 : assert(pl <= BUS_PATH_SIZE_MAX);
2646 1 : prefix = new(char, pl + 1);
2647 1 : if (!prefix)
2648 0 : return -ENOMEM;
2649 :
2650 4 : OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2651 3 : r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2652 3 : if (r < 0)
2653 0 : return r;
2654 3 : if (bus->nodes_modified)
2655 0 : return 0;
2656 : }
2657 :
2658 1 : return 0;
2659 : }
2660 :
2661 1 : _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2662 1 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2663 : struct node *object_manager;
2664 : int r;
2665 :
2666 : /*
2667 : * This is like sd_bus_emit_object_added(), but emits an
2668 : * InterfacesRemoved signal on the given path. This only includes any
2669 : * registered interfaces but skips the properties. Note that this will
2670 : * call into the find() callbacks of any registered vtable. Therefore,
2671 : * you must call this function before destroying/unlinking your object.
2672 : * Otherwise, the list of interfaces will be incomplete. However, note
2673 : * that this will *NOT* call into any property callback. Therefore, the
2674 : * object might be in an "destructed" state, as long as we can find it.
2675 : */
2676 :
2677 1 : assert_return(bus, -EINVAL);
2678 1 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2679 1 : assert_return(object_path_is_valid(path), -EINVAL);
2680 1 : assert_return(!bus_pid_changed(bus), -ECHILD);
2681 :
2682 1 : if (!BUS_IS_OPEN(bus->state))
2683 0 : return -ENOTCONN;
2684 :
2685 1 : r = bus_find_parent_object_manager(bus, &object_manager, path);
2686 1 : if (r < 0)
2687 0 : return r;
2688 1 : if (r == 0)
2689 0 : return -ESRCH;
2690 :
2691 2 : BUS_DONT_DESTROY(bus);
2692 :
2693 : do {
2694 1 : bus->nodes_modified = false;
2695 1 : m = sd_bus_message_unref(m);
2696 :
2697 1 : r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2698 1 : if (r < 0)
2699 0 : return r;
2700 :
2701 1 : r = sd_bus_message_append_basic(m, 'o', path);
2702 1 : if (r < 0)
2703 0 : return r;
2704 :
2705 1 : r = sd_bus_message_open_container(m, 'a', "s");
2706 1 : if (r < 0)
2707 0 : return r;
2708 :
2709 1 : r = object_removed_append_all(bus, m, path);
2710 1 : if (r < 0)
2711 0 : return r;
2712 :
2713 1 : if (bus->nodes_modified)
2714 0 : continue;
2715 :
2716 1 : r = sd_bus_message_close_container(m);
2717 1 : if (r < 0)
2718 0 : return r;
2719 :
2720 1 : } while (bus->nodes_modified);
2721 :
2722 1 : return sd_bus_send(bus, m, NULL);
2723 : }
2724 :
2725 3 : static int interfaces_added_append_one_prefix(
2726 : sd_bus *bus,
2727 : sd_bus_message *m,
2728 : const char *prefix,
2729 : const char *path,
2730 : const char *interface,
2731 : bool require_fallback) {
2732 :
2733 3 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2734 3 : bool found_interface = false;
2735 : struct node_vtable *c;
2736 : struct node *n;
2737 3 : void *u = NULL;
2738 : int r;
2739 :
2740 3 : assert(bus);
2741 3 : assert(m);
2742 3 : assert(prefix);
2743 3 : assert(path);
2744 3 : assert(interface);
2745 :
2746 3 : n = hashmap_get(bus->nodes, prefix);
2747 3 : if (!n)
2748 1 : return 0;
2749 :
2750 3 : LIST_FOREACH(vtables, c, n->vtables) {
2751 1 : if (require_fallback && !c->is_fallback)
2752 0 : continue;
2753 :
2754 1 : if (!streq(c->interface, interface))
2755 0 : continue;
2756 :
2757 1 : r = node_vtable_get_userdata(bus, path, c, &u, &error);
2758 1 : if (r < 0)
2759 0 : return r;
2760 1 : if (bus->nodes_modified)
2761 0 : return 0;
2762 1 : if (r == 0)
2763 0 : continue;
2764 :
2765 1 : if (!found_interface) {
2766 1 : r = sd_bus_message_append_basic(m, 's', interface);
2767 1 : if (r < 0)
2768 0 : return r;
2769 :
2770 1 : r = sd_bus_message_open_container(m, 'a', "{sv}");
2771 1 : if (r < 0)
2772 0 : return r;
2773 :
2774 1 : found_interface = true;
2775 : }
2776 :
2777 1 : r = vtable_append_all_properties(bus, m, path, c, u, &error);
2778 1 : if (r < 0)
2779 0 : return r;
2780 1 : if (bus->nodes_modified)
2781 0 : return 0;
2782 : }
2783 :
2784 2 : if (found_interface) {
2785 1 : r = sd_bus_message_close_container(m);
2786 1 : if (r < 0)
2787 0 : return r;
2788 : }
2789 :
2790 2 : return found_interface;
2791 : }
2792 :
2793 1 : static int interfaces_added_append_one(
2794 : sd_bus *bus,
2795 : sd_bus_message *m,
2796 : const char *path,
2797 : const char *interface) {
2798 :
2799 1 : _cleanup_free_ char *prefix = NULL;
2800 : size_t pl;
2801 : int r;
2802 :
2803 1 : assert(bus);
2804 1 : assert(m);
2805 1 : assert(path);
2806 1 : assert(interface);
2807 :
2808 1 : r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2809 1 : if (r != 0)
2810 0 : return r;
2811 1 : if (bus->nodes_modified)
2812 0 : return 0;
2813 :
2814 1 : pl = strlen(path);
2815 1 : assert(pl <= BUS_PATH_SIZE_MAX);
2816 1 : prefix = new(char, pl + 1);
2817 1 : if (!prefix)
2818 0 : return -ENOMEM;
2819 :
2820 2 : OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2821 2 : r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2822 2 : if (r != 0)
2823 1 : return r;
2824 1 : if (bus->nodes_modified)
2825 0 : return 0;
2826 : }
2827 :
2828 0 : return -ENOENT;
2829 : }
2830 :
2831 1 : _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2832 1 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2833 : struct node *object_manager;
2834 : char **i;
2835 : int r;
2836 :
2837 1 : assert_return(bus, -EINVAL);
2838 1 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2839 1 : assert_return(object_path_is_valid(path), -EINVAL);
2840 1 : assert_return(!bus_pid_changed(bus), -ECHILD);
2841 :
2842 1 : if (!BUS_IS_OPEN(bus->state))
2843 0 : return -ENOTCONN;
2844 :
2845 1 : if (strv_isempty(interfaces))
2846 0 : return 0;
2847 :
2848 1 : r = bus_find_parent_object_manager(bus, &object_manager, path);
2849 1 : if (r < 0)
2850 0 : return r;
2851 1 : if (r == 0)
2852 0 : return -ESRCH;
2853 :
2854 2 : BUS_DONT_DESTROY(bus);
2855 :
2856 : do {
2857 1 : bus->nodes_modified = false;
2858 1 : m = sd_bus_message_unref(m);
2859 :
2860 1 : r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2861 1 : if (r < 0)
2862 0 : return r;
2863 :
2864 1 : r = sd_bus_message_append_basic(m, 'o', path);
2865 1 : if (r < 0)
2866 0 : return r;
2867 :
2868 1 : r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2869 1 : if (r < 0)
2870 0 : return r;
2871 :
2872 2 : STRV_FOREACH(i, interfaces) {
2873 1 : assert_return(interface_name_is_valid(*i), -EINVAL);
2874 :
2875 1 : r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2876 1 : if (r < 0)
2877 0 : return r;
2878 :
2879 1 : r = interfaces_added_append_one(bus, m, path, *i);
2880 1 : if (r < 0)
2881 0 : return r;
2882 :
2883 1 : if (bus->nodes_modified)
2884 0 : break;
2885 :
2886 1 : r = sd_bus_message_close_container(m);
2887 1 : if (r < 0)
2888 0 : return r;
2889 : }
2890 :
2891 1 : if (bus->nodes_modified)
2892 0 : continue;
2893 :
2894 1 : r = sd_bus_message_close_container(m);
2895 1 : if (r < 0)
2896 0 : return r;
2897 :
2898 1 : } while (bus->nodes_modified);
2899 :
2900 1 : return sd_bus_send(bus, m, NULL);
2901 : }
2902 :
2903 1 : _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2904 : char **interfaces;
2905 :
2906 1 : assert_return(bus, -EINVAL);
2907 1 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2908 1 : assert_return(object_path_is_valid(path), -EINVAL);
2909 1 : assert_return(!bus_pid_changed(bus), -ECHILD);
2910 :
2911 1 : if (!BUS_IS_OPEN(bus->state))
2912 0 : return -ENOTCONN;
2913 :
2914 1 : interfaces = strv_from_stdarg_alloca(interface);
2915 :
2916 1 : return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2917 : }
2918 :
2919 1 : _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2920 1 : _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2921 : struct node *object_manager;
2922 : int r;
2923 :
2924 1 : assert_return(bus, -EINVAL);
2925 1 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2926 1 : assert_return(object_path_is_valid(path), -EINVAL);
2927 1 : assert_return(!bus_pid_changed(bus), -ECHILD);
2928 :
2929 1 : if (!BUS_IS_OPEN(bus->state))
2930 0 : return -ENOTCONN;
2931 :
2932 1 : if (strv_isempty(interfaces))
2933 0 : return 0;
2934 :
2935 1 : r = bus_find_parent_object_manager(bus, &object_manager, path);
2936 1 : if (r < 0)
2937 0 : return r;
2938 1 : if (r == 0)
2939 0 : return -ESRCH;
2940 :
2941 1 : r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2942 1 : if (r < 0)
2943 0 : return r;
2944 :
2945 1 : r = sd_bus_message_append_basic(m, 'o', path);
2946 1 : if (r < 0)
2947 0 : return r;
2948 :
2949 1 : r = sd_bus_message_append_strv(m, interfaces);
2950 1 : if (r < 0)
2951 0 : return r;
2952 :
2953 1 : return sd_bus_send(bus, m, NULL);
2954 : }
2955 :
2956 1 : _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2957 : char **interfaces;
2958 :
2959 1 : assert_return(bus, -EINVAL);
2960 1 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2961 1 : assert_return(object_path_is_valid(path), -EINVAL);
2962 1 : assert_return(!bus_pid_changed(bus), -ECHILD);
2963 :
2964 1 : if (!BUS_IS_OPEN(bus->state))
2965 0 : return -ENOTCONN;
2966 :
2967 1 : interfaces = strv_from_stdarg_alloca(interface);
2968 :
2969 1 : return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2970 : }
2971 :
2972 2 : _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2973 : sd_bus_slot *s;
2974 : struct node *n;
2975 : int r;
2976 :
2977 2 : assert_return(bus, -EINVAL);
2978 2 : assert_return(bus = bus_resolve(bus), -ENOPKG);
2979 2 : assert_return(object_path_is_valid(path), -EINVAL);
2980 2 : assert_return(!bus_pid_changed(bus), -ECHILD);
2981 :
2982 2 : n = bus_node_allocate(bus, path);
2983 2 : if (!n)
2984 0 : return -ENOMEM;
2985 :
2986 2 : s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2987 2 : if (!s) {
2988 0 : r = -ENOMEM;
2989 0 : goto fail;
2990 : }
2991 :
2992 2 : s->node_object_manager.node = n;
2993 2 : LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2994 2 : bus->nodes_modified = true;
2995 :
2996 2 : if (slot)
2997 0 : *slot = s;
2998 :
2999 2 : return 0;
3000 :
3001 0 : fail:
3002 0 : sd_bus_slot_unref(s);
3003 0 : bus_node_gc(bus, n);
3004 :
3005 0 : return r;
3006 : }
|