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