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
);
173 /* if set, add_subtree() works recursively */
174 CHILDREN_RECURSIVE
= (1U << 1),
175 /* if set, add_subtree() scans object-manager hierarchies recursively */
176 CHILDREN_SUBHIERARCHIES
= (1U << 0),
179 static int add_subtree_to_set(
185 sd_bus_error
*error
) {
195 r
= add_enumerated_to_set(bus
, prefix
, n
->enumerators
, s
, error
);
198 if (bus
->nodes_modified
)
201 LIST_FOREACH(siblings
, i
, n
->child
) {
204 if (!object_path_startswith(i
->path
, prefix
))
211 r
= set_consume(s
, t
);
212 if (r
< 0 && r
!= -EEXIST
)
215 if ((flags
& CHILDREN_RECURSIVE
) &&
216 ((flags
& CHILDREN_SUBHIERARCHIES
) || !i
->object_managers
)) {
217 r
= add_subtree_to_set(bus
, prefix
, i
, flags
, s
, error
);
220 if (bus
->nodes_modified
)
228 static int get_child_nodes(
234 sd_bus_error
*error
) {
244 s
= set_new(&string_hash_ops
);
248 r
= add_subtree_to_set(bus
, prefix
, n
, flags
, s
, error
);
258 static int node_callbacks_run(
261 struct node_callback
*first
,
262 bool require_fallback
,
263 bool *found_object
) {
265 struct node_callback
*c
;
270 assert(found_object
);
272 LIST_FOREACH(callbacks
, c
, first
) {
273 _cleanup_bus_error_free_ sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
276 if (bus
->nodes_modified
)
279 if (require_fallback
&& !c
->is_fallback
)
282 *found_object
= true;
284 if (c
->last_iteration
== bus
->iteration_counter
)
287 c
->last_iteration
= bus
->iteration_counter
;
289 r
= sd_bus_message_rewind(m
, true);
293 slot
= container_of(c
, sd_bus_slot
, node_callback
);
295 bus
->current_slot
= sd_bus_slot_ref(slot
);
296 bus
->current_handler
= c
->callback
;
297 bus
->current_userdata
= slot
->userdata
;
298 r
= c
->callback(m
, slot
->userdata
, &error_buffer
);
299 bus
->current_userdata
= NULL
;
300 bus
->current_handler
= NULL
;
301 bus
->current_slot
= sd_bus_slot_unref(slot
);
303 r
= bus_maybe_reply_error(m
, r
, &error_buffer
);
311 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
313 static int check_access(sd_bus
*bus
, sd_bus_message
*m
, struct vtable_member
*c
, sd_bus_error
*error
) {
321 /* If the entire bus is trusted let's grant access */
325 /* If the member is marked UNPRIVILEGED let's grant access */
326 if (c
->vtable
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
)
329 /* Check have the caller has the requested capability
330 * set. Note that the flags value contains the capability
331 * number plus one, which we need to subtract here. We do this
332 * so that we have 0 as special value for "default
334 cap
= CAPABILITY_SHIFT(c
->vtable
->flags
);
336 cap
= CAPABILITY_SHIFT(c
->parent
->vtable
[0].flags
);
342 r
= sd_bus_query_sender_privilege(m
, cap
);
348 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Access to %s.%s() not permitted.", c
->interface
, c
->member
);
351 static int method_callbacks_run(
354 struct vtable_member
*c
,
355 bool require_fallback
,
356 bool *found_object
) {
358 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
359 const char *signature
;
366 assert(found_object
);
368 if (require_fallback
&& !c
->parent
->is_fallback
)
371 r
= check_access(bus
, m
, c
, &error
);
373 return bus_maybe_reply_error(m
, r
, &error
);
375 r
= node_vtable_get_userdata(bus
, m
->path
, c
->parent
, &u
, &error
);
377 return bus_maybe_reply_error(m
, r
, &error
);
378 if (bus
->nodes_modified
)
381 u
= vtable_method_convert_userdata(c
->vtable
, u
);
383 *found_object
= true;
385 if (c
->last_iteration
== bus
->iteration_counter
)
388 c
->last_iteration
= bus
->iteration_counter
;
390 r
= sd_bus_message_rewind(m
, true);
394 signature
= sd_bus_message_get_signature(m
, true);
398 if (!streq(strempty(c
->vtable
->x
.method
.signature
), signature
))
399 return sd_bus_reply_method_errorf(
401 SD_BUS_ERROR_INVALID_ARGS
,
402 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
403 signature
, c
->interface
, c
->member
, strempty(c
->vtable
->x
.method
.signature
));
405 /* Keep track what the signature of the reply to this message
406 * should be, so that this can be enforced when sealing the
408 m
->enforced_reply_signature
= strempty(c
->vtable
->x
.method
.result
);
410 if (c
->vtable
->x
.method
.handler
) {
413 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
415 bus
->current_slot
= sd_bus_slot_ref(slot
);
416 bus
->current_handler
= c
->vtable
->x
.method
.handler
;
417 bus
->current_userdata
= u
;
418 r
= c
->vtable
->x
.method
.handler(m
, u
, &error
);
419 bus
->current_userdata
= NULL
;
420 bus
->current_handler
= NULL
;
421 bus
->current_slot
= sd_bus_slot_unref(slot
);
423 return bus_maybe_reply_error(m
, r
, &error
);
426 /* If the method callback is NULL, make this a successful NOP */
427 r
= sd_bus_reply_method_return(m
, NULL
);
434 static int invoke_property_get(
437 const sd_bus_vtable
*v
,
439 const char *interface
,
440 const char *property
,
441 sd_bus_message
*reply
,
443 sd_bus_error
*error
) {
456 if (v
->x
.property
.get
) {
458 bus
->current_slot
= sd_bus_slot_ref(slot
);
459 bus
->current_userdata
= userdata
;
460 r
= v
->x
.property
.get(bus
, path
, interface
, property
, reply
, userdata
, error
);
461 bus
->current_userdata
= NULL
;
462 bus
->current_slot
= sd_bus_slot_unref(slot
);
466 if (sd_bus_error_is_set(error
))
467 return -sd_bus_error_get_errno(error
);
471 /* Automatic handling if no callback is defined. */
473 if (streq(v
->x
.property
.signature
, "as"))
474 return sd_bus_message_append_strv(reply
, *(char***) userdata
);
476 assert(signature_is_single(v
->x
.property
.signature
, false));
477 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
479 switch (v
->x
.property
.signature
[0]) {
481 case SD_BUS_TYPE_STRING
:
482 case SD_BUS_TYPE_SIGNATURE
:
483 p
= strempty(*(char**) userdata
);
486 case SD_BUS_TYPE_OBJECT_PATH
:
487 p
= *(char**) userdata
;
496 return sd_bus_message_append_basic(reply
, v
->x
.property
.signature
[0], p
);
499 static int invoke_property_set(
502 const sd_bus_vtable
*v
,
504 const char *interface
,
505 const char *property
,
506 sd_bus_message
*value
,
508 sd_bus_error
*error
) {
520 if (v
->x
.property
.set
) {
522 bus
->current_slot
= sd_bus_slot_ref(slot
);
523 bus
->current_userdata
= userdata
;
524 r
= v
->x
.property
.set(bus
, path
, interface
, property
, value
, userdata
, error
);
525 bus
->current_userdata
= NULL
;
526 bus
->current_slot
= sd_bus_slot_unref(slot
);
530 if (sd_bus_error_is_set(error
))
531 return -sd_bus_error_get_errno(error
);
535 /* Automatic handling if no callback is defined. */
537 assert(signature_is_single(v
->x
.property
.signature
, false));
538 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
540 switch (v
->x
.property
.signature
[0]) {
542 case SD_BUS_TYPE_STRING
:
543 case SD_BUS_TYPE_OBJECT_PATH
:
544 case SD_BUS_TYPE_SIGNATURE
: {
548 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], &p
);
556 free(*(char**) userdata
);
557 *(char**) userdata
= n
;
563 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], userdata
);
573 static int property_get_set_callbacks_run(
576 struct vtable_member
*c
,
577 bool require_fallback
,
579 bool *found_object
) {
581 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
582 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
590 assert(found_object
);
592 if (require_fallback
&& !c
->parent
->is_fallback
)
595 r
= vtable_property_get_userdata(bus
, m
->path
, c
, &u
, &error
);
597 return bus_maybe_reply_error(m
, r
, &error
);
598 if (bus
->nodes_modified
)
601 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
603 *found_object
= true;
605 r
= sd_bus_message_new_method_return(m
, &reply
);
610 /* Note that we do not protect against reexecution
611 * here (using the last_iteration check, see below),
612 * should the node tree have changed and we got called
613 * again. We assume that property Get() calls are
614 * ultimately without side-effects or if they aren't
615 * then at least idempotent. */
617 r
= sd_bus_message_open_container(reply
, 'v', c
->vtable
->x
.property
.signature
);
621 /* Note that we do not do an access check here. Read
622 * access to properties is always unrestricted, since
623 * PropertiesChanged signals broadcast contents
626 r
= invoke_property_get(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, reply
, u
, &error
);
628 return bus_maybe_reply_error(m
, r
, &error
);
630 if (bus
->nodes_modified
)
633 r
= sd_bus_message_close_container(reply
);
638 const char *signature
= NULL
;
641 if (c
->vtable
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
642 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_PROPERTY_READ_ONLY
, "Property '%s' is not writable.", c
->member
);
644 /* Avoid that we call the set routine more than once
645 * if the processing of this message got restarted
646 * because the node tree changed. */
647 if (c
->last_iteration
== bus
->iteration_counter
)
650 c
->last_iteration
= bus
->iteration_counter
;
652 r
= sd_bus_message_peek_type(m
, &type
, &signature
);
656 if (type
!= 'v' || !streq(strempty(signature
), strempty(c
->vtable
->x
.property
.signature
)))
657 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
));
659 r
= sd_bus_message_enter_container(m
, 'v', c
->vtable
->x
.property
.signature
);
663 r
= check_access(bus
, m
, c
, &error
);
665 return bus_maybe_reply_error(m
, r
, &error
);
667 r
= invoke_property_set(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, m
, u
, &error
);
669 return bus_maybe_reply_error(m
, r
, &error
);
671 if (bus
->nodes_modified
)
674 r
= sd_bus_message_exit_container(m
);
679 r
= sd_bus_send(bus
, reply
, NULL
);
686 static int vtable_append_one_property(
688 sd_bus_message
*reply
,
690 struct node_vtable
*c
,
691 const sd_bus_vtable
*v
,
693 sd_bus_error
*error
) {
704 r
= sd_bus_message_open_container(reply
, 'e', "sv");
708 r
= sd_bus_message_append(reply
, "s", v
->x
.property
.member
);
712 r
= sd_bus_message_open_container(reply
, 'v', v
->x
.property
.signature
);
716 slot
= container_of(c
, sd_bus_slot
, node_vtable
);
718 r
= invoke_property_get(bus
, slot
, v
, path
, c
->interface
, v
->x
.property
.member
, reply
, vtable_property_convert_userdata(v
, userdata
), error
);
721 if (bus
->nodes_modified
)
724 r
= sd_bus_message_close_container(reply
);
728 r
= sd_bus_message_close_container(reply
);
735 static int vtable_append_all_properties(
737 sd_bus_message
*reply
,
739 struct node_vtable
*c
,
741 sd_bus_error
*error
) {
743 const sd_bus_vtable
*v
;
751 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
754 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
755 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
758 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
761 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)
764 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
767 if (bus
->nodes_modified
)
774 static int property_get_all_callbacks_run(
777 struct node_vtable
*first
,
778 bool require_fallback
,
780 bool *found_object
) {
782 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
783 struct node_vtable
*c
;
784 bool found_interface
;
789 assert(found_object
);
791 r
= sd_bus_message_new_method_return(m
, &reply
);
795 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
799 found_interface
= !iface
||
800 streq(iface
, "org.freedesktop.DBus.Properties") ||
801 streq(iface
, "org.freedesktop.DBus.Peer") ||
802 streq(iface
, "org.freedesktop.DBus.Introspectable");
804 LIST_FOREACH(vtables
, c
, first
) {
805 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
808 if (require_fallback
&& !c
->is_fallback
)
811 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
813 return bus_maybe_reply_error(m
, r
, &error
);
814 if (bus
->nodes_modified
)
819 *found_object
= true;
821 if (iface
&& !streq(c
->interface
, iface
))
823 found_interface
= true;
825 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
827 return bus_maybe_reply_error(m
, r
, &error
);
828 if (bus
->nodes_modified
)
832 if (!found_interface
) {
833 r
= sd_bus_reply_method_errorf(
835 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
836 "Unknown interface '%s'.", iface
);
843 r
= sd_bus_message_close_container(reply
);
847 r
= sd_bus_send(bus
, reply
, NULL
);
854 static int bus_node_exists(
858 bool require_fallback
) {
860 struct node_vtable
*c
;
861 struct node_callback
*k
;
868 /* Tests if there's anything attached directly to this node
869 * for the specified path */
871 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
874 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
875 if (require_fallback
&& !k
->is_fallback
)
881 LIST_FOREACH(vtables
, c
, n
->vtables
) {
882 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
884 if (require_fallback
&& !c
->is_fallback
)
887 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
890 if (bus
->nodes_modified
)
897 static int process_introspect(
901 bool require_fallback
,
902 bool *found_object
) {
904 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
905 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
906 _cleanup_set_free_free_ Set
*s
= NULL
;
907 const char *previous_interface
= NULL
;
908 struct introspect intro
;
909 struct node_vtable
*c
;
916 assert(found_object
);
918 r
= get_child_nodes(bus
, m
->path
, n
, 0, &s
, &error
);
920 return bus_maybe_reply_error(m
, r
, &error
);
921 if (bus
->nodes_modified
)
924 r
= introspect_begin(&intro
, bus
->trusted
);
928 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
932 empty
= set_isempty(s
);
934 LIST_FOREACH(vtables
, c
, n
->vtables
) {
935 if (require_fallback
&& !c
->is_fallback
)
938 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
940 r
= bus_maybe_reply_error(m
, r
, &error
);
943 if (bus
->nodes_modified
) {
952 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
955 if (!streq_ptr(previous_interface
, c
->interface
)) {
957 if (previous_interface
)
958 fputs(" </interface>\n", intro
.f
);
960 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
963 r
= introspect_write_interface(&intro
, c
->vtable
);
967 previous_interface
= c
->interface
;
970 if (previous_interface
)
971 fputs(" </interface>\n", intro
.f
);
974 /* Nothing?, let's see if we exist at all, and if not
975 * refuse to do anything */
976 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
979 if (bus
->nodes_modified
) {
985 *found_object
= true;
987 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
991 r
= introspect_finish(&intro
, bus
, m
, &reply
);
995 r
= sd_bus_send(bus
, reply
, NULL
);
1002 introspect_free(&intro
);
1006 static int object_manager_serialize_path(
1008 sd_bus_message
*reply
,
1011 bool require_fallback
,
1012 sd_bus_error
*error
) {
1014 const char *previous_interface
= NULL
;
1015 bool found_something
= false;
1016 struct node_vtable
*i
;
1026 n
= hashmap_get(bus
->nodes
, prefix
);
1030 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1033 if (require_fallback
&& !i
->is_fallback
)
1036 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1039 if (bus
->nodes_modified
)
1044 if (!found_something
) {
1046 /* Open the object part */
1048 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1052 r
= sd_bus_message_append(reply
, "o", path
);
1056 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1060 found_something
= true;
1063 if (!streq_ptr(previous_interface
, i
->interface
)) {
1065 /* Maybe close the previous interface part */
1067 if (previous_interface
) {
1068 r
= sd_bus_message_close_container(reply
);
1072 r
= sd_bus_message_close_container(reply
);
1077 /* Open the new interface part */
1079 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1083 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1087 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1092 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1095 if (bus
->nodes_modified
)
1098 previous_interface
= i
->interface
;
1101 if (previous_interface
) {
1102 r
= sd_bus_message_close_container(reply
);
1106 r
= sd_bus_message_close_container(reply
);
1111 if (found_something
) {
1112 r
= sd_bus_message_close_container(reply
);
1116 r
= sd_bus_message_close_container(reply
);
1124 static int object_manager_serialize_path_and_fallbacks(
1126 sd_bus_message
*reply
,
1128 sd_bus_error
*error
) {
1138 /* First, add all vtables registered for this path */
1139 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1142 if (bus
->nodes_modified
)
1145 /* Second, add fallback vtables registered for any of the prefixes */
1146 prefix
= alloca(strlen(path
) + 1);
1147 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1148 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1151 if (bus
->nodes_modified
)
1158 static int process_get_managed_objects(
1162 bool require_fallback
,
1163 bool *found_object
) {
1165 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1166 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1167 _cleanup_set_free_free_ Set
*s
= NULL
;
1175 assert(found_object
);
1177 /* Spec says, GetManagedObjects() is only implemented on the root of a
1178 * sub-tree. Therefore, we require a registered object-manager on
1179 * exactly the queried path, otherwise, we refuse to respond. */
1181 if (require_fallback
|| !n
->object_managers
)
1184 r
= get_child_nodes(bus
, m
->path
, n
, CHILDREN_RECURSIVE
, &s
, &error
);
1187 if (bus
->nodes_modified
)
1190 r
= sd_bus_message_new_method_return(m
, &reply
);
1194 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1198 SET_FOREACH(path
, s
, i
) {
1199 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1203 if (bus
->nodes_modified
)
1207 r
= sd_bus_message_close_container(reply
);
1211 r
= sd_bus_send(bus
, reply
, NULL
);
1218 static int object_find_and_run(
1222 bool require_fallback
,
1223 bool *found_object
) {
1226 struct vtable_member vtable_key
, *v
;
1232 assert(found_object
);
1234 n
= hashmap_get(bus
->nodes
, p
);
1238 /* First, try object callbacks */
1239 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1242 if (bus
->nodes_modified
)
1245 if (!m
->interface
|| !m
->member
)
1248 /* Then, look for a known method */
1249 vtable_key
.path
= (char*) p
;
1250 vtable_key
.interface
= m
->interface
;
1251 vtable_key
.member
= m
->member
;
1253 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1255 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1258 if (bus
->nodes_modified
)
1262 /* Then, look for a known property */
1263 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1266 get
= streq(m
->member
, "Get");
1268 if (get
|| streq(m
->member
, "Set")) {
1270 r
= sd_bus_message_rewind(m
, true);
1274 vtable_key
.path
= (char*) p
;
1276 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1278 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1280 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1282 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1287 } else if (streq(m
->member
, "GetAll")) {
1290 r
= sd_bus_message_rewind(m
, true);
1294 r
= sd_bus_message_read(m
, "s", &iface
);
1296 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1301 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1306 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1308 if (!isempty(sd_bus_message_get_signature(m
, true)))
1309 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1311 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1315 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1317 if (!isempty(sd_bus_message_get_signature(m
, true)))
1318 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1320 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1325 if (bus
->nodes_modified
)
1328 if (!*found_object
) {
1329 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1332 if (bus
->nodes_modified
)
1335 *found_object
= true;
1341 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1344 bool found_object
= false;
1349 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1352 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1355 if (hashmap_isempty(bus
->nodes
))
1358 /* Never respond to broadcast messages */
1359 if (bus
->bus_client
&& !m
->destination
)
1365 pl
= strlen(m
->path
);
1369 bus
->nodes_modified
= false;
1371 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1375 /* Look for fallback prefixes */
1376 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1378 if (bus
->nodes_modified
)
1381 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1386 } while (bus
->nodes_modified
);
1391 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1392 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1393 r
= sd_bus_reply_method_errorf(
1395 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1396 "Unknown property or interface.");
1398 r
= sd_bus_reply_method_errorf(
1400 SD_BUS_ERROR_UNKNOWN_METHOD
,
1401 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1409 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1410 struct node
*n
, *parent
;
1412 _cleanup_free_
char *s
= NULL
;
1418 assert(path
[0] == '/');
1420 n
= hashmap_get(bus
->nodes
, path
);
1424 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1432 if (streq(path
, "/"))
1435 e
= strrchr(path
, '/');
1438 p
= strndupa(path
, MAX(1, e
- path
));
1440 parent
= bus_node_allocate(bus
, p
);
1445 n
= new0(struct node
, 1);
1451 s
= NULL
; /* do not free */
1453 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1461 LIST_PREPEND(siblings
, parent
->child
, n
);
1466 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1479 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1482 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1485 bus_node_gc(b
, n
->parent
);
1489 static int bus_find_parent_object_manager(sd_bus
*bus
, struct node
**out
, const char *path
) {
1495 n
= hashmap_get(bus
->nodes
, path
);
1499 prefix
= alloca(strlen(path
) + 1);
1500 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1501 n
= hashmap_get(bus
->nodes
, prefix
);
1507 while (n
&& !n
->object_managers
)
1515 static int bus_add_object(
1520 sd_bus_message_handler_t callback
,
1527 assert_return(bus
, -EINVAL
);
1528 assert_return(object_path_is_valid(path
), -EINVAL
);
1529 assert_return(callback
, -EINVAL
);
1530 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1532 n
= bus_node_allocate(bus
, path
);
1536 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1542 s
->node_callback
.callback
= callback
;
1543 s
->node_callback
.is_fallback
= fallback
;
1545 s
->node_callback
.node
= n
;
1546 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1547 bus
->nodes_modified
= true;
1555 sd_bus_slot_unref(s
);
1556 bus_node_gc(bus
, n
);
1561 _public_
int sd_bus_add_object(
1565 sd_bus_message_handler_t callback
,
1568 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1571 _public_
int sd_bus_add_fallback(
1575 sd_bus_message_handler_t callback
,
1578 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1581 static unsigned long vtable_member_hash_func(const void *a
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
1582 const struct vtable_member
*m
= a
;
1583 uint8_t hash_key2
[HASH_KEY_SIZE
];
1588 ret
= string_hash_func(m
->path
, hash_key
);
1590 /* Use a slightly different hash key for the interface */
1591 memcpy(hash_key2
, hash_key
, HASH_KEY_SIZE
);
1593 ret
^= string_hash_func(m
->interface
, hash_key2
);
1595 /* And an even different one for the member */
1597 ret
^= string_hash_func(m
->member
, hash_key2
);
1602 static int vtable_member_compare_func(const void *a
, const void *b
) {
1603 const struct vtable_member
*x
= a
, *y
= b
;
1609 r
= strcmp(x
->path
, y
->path
);
1613 r
= strcmp(x
->interface
, y
->interface
);
1617 return strcmp(x
->member
, y
->member
);
1620 static const struct hash_ops vtable_member_hash_ops
= {
1621 .hash
= vtable_member_hash_func
,
1622 .compare
= vtable_member_compare_func
1625 static int add_object_vtable_internal(
1629 const char *interface
,
1630 const sd_bus_vtable
*vtable
,
1632 sd_bus_object_find_t find
,
1635 sd_bus_slot
*s
= NULL
;
1636 struct node_vtable
*i
, *existing
= NULL
;
1637 const sd_bus_vtable
*v
;
1641 assert_return(bus
, -EINVAL
);
1642 assert_return(object_path_is_valid(path
), -EINVAL
);
1643 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1644 assert_return(vtable
, -EINVAL
);
1645 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1646 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1647 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1648 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1649 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1650 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1651 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1653 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1657 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1661 n
= bus_node_allocate(bus
, path
);
1665 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1666 if (i
->is_fallback
!= fallback
) {
1671 if (streq(i
->interface
, interface
)) {
1673 if (i
->vtable
== vtable
) {
1682 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1688 s
->node_vtable
.is_fallback
= fallback
;
1689 s
->node_vtable
.vtable
= vtable
;
1690 s
->node_vtable
.find
= find
;
1692 s
->node_vtable
.interface
= strdup(interface
);
1693 if (!s
->node_vtable
.interface
) {
1698 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1702 case _SD_BUS_VTABLE_METHOD
: {
1703 struct vtable_member
*m
;
1705 if (!member_name_is_valid(v
->x
.method
.member
) ||
1706 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1707 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1708 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1709 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1714 m
= new0(struct vtable_member
, 1);
1720 m
->parent
= &s
->node_vtable
;
1722 m
->interface
= s
->node_vtable
.interface
;
1723 m
->member
= v
->x
.method
.member
;
1726 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1735 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1737 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1742 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) {
1749 case _SD_BUS_VTABLE_PROPERTY
: {
1750 struct vtable_member
*m
;
1752 if (!member_name_is_valid(v
->x
.property
.member
) ||
1753 !signature_is_single(v
->x
.property
.signature
, false) ||
1754 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1755 (v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
) ||
1756 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1757 ((v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) && (v
->flags
& SD_BUS_VTABLE_PROPERTY_EXPLICIT
)) ||
1758 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1763 m
= new0(struct vtable_member
, 1);
1769 m
->parent
= &s
->node_vtable
;
1771 m
->interface
= s
->node_vtable
.interface
;
1772 m
->member
= v
->x
.property
.member
;
1775 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1784 case _SD_BUS_VTABLE_SIGNAL
:
1786 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1787 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1788 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1801 s
->node_vtable
.node
= n
;
1802 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1803 bus
->nodes_modified
= true;
1811 sd_bus_slot_unref(s
);
1812 bus_node_gc(bus
, n
);
1817 _public_
int sd_bus_add_object_vtable(
1821 const char *interface
,
1822 const sd_bus_vtable
*vtable
,
1825 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1828 _public_
int sd_bus_add_fallback_vtable(
1832 const char *interface
,
1833 const sd_bus_vtable
*vtable
,
1834 sd_bus_object_find_t find
,
1837 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1840 _public_
int sd_bus_add_node_enumerator(
1844 sd_bus_node_enumerator_t callback
,
1851 assert_return(bus
, -EINVAL
);
1852 assert_return(object_path_is_valid(path
), -EINVAL
);
1853 assert_return(callback
, -EINVAL
);
1854 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1856 n
= bus_node_allocate(bus
, path
);
1860 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1866 s
->node_enumerator
.callback
= callback
;
1868 s
->node_enumerator
.node
= n
;
1869 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1870 bus
->nodes_modified
= true;
1878 sd_bus_slot_unref(s
);
1879 bus_node_gc(bus
, n
);
1884 static int emit_properties_changed_on_interface(
1888 const char *interface
,
1889 bool require_fallback
,
1890 bool *found_interface
,
1893 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1894 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1895 bool has_invalidating
= false, has_changing
= false;
1896 struct vtable_member key
= {};
1897 struct node_vtable
*c
;
1907 assert(found_interface
);
1909 n
= hashmap_get(bus
->nodes
, prefix
);
1913 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1917 r
= sd_bus_message_append(m
, "s", interface
);
1921 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1926 key
.interface
= interface
;
1928 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1929 if (require_fallback
&& !c
->is_fallback
)
1932 if (!streq(c
->interface
, interface
))
1935 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1938 if (bus
->nodes_modified
)
1943 *found_interface
= true;
1946 /* If the caller specified a list of
1947 * properties we include exactly those in the
1948 * PropertiesChanged message */
1950 STRV_FOREACH(property
, names
) {
1951 struct vtable_member
*v
;
1953 assert_return(member_name_is_valid(*property
), -EINVAL
);
1955 key
.member
= *property
;
1956 v
= hashmap_get(bus
->vtable_properties
, &key
);
1960 /* If there are two vtables for the same
1961 * interface, let's handle this property when
1962 * we come to that vtable. */
1966 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1967 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1969 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1971 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1972 has_invalidating
= true;
1976 has_changing
= true;
1978 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1981 if (bus
->nodes_modified
)
1985 const sd_bus_vtable
*v
;
1987 /* If the caller specified no properties list
1988 * we include all properties that are marked
1989 * as changing in the message. */
1991 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1992 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
1995 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
1998 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1999 has_invalidating
= true;
2003 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
2006 has_changing
= true;
2008 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
2011 if (bus
->nodes_modified
)
2017 if (!has_invalidating
&& !has_changing
)
2020 r
= sd_bus_message_close_container(m
);
2024 r
= sd_bus_message_open_container(m
, 'a', "s");
2028 if (has_invalidating
) {
2029 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2030 if (require_fallback
&& !c
->is_fallback
)
2033 if (!streq(c
->interface
, interface
))
2036 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2039 if (bus
->nodes_modified
)
2045 STRV_FOREACH(property
, names
) {
2046 struct vtable_member
*v
;
2048 key
.member
= *property
;
2049 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2050 assert(c
== v
->parent
);
2052 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2055 r
= sd_bus_message_append(m
, "s", *property
);
2060 const sd_bus_vtable
*v
;
2062 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2063 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
2066 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2069 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2072 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2080 r
= sd_bus_message_close_container(m
);
2084 r
= sd_bus_send(bus
, m
, NULL
);
2091 _public_
int sd_bus_emit_properties_changed_strv(
2094 const char *interface
,
2097 BUS_DONT_DESTROY(bus
);
2098 bool found_interface
= false;
2102 assert_return(bus
, -EINVAL
);
2103 assert_return(object_path_is_valid(path
), -EINVAL
);
2104 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2105 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2107 if (!BUS_IS_OPEN(bus
->state
))
2110 /* A non-NULL but empty names list means nothing needs to be
2111 generated. A NULL list OTOH indicates that all properties
2112 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2113 included in the PropertiesChanged message. */
2114 if (names
&& names
[0] == NULL
)
2118 bus
->nodes_modified
= false;
2120 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2123 if (bus
->nodes_modified
)
2126 prefix
= alloca(strlen(path
) + 1);
2127 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2128 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2131 if (bus
->nodes_modified
)
2135 } while (bus
->nodes_modified
);
2137 return found_interface
? 0 : -ENOENT
;
2140 _public_
int sd_bus_emit_properties_changed(
2143 const char *interface
,
2144 const char *name
, ...) {
2148 assert_return(bus
, -EINVAL
);
2149 assert_return(object_path_is_valid(path
), -EINVAL
);
2150 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2151 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2153 if (!BUS_IS_OPEN(bus
->state
))
2159 names
= strv_from_stdarg_alloca(name
);
2161 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2164 static int object_added_append_all_prefix(
2170 bool require_fallback
) {
2172 const char *previous_interface
= NULL
;
2173 struct node_vtable
*c
;
2183 n
= hashmap_get(bus
->nodes
, prefix
);
2187 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2188 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2191 if (require_fallback
&& !c
->is_fallback
)
2194 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2197 if (bus
->nodes_modified
)
2202 if (!streq_ptr(c
->interface
, previous_interface
)) {
2203 /* If a child-node already handled this interface, we
2204 * skip it on any of its parents. The child vtables
2205 * always fully override any conflicting vtables of
2206 * any parent node. */
2207 if (set_get(s
, c
->interface
))
2210 r
= set_put(s
, c
->interface
);
2214 if (previous_interface
) {
2215 r
= sd_bus_message_close_container(m
);
2218 r
= sd_bus_message_close_container(m
);
2223 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2226 r
= sd_bus_message_append(m
, "s", c
->interface
);
2229 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2233 previous_interface
= c
->interface
;
2236 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2239 if (bus
->nodes_modified
)
2243 if (previous_interface
) {
2244 r
= sd_bus_message_close_container(m
);
2247 r
= sd_bus_message_close_container(m
);
2255 static int object_added_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2256 _cleanup_set_free_ Set
*s
= NULL
;
2265 * This appends all interfaces registered on path @path. We first add
2266 * the builtin interfaces, which are always available and handled by
2267 * sd-bus. Then, we add all interfaces registered on the exact node,
2268 * followed by all fallback interfaces registered on any parent prefix.
2270 * If an interface is registered multiple times on the same node with
2271 * different vtables, we merge all the properties across all vtables.
2272 * However, if a child node has the same interface registered as one of
2273 * its parent nodes has as fallback, we make the child overwrite the
2274 * parent instead of extending it. Therefore, we keep a "Set" of all
2275 * handled interfaces during parent traversal, so we skip interfaces on
2276 * a parent that were overwritten by a child.
2279 s
= set_new(&string_hash_ops
);
2283 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2286 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2289 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2292 r
= sd_bus_message_append(m
, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2296 r
= object_added_append_all_prefix(bus
, m
, s
, path
, path
, false);
2299 if (bus
->nodes_modified
)
2302 prefix
= alloca(strlen(path
) + 1);
2303 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2304 r
= object_added_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2307 if (bus
->nodes_modified
)
2314 _public_
int sd_bus_emit_object_added(sd_bus
*bus
, const char *path
) {
2315 BUS_DONT_DESTROY(bus
);
2317 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2318 struct node
*object_manager
;
2322 * This emits an InterfacesAdded signal on the given path, by iterating
2323 * all registered vtables and fallback vtables on the path. All
2324 * properties are queried and included in the signal.
2325 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2326 * explicit list of registered interfaces. However, unlike
2327 * interfaces_added(), this call can figure out the list of supported
2328 * interfaces itself. Furthermore, it properly adds the builtin
2329 * org.freedesktop.DBus.* interfaces.
2332 assert_return(bus
, -EINVAL
);
2333 assert_return(object_path_is_valid(path
), -EINVAL
);
2334 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2336 if (!BUS_IS_OPEN(bus
->state
))
2339 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2346 bus
->nodes_modified
= false;
2347 m
= sd_bus_message_unref(m
);
2349 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2353 r
= sd_bus_message_append_basic(m
, 'o', path
);
2357 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2361 r
= object_added_append_all(bus
, m
, path
);
2365 if (bus
->nodes_modified
)
2368 r
= sd_bus_message_close_container(m
);
2372 } while (bus
->nodes_modified
);
2374 return sd_bus_send(bus
, m
, NULL
);
2377 static int object_removed_append_all_prefix(
2383 bool require_fallback
) {
2385 const char *previous_interface
= NULL
;
2386 struct node_vtable
*c
;
2396 n
= hashmap_get(bus
->nodes
, prefix
);
2400 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2401 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2404 if (require_fallback
&& !c
->is_fallback
)
2406 if (streq_ptr(c
->interface
, previous_interface
))
2409 /* If a child-node already handled this interface, we
2410 * skip it on any of its parents. The child vtables
2411 * always fully override any conflicting vtables of
2412 * any parent node. */
2413 if (set_get(s
, c
->interface
))
2416 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2419 if (bus
->nodes_modified
)
2424 r
= set_put(s
, c
->interface
);
2428 r
= sd_bus_message_append(m
, "s", c
->interface
);
2432 previous_interface
= c
->interface
;
2438 static int object_removed_append_all(sd_bus
*bus
, sd_bus_message
*m
, const char *path
) {
2439 _cleanup_set_free_ Set
*s
= NULL
;
2447 /* see sd_bus_emit_object_added() for details */
2449 s
= set_new(&string_hash_ops
);
2453 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Peer");
2456 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Introspectable");
2459 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.Properties");
2462 r
= sd_bus_message_append(m
, "s", "org.freedesktop.DBus.ObjectManager");
2466 r
= object_removed_append_all_prefix(bus
, m
, s
, path
, path
, false);
2469 if (bus
->nodes_modified
)
2472 prefix
= alloca(strlen(path
) + 1);
2473 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2474 r
= object_removed_append_all_prefix(bus
, m
, s
, prefix
, path
, true);
2477 if (bus
->nodes_modified
)
2484 _public_
int sd_bus_emit_object_removed(sd_bus
*bus
, const char *path
) {
2485 BUS_DONT_DESTROY(bus
);
2487 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2488 struct node
*object_manager
;
2492 * This is like sd_bus_emit_object_added(), but emits an
2493 * InterfacesRemoved signal on the given path. This only includes any
2494 * registered interfaces but skips the properties. Note that this will
2495 * call into the find() callbacks of any registered vtable. Therefore,
2496 * you must call this function before destroying/unlinking your object.
2497 * Otherwise, the list of interfaces will be incomplete. However, note
2498 * that this will *NOT* call into any property callback. Therefore, the
2499 * object might be in an "destructed" state, as long as we can find it.
2502 assert_return(bus
, -EINVAL
);
2503 assert_return(object_path_is_valid(path
), -EINVAL
);
2504 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2506 if (!BUS_IS_OPEN(bus
->state
))
2509 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2516 bus
->nodes_modified
= false;
2517 m
= sd_bus_message_unref(m
);
2519 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2523 r
= sd_bus_message_append_basic(m
, 'o', path
);
2527 r
= sd_bus_message_open_container(m
, 'a', "s");
2531 r
= object_removed_append_all(bus
, m
, path
);
2535 if (bus
->nodes_modified
)
2538 r
= sd_bus_message_close_container(m
);
2542 } while (bus
->nodes_modified
);
2544 return sd_bus_send(bus
, m
, NULL
);
2547 static int interfaces_added_append_one_prefix(
2552 const char *interface
,
2553 bool require_fallback
) {
2555 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2556 bool found_interface
= false;
2557 struct node_vtable
*c
;
2568 n
= hashmap_get(bus
->nodes
, prefix
);
2572 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2573 if (require_fallback
&& !c
->is_fallback
)
2576 if (!streq(c
->interface
, interface
))
2579 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2582 if (bus
->nodes_modified
)
2587 if (!found_interface
) {
2588 r
= sd_bus_message_append_basic(m
, 's', interface
);
2592 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2596 found_interface
= true;
2599 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2602 if (bus
->nodes_modified
)
2606 if (found_interface
) {
2607 r
= sd_bus_message_close_container(m
);
2612 return found_interface
;
2615 static int interfaces_added_append_one(
2619 const char *interface
) {
2629 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2632 if (bus
->nodes_modified
)
2635 prefix
= alloca(strlen(path
) + 1);
2636 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2637 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2640 if (bus
->nodes_modified
)
2647 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2648 BUS_DONT_DESTROY(bus
);
2650 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2651 struct node
*object_manager
;
2655 assert_return(bus
, -EINVAL
);
2656 assert_return(object_path_is_valid(path
), -EINVAL
);
2657 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2659 if (!BUS_IS_OPEN(bus
->state
))
2662 if (strv_isempty(interfaces
))
2665 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2672 bus
->nodes_modified
= false;
2673 m
= sd_bus_message_unref(m
);
2675 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2679 r
= sd_bus_message_append_basic(m
, 'o', path
);
2683 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2687 STRV_FOREACH(i
, interfaces
) {
2688 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2690 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2694 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2698 if (bus
->nodes_modified
)
2701 r
= sd_bus_message_close_container(m
);
2706 if (bus
->nodes_modified
)
2709 r
= sd_bus_message_close_container(m
);
2713 } while (bus
->nodes_modified
);
2715 return sd_bus_send(bus
, m
, NULL
);
2718 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2721 assert_return(bus
, -EINVAL
);
2722 assert_return(object_path_is_valid(path
), -EINVAL
);
2723 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2725 if (!BUS_IS_OPEN(bus
->state
))
2728 interfaces
= strv_from_stdarg_alloca(interface
);
2730 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2733 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2734 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2735 struct node
*object_manager
;
2738 assert_return(bus
, -EINVAL
);
2739 assert_return(object_path_is_valid(path
), -EINVAL
);
2740 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2742 if (!BUS_IS_OPEN(bus
->state
))
2745 if (strv_isempty(interfaces
))
2748 r
= bus_find_parent_object_manager(bus
, &object_manager
, path
);
2754 r
= sd_bus_message_new_signal(bus
, &m
, object_manager
->path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2758 r
= sd_bus_message_append_basic(m
, 'o', path
);
2762 r
= sd_bus_message_append_strv(m
, interfaces
);
2766 return sd_bus_send(bus
, m
, NULL
);
2769 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2772 assert_return(bus
, -EINVAL
);
2773 assert_return(object_path_is_valid(path
), -EINVAL
);
2774 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2776 if (!BUS_IS_OPEN(bus
->state
))
2779 interfaces
= strv_from_stdarg_alloca(interface
);
2781 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2784 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2789 assert_return(bus
, -EINVAL
);
2790 assert_return(object_path_is_valid(path
), -EINVAL
);
2791 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2793 n
= bus_node_allocate(bus
, path
);
2797 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2803 s
->node_object_manager
.node
= n
;
2804 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2805 bus
->nodes_modified
= true;
2813 sd_bus_slot_unref(s
);
2814 bus_node_gc(bus
, n
);