1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "bus-internal.h"
23 #include "bus-introspect.h"
24 #include "bus-message.h"
25 #include "bus-signature.h"
30 #include "string-util.h"
32 #include "bus-objects.h"
34 static int node_vtable_get_userdata(
37 struct node_vtable
*c
,
39 sd_bus_error
*error
) {
49 s
= container_of(c
, sd_bus_slot
, node_vtable
);
52 bus
->current_slot
= sd_bus_slot_ref(s
);
53 bus
->current_userdata
= u
;
54 r
= c
->find(bus
, path
, c
->interface
, u
, &u
, error
);
55 bus
->current_userdata
= NULL
;
56 bus
->current_slot
= sd_bus_slot_unref(s
);
60 if (sd_bus_error_is_set(error
))
61 return -sd_bus_error_get_errno(error
);
72 static void *vtable_method_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
75 return (uint8_t*) u
+ p
->x
.method
.offset
;
78 static void *vtable_property_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
81 return (uint8_t*) u
+ p
->x
.property
.offset
;
84 static int vtable_property_get_userdata(
87 struct vtable_member
*p
,
89 sd_bus_error
*error
) {
99 r
= node_vtable_get_userdata(bus
, path
, p
->parent
, &u
, error
);
102 if (bus
->nodes_modified
)
105 *userdata
= vtable_property_convert_userdata(p
->vtable
, u
);
109 static int add_enumerated_to_set(
112 struct node_enumerator
*first
,
114 sd_bus_error
*error
) {
116 struct node_enumerator
*c
;
123 LIST_FOREACH(enumerators
, c
, first
) {
124 char **children
= NULL
, **k
;
127 if (bus
->nodes_modified
)
130 slot
= container_of(c
, sd_bus_slot
, node_enumerator
);
132 bus
->current_slot
= sd_bus_slot_ref(slot
);
133 bus
->current_userdata
= slot
->userdata
;
134 r
= c
->callback(bus
, prefix
, slot
->userdata
, &children
, error
);
135 bus
->current_userdata
= NULL
;
136 bus
->current_slot
= sd_bus_slot_unref(slot
);
140 if (sd_bus_error_is_set(error
))
141 return -sd_bus_error_get_errno(error
);
143 STRV_FOREACH(k
, children
) {
149 if (!object_path_is_valid(*k
)){
155 if (!object_path_startswith(*k
, prefix
)) {
160 r
= set_consume(s
, *k
);
174 /* if set, add_subtree() works recursively */
175 CHILDREN_RECURSIVE
= (1U << 1),
176 /* if set, add_subtree() scans object-manager hierarchies recursively */
177 CHILDREN_SUBHIERARCHIES
= (1U << 0),
180 static int add_subtree_to_set(
186 sd_bus_error
*error
) {
196 r
= add_enumerated_to_set(bus
, prefix
, n
->enumerators
, s
, error
);
199 if (bus
->nodes_modified
)
202 LIST_FOREACH(siblings
, i
, n
->child
) {
205 if (!object_path_startswith(i
->path
, prefix
))
212 r
= set_consume(s
, t
);
213 if (r
< 0 && r
!= -EEXIST
)
216 if ((flags
& CHILDREN_RECURSIVE
) &&
217 ((flags
& CHILDREN_SUBHIERARCHIES
) || !i
->object_managers
)) {
218 r
= add_subtree_to_set(bus
, prefix
, i
, flags
, s
, error
);
221 if (bus
->nodes_modified
)
229 static int get_child_nodes(
235 sd_bus_error
*error
) {
245 s
= set_new(&string_hash_ops
);
249 r
= add_subtree_to_set(bus
, prefix
, n
, flags
, s
, error
);
259 static int node_callbacks_run(
262 struct node_callback
*first
,
263 bool require_fallback
,
264 bool *found_object
) {
266 struct node_callback
*c
;
271 assert(found_object
);
273 LIST_FOREACH(callbacks
, c
, first
) {
274 _cleanup_bus_error_free_ sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
277 if (bus
->nodes_modified
)
280 if (require_fallback
&& !c
->is_fallback
)
283 *found_object
= true;
285 if (c
->last_iteration
== bus
->iteration_counter
)
288 c
->last_iteration
= bus
->iteration_counter
;
290 r
= sd_bus_message_rewind(m
, true);
294 slot
= container_of(c
, sd_bus_slot
, node_callback
);
296 bus
->current_slot
= sd_bus_slot_ref(slot
);
297 bus
->current_handler
= c
->callback
;
298 bus
->current_userdata
= slot
->userdata
;
299 r
= c
->callback(m
, slot
->userdata
, &error_buffer
);
300 bus
->current_userdata
= NULL
;
301 bus
->current_handler
= NULL
;
302 bus
->current_slot
= sd_bus_slot_unref(slot
);
304 r
= bus_maybe_reply_error(m
, r
, &error_buffer
);
312 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
314 static int check_access(sd_bus
*bus
, sd_bus_message
*m
, struct vtable_member
*c
, sd_bus_error
*error
) {
322 /* If the entire bus is trusted let's grant access */
326 /* If the member is marked UNPRIVILEGED let's grant access */
327 if (c
->vtable
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
)
330 /* Check have the caller has the requested capability
331 * set. Note that the flags value contains the capability
332 * number plus one, which we need to subtract here. We do this
333 * so that we have 0 as special value for "default
335 cap
= CAPABILITY_SHIFT(c
->vtable
->flags
);
337 cap
= CAPABILITY_SHIFT(c
->parent
->vtable
[0].flags
);
343 r
= sd_bus_query_sender_privilege(m
, cap
);
349 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Access to %s.%s() not permitted.", c
->interface
, c
->member
);
352 static int method_callbacks_run(
355 struct vtable_member
*c
,
356 bool require_fallback
,
357 bool *found_object
) {
359 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
360 const char *signature
;
367 assert(found_object
);
369 if (require_fallback
&& !c
->parent
->is_fallback
)
372 r
= check_access(bus
, m
, c
, &error
);
374 return bus_maybe_reply_error(m
, r
, &error
);
376 r
= node_vtable_get_userdata(bus
, m
->path
, c
->parent
, &u
, &error
);
378 return bus_maybe_reply_error(m
, r
, &error
);
379 if (bus
->nodes_modified
)
382 u
= vtable_method_convert_userdata(c
->vtable
, u
);
384 *found_object
= true;
386 if (c
->last_iteration
== bus
->iteration_counter
)
389 c
->last_iteration
= bus
->iteration_counter
;
391 r
= sd_bus_message_rewind(m
, true);
395 signature
= sd_bus_message_get_signature(m
, true);
399 if (!streq(strempty(c
->vtable
->x
.method
.signature
), signature
))
400 return sd_bus_reply_method_errorf(
402 SD_BUS_ERROR_INVALID_ARGS
,
403 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
404 signature
, c
->interface
, c
->member
, strempty(c
->vtable
->x
.method
.signature
));
406 /* Keep track what the signature of the reply to this message
407 * should be, so that this can be enforced when sealing the
409 m
->enforced_reply_signature
= strempty(c
->vtable
->x
.method
.result
);
411 if (c
->vtable
->x
.method
.handler
) {
414 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
416 bus
->current_slot
= sd_bus_slot_ref(slot
);
417 bus
->current_handler
= c
->vtable
->x
.method
.handler
;
418 bus
->current_userdata
= u
;
419 r
= c
->vtable
->x
.method
.handler(m
, u
, &error
);
420 bus
->current_userdata
= NULL
;
421 bus
->current_handler
= NULL
;
422 bus
->current_slot
= sd_bus_slot_unref(slot
);
424 return bus_maybe_reply_error(m
, r
, &error
);
427 /* If the method callback is NULL, make this a successful NOP */
428 r
= sd_bus_reply_method_return(m
, NULL
);
435 static int invoke_property_get(
438 const sd_bus_vtable
*v
,
440 const char *interface
,
441 const char *property
,
442 sd_bus_message
*reply
,
444 sd_bus_error
*error
) {
457 if (v
->x
.property
.get
) {
459 bus
->current_slot
= sd_bus_slot_ref(slot
);
460 bus
->current_userdata
= userdata
;
461 r
= v
->x
.property
.get(bus
, path
, interface
, property
, reply
, userdata
, error
);
462 bus
->current_userdata
= NULL
;
463 bus
->current_slot
= sd_bus_slot_unref(slot
);
467 if (sd_bus_error_is_set(error
))
468 return -sd_bus_error_get_errno(error
);
472 /* Automatic handling if no callback is defined. */
474 if (streq(v
->x
.property
.signature
, "as"))
475 return sd_bus_message_append_strv(reply
, *(char***) userdata
);
477 assert(signature_is_single(v
->x
.property
.signature
, false));
478 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
480 switch (v
->x
.property
.signature
[0]) {
482 case SD_BUS_TYPE_STRING
:
483 case SD_BUS_TYPE_SIGNATURE
:
484 p
= strempty(*(char**) userdata
);
487 case SD_BUS_TYPE_OBJECT_PATH
:
488 p
= *(char**) userdata
;
497 return sd_bus_message_append_basic(reply
, v
->x
.property
.signature
[0], p
);
500 static int invoke_property_set(
503 const sd_bus_vtable
*v
,
505 const char *interface
,
506 const char *property
,
507 sd_bus_message
*value
,
509 sd_bus_error
*error
) {
521 if (v
->x
.property
.set
) {
523 bus
->current_slot
= sd_bus_slot_ref(slot
);
524 bus
->current_userdata
= userdata
;
525 r
= v
->x
.property
.set(bus
, path
, interface
, property
, value
, userdata
, error
);
526 bus
->current_userdata
= NULL
;
527 bus
->current_slot
= sd_bus_slot_unref(slot
);
531 if (sd_bus_error_is_set(error
))
532 return -sd_bus_error_get_errno(error
);
536 /* Automatic handling if no callback is defined. */
538 assert(signature_is_single(v
->x
.property
.signature
, false));
539 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
541 switch (v
->x
.property
.signature
[0]) {
543 case SD_BUS_TYPE_STRING
:
544 case SD_BUS_TYPE_OBJECT_PATH
:
545 case SD_BUS_TYPE_SIGNATURE
: {
549 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], &p
);
557 free(*(char**) userdata
);
558 *(char**) userdata
= n
;
564 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], userdata
);
574 static int property_get_set_callbacks_run(
577 struct vtable_member
*c
,
578 bool require_fallback
,
580 bool *found_object
) {
582 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
583 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
591 assert(found_object
);
593 if (require_fallback
&& !c
->parent
->is_fallback
)
596 r
= vtable_property_get_userdata(bus
, m
->path
, c
, &u
, &error
);
598 return bus_maybe_reply_error(m
, r
, &error
);
599 if (bus
->nodes_modified
)
602 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
604 *found_object
= true;
606 r
= sd_bus_message_new_method_return(m
, &reply
);
611 /* Note that we do not protect against reexecution
612 * here (using the last_iteration check, see below),
613 * should the node tree have changed and we got called
614 * again. We assume that property Get() calls are
615 * ultimately without side-effects or if they aren't
616 * then at least idempotent. */
618 r
= sd_bus_message_open_container(reply
, 'v', c
->vtable
->x
.property
.signature
);
622 /* Note that we do not do an access check here. Read
623 * access to properties is always unrestricted, since
624 * PropertiesChanged signals broadcast contents
627 r
= invoke_property_get(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, reply
, u
, &error
);
629 return bus_maybe_reply_error(m
, r
, &error
);
631 if (bus
->nodes_modified
)
634 r
= sd_bus_message_close_container(reply
);
639 const char *signature
= NULL
;
642 if (c
->vtable
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
643 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_PROPERTY_READ_ONLY
, "Property '%s' is not writable.", c
->member
);
645 /* Avoid that we call the set routine more than once
646 * if the processing of this message got restarted
647 * because the node tree changed. */
648 if (c
->last_iteration
== bus
->iteration_counter
)
651 c
->last_iteration
= bus
->iteration_counter
;
653 r
= sd_bus_message_peek_type(m
, &type
, &signature
);
657 if (type
!= 'v' || !streq(strempty(signature
), strempty(c
->vtable
->x
.property
.signature
)))
658 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c
->member
, strempty(c
->vtable
->x
.property
.signature
), strempty(signature
));
660 r
= sd_bus_message_enter_container(m
, 'v', c
->vtable
->x
.property
.signature
);
664 r
= check_access(bus
, m
, c
, &error
);
666 return bus_maybe_reply_error(m
, r
, &error
);
668 r
= invoke_property_set(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, m
, u
, &error
);
670 return bus_maybe_reply_error(m
, r
, &error
);
672 if (bus
->nodes_modified
)
675 r
= sd_bus_message_exit_container(m
);
680 r
= sd_bus_send(bus
, reply
, NULL
);
687 static int vtable_append_one_property(
689 sd_bus_message
*reply
,
691 struct node_vtable
*c
,
692 const sd_bus_vtable
*v
,
694 sd_bus_error
*error
) {
705 r
= sd_bus_message_open_container(reply
, 'e', "sv");
709 r
= sd_bus_message_append(reply
, "s", v
->x
.property
.member
);
713 r
= sd_bus_message_open_container(reply
, 'v', v
->x
.property
.signature
);
717 slot
= container_of(c
, sd_bus_slot
, node_vtable
);
719 r
= invoke_property_get(bus
, slot
, v
, path
, c
->interface
, v
->x
.property
.member
, reply
, vtable_property_convert_userdata(v
, userdata
), error
);
722 if (bus
->nodes_modified
)
725 r
= sd_bus_message_close_container(reply
);
729 r
= sd_bus_message_close_container(reply
);
736 static int vtable_append_all_properties(
738 sd_bus_message
*reply
,
740 struct node_vtable
*c
,
742 sd_bus_error
*error
) {
744 const sd_bus_vtable
*v
;
752 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
755 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
756 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
759 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
762 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)
765 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
768 if (bus
->nodes_modified
)
775 static int property_get_all_callbacks_run(
778 struct node_vtable
*first
,
779 bool require_fallback
,
781 bool *found_object
) {
783 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
784 struct node_vtable
*c
;
785 bool found_interface
;
790 assert(found_object
);
792 r
= sd_bus_message_new_method_return(m
, &reply
);
796 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
800 found_interface
= !iface
||
801 streq(iface
, "org.freedesktop.DBus.Properties") ||
802 streq(iface
, "org.freedesktop.DBus.Peer") ||
803 streq(iface
, "org.freedesktop.DBus.Introspectable");
805 LIST_FOREACH(vtables
, c
, first
) {
806 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
809 if (require_fallback
&& !c
->is_fallback
)
812 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
814 return bus_maybe_reply_error(m
, r
, &error
);
815 if (bus
->nodes_modified
)
820 *found_object
= true;
822 if (iface
&& !streq(c
->interface
, iface
))
824 found_interface
= true;
826 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
828 return bus_maybe_reply_error(m
, r
, &error
);
829 if (bus
->nodes_modified
)
833 if (!found_interface
) {
834 r
= sd_bus_reply_method_errorf(
836 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
837 "Unknown interface '%s'.", iface
);
844 r
= sd_bus_message_close_container(reply
);
848 r
= sd_bus_send(bus
, reply
, NULL
);
855 static int bus_node_exists(
859 bool require_fallback
) {
861 struct node_vtable
*c
;
862 struct node_callback
*k
;
869 /* Tests if there's anything attached directly to this node
870 * for the specified path */
872 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
875 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
876 if (require_fallback
&& !k
->is_fallback
)
882 LIST_FOREACH(vtables
, c
, n
->vtables
) {
883 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
885 if (require_fallback
&& !c
->is_fallback
)
888 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
891 if (bus
->nodes_modified
)
898 static int process_introspect(
902 bool require_fallback
,
903 bool *found_object
) {
905 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
906 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
907 _cleanup_set_free_free_ Set
*s
= NULL
;
908 const char *previous_interface
= NULL
;
909 struct introspect intro
;
910 struct node_vtable
*c
;
917 assert(found_object
);
919 r
= get_child_nodes(bus
, m
->path
, n
, 0, &s
, &error
);
921 return bus_maybe_reply_error(m
, r
, &error
);
922 if (bus
->nodes_modified
)
925 r
= introspect_begin(&intro
, bus
->trusted
);
929 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
933 empty
= set_isempty(s
);
935 LIST_FOREACH(vtables
, c
, n
->vtables
) {
936 if (require_fallback
&& !c
->is_fallback
)
939 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
941 r
= bus_maybe_reply_error(m
, r
, &error
);
944 if (bus
->nodes_modified
) {
953 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
956 if (!streq_ptr(previous_interface
, c
->interface
)) {
958 if (previous_interface
)
959 fputs(" </interface>\n", intro
.f
);
961 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
964 r
= introspect_write_interface(&intro
, c
->vtable
);
968 previous_interface
= c
->interface
;
971 if (previous_interface
)
972 fputs(" </interface>\n", intro
.f
);
975 /* Nothing?, let's see if we exist at all, and if not
976 * refuse to do anything */
977 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
980 if (bus
->nodes_modified
) {
986 *found_object
= true;
988 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
992 r
= introspect_finish(&intro
, bus
, m
, &reply
);
996 r
= sd_bus_send(bus
, reply
, NULL
);
1003 introspect_free(&intro
);
1007 static int object_manager_serialize_path(
1009 sd_bus_message
*reply
,
1012 bool require_fallback
,
1013 sd_bus_error
*error
) {
1015 const char *previous_interface
= NULL
;
1016 bool found_something
= false;
1017 struct node_vtable
*i
;
1027 n
= hashmap_get(bus
->nodes
, prefix
);
1031 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1034 if (require_fallback
&& !i
->is_fallback
)
1037 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1040 if (bus
->nodes_modified
)
1045 if (!found_something
) {
1047 /* Open the object part */
1049 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1053 r
= sd_bus_message_append(reply
, "o", path
);
1057 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1061 found_something
= true;
1064 if (!streq_ptr(previous_interface
, i
->interface
)) {
1066 /* Maybe close the previous interface part */
1068 if (previous_interface
) {
1069 r
= sd_bus_message_close_container(reply
);
1073 r
= sd_bus_message_close_container(reply
);
1078 /* Open the new interface part */
1080 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1084 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1088 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1093 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1096 if (bus
->nodes_modified
)
1099 previous_interface
= i
->interface
;
1102 if (previous_interface
) {
1103 r
= sd_bus_message_close_container(reply
);
1107 r
= sd_bus_message_close_container(reply
);
1112 if (found_something
) {
1113 r
= sd_bus_message_close_container(reply
);
1117 r
= sd_bus_message_close_container(reply
);
1125 static int object_manager_serialize_path_and_fallbacks(
1127 sd_bus_message
*reply
,
1129 sd_bus_error
*error
) {
1139 /* First, add all vtables registered for this path */
1140 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1143 if (bus
->nodes_modified
)
1146 /* Second, add fallback vtables registered for any of the prefixes */
1147 prefix
= alloca(strlen(path
) + 1);
1148 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1149 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1152 if (bus
->nodes_modified
)
1159 static int process_get_managed_objects(
1163 bool require_fallback
,
1164 bool *found_object
) {
1166 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1167 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1168 _cleanup_set_free_free_ Set
*s
= NULL
;
1176 assert(found_object
);
1178 /* Spec says, GetManagedObjects() is only implemented on the root of a
1179 * sub-tree. Therefore, we require a registered object-manager on
1180 * exactly the queried path, otherwise, we refuse to respond. */
1182 if (require_fallback
|| !n
->object_managers
)
1185 r
= get_child_nodes(bus
, m
->path
, n
, CHILDREN_RECURSIVE
, &s
, &error
);
1188 if (bus
->nodes_modified
)
1191 r
= sd_bus_message_new_method_return(m
, &reply
);
1195 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1199 SET_FOREACH(path
, s
, i
) {
1200 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1204 if (bus
->nodes_modified
)
1208 r
= sd_bus_message_close_container(reply
);
1212 r
= sd_bus_send(bus
, reply
, NULL
);
1219 static int object_find_and_run(
1223 bool require_fallback
,
1224 bool *found_object
) {
1227 struct vtable_member vtable_key
, *v
;
1233 assert(found_object
);
1235 n
= hashmap_get(bus
->nodes
, p
);
1239 /* First, try object callbacks */
1240 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1243 if (bus
->nodes_modified
)
1246 if (!m
->interface
|| !m
->member
)
1249 /* Then, look for a known method */
1250 vtable_key
.path
= (char*) p
;
1251 vtable_key
.interface
= m
->interface
;
1252 vtable_key
.member
= m
->member
;
1254 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1256 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1259 if (bus
->nodes_modified
)
1263 /* Then, look for a known property */
1264 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1267 get
= streq(m
->member
, "Get");
1269 if (get
|| streq(m
->member
, "Set")) {
1271 r
= sd_bus_message_rewind(m
, true);
1275 vtable_key
.path
= (char*) p
;
1277 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1279 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1281 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1283 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1288 } else if (streq(m
->member
, "GetAll")) {
1291 r
= sd_bus_message_rewind(m
, true);
1295 r
= sd_bus_message_read(m
, "s", &iface
);
1297 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1302 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1307 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1309 if (!isempty(sd_bus_message_get_signature(m
, true)))
1310 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1312 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1316 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1318 if (!isempty(sd_bus_message_get_signature(m
, true)))
1319 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1321 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1326 if (bus
->nodes_modified
)
1329 if (!*found_object
) {
1330 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1333 if (bus
->nodes_modified
)
1336 *found_object
= true;
1342 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1345 bool found_object
= false;
1350 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1353 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1356 if (hashmap_isempty(bus
->nodes
))
1359 /* Never respond to broadcast messages */
1360 if (bus
->bus_client
&& !m
->destination
)
1366 pl
= strlen(m
->path
);
1370 bus
->nodes_modified
= false;
1372 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1376 /* Look for fallback prefixes */
1377 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1379 if (bus
->nodes_modified
)
1382 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1387 } while (bus
->nodes_modified
);
1392 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1393 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1394 r
= sd_bus_reply_method_errorf(
1396 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1397 "Unknown property or interface.");
1399 r
= sd_bus_reply_method_errorf(
1401 SD_BUS_ERROR_UNKNOWN_METHOD
,
1402 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1410 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1411 struct node
*n
, *parent
;
1413 _cleanup_free_
char *s
= NULL
;
1419 assert(path
[0] == '/');
1421 n
= hashmap_get(bus
->nodes
, path
);
1425 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1433 if (streq(path
, "/"))
1436 e
= strrchr(path
, '/');
1439 p
= strndupa(path
, MAX(1, e
- path
));
1441 parent
= bus_node_allocate(bus
, p
);
1446 n
= new0(struct node
, 1);
1452 s
= NULL
; /* do not free */
1454 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1462 LIST_PREPEND(siblings
, parent
->child
, n
);
1467 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1480 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1483 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1486 bus_node_gc(b
, n
->parent
);
1490 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1496 n
= hashmap_get(bus
->nodes
, path
);
1500 prefix
= alloca(strlen(path
) + 1);
1501 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1502 n
= hashmap_get(bus
->nodes
, prefix
);
1508 while (n
&& !n
->object_managers
)
1516 static int bus_add_object(
1521 sd_bus_message_handler_t callback
,
1528 assert_return(bus
, -EINVAL
);
1529 assert_return(object_path_is_valid(path
), -EINVAL
);
1530 assert_return(callback
, -EINVAL
);
1531 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1533 n
= bus_node_allocate(bus
, path
);
1537 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1543 s
->node_callback
.callback
= callback
;
1544 s
->node_callback
.is_fallback
= fallback
;
1546 s
->node_callback
.node
= n
;
1547 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1548 bus
->nodes_modified
= true;
1556 sd_bus_slot_unref(s
);
1557 bus_node_gc(bus
, n
);
1562 _public_
int sd_bus_add_object(
1566 sd_bus_message_handler_t callback
,
1569 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1572 _public_
int sd_bus_add_fallback(
1576 sd_bus_message_handler_t callback
,
1579 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1582 static void vtable_member_hash_func(const void *a
, struct siphash
*state
) {
1583 const struct vtable_member
*m
= a
;
1587 string_hash_func(m
->path
, state
);
1588 string_hash_func(m
->interface
, state
);
1589 string_hash_func(m
->member
, state
);
1592 static int vtable_member_compare_func(const void *a
, const void *b
) {
1593 const struct vtable_member
*x
= a
, *y
= b
;
1599 r
= strcmp(x
->path
, y
->path
);
1603 r
= strcmp(x
->interface
, y
->interface
);
1607 return strcmp(x
->member
, y
->member
);
1610 static const struct hash_ops vtable_member_hash_ops
= {
1611 .hash
= vtable_member_hash_func
,
1612 .compare
= vtable_member_compare_func
1615 static int add_object_vtable_internal(
1619 const char *interface
,
1620 const sd_bus_vtable
*vtable
,
1622 sd_bus_object_find_t find
,
1625 sd_bus_slot
*s
= NULL
;
1626 struct node_vtable
*i
, *existing
= NULL
;
1627 const sd_bus_vtable
*v
;
1631 assert_return(bus
, -EINVAL
);
1632 assert_return(object_path_is_valid(path
), -EINVAL
);
1633 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1634 assert_return(vtable
, -EINVAL
);
1635 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1636 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1637 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1638 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1639 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1640 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1641 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1643 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1647 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1651 n
= bus_node_allocate(bus
, path
);
1655 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1656 if (i
->is_fallback
!= fallback
) {
1661 if (streq(i
->interface
, interface
)) {
1663 if (i
->vtable
== vtable
) {
1672 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1678 s
->node_vtable
.is_fallback
= fallback
;
1679 s
->node_vtable
.vtable
= vtable
;
1680 s
->node_vtable
.find
= find
;
1682 s
->node_vtable
.interface
= strdup(interface
);
1683 if (!s
->node_vtable
.interface
) {
1688 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1692 case _SD_BUS_VTABLE_METHOD
: {
1693 struct vtable_member
*m
;
1695 if (!member_name_is_valid(v
->x
.method
.member
) ||
1696 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1697 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1698 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1699 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1704 m
= new0(struct vtable_member
, 1);
1710 m
->parent
= &s
->node_vtable
;
1712 m
->interface
= s
->node_vtable
.interface
;
1713 m
->member
= v
->x
.method
.member
;
1716 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1725 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1727 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1732 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1739 case _SD_BUS_VTABLE_PROPERTY
: {
1740 struct vtable_member
*m
;
1742 if (!member_name_is_valid(v
->x
.property
.member
) ||
1743 !signature_is_single(v
->x
.property
.signature
, false) ||
1744 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1745 (v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
) ||
1746 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1747 ((v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) && (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)) ||
1748 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1753 m
= new0(struct vtable_member
, 1);
1759 m
->parent
= &s
->node_vtable
;
1761 m
->interface
= s
->node_vtable
.interface
;
1762 m
->member
= v
->x
.property
.member
;
1765 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1774 case _SD_BUS_VTABLE_SIGNAL
:
1776 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1777 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1778 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1791 s
->node_vtable
.node
= n
;
1792 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1793 bus
->nodes_modified
= true;
1801 sd_bus_slot_unref(s
);
1802 bus_node_gc(bus
, n
);
1807 _public_
int sd_bus_add_object_vtable(
1811 const char *interface
,
1812 const sd_bus_vtable
*vtable
,
1815 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1818 _public_
int sd_bus_add_fallback_vtable(
1822 const char *interface
,
1823 const sd_bus_vtable
*vtable
,
1824 sd_bus_object_find_t find
,
1827 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1830 _public_
int sd_bus_add_node_enumerator(
1834 sd_bus_node_enumerator_t callback
,
1841 assert_return(bus
, -EINVAL
);
1842 assert_return(object_path_is_valid(path
), -EINVAL
);
1843 assert_return(callback
, -EINVAL
);
1844 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1846 n
= bus_node_allocate(bus
, path
);
1850 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1856 s
->node_enumerator
.callback
= callback
;
1858 s
->node_enumerator
.node
= n
;
1859 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1860 bus
->nodes_modified
= true;
1868 sd_bus_slot_unref(s
);
1869 bus_node_gc(bus
, n
);
1874 static int emit_properties_changed_on_interface(
1878 const char *interface
,
1879 bool require_fallback
,
1880 bool *found_interface
,
1883 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1884 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1885 bool has_invalidating
= false, has_changing
= false;
1886 struct vtable_member key
= {};
1887 struct node_vtable
*c
;
1897 assert(found_interface
);
1899 n
= hashmap_get(bus
->nodes
, prefix
);
1903 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1907 r
= sd_bus_message_append(m
, "s", interface
);
1911 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1916 key
.interface
= interface
;
1918 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1919 if (require_fallback
&& !c
->is_fallback
)
1922 if (!streq(c
->interface
, interface
))
1925 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1928 if (bus
->nodes_modified
)
1933 *found_interface
= true;
1936 /* If the caller specified a list of
1937 * properties we include exactly those in the
1938 * PropertiesChanged message */
1940 STRV_FOREACH(property
, names
) {
1941 struct vtable_member
*v
;
1943 assert_return(member_name_is_valid(*property
), -EINVAL
);
1945 key
.member
= *property
;
1946 v
= hashmap_get(bus
->vtable_properties
, &key
);
1950 /* If there are two vtables for the same
1951 * interface, let's handle this property when
1952 * we come to that vtable. */
1956 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1957 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1959 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1961 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1962 has_invalidating
= true;
1966 has_changing
= true;
1968 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1971 if (bus
->nodes_modified
)
1975 const sd_bus_vtable
*v
;
1977 /* If the caller specified no properties list
1978 * we include all properties that are marked
1979 * as changing in the message. */
1981 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1982 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
1985 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
1988 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1989 has_invalidating
= true;
1993 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
1996 has_changing
= true;
1998 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
2001 if (bus
->nodes_modified
)
2007 if (!has_invalidating
&& !has_changing
)
2010 r
= sd_bus_message_close_container(m
);
2014 r
= sd_bus_message_open_container(m
, 'a', "s");
2018 if (has_invalidating
) {
2019 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2020 if (require_fallback
&& !c
->is_fallback
)
2023 if (!streq(c
->interface
, interface
))
2026 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2029 if (bus
->nodes_modified
)
2035 STRV_FOREACH(property
, names
) {
2036 struct vtable_member
*v
;
2038 key
.member
= *property
;
2039 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2040 assert(c
== v
->parent
);
2042 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2045 r
= sd_bus_message_append(m
, "s", *property
);
2050 const sd_bus_vtable
*v
;
2052 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2053 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
2056 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2059 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2062 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2070 r
= sd_bus_message_close_container(m
);
2074 r
= sd_bus_send(bus
, m
, NULL
);
2081 _public_
int sd_bus_emit_properties_changed_strv(
2084 const char *interface
,
2087 BUS_DONT_DESTROY(bus
);
2088 bool found_interface
= false;
2092 assert_return(bus
, -EINVAL
);
2093 assert_return(object_path_is_valid(path
), -EINVAL
);
2094 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2095 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2097 if (!BUS_IS_OPEN(bus
->state
))
2100 /* A non-NULL but empty names list means nothing needs to be
2101 generated. A NULL list OTOH indicates that all properties
2102 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2103 included in the PropertiesChanged message. */
2104 if (names
&& names
[0] == NULL
)
2108 bus
->nodes_modified
= false;
2110 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2113 if (bus
->nodes_modified
)
2116 prefix
= alloca(strlen(path
) + 1);
2117 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2118 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2121 if (bus
->nodes_modified
)
2125 } while (bus
->nodes_modified
);
2127 return found_interface
? 0 : -ENOENT
;
2130 _public_
int sd_bus_emit_properties_changed(
2133 const char *interface
,
2134 const char *name
, ...) {
2138 assert_return(bus
, -EINVAL
);
2139 assert_return(object_path_is_valid(path
), -EINVAL
);
2140 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2141 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2143 if (!BUS_IS_OPEN(bus
->state
))
2149 names
= strv_from_stdarg_alloca(name
);
2151 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2154 static int object_added_append_all_prefix(
2160 bool require_fallback
) {
2162 const char *previous_interface
= NULL
;
2163 struct node_vtable
*c
;
2173 n
= hashmap_get(bus
->nodes
, prefix
);
2177 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2178 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2181 if (require_fallback
&& !c
->is_fallback
)
2184 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2187 if (bus
->nodes_modified
)
2192 if (!streq_ptr(c
->interface
, previous_interface
)) {
2193 /* If a child-node already handled this interface, we
2194 * skip it on any of its parents. The child vtables
2195 * always fully override any conflicting vtables of
2196 * any parent node. */
2197 if (set_get(s
, c
->interface
))
2200 r
= set_put(s
, c
->interface
);
2204 if (previous_interface
) {
2205 r
= sd_bus_message_close_container(m
);
2208 r
= sd_bus_message_close_container(m
);
2213 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2216 r
= sd_bus_message_append(m
, "s", c
->interface
);
2219 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2223 previous_interface
= c
->interface
;
2226 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2229 if (bus
->nodes_modified
)
2233 if (previous_interface
) {
2234 r
= sd_bus_message_close_container(m
);
2237 r
= sd_bus_message_close_container(m
);
2245 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2246 _cleanup_set_free_ Set
*s
= NULL
;
2255 * This appends all interfaces registered on path @path. We first add
2256 * the builtin interfaces, which are always available and handled by
2257 * sd-bus. Then, we add all interfaces registered on the exact node,
2258 * followed by all fallback interfaces registered on any parent prefix.
2260 * If an interface is registered multiple times on the same node with
2261 * different vtables, we merge all the properties across all vtables.
2262 * However, if a child node has the same interface registered as one of
2263 * its parent nodes has as fallback, we make the child overwrite the
2264 * parent instead of extending it. Therefore, we keep a "Set" of all
2265 * handled interfaces during parent traversal, so we skip interfaces on
2266 * a parent that were overwritten by a child.
2269 s
= set_new(&string_hash_ops
);
2273 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2276 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2279 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2282 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2286 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2289 if (bus
->nodes_modified
)
2292 prefix
= alloca(strlen(path
) + 1);
2293 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2294 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2297 if (bus
->nodes_modified
)
2304 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2305 BUS_DONT_DESTROY(bus
);
2307 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2308 struct node
*object_manager
;
2312 * This emits an InterfacesAdded signal on the given path, by iterating
2313 * all registered vtables and fallback vtables on the path. All
2314 * properties are queried and included in the signal.
2315 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2316 * explicit list of registered interfaces. However, unlike
2317 * interfaces_added(), this call can figure out the list of supported
2318 * interfaces itself. Furthermore, it properly adds the builtin
2319 * org.freedesktop.DBus.* interfaces.
2322 assert_return(bus
, -EINVAL
);
2323 assert_return(object_path_is_valid(path
), -EINVAL
);
2324 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2326 if (!BUS_IS_OPEN(bus
->state
))
2329 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2336 bus
->nodes_modified
= false;
2337 m
= sd_bus_message_unref(m
);
2339 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2343 r
= sd_bus_message_append_basic(m
, 'o', path
);
2347 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2351 r
= object_added_append_all(bus
, m
, path
);
2355 if (bus
->nodes_modified
)
2358 r
= sd_bus_message_close_container(m
);
2362 } while (bus
->nodes_modified
);
2364 return sd_bus_send(bus
, m
, NULL
);
2367 static int object_removed_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_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2394 if (require_fallback
&& !c
->is_fallback
)
2396 if (streq_ptr(c
->interface
, previous_interface
))
2399 /* If a child-node already handled this interface, we
2400 * skip it on any of its parents. The child vtables
2401 * always fully override any conflicting vtables of
2402 * any parent node. */
2403 if (set_get(s
, c
->interface
))
2406 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2409 if (bus
->nodes_modified
)
2414 r
= set_put(s
, c
->interface
);
2418 r
= sd_bus_message_append(m
, "s", c
->interface
);
2422 previous_interface
= c
->interface
;
2428 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2429 _cleanup_set_free_ Set
*s
= NULL
;
2437 /* see sd_bus_emit_object_added() for details */
2439 s
= set_new(&string_hash_ops
);
2443 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2446 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2449 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2452 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2456 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2459 if (bus
->nodes_modified
)
2462 prefix
= alloca(strlen(path
) + 1);
2463 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2464 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2467 if (bus
->nodes_modified
)
2474 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2475 BUS_DONT_DESTROY(bus
);
2477 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2478 struct node
*object_manager
;
2482 * This is like sd_bus_emit_object_added(), but emits an
2483 * InterfacesRemoved signal on the given path. This only includes any
2484 * registered interfaces but skips the properties. Note that this will
2485 * call into the find() callbacks of any registered vtable. Therefore,
2486 * you must call this function before destroying/unlinking your object.
2487 * Otherwise, the list of interfaces will be incomplete. However, note
2488 * that this will *NOT* call into any property callback. Therefore, the
2489 * object might be in an "destructed" state, as long as we can find it.
2492 assert_return(bus
, -EINVAL
);
2493 assert_return(object_path_is_valid(path
), -EINVAL
);
2494 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2496 if (!BUS_IS_OPEN(bus
->state
))
2499 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2506 bus
->nodes_modified
= false;
2507 m
= sd_bus_message_unref(m
);
2509 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2513 r
= sd_bus_message_append_basic(m
, 'o', path
);
2517 r
= sd_bus_message_open_container(m
, 'a', "s");
2521 r
= object_removed_append_all(bus
, m
, path
);
2525 if (bus
->nodes_modified
)
2528 r
= sd_bus_message_close_container(m
);
2532 } while (bus
->nodes_modified
);
2534 return sd_bus_send(bus
, m
, NULL
);
2537 static int interfaces_added_append_one_prefix(
2542 const char *interface
,
2543 bool require_fallback
) {
2545 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2546 bool found_interface
= false;
2547 struct node_vtable
*c
;
2558 n
= hashmap_get(bus
->nodes
, prefix
);
2562 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2563 if (require_fallback
&& !c
->is_fallback
)
2566 if (!streq(c
->interface
, interface
))
2569 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2572 if (bus
->nodes_modified
)
2577 if (!found_interface
) {
2578 r
= sd_bus_message_append_basic(m
, 's', interface
);
2582 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2586 found_interface
= true;
2589 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2592 if (bus
->nodes_modified
)
2596 if (found_interface
) {
2597 r
= sd_bus_message_close_container(m
);
2602 return found_interface
;
2605 static int interfaces_added_append_one(
2609 const char *interface
) {
2619 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2622 if (bus
->nodes_modified
)
2625 prefix
= alloca(strlen(path
) + 1);
2626 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2627 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2630 if (bus
->nodes_modified
)
2637 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2638 BUS_DONT_DESTROY(bus
);
2640 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2641 struct node
*object_manager
;
2645 assert_return(bus
, -EINVAL
);
2646 assert_return(object_path_is_valid(path
), -EINVAL
);
2647 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2649 if (!BUS_IS_OPEN(bus
->state
))
2652 if (strv_isempty(interfaces
))
2655 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2662 bus
->nodes_modified
= false;
2663 m
= sd_bus_message_unref(m
);
2665 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2669 r
= sd_bus_message_append_basic(m
, 'o', path
);
2673 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2677 STRV_FOREACH(i
, interfaces
) {
2678 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2680 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2684 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2688 if (bus
->nodes_modified
)
2691 r
= sd_bus_message_close_container(m
);
2696 if (bus
->nodes_modified
)
2699 r
= sd_bus_message_close_container(m
);
2703 } while (bus
->nodes_modified
);
2705 return sd_bus_send(bus
, m
, NULL
);
2708 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2711 assert_return(bus
, -EINVAL
);
2712 assert_return(object_path_is_valid(path
), -EINVAL
);
2713 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2715 if (!BUS_IS_OPEN(bus
->state
))
2718 interfaces
= strv_from_stdarg_alloca(interface
);
2720 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2723 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2724 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2725 struct node
*object_manager
;
2728 assert_return(bus
, -EINVAL
);
2729 assert_return(object_path_is_valid(path
), -EINVAL
);
2730 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2732 if (!BUS_IS_OPEN(bus
->state
))
2735 if (strv_isempty(interfaces
))
2738 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2744 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2748 r
= sd_bus_message_append_basic(m
, 'o', path
);
2752 r
= sd_bus_message_append_strv(m
, interfaces
);
2756 return sd_bus_send(bus
, m
, NULL
);
2759 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2762 assert_return(bus
, -EINVAL
);
2763 assert_return(object_path_is_valid(path
), -EINVAL
);
2764 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2766 if (!BUS_IS_OPEN(bus
->state
))
2769 interfaces
= strv_from_stdarg_alloca(interface
);
2771 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2774 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2779 assert_return(bus
, -EINVAL
);
2780 assert_return(object_path_is_valid(path
), -EINVAL
);
2781 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2783 n
= bus_node_allocate(bus
, path
);
2787 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2793 s
->node_object_manager
.node
= n
;
2794 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2795 bus
->nodes_modified
= true;
2803 sd_bus_slot_unref(s
);
2804 bus_node_gc(bus
, n
);