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
) {
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
) {
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
||
831 streq(iface
, "org.freedesktop.DBus.Properties") ||
832 streq(iface
, "org.freedesktop.DBus.Peer") ||
833 streq(iface
, "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 const char *previous_interface
= NULL
;
943 _cleanup_(introspect_free
) struct introspect intro
= {};
944 struct node_vtable
*c
;
949 n
= hashmap_get(bus
->nodes
, path
);
954 r
= get_child_nodes(bus
, path
, n
, 0, &s
, error
);
957 if (bus
->nodes_modified
&& !ignore_nodes_modified
)
960 r
= introspect_begin(&intro
, bus
->trusted
);
964 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
968 empty
= set_isempty(s
);
970 LIST_FOREACH(vtables
, c
, n
->vtables
) {
971 if (require_fallback
&& !c
->is_fallback
)
974 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, error
);
977 if (bus
->nodes_modified
&& !ignore_nodes_modified
)
984 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
987 if (!streq_ptr(previous_interface
, c
->interface
)) {
988 if (previous_interface
)
989 fputs(" </interface>\n", intro
.f
);
991 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
994 r
= introspect_write_interface(&intro
, c
->vtable
);
998 previous_interface
= c
->interface
;
1001 if (previous_interface
)
1002 fputs(" </interface>\n", intro
.f
);
1005 /* Nothing?, let's see if we exist at all, and if not
1006 * refuse to do anything */
1007 r
= bus_node_exists(bus
, n
, path
, require_fallback
);
1010 if (bus
->nodes_modified
&& !ignore_nodes_modified
)
1015 *found_object
= true;
1017 r
= introspect_write_child_nodes(&intro
, s
, path
);
1021 r
= introspect_finish(&intro
, ret
);
1028 static int process_introspect(
1032 bool require_fallback
,
1033 bool *found_object
) {
1035 _cleanup_free_
char *s
= NULL
;
1036 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1037 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1043 assert(found_object
);
1045 r
= introspect_path(bus
, m
->path
, n
, require_fallback
, false, found_object
, &s
, &error
);
1047 return bus_maybe_reply_error(m
, r
, &error
);
1049 /* nodes_modified == true */
1052 r
= sd_bus_message_new_method_return(m
, &reply
);
1056 r
= sd_bus_message_append(reply
, "s", s
);
1060 r
= sd_bus_send(bus
, reply
, NULL
);
1067 static int object_manager_serialize_path(
1069 sd_bus_message
*reply
,
1072 bool require_fallback
,
1073 sd_bus_error
*error
) {
1075 const char *previous_interface
= NULL
;
1076 bool found_something
= false;
1077 struct node_vtable
*i
;
1087 n
= hashmap_get(bus
->nodes
, prefix
);
1091 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1094 if (require_fallback
&& !i
->is_fallback
)
1097 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1100 if (bus
->nodes_modified
)
1105 if (!found_something
) {
1107 /* Open the object part */
1109 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1113 r
= sd_bus_message_append(reply
, "o", path
);
1117 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1121 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
1125 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
1129 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
1133 r
= sd_bus_message_append(reply
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1137 found_something
= true;
1140 if (!streq_ptr(previous_interface
, i
->interface
)) {
1142 /* Maybe close the previous interface part */
1144 if (previous_interface
) {
1145 r
= sd_bus_message_close_container(reply
);
1149 r
= sd_bus_message_close_container(reply
);
1154 /* Open the new interface part */
1156 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1160 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1164 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1169 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1172 if (bus
->nodes_modified
)
1175 previous_interface
= i
->interface
;
1178 if (previous_interface
) {
1179 r
= sd_bus_message_close_container(reply
);
1183 r
= sd_bus_message_close_container(reply
);
1188 if (found_something
) {
1189 r
= sd_bus_message_close_container(reply
);
1193 r
= sd_bus_message_close_container(reply
);
1201 static int object_manager_serialize_path_and_fallbacks(
1203 sd_bus_message
*reply
,
1205 sd_bus_error
*error
) {
1207 _cleanup_free_
char *prefix
= NULL
;
1216 /* First, add all vtables registered for this path */
1217 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1220 if (bus
->nodes_modified
)
1223 /* Second, add fallback vtables registered for any of the prefixes */
1225 assert(pl
<= BUS_PATH_SIZE_MAX
);
1226 prefix
= new(char, pl
+ 1);
1230 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1231 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1234 if (bus
->nodes_modified
)
1241 static int process_get_managed_objects(
1245 bool require_fallback
,
1246 bool *found_object
) {
1248 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
1249 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
1250 _cleanup_set_free_free_ Set
*s
= NULL
;
1258 assert(found_object
);
1260 /* Spec says, GetManagedObjects() is only implemented on the root of a
1261 * sub-tree. Therefore, we require a registered object-manager on
1262 * exactly the queried path, otherwise, we refuse to respond. */
1264 if (require_fallback
|| !n
->object_managers
)
1267 r
= get_child_nodes(bus
, m
->path
, n
, CHILDREN_RECURSIVE
, &s
, &error
);
1269 return bus_maybe_reply_error(m
, r
, &error
);
1270 if (bus
->nodes_modified
)
1273 r
= sd_bus_message_new_method_return(m
, &reply
);
1277 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1281 SET_FOREACH(path
, s
, i
) {
1282 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1284 return bus_maybe_reply_error(m
, r
, &error
);
1286 if (bus
->nodes_modified
)
1290 r
= sd_bus_message_close_container(reply
);
1294 r
= sd_bus_send(bus
, reply
, NULL
);
1301 static int object_find_and_run(
1305 bool require_fallback
,
1306 bool *found_object
) {
1309 struct vtable_member vtable_key
, *v
;
1315 assert(found_object
);
1317 n
= hashmap_get(bus
->nodes
, p
);
1321 /* First, try object callbacks */
1322 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1325 if (bus
->nodes_modified
)
1328 if (!m
->interface
|| !m
->member
)
1331 /* Then, look for a known method */
1332 vtable_key
.path
= (char*) p
;
1333 vtable_key
.interface
= m
->interface
;
1334 vtable_key
.member
= m
->member
;
1336 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1338 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1341 if (bus
->nodes_modified
)
1345 /* Then, look for a known property */
1346 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1349 get
= streq(m
->member
, "Get");
1351 if (get
|| streq(m
->member
, "Set")) {
1353 r
= sd_bus_message_rewind(m
, true);
1357 vtable_key
.path
= (char*) p
;
1359 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1361 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1363 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1365 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1370 } else if (streq(m
->member
, "GetAll")) {
1373 r
= sd_bus_message_rewind(m
, true);
1377 r
= sd_bus_message_read(m
, "s", &iface
);
1379 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1384 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1389 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1391 if (!isempty(sd_bus_message_get_signature(m
, true)))
1392 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1394 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1398 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1400 if (!isempty(sd_bus_message_get_signature(m
, true)))
1401 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1403 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1408 if (bus
->nodes_modified
)
1411 if (!*found_object
) {
1412 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1414 return bus_maybe_reply_error(m
, r
, NULL
);
1415 if (bus
->nodes_modified
)
1418 *found_object
= true;
1424 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1425 _cleanup_free_
char *prefix
= NULL
;
1428 bool found_object
= false;
1433 if (bus
->is_monitor
)
1436 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1439 if (hashmap_isempty(bus
->nodes
))
1442 /* Never respond to broadcast messages */
1443 if (bus
->bus_client
&& !m
->destination
)
1449 pl
= strlen(m
->path
);
1450 assert(pl
<= BUS_PATH_SIZE_MAX
);
1451 prefix
= new(char, pl
+ 1);
1456 bus
->nodes_modified
= false;
1458 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1462 /* Look for fallback prefixes */
1463 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1465 if (bus
->nodes_modified
)
1468 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1473 } while (bus
->nodes_modified
);
1478 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1479 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set")) {
1480 const char *interface
= NULL
, *property
= NULL
;
1482 (void) sd_bus_message_rewind(m
, true);
1483 (void) sd_bus_message_read_basic(m
, 's', &interface
);
1484 (void) sd_bus_message_read_basic(m
, 's', &property
);
1486 r
= sd_bus_reply_method_errorf(
1488 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1489 "Unknown interface %s or property %s.", strnull(interface
), strnull(property
));
1491 r
= sd_bus_reply_method_errorf(
1493 SD_BUS_ERROR_UNKNOWN_METHOD
,
1494 "Unknown method %s or interface %s.", m
->member
, m
->interface
);
1502 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1503 struct node
*n
, *parent
;
1505 _cleanup_free_
char *s
= NULL
;
1511 assert(path
[0] == '/');
1513 n
= hashmap_get(bus
->nodes
, path
);
1517 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1525 if (streq(path
, "/"))
1528 e
= strrchr(path
, '/');
1531 p
= strndupa(path
, MAX(1, e
- path
));
1533 parent
= bus_node_allocate(bus
, p
);
1538 n
= new0(struct node
, 1);
1543 n
->path
= TAKE_PTR(s
);
1545 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1552 LIST_PREPEND(siblings
, parent
->child
, n
);
1557 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1570 assert_se(hashmap_remove(b
->nodes
, n
->path
) == n
);
1573 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1576 bus_node_gc(b
, n
->parent
);
1580 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1586 n
= hashmap_get(bus
->nodes
, path
);
1588 _cleanup_free_
char *prefix
= NULL
;
1592 assert(pl
<= BUS_PATH_SIZE_MAX
);
1593 prefix
= new(char, pl
+ 1);
1597 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1598 n
= hashmap_get(bus
->nodes
, prefix
);
1604 while (n
&& !n
->object_managers
)
1612 static int bus_add_object(
1617 sd_bus_message_handler_t callback
,
1624 assert_return(bus
, -EINVAL
);
1625 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
1626 assert_return(object_path_is_valid(path
), -EINVAL
);
1627 assert_return(callback
, -EINVAL
);
1628 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1630 n
= bus_node_allocate(bus
, path
);
1634 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1640 s
->node_callback
.callback
= callback
;
1641 s
->node_callback
.is_fallback
= fallback
;
1643 s
->node_callback
.node
= n
;
1644 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1645 bus
->nodes_modified
= true;
1653 sd_bus_slot_unref(s
);
1654 bus_node_gc(bus
, n
);
1659 _public_
int sd_bus_add_object(
1663 sd_bus_message_handler_t callback
,
1666 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1669 _public_
int sd_bus_add_fallback(
1673 sd_bus_message_handler_t callback
,
1676 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1679 static void vtable_member_hash_func(const struct vtable_member
*m
, struct siphash
*state
) {
1682 string_hash_func(m
->path
, state
);
1683 string_hash_func(m
->interface
, state
);
1684 string_hash_func(m
->member
, state
);
1687 static int vtable_member_compare_func(const struct vtable_member
*x
, const struct vtable_member
*y
) {
1693 r
= strcmp(x
->path
, y
->path
);
1697 r
= strcmp(x
->interface
, y
->interface
);
1701 return strcmp(x
->member
, y
->member
);
1704 DEFINE_PRIVATE_HASH_OPS(vtable_member_hash_ops
, struct vtable_member
, vtable_member_hash_func
, vtable_member_compare_func
);
1707 NAMES_FIRST_PART
= 1 << 0, /* first part of argument name list (input names). It is reset by names_are_valid() */
1708 NAMES_PRESENT
= 1 << 1, /* at least one argument name is present, so the names will checked.
1709 This flag is set and used internally by names_are_valid(), but needs to be stored across calls for 2-parts list */
1710 NAMES_SINGLE_PART
= 1 << 2, /* argument name list consisting of a single part */
1713 static bool names_are_valid(const char *signature
, const char **names
, names_flags
*flags
) {
1716 if ((*flags
& NAMES_FIRST_PART
|| *flags
& NAMES_SINGLE_PART
) && **names
!= '\0')
1717 *flags
|= NAMES_PRESENT
;
1719 for (;*flags
& NAMES_PRESENT
;) {
1725 r
= signature_element_length(signature
, &l
);
1729 if (**names
!= '\0') {
1730 if (!member_name_is_valid(*names
))
1732 *names
+= strlen(*names
) + 1;
1733 } else if (*flags
& NAMES_PRESENT
)
1738 /* let's check if there are more argument names specified than the signature allows */
1739 if (*flags
& NAMES_PRESENT
&& **names
!= '\0' && !(*flags
& NAMES_FIRST_PART
))
1741 *flags
&= ~NAMES_FIRST_PART
;
1745 /* the current version of this struct is defined in sd-bus-vtable.h, but we need to list here the historical versions
1746 to make sure the calling code is compatible with one of these */
1747 struct sd_bus_vtable_221
{
1752 size_t element_size
;
1756 const char *signature
;
1758 sd_bus_message_handler_t handler
;
1763 const char *signature
;
1767 const char *signature
;
1768 sd_bus_property_get_t get
;
1769 sd_bus_property_set_t set
;
1774 /* Structure size up to v241 */
1775 #define VTABLE_ELEMENT_SIZE_221 sizeof(struct sd_bus_vtable_221)
1777 /* Size of the structure when "features" field was added. If the structure definition is augmented, a copy of
1778 * the structure definition will need to be made (similarly to the sd_bus_vtable_221 above), and this
1779 * definition updated to refer to it. */
1780 #define VTABLE_ELEMENT_SIZE_242 sizeof(struct sd_bus_vtable)
1782 static int vtable_features(const sd_bus_vtable
*vtable
) {
1783 if (vtable
[0].x
.start
.element_size
< VTABLE_ELEMENT_SIZE_242
||
1784 !vtable
[0].x
.start
.vtable_format_reference
)
1786 return vtable
[0].x
.start
.features
;
1789 bool bus_vtable_has_names(const sd_bus_vtable
*vtable
) {
1790 return vtable_features(vtable
) & _SD_BUS_VTABLE_PARAM_NAMES
;
1793 const sd_bus_vtable
* bus_vtable_next(const sd_bus_vtable
*vtable
, const sd_bus_vtable
*v
) {
1794 return (const sd_bus_vtable
*) ((char*) v
+ vtable
[0].x
.start
.element_size
);
1797 static int add_object_vtable_internal(
1801 const char *interface
,
1802 const sd_bus_vtable
*vtable
,
1804 sd_bus_object_find_t find
,
1807 sd_bus_slot
*s
= NULL
;
1808 struct node_vtable
*i
, *existing
= NULL
;
1809 const sd_bus_vtable
*v
;
1812 const char *names
= "";
1815 assert_return(bus
, -EINVAL
);
1816 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
1817 assert_return(object_path_is_valid(path
), -EINVAL
);
1818 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1819 assert_return(vtable
, -EINVAL
);
1820 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1821 assert_return(vtable
[0].x
.start
.element_size
== VTABLE_ELEMENT_SIZE_221
||
1822 vtable
[0].x
.start
.element_size
>= VTABLE_ELEMENT_SIZE_242
,
1824 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1825 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1826 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1827 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1828 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1830 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1834 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1838 n
= bus_node_allocate(bus
, path
);
1842 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1843 if (i
->is_fallback
!= fallback
) {
1848 if (streq(i
->interface
, interface
)) {
1850 if (i
->vtable
== vtable
) {
1859 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1865 s
->node_vtable
.is_fallback
= fallback
;
1866 s
->node_vtable
.vtable
= vtable
;
1867 s
->node_vtable
.find
= find
;
1869 s
->node_vtable
.interface
= strdup(interface
);
1870 if (!s
->node_vtable
.interface
) {
1875 v
= s
->node_vtable
.vtable
;
1876 for (v
= bus_vtable_next(vtable
, v
); v
->type
!= _SD_BUS_VTABLE_END
; v
= bus_vtable_next(vtable
, v
)) {
1880 case _SD_BUS_VTABLE_METHOD
: {
1881 struct vtable_member
*m
;
1882 nf
= NAMES_FIRST_PART
;
1884 if (bus_vtable_has_names(vtable
))
1885 names
= strempty(v
->x
.method
.names
);
1887 if (!member_name_is_valid(v
->x
.method
.member
) ||
1888 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1889 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1890 !names_are_valid(strempty(v
->x
.method
.signature
), &names
, &nf
) ||
1891 !names_are_valid(strempty(v
->x
.method
.result
), &names
, &nf
) ||
1892 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1893 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1898 m
= new0(struct vtable_member
, 1);
1904 m
->parent
= &s
->node_vtable
;
1906 m
->interface
= s
->node_vtable
.interface
;
1907 m
->member
= v
->x
.method
.member
;
1910 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1919 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1921 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1926 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1932 case _SD_BUS_VTABLE_PROPERTY
: {
1933 struct vtable_member
*m
;
1935 if (!member_name_is_valid(v
->x
.property
.member
) ||
1936 !signature_is_single(v
->x
.property
.signature
, false) ||
1937 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1938 (v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
) ||
1939 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1940 ((v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) && (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)) ||
1941 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1946 m
= new0(struct vtable_member
, 1);
1952 m
->parent
= &s
->node_vtable
;
1954 m
->interface
= s
->node_vtable
.interface
;
1955 m
->member
= v
->x
.property
.member
;
1958 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1967 case _SD_BUS_VTABLE_SIGNAL
:
1968 nf
= NAMES_SINGLE_PART
;
1970 if (bus_vtable_has_names(vtable
))
1971 names
= strempty(v
->x
.signal
.names
);
1973 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1974 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1975 !names_are_valid(strempty(v
->x
.signal
.signature
), &names
, &nf
) ||
1976 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1989 s
->node_vtable
.node
= n
;
1990 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1991 bus
->nodes_modified
= true;
1999 sd_bus_slot_unref(s
);
2000 bus_node_gc(bus
, n
);
2005 /* This symbol exists solely to tell the linker that the "new" vtable format is used. */
2006 _public_
const unsigned sd_bus_object_vtable_format
= 242;
2008 _public_
int sd_bus_add_object_vtable(
2012 const char *interface
,
2013 const sd_bus_vtable
*vtable
,
2016 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
2019 _public_
int sd_bus_add_fallback_vtable(
2023 const char *interface
,
2024 const sd_bus_vtable
*vtable
,
2025 sd_bus_object_find_t find
,
2028 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
2031 _public_
int sd_bus_add_node_enumerator(
2035 sd_bus_node_enumerator_t callback
,
2042 assert_return(bus
, -EINVAL
);
2043 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2044 assert_return(object_path_is_valid(path
), -EINVAL
);
2045 assert_return(callback
, -EINVAL
);
2046 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2048 n
= bus_node_allocate(bus
, path
);
2052 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
2058 s
->node_enumerator
.callback
= callback
;
2060 s
->node_enumerator
.node
= n
;
2061 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
2062 bus
->nodes_modified
= true;
2070 sd_bus_slot_unref(s
);
2071 bus_node_gc(bus
, n
);
2076 static int emit_properties_changed_on_interface(
2080 const char *interface
,
2081 bool require_fallback
,
2082 bool *found_interface
,
2085 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2086 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2087 bool has_invalidating
= false, has_changing
= false;
2088 struct vtable_member key
= {};
2089 struct node_vtable
*c
;
2099 assert(found_interface
);
2101 n
= hashmap_get(bus
->nodes
, prefix
);
2105 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
2109 r
= sd_bus_message_append(m
, "s", interface
);
2113 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2118 key
.interface
= interface
;
2120 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2121 if (require_fallback
&& !c
->is_fallback
)
2124 if (!streq(c
->interface
, interface
))
2127 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2130 if (bus
->nodes_modified
)
2135 *found_interface
= true;
2138 /* If the caller specified a list of
2139 * properties we include exactly those in the
2140 * PropertiesChanged message */
2142 STRV_FOREACH(property
, names
) {
2143 struct vtable_member
*v
;
2145 assert_return(member_name_is_valid(*property
), -EINVAL
);
2147 key
.member
= *property
;
2148 v
= hashmap_get(bus
->vtable_properties
, &key
);
2152 /* If there are two vtables for the same
2153 * interface, let's handle this property when
2154 * we come to that vtable. */
2158 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
2159 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
2161 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
2163 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
2164 has_invalidating
= true;
2168 has_changing
= true;
2170 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
2173 if (bus
->nodes_modified
)
2177 const sd_bus_vtable
*v
;
2179 /* If the caller specified no properties list
2180 * we include all properties that are marked
2181 * as changing in the message. */
2184 for (v
= bus_vtable_next(c
->vtable
, v
); v
->type
!= _SD_BUS_VTABLE_END
; v
= bus_vtable_next(c
->vtable
, v
)) {
2185 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
2188 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2191 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
2192 has_invalidating
= true;
2196 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
2199 has_changing
= true;
2201 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
2204 if (bus
->nodes_modified
)
2210 if (!has_invalidating
&& !has_changing
)
2213 r
= sd_bus_message_close_container(m
);
2217 r
= sd_bus_message_open_container(m
, 'a', "s");
2221 if (has_invalidating
) {
2222 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2223 if (require_fallback
&& !c
->is_fallback
)
2226 if (!streq(c
->interface
, interface
))
2229 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2232 if (bus
->nodes_modified
)
2238 STRV_FOREACH(property
, names
) {
2239 struct vtable_member
*v
;
2241 key
.member
= *property
;
2242 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2243 assert(c
== v
->parent
);
2245 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2248 r
= sd_bus_message_append(m
, "s", *property
);
2253 const sd_bus_vtable
*v
;
2256 for (v
= bus_vtable_next(c
->vtable
, v
); v
->type
!= _SD_BUS_VTABLE_END
; v
= bus_vtable_next(c
->vtable
, v
)) {
2257 if (!IN_SET(v
->type
, _SD_BUS_VTABLE_PROPERTY
, _SD_BUS_VTABLE_WRITABLE_PROPERTY
))
2260 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2263 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2266 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2274 r
= sd_bus_message_close_container(m
);
2278 r
= sd_bus_send(bus
, m
, NULL
);
2285 _public_
int sd_bus_emit_properties_changed_strv(
2288 const char *interface
,
2291 _cleanup_free_
char *prefix
= NULL
;
2292 bool found_interface
= false;
2296 assert_return(bus
, -EINVAL
);
2297 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2298 assert_return(object_path_is_valid(path
), -EINVAL
);
2299 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2300 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2302 if (!BUS_IS_OPEN(bus
->state
))
2305 /* A non-NULL but empty names list means nothing needs to be
2306 generated. A NULL list OTOH indicates that all properties
2307 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2308 included in the PropertiesChanged message. */
2309 if (names
&& names
[0] == NULL
)
2312 BUS_DONT_DESTROY(bus
);
2315 assert(pl
<= BUS_PATH_SIZE_MAX
);
2316 prefix
= new(char, pl
+ 1);
2321 bus
->nodes_modified
= false;
2323 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2326 if (bus
->nodes_modified
)
2329 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2330 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2333 if (bus
->nodes_modified
)
2337 } while (bus
->nodes_modified
);
2339 return found_interface
? 0 : -ENOENT
;
2342 _public_
int sd_bus_emit_properties_changed(
2345 const char *interface
,
2346 const char *name
, ...) {
2350 assert_return(bus
, -EINVAL
);
2351 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2352 assert_return(object_path_is_valid(path
), -EINVAL
);
2353 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2354 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2356 if (!BUS_IS_OPEN(bus
->state
))
2362 names
= strv_from_stdarg_alloca(name
);
2364 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2367 static int object_added_append_all_prefix(
2373 bool require_fallback
) {
2375 const char *previous_interface
= NULL
;
2376 struct node_vtable
*c
;
2386 n
= hashmap_get(bus
->nodes
, prefix
);
2390 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2391 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2394 if (require_fallback
&& !c
->is_fallback
)
2397 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2400 if (bus
->nodes_modified
)
2405 if (!streq_ptr(c
->interface
, previous_interface
)) {
2406 /* If a child-node already handled this interface, we
2407 * skip it on any of its parents. The child vtables
2408 * always fully override any conflicting vtables of
2409 * any parent node. */
2410 if (set_get(s
, c
->interface
))
2413 r
= set_put(s
, c
->interface
);
2417 if (previous_interface
) {
2418 r
= sd_bus_message_close_container(m
);
2421 r
= sd_bus_message_close_container(m
);
2426 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2429 r
= sd_bus_message_append(m
, "s", c
->interface
);
2432 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2436 previous_interface
= c
->interface
;
2439 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2442 if (bus
->nodes_modified
)
2446 if (previous_interface
) {
2447 r
= sd_bus_message_close_container(m
);
2450 r
= sd_bus_message_close_container(m
);
2458 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2459 _cleanup_set_free_ Set
*s
= NULL
;
2460 _cleanup_free_
char *prefix
= NULL
;
2469 * This appends all interfaces registered on path @path. We first add
2470 * the builtin interfaces, which are always available and handled by
2471 * sd-bus. Then, we add all interfaces registered on the exact node,
2472 * followed by all fallback interfaces registered on any parent prefix.
2474 * If an interface is registered multiple times on the same node with
2475 * different vtables, we merge all the properties across all vtables.
2476 * However, if a child node has the same interface registered as one of
2477 * its parent nodes has as fallback, we make the child overwrite the
2478 * parent instead of extending it. Therefore, we keep a "Set" of all
2479 * handled interfaces during parent traversal, so we skip interfaces on
2480 * a parent that were overwritten by a child.
2483 s
= set_new(&string_hash_ops
);
2487 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2490 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2493 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2496 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2500 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2503 if (bus
->nodes_modified
)
2507 assert(pl
<= BUS_PATH_SIZE_MAX
);
2508 prefix
= new(char, pl
+ 1);
2512 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2513 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2516 if (bus
->nodes_modified
)
2523 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2524 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2525 struct node
*object_manager
;
2529 * This emits an InterfacesAdded signal on the given path, by iterating
2530 * all registered vtables and fallback vtables on the path. All
2531 * properties are queried and included in the signal.
2532 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2533 * explicit list of registered interfaces. However, unlike
2534 * interfaces_added(), this call can figure out the list of supported
2535 * interfaces itself. Furthermore, it properly adds the builtin
2536 * org.freedesktop.DBus.* interfaces.
2539 assert_return(bus
, -EINVAL
);
2540 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2541 assert_return(object_path_is_valid(path
), -EINVAL
);
2542 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2544 if (!BUS_IS_OPEN(bus
->state
))
2547 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2553 BUS_DONT_DESTROY(bus
);
2556 bus
->nodes_modified
= false;
2557 m
= sd_bus_message_unref(m
);
2559 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2563 r
= sd_bus_message_append_basic(m
, 'o', path
);
2567 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2571 r
= object_added_append_all(bus
, m
, path
);
2575 if (bus
->nodes_modified
)
2578 r
= sd_bus_message_close_container(m
);
2582 } while (bus
->nodes_modified
);
2584 return sd_bus_send(bus
, m
, NULL
);
2587 static int object_removed_append_all_prefix(
2593 bool require_fallback
) {
2595 const char *previous_interface
= NULL
;
2596 struct node_vtable
*c
;
2606 n
= hashmap_get(bus
->nodes
, prefix
);
2610 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2611 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2614 if (require_fallback
&& !c
->is_fallback
)
2616 if (streq_ptr(c
->interface
, previous_interface
))
2619 /* If a child-node already handled this interface, we
2620 * skip it on any of its parents. The child vtables
2621 * always fully override any conflicting vtables of
2622 * any parent node. */
2623 if (set_get(s
, c
->interface
))
2626 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2629 if (bus
->nodes_modified
)
2634 r
= set_put(s
, c
->interface
);
2638 r
= sd_bus_message_append(m
, "s", c
->interface
);
2642 previous_interface
= c
->interface
;
2648 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2649 _cleanup_set_free_ Set
*s
= NULL
;
2650 _cleanup_free_
char *prefix
= NULL
;
2658 /* see sd_bus_emit_object_added() for details */
2660 s
= set_new(&string_hash_ops
);
2664 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2667 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2670 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2673 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2677 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2680 if (bus
->nodes_modified
)
2684 assert(pl
<= BUS_PATH_SIZE_MAX
);
2685 prefix
= new(char, pl
+ 1);
2689 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2690 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2693 if (bus
->nodes_modified
)
2700 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2701 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2702 struct node
*object_manager
;
2706 * This is like sd_bus_emit_object_added(), but emits an
2707 * InterfacesRemoved signal on the given path. This only includes any
2708 * registered interfaces but skips the properties. Note that this will
2709 * call into the find() callbacks of any registered vtable. Therefore,
2710 * you must call this function before destroying/unlinking your object.
2711 * Otherwise, the list of interfaces will be incomplete. However, note
2712 * that this will *NOT* call into any property callback. Therefore, the
2713 * object might be in an "destructed" state, as long as we can find it.
2716 assert_return(bus
, -EINVAL
);
2717 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2718 assert_return(object_path_is_valid(path
), -EINVAL
);
2719 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2721 if (!BUS_IS_OPEN(bus
->state
))
2724 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2730 BUS_DONT_DESTROY(bus
);
2733 bus
->nodes_modified
= false;
2734 m
= sd_bus_message_unref(m
);
2736 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2740 r
= sd_bus_message_append_basic(m
, 'o', path
);
2744 r
= sd_bus_message_open_container(m
, 'a', "s");
2748 r
= object_removed_append_all(bus
, m
, path
);
2752 if (bus
->nodes_modified
)
2755 r
= sd_bus_message_close_container(m
);
2759 } while (bus
->nodes_modified
);
2761 return sd_bus_send(bus
, m
, NULL
);
2764 static int interfaces_added_append_one_prefix(
2769 const char *interface
,
2770 bool require_fallback
) {
2772 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
2773 bool found_interface
= false;
2774 struct node_vtable
*c
;
2785 n
= hashmap_get(bus
->nodes
, prefix
);
2789 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2790 if (require_fallback
&& !c
->is_fallback
)
2793 if (!streq(c
->interface
, interface
))
2796 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2799 if (bus
->nodes_modified
)
2804 if (!found_interface
) {
2805 r
= sd_bus_message_append_basic(m
, 's', interface
);
2809 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2813 found_interface
= true;
2816 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2819 if (bus
->nodes_modified
)
2823 if (found_interface
) {
2824 r
= sd_bus_message_close_container(m
);
2829 return found_interface
;
2832 static int interfaces_added_append_one(
2836 const char *interface
) {
2838 _cleanup_free_
char *prefix
= NULL
;
2847 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2850 if (bus
->nodes_modified
)
2854 assert(pl
<= BUS_PATH_SIZE_MAX
);
2855 prefix
= new(char, pl
+ 1);
2859 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2860 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2863 if (bus
->nodes_modified
)
2870 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2871 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2872 struct node
*object_manager
;
2876 assert_return(bus
, -EINVAL
);
2877 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2878 assert_return(object_path_is_valid(path
), -EINVAL
);
2879 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2881 if (!BUS_IS_OPEN(bus
->state
))
2884 if (strv_isempty(interfaces
))
2887 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2893 BUS_DONT_DESTROY(bus
);
2896 bus
->nodes_modified
= false;
2897 m
= sd_bus_message_unref(m
);
2899 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2903 r
= sd_bus_message_append_basic(m
, 'o', path
);
2907 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2911 STRV_FOREACH(i
, interfaces
) {
2912 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2914 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2918 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2922 if (bus
->nodes_modified
)
2925 r
= sd_bus_message_close_container(m
);
2930 if (bus
->nodes_modified
)
2933 r
= sd_bus_message_close_container(m
);
2937 } while (bus
->nodes_modified
);
2939 return sd_bus_send(bus
, m
, NULL
);
2942 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2945 assert_return(bus
, -EINVAL
);
2946 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2947 assert_return(object_path_is_valid(path
), -EINVAL
);
2948 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2950 if (!BUS_IS_OPEN(bus
->state
))
2953 interfaces
= strv_from_stdarg_alloca(interface
);
2955 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2958 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2959 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*m
= NULL
;
2960 struct node
*object_manager
;
2963 assert_return(bus
, -EINVAL
);
2964 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
2965 assert_return(object_path_is_valid(path
), -EINVAL
);
2966 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2968 if (!BUS_IS_OPEN(bus
->state
))
2971 if (strv_isempty(interfaces
))
2974 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2980 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2984 r
= sd_bus_message_append_basic(m
, 'o', path
);
2988 r
= sd_bus_message_append_strv(m
, interfaces
);
2992 return sd_bus_send(bus
, m
, NULL
);
2995 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2998 assert_return(bus
, -EINVAL
);
2999 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
3000 assert_return(object_path_is_valid(path
), -EINVAL
);
3001 assert_return(!bus_pid_changed(bus
), -ECHILD
);
3003 if (!BUS_IS_OPEN(bus
->state
))
3006 interfaces
= strv_from_stdarg_alloca(interface
);
3008 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
3011 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
3016 assert_return(bus
, -EINVAL
);
3017 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
3018 assert_return(object_path_is_valid(path
), -EINVAL
);
3019 assert_return(!bus_pid_changed(bus
), -ECHILD
);
3021 n
= bus_node_allocate(bus
, path
);
3025 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
3031 s
->node_object_manager
.node
= n
;
3032 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
3033 bus
->nodes_modified
= true;
3041 sd_bus_slot_unref(s
);
3042 bus_node_gc(bus
, n
);