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