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