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
);
1484 LIST_PREPEND(siblings
, parent
->child
, n
);
1489 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1502 assert_se(hashmap_remove(b
->nodes
, n
->path
) == n
);
1505 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1508 bus_node_gc(b
, n
->parent
);
1512 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1518 n
= hashmap_get(bus
->nodes
, path
);
1522 prefix
= alloca(strlen(path
) + 1);
1523 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1524 n
= hashmap_get(bus
->nodes
, prefix
);
1530 while (n
&& !n
->object_managers
)
1538 static int bus_add_object(
1543 sd_bus_message_handler_t callback
,
1550 assert_return(bus
, -EINVAL
);
1551 assert_return(object_path_is_valid(path
), -EINVAL
);
1552 assert_return(callback
, -EINVAL
);
1553 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1555 n
= bus_node_allocate(bus
, path
);
1559 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1565 s
->node_callback
.callback
= callback
;
1566 s
->node_callback
.is_fallback
= fallback
;
1568 s
->node_callback
.node
= n
;
1569 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1570 bus
->nodes_modified
= true;
1578 sd_bus_slot_unref(s
);
1579 bus_node_gc(bus
, n
);
1584 _public_
int sd_bus_add_object(
1588 sd_bus_message_handler_t callback
,
1591 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1594 _public_
int sd_bus_add_fallback(
1598 sd_bus_message_handler_t callback
,
1601 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1604 static void vtable_member_hash_func(const void *a
, struct siphash
*state
) {
1605 const struct vtable_member
*m
= a
;
1609 string_hash_func(m
->path
, state
);
1610 string_hash_func(m
->interface
, state
);
1611 string_hash_func(m
->member
, state
);
1614 static int vtable_member_compare_func(const void *a
, const void *b
) {
1615 const struct vtable_member
*x
= a
, *y
= b
;
1621 r
= strcmp(x
->path
, y
->path
);
1625 r
= strcmp(x
->interface
, y
->interface
);
1629 return strcmp(x
->member
, y
->member
);
1632 static const struct hash_ops vtable_member_hash_ops
= {
1633 .hash
= vtable_member_hash_func
,
1634 .compare
= vtable_member_compare_func
1637 static int add_object_vtable_internal(
1641 const char *interface
,
1642 const sd_bus_vtable
*vtable
,
1644 sd_bus_object_find_t find
,
1647 sd_bus_slot
*s
= NULL
;
1648 struct node_vtable
*i
, *existing
= NULL
;
1649 const sd_bus_vtable
*v
;
1653 assert_return(bus
, -EINVAL
);
1654 assert_return(object_path_is_valid(path
), -EINVAL
);
1655 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1656 assert_return(vtable
, -EINVAL
);
1657 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1658 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1659 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1660 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1661 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1662 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1663 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1665 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1669 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1673 n
= bus_node_allocate(bus
, path
);
1677 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1678 if (i
->is_fallback
!= fallback
) {
1683 if (streq(i
->interface
, interface
)) {
1685 if (i
->vtable
== vtable
) {
1694 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1700 s
->node_vtable
.is_fallback
= fallback
;
1701 s
->node_vtable
.vtable
= vtable
;
1702 s
->node_vtable
.find
= find
;
1704 s
->node_vtable
.interface
= strdup(interface
);
1705 if (!s
->node_vtable
.interface
) {
1710 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1714 case _SD_BUS_VTABLE_METHOD
: {
1715 struct vtable_member
*m
;
1717 if (!member_name_is_valid(v
->x
.method
.member
) ||
1718 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1719 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1720 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1721 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1726 m
= new0(struct vtable_member
, 1);
1732 m
->parent
= &s
->node_vtable
;
1734 m
->interface
= s
->node_vtable
.interface
;
1735 m
->member
= v
->x
.method
.member
;
1738 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1747 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1749 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1754 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1761 case _SD_BUS_VTABLE_PROPERTY
: {
1762 struct vtable_member
*m
;
1764 if (!member_name_is_valid(v
->x
.property
.member
) ||
1765 !signature_is_single(v
->x
.property
.signature
, false) ||
1766 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1767 (v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
) ||
1768 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1769 ((v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) && (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)) ||
1770 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1775 m
= new0(struct vtable_member
, 1);
1781 m
->parent
= &s
->node_vtable
;
1783 m
->interface
= s
->node_vtable
.interface
;
1784 m
->member
= v
->x
.property
.member
;
1787 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1796 case _SD_BUS_VTABLE_SIGNAL
:
1798 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1799 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1800 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1813 s
->node_vtable
.node
= n
;
1814 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1815 bus
->nodes_modified
= true;
1823 sd_bus_slot_unref(s
);
1824 bus_node_gc(bus
, n
);
1829 _public_
int sd_bus_add_object_vtable(
1833 const char *interface
,
1834 const sd_bus_vtable
*vtable
,
1837 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1840 _public_
int sd_bus_add_fallback_vtable(
1844 const char *interface
,
1845 const sd_bus_vtable
*vtable
,
1846 sd_bus_object_find_t find
,
1849 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1852 _public_
int sd_bus_add_node_enumerator(
1856 sd_bus_node_enumerator_t callback
,
1863 assert_return(bus
, -EINVAL
);
1864 assert_return(object_path_is_valid(path
), -EINVAL
);
1865 assert_return(callback
, -EINVAL
);
1866 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1868 n
= bus_node_allocate(bus
, path
);
1872 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1878 s
->node_enumerator
.callback
= callback
;
1880 s
->node_enumerator
.node
= n
;
1881 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1882 bus
->nodes_modified
= true;
1890 sd_bus_slot_unref(s
);
1891 bus_node_gc(bus
, n
);
1896 static int emit_properties_changed_on_interface(
1900 const char *interface
,
1901 bool require_fallback
,
1902 bool *found_interface
,
1905 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1906 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
1907 bool has_invalidating
= false, has_changing
= false;
1908 struct vtable_member key
= {};
1909 struct node_vtable
*c
;
1919 assert(found_interface
);
1921 n
= hashmap_get(bus
->nodes
, prefix
);
1925 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1929 r
= sd_bus_message_append(m
, "s", interface
);
1933 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1938 key
.interface
= interface
;
1940 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1941 if (require_fallback
&& !c
->is_fallback
)
1944 if (!streq(c
->interface
, interface
))
1947 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1950 if (bus
->nodes_modified
)
1955 *found_interface
= true;
1958 /* If the caller specified a list of
1959 * properties we include exactly those in the
1960 * PropertiesChanged message */
1962 STRV_FOREACH(property
, names
) {
1963 struct vtable_member
*v
;
1965 assert_return(member_name_is_valid(*property
), -EINVAL
);
1967 key
.member
= *property
;
1968 v
= hashmap_get(bus
->vtable_properties
, &key
);
1972 /* If there are two vtables for the same
1973 * interface, let's handle this property when
1974 * we come to that vtable. */
1978 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1979 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1981 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1983 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1984 has_invalidating
= true;
1988 has_changing
= true;
1990 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1993 if (bus
->nodes_modified
)
1997 const sd_bus_vtable
*v
;
1999 /* If the caller specified no properties list
2000 * we include all properties that are marked
2001 * as changing in the message. */
2003 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2004 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
2007 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2010 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
2011 has_invalidating
= true;
2015 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
2018 has_changing
= true;
2020 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
2023 if (bus
->nodes_modified
)
2029 if (!has_invalidating
&& !has_changing
)
2032 r
= sd_bus_message_close_container(m
);
2036 r
= sd_bus_message_open_container(m
, 'a', "s");
2040 if (has_invalidating
) {
2041 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2042 if (require_fallback
&& !c
->is_fallback
)
2045 if (!streq(c
->interface
, interface
))
2048 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2051 if (bus
->nodes_modified
)
2057 STRV_FOREACH(property
, names
) {
2058 struct vtable_member
*v
;
2060 key
.member
= *property
;
2061 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2062 assert(c
== v
->parent
);
2064 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2067 r
= sd_bus_message_append(m
, "s", *property
);
2072 const sd_bus_vtable
*v
;
2074 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2075 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
2078 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2081 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2084 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2092 r
= sd_bus_message_close_container(m
);
2096 r
= sd_bus_send(bus
, m
, NULL
);
2103 _public_
int sd_bus_emit_properties_changed_strv(
2106 const char *interface
,
2109 BUS_DONT_DESTROY(bus
);
2110 bool found_interface
= false;
2114 assert_return(bus
, -EINVAL
);
2115 assert_return(object_path_is_valid(path
), -EINVAL
);
2116 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2117 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2119 if (!BUS_IS_OPEN(bus
->state
))
2122 /* A non-NULL but empty names list means nothing needs to be
2123 generated. A NULL list OTOH indicates that all properties
2124 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2125 included in the PropertiesChanged message. */
2126 if (names
&& names
[0] == NULL
)
2130 bus
->nodes_modified
= false;
2132 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2135 if (bus
->nodes_modified
)
2138 prefix
= alloca(strlen(path
) + 1);
2139 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2140 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2143 if (bus
->nodes_modified
)
2147 } while (bus
->nodes_modified
);
2149 return found_interface
? 0 : -ENOENT
;
2152 _public_
int sd_bus_emit_properties_changed(
2155 const char *interface
,
2156 const char *name
, ...) {
2160 assert_return(bus
, -EINVAL
);
2161 assert_return(object_path_is_valid(path
), -EINVAL
);
2162 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2163 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2165 if (!BUS_IS_OPEN(bus
->state
))
2171 names
= strv_from_stdarg_alloca(name
);
2173 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2176 static int object_added_append_all_prefix(
2182 bool require_fallback
) {
2184 const char *previous_interface
= NULL
;
2185 struct node_vtable
*c
;
2195 n
= hashmap_get(bus
->nodes
, prefix
);
2199 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2200 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2203 if (require_fallback
&& !c
->is_fallback
)
2206 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2209 if (bus
->nodes_modified
)
2214 if (!streq_ptr(c
->interface
, previous_interface
)) {
2215 /* If a child-node already handled this interface, we
2216 * skip it on any of its parents. The child vtables
2217 * always fully override any conflicting vtables of
2218 * any parent node. */
2219 if (set_get(s
, c
->interface
))
2222 r
= set_put(s
, c
->interface
);
2226 if (previous_interface
) {
2227 r
= sd_bus_message_close_container(m
);
2230 r
= sd_bus_message_close_container(m
);
2235 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2238 r
= sd_bus_message_append(m
, "s", c
->interface
);
2241 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2245 previous_interface
= c
->interface
;
2248 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2251 if (bus
->nodes_modified
)
2255 if (previous_interface
) {
2256 r
= sd_bus_message_close_container(m
);
2259 r
= sd_bus_message_close_container(m
);
2267 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2268 _cleanup_set_free_ Set
*s
= NULL
;
2277 * This appends all interfaces registered on path @path. We first add
2278 * the builtin interfaces, which are always available and handled by
2279 * sd-bus. Then, we add all interfaces registered on the exact node,
2280 * followed by all fallback interfaces registered on any parent prefix.
2282 * If an interface is registered multiple times on the same node with
2283 * different vtables, we merge all the properties across all vtables.
2284 * However, if a child node has the same interface registered as one of
2285 * its parent nodes has as fallback, we make the child overwrite the
2286 * parent instead of extending it. Therefore, we keep a "Set" of all
2287 * handled interfaces during parent traversal, so we skip interfaces on
2288 * a parent that were overwritten by a child.
2291 s
= set_new(&string_hash_ops
);
2295 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2298 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2301 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2304 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2308 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2311 if (bus
->nodes_modified
)
2314 prefix
= alloca(strlen(path
) + 1);
2315 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2316 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2319 if (bus
->nodes_modified
)
2326 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2327 BUS_DONT_DESTROY(bus
);
2329 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2330 struct node
*object_manager
;
2334 * This emits an InterfacesAdded signal on the given path, by iterating
2335 * all registered vtables and fallback vtables on the path. All
2336 * properties are queried and included in the signal.
2337 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2338 * explicit list of registered interfaces. However, unlike
2339 * interfaces_added(), this call can figure out the list of supported
2340 * interfaces itself. Furthermore, it properly adds the builtin
2341 * org.freedesktop.DBus.* interfaces.
2344 assert_return(bus
, -EINVAL
);
2345 assert_return(object_path_is_valid(path
), -EINVAL
);
2346 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2348 if (!BUS_IS_OPEN(bus
->state
))
2351 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2358 bus
->nodes_modified
= false;
2359 m
= sd_bus_message_unref(m
);
2361 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2365 r
= sd_bus_message_append_basic(m
, 'o', path
);
2369 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2373 r
= object_added_append_all(bus
, m
, path
);
2377 if (bus
->nodes_modified
)
2380 r
= sd_bus_message_close_container(m
);
2384 } while (bus
->nodes_modified
);
2386 return sd_bus_send(bus
, m
, NULL
);
2389 static int object_removed_append_all_prefix(
2395 bool require_fallback
) {
2397 const char *previous_interface
= NULL
;
2398 struct node_vtable
*c
;
2408 n
= hashmap_get(bus
->nodes
, prefix
);
2412 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2413 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2416 if (require_fallback
&& !c
->is_fallback
)
2418 if (streq_ptr(c
->interface
, previous_interface
))
2421 /* If a child-node already handled this interface, we
2422 * skip it on any of its parents. The child vtables
2423 * always fully override any conflicting vtables of
2424 * any parent node. */
2425 if (set_get(s
, c
->interface
))
2428 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2431 if (bus
->nodes_modified
)
2436 r
= set_put(s
, c
->interface
);
2440 r
= sd_bus_message_append(m
, "s", c
->interface
);
2444 previous_interface
= c
->interface
;
2450 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2451 _cleanup_set_free_ Set
*s
= NULL
;
2459 /* see sd_bus_emit_object_added() for details */
2461 s
= set_new(&string_hash_ops
);
2465 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2468 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2471 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2474 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2478 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2481 if (bus
->nodes_modified
)
2484 prefix
= alloca(strlen(path
) + 1);
2485 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2486 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2489 if (bus
->nodes_modified
)
2496 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2497 BUS_DONT_DESTROY(bus
);
2499 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2500 struct node
*object_manager
;
2504 * This is like sd_bus_emit_object_added(), but emits an
2505 * InterfacesRemoved signal on the given path. This only includes any
2506 * registered interfaces but skips the properties. Note that this will
2507 * call into the find() callbacks of any registered vtable. Therefore,
2508 * you must call this function before destroying/unlinking your object.
2509 * Otherwise, the list of interfaces will be incomplete. However, note
2510 * that this will *NOT* call into any property callback. Therefore, the
2511 * object might be in an "destructed" state, as long as we can find it.
2514 assert_return(bus
, -EINVAL
);
2515 assert_return(object_path_is_valid(path
), -EINVAL
);
2516 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2518 if (!BUS_IS_OPEN(bus
->state
))
2521 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2528 bus
->nodes_modified
= false;
2529 m
= sd_bus_message_unref(m
);
2531 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2535 r
= sd_bus_message_append_basic(m
, 'o', path
);
2539 r
= sd_bus_message_open_container(m
, 'a', "s");
2543 r
= object_removed_append_all(bus
, m
, path
);
2547 if (bus
->nodes_modified
)
2550 r
= sd_bus_message_close_container(m
);
2554 } while (bus
->nodes_modified
);
2556 return sd_bus_send(bus
, m
, NULL
);
2559 static int interfaces_added_append_one_prefix(
2564 const char *interface
,
2565 bool require_fallback
) {
2567 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2568 bool found_interface
= false;
2569 struct node_vtable
*c
;
2580 n
= hashmap_get(bus
->nodes
, prefix
);
2584 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2585 if (require_fallback
&& !c
->is_fallback
)
2588 if (!streq(c
->interface
, interface
))
2591 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2594 if (bus
->nodes_modified
)
2599 if (!found_interface
) {
2600 r
= sd_bus_message_append_basic(m
, 's', interface
);
2604 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2608 found_interface
= true;
2611 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2614 if (bus
->nodes_modified
)
2618 if (found_interface
) {
2619 r
= sd_bus_message_close_container(m
);
2624 return found_interface
;
2627 static int interfaces_added_append_one(
2631 const char *interface
) {
2641 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2644 if (bus
->nodes_modified
)
2647 prefix
= alloca(strlen(path
) + 1);
2648 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2649 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2652 if (bus
->nodes_modified
)
2659 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2660 BUS_DONT_DESTROY(bus
);
2662 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2663 struct node
*object_manager
;
2667 assert_return(bus
, -EINVAL
);
2668 assert_return(object_path_is_valid(path
), -EINVAL
);
2669 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2671 if (!BUS_IS_OPEN(bus
->state
))
2674 if (strv_isempty(interfaces
))
2677 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2684 bus
->nodes_modified
= false;
2685 m
= sd_bus_message_unref(m
);
2687 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2691 r
= sd_bus_message_append_basic(m
, 'o', path
);
2695 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2699 STRV_FOREACH(i
, interfaces
) {
2700 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2702 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2706 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2710 if (bus
->nodes_modified
)
2713 r
= sd_bus_message_close_container(m
);
2718 if (bus
->nodes_modified
)
2721 r
= sd_bus_message_close_container(m
);
2725 } while (bus
->nodes_modified
);
2727 return sd_bus_send(bus
, m
, NULL
);
2730 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2733 assert_return(bus
, -EINVAL
);
2734 assert_return(object_path_is_valid(path
), -EINVAL
);
2735 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2737 if (!BUS_IS_OPEN(bus
->state
))
2740 interfaces
= strv_from_stdarg_alloca(interface
);
2742 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2745 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2746 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2747 struct node
*object_manager
;
2750 assert_return(bus
, -EINVAL
);
2751 assert_return(object_path_is_valid(path
), -EINVAL
);
2752 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2754 if (!BUS_IS_OPEN(bus
->state
))
2757 if (strv_isempty(interfaces
))
2760 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2766 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2770 r
= sd_bus_message_append_basic(m
, 'o', path
);
2774 r
= sd_bus_message_append_strv(m
, interfaces
);
2778 return sd_bus_send(bus
, m
, NULL
);
2781 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2784 assert_return(bus
, -EINVAL
);
2785 assert_return(object_path_is_valid(path
), -EINVAL
);
2786 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2788 if (!BUS_IS_OPEN(bus
->state
))
2791 interfaces
= strv_from_stdarg_alloca(interface
);
2793 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2796 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2801 assert_return(bus
, -EINVAL
);
2802 assert_return(object_path_is_valid(path
), -EINVAL
);
2803 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2805 n
= bus_node_allocate(bus
, path
);
2809 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2815 s
->node_object_manager
.node
= n
;
2816 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2817 bus
->nodes_modified
= true;
2825 sd_bus_slot_unref(s
);
2826 bus_node_gc(bus
, n
);