1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "alloc-util.h"
4 #include "bus-internal.h"
5 #include "bus-introspect.h"
6 #include "bus-message.h"
7 #include "bus-objects.h"
8 #include "bus-signature.h"
12 #include "missing_capability.h"
14 #include "string-util.h"
17 static int node_vtable_get_userdata(
20 struct node_vtable
*c
,
22 sd_bus_error
*error
) {
25 void *u
, *found_u
= NULL
;
32 s
= container_of(c
, sd_bus_slot
, node_vtable
);
35 bus
->current_slot
= sd_bus_slot_ref(s
);
36 bus
->current_userdata
= u
;
37 r
= c
->find(bus
, path
, c
->interface
, u
, &found_u
, error
);
38 bus
->current_userdata
= NULL
;
39 bus
->current_slot
= sd_bus_slot_unref(s
);
43 if (sd_bus_error_is_set(error
))
44 return -sd_bus_error_get_errno(error
);
56 static void *vtable_method_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
59 if (!u
|| FLAGS_SET(p
->flags
, SD_BUS_VTABLE_ABSOLUTE_OFFSET
))
60 return SIZE_TO_PTR(p
->x
.method
.offset
); /* don't add offset on NULL, to make ubsan happy */
62 return (uint8_t*) u
+ p
->x
.method
.offset
;
65 static void *vtable_property_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
68 if (!u
|| FLAGS_SET(p
->flags
, SD_BUS_VTABLE_ABSOLUTE_OFFSET
))
69 return SIZE_TO_PTR(p
->x
.property
.offset
); /* as above */
71 return (uint8_t*) u
+ p
->x
.property
.offset
;
74 static int vtable_property_get_userdata(
77 struct vtable_member
*p
,
79 sd_bus_error
*error
) {
89 r
= node_vtable_get_userdata(bus
, path
, p
->parent
, &u
, error
);
92 if (bus
->nodes_modified
)
95 *userdata
= vtable_property_convert_userdata(p
->vtable
, u
);
99 static int add_enumerated_to_set(
102 struct node_enumerator
*first
,
104 sd_bus_error
*error
) {
106 struct node_enumerator
*c
;
113 LIST_FOREACH(enumerators
, c
, first
) {
114 char **children
= NULL
, **k
;
117 if (bus
->nodes_modified
)
120 slot
= container_of(c
, sd_bus_slot
, node_enumerator
);
122 bus
->current_slot
= sd_bus_slot_ref(slot
);
123 bus
->current_userdata
= slot
->userdata
;
124 r
= c
->callback(bus
, prefix
, slot
->userdata
, &children
, error
);
125 bus
->current_userdata
= NULL
;
126 bus
->current_slot
= sd_bus_slot_unref(slot
);
130 if (sd_bus_error_is_set(error
))
131 return -sd_bus_error_get_errno(error
);
133 STRV_FOREACH(k
, children
) {
139 if (!object_path_is_valid(*k
)) {
145 if (!object_path_startswith(*k
, prefix
)) {
150 r
= set_consume(s
, *k
);
164 /* if set, add_subtree() works recursively */
165 CHILDREN_RECURSIVE
= 1 << 0,
166 /* if set, add_subtree() scans object-manager hierarchies recursively */
167 CHILDREN_SUBHIERARCHIES
= 1 << 1,
170 static int add_subtree_to_set(
176 sd_bus_error
*error
) {
186 r
= add_enumerated_to_set(bus
, prefix
, n
->enumerators
, s
, error
);
189 if (bus
->nodes_modified
)
192 LIST_FOREACH(siblings
, i
, n
->child
) {
195 if (!object_path_startswith(i
->path
, prefix
))
202 r
= set_consume(s
, t
);
203 if (r
< 0 && r
!= -EEXIST
)
206 if ((flags
& CHILDREN_RECURSIVE
) &&
207 ((flags
& CHILDREN_SUBHIERARCHIES
) || !i
->object_managers
)) {
208 r
= add_subtree_to_set(bus
, prefix
, i
, flags
, s
, error
);
211 if (bus
->nodes_modified
)
219 static int get_child_nodes(
225 sd_bus_error
*error
) {
235 s
= set_new(&string_hash_ops
);
239 r
= add_subtree_to_set(bus
, prefix
, n
, flags
, s
, error
);
249 static int node_callbacks_run(
252 struct node_callback
*first
,
253 bool require_fallback
,
254 bool *found_object
) {
256 struct node_callback
*c
;
261 assert(found_object
);
263 LIST_FOREACH(callbacks
, c
, first
) {
264 _cleanup_(sd_bus_error_free
) sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
267 if (bus
->nodes_modified
)
270 if (require_fallback
&& !c
->is_fallback
)
273 *found_object
= true;
275 if (c
->last_iteration
== bus
->iteration_counter
)
278 c
->last_iteration
= bus
->iteration_counter
;
280 r
= sd_bus_message_rewind(m
, true);
284 slot
= container_of(c
, sd_bus_slot
, node_callback
);
286 bus
->current_slot
= sd_bus_slot_ref(slot
);
287 bus
->current_handler
= c
->callback
;
288 bus
->current_userdata
= slot
->userdata
;
289 r
= c
->callback(m
, slot
->userdata
, &error_buffer
);
290 bus
->current_userdata
= NULL
;
291 bus
->current_handler
= NULL
;
292 bus
->current_slot
= sd_bus_slot_unref(slot
);
294 r
= bus_maybe_reply_error(m
, r
, &error_buffer
);
302 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
304 static int check_access(sd_bus
*bus
, sd_bus_message
*m
, struct vtable_member
*c
, sd_bus_error
*error
) {
312 /* If the entire bus is trusted let's grant access */
316 /* If the member is marked UNPRIVILEGED let's grant access */
317 if (c
->vtable
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
)
320 /* Check have the caller has the requested capability
321 * set. Note that the flags value contains the capability
322 * number plus one, which we need to subtract here. We do this
323 * so that we have 0 as special value for "default
325 cap
= CAPABILITY_SHIFT(c
->vtable
->flags
);
327 cap
= CAPABILITY_SHIFT(c
->parent
->vtable
[0].flags
);
333 r
= sd_bus_query_sender_privilege(m
, cap
);
339 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Access to %s.%s() not permitted.", c
->interface
, c
->member
);
342 static int method_callbacks_run(
345 struct vtable_member
*c
,
346 bool require_fallback
,
347 bool *found_object
) {
349 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
350 const char *signature
;
357 assert(found_object
);
359 if (require_fallback
&& !c
->parent
->is_fallback
)
362 if (FLAGS_SET(c
->vtable
->flags
, SD_BUS_VTABLE_SENSITIVE
)) {
363 r
= sd_bus_message_sensitive(m
);
368 r
= check_access(bus
, m
, c
, &error
);
370 return bus_maybe_reply_error(m
, r
, &error
);
372 r
= node_vtable_get_userdata(bus
, m
->path
, c
->parent
, &u
, &error
);
374 return bus_maybe_reply_error(m
, r
, &error
);
375 if (bus
->nodes_modified
)
378 u
= vtable_method_convert_userdata(c
->vtable
, u
);
380 *found_object
= true;
382 if (c
->last_iteration
== bus
->iteration_counter
)
385 c
->last_iteration
= bus
->iteration_counter
;
387 r
= sd_bus_message_rewind(m
, true);
391 signature
= sd_bus_message_get_signature(m
, true);
395 if (!streq(strempty(c
->vtable
->x
.method
.signature
), signature
))
396 return sd_bus_reply_method_errorf(
398 SD_BUS_ERROR_INVALID_ARGS
,
399 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
400 signature
, c
->interface
, c
->member
, strempty(c
->vtable
->x
.method
.signature
));
402 /* Keep track what the signature of the reply to this message
403 * should be, so that this can be enforced when sealing the
405 m
->enforced_reply_signature
= strempty(c
->vtable
->x
.method
.result
);
407 if (c
->vtable
->x
.method
.handler
) {
410 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
412 bus
->current_slot
= sd_bus_slot_ref(slot
);
413 bus
->current_handler
= c
->vtable
->x
.method
.handler
;
414 bus
->current_userdata
= u
;
415 r
= c
->vtable
->x
.method
.handler(m
, u
, &error
);
416 bus
->current_userdata
= NULL
;
417 bus
->current_handler
= NULL
;
418 bus
->current_slot
= sd_bus_slot_unref(slot
);
420 return bus_maybe_reply_error(m
, r
, &error
);
423 /* If the method callback is NULL, make this a successful NOP */
424 r
= sd_bus_reply_method_return(m
, NULL
);
431 static int invoke_property_get(
434 const sd_bus_vtable
*v
,
436 const char *interface
,
437 const char *property
,
438 sd_bus_message
*reply
,
440 sd_bus_error
*error
) {
453 if (v
->x
.property
.get
) {
455 bus
->current_slot
= sd_bus_slot_ref(slot
);
456 bus
->current_userdata
= userdata
;
457 r
= v
->x
.property
.get(bus
, path
, interface
, property
, reply
, userdata
, error
);
458 bus
->current_userdata
= NULL
;
459 bus
->current_slot
= sd_bus_slot_unref(slot
);
463 if (sd_bus_error_is_set(error
))
464 return -sd_bus_error_get_errno(error
);
468 /* Automatic handling if no callback is defined. */
470 if (streq(v
->x
.property
.signature
, "as"))
471 return sd_bus_message_append_strv(reply
, *(char***) userdata
);
473 assert(signature_is_single(v
->x
.property
.signature
, false));
474 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
476 switch (v
->x
.property
.signature
[0]) {
478 case SD_BUS_TYPE_STRING
:
479 case SD_BUS_TYPE_SIGNATURE
:
480 p
= strempty(*(char**) userdata
);
483 case SD_BUS_TYPE_OBJECT_PATH
:
484 p
= *(char**) userdata
;
493 return sd_bus_message_append_basic(reply
, v
->x
.property
.signature
[0], p
);
496 static int invoke_property_set(
499 const sd_bus_vtable
*v
,
501 const char *interface
,
502 const char *property
,
503 sd_bus_message
*value
,
505 sd_bus_error
*error
) {
517 if (v
->x
.property
.set
) {
519 bus
->current_slot
= sd_bus_slot_ref(slot
);
520 bus
->current_userdata
= userdata
;
521 r
= v
->x
.property
.set(bus
, path
, interface
, property
, value
, userdata
, error
);
522 bus
->current_userdata
= NULL
;
523 bus
->current_slot
= sd_bus_slot_unref(slot
);
527 if (sd_bus_error_is_set(error
))
528 return -sd_bus_error_get_errno(error
);
532 /* Automatic handling if no callback is defined. */
534 assert(signature_is_single(v
->x
.property
.signature
, false));
535 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
537 switch (v
->x
.property
.signature
[0]) {
539 case SD_BUS_TYPE_STRING
:
540 case SD_BUS_TYPE_OBJECT_PATH
:
541 case SD_BUS_TYPE_SIGNATURE
: {
545 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], &p
);
553 free(*(char**) userdata
);
554 *(char**) userdata
= n
;
560 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], userdata
);
570 static int property_get_set_callbacks_run(
573 struct vtable_member
*c
,
574 bool require_fallback
,
576 bool *found_object
) {
578 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
579 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
587 assert(found_object
);
589 if (require_fallback
&& !c
->parent
->is_fallback
)
592 if (FLAGS_SET(c
->vtable
->flags
, SD_BUS_VTABLE_SENSITIVE
)) {
593 r
= sd_bus_message_sensitive(m
);
598 r
= vtable_property_get_userdata(bus
, m
->path
, c
, &u
, &error
);
600 return bus_maybe_reply_error(m
, r
, &error
);
601 if (bus
->nodes_modified
)
604 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
606 *found_object
= true;
608 r
= sd_bus_message_new_method_return(m
, &reply
);
612 if (FLAGS_SET(c
->vtable
->flags
, SD_BUS_VTABLE_SENSITIVE
)) {
613 r
= sd_bus_message_sensitive(reply
);
619 /* Note that we do not protect against reexecution
620 * here (using the last_iteration check, see below),
621 * should the node tree have changed and we got called
622 * again. We assume that property Get() calls are
623 * ultimately without side-effects or if they aren't
624 * then at least idempotent. */
626 r
= sd_bus_message_open_container(reply
, 'v', c
->vtable
->x
.property
.signature
);
630 /* Note that we do not do an access check here. Read
631 * access to properties is always unrestricted, since
632 * PropertiesChanged signals broadcast contents
635 r
= invoke_property_get(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, reply
, u
, &error
);
637 return bus_maybe_reply_error(m
, r
, &error
);
639 if (bus
->nodes_modified
)
642 r
= sd_bus_message_close_container(reply
);
647 const char *signature
= NULL
;
650 if (c
->vtable
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
651 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_PROPERTY_READ_ONLY
, "Property '%s' is not writable.", c
->member
);
653 /* Avoid that we call the set routine more than once
654 * if the processing of this message got restarted
655 * because the node tree changed. */
656 if (c
->last_iteration
== bus
->iteration_counter
)
659 c
->last_iteration
= bus
->iteration_counter
;
661 r
= sd_bus_message_peek_type(m
, &type
, &signature
);
666 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_SIGNATURE
,
667 "Incorrect signature when setting property '%s', expected 'v', got '%c'.",
669 if (!streq(strempty(signature
), strempty(c
->vtable
->x
.property
.signature
)))
670 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
,
671 "Incorrect parameters for property '%s', expected '%s', got '%s'.",
672 c
->member
, strempty(c
->vtable
->x
.property
.signature
), strempty(signature
));
674 r
= sd_bus_message_enter_container(m
, 'v', c
->vtable
->x
.property
.signature
);
678 r
= check_access(bus
, m
, c
, &error
);
680 return bus_maybe_reply_error(m
, r
, &error
);
682 r
= invoke_property_set(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, m
, u
, &error
);
684 return bus_maybe_reply_error(m
, r
, &error
);
686 if (bus
->nodes_modified
)
689 r
= sd_bus_message_exit_container(m
);
694 r
= sd_bus_send(bus
, reply
, NULL
);
701 static int vtable_append_one_property(
703 sd_bus_message
*reply
,
705 struct node_vtable
*c
,
706 const sd_bus_vtable
*v
,
708 sd_bus_error
*error
) {
719 if (FLAGS_SET(c
->vtable
->flags
, SD_BUS_VTABLE_SENSITIVE
)) {
720 r
= sd_bus_message_sensitive(reply
);
725 r
= sd_bus_message_open_container(reply
, 'e', "sv");
729 r
= sd_bus_message_append(reply
, "s", v
->x
.property
.member
);
733 r
= sd_bus_message_open_container(reply
, 'v', v
->x
.property
.signature
);
737 slot
= container_of(c
, sd_bus_slot
, node_vtable
);
739 r
= invoke_property_get(bus
, slot
, v
, path
, c
->interface
, v
->x
.property
.member
, reply
, vtable_property_convert_userdata(v
, userdata
), error
);
742 if (bus
->nodes_modified
)
745 r
= sd_bus_message_close_container(reply
);
749 r
= sd_bus_message_close_container(reply
);
756 static int vtable_append_all_properties(
758 sd_bus_message
*reply
,
760 struct node_vtable
*c
,
762 sd_bus_error
*error
) {
764 const sd_bus_vtable
*v
;
772 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
776 for (v
= bus_vtable_next(c
->vtable
, v
); v
->type
!= _SD_BUS_VTABLE_END
; v
= bus_vtable_next(c
->vtable
, v
)) {
777 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
780 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
783 /* Let's not include properties marked as "explicit" in any message that contains a generic
784 * dump of properties, but only in those generated as a response to an explicit request. */
785 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)
788 /* Let's not include properties marked only for invalidation on change (i.e. in contrast to
789 * those whose new values are included in PropertiesChanges message) in any signals. This is
790 * useful to ensure they aren't included in InterfacesAdded messages. */
791 if (reply
->header
->type
!= SD_BUS_MESSAGE_METHOD_RETURN
&&
792 FLAGS_SET(v
->flags
, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
795 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
798 if (bus
->nodes_modified
)
805 static int property_get_all_callbacks_run(
808 struct node_vtable
*first
,
809 bool require_fallback
,
811 bool *found_object
) {
813 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
814 struct node_vtable
*c
;
815 bool found_interface
;
820 assert(found_object
);
822 r
= sd_bus_message_new_method_return(m
, &reply
);
826 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
830 found_interface
= !iface
|| STR_IN_SET(iface
,
831 "org.freedesktop.DBus.Properties",
832 "org.freedesktop.DBus.Peer",
833 "org.freedesktop.DBus.Introspectable");
835 LIST_FOREACH(vtables
, c
, first
) {
836 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
839 if (require_fallback
&& !c
->is_fallback
)
842 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
844 return bus_maybe_reply_error(m
, r
, &error
);
845 if (bus
->nodes_modified
)
850 *found_object
= true;
852 if (iface
&& !streq(c
->interface
, iface
))
854 found_interface
= true;
856 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
858 return bus_maybe_reply_error(m
, r
, &error
);
859 if (bus
->nodes_modified
)
866 if (!found_interface
) {
867 r
= sd_bus_reply_method_errorf(
869 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
870 "Unknown interface '%s'.", iface
);
877 r
= sd_bus_message_close_container(reply
);
881 r
= sd_bus_send(bus
, reply
, NULL
);
888 static int bus_node_exists(
892 bool require_fallback
) {
894 struct node_vtable
*c
;
895 struct node_callback
*k
;
902 /* Tests if there's anything attached directly to this node
903 * for the specified path */
905 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
908 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
909 if (require_fallback
&& !k
->is_fallback
)
915 LIST_FOREACH(vtables
, c
, n
->vtables
) {
916 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
918 if (require_fallback
&& !c
->is_fallback
)
921 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
924 if (bus
->nodes_modified
)
935 bool require_fallback
,
936 bool ignore_nodes_modified
,
939 sd_bus_error
*error
) {
941 _cleanup_set_free_free_ Set
*s
= NULL
;
942 _cleanup_(introspect_free
) struct introspect intro
= {};
943 struct node_vtable
*c
;
948 n
= hashmap_get(bus
->nodes
, path
);
953 r
= get_child_nodes(bus
, path
, n
, 0, &s
, error
);
956 if (bus
->nodes_modified
&& !ignore_nodes_modified
)
959 r
= introspect_begin(&intro
, bus
->trusted
);
963 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
967 empty
= set_isempty(s
);
969 LIST_FOREACH(vtables
, c
, n
->vtables
) {
970 if (require_fallback
&& !c
->is_fallback
)
973 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, error
);
976 if (bus
->nodes_modified
&& !ignore_nodes_modified
)
983 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
986 r
= introspect_write_interface(&intro
, c
->interface
, c
->vtable
);
992 /* Nothing?, let's see if we exist at all, and if not
993 * refuse to do anything */
994 r
= bus_node_exists(bus
, n
, path
, require_fallback
);
997 if (bus
->nodes_modified
&& !ignore_nodes_modified
)
1002 *found_object
= true;
1004 r
= introspect_write_child_nodes(&intro
, s
, path
);
1008 r
= introspect_finish(&intro
, ret
);
1015 static int process_introspect(
1019 bool require_fallback
,
1020 bool *found_object
) {
1022 _cleanup_free_
char *s
= NULL
;
1023 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1024 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1030 assert(found_object
);
1032 r
= introspect_path(bus
, m
->path
, n
, require_fallback
, false, found_object
, &s
, &error
);
1034 return bus_maybe_reply_error(m
, r
, &error
);
1036 /* nodes_modified == true */
1039 r
= sd_bus_message_new_method_return(m
, &reply
);
1043 r
= sd_bus_message_append(reply
, "s", s
);
1047 r
= sd_bus_send(bus
, reply
, NULL
);
1054 static int object_manager_serialize_path(
1056 sd_bus_message
*reply
,
1059 bool require_fallback
,
1060 sd_bus_error
*error
) {
1062 const char *previous_interface
= NULL
;
1063 bool found_something
= false;
1064 struct node_vtable
*i
;
1074 n
= hashmap_get(bus
->nodes
, prefix
);
1078 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1081 if (require_fallback
&& !i
->is_fallback
)
1084 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1087 if (bus
->nodes_modified
)
1092 if (!found_something
) {
1094 /* Open the object part */
1096 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1100 r
= sd_bus_message_append(reply
, "o", path
);
1104 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1108 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
1112 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
1116 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
1120 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1124 found_something
= true;
1127 if (!streq_ptr(previous_interface
, i
->interface
)) {
1129 /* Maybe close the previous interface part */
1131 if (previous_interface
) {
1132 r
= sd_bus_message_close_container(reply
);
1136 r
= sd_bus_message_close_container(reply
);
1141 /* Open the new interface part */
1143 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1147 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1151 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1156 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1159 if (bus
->nodes_modified
)
1162 previous_interface
= i
->interface
;
1165 if (previous_interface
) {
1166 r
= sd_bus_message_close_container(reply
);
1170 r
= sd_bus_message_close_container(reply
);
1175 if (found_something
) {
1176 r
= sd_bus_message_close_container(reply
);
1180 r
= sd_bus_message_close_container(reply
);
1188 static int object_manager_serialize_path_and_fallbacks(
1190 sd_bus_message
*reply
,
1192 sd_bus_error
*error
) {
1194 _cleanup_free_
char *prefix
= NULL
;
1203 /* First, add all vtables registered for this path */
1204 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1207 if (bus
->nodes_modified
)
1210 /* Second, add fallback vtables registered for any of the prefixes */
1212 assert(pl
<= BUS_PATH_SIZE_MAX
);
1213 prefix
= new(char, pl
+ 1);
1217 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1218 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1221 if (bus
->nodes_modified
)
1228 static int process_get_managed_objects(
1232 bool require_fallback
,
1233 bool *found_object
) {
1235 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1236 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1237 _cleanup_set_free_free_ Set
*s
= NULL
;
1245 assert(found_object
);
1247 /* Spec says, GetManagedObjects() is only implemented on the root of a
1248 * sub-tree. Therefore, we require a registered object-manager on
1249 * exactly the queried path, otherwise, we refuse to respond. */
1251 if (require_fallback
|| !n
->object_managers
)
1254 r
= get_child_nodes(bus
, m
->path
, n
, CHILDREN_RECURSIVE
, &s
, &error
);
1256 return bus_maybe_reply_error(m
, r
, &error
);
1257 if (bus
->nodes_modified
)
1260 r
= sd_bus_message_new_method_return(m
, &reply
);
1264 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1268 SET_FOREACH(path
, s
, i
) {
1269 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1271 return bus_maybe_reply_error(m
, r
, &error
);
1273 if (bus
->nodes_modified
)
1277 r
= sd_bus_message_close_container(reply
);
1281 r
= sd_bus_send(bus
, reply
, NULL
);
1288 static int object_find_and_run(
1292 bool require_fallback
,
1293 bool *found_object
) {
1296 struct vtable_member vtable_key
, *v
;
1302 assert(found_object
);
1304 n
= hashmap_get(bus
->nodes
, p
);
1308 /* First, try object callbacks */
1309 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1312 if (bus
->nodes_modified
)
1315 if (!m
->interface
|| !m
->member
)
1318 /* Then, look for a known method */
1319 vtable_key
.path
= (char*) p
;
1320 vtable_key
.interface
= m
->interface
;
1321 vtable_key
.member
= m
->member
;
1323 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1325 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1328 if (bus
->nodes_modified
)
1332 /* Then, look for a known property */
1333 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1336 get
= streq(m
->member
, "Get");
1338 if (get
|| streq(m
->member
, "Set")) {
1340 r
= sd_bus_message_rewind(m
, true);
1344 vtable_key
.path
= (char*) p
;
1346 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1348 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1350 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1352 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1357 } else if (streq(m
->member
, "GetAll")) {
1360 r
= sd_bus_message_rewind(m
, true);
1364 r
= sd_bus_message_read(m
, "s", &iface
);
1366 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1371 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1376 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1378 if (!isempty(sd_bus_message_get_signature(m
, true)))
1379 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1381 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1385 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1387 if (!isempty(sd_bus_message_get_signature(m
, true)))
1388 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1390 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1395 if (bus
->nodes_modified
)
1398 if (!*found_object
) {
1399 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1401 return bus_maybe_reply_error(m
, r
, NULL
);
1402 if (bus
->nodes_modified
)
1405 *found_object
= true;
1411 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1412 _cleanup_free_
char *prefix
= NULL
;
1415 bool found_object
= false;
1420 if (bus
->is_monitor
)
1423 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1426 if (hashmap_isempty(bus
->nodes
))
1429 /* Never respond to broadcast messages */
1430 if (bus
->bus_client
&& !m
->destination
)
1436 pl
= strlen(m
->path
);
1437 assert(pl
<= BUS_PATH_SIZE_MAX
);
1438 prefix
= new(char, pl
+ 1);
1443 bus
->nodes_modified
= false;
1445 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1449 /* Look for fallback prefixes */
1450 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1452 if (bus
->nodes_modified
)
1455 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1460 } while (bus
->nodes_modified
);
1465 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1466 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set")) {
1467 const char *interface
= NULL
, *property
= NULL
;
1469 (void) sd_bus_message_rewind(m
, true);
1470 (void) sd_bus_message_read_basic(m
, 's', &interface
);
1471 (void) sd_bus_message_read_basic(m
, 's', &property
);
1473 r
= sd_bus_reply_method_errorf(
1475 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1476 "Unknown interface %s or property %s.", strnull(interface
), strnull(property
));
1478 r
= sd_bus_reply_method_errorf(
1480 SD_BUS_ERROR_UNKNOWN_METHOD
,
1481 "Unknown method %s or interface %s.", m
->member
, m
->interface
);
1489 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1490 struct node
*n
, *parent
;
1492 _cleanup_free_
char *s
= NULL
;
1498 assert(path
[0] == '/');
1500 n
= hashmap_get(bus
->nodes
, path
);
1504 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1512 if (streq(path
, "/"))
1515 e
= strrchr(path
, '/');
1518 p
= strndupa(path
, MAX(1, e
- path
));
1520 parent
= bus_node_allocate(bus
, p
);
1525 n
= new0(struct node
, 1);
1530 n
->path
= TAKE_PTR(s
);
1532 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1539 LIST_PREPEND(siblings
, parent
->child
, n
);
1544 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1557 assert_se(hashmap_remove(b
->nodes
, n
->path
) == n
);
1560 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1563 bus_node_gc(b
, n
->parent
);
1567 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1573 n
= hashmap_get(bus
->nodes
, path
);
1575 _cleanup_free_
char *prefix
= NULL
;
1579 assert(pl
<= BUS_PATH_SIZE_MAX
);
1580 prefix
= new(char, pl
+ 1);
1584 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1585 n
= hashmap_get(bus
->nodes
, prefix
);
1591 while (n
&& !n
->object_managers
)
1599 static int bus_add_object(
1604 sd_bus_message_handler_t callback
,
1611 assert_return(bus
, -EINVAL
);
1612 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
1613 assert_return(object_path_is_valid(path
), -EINVAL
);
1614 assert_return(callback
, -EINVAL
);
1615 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1617 n
= bus_node_allocate(bus
, path
);
1621 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1627 s
->node_callback
.callback
= callback
;
1628 s
->node_callback
.is_fallback
= fallback
;
1630 s
->node_callback
.node
= n
;
1631 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1632 bus
->nodes_modified
= true;
1640 sd_bus_slot_unref(s
);
1641 bus_node_gc(bus
, n
);
1646 _public_
int sd_bus_add_object(
1650 sd_bus_message_handler_t callback
,
1653 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1656 _public_
int sd_bus_add_fallback(
1660 sd_bus_message_handler_t callback
,
1663 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1666 static void vtable_member_hash_func(const struct vtable_member
*m
, struct siphash
*state
) {
1669 string_hash_func(m
->path
, state
);
1670 string_hash_func(m
->interface
, state
);
1671 string_hash_func(m
->member
, state
);
1674 static int vtable_member_compare_func(const struct vtable_member
*x
, const struct vtable_member
*y
) {
1680 r
= strcmp(x
->path
, y
->path
);
1684 r
= strcmp(x
->interface
, y
->interface
);
1688 return strcmp(x
->member
, y
->member
);
1691 DEFINE_PRIVATE_HASH_OPS(vtable_member_hash_ops
, struct vtable_member
, vtable_member_hash_func
, vtable_member_compare_func
);
1694 NAMES_FIRST_PART
= 1 << 0, /* first part of argument name list (input names). It is reset by names_are_valid() */
1695 NAMES_PRESENT
= 1 << 1, /* at least one argument name is present, so the names will checked.
1696 This flag is set and used internally by names_are_valid(), but needs to be stored across calls for 2-parts list */
1697 NAMES_SINGLE_PART
= 1 << 2, /* argument name list consisting of a single part */
1700 static bool names_are_valid(const char *signature
, const char **names
, names_flags
*flags
) {
1703 if ((*flags
& NAMES_FIRST_PART
|| *flags
& NAMES_SINGLE_PART
) && **names
!= '\0')
1704 *flags
|= NAMES_PRESENT
;
1706 for (;*flags
& NAMES_PRESENT
;) {
1712 r
= signature_element_length(signature
, &l
);
1716 if (**names
!= '\0') {
1717 if (!member_name_is_valid(*names
))
1719 *names
+= strlen(*names
) + 1;
1720 } else if (*flags
& NAMES_PRESENT
)
1725 /* let's check if there are more argument names specified than the signature allows */
1726 if (*flags
& NAMES_PRESENT
&& **names
!= '\0' && !(*flags
& NAMES_FIRST_PART
))
1728 *flags
&= ~NAMES_FIRST_PART
;
1732 /* the current version of this struct is defined in sd-bus-vtable.h, but we need to list here the historical versions
1733 to make sure the calling code is compatible with one of these */
1734 struct sd_bus_vtable_221
{
1739 size_t element_size
;
1743 const char *signature
;
1745 sd_bus_message_handler_t handler
;
1750 const char *signature
;
1754 const char *signature
;
1755 sd_bus_property_get_t get
;
1756 sd_bus_property_set_t set
;
1761 /* Structure size up to v241 */
1762 #define VTABLE_ELEMENT_SIZE_221 sizeof(struct sd_bus_vtable_221)
1764 /* Size of the structure when "features" field was added. If the structure definition is augmented, a copy of
1765 * the structure definition will need to be made (similarly to the sd_bus_vtable_221 above), and this
1766 * definition updated to refer to it. */
1767 #define VTABLE_ELEMENT_SIZE_242 sizeof(struct sd_bus_vtable)
1769 static int vtable_features(const sd_bus_vtable
*vtable
) {
1770 if (vtable
[0].x
.start
.element_size
< VTABLE_ELEMENT_SIZE_242
||
1771 !vtable
[0].x
.start
.vtable_format_reference
)
1773 return vtable
[0].x
.start
.features
;
1776 bool bus_vtable_has_names(const sd_bus_vtable
*vtable
) {
1777 return vtable_features(vtable
) & _SD_BUS_VTABLE_PARAM_NAMES
;
1780 const sd_bus_vtable
* bus_vtable_next(const sd_bus_vtable
*vtable
, const sd_bus_vtable
*v
) {
1781 return (const sd_bus_vtable
*) ((char*) v
+ vtable
[0].x
.start
.element_size
);
1784 static int add_object_vtable_internal(
1788 const char *interface
,
1789 const sd_bus_vtable
*vtable
,
1791 sd_bus_object_find_t find
,
1794 sd_bus_slot
*s
= NULL
;
1795 struct node_vtable
*i
, *existing
= NULL
;
1796 const sd_bus_vtable
*v
;
1799 const char *names
= "";
1802 assert_return(bus
, -EINVAL
);
1803 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
1804 assert_return(object_path_is_valid(path
), -EINVAL
);
1805 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1806 assert_return(vtable
, -EINVAL
);
1807 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1808 assert_return(vtable
[0].x
.start
.element_size
== VTABLE_ELEMENT_SIZE_221
||
1809 vtable
[0].x
.start
.element_size
>= VTABLE_ELEMENT_SIZE_242
,
1811 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1812 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1813 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1814 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1815 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1817 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1821 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1825 n
= bus_node_allocate(bus
, path
);
1829 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1830 if (i
->is_fallback
!= fallback
) {
1835 if (streq(i
->interface
, interface
)) {
1837 if (i
->vtable
== vtable
) {
1846 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1852 s
->node_vtable
.is_fallback
= fallback
;
1853 s
->node_vtable
.vtable
= vtable
;
1854 s
->node_vtable
.find
= find
;
1856 s
->node_vtable
.interface
= strdup(interface
);
1857 if (!s
->node_vtable
.interface
) {
1862 v
= s
->node_vtable
.vtable
;
1863 for (v
= bus_vtable_next(vtable
, v
); v
->type
!= _SD_BUS_VTABLE_END
; v
= bus_vtable_next(vtable
, v
)) {
1867 case _SD_BUS_VTABLE_METHOD
: {
1868 struct vtable_member
*m
;
1869 nf
= NAMES_FIRST_PART
;
1871 if (bus_vtable_has_names(vtable
))
1872 names
= strempty(v
->x
.method
.names
);
1874 if (!member_name_is_valid(v
->x
.method
.member
) ||
1875 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1876 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1877 !names_are_valid(strempty(v
->x
.method
.signature
), &names
, &nf
) ||
1878 !names_are_valid(strempty(v
->x
.method
.result
), &names
, &nf
) ||
1879 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1880 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1885 m
= new0(struct vtable_member
, 1);
1891 m
->parent
= &s
->node_vtable
;
1893 m
->interface
= s
->node_vtable
.interface
;
1894 m
->member
= v
->x
.method
.member
;
1897 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1906 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1908 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1913 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1919 case _SD_BUS_VTABLE_PROPERTY
: {
1920 struct vtable_member
*m
;
1922 if (!member_name_is_valid(v
->x
.property
.member
) ||
1923 !signature_is_single(v
->x
.property
.signature
, false) ||
1924 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1925 (v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
) ||
1926 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1927 ((v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) && (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)) ||
1928 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1933 m
= new0(struct vtable_member
, 1);
1939 m
->parent
= &s
->node_vtable
;
1941 m
->interface
= s
->node_vtable
.interface
;
1942 m
->member
= v
->x
.property
.member
;
1945 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1954 case _SD_BUS_VTABLE_SIGNAL
:
1955 nf
= NAMES_SINGLE_PART
;
1957 if (bus_vtable_has_names(vtable
))
1958 names
= strempty(v
->x
.signal
.names
);
1960 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1961 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1962 !names_are_valid(strempty(v
->x
.signal
.signature
), &names
, &nf
) ||
1963 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1976 s
->node_vtable
.node
= n
;
1977 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1978 bus
->nodes_modified
= true;
1986 sd_bus_slot_unref(s
);
1987 bus_node_gc(bus
, n
);
1992 /* This symbol exists solely to tell the linker that the "new" vtable format is used. */
1993 _public_
const unsigned sd_bus_object_vtable_format
= 242;
1995 _public_
int sd_bus_add_object_vtable(
1999 const char *interface
,
2000 const sd_bus_vtable
*vtable
,
2003 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
2006 _public_
int sd_bus_add_fallback_vtable(
2010 const char *interface
,
2011 const sd_bus_vtable
*vtable
,
2012 sd_bus_object_find_t find
,
2015 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
2018 _public_
int sd_bus_add_node_enumerator(
2022 sd_bus_node_enumerator_t callback
,
2029 assert_return(bus
, -EINVAL
);
2030 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2031 assert_return(object_path_is_valid(path
), -EINVAL
);
2032 assert_return(callback
, -EINVAL
);
2033 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2035 n
= bus_node_allocate(bus
, path
);
2039 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
2045 s
->node_enumerator
.callback
= callback
;
2047 s
->node_enumerator
.node
= n
;
2048 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
2049 bus
->nodes_modified
= true;
2057 sd_bus_slot_unref(s
);
2058 bus_node_gc(bus
, n
);
2063 static int emit_properties_changed_on_interface(
2067 const char *interface
,
2068 bool require_fallback
,
2069 bool *found_interface
,
2072 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2073 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2074 bool has_invalidating
= false, has_changing
= false;
2075 struct vtable_member key
= {};
2076 struct node_vtable
*c
;
2086 assert(found_interface
);
2088 n
= hashmap_get(bus
->nodes
, prefix
);
2092 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
2096 r
= sd_bus_message_append(m
, "s", interface
);
2100 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2105 key
.interface
= interface
;
2107 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2108 if (require_fallback
&& !c
->is_fallback
)
2111 if (!streq(c
->interface
, interface
))
2114 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2117 if (bus
->nodes_modified
)
2122 *found_interface
= true;
2125 /* If the caller specified a list of
2126 * properties we include exactly those in the
2127 * PropertiesChanged message */
2129 STRV_FOREACH(property
, names
) {
2130 struct vtable_member
*v
;
2132 assert_return(member_name_is_valid(*property
), -EINVAL
);
2134 key
.member
= *property
;
2135 v
= hashmap_get(bus
->vtable_properties
, &key
);
2139 /* If there are two vtables for the same
2140 * interface, let's handle this property when
2141 * we come to that vtable. */
2145 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
2146 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
2148 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
2150 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
2151 has_invalidating
= true;
2155 has_changing
= true;
2157 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
2160 if (bus
->nodes_modified
)
2164 const sd_bus_vtable
*v
;
2166 /* If the caller specified no properties list
2167 * we include all properties that are marked
2168 * as changing in the message. */
2171 for (v
= bus_vtable_next(c
->vtable
, v
); v
->type
!= _SD_BUS_VTABLE_END
; v
= bus_vtable_next(c
->vtable
, v
)) {
2172 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
2175 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2178 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
2179 has_invalidating
= true;
2183 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
2186 has_changing
= true;
2188 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
2191 if (bus
->nodes_modified
)
2197 if (!has_invalidating
&& !has_changing
)
2200 r
= sd_bus_message_close_container(m
);
2204 r
= sd_bus_message_open_container(m
, 'a', "s");
2208 if (has_invalidating
) {
2209 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2210 if (require_fallback
&& !c
->is_fallback
)
2213 if (!streq(c
->interface
, interface
))
2216 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2219 if (bus
->nodes_modified
)
2225 STRV_FOREACH(property
, names
) {
2226 struct vtable_member
*v
;
2228 key
.member
= *property
;
2229 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2230 assert(c
== v
->parent
);
2232 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2235 r
= sd_bus_message_append(m
, "s", *property
);
2240 const sd_bus_vtable
*v
;
2243 for (v
= bus_vtable_next(c
->vtable
, v
); v
->type
!= _SD_BUS_VTABLE_END
; v
= bus_vtable_next(c
->vtable
, v
)) {
2244 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
2247 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2250 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2253 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2261 r
= sd_bus_message_close_container(m
);
2265 r
= sd_bus_send(bus
, m
, NULL
);
2272 _public_
int sd_bus_emit_properties_changed_strv(
2275 const char *interface
,
2278 _cleanup_free_
char *prefix
= NULL
;
2279 bool found_interface
= false;
2283 assert_return(bus
, -EINVAL
);
2284 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2285 assert_return(object_path_is_valid(path
), -EINVAL
);
2286 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2287 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2289 if (!BUS_IS_OPEN(bus
->state
))
2292 /* A non-NULL but empty names list means nothing needs to be
2293 generated. A NULL list OTOH indicates that all properties
2294 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2295 included in the PropertiesChanged message. */
2296 if (names
&& names
[0] == NULL
)
2299 BUS_DONT_DESTROY(bus
);
2302 assert(pl
<= BUS_PATH_SIZE_MAX
);
2303 prefix
= new(char, pl
+ 1);
2308 bus
->nodes_modified
= false;
2310 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2313 if (bus
->nodes_modified
)
2316 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2317 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2320 if (bus
->nodes_modified
)
2324 } while (bus
->nodes_modified
);
2326 return found_interface
? 0 : -ENOENT
;
2329 _public_
int sd_bus_emit_properties_changed(
2332 const char *interface
,
2333 const char *name
, ...) {
2337 assert_return(bus
, -EINVAL
);
2338 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2339 assert_return(object_path_is_valid(path
), -EINVAL
);
2340 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2341 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2343 if (!BUS_IS_OPEN(bus
->state
))
2349 names
= strv_from_stdarg_alloca(name
);
2351 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2354 static int object_added_append_all_prefix(
2360 bool require_fallback
) {
2362 const char *previous_interface
= NULL
;
2363 struct node_vtable
*c
;
2373 n
= hashmap_get(bus
->nodes
, prefix
);
2377 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2378 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2381 if (require_fallback
&& !c
->is_fallback
)
2384 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2387 if (bus
->nodes_modified
)
2392 if (!streq_ptr(c
->interface
, previous_interface
)) {
2393 /* If a child-node already handled this interface, we
2394 * skip it on any of its parents. The child vtables
2395 * always fully override any conflicting vtables of
2396 * any parent node. */
2397 if (set_get(s
, c
->interface
))
2400 r
= set_put(s
, c
->interface
);
2404 if (previous_interface
) {
2405 r
= sd_bus_message_close_container(m
);
2408 r
= sd_bus_message_close_container(m
);
2413 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2416 r
= sd_bus_message_append(m
, "s", c
->interface
);
2419 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2423 previous_interface
= c
->interface
;
2426 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2429 if (bus
->nodes_modified
)
2433 if (previous_interface
) {
2434 r
= sd_bus_message_close_container(m
);
2437 r
= sd_bus_message_close_container(m
);
2445 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2446 _cleanup_set_free_ Set
*s
= NULL
;
2447 _cleanup_free_
char *prefix
= NULL
;
2456 * This appends all interfaces registered on path @path. We first add
2457 * the builtin interfaces, which are always available and handled by
2458 * sd-bus. Then, we add all interfaces registered on the exact node,
2459 * followed by all fallback interfaces registered on any parent prefix.
2461 * If an interface is registered multiple times on the same node with
2462 * different vtables, we merge all the properties across all vtables.
2463 * However, if a child node has the same interface registered as one of
2464 * its parent nodes has as fallback, we make the child overwrite the
2465 * parent instead of extending it. Therefore, we keep a "Set" of all
2466 * handled interfaces during parent traversal, so we skip interfaces on
2467 * a parent that were overwritten by a child.
2470 s
= set_new(&string_hash_ops
);
2474 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2477 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2480 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2483 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2487 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2490 if (bus
->nodes_modified
)
2494 assert(pl
<= BUS_PATH_SIZE_MAX
);
2495 prefix
= new(char, pl
+ 1);
2499 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2500 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2503 if (bus
->nodes_modified
)
2510 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2511 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2512 struct node
*object_manager
;
2516 * This emits an InterfacesAdded signal on the given path, by iterating
2517 * all registered vtables and fallback vtables on the path. All
2518 * properties are queried and included in the signal.
2519 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2520 * explicit list of registered interfaces. However, unlike
2521 * interfaces_added(), this call can figure out the list of supported
2522 * interfaces itself. Furthermore, it properly adds the builtin
2523 * org.freedesktop.DBus.* interfaces.
2526 assert_return(bus
, -EINVAL
);
2527 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2528 assert_return(object_path_is_valid(path
), -EINVAL
);
2529 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2531 if (!BUS_IS_OPEN(bus
->state
))
2534 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2540 BUS_DONT_DESTROY(bus
);
2543 bus
->nodes_modified
= false;
2544 m
= sd_bus_message_unref(m
);
2546 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2550 r
= sd_bus_message_append_basic(m
, 'o', path
);
2554 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2558 r
= object_added_append_all(bus
, m
, path
);
2562 if (bus
->nodes_modified
)
2565 r
= sd_bus_message_close_container(m
);
2569 } while (bus
->nodes_modified
);
2571 return sd_bus_send(bus
, m
, NULL
);
2574 static int object_removed_append_all_prefix(
2580 bool require_fallback
) {
2582 const char *previous_interface
= NULL
;
2583 struct node_vtable
*c
;
2593 n
= hashmap_get(bus
->nodes
, prefix
);
2597 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2598 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2601 if (require_fallback
&& !c
->is_fallback
)
2603 if (streq_ptr(c
->interface
, previous_interface
))
2606 /* If a child-node already handled this interface, we
2607 * skip it on any of its parents. The child vtables
2608 * always fully override any conflicting vtables of
2609 * any parent node. */
2610 if (set_get(s
, c
->interface
))
2613 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2616 if (bus
->nodes_modified
)
2621 r
= set_put(s
, c
->interface
);
2625 r
= sd_bus_message_append(m
, "s", c
->interface
);
2629 previous_interface
= c
->interface
;
2635 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2636 _cleanup_set_free_ Set
*s
= NULL
;
2637 _cleanup_free_
char *prefix
= NULL
;
2645 /* see sd_bus_emit_object_added() for details */
2647 s
= set_new(&string_hash_ops
);
2651 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2654 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2657 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2660 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2664 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2667 if (bus
->nodes_modified
)
2671 assert(pl
<= BUS_PATH_SIZE_MAX
);
2672 prefix
= new(char, pl
+ 1);
2676 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2677 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2680 if (bus
->nodes_modified
)
2687 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2688 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2689 struct node
*object_manager
;
2693 * This is like sd_bus_emit_object_added(), but emits an
2694 * InterfacesRemoved signal on the given path. This only includes any
2695 * registered interfaces but skips the properties. Note that this will
2696 * call into the find() callbacks of any registered vtable. Therefore,
2697 * you must call this function before destroying/unlinking your object.
2698 * Otherwise, the list of interfaces will be incomplete. However, note
2699 * that this will *NOT* call into any property callback. Therefore, the
2700 * object might be in an "destructed" state, as long as we can find it.
2703 assert_return(bus
, -EINVAL
);
2704 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2705 assert_return(object_path_is_valid(path
), -EINVAL
);
2706 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2708 if (!BUS_IS_OPEN(bus
->state
))
2711 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2717 BUS_DONT_DESTROY(bus
);
2720 bus
->nodes_modified
= false;
2721 m
= sd_bus_message_unref(m
);
2723 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2727 r
= sd_bus_message_append_basic(m
, 'o', path
);
2731 r
= sd_bus_message_open_container(m
, 'a', "s");
2735 r
= object_removed_append_all(bus
, m
, path
);
2739 if (bus
->nodes_modified
)
2742 r
= sd_bus_message_close_container(m
);
2746 } while (bus
->nodes_modified
);
2748 return sd_bus_send(bus
, m
, NULL
);
2751 static int interfaces_added_append_one_prefix(
2756 const char *interface
,
2757 bool require_fallback
) {
2759 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2760 bool found_interface
= false;
2761 struct node_vtable
*c
;
2772 n
= hashmap_get(bus
->nodes
, prefix
);
2776 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2777 if (require_fallback
&& !c
->is_fallback
)
2780 if (!streq(c
->interface
, interface
))
2783 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2786 if (bus
->nodes_modified
)
2791 if (!found_interface
) {
2792 r
= sd_bus_message_append_basic(m
, 's', interface
);
2796 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2800 found_interface
= true;
2803 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2806 if (bus
->nodes_modified
)
2810 if (found_interface
) {
2811 r
= sd_bus_message_close_container(m
);
2816 return found_interface
;
2819 static int interfaces_added_append_one(
2823 const char *interface
) {
2825 _cleanup_free_
char *prefix
= NULL
;
2834 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2837 if (bus
->nodes_modified
)
2841 assert(pl
<= BUS_PATH_SIZE_MAX
);
2842 prefix
= new(char, pl
+ 1);
2846 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2847 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2850 if (bus
->nodes_modified
)
2857 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2858 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2859 struct node
*object_manager
;
2863 assert_return(bus
, -EINVAL
);
2864 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2865 assert_return(object_path_is_valid(path
), -EINVAL
);
2866 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2868 if (!BUS_IS_OPEN(bus
->state
))
2871 if (strv_isempty(interfaces
))
2874 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2880 BUS_DONT_DESTROY(bus
);
2883 bus
->nodes_modified
= false;
2884 m
= sd_bus_message_unref(m
);
2886 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2890 r
= sd_bus_message_append_basic(m
, 'o', path
);
2894 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2898 STRV_FOREACH(i
, interfaces
) {
2899 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2901 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2905 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2909 if (bus
->nodes_modified
)
2912 r
= sd_bus_message_close_container(m
);
2917 if (bus
->nodes_modified
)
2920 r
= sd_bus_message_close_container(m
);
2924 } while (bus
->nodes_modified
);
2926 return sd_bus_send(bus
, m
, NULL
);
2929 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2932 assert_return(bus
, -EINVAL
);
2933 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2934 assert_return(object_path_is_valid(path
), -EINVAL
);
2935 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2937 if (!BUS_IS_OPEN(bus
->state
))
2940 interfaces
= strv_from_stdarg_alloca(interface
);
2942 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2945 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2946 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2947 struct node
*object_manager
;
2950 assert_return(bus
, -EINVAL
);
2951 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2952 assert_return(object_path_is_valid(path
), -EINVAL
);
2953 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2955 if (!BUS_IS_OPEN(bus
->state
))
2958 if (strv_isempty(interfaces
))
2961 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2967 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2971 r
= sd_bus_message_append_basic(m
, 'o', path
);
2975 r
= sd_bus_message_append_strv(m
, interfaces
);
2979 return sd_bus_send(bus
, m
, NULL
);
2982 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2985 assert_return(bus
, -EINVAL
);
2986 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2987 assert_return(object_path_is_valid(path
), -EINVAL
);
2988 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2990 if (!BUS_IS_OPEN(bus
->state
))
2993 interfaces
= strv_from_stdarg_alloca(interface
);
2995 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2998 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
3003 assert_return(bus
, -EINVAL
);
3004 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
3005 assert_return(object_path_is_valid(path
), -EINVAL
);
3006 assert_return(!bus_pid_changed(bus
), -ECHILD
);
3008 n
= bus_node_allocate(bus
, path
);
3012 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
3018 s
->node_object_manager
.node
= n
;
3019 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
3020 bus
->nodes_modified
= true;
3028 sd_bus_slot_unref(s
);
3029 bus_node_gc(bus
, n
);