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