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
= set_put_strdup(s
, m
->path
);
1182 r
= sd_bus_message_new_method_return(m
, &reply
);
1186 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1190 SET_FOREACH(path
, s
, i
) {
1191 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1195 if (bus
->nodes_modified
)
1199 r
= sd_bus_message_close_container(reply
);
1203 r
= sd_bus_send(bus
, reply
, NULL
);
1210 static int object_find_and_run(
1214 bool require_fallback
,
1215 bool *found_object
) {
1218 struct vtable_member vtable_key
, *v
;
1224 assert(found_object
);
1226 n
= hashmap_get(bus
->nodes
, p
);
1230 /* First, try object callbacks */
1231 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1234 if (bus
->nodes_modified
)
1237 if (!m
->interface
|| !m
->member
)
1240 /* Then, look for a known method */
1241 vtable_key
.path
= (char*) p
;
1242 vtable_key
.interface
= m
->interface
;
1243 vtable_key
.member
= m
->member
;
1245 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1247 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1250 if (bus
->nodes_modified
)
1254 /* Then, look for a known property */
1255 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1258 get
= streq(m
->member
, "Get");
1260 if (get
|| streq(m
->member
, "Set")) {
1262 r
= sd_bus_message_rewind(m
, true);
1266 vtable_key
.path
= (char*) p
;
1268 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1270 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1272 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1274 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1279 } else if (streq(m
->member
, "GetAll")) {
1282 r
= sd_bus_message_rewind(m
, true);
1286 r
= sd_bus_message_read(m
, "s", &iface
);
1288 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1293 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1298 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1300 if (!isempty(sd_bus_message_get_signature(m
, true)))
1301 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1303 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1307 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1309 if (!isempty(sd_bus_message_get_signature(m
, true)))
1310 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1312 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1317 if (bus
->nodes_modified
)
1320 if (!*found_object
) {
1321 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1324 if (bus
->nodes_modified
)
1327 *found_object
= true;
1333 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1336 bool found_object
= false;
1341 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1344 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1347 if (hashmap_isempty(bus
->nodes
))
1350 /* Never respond to broadcast messages */
1351 if (bus
->bus_client
&& !m
->destination
)
1357 pl
= strlen(m
->path
);
1361 bus
->nodes_modified
= false;
1363 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1367 /* Look for fallback prefixes */
1368 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1370 if (bus
->nodes_modified
)
1373 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1378 } while (bus
->nodes_modified
);
1383 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1384 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1385 r
= sd_bus_reply_method_errorf(
1387 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1388 "Unknown property or interface.");
1390 r
= sd_bus_reply_method_errorf(
1392 SD_BUS_ERROR_UNKNOWN_METHOD
,
1393 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1401 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1402 struct node
*n
, *parent
;
1404 _cleanup_free_
char *s
= NULL
;
1410 assert(path
[0] == '/');
1412 n
= hashmap_get(bus
->nodes
, path
);
1416 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1424 if (streq(path
, "/"))
1427 e
= strrchr(path
, '/');
1430 p
= strndupa(path
, MAX(1, e
- path
));
1432 parent
= bus_node_allocate(bus
, p
);
1437 n
= new0(struct node
, 1);
1443 s
= NULL
; /* do not free */
1445 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1453 LIST_PREPEND(siblings
, parent
->child
, n
);
1458 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1471 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1474 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1477 bus_node_gc(b
, n
->parent
);
1481 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1487 n
= hashmap_get(bus
->nodes
, path
);
1491 prefix
= alloca(strlen(path
) + 1);
1492 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1493 n
= hashmap_get(bus
->nodes
, prefix
);
1499 while (n
&& !n
->object_managers
)
1507 static int bus_add_object(
1512 sd_bus_message_handler_t callback
,
1519 assert_return(bus
, -EINVAL
);
1520 assert_return(object_path_is_valid(path
), -EINVAL
);
1521 assert_return(callback
, -EINVAL
);
1522 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1524 n
= bus_node_allocate(bus
, path
);
1528 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1534 s
->node_callback
.callback
= callback
;
1535 s
->node_callback
.is_fallback
= fallback
;
1537 s
->node_callback
.node
= n
;
1538 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1539 bus
->nodes_modified
= true;
1547 sd_bus_slot_unref(s
);
1548 bus_node_gc(bus
, n
);
1553 _public_
int sd_bus_add_object(
1557 sd_bus_message_handler_t callback
,
1560 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1563 _public_
int sd_bus_add_fallback(
1567 sd_bus_message_handler_t callback
,
1570 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1573 static unsigned long vtable_member_hash_func(const void *a
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
1574 const struct vtable_member
*m
= a
;
1575 uint8_t hash_key2
[HASH_KEY_SIZE
];
1580 ret
= string_hash_func(m
->path
, hash_key
);
1582 /* Use a slightly different hash key for the interface */
1583 memcpy(hash_key2
, hash_key
, HASH_KEY_SIZE
);
1585 ret
^= string_hash_func(m
->interface
, hash_key2
);
1587 /* And an even different one for the member */
1589 ret
^= string_hash_func(m
->member
, hash_key2
);
1594 static int vtable_member_compare_func(const void *a
, const void *b
) {
1595 const struct vtable_member
*x
= a
, *y
= b
;
1601 r
= strcmp(x
->path
, y
->path
);
1605 r
= strcmp(x
->interface
, y
->interface
);
1609 return strcmp(x
->member
, y
->member
);
1612 static const struct hash_ops vtable_member_hash_ops
= {
1613 .hash
= vtable_member_hash_func
,
1614 .compare
= vtable_member_compare_func
1617 static int add_object_vtable_internal(
1621 const char *interface
,
1622 const sd_bus_vtable
*vtable
,
1624 sd_bus_object_find_t find
,
1627 sd_bus_slot
*s
= NULL
;
1628 struct node_vtable
*i
, *existing
= NULL
;
1629 const sd_bus_vtable
*v
;
1633 assert_return(bus
, -EINVAL
);
1634 assert_return(object_path_is_valid(path
), -EINVAL
);
1635 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1636 assert_return(vtable
, -EINVAL
);
1637 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1638 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1639 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1640 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1641 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1642 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1643 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1645 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1649 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1653 n
= bus_node_allocate(bus
, path
);
1657 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1658 if (i
->is_fallback
!= fallback
) {
1663 if (streq(i
->interface
, interface
)) {
1665 if (i
->vtable
== vtable
) {
1674 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1680 s
->node_vtable
.is_fallback
= fallback
;
1681 s
->node_vtable
.vtable
= vtable
;
1682 s
->node_vtable
.find
= find
;
1684 s
->node_vtable
.interface
= strdup(interface
);
1685 if (!s
->node_vtable
.interface
) {
1690 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1694 case _SD_BUS_VTABLE_METHOD
: {
1695 struct vtable_member
*m
;
1697 if (!member_name_is_valid(v
->x
.method
.member
) ||
1698 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1699 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1700 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1701 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1706 m
= new0(struct vtable_member
, 1);
1712 m
->parent
= &s
->node_vtable
;
1714 m
->interface
= s
->node_vtable
.interface
;
1715 m
->member
= v
->x
.method
.member
;
1718 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1727 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1729 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1734 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1741 case _SD_BUS_VTABLE_PROPERTY
: {
1742 struct vtable_member
*m
;
1744 if (!member_name_is_valid(v
->x
.property
.member
) ||
1745 !signature_is_single(v
->x
.property
.signature
, false) ||
1746 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1747 v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
||
1748 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
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
);