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 int bus_node_exists(
827 bool require_fallback
) {
829 struct node_vtable
*c
;
830 struct node_callback
*k
;
837 /* Tests if there's anything attached directly to this node
838 * for the specified path */
840 if (!require_fallback
&& (n
->enumerators
|| n
->object_managers
))
843 LIST_FOREACH(callbacks
, k
, n
->callbacks
) {
844 if (require_fallback
&& !k
->is_fallback
)
850 LIST_FOREACH(vtables
, c
, n
->vtables
) {
851 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
853 if (require_fallback
&& !c
->is_fallback
)
856 r
= node_vtable_get_userdata(bus
, path
, c
, NULL
, &error
);
859 if (bus
->nodes_modified
)
866 static int process_introspect(
870 bool require_fallback
,
871 bool *found_object
) {
873 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
874 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
875 _cleanup_set_free_free_ Set
*s
= NULL
;
876 const char *previous_interface
= NULL
;
877 struct introspect intro
;
878 struct node_vtable
*c
;
885 assert(found_object
);
887 r
= get_child_nodes(bus
, m
->path
, n
, &s
, &error
);
889 return bus_maybe_reply_error(m
, r
, &error
);
890 if (bus
->nodes_modified
)
893 r
= introspect_begin(&intro
, bus
->trusted
);
897 r
= introspect_write_default_interfaces(&intro
, !require_fallback
&& n
->object_managers
);
901 empty
= set_isempty(s
);
903 LIST_FOREACH(vtables
, c
, n
->vtables
) {
904 if (require_fallback
&& !c
->is_fallback
)
907 r
= node_vtable_get_userdata(bus
, m
->path
, c
, NULL
, &error
);
909 r
= bus_maybe_reply_error(m
, r
, &error
);
912 if (bus
->nodes_modified
) {
921 if (c
->vtable
[0].flags
& SD_BUS_VTABLE_HIDDEN
)
924 if (!streq_ptr(previous_interface
, c
->interface
)) {
926 if (previous_interface
)
927 fputs(" </interface>\n", intro
.f
);
929 fprintf(intro
.f
, " <interface name=\"%s\">\n", c
->interface
);
932 r
= introspect_write_interface(&intro
, c
->vtable
);
936 previous_interface
= c
->interface
;
939 if (previous_interface
)
940 fputs(" </interface>\n", intro
.f
);
943 /* Nothing?, let's see if we exist at all, and if not
944 * refuse to do anything */
945 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
948 if (bus
->nodes_modified
) {
954 *found_object
= true;
956 r
= introspect_write_child_nodes(&intro
, s
, m
->path
);
960 r
= introspect_finish(&intro
, bus
, m
, &reply
);
964 r
= sd_bus_send(bus
, reply
, NULL
);
971 introspect_free(&intro
);
975 static int object_manager_serialize_path(
977 sd_bus_message
*reply
,
980 bool require_fallback
,
981 sd_bus_error
*error
) {
983 const char *previous_interface
= NULL
;
984 bool found_something
= false;
985 struct node_vtable
*i
;
995 n
= hashmap_get(bus
->nodes
, prefix
);
999 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1002 if (require_fallback
&& !i
->is_fallback
)
1005 r
= node_vtable_get_userdata(bus
, path
, i
, &u
, error
);
1008 if (bus
->nodes_modified
)
1013 if (!found_something
) {
1015 /* Open the object part */
1017 r
= sd_bus_message_open_container(reply
, 'e', "oa{sa{sv}}");
1021 r
= sd_bus_message_append(reply
, "o", path
);
1025 r
= sd_bus_message_open_container(reply
, 'a', "{sa{sv}}");
1029 found_something
= true;
1032 if (!streq_ptr(previous_interface
, i
->interface
)) {
1034 /* Maybe close the previous interface part */
1036 if (previous_interface
) {
1037 r
= sd_bus_message_close_container(reply
);
1041 r
= sd_bus_message_close_container(reply
);
1046 /* Open the new interface part */
1048 r
= sd_bus_message_open_container(reply
, 'e', "sa{sv}");
1052 r
= sd_bus_message_append(reply
, "s", i
->interface
);
1056 r
= sd_bus_message_open_container(reply
, 'a', "{sv}");
1061 r
= vtable_append_all_properties(bus
, reply
, path
, i
, u
, error
);
1064 if (bus
->nodes_modified
)
1067 previous_interface
= i
->interface
;
1070 if (previous_interface
) {
1071 r
= sd_bus_message_close_container(reply
);
1075 r
= sd_bus_message_close_container(reply
);
1080 if (found_something
) {
1081 r
= sd_bus_message_close_container(reply
);
1085 r
= sd_bus_message_close_container(reply
);
1093 static int object_manager_serialize_path_and_fallbacks(
1095 sd_bus_message
*reply
,
1097 sd_bus_error
*error
) {
1107 /* First, add all vtables registered for this path */
1108 r
= object_manager_serialize_path(bus
, reply
, path
, path
, false, error
);
1111 if (bus
->nodes_modified
)
1114 /* Second, add fallback vtables registered for any of the prefixes */
1115 prefix
= alloca(strlen(path
) + 1);
1116 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
1117 r
= object_manager_serialize_path(bus
, reply
, prefix
, path
, true, error
);
1120 if (bus
->nodes_modified
)
1127 static int process_get_managed_objects(
1131 bool require_fallback
,
1132 bool *found_object
) {
1134 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1135 _cleanup_bus_message_unref_ sd_bus_message
*reply
= NULL
;
1136 _cleanup_set_free_free_ Set
*s
= NULL
;
1144 assert(found_object
);
1146 /* Spec says, GetManagedObjects() is only implemented on the root of a
1147 * sub-tree. Therefore, we require a registered object-manager on
1148 * exactly the queried path, otherwise, we refuse to respond. */
1150 if (require_fallback
|| !n
->object_managers
)
1153 r
= get_child_nodes(bus
, m
->path
, n
, &s
, &error
);
1156 if (bus
->nodes_modified
)
1159 r
= sd_bus_message_new_method_return(m
, &reply
);
1163 r
= sd_bus_message_open_container(reply
, 'a', "{oa{sa{sv}}}");
1167 SET_FOREACH(path
, s
, i
) {
1168 r
= object_manager_serialize_path_and_fallbacks(bus
, reply
, path
, &error
);
1172 if (bus
->nodes_modified
)
1176 r
= sd_bus_message_close_container(reply
);
1180 r
= sd_bus_send(bus
, reply
, NULL
);
1187 static int object_find_and_run(
1191 bool require_fallback
,
1192 bool *found_object
) {
1195 struct vtable_member vtable_key
, *v
;
1201 assert(found_object
);
1203 n
= hashmap_get(bus
->nodes
, p
);
1207 /* First, try object callbacks */
1208 r
= node_callbacks_run(bus
, m
, n
->callbacks
, require_fallback
, found_object
);
1211 if (bus
->nodes_modified
)
1214 if (!m
->interface
|| !m
->member
)
1217 /* Then, look for a known method */
1218 vtable_key
.path
= (char*) p
;
1219 vtable_key
.interface
= m
->interface
;
1220 vtable_key
.member
= m
->member
;
1222 v
= hashmap_get(bus
->vtable_methods
, &vtable_key
);
1224 r
= method_callbacks_run(bus
, m
, v
, require_fallback
, found_object
);
1227 if (bus
->nodes_modified
)
1231 /* Then, look for a known property */
1232 if (streq(m
->interface
, "org.freedesktop.DBus.Properties")) {
1235 get
= streq(m
->member
, "Get");
1237 if (get
|| streq(m
->member
, "Set")) {
1239 r
= sd_bus_message_rewind(m
, true);
1243 vtable_key
.path
= (char*) p
;
1245 r
= sd_bus_message_read(m
, "ss", &vtable_key
.interface
, &vtable_key
.member
);
1247 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface and member parameters");
1249 v
= hashmap_get(bus
->vtable_properties
, &vtable_key
);
1251 r
= property_get_set_callbacks_run(bus
, m
, v
, require_fallback
, get
, found_object
);
1256 } else if (streq(m
->member
, "GetAll")) {
1259 r
= sd_bus_message_rewind(m
, true);
1263 r
= sd_bus_message_read(m
, "s", &iface
);
1265 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected interface parameter");
1270 r
= property_get_all_callbacks_run(bus
, m
, n
->vtables
, require_fallback
, iface
, found_object
);
1275 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1277 if (!isempty(sd_bus_message_get_signature(m
, true)))
1278 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1280 r
= process_introspect(bus
, m
, n
, require_fallback
, found_object
);
1284 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1286 if (!isempty(sd_bus_message_get_signature(m
, true)))
1287 return sd_bus_reply_method_errorf(m
, SD_BUS_ERROR_INVALID_ARGS
, "Expected no parameters");
1289 r
= process_get_managed_objects(bus
, m
, n
, require_fallback
, found_object
);
1294 if (bus
->nodes_modified
)
1297 if (!*found_object
) {
1298 r
= bus_node_exists(bus
, n
, m
->path
, require_fallback
);
1301 if (bus
->nodes_modified
)
1304 *found_object
= true;
1310 int bus_process_object(sd_bus
*bus
, sd_bus_message
*m
) {
1313 bool found_object
= false;
1318 if (bus
->hello_flags
& KDBUS_HELLO_MONITOR
)
1321 if (m
->header
->type
!= SD_BUS_MESSAGE_METHOD_CALL
)
1324 if (hashmap_isempty(bus
->nodes
))
1327 /* Never respond to broadcast messages */
1328 if (bus
->bus_client
&& !m
->destination
)
1334 pl
= strlen(m
->path
);
1338 bus
->nodes_modified
= false;
1340 r
= object_find_and_run(bus
, m
, m
->path
, false, &found_object
);
1344 /* Look for fallback prefixes */
1345 OBJECT_PATH_FOREACH_PREFIX(prefix
, m
->path
) {
1347 if (bus
->nodes_modified
)
1350 r
= object_find_and_run(bus
, m
, prefix
, true, &found_object
);
1355 } while (bus
->nodes_modified
);
1360 if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Get") ||
1361 sd_bus_message_is_method_call(m
, "org.freedesktop.DBus.Properties", "Set"))
1362 r
= sd_bus_reply_method_errorf(
1364 SD_BUS_ERROR_UNKNOWN_PROPERTY
,
1365 "Unknown property or interface.");
1367 r
= sd_bus_reply_method_errorf(
1369 SD_BUS_ERROR_UNKNOWN_METHOD
,
1370 "Unknown method '%s' or interface '%s'.", m
->member
, m
->interface
);
1378 static struct node
*bus_node_allocate(sd_bus
*bus
, const char *path
) {
1379 struct node
*n
, *parent
;
1381 _cleanup_free_
char *s
= NULL
;
1387 assert(path
[0] == '/');
1389 n
= hashmap_get(bus
->nodes
, path
);
1393 r
= hashmap_ensure_allocated(&bus
->nodes
, &string_hash_ops
);
1401 if (streq(path
, "/"))
1404 e
= strrchr(path
, '/');
1407 p
= strndupa(path
, MAX(1, path
- e
));
1409 parent
= bus_node_allocate(bus
, p
);
1414 n
= new0(struct node
, 1);
1420 s
= NULL
; /* do not free */
1422 r
= hashmap_put(bus
->nodes
, n
->path
, n
);
1430 LIST_PREPEND(siblings
, parent
->child
, n
);
1435 void bus_node_gc(sd_bus
*b
, struct node
*n
) {
1448 assert(hashmap_remove(b
->nodes
, n
->path
) == n
);
1451 LIST_REMOVE(siblings
, n
->parent
->child
, n
);
1454 bus_node_gc(b
, n
->parent
);
1458 static int bus_add_object(
1463 sd_bus_message_handler_t callback
,
1470 assert_return(bus
, -EINVAL
);
1471 assert_return(object_path_is_valid(path
), -EINVAL
);
1472 assert_return(callback
, -EINVAL
);
1473 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1475 n
= bus_node_allocate(bus
, path
);
1479 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_CALLBACK
, sizeof(struct node_callback
), userdata
);
1485 s
->node_callback
.callback
= callback
;
1486 s
->node_callback
.is_fallback
= fallback
;
1488 s
->node_callback
.node
= n
;
1489 LIST_PREPEND(callbacks
, n
->callbacks
, &s
->node_callback
);
1490 bus
->nodes_modified
= true;
1498 sd_bus_slot_unref(s
);
1499 bus_node_gc(bus
, n
);
1504 _public_
int sd_bus_add_object(
1508 sd_bus_message_handler_t callback
,
1511 return bus_add_object(bus
, slot
, false, path
, callback
, userdata
);
1514 _public_
int sd_bus_add_fallback(
1518 sd_bus_message_handler_t callback
,
1521 return bus_add_object(bus
, slot
, true, prefix
, callback
, userdata
);
1524 static unsigned long vtable_member_hash_func(const void *a
, const uint8_t hash_key
[HASH_KEY_SIZE
]) {
1525 const struct vtable_member
*m
= a
;
1526 uint8_t hash_key2
[HASH_KEY_SIZE
];
1531 ret
= string_hash_func(m
->path
, hash_key
);
1533 /* Use a slightly different hash key for the interface */
1534 memcpy(hash_key2
, hash_key
, HASH_KEY_SIZE
);
1536 ret
^= string_hash_func(m
->interface
, hash_key2
);
1538 /* And an even different one for the member */
1540 ret
^= string_hash_func(m
->member
, hash_key2
);
1545 static int vtable_member_compare_func(const void *a
, const void *b
) {
1546 const struct vtable_member
*x
= a
, *y
= b
;
1552 r
= strcmp(x
->path
, y
->path
);
1556 r
= strcmp(x
->interface
, y
->interface
);
1560 return strcmp(x
->member
, y
->member
);
1563 static const struct hash_ops vtable_member_hash_ops
= {
1564 .hash
= vtable_member_hash_func
,
1565 .compare
= vtable_member_compare_func
1568 static int add_object_vtable_internal(
1572 const char *interface
,
1573 const sd_bus_vtable
*vtable
,
1575 sd_bus_object_find_t find
,
1578 sd_bus_slot
*s
= NULL
;
1579 struct node_vtable
*i
, *existing
= NULL
;
1580 const sd_bus_vtable
*v
;
1584 assert_return(bus
, -EINVAL
);
1585 assert_return(object_path_is_valid(path
), -EINVAL
);
1586 assert_return(interface_name_is_valid(interface
), -EINVAL
);
1587 assert_return(vtable
, -EINVAL
);
1588 assert_return(vtable
[0].type
== _SD_BUS_VTABLE_START
, -EINVAL
);
1589 assert_return(vtable
[0].x
.start
.element_size
== sizeof(struct sd_bus_vtable
), -EINVAL
);
1590 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1591 assert_return(!streq(interface
, "org.freedesktop.DBus.Properties") &&
1592 !streq(interface
, "org.freedesktop.DBus.Introspectable") &&
1593 !streq(interface
, "org.freedesktop.DBus.Peer") &&
1594 !streq(interface
, "org.freedesktop.DBus.ObjectManager"), -EINVAL
);
1596 r
= hashmap_ensure_allocated(&bus
->vtable_methods
, &vtable_member_hash_ops
);
1600 r
= hashmap_ensure_allocated(&bus
->vtable_properties
, &vtable_member_hash_ops
);
1604 n
= bus_node_allocate(bus
, path
);
1608 LIST_FOREACH(vtables
, i
, n
->vtables
) {
1609 if (i
->is_fallback
!= fallback
) {
1614 if (streq(i
->interface
, interface
)) {
1616 if (i
->vtable
== vtable
) {
1625 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_VTABLE
, sizeof(struct node_vtable
), userdata
);
1631 s
->node_vtable
.is_fallback
= fallback
;
1632 s
->node_vtable
.vtable
= vtable
;
1633 s
->node_vtable
.find
= find
;
1635 s
->node_vtable
.interface
= strdup(interface
);
1636 if (!s
->node_vtable
.interface
) {
1641 for (v
= s
->node_vtable
.vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1645 case _SD_BUS_VTABLE_METHOD
: {
1646 struct vtable_member
*m
;
1648 if (!member_name_is_valid(v
->x
.method
.member
) ||
1649 !signature_is_valid(strempty(v
->x
.method
.signature
), false) ||
1650 !signature_is_valid(strempty(v
->x
.method
.result
), false) ||
1651 !(v
->x
.method
.handler
|| (isempty(v
->x
.method
.signature
) && isempty(v
->x
.method
.result
))) ||
1652 v
->flags
& (SD_BUS_VTABLE_PROPERTY_CONST
|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) {
1657 m
= new0(struct vtable_member
, 1);
1663 m
->parent
= &s
->node_vtable
;
1665 m
->interface
= s
->node_vtable
.interface
;
1666 m
->member
= v
->x
.method
.member
;
1669 r
= hashmap_put(bus
->vtable_methods
, m
, m
);
1678 case _SD_BUS_VTABLE_WRITABLE_PROPERTY
:
1680 if (!(v
->x
.property
.set
|| bus_type_is_basic(v
->x
.property
.signature
[0]))) {
1687 case _SD_BUS_VTABLE_PROPERTY
: {
1688 struct vtable_member
*m
;
1690 if (!member_name_is_valid(v
->x
.property
.member
) ||
1691 !signature_is_single(v
->x
.property
.signature
, false) ||
1692 !(v
->x
.property
.get
|| bus_type_is_basic(v
->x
.property
.signature
[0]) || streq(v
->x
.property
.signature
, "as")) ||
1693 v
->flags
& SD_BUS_VTABLE_METHOD_NO_REPLY
||
1694 (!!(v
->flags
& SD_BUS_VTABLE_PROPERTY_CONST
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
) + !!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
)) > 1 ||
1695 (v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
&& v
->type
== _SD_BUS_VTABLE_PROPERTY
)) {
1700 m
= new0(struct vtable_member
, 1);
1706 m
->parent
= &s
->node_vtable
;
1708 m
->interface
= s
->node_vtable
.interface
;
1709 m
->member
= v
->x
.property
.member
;
1712 r
= hashmap_put(bus
->vtable_properties
, m
, m
);
1721 case _SD_BUS_VTABLE_SIGNAL
:
1723 if (!member_name_is_valid(v
->x
.signal
.member
) ||
1724 !signature_is_valid(strempty(v
->x
.signal
.signature
), false) ||
1725 v
->flags
& SD_BUS_VTABLE_UNPRIVILEGED
) {
1738 s
->node_vtable
.node
= n
;
1739 LIST_INSERT_AFTER(vtables
, n
->vtables
, existing
, &s
->node_vtable
);
1740 bus
->nodes_modified
= true;
1748 sd_bus_slot_unref(s
);
1749 bus_node_gc(bus
, n
);
1754 _public_
int sd_bus_add_object_vtable(
1758 const char *interface
,
1759 const sd_bus_vtable
*vtable
,
1762 return add_object_vtable_internal(bus
, slot
, path
, interface
, vtable
, false, NULL
, userdata
);
1765 _public_
int sd_bus_add_fallback_vtable(
1769 const char *interface
,
1770 const sd_bus_vtable
*vtable
,
1771 sd_bus_object_find_t find
,
1774 return add_object_vtable_internal(bus
, slot
, prefix
, interface
, vtable
, true, find
, userdata
);
1777 _public_
int sd_bus_add_node_enumerator(
1781 sd_bus_node_enumerator_t callback
,
1788 assert_return(bus
, -EINVAL
);
1789 assert_return(object_path_is_valid(path
), -EINVAL
);
1790 assert_return(callback
, -EINVAL
);
1791 assert_return(!bus_pid_changed(bus
), -ECHILD
);
1793 n
= bus_node_allocate(bus
, path
);
1797 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_ENUMERATOR
, sizeof(struct node_enumerator
), userdata
);
1803 s
->node_enumerator
.callback
= callback
;
1805 s
->node_enumerator
.node
= n
;
1806 LIST_PREPEND(enumerators
, n
->enumerators
, &s
->node_enumerator
);
1807 bus
->nodes_modified
= true;
1815 sd_bus_slot_unref(s
);
1816 bus_node_gc(bus
, n
);
1821 static int emit_properties_changed_on_interface(
1825 const char *interface
,
1826 bool require_fallback
,
1827 bool *found_interface
,
1830 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
1831 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
1832 bool has_invalidating
= false, has_changing
= false;
1833 struct vtable_member key
= {};
1834 struct node_vtable
*c
;
1844 assert(found_interface
);
1846 n
= hashmap_get(bus
->nodes
, prefix
);
1850 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1854 r
= sd_bus_message_append(m
, "s", interface
);
1858 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
1863 key
.interface
= interface
;
1865 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1866 if (require_fallback
&& !c
->is_fallback
)
1869 if (!streq(c
->interface
, interface
))
1872 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1875 if (bus
->nodes_modified
)
1880 *found_interface
= true;
1883 /* If the caller specified a list of
1884 * properties we include exactly those in the
1885 * PropertiesChanged message */
1887 STRV_FOREACH(property
, names
) {
1888 struct vtable_member
*v
;
1890 assert_return(member_name_is_valid(*property
), -EINVAL
);
1892 key
.member
= *property
;
1893 v
= hashmap_get(bus
->vtable_properties
, &key
);
1897 /* If there are two vtables for the same
1898 * interface, let's handle this property when
1899 * we come to that vtable. */
1903 assert_return(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
||
1904 v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
, -EDOM
);
1906 assert_return(!(v
->vtable
->flags
& SD_BUS_VTABLE_HIDDEN
), -EDOM
);
1908 if (v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1909 has_invalidating
= true;
1913 has_changing
= true;
1915 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
->vtable
, u
, &error
);
1918 if (bus
->nodes_modified
)
1922 const sd_bus_vtable
*v
;
1924 /* If the caller specified no properties list
1925 * we include all properties that are marked
1926 * as changing in the message. */
1928 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
1929 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
1932 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
1935 if (v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
) {
1936 has_invalidating
= true;
1940 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE
))
1943 has_changing
= true;
1945 r
= vtable_append_one_property(bus
, m
, m
->path
, c
, v
, u
, &error
);
1948 if (bus
->nodes_modified
)
1954 if (!has_invalidating
&& !has_changing
)
1957 r
= sd_bus_message_close_container(m
);
1961 r
= sd_bus_message_open_container(m
, 'a', "s");
1965 if (has_invalidating
) {
1966 LIST_FOREACH(vtables
, c
, n
->vtables
) {
1967 if (require_fallback
&& !c
->is_fallback
)
1970 if (!streq(c
->interface
, interface
))
1973 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
1976 if (bus
->nodes_modified
)
1982 STRV_FOREACH(property
, names
) {
1983 struct vtable_member
*v
;
1985 key
.member
= *property
;
1986 assert_se(v
= hashmap_get(bus
->vtable_properties
, &key
));
1987 assert(c
== v
->parent
);
1989 if (!(v
->vtable
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
1992 r
= sd_bus_message_append(m
, "s", *property
);
1997 const sd_bus_vtable
*v
;
1999 for (v
= c
->vtable
+1; v
->type
!= _SD_BUS_VTABLE_END
; v
++) {
2000 if (v
->type
!= _SD_BUS_VTABLE_PROPERTY
&& v
->type
!= _SD_BUS_VTABLE_WRITABLE_PROPERTY
)
2003 if (v
->flags
& SD_BUS_VTABLE_HIDDEN
)
2006 if (!(v
->flags
& SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION
))
2009 r
= sd_bus_message_append(m
, "s", v
->x
.property
.member
);
2017 r
= sd_bus_message_close_container(m
);
2021 r
= sd_bus_send(bus
, m
, NULL
);
2028 _public_
int sd_bus_emit_properties_changed_strv(
2031 const char *interface
,
2034 BUS_DONT_DESTROY(bus
);
2035 bool found_interface
= false;
2039 assert_return(bus
, -EINVAL
);
2040 assert_return(object_path_is_valid(path
), -EINVAL
);
2041 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2042 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2044 if (!BUS_IS_OPEN(bus
->state
))
2047 /* A non-NULL but empty names list means nothing needs to be
2048 generated. A NULL list OTOH indicates that all properties
2049 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2050 included in the PropertiesChanged message. */
2051 if (names
&& names
[0] == NULL
)
2055 bus
->nodes_modified
= false;
2057 r
= emit_properties_changed_on_interface(bus
, path
, path
, interface
, false, &found_interface
, names
);
2060 if (bus
->nodes_modified
)
2063 prefix
= alloca(strlen(path
) + 1);
2064 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2065 r
= emit_properties_changed_on_interface(bus
, prefix
, path
, interface
, true, &found_interface
, names
);
2068 if (bus
->nodes_modified
)
2072 } while (bus
->nodes_modified
);
2074 return found_interface
? 0 : -ENOENT
;
2077 _public_
int sd_bus_emit_properties_changed(
2080 const char *interface
,
2081 const char *name
, ...) {
2085 assert_return(bus
, -EINVAL
);
2086 assert_return(object_path_is_valid(path
), -EINVAL
);
2087 assert_return(interface_name_is_valid(interface
), -EINVAL
);
2088 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2090 if (!BUS_IS_OPEN(bus
->state
))
2096 names
= strv_from_stdarg_alloca(name
);
2098 return sd_bus_emit_properties_changed_strv(bus
, path
, interface
, names
);
2101 static int interfaces_added_append_one_prefix(
2106 const char *interface
,
2107 bool require_fallback
) {
2109 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
2110 bool found_interface
= false;
2111 struct node_vtable
*c
;
2122 n
= hashmap_get(bus
->nodes
, prefix
);
2126 LIST_FOREACH(vtables
, c
, n
->vtables
) {
2127 if (require_fallback
&& !c
->is_fallback
)
2130 if (!streq(c
->interface
, interface
))
2133 r
= node_vtable_get_userdata(bus
, path
, c
, &u
, &error
);
2136 if (bus
->nodes_modified
)
2141 if (!found_interface
) {
2142 r
= sd_bus_message_append_basic(m
, 's', interface
);
2146 r
= sd_bus_message_open_container(m
, 'a', "{sv}");
2150 found_interface
= true;
2153 r
= vtable_append_all_properties(bus
, m
, path
, c
, u
, &error
);
2156 if (bus
->nodes_modified
)
2160 if (found_interface
) {
2161 r
= sd_bus_message_close_container(m
);
2166 return found_interface
;
2169 static int interfaces_added_append_one(
2173 const char *interface
) {
2183 r
= interfaces_added_append_one_prefix(bus
, m
, path
, path
, interface
, false);
2186 if (bus
->nodes_modified
)
2189 prefix
= alloca(strlen(path
) + 1);
2190 OBJECT_PATH_FOREACH_PREFIX(prefix
, path
) {
2191 r
= interfaces_added_append_one_prefix(bus
, m
, prefix
, path
, interface
, true);
2194 if (bus
->nodes_modified
)
2201 _public_
int sd_bus_emit_interfaces_added_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2202 BUS_DONT_DESTROY(bus
);
2204 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2208 assert_return(bus
, -EINVAL
);
2209 assert_return(object_path_is_valid(path
), -EINVAL
);
2210 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2212 if (!BUS_IS_OPEN(bus
->state
))
2215 if (strv_isempty(interfaces
))
2219 bus
->nodes_modified
= false;
2220 m
= sd_bus_message_unref(m
);
2222 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2226 r
= sd_bus_message_append_basic(m
, 'o', path
);
2230 r
= sd_bus_message_open_container(m
, 'a', "{sa{sv}}");
2234 STRV_FOREACH(i
, interfaces
) {
2235 assert_return(interface_name_is_valid(*i
), -EINVAL
);
2237 r
= sd_bus_message_open_container(m
, 'e', "sa{sv}");
2241 r
= interfaces_added_append_one(bus
, m
, path
, *i
);
2245 if (bus
->nodes_modified
)
2248 r
= sd_bus_message_close_container(m
);
2253 if (bus
->nodes_modified
)
2256 r
= sd_bus_message_close_container(m
);
2260 } while (bus
->nodes_modified
);
2262 return sd_bus_send(bus
, m
, NULL
);
2265 _public_
int sd_bus_emit_interfaces_added(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2268 assert_return(bus
, -EINVAL
);
2269 assert_return(object_path_is_valid(path
), -EINVAL
);
2270 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2272 if (!BUS_IS_OPEN(bus
->state
))
2275 interfaces
= strv_from_stdarg_alloca(interface
);
2277 return sd_bus_emit_interfaces_added_strv(bus
, path
, interfaces
);
2280 _public_
int sd_bus_emit_interfaces_removed_strv(sd_bus
*bus
, const char *path
, char **interfaces
) {
2281 _cleanup_bus_message_unref_ sd_bus_message
*m
= NULL
;
2284 assert_return(bus
, -EINVAL
);
2285 assert_return(object_path_is_valid(path
), -EINVAL
);
2286 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2288 if (!BUS_IS_OPEN(bus
->state
))
2291 if (strv_isempty(interfaces
))
2294 r
= sd_bus_message_new_signal(bus
, &m
, path
, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2298 r
= sd_bus_message_append_basic(m
, 'o', path
);
2302 r
= sd_bus_message_append_strv(m
, interfaces
);
2306 return sd_bus_send(bus
, m
, NULL
);
2309 _public_
int sd_bus_emit_interfaces_removed(sd_bus
*bus
, const char *path
, const char *interface
, ...) {
2312 assert_return(bus
, -EINVAL
);
2313 assert_return(object_path_is_valid(path
), -EINVAL
);
2314 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2316 if (!BUS_IS_OPEN(bus
->state
))
2319 interfaces
= strv_from_stdarg_alloca(interface
);
2321 return sd_bus_emit_interfaces_removed_strv(bus
, path
, interfaces
);
2324 _public_
int sd_bus_add_object_manager(sd_bus
*bus
, sd_bus_slot
**slot
, const char *path
) {
2329 assert_return(bus
, -EINVAL
);
2330 assert_return(object_path_is_valid(path
), -EINVAL
);
2331 assert_return(!bus_pid_changed(bus
), -ECHILD
);
2333 n
= bus_node_allocate(bus
, path
);
2337 s
= bus_slot_allocate(bus
, !slot
, BUS_NODE_OBJECT_MANAGER
, sizeof(struct node_object_manager
), NULL
);
2343 s
->node_object_manager
.node
= n
;
2344 LIST_PREPEND(object_managers
, n
->object_managers
, &s
->node_object_manager
);
2345 bus
->nodes_modified
= true;
2353 sd_bus_slot_unref(s
);
2354 bus_node_gc(bus
, n
);