]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-objects.c
Merge pull request #998 from vbatts/tar_nosparse_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 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
753 if (r < 0)
754 return r;
755 if (bus->nodes_modified)
756 return 0;
757 }
758
759 return 1;
760 }
761
762 static int property_get_all_callbacks_run(
763 sd_bus *bus,
764 sd_bus_message *m,
765 struct node_vtable *first,
766 bool require_fallback,
767 const char *iface,
768 bool *found_object) {
769
770 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
771 struct node_vtable *c;
772 bool found_interface;
773 int r;
774
775 assert(bus);
776 assert(m);
777 assert(found_object);
778
779 r = sd_bus_message_new_method_return(m, &reply);
780 if (r < 0)
781 return r;
782
783 r = sd_bus_message_open_container(reply, 'a', "{sv}");
784 if (r < 0)
785 return r;
786
787 found_interface = !iface ||
788 streq(iface, "org.freedesktop.DBus.Properties") ||
789 streq(iface, "org.freedesktop.DBus.Peer") ||
790 streq(iface, "org.freedesktop.DBus.Introspectable");
791
792 LIST_FOREACH(vtables, c, first) {
793 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
794 void *u;
795
796 if (require_fallback && !c->is_fallback)
797 continue;
798
799 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
800 if (r < 0)
801 return bus_maybe_reply_error(m, r, &error);
802 if (bus->nodes_modified)
803 return 0;
804 if (r == 0)
805 continue;
806
807 *found_object = true;
808
809 if (iface && !streq(c->interface, iface))
810 continue;
811 found_interface = true;
812
813 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
814 if (r < 0)
815 return bus_maybe_reply_error(m, r, &error);
816 if (bus->nodes_modified)
817 return 0;
818 }
819
820 if (!found_interface) {
821 r = sd_bus_reply_method_errorf(
822 m,
823 SD_BUS_ERROR_UNKNOWN_INTERFACE,
824 "Unknown interface '%s'.", iface);
825 if (r < 0)
826 return r;
827
828 return 1;
829 }
830
831 r = sd_bus_message_close_container(reply);
832 if (r < 0)
833 return r;
834
835 r = sd_bus_send(bus, reply, NULL);
836 if (r < 0)
837 return r;
838
839 return 1;
840 }
841
842 static int bus_node_exists(
843 sd_bus *bus,
844 struct node *n,
845 const char *path,
846 bool require_fallback) {
847
848 struct node_vtable *c;
849 struct node_callback *k;
850 int r;
851
852 assert(bus);
853 assert(n);
854 assert(path);
855
856 /* Tests if there's anything attached directly to this node
857 * for the specified path */
858
859 if (!require_fallback && (n->enumerators || n->object_managers))
860 return true;
861
862 LIST_FOREACH(callbacks, k, n->callbacks) {
863 if (require_fallback && !k->is_fallback)
864 continue;
865
866 return 1;
867 }
868
869 LIST_FOREACH(vtables, c, n->vtables) {
870 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
871
872 if (require_fallback && !c->is_fallback)
873 continue;
874
875 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
876 if (r != 0)
877 return r;
878 if (bus->nodes_modified)
879 return 0;
880 }
881
882 return 0;
883 }
884
885 static int process_introspect(
886 sd_bus *bus,
887 sd_bus_message *m,
888 struct node *n,
889 bool require_fallback,
890 bool *found_object) {
891
892 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
893 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
894 _cleanup_set_free_free_ Set *s = NULL;
895 const char *previous_interface = NULL;
896 struct introspect intro;
897 struct node_vtable *c;
898 bool empty;
899 int r;
900
901 assert(bus);
902 assert(m);
903 assert(n);
904 assert(found_object);
905
906 r = get_child_nodes(bus, m->path, n, false, &s, &error);
907 if (r < 0)
908 return bus_maybe_reply_error(m, r, &error);
909 if (bus->nodes_modified)
910 return 0;
911
912 r = introspect_begin(&intro, bus->trusted);
913 if (r < 0)
914 return r;
915
916 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
917 if (r < 0)
918 return r;
919
920 empty = set_isempty(s);
921
922 LIST_FOREACH(vtables, c, n->vtables) {
923 if (require_fallback && !c->is_fallback)
924 continue;
925
926 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
927 if (r < 0) {
928 r = bus_maybe_reply_error(m, r, &error);
929 goto finish;
930 }
931 if (bus->nodes_modified) {
932 r = 0;
933 goto finish;
934 }
935 if (r == 0)
936 continue;
937
938 empty = false;
939
940 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
941 continue;
942
943 if (!streq_ptr(previous_interface, c->interface)) {
944
945 if (previous_interface)
946 fputs(" </interface>\n", intro.f);
947
948 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
949 }
950
951 r = introspect_write_interface(&intro, c->vtable);
952 if (r < 0)
953 goto finish;
954
955 previous_interface = c->interface;
956 }
957
958 if (previous_interface)
959 fputs(" </interface>\n", intro.f);
960
961 if (empty) {
962 /* Nothing?, let's see if we exist at all, and if not
963 * refuse to do anything */
964 r = bus_node_exists(bus, n, m->path, require_fallback);
965 if (r <= 0)
966 goto finish;
967 if (bus->nodes_modified) {
968 r = 0;
969 goto finish;
970 }
971 }
972
973 *found_object = true;
974
975 r = introspect_write_child_nodes(&intro, s, m->path);
976 if (r < 0)
977 goto finish;
978
979 r = introspect_finish(&intro, bus, m, &reply);
980 if (r < 0)
981 goto finish;
982
983 r = sd_bus_send(bus, reply, NULL);
984 if (r < 0)
985 goto finish;
986
987 r = 1;
988
989 finish:
990 introspect_free(&intro);
991 return r;
992 }
993
994 static int object_manager_serialize_path(
995 sd_bus *bus,
996 sd_bus_message *reply,
997 const char *prefix,
998 const char *path,
999 bool require_fallback,
1000 sd_bus_error *error) {
1001
1002 const char *previous_interface = NULL;
1003 bool found_something = false;
1004 struct node_vtable *i;
1005 struct node *n;
1006 int r;
1007
1008 assert(bus);
1009 assert(reply);
1010 assert(prefix);
1011 assert(path);
1012 assert(error);
1013
1014 n = hashmap_get(bus->nodes, prefix);
1015 if (!n)
1016 return 0;
1017
1018 LIST_FOREACH(vtables, i, n->vtables) {
1019 void *u;
1020
1021 if (require_fallback && !i->is_fallback)
1022 continue;
1023
1024 r = node_vtable_get_userdata(bus, path, i, &u, error);
1025 if (r < 0)
1026 return r;
1027 if (bus->nodes_modified)
1028 return 0;
1029 if (r == 0)
1030 continue;
1031
1032 if (!found_something) {
1033
1034 /* Open the object part */
1035
1036 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1037 if (r < 0)
1038 return r;
1039
1040 r = sd_bus_message_append(reply, "o", path);
1041 if (r < 0)
1042 return r;
1043
1044 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1045 if (r < 0)
1046 return r;
1047
1048 found_something = true;
1049 }
1050
1051 if (!streq_ptr(previous_interface, i->interface)) {
1052
1053 /* Maybe close the previous interface part */
1054
1055 if (previous_interface) {
1056 r = sd_bus_message_close_container(reply);
1057 if (r < 0)
1058 return r;
1059
1060 r = sd_bus_message_close_container(reply);
1061 if (r < 0)
1062 return r;
1063 }
1064
1065 /* Open the new interface part */
1066
1067 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1068 if (r < 0)
1069 return r;
1070
1071 r = sd_bus_message_append(reply, "s", i->interface);
1072 if (r < 0)
1073 return r;
1074
1075 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1076 if (r < 0)
1077 return r;
1078 }
1079
1080 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1081 if (r < 0)
1082 return r;
1083 if (bus->nodes_modified)
1084 return 0;
1085
1086 previous_interface = i->interface;
1087 }
1088
1089 if (previous_interface) {
1090 r = sd_bus_message_close_container(reply);
1091 if (r < 0)
1092 return r;
1093
1094 r = sd_bus_message_close_container(reply);
1095 if (r < 0)
1096 return r;
1097 }
1098
1099 if (found_something) {
1100 r = sd_bus_message_close_container(reply);
1101 if (r < 0)
1102 return r;
1103
1104 r = sd_bus_message_close_container(reply);
1105 if (r < 0)
1106 return r;
1107 }
1108
1109 return 1;
1110 }
1111
1112 static int object_manager_serialize_path_and_fallbacks(
1113 sd_bus *bus,
1114 sd_bus_message *reply,
1115 const char *path,
1116 sd_bus_error *error) {
1117
1118 char *prefix;
1119 int r;
1120
1121 assert(bus);
1122 assert(reply);
1123 assert(path);
1124 assert(error);
1125
1126 /* First, add all vtables registered for this path */
1127 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1128 if (r < 0)
1129 return r;
1130 if (bus->nodes_modified)
1131 return 0;
1132
1133 /* Second, add fallback vtables registered for any of the prefixes */
1134 prefix = alloca(strlen(path) + 1);
1135 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1136 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1137 if (r < 0)
1138 return r;
1139 if (bus->nodes_modified)
1140 return 0;
1141 }
1142
1143 return 0;
1144 }
1145
1146 static int process_get_managed_objects(
1147 sd_bus *bus,
1148 sd_bus_message *m,
1149 struct node *n,
1150 bool require_fallback,
1151 bool *found_object) {
1152
1153 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1154 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1155 _cleanup_set_free_free_ Set *s = NULL;
1156 Iterator i;
1157 char *path;
1158 int r;
1159
1160 assert(bus);
1161 assert(m);
1162 assert(n);
1163 assert(found_object);
1164
1165 /* Spec says, GetManagedObjects() is only implemented on the root of a
1166 * sub-tree. Therefore, we require a registered object-manager on
1167 * exactly the queried path, otherwise, we refuse to respond. */
1168
1169 if (require_fallback || !n->object_managers)
1170 return 0;
1171
1172 r = get_child_nodes(bus, m->path, n, true, &s, &error);
1173 if (r < 0)
1174 return r;
1175 if (bus->nodes_modified)
1176 return 0;
1177
1178 r = sd_bus_message_new_method_return(m, &reply);
1179 if (r < 0)
1180 return r;
1181
1182 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1183 if (r < 0)
1184 return r;
1185
1186 SET_FOREACH(path, s, i) {
1187 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1188 if (r < 0)
1189 return r;
1190
1191 if (bus->nodes_modified)
1192 return 0;
1193 }
1194
1195 r = sd_bus_message_close_container(reply);
1196 if (r < 0)
1197 return r;
1198
1199 r = sd_bus_send(bus, reply, NULL);
1200 if (r < 0)
1201 return r;
1202
1203 return 1;
1204 }
1205
1206 static int object_find_and_run(
1207 sd_bus *bus,
1208 sd_bus_message *m,
1209 const char *p,
1210 bool require_fallback,
1211 bool *found_object) {
1212
1213 struct node *n;
1214 struct vtable_member vtable_key, *v;
1215 int r;
1216
1217 assert(bus);
1218 assert(m);
1219 assert(p);
1220 assert(found_object);
1221
1222 n = hashmap_get(bus->nodes, p);
1223 if (!n)
1224 return 0;
1225
1226 /* First, try object callbacks */
1227 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1228 if (r != 0)
1229 return r;
1230 if (bus->nodes_modified)
1231 return 0;
1232
1233 if (!m->interface || !m->member)
1234 return 0;
1235
1236 /* Then, look for a known method */
1237 vtable_key.path = (char*) p;
1238 vtable_key.interface = m->interface;
1239 vtable_key.member = m->member;
1240
1241 v = hashmap_get(bus->vtable_methods, &vtable_key);
1242 if (v) {
1243 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1244 if (r != 0)
1245 return r;
1246 if (bus->nodes_modified)
1247 return 0;
1248 }
1249
1250 /* Then, look for a known property */
1251 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1252 bool get = false;
1253
1254 get = streq(m->member, "Get");
1255
1256 if (get || streq(m->member, "Set")) {
1257
1258 r = sd_bus_message_rewind(m, true);
1259 if (r < 0)
1260 return r;
1261
1262 vtable_key.path = (char*) p;
1263
1264 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1265 if (r < 0)
1266 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1267
1268 v = hashmap_get(bus->vtable_properties, &vtable_key);
1269 if (v) {
1270 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1271 if (r != 0)
1272 return r;
1273 }
1274
1275 } else if (streq(m->member, "GetAll")) {
1276 const char *iface;
1277
1278 r = sd_bus_message_rewind(m, true);
1279 if (r < 0)
1280 return r;
1281
1282 r = sd_bus_message_read(m, "s", &iface);
1283 if (r < 0)
1284 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1285
1286 if (iface[0] == 0)
1287 iface = NULL;
1288
1289 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1290 if (r != 0)
1291 return r;
1292 }
1293
1294 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1295
1296 if (!isempty(sd_bus_message_get_signature(m, true)))
1297 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1298
1299 r = process_introspect(bus, m, n, require_fallback, found_object);
1300 if (r != 0)
1301 return r;
1302
1303 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1304
1305 if (!isempty(sd_bus_message_get_signature(m, true)))
1306 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1307
1308 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1309 if (r != 0)
1310 return r;
1311 }
1312
1313 if (bus->nodes_modified)
1314 return 0;
1315
1316 if (!*found_object) {
1317 r = bus_node_exists(bus, n, m->path, require_fallback);
1318 if (r < 0)
1319 return r;
1320 if (bus->nodes_modified)
1321 return 0;
1322 if (r > 0)
1323 *found_object = true;
1324 }
1325
1326 return 0;
1327 }
1328
1329 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1330 int r;
1331 size_t pl;
1332 bool found_object = false;
1333
1334 assert(bus);
1335 assert(m);
1336
1337 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1338 return 0;
1339
1340 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1341 return 0;
1342
1343 if (hashmap_isempty(bus->nodes))
1344 return 0;
1345
1346 /* Never respond to broadcast messages */
1347 if (bus->bus_client && !m->destination)
1348 return 0;
1349
1350 assert(m->path);
1351 assert(m->member);
1352
1353 pl = strlen(m->path);
1354 do {
1355 char prefix[pl+1];
1356
1357 bus->nodes_modified = false;
1358
1359 r = object_find_and_run(bus, m, m->path, false, &found_object);
1360 if (r != 0)
1361 return r;
1362
1363 /* Look for fallback prefixes */
1364 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1365
1366 if (bus->nodes_modified)
1367 break;
1368
1369 r = object_find_and_run(bus, m, prefix, true, &found_object);
1370 if (r != 0)
1371 return r;
1372 }
1373
1374 } while (bus->nodes_modified);
1375
1376 if (!found_object)
1377 return 0;
1378
1379 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1380 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1381 r = sd_bus_reply_method_errorf(
1382 m,
1383 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1384 "Unknown property or interface.");
1385 else
1386 r = sd_bus_reply_method_errorf(
1387 m,
1388 SD_BUS_ERROR_UNKNOWN_METHOD,
1389 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1390
1391 if (r < 0)
1392 return r;
1393
1394 return 1;
1395 }
1396
1397 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1398 struct node *n, *parent;
1399 const char *e;
1400 _cleanup_free_ char *s = NULL;
1401 char *p;
1402 int r;
1403
1404 assert(bus);
1405 assert(path);
1406 assert(path[0] == '/');
1407
1408 n = hashmap_get(bus->nodes, path);
1409 if (n)
1410 return n;
1411
1412 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1413 if (r < 0)
1414 return NULL;
1415
1416 s = strdup(path);
1417 if (!s)
1418 return NULL;
1419
1420 if (streq(path, "/"))
1421 parent = NULL;
1422 else {
1423 e = strrchr(path, '/');
1424 assert(e);
1425
1426 p = strndupa(path, MAX(1, e - path));
1427
1428 parent = bus_node_allocate(bus, p);
1429 if (!parent)
1430 return NULL;
1431 }
1432
1433 n = new0(struct node, 1);
1434 if (!n)
1435 return NULL;
1436
1437 n->parent = parent;
1438 n->path = s;
1439 s = NULL; /* do not free */
1440
1441 r = hashmap_put(bus->nodes, n->path, n);
1442 if (r < 0) {
1443 free(n->path);
1444 free(n);
1445 return NULL;
1446 }
1447
1448 if (parent)
1449 LIST_PREPEND(siblings, parent->child, n);
1450
1451 return n;
1452 }
1453
1454 void bus_node_gc(sd_bus *b, struct node *n) {
1455 assert(b);
1456
1457 if (!n)
1458 return;
1459
1460 if (n->child ||
1461 n->callbacks ||
1462 n->vtables ||
1463 n->enumerators ||
1464 n->object_managers)
1465 return;
1466
1467 assert(hashmap_remove(b->nodes, n->path) == n);
1468
1469 if (n->parent)
1470 LIST_REMOVE(siblings, n->parent->child, n);
1471
1472 free(n->path);
1473 bus_node_gc(b, n->parent);
1474 free(n);
1475 }
1476
1477 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1478 struct node *n;
1479
1480 assert(bus);
1481 assert(path);
1482
1483 n = hashmap_get(bus->nodes, path);
1484 if (!n) {
1485 char *prefix;
1486
1487 prefix = alloca(strlen(path) + 1);
1488 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1489 n = hashmap_get(bus->nodes, prefix);
1490 if (n)
1491 break;
1492 }
1493 }
1494
1495 while (n && !n->object_managers)
1496 n = n->parent;
1497
1498 if (out)
1499 *out = n;
1500 return !!n;
1501 }
1502
1503 static int bus_add_object(
1504 sd_bus *bus,
1505 sd_bus_slot **slot,
1506 bool fallback,
1507 const char *path,
1508 sd_bus_message_handler_t callback,
1509 void *userdata) {
1510
1511 sd_bus_slot *s;
1512 struct node *n;
1513 int r;
1514
1515 assert_return(bus, -EINVAL);
1516 assert_return(object_path_is_valid(path), -EINVAL);
1517 assert_return(callback, -EINVAL);
1518 assert_return(!bus_pid_changed(bus), -ECHILD);
1519
1520 n = bus_node_allocate(bus, path);
1521 if (!n)
1522 return -ENOMEM;
1523
1524 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1525 if (!s) {
1526 r = -ENOMEM;
1527 goto fail;
1528 }
1529
1530 s->node_callback.callback = callback;
1531 s->node_callback.is_fallback = fallback;
1532
1533 s->node_callback.node = n;
1534 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1535 bus->nodes_modified = true;
1536
1537 if (slot)
1538 *slot = s;
1539
1540 return 0;
1541
1542 fail:
1543 sd_bus_slot_unref(s);
1544 bus_node_gc(bus, n);
1545
1546 return r;
1547 }
1548
1549 _public_ int sd_bus_add_object(
1550 sd_bus *bus,
1551 sd_bus_slot **slot,
1552 const char *path,
1553 sd_bus_message_handler_t callback,
1554 void *userdata) {
1555
1556 return bus_add_object(bus, slot, false, path, callback, userdata);
1557 }
1558
1559 _public_ int sd_bus_add_fallback(
1560 sd_bus *bus,
1561 sd_bus_slot **slot,
1562 const char *prefix,
1563 sd_bus_message_handler_t callback,
1564 void *userdata) {
1565
1566 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1567 }
1568
1569 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1570 const struct vtable_member *m = a;
1571 uint8_t hash_key2[HASH_KEY_SIZE];
1572 unsigned long ret;
1573
1574 assert(m);
1575
1576 ret = string_hash_func(m->path, hash_key);
1577
1578 /* Use a slightly different hash key for the interface */
1579 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1580 hash_key2[0]++;
1581 ret ^= string_hash_func(m->interface, hash_key2);
1582
1583 /* And an even different one for the member */
1584 hash_key2[0]++;
1585 ret ^= string_hash_func(m->member, hash_key2);
1586
1587 return ret;
1588 }
1589
1590 static int vtable_member_compare_func(const void *a, const void *b) {
1591 const struct vtable_member *x = a, *y = b;
1592 int r;
1593
1594 assert(x);
1595 assert(y);
1596
1597 r = strcmp(x->path, y->path);
1598 if (r != 0)
1599 return r;
1600
1601 r = strcmp(x->interface, y->interface);
1602 if (r != 0)
1603 return r;
1604
1605 return strcmp(x->member, y->member);
1606 }
1607
1608 static const struct hash_ops vtable_member_hash_ops = {
1609 .hash = vtable_member_hash_func,
1610 .compare = vtable_member_compare_func
1611 };
1612
1613 static int add_object_vtable_internal(
1614 sd_bus *bus,
1615 sd_bus_slot **slot,
1616 const char *path,
1617 const char *interface,
1618 const sd_bus_vtable *vtable,
1619 bool fallback,
1620 sd_bus_object_find_t find,
1621 void *userdata) {
1622
1623 sd_bus_slot *s = NULL;
1624 struct node_vtable *i, *existing = NULL;
1625 const sd_bus_vtable *v;
1626 struct node *n;
1627 int r;
1628
1629 assert_return(bus, -EINVAL);
1630 assert_return(object_path_is_valid(path), -EINVAL);
1631 assert_return(interface_name_is_valid(interface), -EINVAL);
1632 assert_return(vtable, -EINVAL);
1633 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1634 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1635 assert_return(!bus_pid_changed(bus), -ECHILD);
1636 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1637 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1638 !streq(interface, "org.freedesktop.DBus.Peer") &&
1639 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1640
1641 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1642 if (r < 0)
1643 return r;
1644
1645 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1646 if (r < 0)
1647 return r;
1648
1649 n = bus_node_allocate(bus, path);
1650 if (!n)
1651 return -ENOMEM;
1652
1653 LIST_FOREACH(vtables, i, n->vtables) {
1654 if (i->is_fallback != fallback) {
1655 r = -EPROTOTYPE;
1656 goto fail;
1657 }
1658
1659 if (streq(i->interface, interface)) {
1660
1661 if (i->vtable == vtable) {
1662 r = -EEXIST;
1663 goto fail;
1664 }
1665
1666 existing = i;
1667 }
1668 }
1669
1670 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1671 if (!s) {
1672 r = -ENOMEM;
1673 goto fail;
1674 }
1675
1676 s->node_vtable.is_fallback = fallback;
1677 s->node_vtable.vtable = vtable;
1678 s->node_vtable.find = find;
1679
1680 s->node_vtable.interface = strdup(interface);
1681 if (!s->node_vtable.interface) {
1682 r = -ENOMEM;
1683 goto fail;
1684 }
1685
1686 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1687
1688 switch (v->type) {
1689
1690 case _SD_BUS_VTABLE_METHOD: {
1691 struct vtable_member *m;
1692
1693 if (!member_name_is_valid(v->x.method.member) ||
1694 !signature_is_valid(strempty(v->x.method.signature), false) ||
1695 !signature_is_valid(strempty(v->x.method.result), false) ||
1696 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1697 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1698 r = -EINVAL;
1699 goto fail;
1700 }
1701
1702 m = new0(struct vtable_member, 1);
1703 if (!m) {
1704 r = -ENOMEM;
1705 goto fail;
1706 }
1707
1708 m->parent = &s->node_vtable;
1709 m->path = n->path;
1710 m->interface = s->node_vtable.interface;
1711 m->member = v->x.method.member;
1712 m->vtable = v;
1713
1714 r = hashmap_put(bus->vtable_methods, m, m);
1715 if (r < 0) {
1716 free(m);
1717 goto fail;
1718 }
1719
1720 break;
1721 }
1722
1723 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1724
1725 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1726 r = -EINVAL;
1727 goto fail;
1728 }
1729
1730 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1731 r = -EINVAL;
1732 goto fail;
1733 }
1734
1735 /* Fall through */
1736
1737 case _SD_BUS_VTABLE_PROPERTY: {
1738 struct vtable_member *m;
1739
1740 if (!member_name_is_valid(v->x.property.member) ||
1741 !signature_is_single(v->x.property.signature, false) ||
1742 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1743 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1744 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1745 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1746 r = -EINVAL;
1747 goto fail;
1748 }
1749
1750 m = new0(struct vtable_member, 1);
1751 if (!m) {
1752 r = -ENOMEM;
1753 goto fail;
1754 }
1755
1756 m->parent = &s->node_vtable;
1757 m->path = n->path;
1758 m->interface = s->node_vtable.interface;
1759 m->member = v->x.property.member;
1760 m->vtable = v;
1761
1762 r = hashmap_put(bus->vtable_properties, m, m);
1763 if (r < 0) {
1764 free(m);
1765 goto fail;
1766 }
1767
1768 break;
1769 }
1770
1771 case _SD_BUS_VTABLE_SIGNAL:
1772
1773 if (!member_name_is_valid(v->x.signal.member) ||
1774 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1775 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1776 r = -EINVAL;
1777 goto fail;
1778 }
1779
1780 break;
1781
1782 default:
1783 r = -EINVAL;
1784 goto fail;
1785 }
1786 }
1787
1788 s->node_vtable.node = n;
1789 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1790 bus->nodes_modified = true;
1791
1792 if (slot)
1793 *slot = s;
1794
1795 return 0;
1796
1797 fail:
1798 sd_bus_slot_unref(s);
1799 bus_node_gc(bus, n);
1800
1801 return r;
1802 }
1803
1804 _public_ int sd_bus_add_object_vtable(
1805 sd_bus *bus,
1806 sd_bus_slot **slot,
1807 const char *path,
1808 const char *interface,
1809 const sd_bus_vtable *vtable,
1810 void *userdata) {
1811
1812 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1813 }
1814
1815 _public_ int sd_bus_add_fallback_vtable(
1816 sd_bus *bus,
1817 sd_bus_slot **slot,
1818 const char *prefix,
1819 const char *interface,
1820 const sd_bus_vtable *vtable,
1821 sd_bus_object_find_t find,
1822 void *userdata) {
1823
1824 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1825 }
1826
1827 _public_ int sd_bus_add_node_enumerator(
1828 sd_bus *bus,
1829 sd_bus_slot **slot,
1830 const char *path,
1831 sd_bus_node_enumerator_t callback,
1832 void *userdata) {
1833
1834 sd_bus_slot *s;
1835 struct node *n;
1836 int r;
1837
1838 assert_return(bus, -EINVAL);
1839 assert_return(object_path_is_valid(path), -EINVAL);
1840 assert_return(callback, -EINVAL);
1841 assert_return(!bus_pid_changed(bus), -ECHILD);
1842
1843 n = bus_node_allocate(bus, path);
1844 if (!n)
1845 return -ENOMEM;
1846
1847 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1848 if (!s) {
1849 r = -ENOMEM;
1850 goto fail;
1851 }
1852
1853 s->node_enumerator.callback = callback;
1854
1855 s->node_enumerator.node = n;
1856 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1857 bus->nodes_modified = true;
1858
1859 if (slot)
1860 *slot = s;
1861
1862 return 0;
1863
1864 fail:
1865 sd_bus_slot_unref(s);
1866 bus_node_gc(bus, n);
1867
1868 return r;
1869 }
1870
1871 static int emit_properties_changed_on_interface(
1872 sd_bus *bus,
1873 const char *prefix,
1874 const char *path,
1875 const char *interface,
1876 bool require_fallback,
1877 bool *found_interface,
1878 char **names) {
1879
1880 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1881 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1882 bool has_invalidating = false, has_changing = false;
1883 struct vtable_member key = {};
1884 struct node_vtable *c;
1885 struct node *n;
1886 char **property;
1887 void *u = NULL;
1888 int r;
1889
1890 assert(bus);
1891 assert(prefix);
1892 assert(path);
1893 assert(interface);
1894 assert(found_interface);
1895
1896 n = hashmap_get(bus->nodes, prefix);
1897 if (!n)
1898 return 0;
1899
1900 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1901 if (r < 0)
1902 return r;
1903
1904 r = sd_bus_message_append(m, "s", interface);
1905 if (r < 0)
1906 return r;
1907
1908 r = sd_bus_message_open_container(m, 'a', "{sv}");
1909 if (r < 0)
1910 return r;
1911
1912 key.path = prefix;
1913 key.interface = interface;
1914
1915 LIST_FOREACH(vtables, c, n->vtables) {
1916 if (require_fallback && !c->is_fallback)
1917 continue;
1918
1919 if (!streq(c->interface, interface))
1920 continue;
1921
1922 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1923 if (r < 0)
1924 return r;
1925 if (bus->nodes_modified)
1926 return 0;
1927 if (r == 0)
1928 continue;
1929
1930 *found_interface = true;
1931
1932 if (names) {
1933 /* If the caller specified a list of
1934 * properties we include exactly those in the
1935 * PropertiesChanged message */
1936
1937 STRV_FOREACH(property, names) {
1938 struct vtable_member *v;
1939
1940 assert_return(member_name_is_valid(*property), -EINVAL);
1941
1942 key.member = *property;
1943 v = hashmap_get(bus->vtable_properties, &key);
1944 if (!v)
1945 return -ENOENT;
1946
1947 /* If there are two vtables for the same
1948 * interface, let's handle this property when
1949 * we come to that vtable. */
1950 if (c != v->parent)
1951 continue;
1952
1953 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1954 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1955
1956 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1957
1958 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1959 has_invalidating = true;
1960 continue;
1961 }
1962
1963 has_changing = true;
1964
1965 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1966 if (r < 0)
1967 return r;
1968 if (bus->nodes_modified)
1969 return 0;
1970 }
1971 } else {
1972 const sd_bus_vtable *v;
1973
1974 /* If the caller specified no properties list
1975 * we include all properties that are marked
1976 * as changing in the message. */
1977
1978 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1979 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1980 continue;
1981
1982 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1983 continue;
1984
1985 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1986 has_invalidating = true;
1987 continue;
1988 }
1989
1990 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1991 continue;
1992
1993 has_changing = true;
1994
1995 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1996 if (r < 0)
1997 return r;
1998 if (bus->nodes_modified)
1999 return 0;
2000 }
2001 }
2002 }
2003
2004 if (!has_invalidating && !has_changing)
2005 return 0;
2006
2007 r = sd_bus_message_close_container(m);
2008 if (r < 0)
2009 return r;
2010
2011 r = sd_bus_message_open_container(m, 'a', "s");
2012 if (r < 0)
2013 return r;
2014
2015 if (has_invalidating) {
2016 LIST_FOREACH(vtables, c, n->vtables) {
2017 if (require_fallback && !c->is_fallback)
2018 continue;
2019
2020 if (!streq(c->interface, interface))
2021 continue;
2022
2023 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2024 if (r < 0)
2025 return r;
2026 if (bus->nodes_modified)
2027 return 0;
2028 if (r == 0)
2029 continue;
2030
2031 if (names) {
2032 STRV_FOREACH(property, names) {
2033 struct vtable_member *v;
2034
2035 key.member = *property;
2036 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2037 assert(c == v->parent);
2038
2039 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2040 continue;
2041
2042 r = sd_bus_message_append(m, "s", *property);
2043 if (r < 0)
2044 return r;
2045 }
2046 } else {
2047 const sd_bus_vtable *v;
2048
2049 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2050 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2051 continue;
2052
2053 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2054 continue;
2055
2056 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2057 continue;
2058
2059 r = sd_bus_message_append(m, "s", v->x.property.member);
2060 if (r < 0)
2061 return r;
2062 }
2063 }
2064 }
2065 }
2066
2067 r = sd_bus_message_close_container(m);
2068 if (r < 0)
2069 return r;
2070
2071 r = sd_bus_send(bus, m, NULL);
2072 if (r < 0)
2073 return r;
2074
2075 return 1;
2076 }
2077
2078 _public_ int sd_bus_emit_properties_changed_strv(
2079 sd_bus *bus,
2080 const char *path,
2081 const char *interface,
2082 char **names) {
2083
2084 BUS_DONT_DESTROY(bus);
2085 bool found_interface = false;
2086 char *prefix;
2087 int r;
2088
2089 assert_return(bus, -EINVAL);
2090 assert_return(object_path_is_valid(path), -EINVAL);
2091 assert_return(interface_name_is_valid(interface), -EINVAL);
2092 assert_return(!bus_pid_changed(bus), -ECHILD);
2093
2094 if (!BUS_IS_OPEN(bus->state))
2095 return -ENOTCONN;
2096
2097 /* A non-NULL but empty names list means nothing needs to be
2098 generated. A NULL list OTOH indicates that all properties
2099 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2100 included in the PropertiesChanged message. */
2101 if (names && names[0] == NULL)
2102 return 0;
2103
2104 do {
2105 bus->nodes_modified = false;
2106
2107 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2108 if (r != 0)
2109 return r;
2110 if (bus->nodes_modified)
2111 continue;
2112
2113 prefix = alloca(strlen(path) + 1);
2114 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2115 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2116 if (r != 0)
2117 return r;
2118 if (bus->nodes_modified)
2119 break;
2120 }
2121
2122 } while (bus->nodes_modified);
2123
2124 return found_interface ? 0 : -ENOENT;
2125 }
2126
2127 _public_ int sd_bus_emit_properties_changed(
2128 sd_bus *bus,
2129 const char *path,
2130 const char *interface,
2131 const char *name, ...) {
2132
2133 char **names;
2134
2135 assert_return(bus, -EINVAL);
2136 assert_return(object_path_is_valid(path), -EINVAL);
2137 assert_return(interface_name_is_valid(interface), -EINVAL);
2138 assert_return(!bus_pid_changed(bus), -ECHILD);
2139
2140 if (!BUS_IS_OPEN(bus->state))
2141 return -ENOTCONN;
2142
2143 if (!name)
2144 return 0;
2145
2146 names = strv_from_stdarg_alloca(name);
2147
2148 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2149 }
2150
2151 static int object_added_append_all_prefix(
2152 sd_bus *bus,
2153 sd_bus_message *m,
2154 Set *s,
2155 const char *prefix,
2156 const char *path,
2157 bool require_fallback) {
2158
2159 const char *previous_interface = NULL;
2160 struct node_vtable *c;
2161 struct node *n;
2162 int r;
2163
2164 assert(bus);
2165 assert(m);
2166 assert(s);
2167 assert(prefix);
2168 assert(path);
2169
2170 n = hashmap_get(bus->nodes, prefix);
2171 if (!n)
2172 return 0;
2173
2174 LIST_FOREACH(vtables, c, n->vtables) {
2175 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2176 void *u = NULL;
2177
2178 if (require_fallback && !c->is_fallback)
2179 continue;
2180
2181 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2182 if (r < 0)
2183 return r;
2184 if (bus->nodes_modified)
2185 return 0;
2186 if (r == 0)
2187 continue;
2188
2189 if (!streq_ptr(c->interface, previous_interface)) {
2190 /* If a child-node already handled this interface, we
2191 * skip it on any of its parents. The child vtables
2192 * always fully override any conflicting vtables of
2193 * any parent node. */
2194 if (set_get(s, c->interface))
2195 continue;
2196
2197 r = set_put(s, c->interface);
2198 if (r < 0)
2199 return r;
2200
2201 if (previous_interface) {
2202 r = sd_bus_message_close_container(m);
2203 if (r < 0)
2204 return r;
2205 r = sd_bus_message_close_container(m);
2206 if (r < 0)
2207 return r;
2208 }
2209
2210 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2211 if (r < 0)
2212 return r;
2213 r = sd_bus_message_append(m, "s", c->interface);
2214 if (r < 0)
2215 return r;
2216 r = sd_bus_message_open_container(m, 'a', "{sv}");
2217 if (r < 0)
2218 return r;
2219
2220 previous_interface = c->interface;
2221 }
2222
2223 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2224 if (r < 0)
2225 return r;
2226 if (bus->nodes_modified)
2227 return 0;
2228 }
2229
2230 if (previous_interface) {
2231 r = sd_bus_message_close_container(m);
2232 if (r < 0)
2233 return r;
2234 r = sd_bus_message_close_container(m);
2235 if (r < 0)
2236 return r;
2237 }
2238
2239 return 0;
2240 }
2241
2242 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2243 _cleanup_set_free_ Set *s = NULL;
2244 char *prefix;
2245 int r;
2246
2247 assert(bus);
2248 assert(m);
2249 assert(path);
2250
2251 /*
2252 * This appends all interfaces registered on path @path. We first add
2253 * the builtin interfaces, which are always available and handled by
2254 * sd-bus. Then, we add all interfaces registered on the exact node,
2255 * followed by all fallback interfaces registered on any parent prefix.
2256 *
2257 * If an interface is registered multiple times on the same node with
2258 * different vtables, we merge all the properties across all vtables.
2259 * However, if a child node has the same interface registered as one of
2260 * its parent nodes has as fallback, we make the child overwrite the
2261 * parent instead of extending it. Therefore, we keep a "Set" of all
2262 * handled interfaces during parent traversal, so we skip interfaces on
2263 * a parent that were overwritten by a child.
2264 */
2265
2266 s = set_new(&string_hash_ops);
2267 if (!s)
2268 return -ENOMEM;
2269
2270 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2271 if (r < 0)
2272 return r;
2273 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2274 if (r < 0)
2275 return r;
2276 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2277 if (r < 0)
2278 return r;
2279 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2280 if (r < 0)
2281 return r;
2282
2283 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2284 if (r < 0)
2285 return r;
2286 if (bus->nodes_modified)
2287 return 0;
2288
2289 prefix = alloca(strlen(path) + 1);
2290 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2291 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2292 if (r < 0)
2293 return r;
2294 if (bus->nodes_modified)
2295 return 0;
2296 }
2297
2298 return 0;
2299 }
2300
2301 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2302 BUS_DONT_DESTROY(bus);
2303
2304 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2305 struct node *object_manager;
2306 int r;
2307
2308 /*
2309 * This emits an InterfacesAdded signal on the given path, by iterating
2310 * all registered vtables and fallback vtables on the path. All
2311 * properties are queried and included in the signal.
2312 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2313 * explicit list of registered interfaces. However, unlike
2314 * interfaces_added(), this call can figure out the list of supported
2315 * interfaces itself. Furthermore, it properly adds the builtin
2316 * org.freedesktop.DBus.* interfaces.
2317 */
2318
2319 assert_return(bus, -EINVAL);
2320 assert_return(object_path_is_valid(path), -EINVAL);
2321 assert_return(!bus_pid_changed(bus), -ECHILD);
2322
2323 if (!BUS_IS_OPEN(bus->state))
2324 return -ENOTCONN;
2325
2326 r = bus_find_parent_object_manager(bus, &object_manager, path);
2327 if (r < 0)
2328 return r;
2329 if (r == 0)
2330 return -ESRCH;
2331
2332 do {
2333 bus->nodes_modified = false;
2334 m = sd_bus_message_unref(m);
2335
2336 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2337 if (r < 0)
2338 return r;
2339
2340 r = sd_bus_message_append_basic(m, 'o', path);
2341 if (r < 0)
2342 return r;
2343
2344 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2345 if (r < 0)
2346 return r;
2347
2348 r = object_added_append_all(bus, m, path);
2349 if (r < 0)
2350 return r;
2351
2352 if (bus->nodes_modified)
2353 continue;
2354
2355 r = sd_bus_message_close_container(m);
2356 if (r < 0)
2357 return r;
2358
2359 } while (bus->nodes_modified);
2360
2361 return sd_bus_send(bus, m, NULL);
2362 }
2363
2364 static int object_removed_append_all_prefix(
2365 sd_bus *bus,
2366 sd_bus_message *m,
2367 Set *s,
2368 const char *prefix,
2369 const char *path,
2370 bool require_fallback) {
2371
2372 const char *previous_interface = NULL;
2373 struct node_vtable *c;
2374 struct node *n;
2375 int r;
2376
2377 assert(bus);
2378 assert(m);
2379 assert(s);
2380 assert(prefix);
2381 assert(path);
2382
2383 n = hashmap_get(bus->nodes, prefix);
2384 if (!n)
2385 return 0;
2386
2387 LIST_FOREACH(vtables, c, n->vtables) {
2388 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2389 void *u = NULL;
2390
2391 if (require_fallback && !c->is_fallback)
2392 continue;
2393 if (streq_ptr(c->interface, previous_interface))
2394 continue;
2395
2396 /* If a child-node already handled this interface, we
2397 * skip it on any of its parents. The child vtables
2398 * always fully override any conflicting vtables of
2399 * any parent node. */
2400 if (set_get(s, c->interface))
2401 continue;
2402
2403 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2404 if (r < 0)
2405 return r;
2406 if (bus->nodes_modified)
2407 return 0;
2408 if (r == 0)
2409 continue;
2410
2411 r = set_put(s, c->interface);
2412 if (r < 0)
2413 return r;
2414
2415 r = sd_bus_message_append(m, "s", c->interface);
2416 if (r < 0)
2417 return r;
2418
2419 previous_interface = c->interface;
2420 }
2421
2422 return 0;
2423 }
2424
2425 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2426 _cleanup_set_free_ Set *s = NULL;
2427 char *prefix;
2428 int r;
2429
2430 assert(bus);
2431 assert(m);
2432 assert(path);
2433
2434 /* see sd_bus_emit_object_added() for details */
2435
2436 s = set_new(&string_hash_ops);
2437 if (!s)
2438 return -ENOMEM;
2439
2440 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2441 if (r < 0)
2442 return r;
2443 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2444 if (r < 0)
2445 return r;
2446 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2447 if (r < 0)
2448 return r;
2449 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2450 if (r < 0)
2451 return r;
2452
2453 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2454 if (r < 0)
2455 return r;
2456 if (bus->nodes_modified)
2457 return 0;
2458
2459 prefix = alloca(strlen(path) + 1);
2460 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2461 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2462 if (r < 0)
2463 return r;
2464 if (bus->nodes_modified)
2465 return 0;
2466 }
2467
2468 return 0;
2469 }
2470
2471 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2472 BUS_DONT_DESTROY(bus);
2473
2474 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2475 struct node *object_manager;
2476 int r;
2477
2478 /*
2479 * This is like sd_bus_emit_object_added(), but emits an
2480 * InterfacesRemoved signal on the given path. This only includes any
2481 * registered interfaces but skips the properties. Note that this will
2482 * call into the find() callbacks of any registered vtable. Therefore,
2483 * you must call this function before destroying/unlinking your object.
2484 * Otherwise, the list of interfaces will be incomplete. However, note
2485 * that this will *NOT* call into any property callback. Therefore, the
2486 * object might be in an "destructed" state, as long as we can find it.
2487 */
2488
2489 assert_return(bus, -EINVAL);
2490 assert_return(object_path_is_valid(path), -EINVAL);
2491 assert_return(!bus_pid_changed(bus), -ECHILD);
2492
2493 if (!BUS_IS_OPEN(bus->state))
2494 return -ENOTCONN;
2495
2496 r = bus_find_parent_object_manager(bus, &object_manager, path);
2497 if (r < 0)
2498 return r;
2499 if (r == 0)
2500 return -ESRCH;
2501
2502 do {
2503 bus->nodes_modified = false;
2504 m = sd_bus_message_unref(m);
2505
2506 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2507 if (r < 0)
2508 return r;
2509
2510 r = sd_bus_message_append_basic(m, 'o', path);
2511 if (r < 0)
2512 return r;
2513
2514 r = sd_bus_message_open_container(m, 'a', "s");
2515 if (r < 0)
2516 return r;
2517
2518 r = object_removed_append_all(bus, m, path);
2519 if (r < 0)
2520 return r;
2521
2522 if (bus->nodes_modified)
2523 continue;
2524
2525 r = sd_bus_message_close_container(m);
2526 if (r < 0)
2527 return r;
2528
2529 } while (bus->nodes_modified);
2530
2531 return sd_bus_send(bus, m, NULL);
2532 }
2533
2534 static int interfaces_added_append_one_prefix(
2535 sd_bus *bus,
2536 sd_bus_message *m,
2537 const char *prefix,
2538 const char *path,
2539 const char *interface,
2540 bool require_fallback) {
2541
2542 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2543 bool found_interface = false;
2544 struct node_vtable *c;
2545 struct node *n;
2546 void *u = NULL;
2547 int r;
2548
2549 assert(bus);
2550 assert(m);
2551 assert(prefix);
2552 assert(path);
2553 assert(interface);
2554
2555 n = hashmap_get(bus->nodes, prefix);
2556 if (!n)
2557 return 0;
2558
2559 LIST_FOREACH(vtables, c, n->vtables) {
2560 if (require_fallback && !c->is_fallback)
2561 continue;
2562
2563 if (!streq(c->interface, interface))
2564 continue;
2565
2566 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2567 if (r < 0)
2568 return r;
2569 if (bus->nodes_modified)
2570 return 0;
2571 if (r == 0)
2572 continue;
2573
2574 if (!found_interface) {
2575 r = sd_bus_message_append_basic(m, 's', interface);
2576 if (r < 0)
2577 return r;
2578
2579 r = sd_bus_message_open_container(m, 'a', "{sv}");
2580 if (r < 0)
2581 return r;
2582
2583 found_interface = true;
2584 }
2585
2586 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2587 if (r < 0)
2588 return r;
2589 if (bus->nodes_modified)
2590 return 0;
2591 }
2592
2593 if (found_interface) {
2594 r = sd_bus_message_close_container(m);
2595 if (r < 0)
2596 return r;
2597 }
2598
2599 return found_interface;
2600 }
2601
2602 static int interfaces_added_append_one(
2603 sd_bus *bus,
2604 sd_bus_message *m,
2605 const char *path,
2606 const char *interface) {
2607
2608 char *prefix;
2609 int r;
2610
2611 assert(bus);
2612 assert(m);
2613 assert(path);
2614 assert(interface);
2615
2616 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2617 if (r != 0)
2618 return r;
2619 if (bus->nodes_modified)
2620 return 0;
2621
2622 prefix = alloca(strlen(path) + 1);
2623 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2624 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2625 if (r != 0)
2626 return r;
2627 if (bus->nodes_modified)
2628 return 0;
2629 }
2630
2631 return -ENOENT;
2632 }
2633
2634 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2635 BUS_DONT_DESTROY(bus);
2636
2637 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2638 struct node *object_manager;
2639 char **i;
2640 int r;
2641
2642 assert_return(bus, -EINVAL);
2643 assert_return(object_path_is_valid(path), -EINVAL);
2644 assert_return(!bus_pid_changed(bus), -ECHILD);
2645
2646 if (!BUS_IS_OPEN(bus->state))
2647 return -ENOTCONN;
2648
2649 if (strv_isempty(interfaces))
2650 return 0;
2651
2652 r = bus_find_parent_object_manager(bus, &object_manager, path);
2653 if (r < 0)
2654 return r;
2655 if (r == 0)
2656 return -ESRCH;
2657
2658 do {
2659 bus->nodes_modified = false;
2660 m = sd_bus_message_unref(m);
2661
2662 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2663 if (r < 0)
2664 return r;
2665
2666 r = sd_bus_message_append_basic(m, 'o', path);
2667 if (r < 0)
2668 return r;
2669
2670 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2671 if (r < 0)
2672 return r;
2673
2674 STRV_FOREACH(i, interfaces) {
2675 assert_return(interface_name_is_valid(*i), -EINVAL);
2676
2677 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2678 if (r < 0)
2679 return r;
2680
2681 r = interfaces_added_append_one(bus, m, path, *i);
2682 if (r < 0)
2683 return r;
2684
2685 if (bus->nodes_modified)
2686 break;
2687
2688 r = sd_bus_message_close_container(m);
2689 if (r < 0)
2690 return r;
2691 }
2692
2693 if (bus->nodes_modified)
2694 continue;
2695
2696 r = sd_bus_message_close_container(m);
2697 if (r < 0)
2698 return r;
2699
2700 } while (bus->nodes_modified);
2701
2702 return sd_bus_send(bus, m, NULL);
2703 }
2704
2705 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2706 char **interfaces;
2707
2708 assert_return(bus, -EINVAL);
2709 assert_return(object_path_is_valid(path), -EINVAL);
2710 assert_return(!bus_pid_changed(bus), -ECHILD);
2711
2712 if (!BUS_IS_OPEN(bus->state))
2713 return -ENOTCONN;
2714
2715 interfaces = strv_from_stdarg_alloca(interface);
2716
2717 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2718 }
2719
2720 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2721 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2722 struct node *object_manager;
2723 int r;
2724
2725 assert_return(bus, -EINVAL);
2726 assert_return(object_path_is_valid(path), -EINVAL);
2727 assert_return(!bus_pid_changed(bus), -ECHILD);
2728
2729 if (!BUS_IS_OPEN(bus->state))
2730 return -ENOTCONN;
2731
2732 if (strv_isempty(interfaces))
2733 return 0;
2734
2735 r = bus_find_parent_object_manager(bus, &object_manager, path);
2736 if (r < 0)
2737 return r;
2738 if (r == 0)
2739 return -ESRCH;
2740
2741 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2742 if (r < 0)
2743 return r;
2744
2745 r = sd_bus_message_append_basic(m, 'o', path);
2746 if (r < 0)
2747 return r;
2748
2749 r = sd_bus_message_append_strv(m, interfaces);
2750 if (r < 0)
2751 return r;
2752
2753 return sd_bus_send(bus, m, NULL);
2754 }
2755
2756 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2757 char **interfaces;
2758
2759 assert_return(bus, -EINVAL);
2760 assert_return(object_path_is_valid(path), -EINVAL);
2761 assert_return(!bus_pid_changed(bus), -ECHILD);
2762
2763 if (!BUS_IS_OPEN(bus->state))
2764 return -ENOTCONN;
2765
2766 interfaces = strv_from_stdarg_alloca(interface);
2767
2768 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2769 }
2770
2771 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2772 sd_bus_slot *s;
2773 struct node *n;
2774 int r;
2775
2776 assert_return(bus, -EINVAL);
2777 assert_return(object_path_is_valid(path), -EINVAL);
2778 assert_return(!bus_pid_changed(bus), -ECHILD);
2779
2780 n = bus_node_allocate(bus, path);
2781 if (!n)
2782 return -ENOMEM;
2783
2784 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2785 if (!s) {
2786 r = -ENOMEM;
2787 goto fail;
2788 }
2789
2790 s->node_object_manager.node = n;
2791 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2792 bus->nodes_modified = true;
2793
2794 if (slot)
2795 *slot = s;
2796
2797 return 0;
2798
2799 fail:
2800 sd_bus_slot_unref(s);
2801 bus_node_gc(bus, n);
2802
2803 return r;
2804 }