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