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"
31 #include "bus-objects.h"
33 static int node_vtable_get_userdata(
36 struct node_vtable
*c
,
38 sd_bus_error
*error
) {
48 s
= container_of(c
, sd_bus_slot
, node_vtable
);
51 bus
->current_slot
= sd_bus_slot_ref(s
);
52 bus
->current_userdata
= u
;
53 r
= c
->find(bus
, path
, c
->interface
, u
, &u
, error
);
54 bus
->current_userdata
= NULL
;
55 bus
->current_slot
= sd_bus_slot_unref(s
);
59 if (sd_bus_error_is_set(error
))
60 return -sd_bus_error_get_errno(error
);
71 static void *vtable_property_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
74 return (uint8_t*) u
+ p
->x
.property
.offset
;
77 static int vtable_property_get_userdata(
80 struct vtable_member
*p
,
82 sd_bus_error
*error
) {
92 r
= node_vtable_get_userdata(bus
, path
, p
->parent
, &u
, error
);
95 if (bus
->nodes_modified
)
98 *userdata
= vtable_property_convert_userdata(p
->vtable
, u
);
102 static int add_enumerated_to_set(
105 struct node_enumerator
*first
,
107 sd_bus_error
*error
) {
109 struct node_enumerator
*c
;
116 LIST_FOREACH(enumerators
, c
, first
) {
117 char **children
= NULL
, **k
;
120 if (bus
->nodes_modified
)
123 slot
= container_of(c
, sd_bus_slot
, node_enumerator
);
125 bus
->current_slot
= sd_bus_slot_ref(slot
);
126 bus
->current_userdata
= slot
->userdata
;
127 r
= c
->callback(bus
, prefix
, slot
->userdata
, &children
, error
);
128 bus
->current_userdata
= NULL
;
129 bus
->current_slot
= sd_bus_slot_unref(slot
);
133 if (sd_bus_error_is_set(error
))
134 return -sd_bus_error_get_errno(error
);
136 STRV_FOREACH(k
, children
) {
142 if (!object_path_is_valid(*k
)){
148 if (!object_path_startswith(*k
, prefix
)) {
153 r
= set_consume(s
, *k
);
166 static int add_subtree_to_set(
171 sd_bus_error
*error
) {
181 r
= add_enumerated_to_set(bus
, prefix
, n
->enumerators
, s
, error
);
184 if (bus
->nodes_modified
)
187 LIST_FOREACH(siblings
, i
, n
->child
) {
190 if (!object_path_startswith(i
->path
, prefix
))
197 r
= set_consume(s
, t
);
198 if (r
< 0 && r
!= -EEXIST
)
201 r
= add_subtree_to_set(bus
, prefix
, i
, s
, error
);
204 if (bus
->nodes_modified
)
211 static int get_child_nodes(
216 sd_bus_error
*error
) {
226 s
= set_new(&string_hash_ops
);
230 r
= add_subtree_to_set(bus
, prefix
, n
, s
, error
);
240 static int node_callbacks_run(
243 struct node_callback
*first
,
244 bool require_fallback
,
245 bool *found_object
) {
247 struct node_callback
*c
;
252 assert(found_object
);
254 LIST_FOREACH(callbacks
, c
, first
) {
255 _cleanup_bus_error_free_ sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
258 if (bus
->nodes_modified
)
261 if (require_fallback
&& !c
->is_fallback
)
264 *found_object
= true;
266 if (c
->last_iteration
== bus
->iteration_counter
)
269 c
->last_iteration
= bus
->iteration_counter
;
271 r
= sd_bus_message_rewind(m
, true);
275 slot
= container_of(c
, sd_bus_slot
, node_callback
);
277 bus
->current_slot
= sd_bus_slot_ref(slot
);
278 bus
->current_handler
= c
->callback
;
279 bus
->current_userdata
= slot
->userdata
;
280 r
= c
->callback(m
, slot
->userdata
, &error_buffer
);
281 bus
->current_userdata
= NULL
;
282 bus
->current_handler
= NULL
;
283 bus
->current_slot
= sd_bus_slot_unref(slot
);
285 r
= bus_maybe_reply_error(m
, r
, &error_buffer
);
293 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
295 static int check_access(sd_bus
*bus
, sd_bus_message
*m
, struct vtable_member
*c
, sd_bus_error
*error
) {
303 /* If the entire bus is trusted let's grant access */
307 /* If the member is marked UNPRIVILEGED let's grant access */
308 if (c
->vtable
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
)
311 /* Check have the caller has the requested capability
312 * set. Note that the flags value contains the capability
313 * number plus one, which we need to subtract here. We do this
314 * so that we have 0 as special value for "default
316 cap
= CAPABILITY_SHIFT(c
->vtable
->flags
);
318 cap
= CAPABILITY_SHIFT(c
->parent
->vtable
[0].flags
);
324 r
= sd_bus_query_sender_privilege(m
, cap
);
330 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Access to %s.%s() not permitted.", c
->interface
, c
->member
);
333 static int method_callbacks_run(
336 struct vtable_member
*c
,
337 bool require_fallback
,
338 bool *found_object
) {
340 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
341 const char *signature
;
348 assert(found_object
);
350 if (require_fallback
&& !c
->parent
->is_fallback
)
353 r
= check_access(bus
, m
, c
, &error
);
355 return bus_maybe_reply_error(m
, r
, &error
);
357 r
= node_vtable_get_userdata(bus
, m
->path
, c
->parent
, &u
, &error
);
359 return bus_maybe_reply_error(m
, r
, &error
);
360 if (bus
->nodes_modified
)
363 *found_object
= true;
365 if (c
->last_iteration
== bus
->iteration_counter
)
368 c
->last_iteration
= bus
->iteration_counter
;
370 r
= sd_bus_message_rewind(m
, true);
374 signature
= sd_bus_message_get_signature(m
, true);
378 if (!streq(strempty(c
->vtable
->x
.method
.signature
), signature
))
379 return sd_bus_reply_method_errorf(
381 SD_BUS_ERROR_INVALID_ARGS
,
382 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
383 signature
, c
->interface
, c
->member
, strempty(c
->vtable
->x
.method
.signature
));
385 /* Keep track what the signature of the reply to this message
386 * should be, so that this can be enforced when sealing the
388 m
->enforced_reply_signature
= strempty(c
->vtable
->x
.method
.result
);
390 if (c
->vtable
->x
.method
.handler
) {
393 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
395 bus
->current_slot
= sd_bus_slot_ref(slot
);
396 bus
->current_handler
= c
->vtable
->x
.method
.handler
;
397 bus
->current_userdata
= u
;
398 r
= c
->vtable
->x
.method
.handler(m
, u
, &error
);
399 bus
->current_userdata
= NULL
;
400 bus
->current_handler
= NULL
;
401 bus
->current_slot
= sd_bus_slot_unref(slot
);
403 return bus_maybe_reply_error(m
, r
, &error
);
406 /* If the method callback is NULL, make this a successful NOP */
407 r
= sd_bus_reply_method_return(m
, NULL
);
414 static int invoke_property_get(
417 const sd_bus_vtable
*v
,
419 const char *interface
,
420 const char *property
,
421 sd_bus_message
*reply
,
423 sd_bus_error
*error
) {
436 if (v
->x
.property
.get
) {
438 bus
->current_slot
= sd_bus_slot_ref(slot
);
439 bus
->current_userdata
= userdata
;
440 r
= v
->x
.property
.get(bus
, path
, interface
, property
, reply
, userdata
, error
);
441 bus
->current_userdata
= NULL
;
442 bus
->current_slot
= sd_bus_slot_unref(slot
);
446 if (sd_bus_error_is_set(error
))
447 return -sd_bus_error_get_errno(error
);
451 /* Automatic handling if no callback is defined. */
453 if (streq(v
->x
.property
.signature
, "as"))
454 return sd_bus_message_append_strv(reply
, *(char***) userdata
);
456 assert(signature_is_single(v
->x
.property
.signature
, false));
457 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
459 switch (v
->x
.property
.signature
[0]) {
461 case SD_BUS_TYPE_STRING
:
462 case SD_BUS_TYPE_SIGNATURE
:
463 p
= strempty(*(char**) userdata
);
466 case SD_BUS_TYPE_OBJECT_PATH
:
467 p
= *(char**) userdata
;
476 return sd_bus_message_append_basic(reply
, v
->x
.property
.signature
[0], p
);
479 static int invoke_property_set(
482 const sd_bus_vtable
*v
,
484 const char *interface
,
485 const char *property
,
486 sd_bus_message
*value
,
488 sd_bus_error
*error
) {
500 if (v
->x
.property
.set
) {
502 bus
->current_slot
= sd_bus_slot_ref(slot
);
503 bus
->current_userdata
= userdata
;
504 r
= v
->x
.property
.set(bus
, path
, interface
, property
, value
, userdata
, error
);
505 bus
->current_userdata
= NULL
;
506 bus
->current_slot
= sd_bus_slot_unref(slot
);
510 if (sd_bus_error_is_set(error
))
511 return -sd_bus_error_get_errno(error
);
515 /* Automatic handling if no callback is defined. */
517 assert(signature_is_single(v
->x
.property
.signature
, false));
518 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
520 switch (v
->x
.property
.signature
[0]) {
522 case SD_BUS_TYPE_STRING
:
523 case SD_BUS_TYPE_OBJECT_PATH
:
524 case SD_BUS_TYPE_SIGNATURE
: {
528 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], &p
);
536 free(*(char**) userdata
);
537 *(char**) userdata
= n
;
543 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], userdata
);
553 static int property_get_set_callbacks_run(
556 struct vtable_member
*c
,
557 bool require_fallback
,
559 bool *found_object
) {
561 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
562 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
570 assert(found_object
);
572 if (require_fallback
&& !c
->parent
->is_fallback
)
575 r
= vtable_property_get_userdata(bus
, m
->path
, c
, &u
, &error
);
577 return bus_maybe_reply_error(m
, r
, &error
);
578 if (bus
->nodes_modified
)
581 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
583 *found_object
= true;
585 r
= sd_bus_message_new_method_return(m
, &reply
);
590 /* Note that we do not protect against reexecution
591 * here (using the last_iteration check, see below),
592 * should the node tree have changed and we got called
593 * again. We assume that property Get() calls are
594 * ultimately without side-effects or if they aren't
595 * then at least idempotent. */
597 r
= sd_bus_message_open_container(reply
, 'v', c
->vtable
->x
.property
.signature
);
601 /* Note that we do not do an access check here. Read
602 * access to properties is always unrestricted, since
603 * PropertiesChanged signals broadcast contents
606 r
= invoke_property_get(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, reply
, u
, &error
);
608 return bus_maybe_reply_error(m
, r
, &error
);
610 if (bus
->nodes_modified
)
613 r
= sd_bus_message_close_container(reply
);
618 const char *signature
= NULL
;
621 if (c
->vtable
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
622 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_PROPERTY_READ_ONLY
, "Property '%s' is not writable.", c
->member
);
624 /* Avoid that we call the set routine more than once
625 * if the processing of this message got restarted
626 * because the node tree changed. */
627 if (c
->last_iteration
== bus
->iteration_counter
)
630 c
->last_iteration
= bus
->iteration_counter
;
632 r
= sd_bus_message_peek_type(m
, &type
, &signature
);
636 if (type
!= 'v' || !streq(strempty(signature
), strempty(c
->vtable
->x
.property
.signature
)))
637 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
));
639 r
= sd_bus_message_enter_container(m
, 'v', c
->vtable
->x
.property
.signature
);
643 r
= check_access(bus
, m
, c
, &error
);
645 return bus_maybe_reply_error(m
, r
, &error
);
647 r
= invoke_property_set(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, m
, u
, &error
);
649 return bus_maybe_reply_error(m
, r
, &error
);
651 if (bus
->nodes_modified
)
654 r
= sd_bus_message_exit_container(m
);
659 r
= sd_bus_send(bus
, reply
, NULL
);
666 static int vtable_append_one_property(
668 sd_bus_message
*reply
,
670 struct node_vtable
*c
,
671 const sd_bus_vtable
*v
,
673 sd_bus_error
*error
) {
684 r
= sd_bus_message_open_container(reply
, 'e', "sv");
688 r
= sd_bus_message_append(reply
, "s", v
->x
.property
.member
);
692 r
= sd_bus_message_open_container(reply
, 'v', v
->x
.property
.signature
);
696 slot
= container_of(c
, sd_bus_slot
, node_vtable
);
698 r
= invoke_property_get(bus
, slot
, v
, path
, c
->interface
, v
->x
.property
.member
, reply
, vtable_property_convert_userdata(v
, userdata
), error
);
701 if (bus
->nodes_modified
)
704 r
= sd_bus_message_close_container(reply
);
708 r
= sd_bus_message_close_container(reply
);
715 static int vtable_append_all_properties(
717 sd_bus_message
*reply
,
719 struct node_vtable
*c
,
721 sd_bus_error
*error
) {
723 const sd_bus_vtable
*v
;
731 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
734 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
735 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
738 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
741 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
744 if (bus
->nodes_modified
)
751 static int property_get_all_callbacks_run(
754 struct node_vtable
*first
,
755 bool require_fallback
,
757 bool *found_object
) {
759 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
760 struct node_vtable
*c
;
761 bool found_interface
;
766 assert(found_object
);
768 r
= sd_bus_message_new_method_return(m
, &reply
);
772 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
776 found_interface
= !iface
||
777 streq(iface
, "org.freedesktop.DBus.Properties") ||
778 streq(iface
, "org.freedesktop.DBus.Peer") ||
779 streq(iface
, "org.freedesktop.DBus.Introspectable");
781 LIST_FOREACH(vtables
, c
, first
) {
782 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
785 if (require_fallback
&& !c
->is_fallback
)
788 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
790 return bus_maybe_reply_error(m
, r
, &error
);
791 if (bus
->nodes_modified
)
796 *found_object
= true;
798 if (iface
&& !streq(c
->interface
, iface
))
800 found_interface
= true;
802 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
804 return bus_maybe_reply_error(m
, r
, &error
);
805 if (bus
->nodes_modified
)
809 if (!found_interface
) {
810 r
= sd_bus_reply_method_errorf(
812 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
813 "Unknown interface '%s'.", iface
);
820 r
= sd_bus_message_close_container(reply
);
824 r
= sd_bus_send(bus
, reply
, NULL
);
831 static int bus_node_exists(
835 bool require_fallback
) {
837 struct node_vtable
*c
;
838 struct node_callback
*k
;
845 /* Tests if there's anything attached directly to this node
846 * for the specified path */
848 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
851 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
852 if (require_fallback
&& !k
->is_fallback
)
858 LIST_FOREACH(vtables
, c
, n
->vtables
) {
859 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
861 if (require_fallback
&& !c
->is_fallback
)
864 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
867 if (bus
->nodes_modified
)
874 static int process_introspect(
878 bool require_fallback
,
879 bool *found_object
) {
881 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
882 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
883 _cleanup_set_free_free_ Set
*s
= NULL
;
884 const char *previous_interface
= NULL
;
885 struct introspect intro
;
886 struct node_vtable
*c
;
893 assert(found_object
);
895 r
= get_child_nodes(bus
, m
->path
, n
, &s
, &error
);
897 return bus_maybe_reply_error(m
, r
, &error
);
898 if (bus
->nodes_modified
)
901 r
= introspect_begin(&intro
, bus
->trusted
);
905 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
909 empty
= set_isempty(s
);
911 LIST_FOREACH(vtables
, c
, n
->vtables
) {
912 if (require_fallback
&& !c
->is_fallback
)
915 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
917 r
= bus_maybe_reply_error(m
, r
, &error
);
920 if (bus
->nodes_modified
) {
929 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
932 if (!streq_ptr(previous_interface
, c
->interface
)) {
934 if (previous_interface
)
935 fputs(" </interface>\n", intro
.f
);
937 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
940 r
= introspect_write_interface(&intro
, c
->vtable
);
944 previous_interface
= c
->interface
;
947 if (previous_interface
)
948 fputs(" </interface>\n", intro
.f
);
951 /* Nothing?, let's see if we exist at all, and if not
952 * refuse to do anything */
953 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
956 if (bus
->nodes_modified
) {
962 *found_object
= true;
964 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
968 r
= introspect_finish(&intro
, bus
, m
, &reply
);
972 r
= sd_bus_send(bus
, reply
, NULL
);
979 introspect_free(&intro
);
983 static int object_manager_serialize_path(
985 sd_bus_message
*reply
,
988 bool require_fallback
,
989 sd_bus_error
*error
) {
991 const char *previous_interface
= NULL
;
992 bool found_something
= false;
993 struct node_vtable
*i
;
1003 n
= hashmap_get(bus
->nodes
, prefix
);
1007 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1010 if (require_fallback
&& !i
->is_fallback
)
1013 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1016 if (bus
->nodes_modified
)
1021 if (!found_something
) {
1023 /* Open the object part */
1025 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1029 r
= sd_bus_message_append(reply
, "o", path
);
1033 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1037 found_something
= true;
1040 if (!streq_ptr(previous_interface
, i
->interface
)) {
1042 /* Maybe close the previous interface part */
1044 if (previous_interface
) {
1045 r
= sd_bus_message_close_container(reply
);
1049 r
= sd_bus_message_close_container(reply
);
1054 /* Open the new interface part */
1056 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1060 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1064 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1069 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1072 if (bus
->nodes_modified
)
1075 previous_interface
= i
->interface
;
1078 if (previous_interface
) {
1079 r
= sd_bus_message_close_container(reply
);
1083 r
= sd_bus_message_close_container(reply
);
1088 if (found_something
) {
1089 r
= sd_bus_message_close_container(reply
);
1093 r
= sd_bus_message_close_container(reply
);
1101 static int object_manager_serialize_path_and_fallbacks(
1103 sd_bus_message
*reply
,
1105 sd_bus_error
*error
) {
1115 /* First, add all vtables registered for this path */
1116 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1119 if (bus
->nodes_modified
)
1122 /* Second, add fallback vtables registered for any of the prefixes */
1123 prefix
= alloca(strlen(path
) + 1);
1124 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1125 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1128 if (bus
->nodes_modified
)
1135 static int process_get_managed_objects(
1139 bool require_fallback
,
1140 bool *found_object
) {
1142 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1143 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1144 _cleanup_set_free_free_ Set
*s
= NULL
;
1152 assert(found_object
);
1154 /* Spec says, GetManagedObjects() is only implemented on the root of a
1155 * sub-tree. Therefore, we require a registered object-manager on
1156 * exactly the queried path, otherwise, we refuse to respond. */
1158 if (require_fallback
|| !n
->object_managers
)
1161 r
= get_child_nodes(bus
, m
->path
, n
, &s
, &error
);
1164 if (bus
->nodes_modified
)
1167 r
= set_put_strdup(s
, m
->path
);
1171 r
= sd_bus_message_new_method_return(m
, &reply
);
1175 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1179 SET_FOREACH(path
, s
, i
) {
1180 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1184 if (bus
->nodes_modified
)
1188 r
= sd_bus_message_close_container(reply
);
1192 r
= sd_bus_send(bus
, reply
, NULL
);
1199 static int object_find_and_run(
1203 bool require_fallback
,
1204 bool *found_object
) {
1207 struct vtable_member vtable_key
, *v
;
1213 assert(found_object
);
1215 n
= hashmap_get(bus
->nodes
, p
);
1219 /* First, try object callbacks */
1220 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1223 if (bus
->nodes_modified
)
1226 if (!m
->interface
|| !m
->member
)
1229 /* Then, look for a known method */
1230 vtable_key
.path
= (char*) p
;
1231 vtable_key
.interface
= m
->interface
;
1232 vtable_key
.member
= m
->member
;
1234 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1236 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1239 if (bus
->nodes_modified
)
1243 /* Then, look for a known property */
1244 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1247 get
= streq(m
->member
, "Get");
1249 if (get
|| streq(m
->member
, "Set")) {
1251 r
= sd_bus_message_rewind(m
, true);
1255 vtable_key
.path
= (char*) p
;
1257 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1259 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1261 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1263 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1268 } else if (streq(m
->member
, "GetAll")) {
1271 r
= sd_bus_message_rewind(m
, true);
1275 r
= sd_bus_message_read(m
, "s", &iface
);
1277 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1282 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1287 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1289 if (!isempty(sd_bus_message_get_signature(m
, true)))
1290 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1292 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1296 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1298 if (!isempty(sd_bus_message_get_signature(m
, true)))
1299 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1301 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1306 if (bus
->nodes_modified
)
1309 if (!*found_object
) {
1310 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1313 if (bus
->nodes_modified
)
1316 *found_object
= true;
1322 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1325 bool found_object
= false;
1330 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1333 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1336 if (hashmap_isempty(bus
->nodes
))
1339 /* Never respond to broadcast messages */
1340 if (bus
->bus_client
&& !m
->destination
)
1346 pl
= strlen(m
->path
);
1350 bus
->nodes_modified
= false;
1352 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1356 /* Look for fallback prefixes */
1357 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1359 if (bus
->nodes_modified
)
1362 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1367 } while (bus
->nodes_modified
);
1372 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1373 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1374 r
= sd_bus_reply_method_errorf(
1376 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1377 "Unknown property or interface.");
1379 r
= sd_bus_reply_method_errorf(
1381 SD_BUS_ERROR_UNKNOWN_METHOD
,
1382 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1390 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1391 struct node
*n
, *parent
;
1393 _cleanup_free_
char *s
= NULL
;
1399 assert(path
[0] == '/');
1401 n
= hashmap_get(bus
->nodes
, path
);
1405 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1413 if (streq(path
, "/"))
1416 e
= strrchr(path
, '/');
1419 p
= strndupa(path
, MAX(1, e
- path
));
1421 parent
= bus_node_allocate(bus
, p
);
1426 n
= new0(struct node
, 1);
1432 s
= NULL
; /* do not free */
1434 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1442 LIST_PREPEND(siblings
, parent
->child
, n
);
1447 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1460 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1463 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1466 bus_node_gc(b
, n
->parent
);
1470 static int bus_add_object(
1475 sd_bus_message_handler_t callback
,
1482 assert_return(bus
, -EINVAL
);
1483 assert_return(object_path_is_valid(path
), -EINVAL
);
1484 assert_return(callback
, -EINVAL
);
1485 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1487 n
= bus_node_allocate(bus
, path
);
1491 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1497 s
->node_callback
.callback
= callback
;
1498 s
->node_callback
.is_fallback
= fallback
;
1500 s
->node_callback
.node
= n
;
1501 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1502 bus
->nodes_modified
= true;
1510 sd_bus_slot_unref(s
);
1511 bus_node_gc(bus
, n
);
1516 _public_
int sd_bus_add_object(
1520 sd_bus_message_handler_t callback
,
1523 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1526 _public_
int sd_bus_add_fallback(
1530 sd_bus_message_handler_t callback
,
1533 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1536 static unsigned long vtable_member_hash_func(const void *a
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
1537 const struct vtable_member
*m
= a
;
1538 uint8_t hash_key2
[HASH_KEY_SIZE
];
1543 ret
= string_hash_func(m
->path
, hash_key
);
1545 /* Use a slightly different hash key for the interface */
1546 memcpy(hash_key2
, hash_key
, HASH_KEY_SIZE
);
1548 ret
^= string_hash_func(m
->interface
, hash_key2
);
1550 /* And an even different one for the member */
1552 ret
^= string_hash_func(m
->member
, hash_key2
);
1557 static int vtable_member_compare_func(const void *a
, const void *b
) {
1558 const struct vtable_member
*x
= a
, *y
= b
;
1564 r
= strcmp(x
->path
, y
->path
);
1568 r
= strcmp(x
->interface
, y
->interface
);
1572 return strcmp(x
->member
, y
->member
);
1575 static const struct hash_ops vtable_member_hash_ops
= {
1576 .hash
= vtable_member_hash_func
,
1577 .compare
= vtable_member_compare_func
1580 static int add_object_vtable_internal(
1584 const char *interface
,
1585 const sd_bus_vtable
*vtable
,
1587 sd_bus_object_find_t find
,
1590 sd_bus_slot
*s
= NULL
;
1591 struct node_vtable
*i
, *existing
= NULL
;
1592 const sd_bus_vtable
*v
;
1596 assert_return(bus
, -EINVAL
);
1597 assert_return(object_path_is_valid(path
), -EINVAL
);
1598 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1599 assert_return(vtable
, -EINVAL
);
1600 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1601 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1602 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1603 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1604 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1605 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1606 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1608 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1612 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1616 n
= bus_node_allocate(bus
, path
);
1620 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1621 if (i
->is_fallback
!= fallback
) {
1626 if (streq(i
->interface
, interface
)) {
1628 if (i
->vtable
== vtable
) {
1637 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1643 s
->node_vtable
.is_fallback
= fallback
;
1644 s
->node_vtable
.vtable
= vtable
;
1645 s
->node_vtable
.find
= find
;
1647 s
->node_vtable
.interface
= strdup(interface
);
1648 if (!s
->node_vtable
.interface
) {
1653 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1657 case _SD_BUS_VTABLE_METHOD
: {
1658 struct vtable_member
*m
;
1660 if (!member_name_is_valid(v
->x
.method
.member
) ||
1661 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1662 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1663 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1664 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1669 m
= new0(struct vtable_member
, 1);
1675 m
->parent
= &s
->node_vtable
;
1677 m
->interface
= s
->node_vtable
.interface
;
1678 m
->member
= v
->x
.method
.member
;
1681 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1690 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1692 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1697 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1704 case _SD_BUS_VTABLE_PROPERTY
: {
1705 struct vtable_member
*m
;
1707 if (!member_name_is_valid(v
->x
.property
.member
) ||
1708 !signature_is_single(v
->x
.property
.signature
, false) ||
1709 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1710 v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
||
1711 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1712 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1717 m
= new0(struct vtable_member
, 1);
1723 m
->parent
= &s
->node_vtable
;
1725 m
->interface
= s
->node_vtable
.interface
;
1726 m
->member
= v
->x
.property
.member
;
1729 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1738 case _SD_BUS_VTABLE_SIGNAL
:
1740 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1741 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1742 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1755 s
->node_vtable
.node
= n
;
1756 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1757 bus
->nodes_modified
= true;
1765 sd_bus_slot_unref(s
);
1766 bus_node_gc(bus
, n
);
1771 _public_
int sd_bus_add_object_vtable(
1775 const char *interface
,
1776 const sd_bus_vtable
*vtable
,
1779 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1782 _public_
int sd_bus_add_fallback_vtable(
1786 const char *interface
,
1787 const sd_bus_vtable
*vtable
,
1788 sd_bus_object_find_t find
,
1791 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1794 _public_
int sd_bus_add_node_enumerator(
1798 sd_bus_node_enumerator_t callback
,
1805 assert_return(bus
, -EINVAL
);
1806 assert_return(object_path_is_valid(path
), -EINVAL
);
1807 assert_return(callback
, -EINVAL
);
1808 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1810 n
= bus_node_allocate(bus
, path
);
1814 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1820 s
->node_enumerator
.callback
= callback
;
1822 s
->node_enumerator
.node
= n
;
1823 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1824 bus
->nodes_modified
= true;
1832 sd_bus_slot_unref(s
);
1833 bus_node_gc(bus
, n
);
1838 static int emit_properties_changed_on_interface(
1842 const char *interface
,
1843 bool require_fallback
,
1844 bool *found_interface
,
1847 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1848 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1849 bool has_invalidating
= false, has_changing
= false;
1850 struct vtable_member key
= {};
1851 struct node_vtable
*c
;
1861 assert(found_interface
);
1863 n
= hashmap_get(bus
->nodes
, prefix
);
1867 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1871 r
= sd_bus_message_append(m
, "s", interface
);
1875 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1880 key
.interface
= interface
;
1882 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1883 if (require_fallback
&& !c
->is_fallback
)
1886 if (!streq(c
->interface
, interface
))
1889 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1892 if (bus
->nodes_modified
)
1897 *found_interface
= true;
1900 /* If the caller specified a list of
1901 * properties we include exactly those in the
1902 * PropertiesChanged message */
1904 STRV_FOREACH(property
, names
) {
1905 struct vtable_member
*v
;
1907 assert_return(member_name_is_valid(*property
), -EINVAL
);
1909 key
.member
= *property
;
1910 v
= hashmap_get(bus
->vtable_properties
, &key
);
1914 /* If there are two vtables for the same
1915 * interface, let's handle this property when
1916 * we come to that vtable. */
1920 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1921 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1923 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1925 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1926 has_invalidating
= true;
1930 has_changing
= true;
1932 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1935 if (bus
->nodes_modified
)
1939 const sd_bus_vtable
*v
;
1941 /* If the caller specified no properties list
1942 * we include all properties that are marked
1943 * as changing in the message. */
1945 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1946 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
1949 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
1952 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1953 has_invalidating
= true;
1957 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
1960 has_changing
= true;
1962 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
1965 if (bus
->nodes_modified
)
1971 if (!has_invalidating
&& !has_changing
)
1974 r
= sd_bus_message_close_container(m
);
1978 r
= sd_bus_message_open_container(m
, 'a', "s");
1982 if (has_invalidating
) {
1983 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1984 if (require_fallback
&& !c
->is_fallback
)
1987 if (!streq(c
->interface
, interface
))
1990 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1993 if (bus
->nodes_modified
)
1999 STRV_FOREACH(property
, names
) {
2000 struct vtable_member
*v
;
2002 key
.member
= *property
;
2003 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2004 assert(c
== v
->parent
);
2006 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2009 r
= sd_bus_message_append(m
, "s", *property
);
2014 const sd_bus_vtable
*v
;
2016 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2017 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
2020 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2023 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2026 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2034 r
= sd_bus_message_close_container(m
);
2038 r
= sd_bus_send(bus
, m
, NULL
);
2045 _public_
int sd_bus_emit_properties_changed_strv(
2048 const char *interface
,
2051 BUS_DONT_DESTROY(bus
);
2052 bool found_interface
= false;
2056 assert_return(bus
, -EINVAL
);
2057 assert_return(object_path_is_valid(path
), -EINVAL
);
2058 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2059 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2061 if (!BUS_IS_OPEN(bus
->state
))
2064 /* A non-NULL but empty names list means nothing needs to be
2065 generated. A NULL list OTOH indicates that all properties
2066 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2067 included in the PropertiesChanged message. */
2068 if (names
&& names
[0] == NULL
)
2072 bus
->nodes_modified
= false;
2074 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2077 if (bus
->nodes_modified
)
2080 prefix
= alloca(strlen(path
) + 1);
2081 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2082 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2085 if (bus
->nodes_modified
)
2089 } while (bus
->nodes_modified
);
2091 return found_interface
? 0 : -ENOENT
;
2094 _public_
int sd_bus_emit_properties_changed(
2097 const char *interface
,
2098 const char *name
, ...) {
2102 assert_return(bus
, -EINVAL
);
2103 assert_return(object_path_is_valid(path
), -EINVAL
);
2104 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2105 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2107 if (!BUS_IS_OPEN(bus
->state
))
2113 names
= strv_from_stdarg_alloca(name
);
2115 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2118 static int object_added_append_all_prefix(
2124 bool require_fallback
) {
2126 const char *previous_interface
= NULL
;
2127 struct node_vtable
*c
;
2137 n
= hashmap_get(bus
->nodes
, prefix
);
2141 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2142 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2145 if (require_fallback
&& !c
->is_fallback
)
2148 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2151 if (bus
->nodes_modified
)
2156 if (!streq_ptr(c
->interface
, previous_interface
)) {
2157 /* If a child-node already handled this interface, we
2158 * skip it on any of its parents. The child vtables
2159 * always fully override any conflicting vtables of
2160 * any parent node. */
2161 if (set_get(s
, c
->interface
))
2164 r
= set_put(s
, c
->interface
);
2168 if (previous_interface
) {
2169 r
= sd_bus_message_close_container(m
);
2172 r
= sd_bus_message_close_container(m
);
2177 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2180 r
= sd_bus_message_append(m
, "s", c
->interface
);
2183 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2187 previous_interface
= c
->interface
;
2190 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2193 if (bus
->nodes_modified
)
2197 if (previous_interface
) {
2198 r
= sd_bus_message_close_container(m
);
2201 r
= sd_bus_message_close_container(m
);
2209 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2210 _cleanup_set_free_ Set
*s
= NULL
;
2219 * This appends all interfaces registered on path @path. We first add
2220 * the builtin interfaces, which are always available and handled by
2221 * sd-bus. Then, we add all interfaces registered on the exact node,
2222 * followed by all fallback interfaces registered on any parent prefix.
2224 * If an interface is registered multiple times on the same node with
2225 * different vtables, we merge all the properties across all vtables.
2226 * However, if a child node has the same interface registered as one of
2227 * its parent nodes has as fallback, we make the child overwrite the
2228 * parent instead of extending it. Therefore, we keep a "Set" of all
2229 * handled interfaces during parent traversal, so we skip interfaces on
2230 * a parent that were overwritten by a child.
2233 s
= set_new(&string_hash_ops
);
2237 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2240 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2243 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2246 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2250 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2253 if (bus
->nodes_modified
)
2256 prefix
= alloca(strlen(path
) + 1);
2257 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2258 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2261 if (bus
->nodes_modified
)
2268 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2269 BUS_DONT_DESTROY(bus
);
2271 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2275 * This emits an InterfacesAdded signal on the given path, by iterating
2276 * all registered vtables and fallback vtables on the path. All
2277 * properties are queried and included in the signal.
2278 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2279 * explicit list of registered interfaces. However, unlike
2280 * interfaces_added(), this call can figure out the list of supported
2281 * interfaces itself. Furthermore, it properly adds the builtin
2282 * org.freedesktop.DBus.* interfaces.
2285 assert_return(bus
, -EINVAL
);
2286 assert_return(object_path_is_valid(path
), -EINVAL
);
2287 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2289 if (!BUS_IS_OPEN(bus
->state
))
2293 bus
->nodes_modified
= false;
2294 m
= sd_bus_message_unref(m
);
2296 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2300 r
= sd_bus_message_append_basic(m
, 'o', path
);
2304 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2308 r
= object_added_append_all(bus
, m
, path
);
2312 if (bus
->nodes_modified
)
2315 r
= sd_bus_message_close_container(m
);
2319 } while (bus
->nodes_modified
);
2321 return sd_bus_send(bus
, m
, NULL
);
2324 static int object_removed_append_all_prefix(
2330 bool require_fallback
) {
2332 const char *previous_interface
= NULL
;
2333 struct node_vtable
*c
;
2343 n
= hashmap_get(bus
->nodes
, prefix
);
2347 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2348 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2351 if (require_fallback
&& !c
->is_fallback
)
2353 if (streq_ptr(c
->interface
, previous_interface
))
2356 /* If a child-node already handled this interface, we
2357 * skip it on any of its parents. The child vtables
2358 * always fully override any conflicting vtables of
2359 * any parent node. */
2360 if (set_get(s
, c
->interface
))
2363 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2366 if (bus
->nodes_modified
)
2371 r
= set_put(s
, c
->interface
);
2375 r
= sd_bus_message_append(m
, "s", c
->interface
);
2379 previous_interface
= c
->interface
;
2385 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2386 _cleanup_set_free_ Set
*s
= NULL
;
2394 /* see sd_bus_emit_object_added() for details */
2396 s
= set_new(&string_hash_ops
);
2400 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2403 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2406 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2409 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2413 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2416 if (bus
->nodes_modified
)
2419 prefix
= alloca(strlen(path
) + 1);
2420 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2421 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2424 if (bus
->nodes_modified
)
2431 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2432 BUS_DONT_DESTROY(bus
);
2434 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2438 * This is like sd_bus_emit_object_added(), but emits an
2439 * InterfacesRemoved signal on the given path. This only includes any
2440 * registered interfaces but skips the properties. Note that this will
2441 * call into the find() callbacks of any registered vtable. Therefore,
2442 * you must call this function before destroying/unlinking your object.
2443 * Otherwise, the list of interfaces will be incomplete. However, note
2444 * that this will *NOT* call into any property callback. Therefore, the
2445 * object might be in an "destructed" state, as long as we can find it.
2448 assert_return(bus
, -EINVAL
);
2449 assert_return(object_path_is_valid(path
), -EINVAL
);
2450 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2452 if (!BUS_IS_OPEN(bus
->state
))
2456 bus
->nodes_modified
= false;
2457 m
= sd_bus_message_unref(m
);
2459 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2463 r
= sd_bus_message_append_basic(m
, 'o', path
);
2467 r
= sd_bus_message_open_container(m
, 'a', "s");
2471 r
= object_removed_append_all(bus
, m
, path
);
2475 if (bus
->nodes_modified
)
2478 r
= sd_bus_message_close_container(m
);
2482 } while (bus
->nodes_modified
);
2484 return sd_bus_send(bus
, m
, NULL
);
2487 static int interfaces_added_append_one_prefix(
2492 const char *interface
,
2493 bool require_fallback
) {
2495 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2496 bool found_interface
= false;
2497 struct node_vtable
*c
;
2508 n
= hashmap_get(bus
->nodes
, prefix
);
2512 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2513 if (require_fallback
&& !c
->is_fallback
)
2516 if (!streq(c
->interface
, interface
))
2519 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2522 if (bus
->nodes_modified
)
2527 if (!found_interface
) {
2528 r
= sd_bus_message_append_basic(m
, 's', interface
);
2532 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2536 found_interface
= true;
2539 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2542 if (bus
->nodes_modified
)
2546 if (found_interface
) {
2547 r
= sd_bus_message_close_container(m
);
2552 return found_interface
;
2555 static int interfaces_added_append_one(
2559 const char *interface
) {
2569 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2572 if (bus
->nodes_modified
)
2575 prefix
= alloca(strlen(path
) + 1);
2576 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2577 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2580 if (bus
->nodes_modified
)
2587 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2588 BUS_DONT_DESTROY(bus
);
2590 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2594 assert_return(bus
, -EINVAL
);
2595 assert_return(object_path_is_valid(path
), -EINVAL
);
2596 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2598 if (!BUS_IS_OPEN(bus
->state
))
2601 if (strv_isempty(interfaces
))
2605 bus
->nodes_modified
= false;
2606 m
= sd_bus_message_unref(m
);
2608 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2612 r
= sd_bus_message_append_basic(m
, 'o', path
);
2616 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2620 STRV_FOREACH(i
, interfaces
) {
2621 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2623 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2627 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2631 if (bus
->nodes_modified
)
2634 r
= sd_bus_message_close_container(m
);
2639 if (bus
->nodes_modified
)
2642 r
= sd_bus_message_close_container(m
);
2646 } while (bus
->nodes_modified
);
2648 return sd_bus_send(bus
, m
, NULL
);
2651 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2654 assert_return(bus
, -EINVAL
);
2655 assert_return(object_path_is_valid(path
), -EINVAL
);
2656 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2658 if (!BUS_IS_OPEN(bus
->state
))
2661 interfaces
= strv_from_stdarg_alloca(interface
);
2663 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2666 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2667 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2670 assert_return(bus
, -EINVAL
);
2671 assert_return(object_path_is_valid(path
), -EINVAL
);
2672 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2674 if (!BUS_IS_OPEN(bus
->state
))
2677 if (strv_isempty(interfaces
))
2680 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2684 r
= sd_bus_message_append_basic(m
, 'o', path
);
2688 r
= sd_bus_message_append_strv(m
, interfaces
);
2692 return sd_bus_send(bus
, m
, NULL
);
2695 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2698 assert_return(bus
, -EINVAL
);
2699 assert_return(object_path_is_valid(path
), -EINVAL
);
2700 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2702 if (!BUS_IS_OPEN(bus
->state
))
2705 interfaces
= strv_from_stdarg_alloca(interface
);
2707 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2710 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2715 assert_return(bus
, -EINVAL
);
2716 assert_return(object_path_is_valid(path
), -EINVAL
);
2717 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2719 n
= bus_node_allocate(bus
, path
);
2723 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2729 s
->node_object_manager
.node
= n
;
2730 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2731 bus
->nodes_modified
= true;
2739 sd_bus_slot_unref(s
);
2740 bus_node_gc(bus
, n
);