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/>.
22 #include "alloc-util.h"
23 #include "bus-internal.h"
24 #include "bus-introspect.h"
25 #include "bus-message.h"
26 #include "bus-objects.h"
27 #include "bus-signature.h"
32 #include "string-util.h"
35 static int node_vtable_get_userdata(
38 struct node_vtable
*c
,
40 sd_bus_error
*error
) {
50 s
= container_of(c
, sd_bus_slot
, node_vtable
);
53 bus
->current_slot
= sd_bus_slot_ref(s
);
54 bus
->current_userdata
= u
;
55 r
= c
->find(bus
, path
, c
->interface
, u
, &u
, error
);
56 bus
->current_userdata
= NULL
;
57 bus
->current_slot
= sd_bus_slot_unref(s
);
61 if (sd_bus_error_is_set(error
))
62 return -sd_bus_error_get_errno(error
);
73 static void *vtable_method_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
76 return (uint8_t*) u
+ p
->x
.method
.offset
;
79 static void *vtable_property_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
82 return (uint8_t*) u
+ p
->x
.property
.offset
;
85 static int vtable_property_get_userdata(
88 struct vtable_member
*p
,
90 sd_bus_error
*error
) {
100 r
= node_vtable_get_userdata(bus
, path
, p
->parent
, &u
, error
);
103 if (bus
->nodes_modified
)
106 *userdata
= vtable_property_convert_userdata(p
->vtable
, u
);
110 static int add_enumerated_to_set(
113 struct node_enumerator
*first
,
115 sd_bus_error
*error
) {
117 struct node_enumerator
*c
;
124 LIST_FOREACH(enumerators
, c
, first
) {
125 char **children
= NULL
, **k
;
128 if (bus
->nodes_modified
)
131 slot
= container_of(c
, sd_bus_slot
, node_enumerator
);
133 bus
->current_slot
= sd_bus_slot_ref(slot
);
134 bus
->current_userdata
= slot
->userdata
;
135 r
= c
->callback(bus
, prefix
, slot
->userdata
, &children
, error
);
136 bus
->current_userdata
= NULL
;
137 bus
->current_slot
= sd_bus_slot_unref(slot
);
141 if (sd_bus_error_is_set(error
))
142 return -sd_bus_error_get_errno(error
);
144 STRV_FOREACH(k
, children
) {
150 if (!object_path_is_valid(*k
)){
156 if (!object_path_startswith(*k
, prefix
)) {
161 r
= set_consume(s
, *k
);
175 /* if set, add_subtree() works recursively */
176 CHILDREN_RECURSIVE
= (1U << 1),
177 /* if set, add_subtree() scans object-manager hierarchies recursively */
178 CHILDREN_SUBHIERARCHIES
= (1U << 0),
181 static int add_subtree_to_set(
187 sd_bus_error
*error
) {
197 r
= add_enumerated_to_set(bus
, prefix
, n
->enumerators
, s
, error
);
200 if (bus
->nodes_modified
)
203 LIST_FOREACH(siblings
, i
, n
->child
) {
206 if (!object_path_startswith(i
->path
, prefix
))
213 r
= set_consume(s
, t
);
214 if (r
< 0 && r
!= -EEXIST
)
217 if ((flags
& CHILDREN_RECURSIVE
) &&
218 ((flags
& CHILDREN_SUBHIERARCHIES
) || !i
->object_managers
)) {
219 r
= add_subtree_to_set(bus
, prefix
, i
, flags
, s
, error
);
222 if (bus
->nodes_modified
)
230 static int get_child_nodes(
236 sd_bus_error
*error
) {
246 s
= set_new(&string_hash_ops
);
250 r
= add_subtree_to_set(bus
, prefix
, n
, flags
, s
, error
);
260 static int node_callbacks_run(
263 struct node_callback
*first
,
264 bool require_fallback
,
265 bool *found_object
) {
267 struct node_callback
*c
;
272 assert(found_object
);
274 LIST_FOREACH(callbacks
, c
, first
) {
275 _cleanup_bus_error_free_ sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
278 if (bus
->nodes_modified
)
281 if (require_fallback
&& !c
->is_fallback
)
284 *found_object
= true;
286 if (c
->last_iteration
== bus
->iteration_counter
)
289 c
->last_iteration
= bus
->iteration_counter
;
291 r
= sd_bus_message_rewind(m
, true);
295 slot
= container_of(c
, sd_bus_slot
, node_callback
);
297 bus
->current_slot
= sd_bus_slot_ref(slot
);
298 bus
->current_handler
= c
->callback
;
299 bus
->current_userdata
= slot
->userdata
;
300 r
= c
->callback(m
, slot
->userdata
, &error_buffer
);
301 bus
->current_userdata
= NULL
;
302 bus
->current_handler
= NULL
;
303 bus
->current_slot
= sd_bus_slot_unref(slot
);
305 r
= bus_maybe_reply_error(m
, r
, &error_buffer
);
313 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
315 static int check_access(sd_bus
*bus
, sd_bus_message
*m
, struct vtable_member
*c
, sd_bus_error
*error
) {
323 /* If the entire bus is trusted let's grant access */
327 /* If the member is marked UNPRIVILEGED let's grant access */
328 if (c
->vtable
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
)
331 /* Check have the caller has the requested capability
332 * set. Note that the flags value contains the capability
333 * number plus one, which we need to subtract here. We do this
334 * so that we have 0 as special value for "default
336 cap
= CAPABILITY_SHIFT(c
->vtable
->flags
);
338 cap
= CAPABILITY_SHIFT(c
->parent
->vtable
[0].flags
);
344 r
= sd_bus_query_sender_privilege(m
, cap
);
350 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Access to %s.%s() not permitted.", c
->interface
, c
->member
);
353 static int method_callbacks_run(
356 struct vtable_member
*c
,
357 bool require_fallback
,
358 bool *found_object
) {
360 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
361 const char *signature
;
368 assert(found_object
);
370 if (require_fallback
&& !c
->parent
->is_fallback
)
373 r
= check_access(bus
, m
, c
, &error
);
375 return bus_maybe_reply_error(m
, r
, &error
);
377 r
= node_vtable_get_userdata(bus
, m
->path
, c
->parent
, &u
, &error
);
379 return bus_maybe_reply_error(m
, r
, &error
);
380 if (bus
->nodes_modified
)
383 u
= vtable_method_convert_userdata(c
->vtable
, u
);
385 *found_object
= true;
387 if (c
->last_iteration
== bus
->iteration_counter
)
390 c
->last_iteration
= bus
->iteration_counter
;
392 r
= sd_bus_message_rewind(m
, true);
396 signature
= sd_bus_message_get_signature(m
, true);
400 if (!streq(strempty(c
->vtable
->x
.method
.signature
), signature
))
401 return sd_bus_reply_method_errorf(
403 SD_BUS_ERROR_INVALID_ARGS
,
404 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
405 signature
, c
->interface
, c
->member
, strempty(c
->vtable
->x
.method
.signature
));
407 /* Keep track what the signature of the reply to this message
408 * should be, so that this can be enforced when sealing the
410 m
->enforced_reply_signature
= strempty(c
->vtable
->x
.method
.result
);
412 if (c
->vtable
->x
.method
.handler
) {
415 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
417 bus
->current_slot
= sd_bus_slot_ref(slot
);
418 bus
->current_handler
= c
->vtable
->x
.method
.handler
;
419 bus
->current_userdata
= u
;
420 r
= c
->vtable
->x
.method
.handler(m
, u
, &error
);
421 bus
->current_userdata
= NULL
;
422 bus
->current_handler
= NULL
;
423 bus
->current_slot
= sd_bus_slot_unref(slot
);
425 return bus_maybe_reply_error(m
, r
, &error
);
428 /* If the method callback is NULL, make this a successful NOP */
429 r
= sd_bus_reply_method_return(m
, NULL
);
436 static int invoke_property_get(
439 const sd_bus_vtable
*v
,
441 const char *interface
,
442 const char *property
,
443 sd_bus_message
*reply
,
445 sd_bus_error
*error
) {
458 if (v
->x
.property
.get
) {
460 bus
->current_slot
= sd_bus_slot_ref(slot
);
461 bus
->current_userdata
= userdata
;
462 r
= v
->x
.property
.get(bus
, path
, interface
, property
, reply
, userdata
, error
);
463 bus
->current_userdata
= NULL
;
464 bus
->current_slot
= sd_bus_slot_unref(slot
);
468 if (sd_bus_error_is_set(error
))
469 return -sd_bus_error_get_errno(error
);
473 /* Automatic handling if no callback is defined. */
475 if (streq(v
->x
.property
.signature
, "as"))
476 return sd_bus_message_append_strv(reply
, *(char***) userdata
);
478 assert(signature_is_single(v
->x
.property
.signature
, false));
479 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
481 switch (v
->x
.property
.signature
[0]) {
483 case SD_BUS_TYPE_STRING
:
484 case SD_BUS_TYPE_SIGNATURE
:
485 p
= strempty(*(char**) userdata
);
488 case SD_BUS_TYPE_OBJECT_PATH
:
489 p
= *(char**) userdata
;
498 return sd_bus_message_append_basic(reply
, v
->x
.property
.signature
[0], p
);
501 static int invoke_property_set(
504 const sd_bus_vtable
*v
,
506 const char *interface
,
507 const char *property
,
508 sd_bus_message
*value
,
510 sd_bus_error
*error
) {
522 if (v
->x
.property
.set
) {
524 bus
->current_slot
= sd_bus_slot_ref(slot
);
525 bus
->current_userdata
= userdata
;
526 r
= v
->x
.property
.set(bus
, path
, interface
, property
, value
, userdata
, error
);
527 bus
->current_userdata
= NULL
;
528 bus
->current_slot
= sd_bus_slot_unref(slot
);
532 if (sd_bus_error_is_set(error
))
533 return -sd_bus_error_get_errno(error
);
537 /* Automatic handling if no callback is defined. */
539 assert(signature_is_single(v
->x
.property
.signature
, false));
540 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
542 switch (v
->x
.property
.signature
[0]) {
544 case SD_BUS_TYPE_STRING
:
545 case SD_BUS_TYPE_OBJECT_PATH
:
546 case SD_BUS_TYPE_SIGNATURE
: {
550 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], &p
);
558 free(*(char**) userdata
);
559 *(char**) userdata
= n
;
565 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], userdata
);
575 static int property_get_set_callbacks_run(
578 struct vtable_member
*c
,
579 bool require_fallback
,
581 bool *found_object
) {
583 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
584 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
592 assert(found_object
);
594 if (require_fallback
&& !c
->parent
->is_fallback
)
597 r
= vtable_property_get_userdata(bus
, m
->path
, c
, &u
, &error
);
599 return bus_maybe_reply_error(m
, r
, &error
);
600 if (bus
->nodes_modified
)
603 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
605 *found_object
= true;
607 r
= sd_bus_message_new_method_return(m
, &reply
);
612 /* Note that we do not protect against reexecution
613 * here (using the last_iteration check, see below),
614 * should the node tree have changed and we got called
615 * again. We assume that property Get() calls are
616 * ultimately without side-effects or if they aren't
617 * then at least idempotent. */
619 r
= sd_bus_message_open_container(reply
, 'v', c
->vtable
->x
.property
.signature
);
623 /* Note that we do not do an access check here. Read
624 * access to properties is always unrestricted, since
625 * PropertiesChanged signals broadcast contents
628 r
= invoke_property_get(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, reply
, u
, &error
);
630 return bus_maybe_reply_error(m
, r
, &error
);
632 if (bus
->nodes_modified
)
635 r
= sd_bus_message_close_container(reply
);
640 const char *signature
= NULL
;
643 if (c
->vtable
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
644 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_PROPERTY_READ_ONLY
, "Property '%s' is not writable.", c
->member
);
646 /* Avoid that we call the set routine more than once
647 * if the processing of this message got restarted
648 * because the node tree changed. */
649 if (c
->last_iteration
== bus
->iteration_counter
)
652 c
->last_iteration
= bus
->iteration_counter
;
654 r
= sd_bus_message_peek_type(m
, &type
, &signature
);
658 if (type
!= 'v' || !streq(strempty(signature
), strempty(c
->vtable
->x
.property
.signature
)))
659 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
));
661 r
= sd_bus_message_enter_container(m
, 'v', c
->vtable
->x
.property
.signature
);
665 r
= check_access(bus
, m
, c
, &error
);
667 return bus_maybe_reply_error(m
, r
, &error
);
669 r
= invoke_property_set(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, m
, u
, &error
);
671 return bus_maybe_reply_error(m
, r
, &error
);
673 if (bus
->nodes_modified
)
676 r
= sd_bus_message_exit_container(m
);
681 r
= sd_bus_send(bus
, reply
, NULL
);
688 static int vtable_append_one_property(
690 sd_bus_message
*reply
,
692 struct node_vtable
*c
,
693 const sd_bus_vtable
*v
,
695 sd_bus_error
*error
) {
706 r
= sd_bus_message_open_container(reply
, 'e', "sv");
710 r
= sd_bus_message_append(reply
, "s", v
->x
.property
.member
);
714 r
= sd_bus_message_open_container(reply
, 'v', v
->x
.property
.signature
);
718 slot
= container_of(c
, sd_bus_slot
, node_vtable
);
720 r
= invoke_property_get(bus
, slot
, v
, path
, c
->interface
, v
->x
.property
.member
, reply
, vtable_property_convert_userdata(v
, userdata
), error
);
723 if (bus
->nodes_modified
)
726 r
= sd_bus_message_close_container(reply
);
730 r
= sd_bus_message_close_container(reply
);
737 static int vtable_append_all_properties(
739 sd_bus_message
*reply
,
741 struct node_vtable
*c
,
743 sd_bus_error
*error
) {
745 const sd_bus_vtable
*v
;
753 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
756 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
757 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
760 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
763 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)
766 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
769 if (bus
->nodes_modified
)
776 static int property_get_all_callbacks_run(
779 struct node_vtable
*first
,
780 bool require_fallback
,
782 bool *found_object
) {
784 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
785 struct node_vtable
*c
;
786 bool found_interface
;
791 assert(found_object
);
793 r
= sd_bus_message_new_method_return(m
, &reply
);
797 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
801 found_interface
= !iface
||
802 streq(iface
, "org.freedesktop.DBus.Properties") ||
803 streq(iface
, "org.freedesktop.DBus.Peer") ||
804 streq(iface
, "org.freedesktop.DBus.Introspectable");
806 LIST_FOREACH(vtables
, c
, first
) {
807 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
810 if (require_fallback
&& !c
->is_fallback
)
813 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
815 return bus_maybe_reply_error(m
, r
, &error
);
816 if (bus
->nodes_modified
)
821 *found_object
= true;
823 if (iface
&& !streq(c
->interface
, iface
))
825 found_interface
= true;
827 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
829 return bus_maybe_reply_error(m
, r
, &error
);
830 if (bus
->nodes_modified
)
834 if (!found_interface
) {
835 r
= sd_bus_reply_method_errorf(
837 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
838 "Unknown interface '%s'.", iface
);
845 r
= sd_bus_message_close_container(reply
);
849 r
= sd_bus_send(bus
, reply
, NULL
);
856 static int bus_node_exists(
860 bool require_fallback
) {
862 struct node_vtable
*c
;
863 struct node_callback
*k
;
870 /* Tests if there's anything attached directly to this node
871 * for the specified path */
873 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
876 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
877 if (require_fallback
&& !k
->is_fallback
)
883 LIST_FOREACH(vtables
, c
, n
->vtables
) {
884 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
886 if (require_fallback
&& !c
->is_fallback
)
889 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
892 if (bus
->nodes_modified
)
899 static int process_introspect(
903 bool require_fallback
,
904 bool *found_object
) {
906 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
907 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
908 _cleanup_set_free_free_ Set
*s
= NULL
;
909 const char *previous_interface
= NULL
;
910 struct introspect intro
;
911 struct node_vtable
*c
;
918 assert(found_object
);
920 r
= get_child_nodes(bus
, m
->path
, n
, 0, &s
, &error
);
922 return bus_maybe_reply_error(m
, r
, &error
);
923 if (bus
->nodes_modified
)
926 r
= introspect_begin(&intro
, bus
->trusted
);
930 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
934 empty
= set_isempty(s
);
936 LIST_FOREACH(vtables
, c
, n
->vtables
) {
937 if (require_fallback
&& !c
->is_fallback
)
940 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
942 r
= bus_maybe_reply_error(m
, r
, &error
);
945 if (bus
->nodes_modified
) {
954 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
957 if (!streq_ptr(previous_interface
, c
->interface
)) {
959 if (previous_interface
)
960 fputs(" </interface>\n", intro
.f
);
962 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
965 r
= introspect_write_interface(&intro
, c
->vtable
);
969 previous_interface
= c
->interface
;
972 if (previous_interface
)
973 fputs(" </interface>\n", intro
.f
);
976 /* Nothing?, let's see if we exist at all, and if not
977 * refuse to do anything */
978 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
981 if (bus
->nodes_modified
) {
987 *found_object
= true;
989 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
993 r
= introspect_finish(&intro
, bus
, m
, &reply
);
997 r
= sd_bus_send(bus
, reply
, NULL
);
1004 introspect_free(&intro
);
1008 static int object_manager_serialize_path(
1010 sd_bus_message
*reply
,
1013 bool require_fallback
,
1014 sd_bus_error
*error
) {
1016 const char *previous_interface
= NULL
;
1017 bool found_something
= false;
1018 struct node_vtable
*i
;
1028 n
= hashmap_get(bus
->nodes
, prefix
);
1032 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1035 if (require_fallback
&& !i
->is_fallback
)
1038 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1041 if (bus
->nodes_modified
)
1046 if (!found_something
) {
1048 /* Open the object part */
1050 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1054 r
= sd_bus_message_append(reply
, "o", path
);
1058 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1062 found_something
= true;
1065 if (!streq_ptr(previous_interface
, i
->interface
)) {
1067 /* Maybe close the previous interface part */
1069 if (previous_interface
) {
1070 r
= sd_bus_message_close_container(reply
);
1074 r
= sd_bus_message_close_container(reply
);
1079 /* Open the new interface part */
1081 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1085 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1089 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1094 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1097 if (bus
->nodes_modified
)
1100 previous_interface
= i
->interface
;
1103 if (previous_interface
) {
1104 r
= sd_bus_message_close_container(reply
);
1108 r
= sd_bus_message_close_container(reply
);
1113 if (found_something
) {
1114 r
= sd_bus_message_close_container(reply
);
1118 r
= sd_bus_message_close_container(reply
);
1126 static int object_manager_serialize_path_and_fallbacks(
1128 sd_bus_message
*reply
,
1130 sd_bus_error
*error
) {
1140 /* First, add all vtables registered for this path */
1141 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1144 if (bus
->nodes_modified
)
1147 /* Second, add fallback vtables registered for any of the prefixes */
1148 prefix
= alloca(strlen(path
) + 1);
1149 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1150 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1153 if (bus
->nodes_modified
)
1160 static int process_get_managed_objects(
1164 bool require_fallback
,
1165 bool *found_object
) {
1167 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1168 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1169 _cleanup_set_free_free_ Set
*s
= NULL
;
1177 assert(found_object
);
1179 /* Spec says, GetManagedObjects() is only implemented on the root of a
1180 * sub-tree. Therefore, we require a registered object-manager on
1181 * exactly the queried path, otherwise, we refuse to respond. */
1183 if (require_fallback
|| !n
->object_managers
)
1186 r
= get_child_nodes(bus
, m
->path
, n
, CHILDREN_RECURSIVE
, &s
, &error
);
1189 if (bus
->nodes_modified
)
1192 r
= sd_bus_message_new_method_return(m
, &reply
);
1196 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1200 SET_FOREACH(path
, s
, i
) {
1201 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1205 if (bus
->nodes_modified
)
1209 r
= sd_bus_message_close_container(reply
);
1213 r
= sd_bus_send(bus
, reply
, NULL
);
1220 static int object_find_and_run(
1224 bool require_fallback
,
1225 bool *found_object
) {
1228 struct vtable_member vtable_key
, *v
;
1234 assert(found_object
);
1236 n
= hashmap_get(bus
->nodes
, p
);
1240 /* First, try object callbacks */
1241 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1244 if (bus
->nodes_modified
)
1247 if (!m
->interface
|| !m
->member
)
1250 /* Then, look for a known method */
1251 vtable_key
.path
= (char*) p
;
1252 vtable_key
.interface
= m
->interface
;
1253 vtable_key
.member
= m
->member
;
1255 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1257 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1260 if (bus
->nodes_modified
)
1264 /* Then, look for a known property */
1265 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1268 get
= streq(m
->member
, "Get");
1270 if (get
|| streq(m
->member
, "Set")) {
1272 r
= sd_bus_message_rewind(m
, true);
1276 vtable_key
.path
= (char*) p
;
1278 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1280 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1282 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1284 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1289 } else if (streq(m
->member
, "GetAll")) {
1292 r
= sd_bus_message_rewind(m
, true);
1296 r
= sd_bus_message_read(m
, "s", &iface
);
1298 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1303 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1308 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1310 if (!isempty(sd_bus_message_get_signature(m
, true)))
1311 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1313 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1317 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1319 if (!isempty(sd_bus_message_get_signature(m
, true)))
1320 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1322 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1327 if (bus
->nodes_modified
)
1330 if (!*found_object
) {
1331 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1334 if (bus
->nodes_modified
)
1337 *found_object
= true;
1343 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1346 bool found_object
= false;
1351 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1354 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1357 if (hashmap_isempty(bus
->nodes
))
1360 /* Never respond to broadcast messages */
1361 if (bus
->bus_client
&& !m
->destination
)
1367 pl
= strlen(m
->path
);
1371 bus
->nodes_modified
= false;
1373 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1377 /* Look for fallback prefixes */
1378 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1380 if (bus
->nodes_modified
)
1383 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1388 } while (bus
->nodes_modified
);
1393 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1394 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1395 r
= sd_bus_reply_method_errorf(
1397 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1398 "Unknown property or interface.");
1400 r
= sd_bus_reply_method_errorf(
1402 SD_BUS_ERROR_UNKNOWN_METHOD
,
1403 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1411 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1412 struct node
*n
, *parent
;
1414 _cleanup_free_
char *s
= NULL
;
1420 assert(path
[0] == '/');
1422 n
= hashmap_get(bus
->nodes
, path
);
1426 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1434 if (streq(path
, "/"))
1437 e
= strrchr(path
, '/');
1440 p
= strndupa(path
, MAX(1, e
- path
));
1442 parent
= bus_node_allocate(bus
, p
);
1447 n
= new0(struct node
, 1);
1453 s
= NULL
; /* do not free */
1455 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1463 LIST_PREPEND(siblings
, parent
->child
, n
);
1468 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1481 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1484 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1487 bus_node_gc(b
, n
->parent
);
1491 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1497 n
= hashmap_get(bus
->nodes
, path
);
1501 prefix
= alloca(strlen(path
) + 1);
1502 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1503 n
= hashmap_get(bus
->nodes
, prefix
);
1509 while (n
&& !n
->object_managers
)
1517 static int bus_add_object(
1522 sd_bus_message_handler_t callback
,
1529 assert_return(bus
, -EINVAL
);
1530 assert_return(object_path_is_valid(path
), -EINVAL
);
1531 assert_return(callback
, -EINVAL
);
1532 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1534 n
= bus_node_allocate(bus
, path
);
1538 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1544 s
->node_callback
.callback
= callback
;
1545 s
->node_callback
.is_fallback
= fallback
;
1547 s
->node_callback
.node
= n
;
1548 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1549 bus
->nodes_modified
= true;
1557 sd_bus_slot_unref(s
);
1558 bus_node_gc(bus
, n
);
1563 _public_
int sd_bus_add_object(
1567 sd_bus_message_handler_t callback
,
1570 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1573 _public_
int sd_bus_add_fallback(
1577 sd_bus_message_handler_t callback
,
1580 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1583 static void vtable_member_hash_func(const void *a
, struct siphash
*state
) {
1584 const struct vtable_member
*m
= a
;
1588 string_hash_func(m
->path
, state
);
1589 string_hash_func(m
->interface
, state
);
1590 string_hash_func(m
->member
, state
);
1593 static int vtable_member_compare_func(const void *a
, const void *b
) {
1594 const struct vtable_member
*x
= a
, *y
= b
;
1600 r
= strcmp(x
->path
, y
->path
);
1604 r
= strcmp(x
->interface
, y
->interface
);
1608 return strcmp(x
->member
, y
->member
);
1611 static const struct hash_ops vtable_member_hash_ops
= {
1612 .hash
= vtable_member_hash_func
,
1613 .compare
= vtable_member_compare_func
1616 static int add_object_vtable_internal(
1620 const char *interface
,
1621 const sd_bus_vtable
*vtable
,
1623 sd_bus_object_find_t find
,
1626 sd_bus_slot
*s
= NULL
;
1627 struct node_vtable
*i
, *existing
= NULL
;
1628 const sd_bus_vtable
*v
;
1632 assert_return(bus
, -EINVAL
);
1633 assert_return(object_path_is_valid(path
), -EINVAL
);
1634 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1635 assert_return(vtable
, -EINVAL
);
1636 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1637 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1638 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1639 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1640 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1641 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1642 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1644 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1648 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1652 n
= bus_node_allocate(bus
, path
);
1656 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1657 if (i
->is_fallback
!= fallback
) {
1662 if (streq(i
->interface
, interface
)) {
1664 if (i
->vtable
== vtable
) {
1673 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1679 s
->node_vtable
.is_fallback
= fallback
;
1680 s
->node_vtable
.vtable
= vtable
;
1681 s
->node_vtable
.find
= find
;
1683 s
->node_vtable
.interface
= strdup(interface
);
1684 if (!s
->node_vtable
.interface
) {
1689 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1693 case _SD_BUS_VTABLE_METHOD
: {
1694 struct vtable_member
*m
;
1696 if (!member_name_is_valid(v
->x
.method
.member
) ||
1697 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1698 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1699 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1700 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1705 m
= new0(struct vtable_member
, 1);
1711 m
->parent
= &s
->node_vtable
;
1713 m
->interface
= s
->node_vtable
.interface
;
1714 m
->member
= v
->x
.method
.member
;
1717 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1726 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1728 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1733 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1740 case _SD_BUS_VTABLE_PROPERTY
: {
1741 struct vtable_member
*m
;
1743 if (!member_name_is_valid(v
->x
.property
.member
) ||
1744 !signature_is_single(v
->x
.property
.signature
, false) ||
1745 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1746 (v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
) ||
1747 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1748 ((v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) && (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)) ||
1749 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1754 m
= new0(struct vtable_member
, 1);
1760 m
->parent
= &s
->node_vtable
;
1762 m
->interface
= s
->node_vtable
.interface
;
1763 m
->member
= v
->x
.property
.member
;
1766 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1775 case _SD_BUS_VTABLE_SIGNAL
:
1777 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1778 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1779 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1792 s
->node_vtable
.node
= n
;
1793 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1794 bus
->nodes_modified
= true;
1802 sd_bus_slot_unref(s
);
1803 bus_node_gc(bus
, n
);
1808 _public_
int sd_bus_add_object_vtable(
1812 const char *interface
,
1813 const sd_bus_vtable
*vtable
,
1816 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1819 _public_
int sd_bus_add_fallback_vtable(
1823 const char *interface
,
1824 const sd_bus_vtable
*vtable
,
1825 sd_bus_object_find_t find
,
1828 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1831 _public_
int sd_bus_add_node_enumerator(
1835 sd_bus_node_enumerator_t callback
,
1842 assert_return(bus
, -EINVAL
);
1843 assert_return(object_path_is_valid(path
), -EINVAL
);
1844 assert_return(callback
, -EINVAL
);
1845 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1847 n
= bus_node_allocate(bus
, path
);
1851 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1857 s
->node_enumerator
.callback
= callback
;
1859 s
->node_enumerator
.node
= n
;
1860 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1861 bus
->nodes_modified
= true;
1869 sd_bus_slot_unref(s
);
1870 bus_node_gc(bus
, n
);
1875 static int emit_properties_changed_on_interface(
1879 const char *interface
,
1880 bool require_fallback
,
1881 bool *found_interface
,
1884 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1885 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1886 bool has_invalidating
= false, has_changing
= false;
1887 struct vtable_member key
= {};
1888 struct node_vtable
*c
;
1898 assert(found_interface
);
1900 n
= hashmap_get(bus
->nodes
, prefix
);
1904 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1908 r
= sd_bus_message_append(m
, "s", interface
);
1912 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1917 key
.interface
= interface
;
1919 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1920 if (require_fallback
&& !c
->is_fallback
)
1923 if (!streq(c
->interface
, interface
))
1926 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1929 if (bus
->nodes_modified
)
1934 *found_interface
= true;
1937 /* If the caller specified a list of
1938 * properties we include exactly those in the
1939 * PropertiesChanged message */
1941 STRV_FOREACH(property
, names
) {
1942 struct vtable_member
*v
;
1944 assert_return(member_name_is_valid(*property
), -EINVAL
);
1946 key
.member
= *property
;
1947 v
= hashmap_get(bus
->vtable_properties
, &key
);
1951 /* If there are two vtables for the same
1952 * interface, let's handle this property when
1953 * we come to that vtable. */
1957 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1958 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1960 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1962 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1963 has_invalidating
= true;
1967 has_changing
= true;
1969 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1972 if (bus
->nodes_modified
)
1976 const sd_bus_vtable
*v
;
1978 /* If the caller specified no properties list
1979 * we include all properties that are marked
1980 * as changing in the message. */
1982 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1983 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
1986 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
1989 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1990 has_invalidating
= true;
1994 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
1997 has_changing
= true;
1999 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
2002 if (bus
->nodes_modified
)
2008 if (!has_invalidating
&& !has_changing
)
2011 r
= sd_bus_message_close_container(m
);
2015 r
= sd_bus_message_open_container(m
, 'a', "s");
2019 if (has_invalidating
) {
2020 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2021 if (require_fallback
&& !c
->is_fallback
)
2024 if (!streq(c
->interface
, interface
))
2027 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2030 if (bus
->nodes_modified
)
2036 STRV_FOREACH(property
, names
) {
2037 struct vtable_member
*v
;
2039 key
.member
= *property
;
2040 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2041 assert(c
== v
->parent
);
2043 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2046 r
= sd_bus_message_append(m
, "s", *property
);
2051 const sd_bus_vtable
*v
;
2053 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2054 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
2057 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2060 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2063 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2071 r
= sd_bus_message_close_container(m
);
2075 r
= sd_bus_send(bus
, m
, NULL
);
2082 _public_
int sd_bus_emit_properties_changed_strv(
2085 const char *interface
,
2088 BUS_DONT_DESTROY(bus
);
2089 bool found_interface
= false;
2093 assert_return(bus
, -EINVAL
);
2094 assert_return(object_path_is_valid(path
), -EINVAL
);
2095 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2096 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2098 if (!BUS_IS_OPEN(bus
->state
))
2101 /* A non-NULL but empty names list means nothing needs to be
2102 generated. A NULL list OTOH indicates that all properties
2103 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2104 included in the PropertiesChanged message. */
2105 if (names
&& names
[0] == NULL
)
2109 bus
->nodes_modified
= false;
2111 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2114 if (bus
->nodes_modified
)
2117 prefix
= alloca(strlen(path
) + 1);
2118 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2119 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2122 if (bus
->nodes_modified
)
2126 } while (bus
->nodes_modified
);
2128 return found_interface
? 0 : -ENOENT
;
2131 _public_
int sd_bus_emit_properties_changed(
2134 const char *interface
,
2135 const char *name
, ...) {
2139 assert_return(bus
, -EINVAL
);
2140 assert_return(object_path_is_valid(path
), -EINVAL
);
2141 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2142 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2144 if (!BUS_IS_OPEN(bus
->state
))
2150 names
= strv_from_stdarg_alloca(name
);
2152 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2155 static int object_added_append_all_prefix(
2161 bool require_fallback
) {
2163 const char *previous_interface
= NULL
;
2164 struct node_vtable
*c
;
2174 n
= hashmap_get(bus
->nodes
, prefix
);
2178 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2179 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2182 if (require_fallback
&& !c
->is_fallback
)
2185 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2188 if (bus
->nodes_modified
)
2193 if (!streq_ptr(c
->interface
, previous_interface
)) {
2194 /* If a child-node already handled this interface, we
2195 * skip it on any of its parents. The child vtables
2196 * always fully override any conflicting vtables of
2197 * any parent node. */
2198 if (set_get(s
, c
->interface
))
2201 r
= set_put(s
, c
->interface
);
2205 if (previous_interface
) {
2206 r
= sd_bus_message_close_container(m
);
2209 r
= sd_bus_message_close_container(m
);
2214 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2217 r
= sd_bus_message_append(m
, "s", c
->interface
);
2220 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2224 previous_interface
= c
->interface
;
2227 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2230 if (bus
->nodes_modified
)
2234 if (previous_interface
) {
2235 r
= sd_bus_message_close_container(m
);
2238 r
= sd_bus_message_close_container(m
);
2246 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2247 _cleanup_set_free_ Set
*s
= NULL
;
2256 * This appends all interfaces registered on path @path. We first add
2257 * the builtin interfaces, which are always available and handled by
2258 * sd-bus. Then, we add all interfaces registered on the exact node,
2259 * followed by all fallback interfaces registered on any parent prefix.
2261 * If an interface is registered multiple times on the same node with
2262 * different vtables, we merge all the properties across all vtables.
2263 * However, if a child node has the same interface registered as one of
2264 * its parent nodes has as fallback, we make the child overwrite the
2265 * parent instead of extending it. Therefore, we keep a "Set" of all
2266 * handled interfaces during parent traversal, so we skip interfaces on
2267 * a parent that were overwritten by a child.
2270 s
= set_new(&string_hash_ops
);
2274 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2277 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2280 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2283 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2287 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2290 if (bus
->nodes_modified
)
2293 prefix
= alloca(strlen(path
) + 1);
2294 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2295 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2298 if (bus
->nodes_modified
)
2305 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2306 BUS_DONT_DESTROY(bus
);
2308 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2309 struct node
*object_manager
;
2313 * This emits an InterfacesAdded signal on the given path, by iterating
2314 * all registered vtables and fallback vtables on the path. All
2315 * properties are queried and included in the signal.
2316 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2317 * explicit list of registered interfaces. However, unlike
2318 * interfaces_added(), this call can figure out the list of supported
2319 * interfaces itself. Furthermore, it properly adds the builtin
2320 * org.freedesktop.DBus.* interfaces.
2323 assert_return(bus
, -EINVAL
);
2324 assert_return(object_path_is_valid(path
), -EINVAL
);
2325 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2327 if (!BUS_IS_OPEN(bus
->state
))
2330 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2337 bus
->nodes_modified
= false;
2338 m
= sd_bus_message_unref(m
);
2340 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2344 r
= sd_bus_message_append_basic(m
, 'o', path
);
2348 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2352 r
= object_added_append_all(bus
, m
, path
);
2356 if (bus
->nodes_modified
)
2359 r
= sd_bus_message_close_container(m
);
2363 } while (bus
->nodes_modified
);
2365 return sd_bus_send(bus
, m
, NULL
);
2368 static int object_removed_append_all_prefix(
2374 bool require_fallback
) {
2376 const char *previous_interface
= NULL
;
2377 struct node_vtable
*c
;
2387 n
= hashmap_get(bus
->nodes
, prefix
);
2391 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2392 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2395 if (require_fallback
&& !c
->is_fallback
)
2397 if (streq_ptr(c
->interface
, previous_interface
))
2400 /* If a child-node already handled this interface, we
2401 * skip it on any of its parents. The child vtables
2402 * always fully override any conflicting vtables of
2403 * any parent node. */
2404 if (set_get(s
, c
->interface
))
2407 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2410 if (bus
->nodes_modified
)
2415 r
= set_put(s
, c
->interface
);
2419 r
= sd_bus_message_append(m
, "s", c
->interface
);
2423 previous_interface
= c
->interface
;
2429 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2430 _cleanup_set_free_ Set
*s
= NULL
;
2438 /* see sd_bus_emit_object_added() for details */
2440 s
= set_new(&string_hash_ops
);
2444 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2447 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2450 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2453 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2457 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2460 if (bus
->nodes_modified
)
2463 prefix
= alloca(strlen(path
) + 1);
2464 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2465 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2468 if (bus
->nodes_modified
)
2475 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2476 BUS_DONT_DESTROY(bus
);
2478 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2479 struct node
*object_manager
;
2483 * This is like sd_bus_emit_object_added(), but emits an
2484 * InterfacesRemoved signal on the given path. This only includes any
2485 * registered interfaces but skips the properties. Note that this will
2486 * call into the find() callbacks of any registered vtable. Therefore,
2487 * you must call this function before destroying/unlinking your object.
2488 * Otherwise, the list of interfaces will be incomplete. However, note
2489 * that this will *NOT* call into any property callback. Therefore, the
2490 * object might be in an "destructed" state, as long as we can find it.
2493 assert_return(bus
, -EINVAL
);
2494 assert_return(object_path_is_valid(path
), -EINVAL
);
2495 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2497 if (!BUS_IS_OPEN(bus
->state
))
2500 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2507 bus
->nodes_modified
= false;
2508 m
= sd_bus_message_unref(m
);
2510 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2514 r
= sd_bus_message_append_basic(m
, 'o', path
);
2518 r
= sd_bus_message_open_container(m
, 'a', "s");
2522 r
= object_removed_append_all(bus
, m
, path
);
2526 if (bus
->nodes_modified
)
2529 r
= sd_bus_message_close_container(m
);
2533 } while (bus
->nodes_modified
);
2535 return sd_bus_send(bus
, m
, NULL
);
2538 static int interfaces_added_append_one_prefix(
2543 const char *interface
,
2544 bool require_fallback
) {
2546 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2547 bool found_interface
= false;
2548 struct node_vtable
*c
;
2559 n
= hashmap_get(bus
->nodes
, prefix
);
2563 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2564 if (require_fallback
&& !c
->is_fallback
)
2567 if (!streq(c
->interface
, interface
))
2570 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2573 if (bus
->nodes_modified
)
2578 if (!found_interface
) {
2579 r
= sd_bus_message_append_basic(m
, 's', interface
);
2583 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2587 found_interface
= true;
2590 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2593 if (bus
->nodes_modified
)
2597 if (found_interface
) {
2598 r
= sd_bus_message_close_container(m
);
2603 return found_interface
;
2606 static int interfaces_added_append_one(
2610 const char *interface
) {
2620 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2623 if (bus
->nodes_modified
)
2626 prefix
= alloca(strlen(path
) + 1);
2627 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2628 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2631 if (bus
->nodes_modified
)
2638 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2639 BUS_DONT_DESTROY(bus
);
2641 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2642 struct node
*object_manager
;
2646 assert_return(bus
, -EINVAL
);
2647 assert_return(object_path_is_valid(path
), -EINVAL
);
2648 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2650 if (!BUS_IS_OPEN(bus
->state
))
2653 if (strv_isempty(interfaces
))
2656 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2663 bus
->nodes_modified
= false;
2664 m
= sd_bus_message_unref(m
);
2666 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2670 r
= sd_bus_message_append_basic(m
, 'o', path
);
2674 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2678 STRV_FOREACH(i
, interfaces
) {
2679 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2681 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2685 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2689 if (bus
->nodes_modified
)
2692 r
= sd_bus_message_close_container(m
);
2697 if (bus
->nodes_modified
)
2700 r
= sd_bus_message_close_container(m
);
2704 } while (bus
->nodes_modified
);
2706 return sd_bus_send(bus
, m
, NULL
);
2709 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2712 assert_return(bus
, -EINVAL
);
2713 assert_return(object_path_is_valid(path
), -EINVAL
);
2714 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2716 if (!BUS_IS_OPEN(bus
->state
))
2719 interfaces
= strv_from_stdarg_alloca(interface
);
2721 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2724 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2725 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2726 struct node
*object_manager
;
2729 assert_return(bus
, -EINVAL
);
2730 assert_return(object_path_is_valid(path
), -EINVAL
);
2731 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2733 if (!BUS_IS_OPEN(bus
->state
))
2736 if (strv_isempty(interfaces
))
2739 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2745 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2749 r
= sd_bus_message_append_basic(m
, 'o', path
);
2753 r
= sd_bus_message_append_strv(m
, interfaces
);
2757 return sd_bus_send(bus
, m
, NULL
);
2760 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2763 assert_return(bus
, -EINVAL
);
2764 assert_return(object_path_is_valid(path
), -EINVAL
);
2765 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2767 if (!BUS_IS_OPEN(bus
->state
))
2770 interfaces
= strv_from_stdarg_alloca(interface
);
2772 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2775 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2780 assert_return(bus
, -EINVAL
);
2781 assert_return(object_path_is_valid(path
), -EINVAL
);
2782 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2784 n
= bus_node_allocate(bus
, path
);
2788 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2794 s
->node_object_manager
.node
= n
;
2795 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2796 bus
->nodes_modified
= true;
2804 sd_bus_slot_unref(s
);
2805 bus_node_gc(bus
, n
);