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