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