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_method_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
74 return (uint8_t*) u
+ p
->x
.method
.offset
;
77 static void *vtable_property_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
80 return (uint8_t*) u
+ p
->x
.property
.offset
;
83 static int vtable_property_get_userdata(
86 struct vtable_member
*p
,
88 sd_bus_error
*error
) {
98 r
= node_vtable_get_userdata(bus
, path
, p
->parent
, &u
, error
);
101 if (bus
->nodes_modified
)
104 *userdata
= vtable_property_convert_userdata(p
->vtable
, u
);
108 static int add_enumerated_to_set(
111 struct node_enumerator
*first
,
113 sd_bus_error
*error
) {
115 struct node_enumerator
*c
;
122 LIST_FOREACH(enumerators
, c
, first
) {
123 char **children
= NULL
, **k
;
126 if (bus
->nodes_modified
)
129 slot
= container_of(c
, sd_bus_slot
, node_enumerator
);
131 bus
->current_slot
= sd_bus_slot_ref(slot
);
132 bus
->current_userdata
= slot
->userdata
;
133 r
= c
->callback(bus
, prefix
, slot
->userdata
, &children
, error
);
134 bus
->current_userdata
= NULL
;
135 bus
->current_slot
= sd_bus_slot_unref(slot
);
139 if (sd_bus_error_is_set(error
))
140 return -sd_bus_error_get_errno(error
);
142 STRV_FOREACH(k
, children
) {
148 if (!object_path_is_valid(*k
)){
154 if (!object_path_startswith(*k
, prefix
)) {
159 r
= set_consume(s
, *k
);
172 static int add_subtree_to_set(
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 r
= add_subtree_to_set(bus
, prefix
, i
, s
, error
);
210 if (bus
->nodes_modified
)
217 static int get_child_nodes(
222 sd_bus_error
*error
) {
232 s
= set_new(&string_hash_ops
);
236 r
= add_subtree_to_set(bus
, prefix
, n
, s
, error
);
246 static int node_callbacks_run(
249 struct node_callback
*first
,
250 bool require_fallback
,
251 bool *found_object
) {
253 struct node_callback
*c
;
258 assert(found_object
);
260 LIST_FOREACH(callbacks
, c
, first
) {
261 _cleanup_bus_error_free_ sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
264 if (bus
->nodes_modified
)
267 if (require_fallback
&& !c
->is_fallback
)
270 *found_object
= true;
272 if (c
->last_iteration
== bus
->iteration_counter
)
275 c
->last_iteration
= bus
->iteration_counter
;
277 r
= sd_bus_message_rewind(m
, true);
281 slot
= container_of(c
, sd_bus_slot
, node_callback
);
283 bus
->current_slot
= sd_bus_slot_ref(slot
);
284 bus
->current_handler
= c
->callback
;
285 bus
->current_userdata
= slot
->userdata
;
286 r
= c
->callback(m
, slot
->userdata
, &error_buffer
);
287 bus
->current_userdata
= NULL
;
288 bus
->current_handler
= NULL
;
289 bus
->current_slot
= sd_bus_slot_unref(slot
);
291 r
= bus_maybe_reply_error(m
, r
, &error_buffer
);
299 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
301 static int check_access(sd_bus
*bus
, sd_bus_message
*m
, struct vtable_member
*c
, sd_bus_error
*error
) {
309 /* If the entire bus is trusted let's grant access */
313 /* If the member is marked UNPRIVILEGED let's grant access */
314 if (c
->vtable
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
)
317 /* Check have the caller has the requested capability
318 * set. Note that the flags value contains the capability
319 * number plus one, which we need to subtract here. We do this
320 * so that we have 0 as special value for "default
322 cap
= CAPABILITY_SHIFT(c
->vtable
->flags
);
324 cap
= CAPABILITY_SHIFT(c
->parent
->vtable
[0].flags
);
330 r
= sd_bus_query_sender_privilege(m
, cap
);
336 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Access to %s.%s() not permitted.", c
->interface
, c
->member
);
339 static int method_callbacks_run(
342 struct vtable_member
*c
,
343 bool require_fallback
,
344 bool *found_object
) {
346 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
347 const char *signature
;
354 assert(found_object
);
356 if (require_fallback
&& !c
->parent
->is_fallback
)
359 r
= check_access(bus
, m
, c
, &error
);
361 return bus_maybe_reply_error(m
, r
, &error
);
363 r
= node_vtable_get_userdata(bus
, m
->path
, c
->parent
, &u
, &error
);
365 return bus_maybe_reply_error(m
, r
, &error
);
366 if (bus
->nodes_modified
)
369 u
= vtable_method_convert_userdata(c
->vtable
, u
);
371 *found_object
= true;
373 if (c
->last_iteration
== bus
->iteration_counter
)
376 c
->last_iteration
= bus
->iteration_counter
;
378 r
= sd_bus_message_rewind(m
, true);
382 signature
= sd_bus_message_get_signature(m
, true);
386 if (!streq(strempty(c
->vtable
->x
.method
.signature
), signature
))
387 return sd_bus_reply_method_errorf(
389 SD_BUS_ERROR_INVALID_ARGS
,
390 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
391 signature
, c
->interface
, c
->member
, strempty(c
->vtable
->x
.method
.signature
));
393 /* Keep track what the signature of the reply to this message
394 * should be, so that this can be enforced when sealing the
396 m
->enforced_reply_signature
= strempty(c
->vtable
->x
.method
.result
);
398 if (c
->vtable
->x
.method
.handler
) {
401 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
403 bus
->current_slot
= sd_bus_slot_ref(slot
);
404 bus
->current_handler
= c
->vtable
->x
.method
.handler
;
405 bus
->current_userdata
= u
;
406 r
= c
->vtable
->x
.method
.handler(m
, u
, &error
);
407 bus
->current_userdata
= NULL
;
408 bus
->current_handler
= NULL
;
409 bus
->current_slot
= sd_bus_slot_unref(slot
);
411 return bus_maybe_reply_error(m
, r
, &error
);
414 /* If the method callback is NULL, make this a successful NOP */
415 r
= sd_bus_reply_method_return(m
, NULL
);
422 static int invoke_property_get(
425 const sd_bus_vtable
*v
,
427 const char *interface
,
428 const char *property
,
429 sd_bus_message
*reply
,
431 sd_bus_error
*error
) {
444 if (v
->x
.property
.get
) {
446 bus
->current_slot
= sd_bus_slot_ref(slot
);
447 bus
->current_userdata
= userdata
;
448 r
= v
->x
.property
.get(bus
, path
, interface
, property
, reply
, userdata
, error
);
449 bus
->current_userdata
= NULL
;
450 bus
->current_slot
= sd_bus_slot_unref(slot
);
454 if (sd_bus_error_is_set(error
))
455 return -sd_bus_error_get_errno(error
);
459 /* Automatic handling if no callback is defined. */
461 if (streq(v
->x
.property
.signature
, "as"))
462 return sd_bus_message_append_strv(reply
, *(char***) userdata
);
464 assert(signature_is_single(v
->x
.property
.signature
, false));
465 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
467 switch (v
->x
.property
.signature
[0]) {
469 case SD_BUS_TYPE_STRING
:
470 case SD_BUS_TYPE_SIGNATURE
:
471 p
= strempty(*(char**) userdata
);
474 case SD_BUS_TYPE_OBJECT_PATH
:
475 p
= *(char**) userdata
;
484 return sd_bus_message_append_basic(reply
, v
->x
.property
.signature
[0], p
);
487 static int invoke_property_set(
490 const sd_bus_vtable
*v
,
492 const char *interface
,
493 const char *property
,
494 sd_bus_message
*value
,
496 sd_bus_error
*error
) {
508 if (v
->x
.property
.set
) {
510 bus
->current_slot
= sd_bus_slot_ref(slot
);
511 bus
->current_userdata
= userdata
;
512 r
= v
->x
.property
.set(bus
, path
, interface
, property
, value
, userdata
, error
);
513 bus
->current_userdata
= NULL
;
514 bus
->current_slot
= sd_bus_slot_unref(slot
);
518 if (sd_bus_error_is_set(error
))
519 return -sd_bus_error_get_errno(error
);
523 /* Automatic handling if no callback is defined. */
525 assert(signature_is_single(v
->x
.property
.signature
, false));
526 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
528 switch (v
->x
.property
.signature
[0]) {
530 case SD_BUS_TYPE_STRING
:
531 case SD_BUS_TYPE_OBJECT_PATH
:
532 case SD_BUS_TYPE_SIGNATURE
: {
536 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], &p
);
544 free(*(char**) userdata
);
545 *(char**) userdata
= n
;
551 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], userdata
);
561 static int property_get_set_callbacks_run(
564 struct vtable_member
*c
,
565 bool require_fallback
,
567 bool *found_object
) {
569 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
570 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
578 assert(found_object
);
580 if (require_fallback
&& !c
->parent
->is_fallback
)
583 r
= vtable_property_get_userdata(bus
, m
->path
, c
, &u
, &error
);
585 return bus_maybe_reply_error(m
, r
, &error
);
586 if (bus
->nodes_modified
)
589 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
591 *found_object
= true;
593 r
= sd_bus_message_new_method_return(m
, &reply
);
598 /* Note that we do not protect against reexecution
599 * here (using the last_iteration check, see below),
600 * should the node tree have changed and we got called
601 * again. We assume that property Get() calls are
602 * ultimately without side-effects or if they aren't
603 * then at least idempotent. */
605 r
= sd_bus_message_open_container(reply
, 'v', c
->vtable
->x
.property
.signature
);
609 /* Note that we do not do an access check here. Read
610 * access to properties is always unrestricted, since
611 * PropertiesChanged signals broadcast contents
614 r
= invoke_property_get(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, reply
, u
, &error
);
616 return bus_maybe_reply_error(m
, r
, &error
);
618 if (bus
->nodes_modified
)
621 r
= sd_bus_message_close_container(reply
);
626 const char *signature
= NULL
;
629 if (c
->vtable
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
630 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_PROPERTY_READ_ONLY
, "Property '%s' is not writable.", c
->member
);
632 /* Avoid that we call the set routine more than once
633 * if the processing of this message got restarted
634 * because the node tree changed. */
635 if (c
->last_iteration
== bus
->iteration_counter
)
638 c
->last_iteration
= bus
->iteration_counter
;
640 r
= sd_bus_message_peek_type(m
, &type
, &signature
);
644 if (type
!= 'v' || !streq(strempty(signature
), strempty(c
->vtable
->x
.property
.signature
)))
645 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
));
647 r
= sd_bus_message_enter_container(m
, 'v', c
->vtable
->x
.property
.signature
);
651 r
= check_access(bus
, m
, c
, &error
);
653 return bus_maybe_reply_error(m
, r
, &error
);
655 r
= invoke_property_set(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, m
, u
, &error
);
657 return bus_maybe_reply_error(m
, r
, &error
);
659 if (bus
->nodes_modified
)
662 r
= sd_bus_message_exit_container(m
);
667 r
= sd_bus_send(bus
, reply
, NULL
);
674 static int vtable_append_one_property(
676 sd_bus_message
*reply
,
678 struct node_vtable
*c
,
679 const sd_bus_vtable
*v
,
681 sd_bus_error
*error
) {
692 r
= sd_bus_message_open_container(reply
, 'e', "sv");
696 r
= sd_bus_message_append(reply
, "s", v
->x
.property
.member
);
700 r
= sd_bus_message_open_container(reply
, 'v', v
->x
.property
.signature
);
704 slot
= container_of(c
, sd_bus_slot
, node_vtable
);
706 r
= invoke_property_get(bus
, slot
, v
, path
, c
->interface
, v
->x
.property
.member
, reply
, vtable_property_convert_userdata(v
, userdata
), error
);
709 if (bus
->nodes_modified
)
712 r
= sd_bus_message_close_container(reply
);
716 r
= sd_bus_message_close_container(reply
);
723 static int vtable_append_all_properties(
725 sd_bus_message
*reply
,
727 struct node_vtable
*c
,
729 sd_bus_error
*error
) {
731 const sd_bus_vtable
*v
;
739 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
742 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
743 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
746 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
749 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
752 if (bus
->nodes_modified
)
759 static int property_get_all_callbacks_run(
762 struct node_vtable
*first
,
763 bool require_fallback
,
765 bool *found_object
) {
767 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
768 struct node_vtable
*c
;
769 bool found_interface
;
774 assert(found_object
);
776 r
= sd_bus_message_new_method_return(m
, &reply
);
780 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
784 found_interface
= !iface
||
785 streq(iface
, "org.freedesktop.DBus.Properties") ||
786 streq(iface
, "org.freedesktop.DBus.Peer") ||
787 streq(iface
, "org.freedesktop.DBus.Introspectable");
789 LIST_FOREACH(vtables
, c
, first
) {
790 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
793 if (require_fallback
&& !c
->is_fallback
)
796 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
798 return bus_maybe_reply_error(m
, r
, &error
);
799 if (bus
->nodes_modified
)
804 *found_object
= true;
806 if (iface
&& !streq(c
->interface
, iface
))
808 found_interface
= true;
810 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
812 return bus_maybe_reply_error(m
, r
, &error
);
813 if (bus
->nodes_modified
)
817 if (!found_interface
) {
818 r
= sd_bus_reply_method_errorf(
820 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
821 "Unknown interface '%s'.", iface
);
828 r
= sd_bus_message_close_container(reply
);
832 r
= sd_bus_send(bus
, reply
, NULL
);
839 static int bus_node_exists(
843 bool require_fallback
) {
845 struct node_vtable
*c
;
846 struct node_callback
*k
;
853 /* Tests if there's anything attached directly to this node
854 * for the specified path */
856 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
859 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
860 if (require_fallback
&& !k
->is_fallback
)
866 LIST_FOREACH(vtables
, c
, n
->vtables
) {
867 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
869 if (require_fallback
&& !c
->is_fallback
)
872 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
875 if (bus
->nodes_modified
)
882 static int process_introspect(
886 bool require_fallback
,
887 bool *found_object
) {
889 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
890 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
891 _cleanup_set_free_free_ Set
*s
= NULL
;
892 const char *previous_interface
= NULL
;
893 struct introspect intro
;
894 struct node_vtable
*c
;
901 assert(found_object
);
903 r
= get_child_nodes(bus
, m
->path
, n
, &s
, &error
);
905 return bus_maybe_reply_error(m
, r
, &error
);
906 if (bus
->nodes_modified
)
909 r
= introspect_begin(&intro
, bus
->trusted
);
913 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
917 empty
= set_isempty(s
);
919 LIST_FOREACH(vtables
, c
, n
->vtables
) {
920 if (require_fallback
&& !c
->is_fallback
)
923 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
925 r
= bus_maybe_reply_error(m
, r
, &error
);
928 if (bus
->nodes_modified
) {
937 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
940 if (!streq_ptr(previous_interface
, c
->interface
)) {
942 if (previous_interface
)
943 fputs(" </interface>\n", intro
.f
);
945 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
948 r
= introspect_write_interface(&intro
, c
->vtable
);
952 previous_interface
= c
->interface
;
955 if (previous_interface
)
956 fputs(" </interface>\n", intro
.f
);
959 /* Nothing?, let's see if we exist at all, and if not
960 * refuse to do anything */
961 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
964 if (bus
->nodes_modified
) {
970 *found_object
= true;
972 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
976 r
= introspect_finish(&intro
, bus
, m
, &reply
);
980 r
= sd_bus_send(bus
, reply
, NULL
);
987 introspect_free(&intro
);
991 static int object_manager_serialize_path(
993 sd_bus_message
*reply
,
996 bool require_fallback
,
997 sd_bus_error
*error
) {
999 const char *previous_interface
= NULL
;
1000 bool found_something
= false;
1001 struct node_vtable
*i
;
1011 n
= hashmap_get(bus
->nodes
, prefix
);
1015 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1018 if (require_fallback
&& !i
->is_fallback
)
1021 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1024 if (bus
->nodes_modified
)
1029 if (!found_something
) {
1031 /* Open the object part */
1033 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1037 r
= sd_bus_message_append(reply
, "o", path
);
1041 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1045 found_something
= true;
1048 if (!streq_ptr(previous_interface
, i
->interface
)) {
1050 /* Maybe close the previous interface part */
1052 if (previous_interface
) {
1053 r
= sd_bus_message_close_container(reply
);
1057 r
= sd_bus_message_close_container(reply
);
1062 /* Open the new interface part */
1064 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1068 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1072 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1077 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1080 if (bus
->nodes_modified
)
1083 previous_interface
= i
->interface
;
1086 if (previous_interface
) {
1087 r
= sd_bus_message_close_container(reply
);
1091 r
= sd_bus_message_close_container(reply
);
1096 if (found_something
) {
1097 r
= sd_bus_message_close_container(reply
);
1101 r
= sd_bus_message_close_container(reply
);
1109 static int object_manager_serialize_path_and_fallbacks(
1111 sd_bus_message
*reply
,
1113 sd_bus_error
*error
) {
1123 /* First, add all vtables registered for this path */
1124 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1127 if (bus
->nodes_modified
)
1130 /* Second, add fallback vtables registered for any of the prefixes */
1131 prefix
= alloca(strlen(path
) + 1);
1132 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1133 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1136 if (bus
->nodes_modified
)
1143 static int process_get_managed_objects(
1147 bool require_fallback
,
1148 bool *found_object
) {
1150 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1151 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1152 _cleanup_set_free_free_ Set
*s
= NULL
;
1160 assert(found_object
);
1162 /* Spec says, GetManagedObjects() is only implemented on the root of a
1163 * sub-tree. Therefore, we require a registered object-manager on
1164 * exactly the queried path, otherwise, we refuse to respond. */
1166 if (require_fallback
|| !n
->object_managers
)
1169 r
= get_child_nodes(bus
, m
->path
, n
, &s
, &error
);
1172 if (bus
->nodes_modified
)
1175 r
= set_put_strdup(s
, m
->path
);
1179 r
= sd_bus_message_new_method_return(m
, &reply
);
1183 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1187 SET_FOREACH(path
, s
, i
) {
1188 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1192 if (bus
->nodes_modified
)
1196 r
= sd_bus_message_close_container(reply
);
1200 r
= sd_bus_send(bus
, reply
, NULL
);
1207 static int object_find_and_run(
1211 bool require_fallback
,
1212 bool *found_object
) {
1215 struct vtable_member vtable_key
, *v
;
1221 assert(found_object
);
1223 n
= hashmap_get(bus
->nodes
, p
);
1227 /* First, try object callbacks */
1228 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1231 if (bus
->nodes_modified
)
1234 if (!m
->interface
|| !m
->member
)
1237 /* Then, look for a known method */
1238 vtable_key
.path
= (char*) p
;
1239 vtable_key
.interface
= m
->interface
;
1240 vtable_key
.member
= m
->member
;
1242 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1244 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1247 if (bus
->nodes_modified
)
1251 /* Then, look for a known property */
1252 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1255 get
= streq(m
->member
, "Get");
1257 if (get
|| streq(m
->member
, "Set")) {
1259 r
= sd_bus_message_rewind(m
, true);
1263 vtable_key
.path
= (char*) p
;
1265 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1267 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1269 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1271 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1276 } else if (streq(m
->member
, "GetAll")) {
1279 r
= sd_bus_message_rewind(m
, true);
1283 r
= sd_bus_message_read(m
, "s", &iface
);
1285 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1290 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1295 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1297 if (!isempty(sd_bus_message_get_signature(m
, true)))
1298 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1300 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1304 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1306 if (!isempty(sd_bus_message_get_signature(m
, true)))
1307 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1309 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1314 if (bus
->nodes_modified
)
1317 if (!*found_object
) {
1318 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1321 if (bus
->nodes_modified
)
1324 *found_object
= true;
1330 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1333 bool found_object
= false;
1338 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1341 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1344 if (hashmap_isempty(bus
->nodes
))
1347 /* Never respond to broadcast messages */
1348 if (bus
->bus_client
&& !m
->destination
)
1354 pl
= strlen(m
->path
);
1358 bus
->nodes_modified
= false;
1360 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1364 /* Look for fallback prefixes */
1365 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1367 if (bus
->nodes_modified
)
1370 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1375 } while (bus
->nodes_modified
);
1380 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1381 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1382 r
= sd_bus_reply_method_errorf(
1384 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1385 "Unknown property or interface.");
1387 r
= sd_bus_reply_method_errorf(
1389 SD_BUS_ERROR_UNKNOWN_METHOD
,
1390 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1398 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1399 struct node
*n
, *parent
;
1401 _cleanup_free_
char *s
= NULL
;
1407 assert(path
[0] == '/');
1409 n
= hashmap_get(bus
->nodes
, path
);
1413 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1421 if (streq(path
, "/"))
1424 e
= strrchr(path
, '/');
1427 p
= strndupa(path
, MAX(1, e
- path
));
1429 parent
= bus_node_allocate(bus
, p
);
1434 n
= new0(struct node
, 1);
1440 s
= NULL
; /* do not free */
1442 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1450 LIST_PREPEND(siblings
, parent
->child
, n
);
1455 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1468 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1471 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1474 bus_node_gc(b
, n
->parent
);
1478 static int bus_add_object(
1483 sd_bus_message_handler_t callback
,
1490 assert_return(bus
, -EINVAL
);
1491 assert_return(object_path_is_valid(path
), -EINVAL
);
1492 assert_return(callback
, -EINVAL
);
1493 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1495 n
= bus_node_allocate(bus
, path
);
1499 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1505 s
->node_callback
.callback
= callback
;
1506 s
->node_callback
.is_fallback
= fallback
;
1508 s
->node_callback
.node
= n
;
1509 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1510 bus
->nodes_modified
= true;
1518 sd_bus_slot_unref(s
);
1519 bus_node_gc(bus
, n
);
1524 _public_
int sd_bus_add_object(
1528 sd_bus_message_handler_t callback
,
1531 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1534 _public_
int sd_bus_add_fallback(
1538 sd_bus_message_handler_t callback
,
1541 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1544 static unsigned long vtable_member_hash_func(const void *a
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
1545 const struct vtable_member
*m
= a
;
1546 uint8_t hash_key2
[HASH_KEY_SIZE
];
1551 ret
= string_hash_func(m
->path
, hash_key
);
1553 /* Use a slightly different hash key for the interface */
1554 memcpy(hash_key2
, hash_key
, HASH_KEY_SIZE
);
1556 ret
^= string_hash_func(m
->interface
, hash_key2
);
1558 /* And an even different one for the member */
1560 ret
^= string_hash_func(m
->member
, hash_key2
);
1565 static int vtable_member_compare_func(const void *a
, const void *b
) {
1566 const struct vtable_member
*x
= a
, *y
= b
;
1572 r
= strcmp(x
->path
, y
->path
);
1576 r
= strcmp(x
->interface
, y
->interface
);
1580 return strcmp(x
->member
, y
->member
);
1583 static const struct hash_ops vtable_member_hash_ops
= {
1584 .hash
= vtable_member_hash_func
,
1585 .compare
= vtable_member_compare_func
1588 static int add_object_vtable_internal(
1592 const char *interface
,
1593 const sd_bus_vtable
*vtable
,
1595 sd_bus_object_find_t find
,
1598 sd_bus_slot
*s
= NULL
;
1599 struct node_vtable
*i
, *existing
= NULL
;
1600 const sd_bus_vtable
*v
;
1604 assert_return(bus
, -EINVAL
);
1605 assert_return(object_path_is_valid(path
), -EINVAL
);
1606 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1607 assert_return(vtable
, -EINVAL
);
1608 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1609 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1610 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1611 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1612 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1613 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1614 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1616 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1620 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1624 n
= bus_node_allocate(bus
, path
);
1628 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1629 if (i
->is_fallback
!= fallback
) {
1634 if (streq(i
->interface
, interface
)) {
1636 if (i
->vtable
== vtable
) {
1645 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1651 s
->node_vtable
.is_fallback
= fallback
;
1652 s
->node_vtable
.vtable
= vtable
;
1653 s
->node_vtable
.find
= find
;
1655 s
->node_vtable
.interface
= strdup(interface
);
1656 if (!s
->node_vtable
.interface
) {
1661 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1665 case _SD_BUS_VTABLE_METHOD
: {
1666 struct vtable_member
*m
;
1668 if (!member_name_is_valid(v
->x
.method
.member
) ||
1669 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1670 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1671 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1672 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1677 m
= new0(struct vtable_member
, 1);
1683 m
->parent
= &s
->node_vtable
;
1685 m
->interface
= s
->node_vtable
.interface
;
1686 m
->member
= v
->x
.method
.member
;
1689 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1698 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1700 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1705 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1712 case _SD_BUS_VTABLE_PROPERTY
: {
1713 struct vtable_member
*m
;
1715 if (!member_name_is_valid(v
->x
.property
.member
) ||
1716 !signature_is_single(v
->x
.property
.signature
, false) ||
1717 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1718 v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
||
1719 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1720 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1725 m
= new0(struct vtable_member
, 1);
1731 m
->parent
= &s
->node_vtable
;
1733 m
->interface
= s
->node_vtable
.interface
;
1734 m
->member
= v
->x
.property
.member
;
1737 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1746 case _SD_BUS_VTABLE_SIGNAL
:
1748 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1749 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1750 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1763 s
->node_vtable
.node
= n
;
1764 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1765 bus
->nodes_modified
= true;
1773 sd_bus_slot_unref(s
);
1774 bus_node_gc(bus
, n
);
1779 _public_
int sd_bus_add_object_vtable(
1783 const char *interface
,
1784 const sd_bus_vtable
*vtable
,
1787 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1790 _public_
int sd_bus_add_fallback_vtable(
1794 const char *interface
,
1795 const sd_bus_vtable
*vtable
,
1796 sd_bus_object_find_t find
,
1799 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1802 _public_
int sd_bus_add_node_enumerator(
1806 sd_bus_node_enumerator_t callback
,
1813 assert_return(bus
, -EINVAL
);
1814 assert_return(object_path_is_valid(path
), -EINVAL
);
1815 assert_return(callback
, -EINVAL
);
1816 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1818 n
= bus_node_allocate(bus
, path
);
1822 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1828 s
->node_enumerator
.callback
= callback
;
1830 s
->node_enumerator
.node
= n
;
1831 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1832 bus
->nodes_modified
= true;
1840 sd_bus_slot_unref(s
);
1841 bus_node_gc(bus
, n
);
1846 static int emit_properties_changed_on_interface(
1850 const char *interface
,
1851 bool require_fallback
,
1852 bool *found_interface
,
1855 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1856 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1857 bool has_invalidating
= false, has_changing
= false;
1858 struct vtable_member key
= {};
1859 struct node_vtable
*c
;
1869 assert(found_interface
);
1871 n
= hashmap_get(bus
->nodes
, prefix
);
1875 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1879 r
= sd_bus_message_append(m
, "s", interface
);
1883 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1888 key
.interface
= interface
;
1890 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1891 if (require_fallback
&& !c
->is_fallback
)
1894 if (!streq(c
->interface
, interface
))
1897 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1900 if (bus
->nodes_modified
)
1905 *found_interface
= true;
1908 /* If the caller specified a list of
1909 * properties we include exactly those in the
1910 * PropertiesChanged message */
1912 STRV_FOREACH(property
, names
) {
1913 struct vtable_member
*v
;
1915 assert_return(member_name_is_valid(*property
), -EINVAL
);
1917 key
.member
= *property
;
1918 v
= hashmap_get(bus
->vtable_properties
, &key
);
1922 /* If there are two vtables for the same
1923 * interface, let's handle this property when
1924 * we come to that vtable. */
1928 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1929 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1931 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1933 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1934 has_invalidating
= true;
1938 has_changing
= true;
1940 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1943 if (bus
->nodes_modified
)
1947 const sd_bus_vtable
*v
;
1949 /* If the caller specified no properties list
1950 * we include all properties that are marked
1951 * as changing in the message. */
1953 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1954 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
1957 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
1960 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1961 has_invalidating
= true;
1965 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
1968 has_changing
= true;
1970 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
1973 if (bus
->nodes_modified
)
1979 if (!has_invalidating
&& !has_changing
)
1982 r
= sd_bus_message_close_container(m
);
1986 r
= sd_bus_message_open_container(m
, 'a', "s");
1990 if (has_invalidating
) {
1991 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1992 if (require_fallback
&& !c
->is_fallback
)
1995 if (!streq(c
->interface
, interface
))
1998 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2001 if (bus
->nodes_modified
)
2007 STRV_FOREACH(property
, names
) {
2008 struct vtable_member
*v
;
2010 key
.member
= *property
;
2011 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2012 assert(c
== v
->parent
);
2014 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2017 r
= sd_bus_message_append(m
, "s", *property
);
2022 const sd_bus_vtable
*v
;
2024 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2025 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
2028 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2031 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2034 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2042 r
= sd_bus_message_close_container(m
);
2046 r
= sd_bus_send(bus
, m
, NULL
);
2053 _public_
int sd_bus_emit_properties_changed_strv(
2056 const char *interface
,
2059 BUS_DONT_DESTROY(bus
);
2060 bool found_interface
= false;
2064 assert_return(bus
, -EINVAL
);
2065 assert_return(object_path_is_valid(path
), -EINVAL
);
2066 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2067 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2069 if (!BUS_IS_OPEN(bus
->state
))
2072 /* A non-NULL but empty names list means nothing needs to be
2073 generated. A NULL list OTOH indicates that all properties
2074 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2075 included in the PropertiesChanged message. */
2076 if (names
&& names
[0] == NULL
)
2080 bus
->nodes_modified
= false;
2082 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2085 if (bus
->nodes_modified
)
2088 prefix
= alloca(strlen(path
) + 1);
2089 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2090 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2093 if (bus
->nodes_modified
)
2097 } while (bus
->nodes_modified
);
2099 return found_interface
? 0 : -ENOENT
;
2102 _public_
int sd_bus_emit_properties_changed(
2105 const char *interface
,
2106 const char *name
, ...) {
2110 assert_return(bus
, -EINVAL
);
2111 assert_return(object_path_is_valid(path
), -EINVAL
);
2112 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2113 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2115 if (!BUS_IS_OPEN(bus
->state
))
2121 names
= strv_from_stdarg_alloca(name
);
2123 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2126 static int object_added_append_all_prefix(
2132 bool require_fallback
) {
2134 const char *previous_interface
= NULL
;
2135 struct node_vtable
*c
;
2145 n
= hashmap_get(bus
->nodes
, prefix
);
2149 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2150 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2153 if (require_fallback
&& !c
->is_fallback
)
2156 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2159 if (bus
->nodes_modified
)
2164 if (!streq_ptr(c
->interface
, previous_interface
)) {
2165 /* If a child-node already handled this interface, we
2166 * skip it on any of its parents. The child vtables
2167 * always fully override any conflicting vtables of
2168 * any parent node. */
2169 if (set_get(s
, c
->interface
))
2172 r
= set_put(s
, c
->interface
);
2176 if (previous_interface
) {
2177 r
= sd_bus_message_close_container(m
);
2180 r
= sd_bus_message_close_container(m
);
2185 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2188 r
= sd_bus_message_append(m
, "s", c
->interface
);
2191 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2195 previous_interface
= c
->interface
;
2198 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2201 if (bus
->nodes_modified
)
2205 if (previous_interface
) {
2206 r
= sd_bus_message_close_container(m
);
2209 r
= sd_bus_message_close_container(m
);
2217 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2218 _cleanup_set_free_ Set
*s
= NULL
;
2227 * This appends all interfaces registered on path @path. We first add
2228 * the builtin interfaces, which are always available and handled by
2229 * sd-bus. Then, we add all interfaces registered on the exact node,
2230 * followed by all fallback interfaces registered on any parent prefix.
2232 * If an interface is registered multiple times on the same node with
2233 * different vtables, we merge all the properties across all vtables.
2234 * However, if a child node has the same interface registered as one of
2235 * its parent nodes has as fallback, we make the child overwrite the
2236 * parent instead of extending it. Therefore, we keep a "Set" of all
2237 * handled interfaces during parent traversal, so we skip interfaces on
2238 * a parent that were overwritten by a child.
2241 s
= set_new(&string_hash_ops
);
2245 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2248 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2251 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2254 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2258 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2261 if (bus
->nodes_modified
)
2264 prefix
= alloca(strlen(path
) + 1);
2265 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2266 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2269 if (bus
->nodes_modified
)
2276 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2277 BUS_DONT_DESTROY(bus
);
2279 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2283 * This emits an InterfacesAdded signal on the given path, by iterating
2284 * all registered vtables and fallback vtables on the path. All
2285 * properties are queried and included in the signal.
2286 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2287 * explicit list of registered interfaces. However, unlike
2288 * interfaces_added(), this call can figure out the list of supported
2289 * interfaces itself. Furthermore, it properly adds the builtin
2290 * org.freedesktop.DBus.* interfaces.
2293 assert_return(bus
, -EINVAL
);
2294 assert_return(object_path_is_valid(path
), -EINVAL
);
2295 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2297 if (!BUS_IS_OPEN(bus
->state
))
2301 bus
->nodes_modified
= false;
2302 m
= sd_bus_message_unref(m
);
2304 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2308 r
= sd_bus_message_append_basic(m
, 'o', path
);
2312 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2316 r
= object_added_append_all(bus
, m
, path
);
2320 if (bus
->nodes_modified
)
2323 r
= sd_bus_message_close_container(m
);
2327 } while (bus
->nodes_modified
);
2329 return sd_bus_send(bus
, m
, NULL
);
2332 static int object_removed_append_all_prefix(
2338 bool require_fallback
) {
2340 const char *previous_interface
= NULL
;
2341 struct node_vtable
*c
;
2351 n
= hashmap_get(bus
->nodes
, prefix
);
2355 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2356 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2359 if (require_fallback
&& !c
->is_fallback
)
2361 if (streq_ptr(c
->interface
, previous_interface
))
2364 /* If a child-node already handled this interface, we
2365 * skip it on any of its parents. The child vtables
2366 * always fully override any conflicting vtables of
2367 * any parent node. */
2368 if (set_get(s
, c
->interface
))
2371 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2374 if (bus
->nodes_modified
)
2379 r
= set_put(s
, c
->interface
);
2383 r
= sd_bus_message_append(m
, "s", c
->interface
);
2387 previous_interface
= c
->interface
;
2393 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2394 _cleanup_set_free_ Set
*s
= NULL
;
2402 /* see sd_bus_emit_object_added() for details */
2404 s
= set_new(&string_hash_ops
);
2408 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2411 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2414 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2417 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2421 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2424 if (bus
->nodes_modified
)
2427 prefix
= alloca(strlen(path
) + 1);
2428 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2429 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2432 if (bus
->nodes_modified
)
2439 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2440 BUS_DONT_DESTROY(bus
);
2442 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2446 * This is like sd_bus_emit_object_added(), but emits an
2447 * InterfacesRemoved signal on the given path. This only includes any
2448 * registered interfaces but skips the properties. Note that this will
2449 * call into the find() callbacks of any registered vtable. Therefore,
2450 * you must call this function before destroying/unlinking your object.
2451 * Otherwise, the list of interfaces will be incomplete. However, note
2452 * that this will *NOT* call into any property callback. Therefore, the
2453 * object might be in an "destructed" state, as long as we can find it.
2456 assert_return(bus
, -EINVAL
);
2457 assert_return(object_path_is_valid(path
), -EINVAL
);
2458 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2460 if (!BUS_IS_OPEN(bus
->state
))
2464 bus
->nodes_modified
= false;
2465 m
= sd_bus_message_unref(m
);
2467 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2471 r
= sd_bus_message_append_basic(m
, 'o', path
);
2475 r
= sd_bus_message_open_container(m
, 'a', "s");
2479 r
= object_removed_append_all(bus
, m
, path
);
2483 if (bus
->nodes_modified
)
2486 r
= sd_bus_message_close_container(m
);
2490 } while (bus
->nodes_modified
);
2492 return sd_bus_send(bus
, m
, NULL
);
2495 static int interfaces_added_append_one_prefix(
2500 const char *interface
,
2501 bool require_fallback
) {
2503 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2504 bool found_interface
= false;
2505 struct node_vtable
*c
;
2516 n
= hashmap_get(bus
->nodes
, prefix
);
2520 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2521 if (require_fallback
&& !c
->is_fallback
)
2524 if (!streq(c
->interface
, interface
))
2527 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2530 if (bus
->nodes_modified
)
2535 if (!found_interface
) {
2536 r
= sd_bus_message_append_basic(m
, 's', interface
);
2540 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2544 found_interface
= true;
2547 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2550 if (bus
->nodes_modified
)
2554 if (found_interface
) {
2555 r
= sd_bus_message_close_container(m
);
2560 return found_interface
;
2563 static int interfaces_added_append_one(
2567 const char *interface
) {
2577 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2580 if (bus
->nodes_modified
)
2583 prefix
= alloca(strlen(path
) + 1);
2584 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2585 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2588 if (bus
->nodes_modified
)
2595 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2596 BUS_DONT_DESTROY(bus
);
2598 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2602 assert_return(bus
, -EINVAL
);
2603 assert_return(object_path_is_valid(path
), -EINVAL
);
2604 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2606 if (!BUS_IS_OPEN(bus
->state
))
2609 if (strv_isempty(interfaces
))
2613 bus
->nodes_modified
= false;
2614 m
= sd_bus_message_unref(m
);
2616 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2620 r
= sd_bus_message_append_basic(m
, 'o', path
);
2624 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2628 STRV_FOREACH(i
, interfaces
) {
2629 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2631 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2635 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2639 if (bus
->nodes_modified
)
2642 r
= sd_bus_message_close_container(m
);
2647 if (bus
->nodes_modified
)
2650 r
= sd_bus_message_close_container(m
);
2654 } while (bus
->nodes_modified
);
2656 return sd_bus_send(bus
, m
, NULL
);
2659 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2662 assert_return(bus
, -EINVAL
);
2663 assert_return(object_path_is_valid(path
), -EINVAL
);
2664 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2666 if (!BUS_IS_OPEN(bus
->state
))
2669 interfaces
= strv_from_stdarg_alloca(interface
);
2671 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2674 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2675 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2678 assert_return(bus
, -EINVAL
);
2679 assert_return(object_path_is_valid(path
), -EINVAL
);
2680 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2682 if (!BUS_IS_OPEN(bus
->state
))
2685 if (strv_isempty(interfaces
))
2688 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2692 r
= sd_bus_message_append_basic(m
, 'o', path
);
2696 r
= sd_bus_message_append_strv(m
, interfaces
);
2700 return sd_bus_send(bus
, m
, NULL
);
2703 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2706 assert_return(bus
, -EINVAL
);
2707 assert_return(object_path_is_valid(path
), -EINVAL
);
2708 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2710 if (!BUS_IS_OPEN(bus
->state
))
2713 interfaces
= strv_from_stdarg_alloca(interface
);
2715 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2718 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2723 assert_return(bus
, -EINVAL
);
2724 assert_return(object_path_is_valid(path
), -EINVAL
);
2725 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2727 n
= bus_node_allocate(bus
, path
);
2731 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2737 s
->node_object_manager
.node
= n
;
2738 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2739 bus
->nodes_modified
= true;
2747 sd_bus_slot_unref(s
);
2748 bus_node_gc(bus
, n
);