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 <sys/capability.h>
26 #include "bus-internal.h"
27 #include "bus-message.h"
29 #include "bus-signature.h"
30 #include "bus-introspect.h"
33 #include "bus-objects.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_property_convert_userdata(const sd_bus_vtable
*p
, void *u
) {
76 return (uint8_t*) u
+ p
->x
.property
.offset
;
79 static int vtable_property_get_userdata(
82 struct vtable_member
*p
,
84 sd_bus_error
*error
) {
94 r
= node_vtable_get_userdata(bus
, path
, p
->parent
, &u
, error
);
97 if (bus
->nodes_modified
)
100 *userdata
= vtable_property_convert_userdata(p
->vtable
, u
);
104 static int add_enumerated_to_set(
107 struct node_enumerator
*first
,
109 sd_bus_error
*error
) {
111 struct node_enumerator
*c
;
118 LIST_FOREACH(enumerators
, c
, first
) {
119 char **children
= NULL
, **k
;
122 if (bus
->nodes_modified
)
125 slot
= container_of(c
, sd_bus_slot
, node_enumerator
);
127 bus
->current_slot
= sd_bus_slot_ref(slot
);
128 bus
->current_userdata
= slot
->userdata
;
129 r
= c
->callback(bus
, prefix
, slot
->userdata
, &children
, error
);
130 bus
->current_userdata
= NULL
;
131 bus
->current_slot
= sd_bus_slot_unref(slot
);
135 if (sd_bus_error_is_set(error
))
136 return -sd_bus_error_get_errno(error
);
138 STRV_FOREACH(k
, children
) {
144 if (!object_path_is_valid(*k
)){
150 if (!object_path_startswith(*k
, prefix
)) {
155 r
= set_consume(s
, *k
);
168 static int add_subtree_to_set(
173 sd_bus_error
*error
) {
183 r
= add_enumerated_to_set(bus
, prefix
, n
->enumerators
, s
, error
);
186 if (bus
->nodes_modified
)
189 LIST_FOREACH(siblings
, i
, n
->child
) {
192 if (!object_path_startswith(i
->path
, prefix
))
199 r
= set_consume(s
, t
);
200 if (r
< 0 && r
!= -EEXIST
)
203 r
= add_subtree_to_set(bus
, prefix
, i
, s
, error
);
206 if (bus
->nodes_modified
)
213 static int get_child_nodes(
218 sd_bus_error
*error
) {
228 s
= set_new(&string_hash_ops
);
232 r
= add_subtree_to_set(bus
, prefix
, n
, s
, error
);
242 static int node_callbacks_run(
245 struct node_callback
*first
,
246 bool require_fallback
,
247 bool *found_object
) {
249 struct node_callback
*c
;
254 assert(found_object
);
256 LIST_FOREACH(callbacks
, c
, first
) {
257 _cleanup_bus_error_free_ sd_bus_error error_buffer
= SD_BUS_ERROR_NULL
;
260 if (bus
->nodes_modified
)
263 if (require_fallback
&& !c
->is_fallback
)
266 *found_object
= true;
268 if (c
->last_iteration
== bus
->iteration_counter
)
271 c
->last_iteration
= bus
->iteration_counter
;
273 r
= sd_bus_message_rewind(m
, true);
277 slot
= container_of(c
, sd_bus_slot
, node_callback
);
279 bus
->current_slot
= sd_bus_slot_ref(slot
);
280 bus
->current_handler
= c
->callback
;
281 bus
->current_userdata
= slot
->userdata
;
282 r
= c
->callback(bus
, m
, slot
->userdata
, &error_buffer
);
283 bus
->current_userdata
= NULL
;
284 bus
->current_handler
= NULL
;
285 bus
->current_slot
= sd_bus_slot_unref(slot
);
287 r
= bus_maybe_reply_error(m
, r
, &error_buffer
);
295 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
297 static int check_access(sd_bus
*bus
, sd_bus_message
*m
, struct vtable_member
*c
, sd_bus_error
*error
) {
305 /* If the entire bus is trusted let's grant access */
309 /* If the member is marked UNPRIVILEGED let's grant access */
310 if (c
->vtable
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
)
313 /* Check have the caller has the requested capability
314 * set. Note that the flags value contains the capability
315 * number plus one, which we need to subtract here. We do this
316 * so that we have 0 as special value for "default
318 cap
= CAPABILITY_SHIFT(c
->vtable
->flags
);
320 cap
= CAPABILITY_SHIFT(c
->parent
->vtable
[0].flags
);
326 r
= sd_bus_query_sender_privilege(m
, cap
);
332 return sd_bus_error_setf(error
, SD_BUS_ERROR_ACCESS_DENIED
, "Access to %s.%s() not permitted.", c
->interface
, c
->member
);
335 static int method_callbacks_run(
338 struct vtable_member
*c
,
339 bool require_fallback
,
340 bool *found_object
) {
342 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
343 const char *signature
;
350 assert(found_object
);
352 if (require_fallback
&& !c
->parent
->is_fallback
)
355 r
= check_access(bus
, m
, c
, &error
);
357 return bus_maybe_reply_error(m
, r
, &error
);
359 r
= node_vtable_get_userdata(bus
, m
->path
, c
->parent
, &u
, &error
);
361 return bus_maybe_reply_error(m
, r
, &error
);
362 if (bus
->nodes_modified
)
365 *found_object
= true;
367 if (c
->last_iteration
== bus
->iteration_counter
)
370 c
->last_iteration
= bus
->iteration_counter
;
372 r
= sd_bus_message_rewind(m
, true);
376 signature
= sd_bus_message_get_signature(m
, true);
380 if (!streq(strempty(c
->vtable
->x
.method
.signature
), signature
))
381 return sd_bus_reply_method_errorf(
383 SD_BUS_ERROR_INVALID_ARGS
,
384 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
385 signature
, c
->interface
, c
->member
, strempty(c
->vtable
->x
.method
.signature
));
387 /* Keep track what the signature of the reply to this message
388 * should be, so that this can be enforced when sealing the
390 m
->enforced_reply_signature
= strempty(c
->vtable
->x
.method
.result
);
392 if (c
->vtable
->x
.method
.handler
) {
395 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
397 bus
->current_slot
= sd_bus_slot_ref(slot
);
398 bus
->current_handler
= c
->vtable
->x
.method
.handler
;
399 bus
->current_userdata
= u
;
400 r
= c
->vtable
->x
.method
.handler(bus
, m
, u
, &error
);
401 bus
->current_userdata
= NULL
;
402 bus
->current_handler
= NULL
;
403 bus
->current_slot
= sd_bus_slot_unref(slot
);
405 return bus_maybe_reply_error(m
, r
, &error
);
408 /* If the method callback is NULL, make this a successful NOP */
409 r
= sd_bus_reply_method_return(m
, NULL
);
416 static int invoke_property_get(
419 const sd_bus_vtable
*v
,
421 const char *interface
,
422 const char *property
,
423 sd_bus_message
*reply
,
425 sd_bus_error
*error
) {
438 if (v
->x
.property
.get
) {
440 bus
->current_slot
= sd_bus_slot_ref(slot
);
441 bus
->current_userdata
= userdata
;
442 r
= v
->x
.property
.get(bus
, path
, interface
, property
, reply
, userdata
, error
);
443 bus
->current_userdata
= NULL
;
444 bus
->current_slot
= sd_bus_slot_unref(slot
);
448 if (sd_bus_error_is_set(error
))
449 return -sd_bus_error_get_errno(error
);
453 /* Automatic handling if no callback is defined. */
455 if (streq(v
->x
.property
.signature
, "as"))
456 return sd_bus_message_append_strv(reply
, *(char***) userdata
);
458 assert(signature_is_single(v
->x
.property
.signature
, false));
459 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
461 switch (v
->x
.property
.signature
[0]) {
463 case SD_BUS_TYPE_STRING
:
464 case SD_BUS_TYPE_SIGNATURE
:
465 p
= strempty(*(char**) userdata
);
468 case SD_BUS_TYPE_OBJECT_PATH
:
469 p
= *(char**) userdata
;
478 return sd_bus_message_append_basic(reply
, v
->x
.property
.signature
[0], p
);
481 static int invoke_property_set(
484 const sd_bus_vtable
*v
,
486 const char *interface
,
487 const char *property
,
488 sd_bus_message
*value
,
490 sd_bus_error
*error
) {
502 if (v
->x
.property
.set
) {
504 bus
->current_slot
= sd_bus_slot_ref(slot
);
505 bus
->current_userdata
= userdata
;
506 r
= v
->x
.property
.set(bus
, path
, interface
, property
, value
, userdata
, error
);
507 bus
->current_userdata
= NULL
;
508 bus
->current_slot
= sd_bus_slot_unref(slot
);
512 if (sd_bus_error_is_set(error
))
513 return -sd_bus_error_get_errno(error
);
517 /* Automatic handling if no callback is defined. */
519 assert(signature_is_single(v
->x
.property
.signature
, false));
520 assert(bus_type_is_basic(v
->x
.property
.signature
[0]));
522 switch (v
->x
.property
.signature
[0]) {
524 case SD_BUS_TYPE_STRING
:
525 case SD_BUS_TYPE_OBJECT_PATH
:
526 case SD_BUS_TYPE_SIGNATURE
: {
530 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], &p
);
538 free(*(char**) userdata
);
539 *(char**) userdata
= n
;
545 r
= sd_bus_message_read_basic(value
, v
->x
.property
.signature
[0], userdata
);
555 static int property_get_set_callbacks_run(
558 struct vtable_member
*c
,
559 bool require_fallback
,
561 bool *found_object
) {
563 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
564 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
572 assert(found_object
);
574 if (require_fallback
&& !c
->parent
->is_fallback
)
577 r
= vtable_property_get_userdata(bus
, m
->path
, c
, &u
, &error
);
579 return bus_maybe_reply_error(m
, r
, &error
);
580 if (bus
->nodes_modified
)
583 slot
= container_of(c
->parent
, sd_bus_slot
, node_vtable
);
585 *found_object
= true;
587 r
= sd_bus_message_new_method_return(m
, &reply
);
592 /* Note that we do not protect against reexecution
593 * here (using the last_iteration check, see below),
594 * should the node tree have changed and we got called
595 * again. We assume that property Get() calls are
596 * ultimately without side-effects or if they aren't
597 * then at least idempotent. */
599 r
= sd_bus_message_open_container(reply
, 'v', c
->vtable
->x
.property
.signature
);
603 /* Note that we do not do an access check here. Read
604 * access to properties is always unrestricted, since
605 * PropertiesChanged signals broadcast contents
608 r
= invoke_property_get(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, reply
, u
, &error
);
610 return bus_maybe_reply_error(m
, r
, &error
);
612 if (bus
->nodes_modified
)
615 r
= sd_bus_message_close_container(reply
);
620 if (c
->vtable
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
621 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_PROPERTY_READ_ONLY
, "Property '%s' is not writable.", c
->member
);
623 /* Avoid that we call the set routine more than once
624 * if the processing of this message got restarted
625 * because the node tree changed. */
626 if (c
->last_iteration
== bus
->iteration_counter
)
629 c
->last_iteration
= bus
->iteration_counter
;
631 r
= sd_bus_message_enter_container(m
, 'v', c
->vtable
->x
.property
.signature
);
635 r
= check_access(bus
, m
, c
, &error
);
637 return bus_maybe_reply_error(m
, r
, &error
);
639 r
= invoke_property_set(bus
, slot
, c
->vtable
, m
->path
, c
->interface
, c
->member
, m
, u
, &error
);
641 return bus_maybe_reply_error(m
, r
, &error
);
643 if (bus
->nodes_modified
)
646 r
= sd_bus_message_exit_container(m
);
651 r
= sd_bus_send(bus
, reply
, NULL
);
658 static int vtable_append_one_property(
660 sd_bus_message
*reply
,
662 struct node_vtable
*c
,
663 const sd_bus_vtable
*v
,
665 sd_bus_error
*error
) {
676 r
= sd_bus_message_open_container(reply
, 'e', "sv");
680 r
= sd_bus_message_append(reply
, "s", v
->x
.property
.member
);
684 r
= sd_bus_message_open_container(reply
, 'v', v
->x
.property
.signature
);
688 slot
= container_of(c
, sd_bus_slot
, node_vtable
);
690 r
= invoke_property_get(bus
, slot
, v
, path
, c
->interface
, v
->x
.property
.member
, reply
, vtable_property_convert_userdata(v
, userdata
), error
);
693 if (bus
->nodes_modified
)
696 r
= sd_bus_message_close_container(reply
);
700 r
= sd_bus_message_close_container(reply
);
707 static int vtable_append_all_properties(
709 sd_bus_message
*reply
,
711 struct node_vtable
*c
,
713 sd_bus_error
*error
) {
715 const sd_bus_vtable
*v
;
723 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
726 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
727 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
730 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
733 r
= vtable_append_one_property(bus
, reply
, path
, c
, v
, userdata
, error
);
736 if (bus
->nodes_modified
)
743 static int property_get_all_callbacks_run(
746 struct node_vtable
*first
,
747 bool require_fallback
,
749 bool *found_object
) {
751 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
752 struct node_vtable
*c
;
753 bool found_interface
;
758 assert(found_object
);
760 r
= sd_bus_message_new_method_return(m
, &reply
);
764 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
768 found_interface
= !iface
||
769 streq(iface
, "org.freedesktop.DBus.Properties") ||
770 streq(iface
, "org.freedesktop.DBus.Peer") ||
771 streq(iface
, "org.freedesktop.DBus.Introspectable");
773 LIST_FOREACH(vtables
, c
, first
) {
774 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
777 if (require_fallback
&& !c
->is_fallback
)
780 r
= node_vtable_get_userdata(bus
, m
->path
, c
, &u
, &error
);
782 return bus_maybe_reply_error(m
, r
, &error
);
783 if (bus
->nodes_modified
)
788 *found_object
= true;
790 if (iface
&& !streq(c
->interface
, iface
))
792 found_interface
= true;
794 r
= vtable_append_all_properties(bus
, reply
, m
->path
, c
, u
, &error
);
796 return bus_maybe_reply_error(m
, r
, &error
);
797 if (bus
->nodes_modified
)
801 if (!found_interface
) {
802 r
= sd_bus_reply_method_errorf(
804 SD_BUS_ERROR_UNKNOWN_INTERFACE
,
805 "Unknown interface '%s'.", iface
);
812 r
= sd_bus_message_close_container(reply
);
816 r
= sd_bus_send(bus
, reply
, NULL
);
823 static bool bus_node_with_object_manager(sd_bus
*bus
, struct node
*n
) {
827 if (n
->object_managers
)
831 return bus_node_with_object_manager(bus
, n
->parent
);
836 static bool bus_node_exists(
840 bool require_fallback
) {
842 struct node_vtable
*c
;
843 struct node_callback
*k
;
849 /* Tests if there's anything attached directly to this node
850 * for the specified path */
852 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
853 if (require_fallback
&& !k
->is_fallback
)
859 LIST_FOREACH(vtables
, c
, n
->vtables
) {
860 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
862 if (require_fallback
&& !c
->is_fallback
)
865 if (node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
) > 0)
867 if (bus
->nodes_modified
)
871 return !require_fallback
&& (n
->enumerators
|| n
->object_managers
);
874 static int process_introspect(
878 bool require_fallback
,
879 bool *found_object
) {
881 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
882 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
883 _cleanup_set_free_free_ Set
*s
= NULL
;
884 const char *previous_interface
= NULL
;
885 struct introspect intro
;
886 struct node_vtable
*c
;
893 assert(found_object
);
895 r
= get_child_nodes(bus
, m
->path
, n
, &s
, &error
);
897 return bus_maybe_reply_error(m
, r
, &error
);
898 if (bus
->nodes_modified
)
901 r
= introspect_begin(&intro
, bus
->trusted
);
905 r
= introspect_write_default_interfaces(&intro
, bus_node_with_object_manager(bus
, n
));
909 empty
= set_isempty(s
);
911 LIST_FOREACH(vtables
, c
, n
->vtables
) {
912 if (require_fallback
&& !c
->is_fallback
)
915 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
917 r
= bus_maybe_reply_error(m
, r
, &error
);
920 if (bus
->nodes_modified
) {
929 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
932 if (!streq_ptr(previous_interface
, c
->interface
)) {
934 if (previous_interface
)
935 fputs(" </interface>\n", intro
.f
);
937 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
940 r
= introspect_write_interface(&intro
, c
->vtable
);
944 previous_interface
= c
->interface
;
947 if (previous_interface
)
948 fputs(" </interface>\n", intro
.f
);
951 /* Nothing?, let's see if we exist at all, and if not
952 * refuse to do anything */
953 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
956 if (bus
->nodes_modified
)
962 *found_object
= true;
964 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
968 r
= introspect_finish(&intro
, bus
, m
, &reply
);
972 r
= sd_bus_send(bus
, reply
, NULL
);
979 introspect_free(&intro
);
983 static int object_manager_serialize_path(
985 sd_bus_message
*reply
,
988 bool require_fallback
,
989 sd_bus_error
*error
) {
991 const char *previous_interface
= NULL
;
992 bool found_something
= false;
993 struct node_vtable
*i
;
1003 n
= hashmap_get(bus
->nodes
, prefix
);
1007 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1010 if (require_fallback
&& !i
->is_fallback
)
1013 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1016 if (bus
->nodes_modified
)
1021 if (!found_something
) {
1023 /* Open the object part */
1025 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1029 r
= sd_bus_message_append(reply
, "o", path
);
1033 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1037 found_something
= true;
1040 if (!streq_ptr(previous_interface
, i
->interface
)) {
1042 /* Maybe close the previous interface part */
1044 if (previous_interface
) {
1045 r
= sd_bus_message_close_container(reply
);
1049 r
= sd_bus_message_close_container(reply
);
1054 /* Open the new interface part */
1056 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1060 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1064 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1069 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1072 if (bus
->nodes_modified
)
1075 previous_interface
= i
->interface
;
1078 if (previous_interface
) {
1079 r
= sd_bus_message_close_container(reply
);
1083 r
= sd_bus_message_close_container(reply
);
1088 if (found_something
) {
1089 r
= sd_bus_message_close_container(reply
);
1093 r
= sd_bus_message_close_container(reply
);
1101 static int object_manager_serialize_path_and_fallbacks(
1103 sd_bus_message
*reply
,
1105 sd_bus_error
*error
) {
1115 /* First, add all vtables registered for this path */
1116 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1119 if (bus
->nodes_modified
)
1122 /* Second, add fallback vtables registered for any of the prefixes */
1123 prefix
= alloca(strlen(path
) + 1);
1124 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1125 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1128 if (bus
->nodes_modified
)
1135 static int process_get_managed_objects(
1139 bool require_fallback
,
1140 bool *found_object
) {
1142 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1143 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1144 _cleanup_set_free_free_ Set
*s
= NULL
;
1151 assert(found_object
);
1153 if (!bus_node_with_object_manager(bus
, n
))
1156 r
= get_child_nodes(bus
, m
->path
, n
, &s
, &error
);
1159 if (bus
->nodes_modified
)
1162 r
= sd_bus_message_new_method_return(m
, &reply
);
1166 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1170 empty
= set_isempty(s
);
1172 struct node_vtable
*c
;
1174 /* Hmm, so we have no children? Then let's check
1175 * whether we exist at all, i.e. whether at least one
1178 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1180 if (require_fallback
&& !c
->is_fallback
)
1198 SET_FOREACH(path
, s
, i
) {
1199 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1203 if (bus
->nodes_modified
)
1208 r
= sd_bus_message_close_container(reply
);
1212 r
= sd_bus_send(bus
, reply
, NULL
);
1219 static int object_find_and_run(
1223 bool require_fallback
,
1224 bool *found_object
) {
1227 struct vtable_member vtable_key
, *v
;
1233 assert(found_object
);
1235 n
= hashmap_get(bus
->nodes
, p
);
1239 /* First, try object callbacks */
1240 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1243 if (bus
->nodes_modified
)
1246 if (!m
->interface
|| !m
->member
)
1249 /* Then, look for a known method */
1250 vtable_key
.path
= (char*) p
;
1251 vtable_key
.interface
= m
->interface
;
1252 vtable_key
.member
= m
->member
;
1254 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1256 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1259 if (bus
->nodes_modified
)
1263 /* Then, look for a known property */
1264 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1267 get
= streq(m
->member
, "Get");
1269 if (get
|| streq(m
->member
, "Set")) {
1271 r
= sd_bus_message_rewind(m
, true);
1275 vtable_key
.path
= (char*) p
;
1277 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1279 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1281 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1283 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1288 } else if (streq(m
->member
, "GetAll")) {
1291 r
= sd_bus_message_rewind(m
, true);
1295 r
= sd_bus_message_read(m
, "s", &iface
);
1297 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1302 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1307 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1309 if (!isempty(sd_bus_message_get_signature(m
, true)))
1310 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1312 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1316 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1318 if (!isempty(sd_bus_message_get_signature(m
, true)))
1319 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1321 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1326 if (bus
->nodes_modified
)
1329 if (!*found_object
) {
1330 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1334 *found_object
= true;
1340 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1343 bool found_object
= false;
1348 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1351 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1354 if (hashmap_isempty(bus
->nodes
))
1357 /* Never respond to broadcast messages */
1358 if (bus
->bus_client
&& !m
->destination
)
1364 pl
= strlen(m
->path
);
1368 bus
->nodes_modified
= false;
1370 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1374 /* Look for fallback prefixes */
1375 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1377 if (bus
->nodes_modified
)
1380 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1385 } while (bus
->nodes_modified
);
1390 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1391 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1392 r
= sd_bus_reply_method_errorf(
1394 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1395 "Unknown property or interface.");
1397 r
= sd_bus_reply_method_errorf(
1399 SD_BUS_ERROR_UNKNOWN_METHOD
,
1400 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1408 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1409 struct node
*n
, *parent
;
1411 _cleanup_free_
char *s
= NULL
;
1417 assert(path
[0] == '/');
1419 n
= hashmap_get(bus
->nodes
, path
);
1423 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1431 if (streq(path
, "/"))
1434 e
= strrchr(path
, '/');
1437 p
= strndupa(path
, MAX(1, path
- e
));
1439 parent
= bus_node_allocate(bus
, p
);
1444 n
= new0(struct node
, 1);
1450 s
= NULL
; /* do not free */
1452 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1460 LIST_PREPEND(siblings
, parent
->child
, n
);
1465 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1478 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1481 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1484 bus_node_gc(b
, n
->parent
);
1488 static int bus_add_object(
1493 sd_bus_message_handler_t callback
,
1500 assert_return(bus
, -EINVAL
);
1501 assert_return(object_path_is_valid(path
), -EINVAL
);
1502 assert_return(callback
, -EINVAL
);
1503 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1505 n
= bus_node_allocate(bus
, path
);
1509 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1515 s
->node_callback
.callback
= callback
;
1516 s
->node_callback
.is_fallback
= fallback
;
1518 s
->node_callback
.node
= n
;
1519 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1520 bus
->nodes_modified
= true;
1528 sd_bus_slot_unref(s
);
1529 bus_node_gc(bus
, n
);
1534 _public_
int sd_bus_add_object(
1538 sd_bus_message_handler_t callback
,
1541 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1544 _public_
int sd_bus_add_fallback(
1548 sd_bus_message_handler_t callback
,
1551 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1554 static unsigned long vtable_member_hash_func(const void *a
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
1555 const struct vtable_member
*m
= a
;
1556 uint8_t hash_key2
[HASH_KEY_SIZE
];
1561 ret
= string_hash_func(m
->path
, hash_key
);
1563 /* Use a slightly different hash key for the interface */
1564 memcpy(hash_key2
, hash_key
, HASH_KEY_SIZE
);
1566 ret
^= string_hash_func(m
->interface
, hash_key2
);
1568 /* And an even different one for the member */
1570 ret
^= string_hash_func(m
->member
, hash_key2
);
1575 static int vtable_member_compare_func(const void *a
, const void *b
) {
1576 const struct vtable_member
*x
= a
, *y
= b
;
1582 r
= strcmp(x
->path
, y
->path
);
1586 r
= strcmp(x
->interface
, y
->interface
);
1590 return strcmp(x
->member
, y
->member
);
1593 static const struct hash_ops vtable_member_hash_ops
= {
1594 .hash
= vtable_member_hash_func
,
1595 .compare
= vtable_member_compare_func
1598 static int add_object_vtable_internal(
1602 const char *interface
,
1603 const sd_bus_vtable
*vtable
,
1605 sd_bus_object_find_t find
,
1608 sd_bus_slot
*s
= NULL
;
1609 struct node_vtable
*i
, *existing
= NULL
;
1610 const sd_bus_vtable
*v
;
1614 assert_return(bus
, -EINVAL
);
1615 assert_return(object_path_is_valid(path
), -EINVAL
);
1616 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1617 assert_return(vtable
, -EINVAL
);
1618 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1619 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1620 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1621 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1622 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1623 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1624 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1626 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1630 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1634 n
= bus_node_allocate(bus
, path
);
1638 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1639 if (i
->is_fallback
!= fallback
) {
1644 if (streq(i
->interface
, interface
)) {
1646 if (i
->vtable
== vtable
) {
1655 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1661 s
->node_vtable
.is_fallback
= fallback
;
1662 s
->node_vtable
.vtable
= vtable
;
1663 s
->node_vtable
.find
= find
;
1665 s
->node_vtable
.interface
= strdup(interface
);
1666 if (!s
->node_vtable
.interface
) {
1671 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1675 case _SD_BUS_VTABLE_METHOD
: {
1676 struct vtable_member
*m
;
1678 if (!member_name_is_valid(v
->x
.method
.member
) ||
1679 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1680 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1681 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1682 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1687 m
= new0(struct vtable_member
, 1);
1693 m
->parent
= &s
->node_vtable
;
1695 m
->interface
= s
->node_vtable
.interface
;
1696 m
->member
= v
->x
.method
.member
;
1699 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1708 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1710 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1717 case _SD_BUS_VTABLE_PROPERTY
: {
1718 struct vtable_member
*m
;
1720 if (!member_name_is_valid(v
->x
.property
.member
) ||
1721 !signature_is_single(v
->x
.property
.signature
, false) ||
1722 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1723 v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
||
1724 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1725 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1730 m
= new0(struct vtable_member
, 1);
1736 m
->parent
= &s
->node_vtable
;
1738 m
->interface
= s
->node_vtable
.interface
;
1739 m
->member
= v
->x
.property
.member
;
1742 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1751 case _SD_BUS_VTABLE_SIGNAL
:
1753 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1754 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1755 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1768 s
->node_vtable
.node
= n
;
1769 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1770 bus
->nodes_modified
= true;
1778 sd_bus_slot_unref(s
);
1779 bus_node_gc(bus
, n
);
1784 _public_
int sd_bus_add_object_vtable(
1788 const char *interface
,
1789 const sd_bus_vtable
*vtable
,
1792 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1795 _public_
int sd_bus_add_fallback_vtable(
1799 const char *interface
,
1800 const sd_bus_vtable
*vtable
,
1801 sd_bus_object_find_t find
,
1804 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1807 _public_
int sd_bus_add_node_enumerator(
1811 sd_bus_node_enumerator_t callback
,
1818 assert_return(bus
, -EINVAL
);
1819 assert_return(object_path_is_valid(path
), -EINVAL
);
1820 assert_return(callback
, -EINVAL
);
1821 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1823 n
= bus_node_allocate(bus
, path
);
1827 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1833 s
->node_enumerator
.callback
= callback
;
1835 s
->node_enumerator
.node
= n
;
1836 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1837 bus
->nodes_modified
= true;
1845 sd_bus_slot_unref(s
);
1846 bus_node_gc(bus
, n
);
1851 static int emit_properties_changed_on_interface(
1855 const char *interface
,
1856 bool require_fallback
,
1857 bool *found_interface
,
1860 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1861 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1862 bool has_invalidating
= false, has_changing
= false;
1863 struct vtable_member key
= {};
1864 struct node_vtable
*c
;
1874 assert(found_interface
);
1876 n
= hashmap_get(bus
->nodes
, prefix
);
1880 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1884 r
= sd_bus_message_append(m
, "s", interface
);
1888 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1893 key
.interface
= interface
;
1895 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1896 if (require_fallback
&& !c
->is_fallback
)
1899 if (!streq(c
->interface
, interface
))
1902 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1905 if (bus
->nodes_modified
)
1910 *found_interface
= true;
1913 /* If the caller specified a list of
1914 * properties we include exactly those in the
1915 * PropertiesChanged message */
1917 STRV_FOREACH(property
, names
) {
1918 struct vtable_member
*v
;
1920 assert_return(member_name_is_valid(*property
), -EINVAL
);
1922 key
.member
= *property
;
1923 v
= hashmap_get(bus
->vtable_properties
, &key
);
1927 /* If there are two vtables for the same
1928 * interface, let's handle this property when
1929 * we come to that vtable. */
1933 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1934 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1936 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1938 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1939 has_invalidating
= true;
1943 has_changing
= true;
1945 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1948 if (bus
->nodes_modified
)
1952 const sd_bus_vtable
*v
;
1954 /* If the caller specified no properties list
1955 * we include all properties that are marked
1956 * as changing in the message. */
1958 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1959 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
1962 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
1965 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1966 has_invalidating
= true;
1970 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
1973 has_changing
= true;
1975 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
1978 if (bus
->nodes_modified
)
1984 if (!has_invalidating
&& !has_changing
)
1987 r
= sd_bus_message_close_container(m
);
1991 r
= sd_bus_message_open_container(m
, 'a', "s");
1995 if (has_invalidating
) {
1996 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1997 if (require_fallback
&& !c
->is_fallback
)
2000 if (!streq(c
->interface
, interface
))
2003 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2006 if (bus
->nodes_modified
)
2012 STRV_FOREACH(property
, names
) {
2013 struct vtable_member
*v
;
2015 key
.member
= *property
;
2016 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
2017 assert(c
== v
->parent
);
2019 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2022 r
= sd_bus_message_append(m
, "s", *property
);
2027 const sd_bus_vtable
*v
;
2029 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2030 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
2033 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2036 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2039 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2047 r
= sd_bus_message_close_container(m
);
2051 r
= sd_bus_send(bus
, m
, NULL
);
2058 _public_
int sd_bus_emit_properties_changed_strv(
2061 const char *interface
,
2064 BUS_DONT_DESTROY(bus
);
2065 bool found_interface
= false;
2069 assert_return(bus
, -EINVAL
);
2070 assert_return(object_path_is_valid(path
), -EINVAL
);
2071 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2072 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2074 if (!BUS_IS_OPEN(bus
->state
))
2077 /* A non-NULL but empty names list means nothing needs to be
2078 generated. A NULL list OTOH indicates that all properties
2079 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2080 included in the PropertiesChanged message. */
2081 if (names
&& names
[0] == NULL
)
2085 bus
->nodes_modified
= false;
2087 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2090 if (bus
->nodes_modified
)
2093 prefix
= alloca(strlen(path
) + 1);
2094 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2095 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2098 if (bus
->nodes_modified
)
2102 } while (bus
->nodes_modified
);
2104 return found_interface
? 0 : -ENOENT
;
2107 _public_
int sd_bus_emit_properties_changed(
2110 const char *interface
,
2111 const char *name
, ...) {
2115 assert_return(bus
, -EINVAL
);
2116 assert_return(object_path_is_valid(path
), -EINVAL
);
2117 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2118 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2120 if (!BUS_IS_OPEN(bus
->state
))
2126 names
= strv_from_stdarg_alloca(name
);
2128 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2131 static int interfaces_added_append_one_prefix(
2136 const char *interface
,
2137 bool require_fallback
) {
2139 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2140 bool found_interface
= false;
2141 struct node_vtable
*c
;
2152 n
= hashmap_get(bus
->nodes
, prefix
);
2156 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2157 if (require_fallback
&& !c
->is_fallback
)
2160 if (!streq(c
->interface
, interface
))
2163 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2166 if (bus
->nodes_modified
)
2171 if (!found_interface
) {
2172 r
= sd_bus_message_append_basic(m
, 's', interface
);
2176 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2180 found_interface
= true;
2183 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2186 if (bus
->nodes_modified
)
2190 if (found_interface
) {
2191 r
= sd_bus_message_close_container(m
);
2196 return found_interface
;
2199 static int interfaces_added_append_one(
2203 const char *interface
) {
2213 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2216 if (bus
->nodes_modified
)
2219 prefix
= alloca(strlen(path
) + 1);
2220 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2221 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2224 if (bus
->nodes_modified
)
2231 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2232 BUS_DONT_DESTROY(bus
);
2234 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2238 assert_return(bus
, -EINVAL
);
2239 assert_return(object_path_is_valid(path
), -EINVAL
);
2240 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2242 if (!BUS_IS_OPEN(bus
->state
))
2245 if (strv_isempty(interfaces
))
2249 bus
->nodes_modified
= false;
2250 m
= sd_bus_message_unref(m
);
2252 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2256 r
= sd_bus_message_append_basic(m
, 'o', path
);
2260 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2264 STRV_FOREACH(i
, interfaces
) {
2265 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2267 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2271 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2275 if (bus
->nodes_modified
)
2278 r
= sd_bus_message_close_container(m
);
2283 if (bus
->nodes_modified
)
2286 r
= sd_bus_message_close_container(m
);
2290 } while (bus
->nodes_modified
);
2292 return sd_bus_send(bus
, m
, NULL
);
2295 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2298 assert_return(bus
, -EINVAL
);
2299 assert_return(object_path_is_valid(path
), -EINVAL
);
2300 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2302 if (!BUS_IS_OPEN(bus
->state
))
2305 interfaces
= strv_from_stdarg_alloca(interface
);
2307 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2310 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2311 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2314 assert_return(bus
, -EINVAL
);
2315 assert_return(object_path_is_valid(path
), -EINVAL
);
2316 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2318 if (!BUS_IS_OPEN(bus
->state
))
2321 if (strv_isempty(interfaces
))
2324 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2328 r
= sd_bus_message_append_basic(m
, 'o', path
);
2332 r
= sd_bus_message_append_strv(m
, interfaces
);
2336 return sd_bus_send(bus
, m
, NULL
);
2339 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2342 assert_return(bus
, -EINVAL
);
2343 assert_return(object_path_is_valid(path
), -EINVAL
);
2344 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2346 if (!BUS_IS_OPEN(bus
->state
))
2349 interfaces
= strv_from_stdarg_alloca(interface
);
2351 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2354 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2359 assert_return(bus
, -EINVAL
);
2360 assert_return(object_path_is_valid(path
), -EINVAL
);
2361 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2363 n
= bus_node_allocate(bus
, path
);
2367 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2373 s
->node_object_manager
.node
= n
;
2374 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2375 bus
->nodes_modified
= true;
2383 sd_bus_slot_unref(s
);
2384 bus_node_gc(bus
, n
);