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