]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-objects.c
sd-bus: introduce new SD_BUS_VTABLE_PROPERTY_EXPLICIT flag
[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 "strv.h"
23 #include "set.h"
24 #include "bus-internal.h"
25 #include "bus-message.h"
26 #include "bus-type.h"
27 #include "bus-signature.h"
28 #include "bus-introspect.h"
29 #include "bus-slot.h"
30 #include "bus-objects.h"
31
32 static int node_vtable_get_userdata(
33 sd_bus *bus,
34 const char *path,
35 struct node_vtable *c,
36 void **userdata,
37 sd_bus_error *error) {
38
39 sd_bus_slot *s;
40 void *u;
41 int r;
42
43 assert(bus);
44 assert(path);
45 assert(c);
46
47 s = container_of(c, sd_bus_slot, node_vtable);
48 u = s->userdata;
49 if (c->find) {
50 bus->current_slot = sd_bus_slot_ref(s);
51 bus->current_userdata = u;
52 r = c->find(bus, path, c->interface, u, &u, error);
53 bus->current_userdata = NULL;
54 bus->current_slot = sd_bus_slot_unref(s);
55
56 if (r < 0)
57 return r;
58 if (sd_bus_error_is_set(error))
59 return -sd_bus_error_get_errno(error);
60 if (r == 0)
61 return r;
62 }
63
64 if (userdata)
65 *userdata = u;
66
67 return 1;
68 }
69
70 static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
71 assert(p);
72
73 return (uint8_t*) u + p->x.method.offset;
74 }
75
76 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
77 assert(p);
78
79 return (uint8_t*) u + p->x.property.offset;
80 }
81
82 static int vtable_property_get_userdata(
83 sd_bus *bus,
84 const char *path,
85 struct vtable_member *p,
86 void **userdata,
87 sd_bus_error *error) {
88
89 void *u;
90 int r;
91
92 assert(bus);
93 assert(path);
94 assert(p);
95 assert(userdata);
96
97 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
98 if (r <= 0)
99 return r;
100 if (bus->nodes_modified)
101 return 0;
102
103 *userdata = vtable_property_convert_userdata(p->vtable, u);
104 return 1;
105 }
106
107 static int add_enumerated_to_set(
108 sd_bus *bus,
109 const char *prefix,
110 struct node_enumerator *first,
111 Set *s,
112 sd_bus_error *error) {
113
114 struct node_enumerator *c;
115 int r;
116
117 assert(bus);
118 assert(prefix);
119 assert(s);
120
121 LIST_FOREACH(enumerators, c, first) {
122 char **children = NULL, **k;
123 sd_bus_slot *slot;
124
125 if (bus->nodes_modified)
126 return 0;
127
128 slot = container_of(c, sd_bus_slot, node_enumerator);
129
130 bus->current_slot = sd_bus_slot_ref(slot);
131 bus->current_userdata = slot->userdata;
132 r = c->callback(bus, prefix, slot->userdata, &children, error);
133 bus->current_userdata = NULL;
134 bus->current_slot = sd_bus_slot_unref(slot);
135
136 if (r < 0)
137 return r;
138 if (sd_bus_error_is_set(error))
139 return -sd_bus_error_get_errno(error);
140
141 STRV_FOREACH(k, children) {
142 if (r < 0) {
143 free(*k);
144 continue;
145 }
146
147 if (!object_path_is_valid(*k)){
148 free(*k);
149 r = -EINVAL;
150 continue;
151 }
152
153 if (!object_path_startswith(*k, prefix)) {
154 free(*k);
155 continue;
156 }
157
158 r = set_consume(s, *k);
159 if (r == -EEXIST)
160 r = 0;
161 }
162
163 free(children);
164 if (r < 0)
165 return r;
166 }
167
168 return 0;
169 }
170
171 static int add_subtree_to_set(
172 sd_bus *bus,
173 const char *prefix,
174 struct node *n,
175 bool skip_subhierarchies,
176 Set *s,
177 sd_bus_error *error) {
178
179 struct node *i;
180 int r;
181
182 assert(bus);
183 assert(prefix);
184 assert(n);
185 assert(s);
186
187 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
188 if (r < 0)
189 return r;
190 if (bus->nodes_modified)
191 return 0;
192
193 LIST_FOREACH(siblings, i, n->child) {
194 char *t;
195
196 if (!object_path_startswith(i->path, prefix))
197 continue;
198
199 t = strdup(i->path);
200 if (!t)
201 return -ENOMEM;
202
203 r = set_consume(s, t);
204 if (r < 0 && r != -EEXIST)
205 return r;
206
207 if (!skip_subhierarchies || !i->object_managers) {
208 r = add_subtree_to_set(bus, prefix, i, skip_subhierarchies, s, error);
209 if (r < 0)
210 return r;
211 if (bus->nodes_modified)
212 return 0;
213 }
214 }
215
216 return 0;
217 }
218
219 static int get_child_nodes(
220 sd_bus *bus,
221 const char *prefix,
222 struct node *n,
223 bool skip_subhierarchies,
224 Set **_s,
225 sd_bus_error *error) {
226
227 Set *s = NULL;
228 int r;
229
230 assert(bus);
231 assert(prefix);
232 assert(n);
233 assert(_s);
234
235 s = set_new(&string_hash_ops);
236 if (!s)
237 return -ENOMEM;
238
239 r = add_subtree_to_set(bus, prefix, n, skip_subhierarchies, s, error);
240 if (r < 0) {
241 set_free_free(s);
242 return r;
243 }
244
245 *_s = s;
246 return 0;
247 }
248
249 static int node_callbacks_run(
250 sd_bus *bus,
251 sd_bus_message *m,
252 struct node_callback *first,
253 bool require_fallback,
254 bool *found_object) {
255
256 struct node_callback *c;
257 int r;
258
259 assert(bus);
260 assert(m);
261 assert(found_object);
262
263 LIST_FOREACH(callbacks, c, first) {
264 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
265 sd_bus_slot *slot;
266
267 if (bus->nodes_modified)
268 return 0;
269
270 if (require_fallback && !c->is_fallback)
271 continue;
272
273 *found_object = true;
274
275 if (c->last_iteration == bus->iteration_counter)
276 continue;
277
278 c->last_iteration = bus->iteration_counter;
279
280 r = sd_bus_message_rewind(m, true);
281 if (r < 0)
282 return r;
283
284 slot = container_of(c, sd_bus_slot, node_callback);
285
286 bus->current_slot = sd_bus_slot_ref(slot);
287 bus->current_handler = c->callback;
288 bus->current_userdata = slot->userdata;
289 r = c->callback(m, slot->userdata, &error_buffer);
290 bus->current_userdata = NULL;
291 bus->current_handler = NULL;
292 bus->current_slot = sd_bus_slot_unref(slot);
293
294 r = bus_maybe_reply_error(m, r, &error_buffer);
295 if (r != 0)
296 return r;
297 }
298
299 return 0;
300 }
301
302 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
303
304 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
305 uint64_t cap;
306 int r;
307
308 assert(bus);
309 assert(m);
310 assert(c);
311
312 /* If the entire bus is trusted let's grant access */
313 if (bus->trusted)
314 return 0;
315
316 /* If the member is marked UNPRIVILEGED let's grant access */
317 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
318 return 0;
319
320 /* Check have the caller has the requested capability
321 * set. Note that the flags value contains the capability
322 * number plus one, which we need to subtract here. We do this
323 * so that we have 0 as special value for "default
324 * capability". */
325 cap = CAPABILITY_SHIFT(c->vtable->flags);
326 if (cap == 0)
327 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
328 if (cap == 0)
329 cap = CAP_SYS_ADMIN;
330 else
331 cap --;
332
333 r = sd_bus_query_sender_privilege(m, cap);
334 if (r < 0)
335 return r;
336 if (r > 0)
337 return 0;
338
339 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
340 }
341
342 static int method_callbacks_run(
343 sd_bus *bus,
344 sd_bus_message *m,
345 struct vtable_member *c,
346 bool require_fallback,
347 bool *found_object) {
348
349 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
350 const char *signature;
351 void *u;
352 int r;
353
354 assert(bus);
355 assert(m);
356 assert(c);
357 assert(found_object);
358
359 if (require_fallback && !c->parent->is_fallback)
360 return 0;
361
362 r = check_access(bus, m, c, &error);
363 if (r < 0)
364 return bus_maybe_reply_error(m, r, &error);
365
366 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
367 if (r <= 0)
368 return bus_maybe_reply_error(m, r, &error);
369 if (bus->nodes_modified)
370 return 0;
371
372 u = vtable_method_convert_userdata(c->vtable, u);
373
374 *found_object = true;
375
376 if (c->last_iteration == bus->iteration_counter)
377 return 0;
378
379 c->last_iteration = bus->iteration_counter;
380
381 r = sd_bus_message_rewind(m, true);
382 if (r < 0)
383 return r;
384
385 signature = sd_bus_message_get_signature(m, true);
386 if (!signature)
387 return -EINVAL;
388
389 if (!streq(strempty(c->vtable->x.method.signature), signature))
390 return sd_bus_reply_method_errorf(
391 m,
392 SD_BUS_ERROR_INVALID_ARGS,
393 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
394 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
395
396 /* Keep track what the signature of the reply to this message
397 * should be, so that this can be enforced when sealing the
398 * reply. */
399 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
400
401 if (c->vtable->x.method.handler) {
402 sd_bus_slot *slot;
403
404 slot = container_of(c->parent, sd_bus_slot, node_vtable);
405
406 bus->current_slot = sd_bus_slot_ref(slot);
407 bus->current_handler = c->vtable->x.method.handler;
408 bus->current_userdata = u;
409 r = c->vtable->x.method.handler(m, u, &error);
410 bus->current_userdata = NULL;
411 bus->current_handler = NULL;
412 bus->current_slot = sd_bus_slot_unref(slot);
413
414 return bus_maybe_reply_error(m, r, &error);
415 }
416
417 /* If the method callback is NULL, make this a successful NOP */
418 r = sd_bus_reply_method_return(m, NULL);
419 if (r < 0)
420 return r;
421
422 return 1;
423 }
424
425 static int invoke_property_get(
426 sd_bus *bus,
427 sd_bus_slot *slot,
428 const sd_bus_vtable *v,
429 const char *path,
430 const char *interface,
431 const char *property,
432 sd_bus_message *reply,
433 void *userdata,
434 sd_bus_error *error) {
435
436 const void *p;
437 int r;
438
439 assert(bus);
440 assert(slot);
441 assert(v);
442 assert(path);
443 assert(interface);
444 assert(property);
445 assert(reply);
446
447 if (v->x.property.get) {
448
449 bus->current_slot = sd_bus_slot_ref(slot);
450 bus->current_userdata = userdata;
451 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
452 bus->current_userdata = NULL;
453 bus->current_slot = sd_bus_slot_unref(slot);
454
455 if (r < 0)
456 return r;
457 if (sd_bus_error_is_set(error))
458 return -sd_bus_error_get_errno(error);
459 return r;
460 }
461
462 /* Automatic handling if no callback is defined. */
463
464 if (streq(v->x.property.signature, "as"))
465 return sd_bus_message_append_strv(reply, *(char***) userdata);
466
467 assert(signature_is_single(v->x.property.signature, false));
468 assert(bus_type_is_basic(v->x.property.signature[0]));
469
470 switch (v->x.property.signature[0]) {
471
472 case SD_BUS_TYPE_STRING:
473 case SD_BUS_TYPE_SIGNATURE:
474 p = strempty(*(char**) userdata);
475 break;
476
477 case SD_BUS_TYPE_OBJECT_PATH:
478 p = *(char**) userdata;
479 assert(p);
480 break;
481
482 default:
483 p = userdata;
484 break;
485 }
486
487 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
488 }
489
490 static int invoke_property_set(
491 sd_bus *bus,
492 sd_bus_slot *slot,
493 const sd_bus_vtable *v,
494 const char *path,
495 const char *interface,
496 const char *property,
497 sd_bus_message *value,
498 void *userdata,
499 sd_bus_error *error) {
500
501 int r;
502
503 assert(bus);
504 assert(slot);
505 assert(v);
506 assert(path);
507 assert(interface);
508 assert(property);
509 assert(value);
510
511 if (v->x.property.set) {
512
513 bus->current_slot = sd_bus_slot_ref(slot);
514 bus->current_userdata = userdata;
515 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
516 bus->current_userdata = NULL;
517 bus->current_slot = sd_bus_slot_unref(slot);
518
519 if (r < 0)
520 return r;
521 if (sd_bus_error_is_set(error))
522 return -sd_bus_error_get_errno(error);
523 return r;
524 }
525
526 /* Automatic handling if no callback is defined. */
527
528 assert(signature_is_single(v->x.property.signature, false));
529 assert(bus_type_is_basic(v->x.property.signature[0]));
530
531 switch (v->x.property.signature[0]) {
532
533 case SD_BUS_TYPE_STRING:
534 case SD_BUS_TYPE_OBJECT_PATH:
535 case SD_BUS_TYPE_SIGNATURE: {
536 const char *p;
537 char *n;
538
539 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
540 if (r < 0)
541 return r;
542
543 n = strdup(p);
544 if (!n)
545 return -ENOMEM;
546
547 free(*(char**) userdata);
548 *(char**) userdata = n;
549
550 break;
551 }
552
553 default:
554 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
555 if (r < 0)
556 return r;
557
558 break;
559 }
560
561 return 1;
562 }
563
564 static int property_get_set_callbacks_run(
565 sd_bus *bus,
566 sd_bus_message *m,
567 struct vtable_member *c,
568 bool require_fallback,
569 bool is_get,
570 bool *found_object) {
571
572 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
573 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
574 sd_bus_slot *slot;
575 void *u = NULL;
576 int r;
577
578 assert(bus);
579 assert(m);
580 assert(c);
581 assert(found_object);
582
583 if (require_fallback && !c->parent->is_fallback)
584 return 0;
585
586 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
587 if (r <= 0)
588 return bus_maybe_reply_error(m, r, &error);
589 if (bus->nodes_modified)
590 return 0;
591
592 slot = container_of(c->parent, sd_bus_slot, node_vtable);
593
594 *found_object = true;
595
596 r = sd_bus_message_new_method_return(m, &reply);
597 if (r < 0)
598 return r;
599
600 if (is_get) {
601 /* Note that we do not protect against reexecution
602 * here (using the last_iteration check, see below),
603 * should the node tree have changed and we got called
604 * again. We assume that property Get() calls are
605 * ultimately without side-effects or if they aren't
606 * then at least idempotent. */
607
608 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
609 if (r < 0)
610 return r;
611
612 /* Note that we do not do an access check here. Read
613 * access to properties is always unrestricted, since
614 * PropertiesChanged signals broadcast contents
615 * anyway. */
616
617 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
618 if (r < 0)
619 return bus_maybe_reply_error(m, r, &error);
620
621 if (bus->nodes_modified)
622 return 0;
623
624 r = sd_bus_message_close_container(reply);
625 if (r < 0)
626 return r;
627
628 } else {
629 const char *signature = NULL;
630 char type = 0;
631
632 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
633 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
634
635 /* Avoid that we call the set routine more than once
636 * if the processing of this message got restarted
637 * because the node tree changed. */
638 if (c->last_iteration == bus->iteration_counter)
639 return 0;
640
641 c->last_iteration = bus->iteration_counter;
642
643 r = sd_bus_message_peek_type(m, &type, &signature);
644 if (r < 0)
645 return r;
646
647 if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
648 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));
649
650 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
651 if (r < 0)
652 return r;
653
654 r = check_access(bus, m, c, &error);
655 if (r < 0)
656 return bus_maybe_reply_error(m, r, &error);
657
658 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
659 if (r < 0)
660 return bus_maybe_reply_error(m, r, &error);
661
662 if (bus->nodes_modified)
663 return 0;
664
665 r = sd_bus_message_exit_container(m);
666 if (r < 0)
667 return r;
668 }
669
670 r = sd_bus_send(bus, reply, NULL);
671 if (r < 0)
672 return r;
673
674 return 1;
675 }
676
677 static int vtable_append_one_property(
678 sd_bus *bus,
679 sd_bus_message *reply,
680 const char *path,
681 struct node_vtable *c,
682 const sd_bus_vtable *v,
683 void *userdata,
684 sd_bus_error *error) {
685
686 sd_bus_slot *slot;
687 int r;
688
689 assert(bus);
690 assert(reply);
691 assert(path);
692 assert(c);
693 assert(v);
694
695 r = sd_bus_message_open_container(reply, 'e', "sv");
696 if (r < 0)
697 return r;
698
699 r = sd_bus_message_append(reply, "s", v->x.property.member);
700 if (r < 0)
701 return r;
702
703 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
704 if (r < 0)
705 return r;
706
707 slot = container_of(c, sd_bus_slot, node_vtable);
708
709 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
710 if (r < 0)
711 return r;
712 if (bus->nodes_modified)
713 return 0;
714
715 r = sd_bus_message_close_container(reply);
716 if (r < 0)
717 return r;
718
719 r = sd_bus_message_close_container(reply);
720 if (r < 0)
721 return r;
722
723 return 0;
724 }
725
726 static int vtable_append_all_properties(
727 sd_bus *bus,
728 sd_bus_message *reply,
729 const char *path,
730 struct node_vtable *c,
731 void *userdata,
732 sd_bus_error *error) {
733
734 const sd_bus_vtable *v;
735 int r;
736
737 assert(bus);
738 assert(reply);
739 assert(path);
740 assert(c);
741
742 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
743 return 1;
744
745 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
746 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
747 continue;
748
749 if (v->flags & SD_BUS_VTABLE_HIDDEN)
750 continue;
751
752 if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
753 continue;
754
755 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
756 if (r < 0)
757 return r;
758 if (bus->nodes_modified)
759 return 0;
760 }
761
762 return 1;
763 }
764
765 static int property_get_all_callbacks_run(
766 sd_bus *bus,
767 sd_bus_message *m,
768 struct node_vtable *first,
769 bool require_fallback,
770 const char *iface,
771 bool *found_object) {
772
773 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
774 struct node_vtable *c;
775 bool found_interface;
776 int r;
777
778 assert(bus);
779 assert(m);
780 assert(found_object);
781
782 r = sd_bus_message_new_method_return(m, &reply);
783 if (r < 0)
784 return r;
785
786 r = sd_bus_message_open_container(reply, 'a', "{sv}");
787 if (r < 0)
788 return r;
789
790 found_interface = !iface ||
791 streq(iface, "org.freedesktop.DBus.Properties") ||
792 streq(iface, "org.freedesktop.DBus.Peer") ||
793 streq(iface, "org.freedesktop.DBus.Introspectable");
794
795 LIST_FOREACH(vtables, c, first) {
796 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
797 void *u;
798
799 if (require_fallback && !c->is_fallback)
800 continue;
801
802 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
803 if (r < 0)
804 return bus_maybe_reply_error(m, r, &error);
805 if (bus->nodes_modified)
806 return 0;
807 if (r == 0)
808 continue;
809
810 *found_object = true;
811
812 if (iface && !streq(c->interface, iface))
813 continue;
814 found_interface = true;
815
816 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
817 if (r < 0)
818 return bus_maybe_reply_error(m, r, &error);
819 if (bus->nodes_modified)
820 return 0;
821 }
822
823 if (!found_interface) {
824 r = sd_bus_reply_method_errorf(
825 m,
826 SD_BUS_ERROR_UNKNOWN_INTERFACE,
827 "Unknown interface '%s'.", iface);
828 if (r < 0)
829 return r;
830
831 return 1;
832 }
833
834 r = sd_bus_message_close_container(reply);
835 if (r < 0)
836 return r;
837
838 r = sd_bus_send(bus, reply, NULL);
839 if (r < 0)
840 return r;
841
842 return 1;
843 }
844
845 static int bus_node_exists(
846 sd_bus *bus,
847 struct node *n,
848 const char *path,
849 bool require_fallback) {
850
851 struct node_vtable *c;
852 struct node_callback *k;
853 int r;
854
855 assert(bus);
856 assert(n);
857 assert(path);
858
859 /* Tests if there's anything attached directly to this node
860 * for the specified path */
861
862 if (!require_fallback && (n->enumerators || n->object_managers))
863 return true;
864
865 LIST_FOREACH(callbacks, k, n->callbacks) {
866 if (require_fallback && !k->is_fallback)
867 continue;
868
869 return 1;
870 }
871
872 LIST_FOREACH(vtables, c, n->vtables) {
873 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
874
875 if (require_fallback && !c->is_fallback)
876 continue;
877
878 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
879 if (r != 0)
880 return r;
881 if (bus->nodes_modified)
882 return 0;
883 }
884
885 return 0;
886 }
887
888 static int process_introspect(
889 sd_bus *bus,
890 sd_bus_message *m,
891 struct node *n,
892 bool require_fallback,
893 bool *found_object) {
894
895 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
896 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
897 _cleanup_set_free_free_ Set *s = NULL;
898 const char *previous_interface = NULL;
899 struct introspect intro;
900 struct node_vtable *c;
901 bool empty;
902 int r;
903
904 assert(bus);
905 assert(m);
906 assert(n);
907 assert(found_object);
908
909 r = get_child_nodes(bus, m->path, n, false, &s, &error);
910 if (r < 0)
911 return bus_maybe_reply_error(m, r, &error);
912 if (bus->nodes_modified)
913 return 0;
914
915 r = introspect_begin(&intro, bus->trusted);
916 if (r < 0)
917 return r;
918
919 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
920 if (r < 0)
921 return r;
922
923 empty = set_isempty(s);
924
925 LIST_FOREACH(vtables, c, n->vtables) {
926 if (require_fallback && !c->is_fallback)
927 continue;
928
929 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
930 if (r < 0) {
931 r = bus_maybe_reply_error(m, r, &error);
932 goto finish;
933 }
934 if (bus->nodes_modified) {
935 r = 0;
936 goto finish;
937 }
938 if (r == 0)
939 continue;
940
941 empty = false;
942
943 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
944 continue;
945
946 if (!streq_ptr(previous_interface, c->interface)) {
947
948 if (previous_interface)
949 fputs(" </interface>\n", intro.f);
950
951 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
952 }
953
954 r = introspect_write_interface(&intro, c->vtable);
955 if (r < 0)
956 goto finish;
957
958 previous_interface = c->interface;
959 }
960
961 if (previous_interface)
962 fputs(" </interface>\n", intro.f);
963
964 if (empty) {
965 /* Nothing?, let's see if we exist at all, and if not
966 * refuse to do anything */
967 r = bus_node_exists(bus, n, m->path, require_fallback);
968 if (r <= 0)
969 goto finish;
970 if (bus->nodes_modified) {
971 r = 0;
972 goto finish;
973 }
974 }
975
976 *found_object = true;
977
978 r = introspect_write_child_nodes(&intro, s, m->path);
979 if (r < 0)
980 goto finish;
981
982 r = introspect_finish(&intro, bus, m, &reply);
983 if (r < 0)
984 goto finish;
985
986 r = sd_bus_send(bus, reply, NULL);
987 if (r < 0)
988 goto finish;
989
990 r = 1;
991
992 finish:
993 introspect_free(&intro);
994 return r;
995 }
996
997 static int object_manager_serialize_path(
998 sd_bus *bus,
999 sd_bus_message *reply,
1000 const char *prefix,
1001 const char *path,
1002 bool require_fallback,
1003 sd_bus_error *error) {
1004
1005 const char *previous_interface = NULL;
1006 bool found_something = false;
1007 struct node_vtable *i;
1008 struct node *n;
1009 int r;
1010
1011 assert(bus);
1012 assert(reply);
1013 assert(prefix);
1014 assert(path);
1015 assert(error);
1016
1017 n = hashmap_get(bus->nodes, prefix);
1018 if (!n)
1019 return 0;
1020
1021 LIST_FOREACH(vtables, i, n->vtables) {
1022 void *u;
1023
1024 if (require_fallback && !i->is_fallback)
1025 continue;
1026
1027 r = node_vtable_get_userdata(bus, path, i, &u, error);
1028 if (r < 0)
1029 return r;
1030 if (bus->nodes_modified)
1031 return 0;
1032 if (r == 0)
1033 continue;
1034
1035 if (!found_something) {
1036
1037 /* Open the object part */
1038
1039 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1040 if (r < 0)
1041 return r;
1042
1043 r = sd_bus_message_append(reply, "o", path);
1044 if (r < 0)
1045 return r;
1046
1047 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1048 if (r < 0)
1049 return r;
1050
1051 found_something = true;
1052 }
1053
1054 if (!streq_ptr(previous_interface, i->interface)) {
1055
1056 /* Maybe close the previous interface part */
1057
1058 if (previous_interface) {
1059 r = sd_bus_message_close_container(reply);
1060 if (r < 0)
1061 return r;
1062
1063 r = sd_bus_message_close_container(reply);
1064 if (r < 0)
1065 return r;
1066 }
1067
1068 /* Open the new interface part */
1069
1070 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1071 if (r < 0)
1072 return r;
1073
1074 r = sd_bus_message_append(reply, "s", i->interface);
1075 if (r < 0)
1076 return r;
1077
1078 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1079 if (r < 0)
1080 return r;
1081 }
1082
1083 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1084 if (r < 0)
1085 return r;
1086 if (bus->nodes_modified)
1087 return 0;
1088
1089 previous_interface = i->interface;
1090 }
1091
1092 if (previous_interface) {
1093 r = sd_bus_message_close_container(reply);
1094 if (r < 0)
1095 return r;
1096
1097 r = sd_bus_message_close_container(reply);
1098 if (r < 0)
1099 return r;
1100 }
1101
1102 if (found_something) {
1103 r = sd_bus_message_close_container(reply);
1104 if (r < 0)
1105 return r;
1106
1107 r = sd_bus_message_close_container(reply);
1108 if (r < 0)
1109 return r;
1110 }
1111
1112 return 1;
1113 }
1114
1115 static int object_manager_serialize_path_and_fallbacks(
1116 sd_bus *bus,
1117 sd_bus_message *reply,
1118 const char *path,
1119 sd_bus_error *error) {
1120
1121 char *prefix;
1122 int r;
1123
1124 assert(bus);
1125 assert(reply);
1126 assert(path);
1127 assert(error);
1128
1129 /* First, add all vtables registered for this path */
1130 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1131 if (r < 0)
1132 return r;
1133 if (bus->nodes_modified)
1134 return 0;
1135
1136 /* Second, add fallback vtables registered for any of the prefixes */
1137 prefix = alloca(strlen(path) + 1);
1138 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1139 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1140 if (r < 0)
1141 return r;
1142 if (bus->nodes_modified)
1143 return 0;
1144 }
1145
1146 return 0;
1147 }
1148
1149 static int process_get_managed_objects(
1150 sd_bus *bus,
1151 sd_bus_message *m,
1152 struct node *n,
1153 bool require_fallback,
1154 bool *found_object) {
1155
1156 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1157 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1158 _cleanup_set_free_free_ Set *s = NULL;
1159 Iterator i;
1160 char *path;
1161 int r;
1162
1163 assert(bus);
1164 assert(m);
1165 assert(n);
1166 assert(found_object);
1167
1168 /* Spec says, GetManagedObjects() is only implemented on the root of a
1169 * sub-tree. Therefore, we require a registered object-manager on
1170 * exactly the queried path, otherwise, we refuse to respond. */
1171
1172 if (require_fallback || !n->object_managers)
1173 return 0;
1174
1175 r = get_child_nodes(bus, m->path, n, true, &s, &error);
1176 if (r < 0)
1177 return r;
1178 if (bus->nodes_modified)
1179 return 0;
1180
1181 r = sd_bus_message_new_method_return(m, &reply);
1182 if (r < 0)
1183 return r;
1184
1185 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1186 if (r < 0)
1187 return r;
1188
1189 SET_FOREACH(path, s, i) {
1190 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1191 if (r < 0)
1192 return r;
1193
1194 if (bus->nodes_modified)
1195 return 0;
1196 }
1197
1198 r = sd_bus_message_close_container(reply);
1199 if (r < 0)
1200 return r;
1201
1202 r = sd_bus_send(bus, reply, NULL);
1203 if (r < 0)
1204 return r;
1205
1206 return 1;
1207 }
1208
1209 static int object_find_and_run(
1210 sd_bus *bus,
1211 sd_bus_message *m,
1212 const char *p,
1213 bool require_fallback,
1214 bool *found_object) {
1215
1216 struct node *n;
1217 struct vtable_member vtable_key, *v;
1218 int r;
1219
1220 assert(bus);
1221 assert(m);
1222 assert(p);
1223 assert(found_object);
1224
1225 n = hashmap_get(bus->nodes, p);
1226 if (!n)
1227 return 0;
1228
1229 /* First, try object callbacks */
1230 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1231 if (r != 0)
1232 return r;
1233 if (bus->nodes_modified)
1234 return 0;
1235
1236 if (!m->interface || !m->member)
1237 return 0;
1238
1239 /* Then, look for a known method */
1240 vtable_key.path = (char*) p;
1241 vtable_key.interface = m->interface;
1242 vtable_key.member = m->member;
1243
1244 v = hashmap_get(bus->vtable_methods, &vtable_key);
1245 if (v) {
1246 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1247 if (r != 0)
1248 return r;
1249 if (bus->nodes_modified)
1250 return 0;
1251 }
1252
1253 /* Then, look for a known property */
1254 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1255 bool get = false;
1256
1257 get = streq(m->member, "Get");
1258
1259 if (get || streq(m->member, "Set")) {
1260
1261 r = sd_bus_message_rewind(m, true);
1262 if (r < 0)
1263 return r;
1264
1265 vtable_key.path = (char*) p;
1266
1267 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1268 if (r < 0)
1269 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1270
1271 v = hashmap_get(bus->vtable_properties, &vtable_key);
1272 if (v) {
1273 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1274 if (r != 0)
1275 return r;
1276 }
1277
1278 } else if (streq(m->member, "GetAll")) {
1279 const char *iface;
1280
1281 r = sd_bus_message_rewind(m, true);
1282 if (r < 0)
1283 return r;
1284
1285 r = sd_bus_message_read(m, "s", &iface);
1286 if (r < 0)
1287 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1288
1289 if (iface[0] == 0)
1290 iface = NULL;
1291
1292 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1293 if (r != 0)
1294 return r;
1295 }
1296
1297 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1298
1299 if (!isempty(sd_bus_message_get_signature(m, true)))
1300 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1301
1302 r = process_introspect(bus, m, n, require_fallback, found_object);
1303 if (r != 0)
1304 return r;
1305
1306 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1307
1308 if (!isempty(sd_bus_message_get_signature(m, true)))
1309 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1310
1311 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1312 if (r != 0)
1313 return r;
1314 }
1315
1316 if (bus->nodes_modified)
1317 return 0;
1318
1319 if (!*found_object) {
1320 r = bus_node_exists(bus, n, m->path, require_fallback);
1321 if (r < 0)
1322 return r;
1323 if (bus->nodes_modified)
1324 return 0;
1325 if (r > 0)
1326 *found_object = true;
1327 }
1328
1329 return 0;
1330 }
1331
1332 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1333 int r;
1334 size_t pl;
1335 bool found_object = false;
1336
1337 assert(bus);
1338 assert(m);
1339
1340 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1341 return 0;
1342
1343 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1344 return 0;
1345
1346 if (hashmap_isempty(bus->nodes))
1347 return 0;
1348
1349 /* Never respond to broadcast messages */
1350 if (bus->bus_client && !m->destination)
1351 return 0;
1352
1353 assert(m->path);
1354 assert(m->member);
1355
1356 pl = strlen(m->path);
1357 do {
1358 char prefix[pl+1];
1359
1360 bus->nodes_modified = false;
1361
1362 r = object_find_and_run(bus, m, m->path, false, &found_object);
1363 if (r != 0)
1364 return r;
1365
1366 /* Look for fallback prefixes */
1367 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1368
1369 if (bus->nodes_modified)
1370 break;
1371
1372 r = object_find_and_run(bus, m, prefix, true, &found_object);
1373 if (r != 0)
1374 return r;
1375 }
1376
1377 } while (bus->nodes_modified);
1378
1379 if (!found_object)
1380 return 0;
1381
1382 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1383 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1384 r = sd_bus_reply_method_errorf(
1385 m,
1386 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1387 "Unknown property or interface.");
1388 else
1389 r = sd_bus_reply_method_errorf(
1390 m,
1391 SD_BUS_ERROR_UNKNOWN_METHOD,
1392 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1393
1394 if (r < 0)
1395 return r;
1396
1397 return 1;
1398 }
1399
1400 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1401 struct node *n, *parent;
1402 const char *e;
1403 _cleanup_free_ char *s = NULL;
1404 char *p;
1405 int r;
1406
1407 assert(bus);
1408 assert(path);
1409 assert(path[0] == '/');
1410
1411 n = hashmap_get(bus->nodes, path);
1412 if (n)
1413 return n;
1414
1415 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1416 if (r < 0)
1417 return NULL;
1418
1419 s = strdup(path);
1420 if (!s)
1421 return NULL;
1422
1423 if (streq(path, "/"))
1424 parent = NULL;
1425 else {
1426 e = strrchr(path, '/');
1427 assert(e);
1428
1429 p = strndupa(path, MAX(1, e - path));
1430
1431 parent = bus_node_allocate(bus, p);
1432 if (!parent)
1433 return NULL;
1434 }
1435
1436 n = new0(struct node, 1);
1437 if (!n)
1438 return NULL;
1439
1440 n->parent = parent;
1441 n->path = s;
1442 s = NULL; /* do not free */
1443
1444 r = hashmap_put(bus->nodes, n->path, n);
1445 if (r < 0) {
1446 free(n->path);
1447 free(n);
1448 return NULL;
1449 }
1450
1451 if (parent)
1452 LIST_PREPEND(siblings, parent->child, n);
1453
1454 return n;
1455 }
1456
1457 void bus_node_gc(sd_bus *b, struct node *n) {
1458 assert(b);
1459
1460 if (!n)
1461 return;
1462
1463 if (n->child ||
1464 n->callbacks ||
1465 n->vtables ||
1466 n->enumerators ||
1467 n->object_managers)
1468 return;
1469
1470 assert(hashmap_remove(b->nodes, n->path) == n);
1471
1472 if (n->parent)
1473 LIST_REMOVE(siblings, n->parent->child, n);
1474
1475 free(n->path);
1476 bus_node_gc(b, n->parent);
1477 free(n);
1478 }
1479
1480 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1481 struct node *n;
1482
1483 assert(bus);
1484 assert(path);
1485
1486 n = hashmap_get(bus->nodes, path);
1487 if (!n) {
1488 char *prefix;
1489
1490 prefix = alloca(strlen(path) + 1);
1491 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1492 n = hashmap_get(bus->nodes, prefix);
1493 if (n)
1494 break;
1495 }
1496 }
1497
1498 while (n && !n->object_managers)
1499 n = n->parent;
1500
1501 if (out)
1502 *out = n;
1503 return !!n;
1504 }
1505
1506 static int bus_add_object(
1507 sd_bus *bus,
1508 sd_bus_slot **slot,
1509 bool fallback,
1510 const char *path,
1511 sd_bus_message_handler_t callback,
1512 void *userdata) {
1513
1514 sd_bus_slot *s;
1515 struct node *n;
1516 int r;
1517
1518 assert_return(bus, -EINVAL);
1519 assert_return(object_path_is_valid(path), -EINVAL);
1520 assert_return(callback, -EINVAL);
1521 assert_return(!bus_pid_changed(bus), -ECHILD);
1522
1523 n = bus_node_allocate(bus, path);
1524 if (!n)
1525 return -ENOMEM;
1526
1527 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1528 if (!s) {
1529 r = -ENOMEM;
1530 goto fail;
1531 }
1532
1533 s->node_callback.callback = callback;
1534 s->node_callback.is_fallback = fallback;
1535
1536 s->node_callback.node = n;
1537 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1538 bus->nodes_modified = true;
1539
1540 if (slot)
1541 *slot = s;
1542
1543 return 0;
1544
1545 fail:
1546 sd_bus_slot_unref(s);
1547 bus_node_gc(bus, n);
1548
1549 return r;
1550 }
1551
1552 _public_ int sd_bus_add_object(
1553 sd_bus *bus,
1554 sd_bus_slot **slot,
1555 const char *path,
1556 sd_bus_message_handler_t callback,
1557 void *userdata) {
1558
1559 return bus_add_object(bus, slot, false, path, callback, userdata);
1560 }
1561
1562 _public_ int sd_bus_add_fallback(
1563 sd_bus *bus,
1564 sd_bus_slot **slot,
1565 const char *prefix,
1566 sd_bus_message_handler_t callback,
1567 void *userdata) {
1568
1569 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1570 }
1571
1572 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1573 const struct vtable_member *m = a;
1574 uint8_t hash_key2[HASH_KEY_SIZE];
1575 unsigned long ret;
1576
1577 assert(m);
1578
1579 ret = string_hash_func(m->path, hash_key);
1580
1581 /* Use a slightly different hash key for the interface */
1582 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1583 hash_key2[0]++;
1584 ret ^= string_hash_func(m->interface, hash_key2);
1585
1586 /* And an even different one for the member */
1587 hash_key2[0]++;
1588 ret ^= string_hash_func(m->member, hash_key2);
1589
1590 return ret;
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_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1885 _cleanup_bus_message_unref_ 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_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_bus_message_unref_ 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_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_bus_message_unref_ 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_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_bus_message_unref_ 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_bus_message_unref_ 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 }