]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-objects.c
Add SPDX license identifiers to source files under the LGPL
[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 free(n);
1480 return NULL;
1481 }
1482
1483 if (parent)
1484 LIST_PREPEND(siblings, parent->child, n);
1485
1486 return n;
1487 }
1488
1489 void bus_node_gc(sd_bus *b, struct node *n) {
1490 assert(b);
1491
1492 if (!n)
1493 return;
1494
1495 if (n->child ||
1496 n->callbacks ||
1497 n->vtables ||
1498 n->enumerators ||
1499 n->object_managers)
1500 return;
1501
1502 assert_se(hashmap_remove(b->nodes, n->path) == n);
1503
1504 if (n->parent)
1505 LIST_REMOVE(siblings, n->parent->child, n);
1506
1507 free(n->path);
1508 bus_node_gc(b, n->parent);
1509 free(n);
1510 }
1511
1512 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1513 struct node *n;
1514
1515 assert(bus);
1516 assert(path);
1517
1518 n = hashmap_get(bus->nodes, path);
1519 if (!n) {
1520 char *prefix;
1521
1522 prefix = alloca(strlen(path) + 1);
1523 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1524 n = hashmap_get(bus->nodes, prefix);
1525 if (n)
1526 break;
1527 }
1528 }
1529
1530 while (n && !n->object_managers)
1531 n = n->parent;
1532
1533 if (out)
1534 *out = n;
1535 return !!n;
1536 }
1537
1538 static int bus_add_object(
1539 sd_bus *bus,
1540 sd_bus_slot **slot,
1541 bool fallback,
1542 const char *path,
1543 sd_bus_message_handler_t callback,
1544 void *userdata) {
1545
1546 sd_bus_slot *s;
1547 struct node *n;
1548 int r;
1549
1550 assert_return(bus, -EINVAL);
1551 assert_return(object_path_is_valid(path), -EINVAL);
1552 assert_return(callback, -EINVAL);
1553 assert_return(!bus_pid_changed(bus), -ECHILD);
1554
1555 n = bus_node_allocate(bus, path);
1556 if (!n)
1557 return -ENOMEM;
1558
1559 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1560 if (!s) {
1561 r = -ENOMEM;
1562 goto fail;
1563 }
1564
1565 s->node_callback.callback = callback;
1566 s->node_callback.is_fallback = fallback;
1567
1568 s->node_callback.node = n;
1569 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1570 bus->nodes_modified = true;
1571
1572 if (slot)
1573 *slot = s;
1574
1575 return 0;
1576
1577 fail:
1578 sd_bus_slot_unref(s);
1579 bus_node_gc(bus, n);
1580
1581 return r;
1582 }
1583
1584 _public_ int sd_bus_add_object(
1585 sd_bus *bus,
1586 sd_bus_slot **slot,
1587 const char *path,
1588 sd_bus_message_handler_t callback,
1589 void *userdata) {
1590
1591 return bus_add_object(bus, slot, false, path, callback, userdata);
1592 }
1593
1594 _public_ int sd_bus_add_fallback(
1595 sd_bus *bus,
1596 sd_bus_slot **slot,
1597 const char *prefix,
1598 sd_bus_message_handler_t callback,
1599 void *userdata) {
1600
1601 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1602 }
1603
1604 static void vtable_member_hash_func(const void *a, struct siphash *state) {
1605 const struct vtable_member *m = a;
1606
1607 assert(m);
1608
1609 string_hash_func(m->path, state);
1610 string_hash_func(m->interface, state);
1611 string_hash_func(m->member, state);
1612 }
1613
1614 static int vtable_member_compare_func(const void *a, const void *b) {
1615 const struct vtable_member *x = a, *y = b;
1616 int r;
1617
1618 assert(x);
1619 assert(y);
1620
1621 r = strcmp(x->path, y->path);
1622 if (r != 0)
1623 return r;
1624
1625 r = strcmp(x->interface, y->interface);
1626 if (r != 0)
1627 return r;
1628
1629 return strcmp(x->member, y->member);
1630 }
1631
1632 static const struct hash_ops vtable_member_hash_ops = {
1633 .hash = vtable_member_hash_func,
1634 .compare = vtable_member_compare_func
1635 };
1636
1637 static int add_object_vtable_internal(
1638 sd_bus *bus,
1639 sd_bus_slot **slot,
1640 const char *path,
1641 const char *interface,
1642 const sd_bus_vtable *vtable,
1643 bool fallback,
1644 sd_bus_object_find_t find,
1645 void *userdata) {
1646
1647 sd_bus_slot *s = NULL;
1648 struct node_vtable *i, *existing = NULL;
1649 const sd_bus_vtable *v;
1650 struct node *n;
1651 int r;
1652
1653 assert_return(bus, -EINVAL);
1654 assert_return(object_path_is_valid(path), -EINVAL);
1655 assert_return(interface_name_is_valid(interface), -EINVAL);
1656 assert_return(vtable, -EINVAL);
1657 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1658 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1659 assert_return(!bus_pid_changed(bus), -ECHILD);
1660 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1661 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1662 !streq(interface, "org.freedesktop.DBus.Peer") &&
1663 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1664
1665 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1666 if (r < 0)
1667 return r;
1668
1669 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1670 if (r < 0)
1671 return r;
1672
1673 n = bus_node_allocate(bus, path);
1674 if (!n)
1675 return -ENOMEM;
1676
1677 LIST_FOREACH(vtables, i, n->vtables) {
1678 if (i->is_fallback != fallback) {
1679 r = -EPROTOTYPE;
1680 goto fail;
1681 }
1682
1683 if (streq(i->interface, interface)) {
1684
1685 if (i->vtable == vtable) {
1686 r = -EEXIST;
1687 goto fail;
1688 }
1689
1690 existing = i;
1691 }
1692 }
1693
1694 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1695 if (!s) {
1696 r = -ENOMEM;
1697 goto fail;
1698 }
1699
1700 s->node_vtable.is_fallback = fallback;
1701 s->node_vtable.vtable = vtable;
1702 s->node_vtable.find = find;
1703
1704 s->node_vtable.interface = strdup(interface);
1705 if (!s->node_vtable.interface) {
1706 r = -ENOMEM;
1707 goto fail;
1708 }
1709
1710 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1711
1712 switch (v->type) {
1713
1714 case _SD_BUS_VTABLE_METHOD: {
1715 struct vtable_member *m;
1716
1717 if (!member_name_is_valid(v->x.method.member) ||
1718 !signature_is_valid(strempty(v->x.method.signature), false) ||
1719 !signature_is_valid(strempty(v->x.method.result), false) ||
1720 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1721 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1722 r = -EINVAL;
1723 goto fail;
1724 }
1725
1726 m = new0(struct vtable_member, 1);
1727 if (!m) {
1728 r = -ENOMEM;
1729 goto fail;
1730 }
1731
1732 m->parent = &s->node_vtable;
1733 m->path = n->path;
1734 m->interface = s->node_vtable.interface;
1735 m->member = v->x.method.member;
1736 m->vtable = v;
1737
1738 r = hashmap_put(bus->vtable_methods, m, m);
1739 if (r < 0) {
1740 free(m);
1741 goto fail;
1742 }
1743
1744 break;
1745 }
1746
1747 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1748
1749 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1750 r = -EINVAL;
1751 goto fail;
1752 }
1753
1754 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1755 r = -EINVAL;
1756 goto fail;
1757 }
1758
1759 /* Fall through */
1760
1761 case _SD_BUS_VTABLE_PROPERTY: {
1762 struct vtable_member *m;
1763
1764 if (!member_name_is_valid(v->x.property.member) ||
1765 !signature_is_single(v->x.property.signature, false) ||
1766 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1767 (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1768 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1769 ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1770 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1771 r = -EINVAL;
1772 goto fail;
1773 }
1774
1775 m = new0(struct vtable_member, 1);
1776 if (!m) {
1777 r = -ENOMEM;
1778 goto fail;
1779 }
1780
1781 m->parent = &s->node_vtable;
1782 m->path = n->path;
1783 m->interface = s->node_vtable.interface;
1784 m->member = v->x.property.member;
1785 m->vtable = v;
1786
1787 r = hashmap_put(bus->vtable_properties, m, m);
1788 if (r < 0) {
1789 free(m);
1790 goto fail;
1791 }
1792
1793 break;
1794 }
1795
1796 case _SD_BUS_VTABLE_SIGNAL:
1797
1798 if (!member_name_is_valid(v->x.signal.member) ||
1799 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1800 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1801 r = -EINVAL;
1802 goto fail;
1803 }
1804
1805 break;
1806
1807 default:
1808 r = -EINVAL;
1809 goto fail;
1810 }
1811 }
1812
1813 s->node_vtable.node = n;
1814 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1815 bus->nodes_modified = true;
1816
1817 if (slot)
1818 *slot = s;
1819
1820 return 0;
1821
1822 fail:
1823 sd_bus_slot_unref(s);
1824 bus_node_gc(bus, n);
1825
1826 return r;
1827 }
1828
1829 _public_ int sd_bus_add_object_vtable(
1830 sd_bus *bus,
1831 sd_bus_slot **slot,
1832 const char *path,
1833 const char *interface,
1834 const sd_bus_vtable *vtable,
1835 void *userdata) {
1836
1837 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1838 }
1839
1840 _public_ int sd_bus_add_fallback_vtable(
1841 sd_bus *bus,
1842 sd_bus_slot **slot,
1843 const char *prefix,
1844 const char *interface,
1845 const sd_bus_vtable *vtable,
1846 sd_bus_object_find_t find,
1847 void *userdata) {
1848
1849 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1850 }
1851
1852 _public_ int sd_bus_add_node_enumerator(
1853 sd_bus *bus,
1854 sd_bus_slot **slot,
1855 const char *path,
1856 sd_bus_node_enumerator_t callback,
1857 void *userdata) {
1858
1859 sd_bus_slot *s;
1860 struct node *n;
1861 int r;
1862
1863 assert_return(bus, -EINVAL);
1864 assert_return(object_path_is_valid(path), -EINVAL);
1865 assert_return(callback, -EINVAL);
1866 assert_return(!bus_pid_changed(bus), -ECHILD);
1867
1868 n = bus_node_allocate(bus, path);
1869 if (!n)
1870 return -ENOMEM;
1871
1872 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1873 if (!s) {
1874 r = -ENOMEM;
1875 goto fail;
1876 }
1877
1878 s->node_enumerator.callback = callback;
1879
1880 s->node_enumerator.node = n;
1881 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1882 bus->nodes_modified = true;
1883
1884 if (slot)
1885 *slot = s;
1886
1887 return 0;
1888
1889 fail:
1890 sd_bus_slot_unref(s);
1891 bus_node_gc(bus, n);
1892
1893 return r;
1894 }
1895
1896 static int emit_properties_changed_on_interface(
1897 sd_bus *bus,
1898 const char *prefix,
1899 const char *path,
1900 const char *interface,
1901 bool require_fallback,
1902 bool *found_interface,
1903 char **names) {
1904
1905 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1906 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1907 bool has_invalidating = false, has_changing = false;
1908 struct vtable_member key = {};
1909 struct node_vtable *c;
1910 struct node *n;
1911 char **property;
1912 void *u = NULL;
1913 int r;
1914
1915 assert(bus);
1916 assert(prefix);
1917 assert(path);
1918 assert(interface);
1919 assert(found_interface);
1920
1921 n = hashmap_get(bus->nodes, prefix);
1922 if (!n)
1923 return 0;
1924
1925 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1926 if (r < 0)
1927 return r;
1928
1929 r = sd_bus_message_append(m, "s", interface);
1930 if (r < 0)
1931 return r;
1932
1933 r = sd_bus_message_open_container(m, 'a', "{sv}");
1934 if (r < 0)
1935 return r;
1936
1937 key.path = prefix;
1938 key.interface = interface;
1939
1940 LIST_FOREACH(vtables, c, n->vtables) {
1941 if (require_fallback && !c->is_fallback)
1942 continue;
1943
1944 if (!streq(c->interface, interface))
1945 continue;
1946
1947 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1948 if (r < 0)
1949 return r;
1950 if (bus->nodes_modified)
1951 return 0;
1952 if (r == 0)
1953 continue;
1954
1955 *found_interface = true;
1956
1957 if (names) {
1958 /* If the caller specified a list of
1959 * properties we include exactly those in the
1960 * PropertiesChanged message */
1961
1962 STRV_FOREACH(property, names) {
1963 struct vtable_member *v;
1964
1965 assert_return(member_name_is_valid(*property), -EINVAL);
1966
1967 key.member = *property;
1968 v = hashmap_get(bus->vtable_properties, &key);
1969 if (!v)
1970 return -ENOENT;
1971
1972 /* If there are two vtables for the same
1973 * interface, let's handle this property when
1974 * we come to that vtable. */
1975 if (c != v->parent)
1976 continue;
1977
1978 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1979 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1980
1981 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1982
1983 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1984 has_invalidating = true;
1985 continue;
1986 }
1987
1988 has_changing = true;
1989
1990 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1991 if (r < 0)
1992 return r;
1993 if (bus->nodes_modified)
1994 return 0;
1995 }
1996 } else {
1997 const sd_bus_vtable *v;
1998
1999 /* If the caller specified no properties list
2000 * we include all properties that are marked
2001 * as changing in the message. */
2002
2003 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2004 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
2005 continue;
2006
2007 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2008 continue;
2009
2010 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2011 has_invalidating = true;
2012 continue;
2013 }
2014
2015 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2016 continue;
2017
2018 has_changing = true;
2019
2020 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2021 if (r < 0)
2022 return r;
2023 if (bus->nodes_modified)
2024 return 0;
2025 }
2026 }
2027 }
2028
2029 if (!has_invalidating && !has_changing)
2030 return 0;
2031
2032 r = sd_bus_message_close_container(m);
2033 if (r < 0)
2034 return r;
2035
2036 r = sd_bus_message_open_container(m, 'a', "s");
2037 if (r < 0)
2038 return r;
2039
2040 if (has_invalidating) {
2041 LIST_FOREACH(vtables, c, n->vtables) {
2042 if (require_fallback && !c->is_fallback)
2043 continue;
2044
2045 if (!streq(c->interface, interface))
2046 continue;
2047
2048 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2049 if (r < 0)
2050 return r;
2051 if (bus->nodes_modified)
2052 return 0;
2053 if (r == 0)
2054 continue;
2055
2056 if (names) {
2057 STRV_FOREACH(property, names) {
2058 struct vtable_member *v;
2059
2060 key.member = *property;
2061 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2062 assert(c == v->parent);
2063
2064 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2065 continue;
2066
2067 r = sd_bus_message_append(m, "s", *property);
2068 if (r < 0)
2069 return r;
2070 }
2071 } else {
2072 const sd_bus_vtable *v;
2073
2074 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2075 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
2076 continue;
2077
2078 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2079 continue;
2080
2081 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2082 continue;
2083
2084 r = sd_bus_message_append(m, "s", v->x.property.member);
2085 if (r < 0)
2086 return r;
2087 }
2088 }
2089 }
2090 }
2091
2092 r = sd_bus_message_close_container(m);
2093 if (r < 0)
2094 return r;
2095
2096 r = sd_bus_send(bus, m, NULL);
2097 if (r < 0)
2098 return r;
2099
2100 return 1;
2101 }
2102
2103 _public_ int sd_bus_emit_properties_changed_strv(
2104 sd_bus *bus,
2105 const char *path,
2106 const char *interface,
2107 char **names) {
2108
2109 BUS_DONT_DESTROY(bus);
2110 bool found_interface = false;
2111 char *prefix;
2112 int r;
2113
2114 assert_return(bus, -EINVAL);
2115 assert_return(object_path_is_valid(path), -EINVAL);
2116 assert_return(interface_name_is_valid(interface), -EINVAL);
2117 assert_return(!bus_pid_changed(bus), -ECHILD);
2118
2119 if (!BUS_IS_OPEN(bus->state))
2120 return -ENOTCONN;
2121
2122 /* A non-NULL but empty names list means nothing needs to be
2123 generated. A NULL list OTOH indicates that all properties
2124 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2125 included in the PropertiesChanged message. */
2126 if (names && names[0] == NULL)
2127 return 0;
2128
2129 do {
2130 bus->nodes_modified = false;
2131
2132 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2133 if (r != 0)
2134 return r;
2135 if (bus->nodes_modified)
2136 continue;
2137
2138 prefix = alloca(strlen(path) + 1);
2139 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2140 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2141 if (r != 0)
2142 return r;
2143 if (bus->nodes_modified)
2144 break;
2145 }
2146
2147 } while (bus->nodes_modified);
2148
2149 return found_interface ? 0 : -ENOENT;
2150 }
2151
2152 _public_ int sd_bus_emit_properties_changed(
2153 sd_bus *bus,
2154 const char *path,
2155 const char *interface,
2156 const char *name, ...) {
2157
2158 char **names;
2159
2160 assert_return(bus, -EINVAL);
2161 assert_return(object_path_is_valid(path), -EINVAL);
2162 assert_return(interface_name_is_valid(interface), -EINVAL);
2163 assert_return(!bus_pid_changed(bus), -ECHILD);
2164
2165 if (!BUS_IS_OPEN(bus->state))
2166 return -ENOTCONN;
2167
2168 if (!name)
2169 return 0;
2170
2171 names = strv_from_stdarg_alloca(name);
2172
2173 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2174 }
2175
2176 static int object_added_append_all_prefix(
2177 sd_bus *bus,
2178 sd_bus_message *m,
2179 Set *s,
2180 const char *prefix,
2181 const char *path,
2182 bool require_fallback) {
2183
2184 const char *previous_interface = NULL;
2185 struct node_vtable *c;
2186 struct node *n;
2187 int r;
2188
2189 assert(bus);
2190 assert(m);
2191 assert(s);
2192 assert(prefix);
2193 assert(path);
2194
2195 n = hashmap_get(bus->nodes, prefix);
2196 if (!n)
2197 return 0;
2198
2199 LIST_FOREACH(vtables, c, n->vtables) {
2200 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2201 void *u = NULL;
2202
2203 if (require_fallback && !c->is_fallback)
2204 continue;
2205
2206 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2207 if (r < 0)
2208 return r;
2209 if (bus->nodes_modified)
2210 return 0;
2211 if (r == 0)
2212 continue;
2213
2214 if (!streq_ptr(c->interface, previous_interface)) {
2215 /* If a child-node already handled this interface, we
2216 * skip it on any of its parents. The child vtables
2217 * always fully override any conflicting vtables of
2218 * any parent node. */
2219 if (set_get(s, c->interface))
2220 continue;
2221
2222 r = set_put(s, c->interface);
2223 if (r < 0)
2224 return r;
2225
2226 if (previous_interface) {
2227 r = sd_bus_message_close_container(m);
2228 if (r < 0)
2229 return r;
2230 r = sd_bus_message_close_container(m);
2231 if (r < 0)
2232 return r;
2233 }
2234
2235 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2236 if (r < 0)
2237 return r;
2238 r = sd_bus_message_append(m, "s", c->interface);
2239 if (r < 0)
2240 return r;
2241 r = sd_bus_message_open_container(m, 'a', "{sv}");
2242 if (r < 0)
2243 return r;
2244
2245 previous_interface = c->interface;
2246 }
2247
2248 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2249 if (r < 0)
2250 return r;
2251 if (bus->nodes_modified)
2252 return 0;
2253 }
2254
2255 if (previous_interface) {
2256 r = sd_bus_message_close_container(m);
2257 if (r < 0)
2258 return r;
2259 r = sd_bus_message_close_container(m);
2260 if (r < 0)
2261 return r;
2262 }
2263
2264 return 0;
2265 }
2266
2267 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2268 _cleanup_set_free_ Set *s = NULL;
2269 char *prefix;
2270 int r;
2271
2272 assert(bus);
2273 assert(m);
2274 assert(path);
2275
2276 /*
2277 * This appends all interfaces registered on path @path. We first add
2278 * the builtin interfaces, which are always available and handled by
2279 * sd-bus. Then, we add all interfaces registered on the exact node,
2280 * followed by all fallback interfaces registered on any parent prefix.
2281 *
2282 * If an interface is registered multiple times on the same node with
2283 * different vtables, we merge all the properties across all vtables.
2284 * However, if a child node has the same interface registered as one of
2285 * its parent nodes has as fallback, we make the child overwrite the
2286 * parent instead of extending it. Therefore, we keep a "Set" of all
2287 * handled interfaces during parent traversal, so we skip interfaces on
2288 * a parent that were overwritten by a child.
2289 */
2290
2291 s = set_new(&string_hash_ops);
2292 if (!s)
2293 return -ENOMEM;
2294
2295 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2296 if (r < 0)
2297 return r;
2298 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2299 if (r < 0)
2300 return r;
2301 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2302 if (r < 0)
2303 return r;
2304 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2305 if (r < 0)
2306 return r;
2307
2308 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2309 if (r < 0)
2310 return r;
2311 if (bus->nodes_modified)
2312 return 0;
2313
2314 prefix = alloca(strlen(path) + 1);
2315 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2316 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2317 if (r < 0)
2318 return r;
2319 if (bus->nodes_modified)
2320 return 0;
2321 }
2322
2323 return 0;
2324 }
2325
2326 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2327 BUS_DONT_DESTROY(bus);
2328
2329 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2330 struct node *object_manager;
2331 int r;
2332
2333 /*
2334 * This emits an InterfacesAdded signal on the given path, by iterating
2335 * all registered vtables and fallback vtables on the path. All
2336 * properties are queried and included in the signal.
2337 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2338 * explicit list of registered interfaces. However, unlike
2339 * interfaces_added(), this call can figure out the list of supported
2340 * interfaces itself. Furthermore, it properly adds the builtin
2341 * org.freedesktop.DBus.* interfaces.
2342 */
2343
2344 assert_return(bus, -EINVAL);
2345 assert_return(object_path_is_valid(path), -EINVAL);
2346 assert_return(!bus_pid_changed(bus), -ECHILD);
2347
2348 if (!BUS_IS_OPEN(bus->state))
2349 return -ENOTCONN;
2350
2351 r = bus_find_parent_object_manager(bus, &object_manager, path);
2352 if (r < 0)
2353 return r;
2354 if (r == 0)
2355 return -ESRCH;
2356
2357 do {
2358 bus->nodes_modified = false;
2359 m = sd_bus_message_unref(m);
2360
2361 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2362 if (r < 0)
2363 return r;
2364
2365 r = sd_bus_message_append_basic(m, 'o', path);
2366 if (r < 0)
2367 return r;
2368
2369 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2370 if (r < 0)
2371 return r;
2372
2373 r = object_added_append_all(bus, m, path);
2374 if (r < 0)
2375 return r;
2376
2377 if (bus->nodes_modified)
2378 continue;
2379
2380 r = sd_bus_message_close_container(m);
2381 if (r < 0)
2382 return r;
2383
2384 } while (bus->nodes_modified);
2385
2386 return sd_bus_send(bus, m, NULL);
2387 }
2388
2389 static int object_removed_append_all_prefix(
2390 sd_bus *bus,
2391 sd_bus_message *m,
2392 Set *s,
2393 const char *prefix,
2394 const char *path,
2395 bool require_fallback) {
2396
2397 const char *previous_interface = NULL;
2398 struct node_vtable *c;
2399 struct node *n;
2400 int r;
2401
2402 assert(bus);
2403 assert(m);
2404 assert(s);
2405 assert(prefix);
2406 assert(path);
2407
2408 n = hashmap_get(bus->nodes, prefix);
2409 if (!n)
2410 return 0;
2411
2412 LIST_FOREACH(vtables, c, n->vtables) {
2413 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2414 void *u = NULL;
2415
2416 if (require_fallback && !c->is_fallback)
2417 continue;
2418 if (streq_ptr(c->interface, previous_interface))
2419 continue;
2420
2421 /* If a child-node already handled this interface, we
2422 * skip it on any of its parents. The child vtables
2423 * always fully override any conflicting vtables of
2424 * any parent node. */
2425 if (set_get(s, c->interface))
2426 continue;
2427
2428 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2429 if (r < 0)
2430 return r;
2431 if (bus->nodes_modified)
2432 return 0;
2433 if (r == 0)
2434 continue;
2435
2436 r = set_put(s, c->interface);
2437 if (r < 0)
2438 return r;
2439
2440 r = sd_bus_message_append(m, "s", c->interface);
2441 if (r < 0)
2442 return r;
2443
2444 previous_interface = c->interface;
2445 }
2446
2447 return 0;
2448 }
2449
2450 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2451 _cleanup_set_free_ Set *s = NULL;
2452 char *prefix;
2453 int r;
2454
2455 assert(bus);
2456 assert(m);
2457 assert(path);
2458
2459 /* see sd_bus_emit_object_added() for details */
2460
2461 s = set_new(&string_hash_ops);
2462 if (!s)
2463 return -ENOMEM;
2464
2465 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2466 if (r < 0)
2467 return r;
2468 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2469 if (r < 0)
2470 return r;
2471 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2472 if (r < 0)
2473 return r;
2474 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2475 if (r < 0)
2476 return r;
2477
2478 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2479 if (r < 0)
2480 return r;
2481 if (bus->nodes_modified)
2482 return 0;
2483
2484 prefix = alloca(strlen(path) + 1);
2485 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2486 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2487 if (r < 0)
2488 return r;
2489 if (bus->nodes_modified)
2490 return 0;
2491 }
2492
2493 return 0;
2494 }
2495
2496 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2497 BUS_DONT_DESTROY(bus);
2498
2499 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2500 struct node *object_manager;
2501 int r;
2502
2503 /*
2504 * This is like sd_bus_emit_object_added(), but emits an
2505 * InterfacesRemoved signal on the given path. This only includes any
2506 * registered interfaces but skips the properties. Note that this will
2507 * call into the find() callbacks of any registered vtable. Therefore,
2508 * you must call this function before destroying/unlinking your object.
2509 * Otherwise, the list of interfaces will be incomplete. However, note
2510 * that this will *NOT* call into any property callback. Therefore, the
2511 * object might be in an "destructed" state, as long as we can find it.
2512 */
2513
2514 assert_return(bus, -EINVAL);
2515 assert_return(object_path_is_valid(path), -EINVAL);
2516 assert_return(!bus_pid_changed(bus), -ECHILD);
2517
2518 if (!BUS_IS_OPEN(bus->state))
2519 return -ENOTCONN;
2520
2521 r = bus_find_parent_object_manager(bus, &object_manager, path);
2522 if (r < 0)
2523 return r;
2524 if (r == 0)
2525 return -ESRCH;
2526
2527 do {
2528 bus->nodes_modified = false;
2529 m = sd_bus_message_unref(m);
2530
2531 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2532 if (r < 0)
2533 return r;
2534
2535 r = sd_bus_message_append_basic(m, 'o', path);
2536 if (r < 0)
2537 return r;
2538
2539 r = sd_bus_message_open_container(m, 'a', "s");
2540 if (r < 0)
2541 return r;
2542
2543 r = object_removed_append_all(bus, m, path);
2544 if (r < 0)
2545 return r;
2546
2547 if (bus->nodes_modified)
2548 continue;
2549
2550 r = sd_bus_message_close_container(m);
2551 if (r < 0)
2552 return r;
2553
2554 } while (bus->nodes_modified);
2555
2556 return sd_bus_send(bus, m, NULL);
2557 }
2558
2559 static int interfaces_added_append_one_prefix(
2560 sd_bus *bus,
2561 sd_bus_message *m,
2562 const char *prefix,
2563 const char *path,
2564 const char *interface,
2565 bool require_fallback) {
2566
2567 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2568 bool found_interface = false;
2569 struct node_vtable *c;
2570 struct node *n;
2571 void *u = NULL;
2572 int r;
2573
2574 assert(bus);
2575 assert(m);
2576 assert(prefix);
2577 assert(path);
2578 assert(interface);
2579
2580 n = hashmap_get(bus->nodes, prefix);
2581 if (!n)
2582 return 0;
2583
2584 LIST_FOREACH(vtables, c, n->vtables) {
2585 if (require_fallback && !c->is_fallback)
2586 continue;
2587
2588 if (!streq(c->interface, interface))
2589 continue;
2590
2591 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2592 if (r < 0)
2593 return r;
2594 if (bus->nodes_modified)
2595 return 0;
2596 if (r == 0)
2597 continue;
2598
2599 if (!found_interface) {
2600 r = sd_bus_message_append_basic(m, 's', interface);
2601 if (r < 0)
2602 return r;
2603
2604 r = sd_bus_message_open_container(m, 'a', "{sv}");
2605 if (r < 0)
2606 return r;
2607
2608 found_interface = true;
2609 }
2610
2611 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2612 if (r < 0)
2613 return r;
2614 if (bus->nodes_modified)
2615 return 0;
2616 }
2617
2618 if (found_interface) {
2619 r = sd_bus_message_close_container(m);
2620 if (r < 0)
2621 return r;
2622 }
2623
2624 return found_interface;
2625 }
2626
2627 static int interfaces_added_append_one(
2628 sd_bus *bus,
2629 sd_bus_message *m,
2630 const char *path,
2631 const char *interface) {
2632
2633 char *prefix;
2634 int r;
2635
2636 assert(bus);
2637 assert(m);
2638 assert(path);
2639 assert(interface);
2640
2641 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2642 if (r != 0)
2643 return r;
2644 if (bus->nodes_modified)
2645 return 0;
2646
2647 prefix = alloca(strlen(path) + 1);
2648 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2649 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2650 if (r != 0)
2651 return r;
2652 if (bus->nodes_modified)
2653 return 0;
2654 }
2655
2656 return -ENOENT;
2657 }
2658
2659 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2660 BUS_DONT_DESTROY(bus);
2661
2662 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2663 struct node *object_manager;
2664 char **i;
2665 int r;
2666
2667 assert_return(bus, -EINVAL);
2668 assert_return(object_path_is_valid(path), -EINVAL);
2669 assert_return(!bus_pid_changed(bus), -ECHILD);
2670
2671 if (!BUS_IS_OPEN(bus->state))
2672 return -ENOTCONN;
2673
2674 if (strv_isempty(interfaces))
2675 return 0;
2676
2677 r = bus_find_parent_object_manager(bus, &object_manager, path);
2678 if (r < 0)
2679 return r;
2680 if (r == 0)
2681 return -ESRCH;
2682
2683 do {
2684 bus->nodes_modified = false;
2685 m = sd_bus_message_unref(m);
2686
2687 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2688 if (r < 0)
2689 return r;
2690
2691 r = sd_bus_message_append_basic(m, 'o', path);
2692 if (r < 0)
2693 return r;
2694
2695 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2696 if (r < 0)
2697 return r;
2698
2699 STRV_FOREACH(i, interfaces) {
2700 assert_return(interface_name_is_valid(*i), -EINVAL);
2701
2702 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2703 if (r < 0)
2704 return r;
2705
2706 r = interfaces_added_append_one(bus, m, path, *i);
2707 if (r < 0)
2708 return r;
2709
2710 if (bus->nodes_modified)
2711 break;
2712
2713 r = sd_bus_message_close_container(m);
2714 if (r < 0)
2715 return r;
2716 }
2717
2718 if (bus->nodes_modified)
2719 continue;
2720
2721 r = sd_bus_message_close_container(m);
2722 if (r < 0)
2723 return r;
2724
2725 } while (bus->nodes_modified);
2726
2727 return sd_bus_send(bus, m, NULL);
2728 }
2729
2730 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2731 char **interfaces;
2732
2733 assert_return(bus, -EINVAL);
2734 assert_return(object_path_is_valid(path), -EINVAL);
2735 assert_return(!bus_pid_changed(bus), -ECHILD);
2736
2737 if (!BUS_IS_OPEN(bus->state))
2738 return -ENOTCONN;
2739
2740 interfaces = strv_from_stdarg_alloca(interface);
2741
2742 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2743 }
2744
2745 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2746 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2747 struct node *object_manager;
2748 int r;
2749
2750 assert_return(bus, -EINVAL);
2751 assert_return(object_path_is_valid(path), -EINVAL);
2752 assert_return(!bus_pid_changed(bus), -ECHILD);
2753
2754 if (!BUS_IS_OPEN(bus->state))
2755 return -ENOTCONN;
2756
2757 if (strv_isempty(interfaces))
2758 return 0;
2759
2760 r = bus_find_parent_object_manager(bus, &object_manager, path);
2761 if (r < 0)
2762 return r;
2763 if (r == 0)
2764 return -ESRCH;
2765
2766 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2767 if (r < 0)
2768 return r;
2769
2770 r = sd_bus_message_append_basic(m, 'o', path);
2771 if (r < 0)
2772 return r;
2773
2774 r = sd_bus_message_append_strv(m, interfaces);
2775 if (r < 0)
2776 return r;
2777
2778 return sd_bus_send(bus, m, NULL);
2779 }
2780
2781 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2782 char **interfaces;
2783
2784 assert_return(bus, -EINVAL);
2785 assert_return(object_path_is_valid(path), -EINVAL);
2786 assert_return(!bus_pid_changed(bus), -ECHILD);
2787
2788 if (!BUS_IS_OPEN(bus->state))
2789 return -ENOTCONN;
2790
2791 interfaces = strv_from_stdarg_alloca(interface);
2792
2793 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2794 }
2795
2796 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2797 sd_bus_slot *s;
2798 struct node *n;
2799 int r;
2800
2801 assert_return(bus, -EINVAL);
2802 assert_return(object_path_is_valid(path), -EINVAL);
2803 assert_return(!bus_pid_changed(bus), -ECHILD);
2804
2805 n = bus_node_allocate(bus, path);
2806 if (!n)
2807 return -ENOMEM;
2808
2809 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2810 if (!s) {
2811 r = -ENOMEM;
2812 goto fail;
2813 }
2814
2815 s->node_object_manager.node = n;
2816 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2817 bus->nodes_modified = true;
2818
2819 if (slot)
2820 *slot = s;
2821
2822 return 0;
2823
2824 fail:
2825 sd_bus_slot_unref(s);
2826 bus_node_gc(bus, n);
2827
2828 return r;
2829 }