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 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
755 if (bus
->nodes_modified
)
762 static int property_get_all_callbacks_run(
765 struct node_vtable
*first
,
766 bool require_fallback
,
768 bool *found_object
) {
770 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
771 struct node_vtable
*c
;
772 bool found_interface
;
777 assert(found_object
);
779 r
= sd_bus_message_new_method_return(m
, &reply
);
783 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
787 found_interface
= !iface
||
788 streq(iface
, "org.freedesktop.DBus.Properties") ||
789 streq(iface
, "org.freedesktop.DBus.Peer") ||
790 streq(iface
, "org.freedesktop.DBus.Introspectable");
792 LIST_FOREACH(vtables
, c
, first
) {
793 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
796 if (require_fallback
&& !c
->is_fallback
)
799 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
801 return bus_maybe_reply_error(m
, r
, &error
);
802 if (bus
->nodes_modified
)
807 *found_object
= true;
809 if (iface
&& !streq(c
->interface
, iface
))
811 found_interface
= true;
813 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
815 return bus_maybe_reply_error(m
, r
, &error
);
816 if (bus
->nodes_modified
)
820 if (!found_interface
) {
821 r
= sd_bus_reply_method_errorf(
823 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
824 "Unknown interface '%s'.", iface
);
831 r
= sd_bus_message_close_container(reply
);
835 r
= sd_bus_send(bus
, reply
, NULL
);
842 static int bus_node_exists(
846 bool require_fallback
) {
848 struct node_vtable
*c
;
849 struct node_callback
*k
;
856 /* Tests if there's anything attached directly to this node
857 * for the specified path */
859 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
862 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
863 if (require_fallback
&& !k
->is_fallback
)
869 LIST_FOREACH(vtables
, c
, n
->vtables
) {
870 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
872 if (require_fallback
&& !c
->is_fallback
)
875 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
878 if (bus
->nodes_modified
)
885 static int process_introspect(
889 bool require_fallback
,
890 bool *found_object
) {
892 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
893 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
894 _cleanup_set_free_free_ Set
*s
= NULL
;
895 const char *previous_interface
= NULL
;
896 struct introspect intro
;
897 struct node_vtable
*c
;
904 assert(found_object
);
906 r
= get_child_nodes(bus
, m
->path
, n
, false, &s
, &error
);
908 return bus_maybe_reply_error(m
, r
, &error
);
909 if (bus
->nodes_modified
)
912 r
= introspect_begin(&intro
, bus
->trusted
);
916 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
920 empty
= set_isempty(s
);
922 LIST_FOREACH(vtables
, c
, n
->vtables
) {
923 if (require_fallback
&& !c
->is_fallback
)
926 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
928 r
= bus_maybe_reply_error(m
, r
, &error
);
931 if (bus
->nodes_modified
) {
940 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
943 if (!streq_ptr(previous_interface
, c
->interface
)) {
945 if (previous_interface
)
946 fputs(" </interface>\n", intro
.f
);
948 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
951 r
= introspect_write_interface(&intro
, c
->vtable
);
955 previous_interface
= c
->interface
;
958 if (previous_interface
)
959 fputs(" </interface>\n", intro
.f
);
962 /* Nothing?, let's see if we exist at all, and if not
963 * refuse to do anything */
964 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
967 if (bus
->nodes_modified
) {
973 *found_object
= true;
975 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
979 r
= introspect_finish(&intro
, bus
, m
, &reply
);
983 r
= sd_bus_send(bus
, reply
, NULL
);
990 introspect_free(&intro
);
994 static int object_manager_serialize_path(
996 sd_bus_message
*reply
,
999 bool require_fallback
,
1000 sd_bus_error
*error
) {
1002 const char *previous_interface
= NULL
;
1003 bool found_something
= false;
1004 struct node_vtable
*i
;
1014 n
= hashmap_get(bus
->nodes
, prefix
);
1018 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1021 if (require_fallback
&& !i
->is_fallback
)
1024 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1027 if (bus
->nodes_modified
)
1032 if (!found_something
) {
1034 /* Open the object part */
1036 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1040 r
= sd_bus_message_append(reply
, "o", path
);
1044 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1048 found_something
= true;
1051 if (!streq_ptr(previous_interface
, i
->interface
)) {
1053 /* Maybe close the previous interface part */
1055 if (previous_interface
) {
1056 r
= sd_bus_message_close_container(reply
);
1060 r
= sd_bus_message_close_container(reply
);
1065 /* Open the new interface part */
1067 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1071 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1075 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1080 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1083 if (bus
->nodes_modified
)
1086 previous_interface
= i
->interface
;
1089 if (previous_interface
) {
1090 r
= sd_bus_message_close_container(reply
);
1094 r
= sd_bus_message_close_container(reply
);
1099 if (found_something
) {
1100 r
= sd_bus_message_close_container(reply
);
1104 r
= sd_bus_message_close_container(reply
);
1112 static int object_manager_serialize_path_and_fallbacks(
1114 sd_bus_message
*reply
,
1116 sd_bus_error
*error
) {
1126 /* First, add all vtables registered for this path */
1127 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1130 if (bus
->nodes_modified
)
1133 /* Second, add fallback vtables registered for any of the prefixes */
1134 prefix
= alloca(strlen(path
) + 1);
1135 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1136 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1139 if (bus
->nodes_modified
)
1146 static int process_get_managed_objects(
1150 bool require_fallback
,
1151 bool *found_object
) {
1153 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1154 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1155 _cleanup_set_free_free_ Set
*s
= NULL
;
1163 assert(found_object
);
1165 /* Spec says, GetManagedObjects() is only implemented on the root of a
1166 * sub-tree. Therefore, we require a registered object-manager on
1167 * exactly the queried path, otherwise, we refuse to respond. */
1169 if (require_fallback
|| !n
->object_managers
)
1172 r
= get_child_nodes(bus
, m
->path
, n
, true, &s
, &error
);
1175 if (bus
->nodes_modified
)
1178 r
= sd_bus_message_new_method_return(m
, &reply
);
1182 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1186 SET_FOREACH(path
, s
, i
) {
1187 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1191 if (bus
->nodes_modified
)
1195 r
= sd_bus_message_close_container(reply
);
1199 r
= sd_bus_send(bus
, reply
, NULL
);
1206 static int object_find_and_run(
1210 bool require_fallback
,
1211 bool *found_object
) {
1214 struct vtable_member vtable_key
, *v
;
1220 assert(found_object
);
1222 n
= hashmap_get(bus
->nodes
, p
);
1226 /* First, try object callbacks */
1227 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1230 if (bus
->nodes_modified
)
1233 if (!m
->interface
|| !m
->member
)
1236 /* Then, look for a known method */
1237 vtable_key
.path
= (char*) p
;
1238 vtable_key
.interface
= m
->interface
;
1239 vtable_key
.member
= m
->member
;
1241 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1243 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1246 if (bus
->nodes_modified
)
1250 /* Then, look for a known property */
1251 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1254 get
= streq(m
->member
, "Get");
1256 if (get
|| streq(m
->member
, "Set")) {
1258 r
= sd_bus_message_rewind(m
, true);
1262 vtable_key
.path
= (char*) p
;
1264 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1266 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1268 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1270 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1275 } else if (streq(m
->member
, "GetAll")) {
1278 r
= sd_bus_message_rewind(m
, true);
1282 r
= sd_bus_message_read(m
, "s", &iface
);
1284 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1289 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1294 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1296 if (!isempty(sd_bus_message_get_signature(m
, true)))
1297 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1299 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1303 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1305 if (!isempty(sd_bus_message_get_signature(m
, true)))
1306 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1308 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1313 if (bus
->nodes_modified
)
1316 if (!*found_object
) {
1317 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1320 if (bus
->nodes_modified
)
1323 *found_object
= true;
1329 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1332 bool found_object
= false;
1337 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1340 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1343 if (hashmap_isempty(bus
->nodes
))
1346 /* Never respond to broadcast messages */
1347 if (bus
->bus_client
&& !m
->destination
)
1353 pl
= strlen(m
->path
);
1357 bus
->nodes_modified
= false;
1359 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1363 /* Look for fallback prefixes */
1364 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1366 if (bus
->nodes_modified
)
1369 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1374 } while (bus
->nodes_modified
);
1379 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1380 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1381 r
= sd_bus_reply_method_errorf(
1383 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1384 "Unknown property or interface.");
1386 r
= sd_bus_reply_method_errorf(
1388 SD_BUS_ERROR_UNKNOWN_METHOD
,
1389 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1397 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1398 struct node
*n
, *parent
;
1400 _cleanup_free_
char *s
= NULL
;
1406 assert(path
[0] == '/');
1408 n
= hashmap_get(bus
->nodes
, path
);
1412 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1420 if (streq(path
, "/"))
1423 e
= strrchr(path
, '/');
1426 p
= strndupa(path
, MAX(1, e
- path
));
1428 parent
= bus_node_allocate(bus
, p
);
1433 n
= new0(struct node
, 1);
1439 s
= NULL
; /* do not free */
1441 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1449 LIST_PREPEND(siblings
, parent
->child
, n
);
1454 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1467 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1470 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1473 bus_node_gc(b
, n
->parent
);
1477 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1483 n
= hashmap_get(bus
->nodes
, path
);
1487 prefix
= alloca(strlen(path
) + 1);
1488 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1489 n
= hashmap_get(bus
->nodes
, prefix
);
1495 while (n
&& !n
->object_managers
)
1503 static int bus_add_object(
1508 sd_bus_message_handler_t callback
,
1515 assert_return(bus
, -EINVAL
);
1516 assert_return(object_path_is_valid(path
), -EINVAL
);
1517 assert_return(callback
, -EINVAL
);
1518 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1520 n
= bus_node_allocate(bus
, path
);
1524 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1530 s
->node_callback
.callback
= callback
;
1531 s
->node_callback
.is_fallback
= fallback
;
1533 s
->node_callback
.node
= n
;
1534 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1535 bus
->nodes_modified
= true;
1543 sd_bus_slot_unref(s
);
1544 bus_node_gc(bus
, n
);
1549 _public_
int sd_bus_add_object(
1553 sd_bus_message_handler_t callback
,
1556 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1559 _public_
int sd_bus_add_fallback(
1563 sd_bus_message_handler_t callback
,
1566 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1569 static unsigned long vtable_member_hash_func(const void *a
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
1570 const struct vtable_member
*m
= a
;
1571 uint8_t hash_key2
[HASH_KEY_SIZE
];
1576 ret
= string_hash_func(m
->path
, hash_key
);
1578 /* Use a slightly different hash key for the interface */
1579 memcpy(hash_key2
, hash_key
, HASH_KEY_SIZE
);
1581 ret
^= string_hash_func(m
->interface
, hash_key2
);
1583 /* And an even different one for the member */
1585 ret
^= string_hash_func(m
->member
, hash_key2
);
1590 static int vtable_member_compare_func(const void *a
, const void *b
) {
1591 const struct vtable_member
*x
= a
, *y
= b
;
1597 r
= strcmp(x
->path
, y
->path
);
1601 r
= strcmp(x
->interface
, y
->interface
);
1605 return strcmp(x
->member
, y
->member
);
1608 static const struct hash_ops vtable_member_hash_ops
= {
1609 .hash
= vtable_member_hash_func
,
1610 .compare
= vtable_member_compare_func
1613 static int add_object_vtable_internal(
1617 const char *interface
,
1618 const sd_bus_vtable
*vtable
,
1620 sd_bus_object_find_t find
,
1623 sd_bus_slot
*s
= NULL
;
1624 struct node_vtable
*i
, *existing
= NULL
;
1625 const sd_bus_vtable
*v
;
1629 assert_return(bus
, -EINVAL
);
1630 assert_return(object_path_is_valid(path
), -EINVAL
);
1631 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1632 assert_return(vtable
, -EINVAL
);
1633 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1634 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1635 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1636 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1637 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1638 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1639 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1641 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1645 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1649 n
= bus_node_allocate(bus
, path
);
1653 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1654 if (i
->is_fallback
!= fallback
) {
1659 if (streq(i
->interface
, interface
)) {
1661 if (i
->vtable
== vtable
) {
1670 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1676 s
->node_vtable
.is_fallback
= fallback
;
1677 s
->node_vtable
.vtable
= vtable
;
1678 s
->node_vtable
.find
= find
;
1680 s
->node_vtable
.interface
= strdup(interface
);
1681 if (!s
->node_vtable
.interface
) {
1686 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1690 case _SD_BUS_VTABLE_METHOD
: {
1691 struct vtable_member
*m
;
1693 if (!member_name_is_valid(v
->x
.method
.member
) ||
1694 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1695 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1696 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1697 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1702 m
= new0(struct vtable_member
, 1);
1708 m
->parent
= &s
->node_vtable
;
1710 m
->interface
= s
->node_vtable
.interface
;
1711 m
->member
= v
->x
.method
.member
;
1714 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1723 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1725 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1730 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1737 case _SD_BUS_VTABLE_PROPERTY
: {
1738 struct vtable_member
*m
;
1740 if (!member_name_is_valid(v
->x
.property
.member
) ||
1741 !signature_is_single(v
->x
.property
.signature
, false) ||
1742 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1743 v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
||
1744 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1745 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1750 m
= new0(struct vtable_member
, 1);
1756 m
->parent
= &s
->node_vtable
;
1758 m
->interface
= s
->node_vtable
.interface
;
1759 m
->member
= v
->x
.property
.member
;
1762 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1771 case _SD_BUS_VTABLE_SIGNAL
:
1773 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1774 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1775 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1788 s
->node_vtable
.node
= n
;
1789 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1790 bus
->nodes_modified
= true;
1798 sd_bus_slot_unref(s
);
1799 bus_node_gc(bus
, n
);
1804 _public_
int sd_bus_add_object_vtable(
1808 const char *interface
,
1809 const sd_bus_vtable
*vtable
,
1812 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1815 _public_
int sd_bus_add_fallback_vtable(
1819 const char *interface
,
1820 const sd_bus_vtable
*vtable
,
1821 sd_bus_object_find_t find
,
1824 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1827 _public_
int sd_bus_add_node_enumerator(
1831 sd_bus_node_enumerator_t callback
,
1838 assert_return(bus
, -EINVAL
);
1839 assert_return(object_path_is_valid(path
), -EINVAL
);
1840 assert_return(callback
, -EINVAL
);
1841 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1843 n
= bus_node_allocate(bus
, path
);
1847 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1853 s
->node_enumerator
.callback
= callback
;
1855 s
->node_enumerator
.node
= n
;
1856 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1857 bus
->nodes_modified
= true;
1865 sd_bus_slot_unref(s
);
1866 bus_node_gc(bus
, n
);
1871 static int emit_properties_changed_on_interface(
1875 const char *interface
,
1876 bool require_fallback
,
1877 bool *found_interface
,
1880 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1881 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1882 bool has_invalidating
= false, has_changing
= false;
1883 struct vtable_member key
= {};
1884 struct node_vtable
*c
;
1894 assert(found_interface
);
1896 n
= hashmap_get(bus
->nodes
, prefix
);
1900 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1904 r
= sd_bus_message_append(m
, "s", interface
);
1908 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1913 key
.interface
= interface
;
1915 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1916 if (require_fallback
&& !c
->is_fallback
)
1919 if (!streq(c
->interface
, interface
))
1922 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1925 if (bus
->nodes_modified
)
1930 *found_interface
= true;
1933 /* If the caller specified a list of
1934 * properties we include exactly those in the
1935 * PropertiesChanged message */
1937 STRV_FOREACH(property
, names
) {
1938 struct vtable_member
*v
;
1940 assert_return(member_name_is_valid(*property
), -EINVAL
);
1942 key
.member
= *property
;
1943 v
= hashmap_get(bus
->vtable_properties
, &key
);
1947 /* If there are two vtables for the same
1948 * interface, let's handle this property when
1949 * we come to that vtable. */
1953 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1954 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1956 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1958 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1959 has_invalidating
= true;
1963 has_changing
= true;
1965 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1968 if (bus
->nodes_modified
)
1972 const sd_bus_vtable
*v
;
1974 /* If the caller specified no properties list
1975 * we include all properties that are marked
1976 * as changing in the message. */
1978 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1979 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
1982 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
1985 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1986 has_invalidating
= true;
1990 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
1993 has_changing
= true;
1995 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
1998 if (bus
->nodes_modified
)
2004 if (!has_invalidating
&& !has_changing
)
2007 r
= sd_bus_message_close_container(m
);
2011 r
= sd_bus_message_open_container(m
, 'a', "s");
2015 if (has_invalidating
) {
2016 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2017 if (require_fallback
&& !c
->is_fallback
)
2020 if (!streq(c
->interface
, interface
))
2023 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2026 if (bus
->nodes_modified
)
2032 STRV_FOREACH(property
, names
) {
2033 struct vtable_member
*v
;
2035 key
.member
= *property
;
2036 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2037 assert(c
== v
->parent
);
2039 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2042 r
= sd_bus_message_append(m
, "s", *property
);
2047 const sd_bus_vtable
*v
;
2049 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2050 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
2053 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2056 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2059 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2067 r
= sd_bus_message_close_container(m
);
2071 r
= sd_bus_send(bus
, m
, NULL
);
2078 _public_
int sd_bus_emit_properties_changed_strv(
2081 const char *interface
,
2084 BUS_DONT_DESTROY(bus
);
2085 bool found_interface
= false;
2089 assert_return(bus
, -EINVAL
);
2090 assert_return(object_path_is_valid(path
), -EINVAL
);
2091 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2092 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2094 if (!BUS_IS_OPEN(bus
->state
))
2097 /* A non-NULL but empty names list means nothing needs to be
2098 generated. A NULL list OTOH indicates that all properties
2099 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2100 included in the PropertiesChanged message. */
2101 if (names
&& names
[0] == NULL
)
2105 bus
->nodes_modified
= false;
2107 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2110 if (bus
->nodes_modified
)
2113 prefix
= alloca(strlen(path
) + 1);
2114 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2115 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2118 if (bus
->nodes_modified
)
2122 } while (bus
->nodes_modified
);
2124 return found_interface
? 0 : -ENOENT
;
2127 _public_
int sd_bus_emit_properties_changed(
2130 const char *interface
,
2131 const char *name
, ...) {
2135 assert_return(bus
, -EINVAL
);
2136 assert_return(object_path_is_valid(path
), -EINVAL
);
2137 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2138 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2140 if (!BUS_IS_OPEN(bus
->state
))
2146 names
= strv_from_stdarg_alloca(name
);
2148 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2151 static int object_added_append_all_prefix(
2157 bool require_fallback
) {
2159 const char *previous_interface
= NULL
;
2160 struct node_vtable
*c
;
2170 n
= hashmap_get(bus
->nodes
, prefix
);
2174 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2175 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2178 if (require_fallback
&& !c
->is_fallback
)
2181 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2184 if (bus
->nodes_modified
)
2189 if (!streq_ptr(c
->interface
, previous_interface
)) {
2190 /* If a child-node already handled this interface, we
2191 * skip it on any of its parents. The child vtables
2192 * always fully override any conflicting vtables of
2193 * any parent node. */
2194 if (set_get(s
, c
->interface
))
2197 r
= set_put(s
, c
->interface
);
2201 if (previous_interface
) {
2202 r
= sd_bus_message_close_container(m
);
2205 r
= sd_bus_message_close_container(m
);
2210 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2213 r
= sd_bus_message_append(m
, "s", c
->interface
);
2216 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2220 previous_interface
= c
->interface
;
2223 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2226 if (bus
->nodes_modified
)
2230 if (previous_interface
) {
2231 r
= sd_bus_message_close_container(m
);
2234 r
= sd_bus_message_close_container(m
);
2242 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2243 _cleanup_set_free_ Set
*s
= NULL
;
2252 * This appends all interfaces registered on path @path. We first add
2253 * the builtin interfaces, which are always available and handled by
2254 * sd-bus. Then, we add all interfaces registered on the exact node,
2255 * followed by all fallback interfaces registered on any parent prefix.
2257 * If an interface is registered multiple times on the same node with
2258 * different vtables, we merge all the properties across all vtables.
2259 * However, if a child node has the same interface registered as one of
2260 * its parent nodes has as fallback, we make the child overwrite the
2261 * parent instead of extending it. Therefore, we keep a "Set" of all
2262 * handled interfaces during parent traversal, so we skip interfaces on
2263 * a parent that were overwritten by a child.
2266 s
= set_new(&string_hash_ops
);
2270 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2273 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2276 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2279 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2283 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2286 if (bus
->nodes_modified
)
2289 prefix
= alloca(strlen(path
) + 1);
2290 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2291 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2294 if (bus
->nodes_modified
)
2301 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2302 BUS_DONT_DESTROY(bus
);
2304 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2305 struct node
*object_manager
;
2309 * This emits an InterfacesAdded signal on the given path, by iterating
2310 * all registered vtables and fallback vtables on the path. All
2311 * properties are queried and included in the signal.
2312 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2313 * explicit list of registered interfaces. However, unlike
2314 * interfaces_added(), this call can figure out the list of supported
2315 * interfaces itself. Furthermore, it properly adds the builtin
2316 * org.freedesktop.DBus.* interfaces.
2319 assert_return(bus
, -EINVAL
);
2320 assert_return(object_path_is_valid(path
), -EINVAL
);
2321 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2323 if (!BUS_IS_OPEN(bus
->state
))
2326 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2333 bus
->nodes_modified
= false;
2334 m
= sd_bus_message_unref(m
);
2336 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2340 r
= sd_bus_message_append_basic(m
, 'o', path
);
2344 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2348 r
= object_added_append_all(bus
, m
, path
);
2352 if (bus
->nodes_modified
)
2355 r
= sd_bus_message_close_container(m
);
2359 } while (bus
->nodes_modified
);
2361 return sd_bus_send(bus
, m
, NULL
);
2364 static int object_removed_append_all_prefix(
2370 bool require_fallback
) {
2372 const char *previous_interface
= NULL
;
2373 struct node_vtable
*c
;
2383 n
= hashmap_get(bus
->nodes
, prefix
);
2387 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2388 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2391 if (require_fallback
&& !c
->is_fallback
)
2393 if (streq_ptr(c
->interface
, previous_interface
))
2396 /* If a child-node already handled this interface, we
2397 * skip it on any of its parents. The child vtables
2398 * always fully override any conflicting vtables of
2399 * any parent node. */
2400 if (set_get(s
, c
->interface
))
2403 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2406 if (bus
->nodes_modified
)
2411 r
= set_put(s
, c
->interface
);
2415 r
= sd_bus_message_append(m
, "s", c
->interface
);
2419 previous_interface
= c
->interface
;
2425 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2426 _cleanup_set_free_ Set
*s
= NULL
;
2434 /* see sd_bus_emit_object_added() for details */
2436 s
= set_new(&string_hash_ops
);
2440 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2443 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2446 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2449 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2453 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2456 if (bus
->nodes_modified
)
2459 prefix
= alloca(strlen(path
) + 1);
2460 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2461 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2464 if (bus
->nodes_modified
)
2471 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2472 BUS_DONT_DESTROY(bus
);
2474 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2475 struct node
*object_manager
;
2479 * This is like sd_bus_emit_object_added(), but emits an
2480 * InterfacesRemoved signal on the given path. This only includes any
2481 * registered interfaces but skips the properties. Note that this will
2482 * call into the find() callbacks of any registered vtable. Therefore,
2483 * you must call this function before destroying/unlinking your object.
2484 * Otherwise, the list of interfaces will be incomplete. However, note
2485 * that this will *NOT* call into any property callback. Therefore, the
2486 * object might be in an "destructed" state, as long as we can find it.
2489 assert_return(bus
, -EINVAL
);
2490 assert_return(object_path_is_valid(path
), -EINVAL
);
2491 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2493 if (!BUS_IS_OPEN(bus
->state
))
2496 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2503 bus
->nodes_modified
= false;
2504 m
= sd_bus_message_unref(m
);
2506 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2510 r
= sd_bus_message_append_basic(m
, 'o', path
);
2514 r
= sd_bus_message_open_container(m
, 'a', "s");
2518 r
= object_removed_append_all(bus
, m
, path
);
2522 if (bus
->nodes_modified
)
2525 r
= sd_bus_message_close_container(m
);
2529 } while (bus
->nodes_modified
);
2531 return sd_bus_send(bus
, m
, NULL
);
2534 static int interfaces_added_append_one_prefix(
2539 const char *interface
,
2540 bool require_fallback
) {
2542 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2543 bool found_interface
= false;
2544 struct node_vtable
*c
;
2555 n
= hashmap_get(bus
->nodes
, prefix
);
2559 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2560 if (require_fallback
&& !c
->is_fallback
)
2563 if (!streq(c
->interface
, interface
))
2566 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2569 if (bus
->nodes_modified
)
2574 if (!found_interface
) {
2575 r
= sd_bus_message_append_basic(m
, 's', interface
);
2579 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2583 found_interface
= true;
2586 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2589 if (bus
->nodes_modified
)
2593 if (found_interface
) {
2594 r
= sd_bus_message_close_container(m
);
2599 return found_interface
;
2602 static int interfaces_added_append_one(
2606 const char *interface
) {
2616 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2619 if (bus
->nodes_modified
)
2622 prefix
= alloca(strlen(path
) + 1);
2623 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2624 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2627 if (bus
->nodes_modified
)
2634 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2635 BUS_DONT_DESTROY(bus
);
2637 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2638 struct node
*object_manager
;
2642 assert_return(bus
, -EINVAL
);
2643 assert_return(object_path_is_valid(path
), -EINVAL
);
2644 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2646 if (!BUS_IS_OPEN(bus
->state
))
2649 if (strv_isempty(interfaces
))
2652 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2659 bus
->nodes_modified
= false;
2660 m
= sd_bus_message_unref(m
);
2662 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2666 r
= sd_bus_message_append_basic(m
, 'o', path
);
2670 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2674 STRV_FOREACH(i
, interfaces
) {
2675 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2677 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2681 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2685 if (bus
->nodes_modified
)
2688 r
= sd_bus_message_close_container(m
);
2693 if (bus
->nodes_modified
)
2696 r
= sd_bus_message_close_container(m
);
2700 } while (bus
->nodes_modified
);
2702 return sd_bus_send(bus
, m
, NULL
);
2705 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2708 assert_return(bus
, -EINVAL
);
2709 assert_return(object_path_is_valid(path
), -EINVAL
);
2710 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2712 if (!BUS_IS_OPEN(bus
->state
))
2715 interfaces
= strv_from_stdarg_alloca(interface
);
2717 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2720 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2721 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2722 struct node
*object_manager
;
2725 assert_return(bus
, -EINVAL
);
2726 assert_return(object_path_is_valid(path
), -EINVAL
);
2727 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2729 if (!BUS_IS_OPEN(bus
->state
))
2732 if (strv_isempty(interfaces
))
2735 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2741 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2745 r
= sd_bus_message_append_basic(m
, 'o', path
);
2749 r
= sd_bus_message_append_strv(m
, interfaces
);
2753 return sd_bus_send(bus
, m
, NULL
);
2756 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2759 assert_return(bus
, -EINVAL
);
2760 assert_return(object_path_is_valid(path
), -EINVAL
);
2761 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2763 if (!BUS_IS_OPEN(bus
->state
))
2766 interfaces
= strv_from_stdarg_alloca(interface
);
2768 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2771 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2776 assert_return(bus
, -EINVAL
);
2777 assert_return(object_path_is_valid(path
), -EINVAL
);
2778 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2780 n
= bus_node_allocate(bus
, path
);
2784 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2790 s
->node_object_manager
.node
= n
;
2791 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2792 bus
->nodes_modified
= true;
2800 sd_bus_slot_unref(s
);
2801 bus_node_gc(bus
, n
);