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