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