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