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/>.
24 #include "bus-internal.h"
25 #include "bus-message.h"
27 #include "bus-signature.h"
28 #include "bus-introspect.h"
30 #include "bus-objects.h"
32 static int node_vtable_get_userdata(
35 struct node_vtable
*c
,
37 sd_bus_error
*error
) {
47 s
= container_of(c
, sd_bus_slot
, node_vtable
);
50 bus
->current_slot
= sd_bus_slot_ref(s
);
51 bus
->current_userdata
= u
;
52 r
= c
->find(bus
, path
, c
->interface
, u
, &u
, error
);
53 bus
->current_userdata
= NULL
;
54 bus
->current_slot
= sd_bus_slot_unref(s
);
58 if (sd_bus_error_is_set(error
))
59 return -sd_bus_error_get_errno(error
);
70 static void *vtable_method_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
73 return (uint8_t*) u
+ p
->x
.method
.offset
;
76 static void *vtable_property_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
79 return (uint8_t*) u
+ p
->x
.property
.offset
;
82 static int vtable_property_get_userdata(
85 struct vtable_member
*p
,
87 sd_bus_error
*error
) {
97 r
= node_vtable_get_userdata(bus
, path
, p
->parent
, &u
, error
);
100 if (bus
->nodes_modified
)
103 *userdata
= vtable_property_convert_userdata(p
->vtable
, u
);
107 static int add_enumerated_to_set(
110 struct node_enumerator
*first
,
112 sd_bus_error
*error
) {
114 struct node_enumerator
*c
;
121 LIST_FOREACH(enumerators
, c
, first
) {
122 char **children
= NULL
, **k
;
125 if (bus
->nodes_modified
)
128 slot
= container_of(c
, sd_bus_slot
, node_enumerator
);
130 bus
->current_slot
= sd_bus_slot_ref(slot
);
131 bus
->current_userdata
= slot
->userdata
;
132 r
= c
->callback(bus
, prefix
, slot
->userdata
, &children
, error
);
133 bus
->current_userdata
= NULL
;
134 bus
->current_slot
= sd_bus_slot_unref(slot
);
138 if (sd_bus_error_is_set(error
))
139 return -sd_bus_error_get_errno(error
);
141 STRV_FOREACH(k
, children
) {
147 if (!object_path_is_valid(*k
)){
153 if (!object_path_startswith(*k
, prefix
)) {
158 r
= set_consume(s
, *k
);
171 static int add_subtree_to_set(
175 bool skip_subhierarchies
,
177 sd_bus_error
*error
) {
187 r
= add_enumerated_to_set(bus
, prefix
, n
->enumerators
, s
, error
);
190 if (bus
->nodes_modified
)
193 LIST_FOREACH(siblings
, i
, n
->child
) {
196 if (!object_path_startswith(i
->path
, prefix
))
203 r
= set_consume(s
, t
);
204 if (r
< 0 && r
!= -EEXIST
)
207 if (!skip_subhierarchies
|| !i
->object_managers
) {
208 r
= add_subtree_to_set(bus
, prefix
, i
, skip_subhierarchies
, s
, error
);
211 if (bus
->nodes_modified
)
219 static int get_child_nodes(
223 bool skip_subhierarchies
,
225 sd_bus_error
*error
) {
235 s
= set_new(&string_hash_ops
);
239 r
= add_subtree_to_set(bus
, prefix
, n
, skip_subhierarchies
, 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_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_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 r
= check_access(bus
, m
, c
, &error
);
364 return bus_maybe_reply_error(m
, r
, &error
);
366 r
= node_vtable_get_userdata(bus
, m
->path
, c
->parent
, &u
, &error
);
368 return bus_maybe_reply_error(m
, r
, &error
);
369 if (bus
->nodes_modified
)
372 u
= vtable_method_convert_userdata(c
->vtable
, u
);
374 *found_object
= true;
376 if (c
->last_iteration
== bus
->iteration_counter
)
379 c
->last_iteration
= bus
->iteration_counter
;
381 r
= sd_bus_message_rewind(m
, true);
385 signature
= sd_bus_message_get_signature(m
, true);
389 if (!streq(strempty(c
->vtable
->x
.method
.signature
), signature
))
390 return sd_bus_reply_method_errorf(
392 SD_BUS_ERROR_INVALID_ARGS
,
393 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
394 signature
, c
->interface
, c
->member
, strempty(c
->vtable
->x
.method
.signature
));
396 /* Keep track what the signature of the reply to this message
397 * should be, so that this can be enforced when sealing the
399 m
->enforced_reply_signature
= strempty(c
->vtable
->x
.method
.result
);
401 if (c
->vtable
->x
.method
.handler
) {
404 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
406 bus
->current_slot
= sd_bus_slot_ref(slot
);
407 bus
->current_handler
= c
->vtable
->x
.method
.handler
;
408 bus
->current_userdata
= u
;
409 r
= c
->vtable
->x
.method
.handler(m
, u
, &error
);
410 bus
->current_userdata
= NULL
;
411 bus
->current_handler
= NULL
;
412 bus
->current_slot
= sd_bus_slot_unref(slot
);
414 return bus_maybe_reply_error(m
, r
, &error
);
417 /* If the method callback is NULL, make this a successful NOP */
418 r
= sd_bus_reply_method_return(m
, NULL
);
425 static int invoke_property_get(
428 const sd_bus_vtable
*v
,
430 const char *interface
,
431 const char *property
,
432 sd_bus_message
*reply
,
434 sd_bus_error
*error
) {
447 if (v
->x
.property
.get
) {
449 bus
->current_slot
= sd_bus_slot_ref(slot
);
450 bus
->current_userdata
= userdata
;
451 r
= v
->x
.property
.get(bus
, path
, interface
, property
, reply
, userdata
, error
);
452 bus
->current_userdata
= NULL
;
453 bus
->current_slot
= sd_bus_slot_unref(slot
);
457 if (sd_bus_error_is_set(error
))
458 return -sd_bus_error_get_errno(error
);
462 /* Automatic handling if no callback is defined. */
464 if (streq(v
->x
.property
.signature
, "as"))
465 return sd_bus_message_append_strv(reply
, *(char***) userdata
);
467 assert(signature_is_single(v
->x
.property
.signature
, false));
468 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
470 switch (v
->x
.property
.signature
[0]) {
472 case SD_BUS_TYPE_STRING
:
473 case SD_BUS_TYPE_SIGNATURE
:
474 p
= strempty(*(char**) userdata
);
477 case SD_BUS_TYPE_OBJECT_PATH
:
478 p
= *(char**) userdata
;
487 return sd_bus_message_append_basic(reply
, v
->x
.property
.signature
[0], p
);
490 static int invoke_property_set(
493 const sd_bus_vtable
*v
,
495 const char *interface
,
496 const char *property
,
497 sd_bus_message
*value
,
499 sd_bus_error
*error
) {
511 if (v
->x
.property
.set
) {
513 bus
->current_slot
= sd_bus_slot_ref(slot
);
514 bus
->current_userdata
= userdata
;
515 r
= v
->x
.property
.set(bus
, path
, interface
, property
, value
, userdata
, error
);
516 bus
->current_userdata
= NULL
;
517 bus
->current_slot
= sd_bus_slot_unref(slot
);
521 if (sd_bus_error_is_set(error
))
522 return -sd_bus_error_get_errno(error
);
526 /* Automatic handling if no callback is defined. */
528 assert(signature_is_single(v
->x
.property
.signature
, false));
529 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
531 switch (v
->x
.property
.signature
[0]) {
533 case SD_BUS_TYPE_STRING
:
534 case SD_BUS_TYPE_OBJECT_PATH
:
535 case SD_BUS_TYPE_SIGNATURE
: {
539 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], &p
);
547 free(*(char**) userdata
);
548 *(char**) userdata
= n
;
554 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], userdata
);
564 static int property_get_set_callbacks_run(
567 struct vtable_member
*c
,
568 bool require_fallback
,
570 bool *found_object
) {
572 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
573 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
581 assert(found_object
);
583 if (require_fallback
&& !c
->parent
->is_fallback
)
586 r
= vtable_property_get_userdata(bus
, m
->path
, c
, &u
, &error
);
588 return bus_maybe_reply_error(m
, r
, &error
);
589 if (bus
->nodes_modified
)
592 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
594 *found_object
= true;
596 r
= sd_bus_message_new_method_return(m
, &reply
);
601 /* Note that we do not protect against reexecution
602 * here (using the last_iteration check, see below),
603 * should the node tree have changed and we got called
604 * again. We assume that property Get() calls are
605 * ultimately without side-effects or if they aren't
606 * then at least idempotent. */
608 r
= sd_bus_message_open_container(reply
, 'v', c
->vtable
->x
.property
.signature
);
612 /* Note that we do not do an access check here. Read
613 * access to properties is always unrestricted, since
614 * PropertiesChanged signals broadcast contents
617 r
= invoke_property_get(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, reply
, u
, &error
);
619 return bus_maybe_reply_error(m
, r
, &error
);
621 if (bus
->nodes_modified
)
624 r
= sd_bus_message_close_container(reply
);
629 const char *signature
= NULL
;
632 if (c
->vtable
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
633 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_PROPERTY_READ_ONLY
, "Property '%s' is not writable.", c
->member
);
635 /* Avoid that we call the set routine more than once
636 * if the processing of this message got restarted
637 * because the node tree changed. */
638 if (c
->last_iteration
== bus
->iteration_counter
)
641 c
->last_iteration
= bus
->iteration_counter
;
643 r
= sd_bus_message_peek_type(m
, &type
, &signature
);
647 if (type
!= 'v' || !streq(strempty(signature
), strempty(c
->vtable
->x
.property
.signature
)))
648 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
));
650 r
= sd_bus_message_enter_container(m
, 'v', c
->vtable
->x
.property
.signature
);
654 r
= check_access(bus
, m
, c
, &error
);
656 return bus_maybe_reply_error(m
, r
, &error
);
658 r
= invoke_property_set(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, m
, u
, &error
);
660 return bus_maybe_reply_error(m
, r
, &error
);
662 if (bus
->nodes_modified
)
665 r
= sd_bus_message_exit_container(m
);
670 r
= sd_bus_send(bus
, reply
, NULL
);
677 static int vtable_append_one_property(
679 sd_bus_message
*reply
,
681 struct node_vtable
*c
,
682 const sd_bus_vtable
*v
,
684 sd_bus_error
*error
) {
695 r
= sd_bus_message_open_container(reply
, 'e', "sv");
699 r
= sd_bus_message_append(reply
, "s", v
->x
.property
.member
);
703 r
= sd_bus_message_open_container(reply
, 'v', v
->x
.property
.signature
);
707 slot
= container_of(c
, sd_bus_slot
, node_vtable
);
709 r
= invoke_property_get(bus
, slot
, v
, path
, c
->interface
, v
->x
.property
.member
, reply
, vtable_property_convert_userdata(v
, userdata
), error
);
712 if (bus
->nodes_modified
)
715 r
= sd_bus_message_close_container(reply
);
719 r
= sd_bus_message_close_container(reply
);
726 static int vtable_append_all_properties(
728 sd_bus_message
*reply
,
730 struct node_vtable
*c
,
732 sd_bus_error
*error
) {
734 const sd_bus_vtable
*v
;
742 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
745 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
746 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
749 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
752 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)
755 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
758 if (bus
->nodes_modified
)
765 static int property_get_all_callbacks_run(
768 struct node_vtable
*first
,
769 bool require_fallback
,
771 bool *found_object
) {
773 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
774 struct node_vtable
*c
;
775 bool found_interface
;
780 assert(found_object
);
782 r
= sd_bus_message_new_method_return(m
, &reply
);
786 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
790 found_interface
= !iface
||
791 streq(iface
, "org.freedesktop.DBus.Properties") ||
792 streq(iface
, "org.freedesktop.DBus.Peer") ||
793 streq(iface
, "org.freedesktop.DBus.Introspectable");
795 LIST_FOREACH(vtables
, c
, first
) {
796 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
799 if (require_fallback
&& !c
->is_fallback
)
802 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
804 return bus_maybe_reply_error(m
, r
, &error
);
805 if (bus
->nodes_modified
)
810 *found_object
= true;
812 if (iface
&& !streq(c
->interface
, iface
))
814 found_interface
= true;
816 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
818 return bus_maybe_reply_error(m
, r
, &error
);
819 if (bus
->nodes_modified
)
823 if (!found_interface
) {
824 r
= sd_bus_reply_method_errorf(
826 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
827 "Unknown interface '%s'.", iface
);
834 r
= sd_bus_message_close_container(reply
);
838 r
= sd_bus_send(bus
, reply
, NULL
);
845 static int bus_node_exists(
849 bool require_fallback
) {
851 struct node_vtable
*c
;
852 struct node_callback
*k
;
859 /* Tests if there's anything attached directly to this node
860 * for the specified path */
862 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
865 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
866 if (require_fallback
&& !k
->is_fallback
)
872 LIST_FOREACH(vtables
, c
, n
->vtables
) {
873 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
875 if (require_fallback
&& !c
->is_fallback
)
878 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
881 if (bus
->nodes_modified
)
888 static int process_introspect(
892 bool require_fallback
,
893 bool *found_object
) {
895 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
896 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
897 _cleanup_set_free_free_ Set
*s
= NULL
;
898 const char *previous_interface
= NULL
;
899 struct introspect intro
;
900 struct node_vtable
*c
;
907 assert(found_object
);
909 r
= get_child_nodes(bus
, m
->path
, n
, false, &s
, &error
);
911 return bus_maybe_reply_error(m
, r
, &error
);
912 if (bus
->nodes_modified
)
915 r
= introspect_begin(&intro
, bus
->trusted
);
919 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
923 empty
= set_isempty(s
);
925 LIST_FOREACH(vtables
, c
, n
->vtables
) {
926 if (require_fallback
&& !c
->is_fallback
)
929 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
931 r
= bus_maybe_reply_error(m
, r
, &error
);
934 if (bus
->nodes_modified
) {
943 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
946 if (!streq_ptr(previous_interface
, c
->interface
)) {
948 if (previous_interface
)
949 fputs(" </interface>\n", intro
.f
);
951 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
954 r
= introspect_write_interface(&intro
, c
->vtable
);
958 previous_interface
= c
->interface
;
961 if (previous_interface
)
962 fputs(" </interface>\n", intro
.f
);
965 /* Nothing?, let's see if we exist at all, and if not
966 * refuse to do anything */
967 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
970 if (bus
->nodes_modified
) {
976 *found_object
= true;
978 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
982 r
= introspect_finish(&intro
, bus
, m
, &reply
);
986 r
= sd_bus_send(bus
, reply
, NULL
);
993 introspect_free(&intro
);
997 static int object_manager_serialize_path(
999 sd_bus_message
*reply
,
1002 bool require_fallback
,
1003 sd_bus_error
*error
) {
1005 const char *previous_interface
= NULL
;
1006 bool found_something
= false;
1007 struct node_vtable
*i
;
1017 n
= hashmap_get(bus
->nodes
, prefix
);
1021 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1024 if (require_fallback
&& !i
->is_fallback
)
1027 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1030 if (bus
->nodes_modified
)
1035 if (!found_something
) {
1037 /* Open the object part */
1039 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1043 r
= sd_bus_message_append(reply
, "o", path
);
1047 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1051 found_something
= true;
1054 if (!streq_ptr(previous_interface
, i
->interface
)) {
1056 /* Maybe close the previous interface part */
1058 if (previous_interface
) {
1059 r
= sd_bus_message_close_container(reply
);
1063 r
= sd_bus_message_close_container(reply
);
1068 /* Open the new interface part */
1070 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1074 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1078 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1083 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1086 if (bus
->nodes_modified
)
1089 previous_interface
= i
->interface
;
1092 if (previous_interface
) {
1093 r
= sd_bus_message_close_container(reply
);
1097 r
= sd_bus_message_close_container(reply
);
1102 if (found_something
) {
1103 r
= sd_bus_message_close_container(reply
);
1107 r
= sd_bus_message_close_container(reply
);
1115 static int object_manager_serialize_path_and_fallbacks(
1117 sd_bus_message
*reply
,
1119 sd_bus_error
*error
) {
1129 /* First, add all vtables registered for this path */
1130 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1133 if (bus
->nodes_modified
)
1136 /* Second, add fallback vtables registered for any of the prefixes */
1137 prefix
= alloca(strlen(path
) + 1);
1138 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1139 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1142 if (bus
->nodes_modified
)
1149 static int process_get_managed_objects(
1153 bool require_fallback
,
1154 bool *found_object
) {
1156 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1157 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1158 _cleanup_set_free_free_ Set
*s
= NULL
;
1166 assert(found_object
);
1168 /* Spec says, GetManagedObjects() is only implemented on the root of a
1169 * sub-tree. Therefore, we require a registered object-manager on
1170 * exactly the queried path, otherwise, we refuse to respond. */
1172 if (require_fallback
|| !n
->object_managers
)
1175 r
= get_child_nodes(bus
, m
->path
, n
, true, &s
, &error
);
1178 if (bus
->nodes_modified
)
1181 r
= sd_bus_message_new_method_return(m
, &reply
);
1185 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1189 SET_FOREACH(path
, s
, i
) {
1190 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1194 if (bus
->nodes_modified
)
1198 r
= sd_bus_message_close_container(reply
);
1202 r
= sd_bus_send(bus
, reply
, NULL
);
1209 static int object_find_and_run(
1213 bool require_fallback
,
1214 bool *found_object
) {
1217 struct vtable_member vtable_key
, *v
;
1223 assert(found_object
);
1225 n
= hashmap_get(bus
->nodes
, p
);
1229 /* First, try object callbacks */
1230 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1233 if (bus
->nodes_modified
)
1236 if (!m
->interface
|| !m
->member
)
1239 /* Then, look for a known method */
1240 vtable_key
.path
= (char*) p
;
1241 vtable_key
.interface
= m
->interface
;
1242 vtable_key
.member
= m
->member
;
1244 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1246 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1249 if (bus
->nodes_modified
)
1253 /* Then, look for a known property */
1254 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1257 get
= streq(m
->member
, "Get");
1259 if (get
|| streq(m
->member
, "Set")) {
1261 r
= sd_bus_message_rewind(m
, true);
1265 vtable_key
.path
= (char*) p
;
1267 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1269 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1271 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1273 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1278 } else if (streq(m
->member
, "GetAll")) {
1281 r
= sd_bus_message_rewind(m
, true);
1285 r
= sd_bus_message_read(m
, "s", &iface
);
1287 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1292 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1297 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1299 if (!isempty(sd_bus_message_get_signature(m
, true)))
1300 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1302 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1306 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1308 if (!isempty(sd_bus_message_get_signature(m
, true)))
1309 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1311 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1316 if (bus
->nodes_modified
)
1319 if (!*found_object
) {
1320 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1323 if (bus
->nodes_modified
)
1326 *found_object
= true;
1332 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1335 bool found_object
= false;
1340 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1343 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1346 if (hashmap_isempty(bus
->nodes
))
1349 /* Never respond to broadcast messages */
1350 if (bus
->bus_client
&& !m
->destination
)
1356 pl
= strlen(m
->path
);
1360 bus
->nodes_modified
= false;
1362 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1366 /* Look for fallback prefixes */
1367 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1369 if (bus
->nodes_modified
)
1372 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1377 } while (bus
->nodes_modified
);
1382 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1383 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1384 r
= sd_bus_reply_method_errorf(
1386 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1387 "Unknown property or interface.");
1389 r
= sd_bus_reply_method_errorf(
1391 SD_BUS_ERROR_UNKNOWN_METHOD
,
1392 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1400 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1401 struct node
*n
, *parent
;
1403 _cleanup_free_
char *s
= NULL
;
1409 assert(path
[0] == '/');
1411 n
= hashmap_get(bus
->nodes
, path
);
1415 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1423 if (streq(path
, "/"))
1426 e
= strrchr(path
, '/');
1429 p
= strndupa(path
, MAX(1, e
- path
));
1431 parent
= bus_node_allocate(bus
, p
);
1436 n
= new0(struct node
, 1);
1442 s
= NULL
; /* do not free */
1444 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1452 LIST_PREPEND(siblings
, parent
->child
, n
);
1457 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1470 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1473 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1476 bus_node_gc(b
, n
->parent
);
1480 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1486 n
= hashmap_get(bus
->nodes
, path
);
1490 prefix
= alloca(strlen(path
) + 1);
1491 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1492 n
= hashmap_get(bus
->nodes
, prefix
);
1498 while (n
&& !n
->object_managers
)
1506 static int bus_add_object(
1511 sd_bus_message_handler_t callback
,
1518 assert_return(bus
, -EINVAL
);
1519 assert_return(object_path_is_valid(path
), -EINVAL
);
1520 assert_return(callback
, -EINVAL
);
1521 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1523 n
= bus_node_allocate(bus
, path
);
1527 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1533 s
->node_callback
.callback
= callback
;
1534 s
->node_callback
.is_fallback
= fallback
;
1536 s
->node_callback
.node
= n
;
1537 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1538 bus
->nodes_modified
= true;
1546 sd_bus_slot_unref(s
);
1547 bus_node_gc(bus
, n
);
1552 _public_
int sd_bus_add_object(
1556 sd_bus_message_handler_t callback
,
1559 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1562 _public_
int sd_bus_add_fallback(
1566 sd_bus_message_handler_t callback
,
1569 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1572 static unsigned long vtable_member_hash_func(const void *a
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
1573 const struct vtable_member
*m
= a
;
1574 uint8_t hash_key2
[HASH_KEY_SIZE
];
1579 ret
= string_hash_func(m
->path
, hash_key
);
1581 /* Use a slightly different hash key for the interface */
1582 memcpy(hash_key2
, hash_key
, HASH_KEY_SIZE
);
1584 ret
^= string_hash_func(m
->interface
, hash_key2
);
1586 /* And an even different one for the member */
1588 ret
^= string_hash_func(m
->member
, hash_key2
);
1593 static int vtable_member_compare_func(const void *a
, const void *b
) {
1594 const struct vtable_member
*x
= a
, *y
= b
;
1600 r
= strcmp(x
->path
, y
->path
);
1604 r
= strcmp(x
->interface
, y
->interface
);
1608 return strcmp(x
->member
, y
->member
);
1611 static const struct hash_ops vtable_member_hash_ops
= {
1612 .hash
= vtable_member_hash_func
,
1613 .compare
= vtable_member_compare_func
1616 static int add_object_vtable_internal(
1620 const char *interface
,
1621 const sd_bus_vtable
*vtable
,
1623 sd_bus_object_find_t find
,
1626 sd_bus_slot
*s
= NULL
;
1627 struct node_vtable
*i
, *existing
= NULL
;
1628 const sd_bus_vtable
*v
;
1632 assert_return(bus
, -EINVAL
);
1633 assert_return(object_path_is_valid(path
), -EINVAL
);
1634 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1635 assert_return(vtable
, -EINVAL
);
1636 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1637 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1638 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1639 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1640 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1641 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1642 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1644 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1648 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1652 n
= bus_node_allocate(bus
, path
);
1656 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1657 if (i
->is_fallback
!= fallback
) {
1662 if (streq(i
->interface
, interface
)) {
1664 if (i
->vtable
== vtable
) {
1673 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1679 s
->node_vtable
.is_fallback
= fallback
;
1680 s
->node_vtable
.vtable
= vtable
;
1681 s
->node_vtable
.find
= find
;
1683 s
->node_vtable
.interface
= strdup(interface
);
1684 if (!s
->node_vtable
.interface
) {
1689 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1693 case _SD_BUS_VTABLE_METHOD
: {
1694 struct vtable_member
*m
;
1696 if (!member_name_is_valid(v
->x
.method
.member
) ||
1697 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1698 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1699 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1700 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1705 m
= new0(struct vtable_member
, 1);
1711 m
->parent
= &s
->node_vtable
;
1713 m
->interface
= s
->node_vtable
.interface
;
1714 m
->member
= v
->x
.method
.member
;
1717 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1726 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1728 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1733 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1740 case _SD_BUS_VTABLE_PROPERTY
: {
1741 struct vtable_member
*m
;
1743 if (!member_name_is_valid(v
->x
.property
.member
) ||
1744 !signature_is_single(v
->x
.property
.signature
, false) ||
1745 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1746 (v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
) ||
1747 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1748 ((v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) && (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)) ||
1749 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1754 m
= new0(struct vtable_member
, 1);
1760 m
->parent
= &s
->node_vtable
;
1762 m
->interface
= s
->node_vtable
.interface
;
1763 m
->member
= v
->x
.property
.member
;
1766 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1775 case _SD_BUS_VTABLE_SIGNAL
:
1777 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1778 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1779 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1792 s
->node_vtable
.node
= n
;
1793 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1794 bus
->nodes_modified
= true;
1802 sd_bus_slot_unref(s
);
1803 bus_node_gc(bus
, n
);
1808 _public_
int sd_bus_add_object_vtable(
1812 const char *interface
,
1813 const sd_bus_vtable
*vtable
,
1816 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1819 _public_
int sd_bus_add_fallback_vtable(
1823 const char *interface
,
1824 const sd_bus_vtable
*vtable
,
1825 sd_bus_object_find_t find
,
1828 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1831 _public_
int sd_bus_add_node_enumerator(
1835 sd_bus_node_enumerator_t callback
,
1842 assert_return(bus
, -EINVAL
);
1843 assert_return(object_path_is_valid(path
), -EINVAL
);
1844 assert_return(callback
, -EINVAL
);
1845 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1847 n
= bus_node_allocate(bus
, path
);
1851 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1857 s
->node_enumerator
.callback
= callback
;
1859 s
->node_enumerator
.node
= n
;
1860 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1861 bus
->nodes_modified
= true;
1869 sd_bus_slot_unref(s
);
1870 bus_node_gc(bus
, n
);
1875 static int emit_properties_changed_on_interface(
1879 const char *interface
,
1880 bool require_fallback
,
1881 bool *found_interface
,
1884 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1885 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1886 bool has_invalidating
= false, has_changing
= false;
1887 struct vtable_member key
= {};
1888 struct node_vtable
*c
;
1898 assert(found_interface
);
1900 n
= hashmap_get(bus
->nodes
, prefix
);
1904 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1908 r
= sd_bus_message_append(m
, "s", interface
);
1912 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1917 key
.interface
= interface
;
1919 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1920 if (require_fallback
&& !c
->is_fallback
)
1923 if (!streq(c
->interface
, interface
))
1926 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1929 if (bus
->nodes_modified
)
1934 *found_interface
= true;
1937 /* If the caller specified a list of
1938 * properties we include exactly those in the
1939 * PropertiesChanged message */
1941 STRV_FOREACH(property
, names
) {
1942 struct vtable_member
*v
;
1944 assert_return(member_name_is_valid(*property
), -EINVAL
);
1946 key
.member
= *property
;
1947 v
= hashmap_get(bus
->vtable_properties
, &key
);
1951 /* If there are two vtables for the same
1952 * interface, let's handle this property when
1953 * we come to that vtable. */
1957 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1958 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1960 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1962 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1963 has_invalidating
= true;
1967 has_changing
= true;
1969 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1972 if (bus
->nodes_modified
)
1976 const sd_bus_vtable
*v
;
1978 /* If the caller specified no properties list
1979 * we include all properties that are marked
1980 * as changing in the message. */
1982 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1983 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
1986 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
1989 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1990 has_invalidating
= true;
1994 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
1997 has_changing
= true;
1999 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
2002 if (bus
->nodes_modified
)
2008 if (!has_invalidating
&& !has_changing
)
2011 r
= sd_bus_message_close_container(m
);
2015 r
= sd_bus_message_open_container(m
, 'a', "s");
2019 if (has_invalidating
) {
2020 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2021 if (require_fallback
&& !c
->is_fallback
)
2024 if (!streq(c
->interface
, interface
))
2027 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2030 if (bus
->nodes_modified
)
2036 STRV_FOREACH(property
, names
) {
2037 struct vtable_member
*v
;
2039 key
.member
= *property
;
2040 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2041 assert(c
== v
->parent
);
2043 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2046 r
= sd_bus_message_append(m
, "s", *property
);
2051 const sd_bus_vtable
*v
;
2053 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2054 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
2057 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2060 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2063 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2071 r
= sd_bus_message_close_container(m
);
2075 r
= sd_bus_send(bus
, m
, NULL
);
2082 _public_
int sd_bus_emit_properties_changed_strv(
2085 const char *interface
,
2088 BUS_DONT_DESTROY(bus
);
2089 bool found_interface
= false;
2093 assert_return(bus
, -EINVAL
);
2094 assert_return(object_path_is_valid(path
), -EINVAL
);
2095 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2096 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2098 if (!BUS_IS_OPEN(bus
->state
))
2101 /* A non-NULL but empty names list means nothing needs to be
2102 generated. A NULL list OTOH indicates that all properties
2103 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2104 included in the PropertiesChanged message. */
2105 if (names
&& names
[0] == NULL
)
2109 bus
->nodes_modified
= false;
2111 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2114 if (bus
->nodes_modified
)
2117 prefix
= alloca(strlen(path
) + 1);
2118 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2119 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2122 if (bus
->nodes_modified
)
2126 } while (bus
->nodes_modified
);
2128 return found_interface
? 0 : -ENOENT
;
2131 _public_
int sd_bus_emit_properties_changed(
2134 const char *interface
,
2135 const char *name
, ...) {
2139 assert_return(bus
, -EINVAL
);
2140 assert_return(object_path_is_valid(path
), -EINVAL
);
2141 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2142 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2144 if (!BUS_IS_OPEN(bus
->state
))
2150 names
= strv_from_stdarg_alloca(name
);
2152 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2155 static int object_added_append_all_prefix(
2161 bool require_fallback
) {
2163 const char *previous_interface
= NULL
;
2164 struct node_vtable
*c
;
2174 n
= hashmap_get(bus
->nodes
, prefix
);
2178 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2179 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2182 if (require_fallback
&& !c
->is_fallback
)
2185 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2188 if (bus
->nodes_modified
)
2193 if (!streq_ptr(c
->interface
, previous_interface
)) {
2194 /* If a child-node already handled this interface, we
2195 * skip it on any of its parents. The child vtables
2196 * always fully override any conflicting vtables of
2197 * any parent node. */
2198 if (set_get(s
, c
->interface
))
2201 r
= set_put(s
, c
->interface
);
2205 if (previous_interface
) {
2206 r
= sd_bus_message_close_container(m
);
2209 r
= sd_bus_message_close_container(m
);
2214 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2217 r
= sd_bus_message_append(m
, "s", c
->interface
);
2220 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2224 previous_interface
= c
->interface
;
2227 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2230 if (bus
->nodes_modified
)
2234 if (previous_interface
) {
2235 r
= sd_bus_message_close_container(m
);
2238 r
= sd_bus_message_close_container(m
);
2246 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2247 _cleanup_set_free_ Set
*s
= NULL
;
2256 * This appends all interfaces registered on path @path. We first add
2257 * the builtin interfaces, which are always available and handled by
2258 * sd-bus. Then, we add all interfaces registered on the exact node,
2259 * followed by all fallback interfaces registered on any parent prefix.
2261 * If an interface is registered multiple times on the same node with
2262 * different vtables, we merge all the properties across all vtables.
2263 * However, if a child node has the same interface registered as one of
2264 * its parent nodes has as fallback, we make the child overwrite the
2265 * parent instead of extending it. Therefore, we keep a "Set" of all
2266 * handled interfaces during parent traversal, so we skip interfaces on
2267 * a parent that were overwritten by a child.
2270 s
= set_new(&string_hash_ops
);
2274 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2277 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2280 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2283 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2287 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2290 if (bus
->nodes_modified
)
2293 prefix
= alloca(strlen(path
) + 1);
2294 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2295 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2298 if (bus
->nodes_modified
)
2305 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2306 BUS_DONT_DESTROY(bus
);
2308 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2309 struct node
*object_manager
;
2313 * This emits an InterfacesAdded signal on the given path, by iterating
2314 * all registered vtables and fallback vtables on the path. All
2315 * properties are queried and included in the signal.
2316 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2317 * explicit list of registered interfaces. However, unlike
2318 * interfaces_added(), this call can figure out the list of supported
2319 * interfaces itself. Furthermore, it properly adds the builtin
2320 * org.freedesktop.DBus.* interfaces.
2323 assert_return(bus
, -EINVAL
);
2324 assert_return(object_path_is_valid(path
), -EINVAL
);
2325 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2327 if (!BUS_IS_OPEN(bus
->state
))
2330 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2337 bus
->nodes_modified
= false;
2338 m
= sd_bus_message_unref(m
);
2340 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2344 r
= sd_bus_message_append_basic(m
, 'o', path
);
2348 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2352 r
= object_added_append_all(bus
, m
, path
);
2356 if (bus
->nodes_modified
)
2359 r
= sd_bus_message_close_container(m
);
2363 } while (bus
->nodes_modified
);
2365 return sd_bus_send(bus
, m
, NULL
);
2368 static int object_removed_append_all_prefix(
2374 bool require_fallback
) {
2376 const char *previous_interface
= NULL
;
2377 struct node_vtable
*c
;
2387 n
= hashmap_get(bus
->nodes
, prefix
);
2391 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2392 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2395 if (require_fallback
&& !c
->is_fallback
)
2397 if (streq_ptr(c
->interface
, previous_interface
))
2400 /* If a child-node already handled this interface, we
2401 * skip it on any of its parents. The child vtables
2402 * always fully override any conflicting vtables of
2403 * any parent node. */
2404 if (set_get(s
, c
->interface
))
2407 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2410 if (bus
->nodes_modified
)
2415 r
= set_put(s
, c
->interface
);
2419 r
= sd_bus_message_append(m
, "s", c
->interface
);
2423 previous_interface
= c
->interface
;
2429 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2430 _cleanup_set_free_ Set
*s
= NULL
;
2438 /* see sd_bus_emit_object_added() for details */
2440 s
= set_new(&string_hash_ops
);
2444 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2447 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2450 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2453 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2457 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2460 if (bus
->nodes_modified
)
2463 prefix
= alloca(strlen(path
) + 1);
2464 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2465 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2468 if (bus
->nodes_modified
)
2475 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2476 BUS_DONT_DESTROY(bus
);
2478 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2479 struct node
*object_manager
;
2483 * This is like sd_bus_emit_object_added(), but emits an
2484 * InterfacesRemoved signal on the given path. This only includes any
2485 * registered interfaces but skips the properties. Note that this will
2486 * call into the find() callbacks of any registered vtable. Therefore,
2487 * you must call this function before destroying/unlinking your object.
2488 * Otherwise, the list of interfaces will be incomplete. However, note
2489 * that this will *NOT* call into any property callback. Therefore, the
2490 * object might be in an "destructed" state, as long as we can find it.
2493 assert_return(bus
, -EINVAL
);
2494 assert_return(object_path_is_valid(path
), -EINVAL
);
2495 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2497 if (!BUS_IS_OPEN(bus
->state
))
2500 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2507 bus
->nodes_modified
= false;
2508 m
= sd_bus_message_unref(m
);
2510 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2514 r
= sd_bus_message_append_basic(m
, 'o', path
);
2518 r
= sd_bus_message_open_container(m
, 'a', "s");
2522 r
= object_removed_append_all(bus
, m
, path
);
2526 if (bus
->nodes_modified
)
2529 r
= sd_bus_message_close_container(m
);
2533 } while (bus
->nodes_modified
);
2535 return sd_bus_send(bus
, m
, NULL
);
2538 static int interfaces_added_append_one_prefix(
2543 const char *interface
,
2544 bool require_fallback
) {
2546 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2547 bool found_interface
= false;
2548 struct node_vtable
*c
;
2559 n
= hashmap_get(bus
->nodes
, prefix
);
2563 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2564 if (require_fallback
&& !c
->is_fallback
)
2567 if (!streq(c
->interface
, interface
))
2570 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2573 if (bus
->nodes_modified
)
2578 if (!found_interface
) {
2579 r
= sd_bus_message_append_basic(m
, 's', interface
);
2583 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2587 found_interface
= true;
2590 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2593 if (bus
->nodes_modified
)
2597 if (found_interface
) {
2598 r
= sd_bus_message_close_container(m
);
2603 return found_interface
;
2606 static int interfaces_added_append_one(
2610 const char *interface
) {
2620 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2623 if (bus
->nodes_modified
)
2626 prefix
= alloca(strlen(path
) + 1);
2627 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2628 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2631 if (bus
->nodes_modified
)
2638 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2639 BUS_DONT_DESTROY(bus
);
2641 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2642 struct node
*object_manager
;
2646 assert_return(bus
, -EINVAL
);
2647 assert_return(object_path_is_valid(path
), -EINVAL
);
2648 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2650 if (!BUS_IS_OPEN(bus
->state
))
2653 if (strv_isempty(interfaces
))
2656 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2663 bus
->nodes_modified
= false;
2664 m
= sd_bus_message_unref(m
);
2666 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2670 r
= sd_bus_message_append_basic(m
, 'o', path
);
2674 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2678 STRV_FOREACH(i
, interfaces
) {
2679 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2681 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2685 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2689 if (bus
->nodes_modified
)
2692 r
= sd_bus_message_close_container(m
);
2697 if (bus
->nodes_modified
)
2700 r
= sd_bus_message_close_container(m
);
2704 } while (bus
->nodes_modified
);
2706 return sd_bus_send(bus
, m
, NULL
);
2709 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2712 assert_return(bus
, -EINVAL
);
2713 assert_return(object_path_is_valid(path
), -EINVAL
);
2714 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2716 if (!BUS_IS_OPEN(bus
->state
))
2719 interfaces
= strv_from_stdarg_alloca(interface
);
2721 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2724 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2725 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2726 struct node
*object_manager
;
2729 assert_return(bus
, -EINVAL
);
2730 assert_return(object_path_is_valid(path
), -EINVAL
);
2731 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2733 if (!BUS_IS_OPEN(bus
->state
))
2736 if (strv_isempty(interfaces
))
2739 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2745 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2749 r
= sd_bus_message_append_basic(m
, 'o', path
);
2753 r
= sd_bus_message_append_strv(m
, interfaces
);
2757 return sd_bus_send(bus
, m
, NULL
);
2760 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2763 assert_return(bus
, -EINVAL
);
2764 assert_return(object_path_is_valid(path
), -EINVAL
);
2765 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2767 if (!BUS_IS_OPEN(bus
->state
))
2770 interfaces
= strv_from_stdarg_alloca(interface
);
2772 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2775 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2780 assert_return(bus
, -EINVAL
);
2781 assert_return(object_path_is_valid(path
), -EINVAL
);
2782 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2784 n
= bus_node_allocate(bus
, path
);
2788 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2794 s
->node_object_manager
.node
= n
;
2795 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2796 bus
->nodes_modified
= true;
2804 sd_bus_slot_unref(s
);
2805 bus_node_gc(bus
, n
);