1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include "alloc-util.h"
22 #include "bus-internal.h"
23 #include "bus-introspect.h"
24 #include "bus-message.h"
25 #include "bus-objects.h"
26 #include "bus-signature.h"
31 #include "string-util.h"
34 static int node_vtable_get_userdata(
37 struct node_vtable
*c
,
39 sd_bus_error
*error
) {
49 s
= container_of(c
, sd_bus_slot
, node_vtable
);
52 bus
->current_slot
= sd_bus_slot_ref(s
);
53 bus
->current_userdata
= u
;
54 r
= c
->find(bus
, path
, c
->interface
, u
, &found_u
, error
);
55 bus
->current_userdata
= NULL
;
56 bus
->current_slot
= sd_bus_slot_unref(s
);
60 if (sd_bus_error_is_set(error
))
61 return -sd_bus_error_get_errno(error
);
73 static void *vtable_method_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
76 return (uint8_t*) u
+ p
->x
.method
.offset
;
79 static void *vtable_property_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
82 return (uint8_t*) u
+ p
->x
.property
.offset
;
85 static int vtable_property_get_userdata(
88 struct vtable_member
*p
,
90 sd_bus_error
*error
) {
100 r
= node_vtable_get_userdata(bus
, path
, p
->parent
, &u
, error
);
103 if (bus
->nodes_modified
)
106 *userdata
= vtable_property_convert_userdata(p
->vtable
, u
);
110 static int add_enumerated_to_set(
113 struct node_enumerator
*first
,
115 sd_bus_error
*error
) {
117 struct node_enumerator
*c
;
124 LIST_FOREACH(enumerators
, c
, first
) {
125 char **children
= NULL
, **k
;
128 if (bus
->nodes_modified
)
131 slot
= container_of(c
, sd_bus_slot
, node_enumerator
);
133 bus
->current_slot
= sd_bus_slot_ref(slot
);
134 bus
->current_userdata
= slot
->userdata
;
135 r
= c
->callback(bus
, prefix
, slot
->userdata
, &children
, error
);
136 bus
->current_userdata
= NULL
;
137 bus
->current_slot
= sd_bus_slot_unref(slot
);
141 if (sd_bus_error_is_set(error
))
142 return -sd_bus_error_get_errno(error
);
144 STRV_FOREACH(k
, children
) {
150 if (!object_path_is_valid(*k
)) {
156 if (!object_path_startswith(*k
, prefix
)) {
161 r
= set_consume(s
, *k
);
175 /* if set, add_subtree() works recursively */
176 CHILDREN_RECURSIVE
= (1U << 1),
177 /* if set, add_subtree() scans object-manager hierarchies recursively */
178 CHILDREN_SUBHIERARCHIES
= (1U << 0),
181 static int add_subtree_to_set(
187 sd_bus_error
*error
) {
197 r
= add_enumerated_to_set(bus
, prefix
, n
->enumerators
, s
, error
);
200 if (bus
->nodes_modified
)
203 LIST_FOREACH(siblings
, i
, n
->child
) {
206 if (!object_path_startswith(i
->path
, prefix
))
213 r
= set_consume(s
, t
);
214 if (r
< 0 && r
!= -EEXIST
)
217 if ((flags
& CHILDREN_RECURSIVE
) &&
218 ((flags
& CHILDREN_SUBHIERARCHIES
) || !i
->object_managers
)) {
219 r
= add_subtree_to_set(bus
, prefix
, i
, flags
, s
, error
);
222 if (bus
->nodes_modified
)
230 static int get_child_nodes(
236 sd_bus_error
*error
) {
246 s
= set_new(&string_hash_ops
);
250 r
= add_subtree_to_set(bus
, prefix
, n
, flags
, s
, error
);
260 static int node_callbacks_run(
263 struct node_callback
*first
,
264 bool require_fallback
,
265 bool *found_object
) {
267 struct node_callback
*c
;
272 assert(found_object
);
274 LIST_FOREACH(callbacks
, c
, first
) {
275 _cleanup_(sd_bus_error_free
) sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
278 if (bus
->nodes_modified
)
281 if (require_fallback
&& !c
->is_fallback
)
284 *found_object
= true;
286 if (c
->last_iteration
== bus
->iteration_counter
)
289 c
->last_iteration
= bus
->iteration_counter
;
291 r
= sd_bus_message_rewind(m
, true);
295 slot
= container_of(c
, sd_bus_slot
, node_callback
);
297 bus
->current_slot
= sd_bus_slot_ref(slot
);
298 bus
->current_handler
= c
->callback
;
299 bus
->current_userdata
= slot
->userdata
;
300 r
= c
->callback(m
, slot
->userdata
, &error_buffer
);
301 bus
->current_userdata
= NULL
;
302 bus
->current_handler
= NULL
;
303 bus
->current_slot
= sd_bus_slot_unref(slot
);
305 r
= bus_maybe_reply_error(m
, r
, &error_buffer
);
313 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
315 static int check_access(sd_bus
*bus
, sd_bus_message
*m
, struct vtable_member
*c
, sd_bus_error
*error
) {
323 /* If the entire bus is trusted let's grant access */
327 /* If the member is marked UNPRIVILEGED let's grant access */
328 if (c
->vtable
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
)
331 /* Check have the caller has the requested capability
332 * set. Note that the flags value contains the capability
333 * number plus one, which we need to subtract here. We do this
334 * so that we have 0 as special value for "default
336 cap
= CAPABILITY_SHIFT(c
->vtable
->flags
);
338 cap
= CAPABILITY_SHIFT(c
->parent
->vtable
[0].flags
);
344 r
= sd_bus_query_sender_privilege(m
, cap
);
350 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Access to %s.%s() not permitted.", c
->interface
, c
->member
);
353 static int method_callbacks_run(
356 struct vtable_member
*c
,
357 bool require_fallback
,
358 bool *found_object
) {
360 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
361 const char *signature
;
368 assert(found_object
);
370 if (require_fallback
&& !c
->parent
->is_fallback
)
373 r
= check_access(bus
, m
, c
, &error
);
375 return bus_maybe_reply_error(m
, r
, &error
);
377 r
= node_vtable_get_userdata(bus
, m
->path
, c
->parent
, &u
, &error
);
379 return bus_maybe_reply_error(m
, r
, &error
);
380 if (bus
->nodes_modified
)
383 u
= vtable_method_convert_userdata(c
->vtable
, u
);
385 *found_object
= true;
387 if (c
->last_iteration
== bus
->iteration_counter
)
390 c
->last_iteration
= bus
->iteration_counter
;
392 r
= sd_bus_message_rewind(m
, true);
396 signature
= sd_bus_message_get_signature(m
, true);
400 if (!streq(strempty(c
->vtable
->x
.method
.signature
), signature
))
401 return sd_bus_reply_method_errorf(
403 SD_BUS_ERROR_INVALID_ARGS
,
404 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
405 signature
, c
->interface
, c
->member
, strempty(c
->vtable
->x
.method
.signature
));
407 /* Keep track what the signature of the reply to this message
408 * should be, so that this can be enforced when sealing the
410 m
->enforced_reply_signature
= strempty(c
->vtable
->x
.method
.result
);
412 if (c
->vtable
->x
.method
.handler
) {
415 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
417 bus
->current_slot
= sd_bus_slot_ref(slot
);
418 bus
->current_handler
= c
->vtable
->x
.method
.handler
;
419 bus
->current_userdata
= u
;
420 r
= c
->vtable
->x
.method
.handler(m
, u
, &error
);
421 bus
->current_userdata
= NULL
;
422 bus
->current_handler
= NULL
;
423 bus
->current_slot
= sd_bus_slot_unref(slot
);
425 return bus_maybe_reply_error(m
, r
, &error
);
428 /* If the method callback is NULL, make this a successful NOP */
429 r
= sd_bus_reply_method_return(m
, NULL
);
436 static int invoke_property_get(
439 const sd_bus_vtable
*v
,
441 const char *interface
,
442 const char *property
,
443 sd_bus_message
*reply
,
445 sd_bus_error
*error
) {
458 if (v
->x
.property
.get
) {
460 bus
->current_slot
= sd_bus_slot_ref(slot
);
461 bus
->current_userdata
= userdata
;
462 r
= v
->x
.property
.get(bus
, path
, interface
, property
, reply
, userdata
, error
);
463 bus
->current_userdata
= NULL
;
464 bus
->current_slot
= sd_bus_slot_unref(slot
);
468 if (sd_bus_error_is_set(error
))
469 return -sd_bus_error_get_errno(error
);
473 /* Automatic handling if no callback is defined. */
475 if (streq(v
->x
.property
.signature
, "as"))
476 return sd_bus_message_append_strv(reply
, *(char***) userdata
);
478 assert(signature_is_single(v
->x
.property
.signature
, false));
479 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
481 switch (v
->x
.property
.signature
[0]) {
483 case SD_BUS_TYPE_STRING
:
484 case SD_BUS_TYPE_SIGNATURE
:
485 p
= strempty(*(char**) userdata
);
488 case SD_BUS_TYPE_OBJECT_PATH
:
489 p
= *(char**) userdata
;
498 return sd_bus_message_append_basic(reply
, v
->x
.property
.signature
[0], p
);
501 static int invoke_property_set(
504 const sd_bus_vtable
*v
,
506 const char *interface
,
507 const char *property
,
508 sd_bus_message
*value
,
510 sd_bus_error
*error
) {
522 if (v
->x
.property
.set
) {
524 bus
->current_slot
= sd_bus_slot_ref(slot
);
525 bus
->current_userdata
= userdata
;
526 r
= v
->x
.property
.set(bus
, path
, interface
, property
, value
, userdata
, error
);
527 bus
->current_userdata
= NULL
;
528 bus
->current_slot
= sd_bus_slot_unref(slot
);
532 if (sd_bus_error_is_set(error
))
533 return -sd_bus_error_get_errno(error
);
537 /* Automatic handling if no callback is defined. */
539 assert(signature_is_single(v
->x
.property
.signature
, false));
540 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
542 switch (v
->x
.property
.signature
[0]) {
544 case SD_BUS_TYPE_STRING
:
545 case SD_BUS_TYPE_OBJECT_PATH
:
546 case SD_BUS_TYPE_SIGNATURE
: {
550 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], &p
);
558 free(*(char**) userdata
);
559 *(char**) userdata
= n
;
565 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], userdata
);
575 static int property_get_set_callbacks_run(
578 struct vtable_member
*c
,
579 bool require_fallback
,
581 bool *found_object
) {
583 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
584 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
592 assert(found_object
);
594 if (require_fallback
&& !c
->parent
->is_fallback
)
597 r
= vtable_property_get_userdata(bus
, m
->path
, c
, &u
, &error
);
599 return bus_maybe_reply_error(m
, r
, &error
);
600 if (bus
->nodes_modified
)
603 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
605 *found_object
= true;
607 r
= sd_bus_message_new_method_return(m
, &reply
);
612 /* Note that we do not protect against reexecution
613 * here (using the last_iteration check, see below),
614 * should the node tree have changed and we got called
615 * again. We assume that property Get() calls are
616 * ultimately without side-effects or if they aren't
617 * then at least idempotent. */
619 r
= sd_bus_message_open_container(reply
, 'v', c
->vtable
->x
.property
.signature
);
623 /* Note that we do not do an access check here. Read
624 * access to properties is always unrestricted, since
625 * PropertiesChanged signals broadcast contents
628 r
= invoke_property_get(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, reply
, u
, &error
);
630 return bus_maybe_reply_error(m
, r
, &error
);
632 if (bus
->nodes_modified
)
635 r
= sd_bus_message_close_container(reply
);
640 const char *signature
= NULL
;
643 if (c
->vtable
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
644 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_PROPERTY_READ_ONLY
, "Property '%s' is not writable.", c
->member
);
646 /* Avoid that we call the set routine more than once
647 * if the processing of this message got restarted
648 * because the node tree changed. */
649 if (c
->last_iteration
== bus
->iteration_counter
)
652 c
->last_iteration
= bus
->iteration_counter
;
654 r
= sd_bus_message_peek_type(m
, &type
, &signature
);
658 if (type
!= 'v' || !streq(strempty(signature
), strempty(c
->vtable
->x
.property
.signature
)))
659 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
));
661 r
= sd_bus_message_enter_container(m
, 'v', c
->vtable
->x
.property
.signature
);
665 r
= check_access(bus
, m
, c
, &error
);
667 return bus_maybe_reply_error(m
, r
, &error
);
669 r
= invoke_property_set(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, m
, u
, &error
);
671 return bus_maybe_reply_error(m
, r
, &error
);
673 if (bus
->nodes_modified
)
676 r
= sd_bus_message_exit_container(m
);
681 r
= sd_bus_send(bus
, reply
, NULL
);
688 static int vtable_append_one_property(
690 sd_bus_message
*reply
,
692 struct node_vtable
*c
,
693 const sd_bus_vtable
*v
,
695 sd_bus_error
*error
) {
706 r
= sd_bus_message_open_container(reply
, 'e', "sv");
710 r
= sd_bus_message_append(reply
, "s", v
->x
.property
.member
);
714 r
= sd_bus_message_open_container(reply
, 'v', v
->x
.property
.signature
);
718 slot
= container_of(c
, sd_bus_slot
, node_vtable
);
720 r
= invoke_property_get(bus
, slot
, v
, path
, c
->interface
, v
->x
.property
.member
, reply
, vtable_property_convert_userdata(v
, userdata
), error
);
723 if (bus
->nodes_modified
)
726 r
= sd_bus_message_close_container(reply
);
730 r
= sd_bus_message_close_container(reply
);
737 static int vtable_append_all_properties(
739 sd_bus_message
*reply
,
741 struct node_vtable
*c
,
743 sd_bus_error
*error
) {
745 const sd_bus_vtable
*v
;
753 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
756 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
757 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
760 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
763 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)
766 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
769 if (bus
->nodes_modified
)
776 static int property_get_all_callbacks_run(
779 struct node_vtable
*first
,
780 bool require_fallback
,
782 bool *found_object
) {
784 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
785 struct node_vtable
*c
;
786 bool found_interface
;
791 assert(found_object
);
793 r
= sd_bus_message_new_method_return(m
, &reply
);
797 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
801 found_interface
= !iface
||
802 streq(iface
, "org.freedesktop.DBus.Properties") ||
803 streq(iface
, "org.freedesktop.DBus.Peer") ||
804 streq(iface
, "org.freedesktop.DBus.Introspectable");
806 LIST_FOREACH(vtables
, c
, first
) {
807 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
810 if (require_fallback
&& !c
->is_fallback
)
813 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
815 return bus_maybe_reply_error(m
, r
, &error
);
816 if (bus
->nodes_modified
)
821 *found_object
= true;
823 if (iface
&& !streq(c
->interface
, iface
))
825 found_interface
= true;
827 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
829 return bus_maybe_reply_error(m
, r
, &error
);
830 if (bus
->nodes_modified
)
837 if (!found_interface
) {
838 r
= sd_bus_reply_method_errorf(
840 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
841 "Unknown interface '%s'.", iface
);
848 r
= sd_bus_message_close_container(reply
);
852 r
= sd_bus_send(bus
, reply
, NULL
);
859 static int bus_node_exists(
863 bool require_fallback
) {
865 struct node_vtable
*c
;
866 struct node_callback
*k
;
873 /* Tests if there's anything attached directly to this node
874 * for the specified path */
876 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
879 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
880 if (require_fallback
&& !k
->is_fallback
)
886 LIST_FOREACH(vtables
, c
, n
->vtables
) {
887 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
889 if (require_fallback
&& !c
->is_fallback
)
892 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
895 if (bus
->nodes_modified
)
902 static int process_introspect(
906 bool require_fallback
,
907 bool *found_object
) {
909 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
910 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
911 _cleanup_set_free_free_ Set
*s
= NULL
;
912 const char *previous_interface
= NULL
;
913 struct introspect intro
;
914 struct node_vtable
*c
;
921 assert(found_object
);
923 r
= get_child_nodes(bus
, m
->path
, n
, 0, &s
, &error
);
925 return bus_maybe_reply_error(m
, r
, &error
);
926 if (bus
->nodes_modified
)
929 r
= introspect_begin(&intro
, bus
->trusted
);
933 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
937 empty
= set_isempty(s
);
939 LIST_FOREACH(vtables
, c
, n
->vtables
) {
940 if (require_fallback
&& !c
->is_fallback
)
943 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
945 r
= bus_maybe_reply_error(m
, r
, &error
);
948 if (bus
->nodes_modified
) {
957 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
960 if (!streq_ptr(previous_interface
, c
->interface
)) {
962 if (previous_interface
)
963 fputs_unlocked(" </interface>\n", intro
.f
);
965 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
968 r
= introspect_write_interface(&intro
, c
->vtable
);
972 previous_interface
= c
->interface
;
975 if (previous_interface
)
976 fputs_unlocked(" </interface>\n", intro
.f
);
979 /* Nothing?, let's see if we exist at all, and if not
980 * refuse to do anything */
981 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
983 r
= bus_maybe_reply_error(m
, r
, &error
);
986 if (bus
->nodes_modified
) {
992 *found_object
= true;
994 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
998 r
= introspect_finish(&intro
, bus
, m
, &reply
);
1002 r
= sd_bus_send(bus
, reply
, NULL
);
1009 introspect_free(&intro
);
1013 static int object_manager_serialize_path(
1015 sd_bus_message
*reply
,
1018 bool require_fallback
,
1019 sd_bus_error
*error
) {
1021 const char *previous_interface
= NULL
;
1022 bool found_something
= false;
1023 struct node_vtable
*i
;
1033 n
= hashmap_get(bus
->nodes
, prefix
);
1037 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1040 if (require_fallback
&& !i
->is_fallback
)
1043 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1046 if (bus
->nodes_modified
)
1051 if (!found_something
) {
1053 /* Open the object part */
1055 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1059 r
= sd_bus_message_append(reply
, "o", path
);
1063 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1067 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
1071 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
1075 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
1079 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1083 found_something
= true;
1086 if (!streq_ptr(previous_interface
, i
->interface
)) {
1088 /* Maybe close the previous interface part */
1090 if (previous_interface
) {
1091 r
= sd_bus_message_close_container(reply
);
1095 r
= sd_bus_message_close_container(reply
);
1100 /* Open the new interface part */
1102 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1106 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1110 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1115 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1118 if (bus
->nodes_modified
)
1121 previous_interface
= i
->interface
;
1124 if (previous_interface
) {
1125 r
= sd_bus_message_close_container(reply
);
1129 r
= sd_bus_message_close_container(reply
);
1134 if (found_something
) {
1135 r
= sd_bus_message_close_container(reply
);
1139 r
= sd_bus_message_close_container(reply
);
1147 static int object_manager_serialize_path_and_fallbacks(
1149 sd_bus_message
*reply
,
1151 sd_bus_error
*error
) {
1161 /* First, add all vtables registered for this path */
1162 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1165 if (bus
->nodes_modified
)
1168 /* Second, add fallback vtables registered for any of the prefixes */
1169 prefix
= alloca(strlen(path
) + 1);
1170 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1171 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1174 if (bus
->nodes_modified
)
1181 static int process_get_managed_objects(
1185 bool require_fallback
,
1186 bool *found_object
) {
1188 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1189 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1190 _cleanup_set_free_free_ Set
*s
= NULL
;
1198 assert(found_object
);
1200 /* Spec says, GetManagedObjects() is only implemented on the root of a
1201 * sub-tree. Therefore, we require a registered object-manager on
1202 * exactly the queried path, otherwise, we refuse to respond. */
1204 if (require_fallback
|| !n
->object_managers
)
1207 r
= get_child_nodes(bus
, m
->path
, n
, CHILDREN_RECURSIVE
, &s
, &error
);
1209 return bus_maybe_reply_error(m
, r
, &error
);
1210 if (bus
->nodes_modified
)
1213 r
= sd_bus_message_new_method_return(m
, &reply
);
1217 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1221 SET_FOREACH(path
, s
, i
) {
1222 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1224 return bus_maybe_reply_error(m
, r
, &error
);
1226 if (bus
->nodes_modified
)
1230 r
= sd_bus_message_close_container(reply
);
1234 r
= sd_bus_send(bus
, reply
, NULL
);
1241 static int object_find_and_run(
1245 bool require_fallback
,
1246 bool *found_object
) {
1249 struct vtable_member vtable_key
, *v
;
1255 assert(found_object
);
1257 n
= hashmap_get(bus
->nodes
, p
);
1261 /* First, try object callbacks */
1262 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1265 if (bus
->nodes_modified
)
1268 if (!m
->interface
|| !m
->member
)
1271 /* Then, look for a known method */
1272 vtable_key
.path
= (char*) p
;
1273 vtable_key
.interface
= m
->interface
;
1274 vtable_key
.member
= m
->member
;
1276 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1278 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1281 if (bus
->nodes_modified
)
1285 /* Then, look for a known property */
1286 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1289 get
= streq(m
->member
, "Get");
1291 if (get
|| streq(m
->member
, "Set")) {
1293 r
= sd_bus_message_rewind(m
, true);
1297 vtable_key
.path
= (char*) p
;
1299 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1301 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1303 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1305 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1310 } else if (streq(m
->member
, "GetAll")) {
1313 r
= sd_bus_message_rewind(m
, true);
1317 r
= sd_bus_message_read(m
, "s", &iface
);
1319 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1324 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1329 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1331 if (!isempty(sd_bus_message_get_signature(m
, true)))
1332 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1334 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1338 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1340 if (!isempty(sd_bus_message_get_signature(m
, true)))
1341 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1343 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1348 if (bus
->nodes_modified
)
1351 if (!*found_object
) {
1352 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1354 return bus_maybe_reply_error(m
, r
, NULL
);
1355 if (bus
->nodes_modified
)
1358 *found_object
= true;
1364 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1367 bool found_object
= false;
1372 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1375 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1378 if (hashmap_isempty(bus
->nodes
))
1381 /* Never respond to broadcast messages */
1382 if (bus
->bus_client
&& !m
->destination
)
1388 pl
= strlen(m
->path
);
1392 bus
->nodes_modified
= false;
1394 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1398 /* Look for fallback prefixes */
1399 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1401 if (bus
->nodes_modified
)
1404 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1409 } while (bus
->nodes_modified
);
1414 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1415 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1416 r
= sd_bus_reply_method_errorf(
1418 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1419 "Unknown property or interface.");
1421 r
= sd_bus_reply_method_errorf(
1423 SD_BUS_ERROR_UNKNOWN_METHOD
,
1424 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1432 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1433 struct node
*n
, *parent
;
1435 _cleanup_free_
char *s
= NULL
;
1441 assert(path
[0] == '/');
1443 n
= hashmap_get(bus
->nodes
, path
);
1447 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1455 if (streq(path
, "/"))
1458 e
= strrchr(path
, '/');
1461 p
= strndupa(path
, MAX(1, e
- path
));
1463 parent
= bus_node_allocate(bus
, p
);
1468 n
= new0(struct node
, 1);
1474 s
= NULL
; /* do not free */
1476 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1483 LIST_PREPEND(siblings
, parent
->child
, n
);
1488 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1501 assert_se(hashmap_remove(b
->nodes
, n
->path
) == n
);
1504 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1507 bus_node_gc(b
, n
->parent
);
1511 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1517 n
= hashmap_get(bus
->nodes
, path
);
1521 prefix
= alloca(strlen(path
) + 1);
1522 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1523 n
= hashmap_get(bus
->nodes
, prefix
);
1529 while (n
&& !n
->object_managers
)
1537 static int bus_add_object(
1542 sd_bus_message_handler_t callback
,
1549 assert_return(bus
, -EINVAL
);
1550 assert_return(object_path_is_valid(path
), -EINVAL
);
1551 assert_return(callback
, -EINVAL
);
1552 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1554 n
= bus_node_allocate(bus
, path
);
1558 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1564 s
->node_callback
.callback
= callback
;
1565 s
->node_callback
.is_fallback
= fallback
;
1567 s
->node_callback
.node
= n
;
1568 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1569 bus
->nodes_modified
= true;
1577 sd_bus_slot_unref(s
);
1578 bus_node_gc(bus
, n
);
1583 _public_
int sd_bus_add_object(
1587 sd_bus_message_handler_t callback
,
1590 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1593 _public_
int sd_bus_add_fallback(
1597 sd_bus_message_handler_t callback
,
1600 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1603 static void vtable_member_hash_func(const void *a
, struct siphash
*state
) {
1604 const struct vtable_member
*m
= a
;
1608 string_hash_func(m
->path
, state
);
1609 string_hash_func(m
->interface
, state
);
1610 string_hash_func(m
->member
, state
);
1613 static int vtable_member_compare_func(const void *a
, const void *b
) {
1614 const struct vtable_member
*x
= a
, *y
= b
;
1620 r
= strcmp(x
->path
, y
->path
);
1624 r
= strcmp(x
->interface
, y
->interface
);
1628 return strcmp(x
->member
, y
->member
);
1631 static const struct hash_ops vtable_member_hash_ops
= {
1632 .hash
= vtable_member_hash_func
,
1633 .compare
= vtable_member_compare_func
1636 static int add_object_vtable_internal(
1640 const char *interface
,
1641 const sd_bus_vtable
*vtable
,
1643 sd_bus_object_find_t find
,
1646 sd_bus_slot
*s
= NULL
;
1647 struct node_vtable
*i
, *existing
= NULL
;
1648 const sd_bus_vtable
*v
;
1652 assert_return(bus
, -EINVAL
);
1653 assert_return(object_path_is_valid(path
), -EINVAL
);
1654 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1655 assert_return(vtable
, -EINVAL
);
1656 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1657 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1658 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1659 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1660 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1661 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1662 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1664 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1668 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1672 n
= bus_node_allocate(bus
, path
);
1676 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1677 if (i
->is_fallback
!= fallback
) {
1682 if (streq(i
->interface
, interface
)) {
1684 if (i
->vtable
== vtable
) {
1693 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1699 s
->node_vtable
.is_fallback
= fallback
;
1700 s
->node_vtable
.vtable
= vtable
;
1701 s
->node_vtable
.find
= find
;
1703 s
->node_vtable
.interface
= strdup(interface
);
1704 if (!s
->node_vtable
.interface
) {
1709 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1713 case _SD_BUS_VTABLE_METHOD
: {
1714 struct vtable_member
*m
;
1716 if (!member_name_is_valid(v
->x
.method
.member
) ||
1717 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1718 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1719 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1720 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1725 m
= new0(struct vtable_member
, 1);
1731 m
->parent
= &s
->node_vtable
;
1733 m
->interface
= s
->node_vtable
.interface
;
1734 m
->member
= v
->x
.method
.member
;
1737 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1746 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1748 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1753 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1759 case _SD_BUS_VTABLE_PROPERTY
: {
1760 struct vtable_member
*m
;
1762 if (!member_name_is_valid(v
->x
.property
.member
) ||
1763 !signature_is_single(v
->x
.property
.signature
, false) ||
1764 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1765 (v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
) ||
1766 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1767 ((v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) && (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)) ||
1768 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1773 m
= new0(struct vtable_member
, 1);
1779 m
->parent
= &s
->node_vtable
;
1781 m
->interface
= s
->node_vtable
.interface
;
1782 m
->member
= v
->x
.property
.member
;
1785 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1794 case _SD_BUS_VTABLE_SIGNAL
:
1796 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1797 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1798 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1811 s
->node_vtable
.node
= n
;
1812 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1813 bus
->nodes_modified
= true;
1821 sd_bus_slot_unref(s
);
1822 bus_node_gc(bus
, n
);
1827 _public_
int sd_bus_add_object_vtable(
1831 const char *interface
,
1832 const sd_bus_vtable
*vtable
,
1835 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1838 _public_
int sd_bus_add_fallback_vtable(
1842 const char *interface
,
1843 const sd_bus_vtable
*vtable
,
1844 sd_bus_object_find_t find
,
1847 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1850 _public_
int sd_bus_add_node_enumerator(
1854 sd_bus_node_enumerator_t callback
,
1861 assert_return(bus
, -EINVAL
);
1862 assert_return(object_path_is_valid(path
), -EINVAL
);
1863 assert_return(callback
, -EINVAL
);
1864 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1866 n
= bus_node_allocate(bus
, path
);
1870 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1876 s
->node_enumerator
.callback
= callback
;
1878 s
->node_enumerator
.node
= n
;
1879 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1880 bus
->nodes_modified
= true;
1888 sd_bus_slot_unref(s
);
1889 bus_node_gc(bus
, n
);
1894 static int emit_properties_changed_on_interface(
1898 const char *interface
,
1899 bool require_fallback
,
1900 bool *found_interface
,
1903 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1904 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1905 bool has_invalidating
= false, has_changing
= false;
1906 struct vtable_member key
= {};
1907 struct node_vtable
*c
;
1917 assert(found_interface
);
1919 n
= hashmap_get(bus
->nodes
, prefix
);
1923 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1927 r
= sd_bus_message_append(m
, "s", interface
);
1931 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1936 key
.interface
= interface
;
1938 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1939 if (require_fallback
&& !c
->is_fallback
)
1942 if (!streq(c
->interface
, interface
))
1945 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1948 if (bus
->nodes_modified
)
1953 *found_interface
= true;
1956 /* If the caller specified a list of
1957 * properties we include exactly those in the
1958 * PropertiesChanged message */
1960 STRV_FOREACH(property
, names
) {
1961 struct vtable_member
*v
;
1963 assert_return(member_name_is_valid(*property
), -EINVAL
);
1965 key
.member
= *property
;
1966 v
= hashmap_get(bus
->vtable_properties
, &key
);
1970 /* If there are two vtables for the same
1971 * interface, let's handle this property when
1972 * we come to that vtable. */
1976 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1977 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1979 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1981 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1982 has_invalidating
= true;
1986 has_changing
= true;
1988 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1991 if (bus
->nodes_modified
)
1995 const sd_bus_vtable
*v
;
1997 /* If the caller specified no properties list
1998 * we include all properties that are marked
1999 * as changing in the message. */
2001 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2002 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
2005 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2008 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
2009 has_invalidating
= true;
2013 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
2016 has_changing
= true;
2018 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
2021 if (bus
->nodes_modified
)
2027 if (!has_invalidating
&& !has_changing
)
2030 r
= sd_bus_message_close_container(m
);
2034 r
= sd_bus_message_open_container(m
, 'a', "s");
2038 if (has_invalidating
) {
2039 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2040 if (require_fallback
&& !c
->is_fallback
)
2043 if (!streq(c
->interface
, interface
))
2046 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2049 if (bus
->nodes_modified
)
2055 STRV_FOREACH(property
, names
) {
2056 struct vtable_member
*v
;
2058 key
.member
= *property
;
2059 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2060 assert(c
== v
->parent
);
2062 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2065 r
= sd_bus_message_append(m
, "s", *property
);
2070 const sd_bus_vtable
*v
;
2072 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2073 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
2076 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2079 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2082 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2090 r
= sd_bus_message_close_container(m
);
2094 r
= sd_bus_send(bus
, m
, NULL
);
2101 _public_
int sd_bus_emit_properties_changed_strv(
2104 const char *interface
,
2107 BUS_DONT_DESTROY(bus
);
2108 bool found_interface
= false;
2112 assert_return(bus
, -EINVAL
);
2113 assert_return(object_path_is_valid(path
), -EINVAL
);
2114 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2115 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2117 if (!BUS_IS_OPEN(bus
->state
))
2120 /* A non-NULL but empty names list means nothing needs to be
2121 generated. A NULL list OTOH indicates that all properties
2122 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2123 included in the PropertiesChanged message. */
2124 if (names
&& names
[0] == NULL
)
2128 bus
->nodes_modified
= false;
2130 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2133 if (bus
->nodes_modified
)
2136 prefix
= alloca(strlen(path
) + 1);
2137 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2138 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2141 if (bus
->nodes_modified
)
2145 } while (bus
->nodes_modified
);
2147 return found_interface
? 0 : -ENOENT
;
2150 _public_
int sd_bus_emit_properties_changed(
2153 const char *interface
,
2154 const char *name
, ...) {
2158 assert_return(bus
, -EINVAL
);
2159 assert_return(object_path_is_valid(path
), -EINVAL
);
2160 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2161 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2163 if (!BUS_IS_OPEN(bus
->state
))
2169 names
= strv_from_stdarg_alloca(name
);
2171 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2174 static int object_added_append_all_prefix(
2180 bool require_fallback
) {
2182 const char *previous_interface
= NULL
;
2183 struct node_vtable
*c
;
2193 n
= hashmap_get(bus
->nodes
, prefix
);
2197 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2198 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2201 if (require_fallback
&& !c
->is_fallback
)
2204 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2207 if (bus
->nodes_modified
)
2212 if (!streq_ptr(c
->interface
, previous_interface
)) {
2213 /* If a child-node already handled this interface, we
2214 * skip it on any of its parents. The child vtables
2215 * always fully override any conflicting vtables of
2216 * any parent node. */
2217 if (set_get(s
, c
->interface
))
2220 r
= set_put(s
, c
->interface
);
2224 if (previous_interface
) {
2225 r
= sd_bus_message_close_container(m
);
2228 r
= sd_bus_message_close_container(m
);
2233 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2236 r
= sd_bus_message_append(m
, "s", c
->interface
);
2239 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2243 previous_interface
= c
->interface
;
2246 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2249 if (bus
->nodes_modified
)
2253 if (previous_interface
) {
2254 r
= sd_bus_message_close_container(m
);
2257 r
= sd_bus_message_close_container(m
);
2265 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2266 _cleanup_set_free_ Set
*s
= NULL
;
2275 * This appends all interfaces registered on path @path. We first add
2276 * the builtin interfaces, which are always available and handled by
2277 * sd-bus. Then, we add all interfaces registered on the exact node,
2278 * followed by all fallback interfaces registered on any parent prefix.
2280 * If an interface is registered multiple times on the same node with
2281 * different vtables, we merge all the properties across all vtables.
2282 * However, if a child node has the same interface registered as one of
2283 * its parent nodes has as fallback, we make the child overwrite the
2284 * parent instead of extending it. Therefore, we keep a "Set" of all
2285 * handled interfaces during parent traversal, so we skip interfaces on
2286 * a parent that were overwritten by a child.
2289 s
= set_new(&string_hash_ops
);
2293 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2296 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2299 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2302 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2306 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2309 if (bus
->nodes_modified
)
2312 prefix
= alloca(strlen(path
) + 1);
2313 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2314 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2317 if (bus
->nodes_modified
)
2324 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2325 BUS_DONT_DESTROY(bus
);
2327 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2328 struct node
*object_manager
;
2332 * This emits an InterfacesAdded signal on the given path, by iterating
2333 * all registered vtables and fallback vtables on the path. All
2334 * properties are queried and included in the signal.
2335 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2336 * explicit list of registered interfaces. However, unlike
2337 * interfaces_added(), this call can figure out the list of supported
2338 * interfaces itself. Furthermore, it properly adds the builtin
2339 * org.freedesktop.DBus.* interfaces.
2342 assert_return(bus
, -EINVAL
);
2343 assert_return(object_path_is_valid(path
), -EINVAL
);
2344 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2346 if (!BUS_IS_OPEN(bus
->state
))
2349 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2356 bus
->nodes_modified
= false;
2357 m
= sd_bus_message_unref(m
);
2359 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2363 r
= sd_bus_message_append_basic(m
, 'o', path
);
2367 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2371 r
= object_added_append_all(bus
, m
, path
);
2375 if (bus
->nodes_modified
)
2378 r
= sd_bus_message_close_container(m
);
2382 } while (bus
->nodes_modified
);
2384 return sd_bus_send(bus
, m
, NULL
);
2387 static int object_removed_append_all_prefix(
2393 bool require_fallback
) {
2395 const char *previous_interface
= NULL
;
2396 struct node_vtable
*c
;
2406 n
= hashmap_get(bus
->nodes
, prefix
);
2410 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2411 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2414 if (require_fallback
&& !c
->is_fallback
)
2416 if (streq_ptr(c
->interface
, previous_interface
))
2419 /* If a child-node already handled this interface, we
2420 * skip it on any of its parents. The child vtables
2421 * always fully override any conflicting vtables of
2422 * any parent node. */
2423 if (set_get(s
, c
->interface
))
2426 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2429 if (bus
->nodes_modified
)
2434 r
= set_put(s
, c
->interface
);
2438 r
= sd_bus_message_append(m
, "s", c
->interface
);
2442 previous_interface
= c
->interface
;
2448 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2449 _cleanup_set_free_ Set
*s
= NULL
;
2457 /* see sd_bus_emit_object_added() for details */
2459 s
= set_new(&string_hash_ops
);
2463 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2466 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2469 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2472 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2476 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2479 if (bus
->nodes_modified
)
2482 prefix
= alloca(strlen(path
) + 1);
2483 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2484 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2487 if (bus
->nodes_modified
)
2494 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2495 BUS_DONT_DESTROY(bus
);
2497 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2498 struct node
*object_manager
;
2502 * This is like sd_bus_emit_object_added(), but emits an
2503 * InterfacesRemoved signal on the given path. This only includes any
2504 * registered interfaces but skips the properties. Note that this will
2505 * call into the find() callbacks of any registered vtable. Therefore,
2506 * you must call this function before destroying/unlinking your object.
2507 * Otherwise, the list of interfaces will be incomplete. However, note
2508 * that this will *NOT* call into any property callback. Therefore, the
2509 * object might be in an "destructed" state, as long as we can find it.
2512 assert_return(bus
, -EINVAL
);
2513 assert_return(object_path_is_valid(path
), -EINVAL
);
2514 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2516 if (!BUS_IS_OPEN(bus
->state
))
2519 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2526 bus
->nodes_modified
= false;
2527 m
= sd_bus_message_unref(m
);
2529 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2533 r
= sd_bus_message_append_basic(m
, 'o', path
);
2537 r
= sd_bus_message_open_container(m
, 'a', "s");
2541 r
= object_removed_append_all(bus
, m
, path
);
2545 if (bus
->nodes_modified
)
2548 r
= sd_bus_message_close_container(m
);
2552 } while (bus
->nodes_modified
);
2554 return sd_bus_send(bus
, m
, NULL
);
2557 static int interfaces_added_append_one_prefix(
2562 const char *interface
,
2563 bool require_fallback
) {
2565 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2566 bool found_interface
= false;
2567 struct node_vtable
*c
;
2578 n
= hashmap_get(bus
->nodes
, prefix
);
2582 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2583 if (require_fallback
&& !c
->is_fallback
)
2586 if (!streq(c
->interface
, interface
))
2589 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2592 if (bus
->nodes_modified
)
2597 if (!found_interface
) {
2598 r
= sd_bus_message_append_basic(m
, 's', interface
);
2602 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2606 found_interface
= true;
2609 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2612 if (bus
->nodes_modified
)
2616 if (found_interface
) {
2617 r
= sd_bus_message_close_container(m
);
2622 return found_interface
;
2625 static int interfaces_added_append_one(
2629 const char *interface
) {
2639 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2642 if (bus
->nodes_modified
)
2645 prefix
= alloca(strlen(path
) + 1);
2646 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2647 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2650 if (bus
->nodes_modified
)
2657 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2658 BUS_DONT_DESTROY(bus
);
2660 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2661 struct node
*object_manager
;
2665 assert_return(bus
, -EINVAL
);
2666 assert_return(object_path_is_valid(path
), -EINVAL
);
2667 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2669 if (!BUS_IS_OPEN(bus
->state
))
2672 if (strv_isempty(interfaces
))
2675 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2682 bus
->nodes_modified
= false;
2683 m
= sd_bus_message_unref(m
);
2685 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2689 r
= sd_bus_message_append_basic(m
, 'o', path
);
2693 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2697 STRV_FOREACH(i
, interfaces
) {
2698 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2700 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2704 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2708 if (bus
->nodes_modified
)
2711 r
= sd_bus_message_close_container(m
);
2716 if (bus
->nodes_modified
)
2719 r
= sd_bus_message_close_container(m
);
2723 } while (bus
->nodes_modified
);
2725 return sd_bus_send(bus
, m
, NULL
);
2728 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2731 assert_return(bus
, -EINVAL
);
2732 assert_return(object_path_is_valid(path
), -EINVAL
);
2733 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2735 if (!BUS_IS_OPEN(bus
->state
))
2738 interfaces
= strv_from_stdarg_alloca(interface
);
2740 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2743 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2744 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2745 struct node
*object_manager
;
2748 assert_return(bus
, -EINVAL
);
2749 assert_return(object_path_is_valid(path
), -EINVAL
);
2750 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2752 if (!BUS_IS_OPEN(bus
->state
))
2755 if (strv_isempty(interfaces
))
2758 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2764 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2768 r
= sd_bus_message_append_basic(m
, 'o', path
);
2772 r
= sd_bus_message_append_strv(m
, interfaces
);
2776 return sd_bus_send(bus
, m
, NULL
);
2779 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2782 assert_return(bus
, -EINVAL
);
2783 assert_return(object_path_is_valid(path
), -EINVAL
);
2784 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2786 if (!BUS_IS_OPEN(bus
->state
))
2789 interfaces
= strv_from_stdarg_alloca(interface
);
2791 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2794 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2799 assert_return(bus
, -EINVAL
);
2800 assert_return(object_path_is_valid(path
), -EINVAL
);
2801 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2803 n
= bus_node_allocate(bus
, path
);
2807 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2813 s
->node_object_manager
.node
= n
;
2814 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2815 bus
->nodes_modified
= true;
2823 sd_bus_slot_unref(s
);
2824 bus_node_gc(bus
, n
);