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_property_convert_userdata(const sd_bus_vtable *p, void *u) {
74 return (uint8_t*) u + p->x.property.offset;
77 static int vtable_property_get_userdata(
80 struct vtable_member *p,
82 sd_bus_error *error) {
92 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
95 if (bus->nodes_modified)
98 *userdata = vtable_property_convert_userdata(p->vtable, u);
102 static int add_enumerated_to_set(
105 struct node_enumerator *first,
107 sd_bus_error *error) {
109 struct node_enumerator *c;
116 LIST_FOREACH(enumerators, c, first) {
117 char **children = NULL, **k;
120 if (bus->nodes_modified)
123 slot = container_of(c, sd_bus_slot, node_enumerator);
125 bus->current_slot = sd_bus_slot_ref(slot);
126 bus->current_userdata = slot->userdata;
127 r = c->callback(bus, prefix, slot->userdata, &children, error);
128 bus->current_userdata = NULL;
129 bus->current_slot = sd_bus_slot_unref(slot);
133 if (sd_bus_error_is_set(error))
134 return -sd_bus_error_get_errno(error);
136 STRV_FOREACH(k, children) {
142 if (!object_path_is_valid(*k)){
148 if (!object_path_startswith(*k, prefix)) {
153 r = set_consume(s, *k);
166 static int add_subtree_to_set(
170 bool skip_subhierarchies,
172 sd_bus_error *error) {
182 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
185 if (bus->nodes_modified)
188 LIST_FOREACH(siblings, i, n->child) {
191 if (!object_path_startswith(i->path, prefix))
198 r = set_consume(s, t);
199 if (r < 0 && r != -EEXIST)
202 if (!skip_subhierarchies || !i->object_managers) {
203 r = add_subtree_to_set(bus, prefix, i, skip_subhierarchies, s, error);
206 if (bus->nodes_modified)
214 static int get_child_nodes(
218 bool skip_subhierarchies,
220 sd_bus_error *error) {
230 s = set_new(&string_hash_ops);
234 r = add_subtree_to_set(bus, prefix, n, skip_subhierarchies, s, error);
244 static int node_callbacks_run(
247 struct node_callback *first,
248 bool require_fallback,
249 bool *found_object) {
251 struct node_callback *c;
256 assert(found_object);
258 LIST_FOREACH(callbacks, c, first) {
259 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
262 if (bus->nodes_modified)
265 if (require_fallback && !c->is_fallback)
268 *found_object = true;
270 if (c->last_iteration == bus->iteration_counter)
273 c->last_iteration = bus->iteration_counter;
275 r = sd_bus_message_rewind(m, true);
279 slot = container_of(c, sd_bus_slot, node_callback);
281 bus->current_slot = sd_bus_slot_ref(slot);
282 bus->current_handler = c->callback;
283 bus->current_userdata = slot->userdata;
284 r = c->callback(m, slot->userdata, &error_buffer);
285 bus->current_userdata = NULL;
286 bus->current_handler = NULL;
287 bus->current_slot = sd_bus_slot_unref(slot);
289 r = bus_maybe_reply_error(m, r, &error_buffer);
297 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
299 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
307 /* If the entire bus is trusted let's grant access */
311 /* If the member is marked UNPRIVILEGED let's grant access */
312 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
315 /* Check have the caller has the requested capability
316 * set. Note that the flags value contains the capability
317 * number plus one, which we need to subtract here. We do this
318 * so that we have 0 as special value for "default
320 cap = CAPABILITY_SHIFT(c->vtable->flags);
322 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
328 r = sd_bus_query_sender_privilege(m, cap);
334 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
337 static int method_callbacks_run(
340 struct vtable_member *c,
341 bool require_fallback,
342 bool *found_object) {
344 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
345 const char *signature;
352 assert(found_object);
354 if (require_fallback && !c->parent->is_fallback)
357 r = check_access(bus, m, c, &error);
359 return bus_maybe_reply_error(m, r, &error);
361 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
363 return bus_maybe_reply_error(m, r, &error);
364 if (bus->nodes_modified)
367 *found_object = true;
369 if (c->last_iteration == bus->iteration_counter)
372 c->last_iteration = bus->iteration_counter;
374 r = sd_bus_message_rewind(m, true);
378 signature = sd_bus_message_get_signature(m, true);
382 if (!streq(strempty(c->vtable->x.method.signature), signature))
383 return sd_bus_reply_method_errorf(
385 SD_BUS_ERROR_INVALID_ARGS,
386 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
387 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
389 /* Keep track what the signature of the reply to this message
390 * should be, so that this can be enforced when sealing the
392 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
394 if (c->vtable->x.method.handler) {
397 slot = container_of(c->parent, sd_bus_slot, node_vtable);
399 bus->current_slot = sd_bus_slot_ref(slot);
400 bus->current_handler = c->vtable->x.method.handler;
401 bus->current_userdata = u;
402 r = c->vtable->x.method.handler(m, u, &error);
403 bus->current_userdata = NULL;
404 bus->current_handler = NULL;
405 bus->current_slot = sd_bus_slot_unref(slot);
407 return bus_maybe_reply_error(m, r, &error);
410 /* If the method callback is NULL, make this a successful NOP */
411 r = sd_bus_reply_method_return(m, NULL);
418 static int invoke_property_get(
421 const sd_bus_vtable *v,
423 const char *interface,
424 const char *property,
425 sd_bus_message *reply,
427 sd_bus_error *error) {
440 if (v->x.property.get) {
442 bus->current_slot = sd_bus_slot_ref(slot);
443 bus->current_userdata = userdata;
444 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
445 bus->current_userdata = NULL;
446 bus->current_slot = sd_bus_slot_unref(slot);
450 if (sd_bus_error_is_set(error))
451 return -sd_bus_error_get_errno(error);
455 /* Automatic handling if no callback is defined. */
457 if (streq(v->x.property.signature, "as"))
458 return sd_bus_message_append_strv(reply, *(char***) userdata);
460 assert(signature_is_single(v->x.property.signature, false));
461 assert(bus_type_is_basic(v->x.property.signature[0]));
463 switch (v->x.property.signature[0]) {
465 case SD_BUS_TYPE_STRING:
466 case SD_BUS_TYPE_SIGNATURE:
467 p = strempty(*(char**) userdata);
470 case SD_BUS_TYPE_OBJECT_PATH:
471 p = *(char**) userdata;
480 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
483 static int invoke_property_set(
486 const sd_bus_vtable *v,
488 const char *interface,
489 const char *property,
490 sd_bus_message *value,
492 sd_bus_error *error) {
504 if (v->x.property.set) {
506 bus->current_slot = sd_bus_slot_ref(slot);
507 bus->current_userdata = userdata;
508 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
509 bus->current_userdata = NULL;
510 bus->current_slot = sd_bus_slot_unref(slot);
514 if (sd_bus_error_is_set(error))
515 return -sd_bus_error_get_errno(error);
519 /* Automatic handling if no callback is defined. */
521 assert(signature_is_single(v->x.property.signature, false));
522 assert(bus_type_is_basic(v->x.property.signature[0]));
524 switch (v->x.property.signature[0]) {
526 case SD_BUS_TYPE_STRING:
527 case SD_BUS_TYPE_OBJECT_PATH:
528 case SD_BUS_TYPE_SIGNATURE: {
532 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
540 free(*(char**) userdata);
541 *(char**) userdata = n;
547 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
557 static int property_get_set_callbacks_run(
560 struct vtable_member *c,
561 bool require_fallback,
563 bool *found_object) {
565 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
566 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
574 assert(found_object);
576 if (require_fallback && !c->parent->is_fallback)
579 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
581 return bus_maybe_reply_error(m, r, &error);
582 if (bus->nodes_modified)
585 slot = container_of(c->parent, sd_bus_slot, node_vtable);
587 *found_object = true;
589 r = sd_bus_message_new_method_return(m, &reply);
594 /* Note that we do not protect against reexecution
595 * here (using the last_iteration check, see below),
596 * should the node tree have changed and we got called
597 * again. We assume that property Get() calls are
598 * ultimately without side-effects or if they aren't
599 * then at least idempotent. */
601 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
605 /* Note that we do not do an access check here. Read
606 * access to properties is always unrestricted, since
607 * PropertiesChanged signals broadcast contents
610 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
612 return bus_maybe_reply_error(m, r, &error);
614 if (bus->nodes_modified)
617 r = sd_bus_message_close_container(reply);
622 const char *signature = NULL;
625 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
626 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
628 /* Avoid that we call the set routine more than once
629 * if the processing of this message got restarted
630 * because the node tree changed. */
631 if (c->last_iteration == bus->iteration_counter)
634 c->last_iteration = bus->iteration_counter;
636 r = sd_bus_message_peek_type(m, &type, &signature);
640 if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
641 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));
643 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
647 r = check_access(bus, m, c, &error);
649 return bus_maybe_reply_error(m, r, &error);
651 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
653 return bus_maybe_reply_error(m, r, &error);
655 if (bus->nodes_modified)
658 r = sd_bus_message_exit_container(m);
663 r = sd_bus_send(bus, reply, NULL);
670 static int vtable_append_one_property(
672 sd_bus_message *reply,
674 struct node_vtable *c,
675 const sd_bus_vtable *v,
677 sd_bus_error *error) {
688 r = sd_bus_message_open_container(reply, 'e', "sv");
692 r = sd_bus_message_append(reply, "s", v->x.property.member);
696 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
700 slot = container_of(c, sd_bus_slot, node_vtable);
702 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
705 if (bus->nodes_modified)
708 r = sd_bus_message_close_container(reply);
712 r = sd_bus_message_close_container(reply);
719 static int vtable_append_all_properties(
721 sd_bus_message *reply,
723 struct node_vtable *c,
725 sd_bus_error *error) {
727 const sd_bus_vtable *v;
735 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
738 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
739 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
742 if (v->flags & SD_BUS_VTABLE_HIDDEN)
745 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
748 if (bus->nodes_modified)
755 static int property_get_all_callbacks_run(
758 struct node_vtable *first,
759 bool require_fallback,
761 bool *found_object) {
763 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
764 struct node_vtable *c;
765 bool found_interface;
770 assert(found_object);
772 r = sd_bus_message_new_method_return(m, &reply);
776 r = sd_bus_message_open_container(reply, 'a', "{sv}");
780 found_interface = !iface ||
781 streq(iface, "org.freedesktop.DBus.Properties") ||
782 streq(iface, "org.freedesktop.DBus.Peer") ||
783 streq(iface, "org.freedesktop.DBus.Introspectable");
785 LIST_FOREACH(vtables, c, first) {
786 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
789 if (require_fallback && !c->is_fallback)
792 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
794 return bus_maybe_reply_error(m, r, &error);
795 if (bus->nodes_modified)
800 *found_object = true;
802 if (iface && !streq(c->interface, iface))
804 found_interface = true;
806 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
808 return bus_maybe_reply_error(m, r, &error);
809 if (bus->nodes_modified)
813 if (!found_interface) {
814 r = sd_bus_reply_method_errorf(
816 SD_BUS_ERROR_UNKNOWN_INTERFACE,
817 "Unknown interface '%s'.", iface);
824 r = sd_bus_message_close_container(reply);
828 r = sd_bus_send(bus, reply, NULL);
835 static int bus_node_exists(
839 bool require_fallback) {
841 struct node_vtable *c;
842 struct node_callback *k;
849 /* Tests if there's anything attached directly to this node
850 * for the specified path */
852 if (!require_fallback && (n->enumerators || n->object_managers))
855 LIST_FOREACH(callbacks, k, n->callbacks) {
856 if (require_fallback && !k->is_fallback)
862 LIST_FOREACH(vtables, c, n->vtables) {
863 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
865 if (require_fallback && !c->is_fallback)
868 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
871 if (bus->nodes_modified)
878 static int process_introspect(
882 bool require_fallback,
883 bool *found_object) {
885 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
886 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
887 _cleanup_set_free_free_ Set *s = NULL;
888 const char *previous_interface = NULL;
889 struct introspect intro;
890 struct node_vtable *c;
897 assert(found_object);
899 r = get_child_nodes(bus, m->path, n, false, &s, &error);
901 return bus_maybe_reply_error(m, r, &error);
902 if (bus->nodes_modified)
905 r = introspect_begin(&intro, bus->trusted);
909 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
913 empty = set_isempty(s);
915 LIST_FOREACH(vtables, c, n->vtables) {
916 if (require_fallback && !c->is_fallback)
919 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
921 r = bus_maybe_reply_error(m, r, &error);
924 if (bus->nodes_modified) {
933 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
936 if (!streq_ptr(previous_interface, c->interface)) {
938 if (previous_interface)
939 fputs(" </interface>\n", intro.f);
941 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
944 r = introspect_write_interface(&intro, c->vtable);
948 previous_interface = c->interface;
951 if (previous_interface)
952 fputs(" </interface>\n", intro.f);
955 /* Nothing?, let's see if we exist at all, and if not
956 * refuse to do anything */
957 r = bus_node_exists(bus, n, m->path, require_fallback);
960 if (bus->nodes_modified) {
966 *found_object = true;
968 r = introspect_write_child_nodes(&intro, s, m->path);
972 r = introspect_finish(&intro, bus, m, &reply);
976 r = sd_bus_send(bus, reply, NULL);
983 introspect_free(&intro);
987 static int object_manager_serialize_path(
989 sd_bus_message *reply,
992 bool require_fallback,
993 sd_bus_error *error) {
995 const char *previous_interface = NULL;
996 bool found_something = false;
997 struct node_vtable *i;
1007 n = hashmap_get(bus->nodes, prefix);
1011 LIST_FOREACH(vtables, i, n->vtables) {
1014 if (require_fallback && !i->is_fallback)
1017 r = node_vtable_get_userdata(bus, path, i, &u, error);
1020 if (bus->nodes_modified)
1025 if (!found_something) {
1027 /* Open the object part */
1029 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1033 r = sd_bus_message_append(reply, "o", path);
1037 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1041 found_something = true;
1044 if (!streq_ptr(previous_interface, i->interface)) {
1046 /* Maybe close the previous interface part */
1048 if (previous_interface) {
1049 r = sd_bus_message_close_container(reply);
1053 r = sd_bus_message_close_container(reply);
1058 /* Open the new interface part */
1060 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1064 r = sd_bus_message_append(reply, "s", i->interface);
1068 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1073 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1076 if (bus->nodes_modified)
1079 previous_interface = i->interface;
1082 if (previous_interface) {
1083 r = sd_bus_message_close_container(reply);
1087 r = sd_bus_message_close_container(reply);
1092 if (found_something) {
1093 r = sd_bus_message_close_container(reply);
1097 r = sd_bus_message_close_container(reply);
1105 static int object_manager_serialize_path_and_fallbacks(
1107 sd_bus_message *reply,
1109 sd_bus_error *error) {
1119 /* First, add all vtables registered for this path */
1120 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1123 if (bus->nodes_modified)
1126 /* Second, add fallback vtables registered for any of the prefixes */
1127 prefix = alloca(strlen(path) + 1);
1128 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1129 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1132 if (bus->nodes_modified)
1139 static int process_get_managed_objects(
1143 bool require_fallback,
1144 bool *found_object) {
1146 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1147 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1148 _cleanup_set_free_free_ Set *s = NULL;
1156 assert(found_object);
1158 /* Spec says, GetManagedObjects() is only implemented on the root of a
1159 * sub-tree. Therefore, we require a registered object-manager on
1160 * exactly the queried path, otherwise, we refuse to respond. */
1162 if (require_fallback || !n->object_managers)
1165 r = get_child_nodes(bus, m->path, n, true, &s, &error);
1168 if (bus->nodes_modified)
1171 r = set_put_strdup(s, m->path);
1175 r = sd_bus_message_new_method_return(m, &reply);
1179 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1183 SET_FOREACH(path, s, i) {
1184 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1188 if (bus->nodes_modified)
1192 r = sd_bus_message_close_container(reply);
1196 r = sd_bus_send(bus, reply, NULL);
1203 static int object_find_and_run(
1207 bool require_fallback,
1208 bool *found_object) {
1211 struct vtable_member vtable_key, *v;
1217 assert(found_object);
1219 n = hashmap_get(bus->nodes, p);
1223 /* First, try object callbacks */
1224 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1227 if (bus->nodes_modified)
1230 if (!m->interface || !m->member)
1233 /* Then, look for a known method */
1234 vtable_key.path = (char*) p;
1235 vtable_key.interface = m->interface;
1236 vtable_key.member = m->member;
1238 v = hashmap_get(bus->vtable_methods, &vtable_key);
1240 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1243 if (bus->nodes_modified)
1247 /* Then, look for a known property */
1248 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1251 get = streq(m->member, "Get");
1253 if (get || streq(m->member, "Set")) {
1255 r = sd_bus_message_rewind(m, true);
1259 vtable_key.path = (char*) p;
1261 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1263 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1265 v = hashmap_get(bus->vtable_properties, &vtable_key);
1267 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1272 } else if (streq(m->member, "GetAll")) {
1275 r = sd_bus_message_rewind(m, true);
1279 r = sd_bus_message_read(m, "s", &iface);
1281 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1286 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1291 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1293 if (!isempty(sd_bus_message_get_signature(m, true)))
1294 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1296 r = process_introspect(bus, m, n, require_fallback, found_object);
1300 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1302 if (!isempty(sd_bus_message_get_signature(m, true)))
1303 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1305 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1310 if (bus->nodes_modified)
1313 if (!*found_object) {
1314 r = bus_node_exists(bus, n, m->path, require_fallback);
1317 if (bus->nodes_modified)
1320 *found_object = true;
1326 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1329 bool found_object = false;
1334 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1337 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1340 if (hashmap_isempty(bus->nodes))
1343 /* Never respond to broadcast messages */
1344 if (bus->bus_client && !m->destination)
1350 pl = strlen(m->path);
1354 bus->nodes_modified = false;
1356 r = object_find_and_run(bus, m, m->path, false, &found_object);
1360 /* Look for fallback prefixes */
1361 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1363 if (bus->nodes_modified)
1366 r = object_find_and_run(bus, m, prefix, true, &found_object);
1371 } while (bus->nodes_modified);
1376 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1377 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1378 r = sd_bus_reply_method_errorf(
1380 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1381 "Unknown property or interface.");
1383 r = sd_bus_reply_method_errorf(
1385 SD_BUS_ERROR_UNKNOWN_METHOD,
1386 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1394 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1395 struct node *n, *parent;
1397 _cleanup_free_ char *s = NULL;
1403 assert(path[0] == '/');
1405 n = hashmap_get(bus->nodes, path);
1409 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1417 if (streq(path, "/"))
1420 e = strrchr(path, '/');
1423 p = strndupa(path, MAX(1, e - path));
1425 parent = bus_node_allocate(bus, p);
1430 n = new0(struct node, 1);
1436 s = NULL; /* do not free */
1438 r = hashmap_put(bus->nodes, n->path, n);
1446 LIST_PREPEND(siblings, parent->child, n);
1451 void bus_node_gc(sd_bus *b, struct node *n) {
1464 assert(hashmap_remove(b->nodes, n->path) == n);
1467 LIST_REMOVE(siblings, n->parent->child, n);
1470 bus_node_gc(b, n->parent);
1474 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1480 n = hashmap_get(bus->nodes, path);
1484 prefix = alloca(strlen(path) + 1);
1485 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1486 n = hashmap_get(bus->nodes, prefix);
1492 while (n && !n->object_managers)
1500 static int bus_add_object(
1505 sd_bus_message_handler_t callback,
1512 assert_return(bus, -EINVAL);
1513 assert_return(object_path_is_valid(path), -EINVAL);
1514 assert_return(callback, -EINVAL);
1515 assert_return(!bus_pid_changed(bus), -ECHILD);
1517 n = bus_node_allocate(bus, path);
1521 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1527 s->node_callback.callback = callback;
1528 s->node_callback.is_fallback = fallback;
1530 s->node_callback.node = n;
1531 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1532 bus->nodes_modified = true;
1540 sd_bus_slot_unref(s);
1541 bus_node_gc(bus, n);
1546 _public_ int sd_bus_add_object(
1550 sd_bus_message_handler_t callback,
1553 return bus_add_object(bus, slot, false, path, callback, userdata);
1556 _public_ int sd_bus_add_fallback(
1560 sd_bus_message_handler_t callback,
1563 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1566 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1567 const struct vtable_member *m = a;
1568 uint8_t hash_key2[HASH_KEY_SIZE];
1573 ret = string_hash_func(m->path, hash_key);
1575 /* Use a slightly different hash key for the interface */
1576 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1578 ret ^= string_hash_func(m->interface, hash_key2);
1580 /* And an even different one for the member */
1582 ret ^= string_hash_func(m->member, hash_key2);
1587 static int vtable_member_compare_func(const void *a, const void *b) {
1588 const struct vtable_member *x = a, *y = b;
1594 r = strcmp(x->path, y->path);
1598 r = strcmp(x->interface, y->interface);
1602 return strcmp(x->member, y->member);
1605 static const struct hash_ops vtable_member_hash_ops = {
1606 .hash = vtable_member_hash_func,
1607 .compare = vtable_member_compare_func
1610 static int add_object_vtable_internal(
1614 const char *interface,
1615 const sd_bus_vtable *vtable,
1617 sd_bus_object_find_t find,
1620 sd_bus_slot *s = NULL;
1621 struct node_vtable *i, *existing = NULL;
1622 const sd_bus_vtable *v;
1626 assert_return(bus, -EINVAL);
1627 assert_return(object_path_is_valid(path), -EINVAL);
1628 assert_return(interface_name_is_valid(interface), -EINVAL);
1629 assert_return(vtable, -EINVAL);
1630 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1631 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1632 assert_return(!bus_pid_changed(bus), -ECHILD);
1633 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1634 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1635 !streq(interface, "org.freedesktop.DBus.Peer") &&
1636 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1638 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1642 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1646 n = bus_node_allocate(bus, path);
1650 LIST_FOREACH(vtables, i, n->vtables) {
1651 if (i->is_fallback != fallback) {
1656 if (streq(i->interface, interface)) {
1658 if (i->vtable == vtable) {
1667 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1673 s->node_vtable.is_fallback = fallback;
1674 s->node_vtable.vtable = vtable;
1675 s->node_vtable.find = find;
1677 s->node_vtable.interface = strdup(interface);
1678 if (!s->node_vtable.interface) {
1683 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1687 case _SD_BUS_VTABLE_METHOD: {
1688 struct vtable_member *m;
1690 if (!member_name_is_valid(v->x.method.member) ||
1691 !signature_is_valid(strempty(v->x.method.signature), false) ||
1692 !signature_is_valid(strempty(v->x.method.result), false) ||
1693 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1694 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1699 m = new0(struct vtable_member, 1);
1705 m->parent = &s->node_vtable;
1707 m->interface = s->node_vtable.interface;
1708 m->member = v->x.method.member;
1711 r = hashmap_put(bus->vtable_methods, m, m);
1720 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1722 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1727 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1734 case _SD_BUS_VTABLE_PROPERTY: {
1735 struct vtable_member *m;
1737 if (!member_name_is_valid(v->x.property.member) ||
1738 !signature_is_single(v->x.property.signature, false) ||
1739 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1740 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1741 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1742 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1747 m = new0(struct vtable_member, 1);
1753 m->parent = &s->node_vtable;
1755 m->interface = s->node_vtable.interface;
1756 m->member = v->x.property.member;
1759 r = hashmap_put(bus->vtable_properties, m, m);
1768 case _SD_BUS_VTABLE_SIGNAL:
1770 if (!member_name_is_valid(v->x.signal.member) ||
1771 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1772 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1785 s->node_vtable.node = n;
1786 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1787 bus->nodes_modified = true;
1795 sd_bus_slot_unref(s);
1796 bus_node_gc(bus, n);
1801 _public_ int sd_bus_add_object_vtable(
1805 const char *interface,
1806 const sd_bus_vtable *vtable,
1809 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1812 _public_ int sd_bus_add_fallback_vtable(
1816 const char *interface,
1817 const sd_bus_vtable *vtable,
1818 sd_bus_object_find_t find,
1821 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1824 _public_ int sd_bus_add_node_enumerator(
1828 sd_bus_node_enumerator_t callback,
1835 assert_return(bus, -EINVAL);
1836 assert_return(object_path_is_valid(path), -EINVAL);
1837 assert_return(callback, -EINVAL);
1838 assert_return(!bus_pid_changed(bus), -ECHILD);
1840 n = bus_node_allocate(bus, path);
1844 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1850 s->node_enumerator.callback = callback;
1852 s->node_enumerator.node = n;
1853 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1854 bus->nodes_modified = true;
1862 sd_bus_slot_unref(s);
1863 bus_node_gc(bus, n);
1868 static int emit_properties_changed_on_interface(
1872 const char *interface,
1873 bool require_fallback,
1874 bool *found_interface,
1877 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1878 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1879 bool has_invalidating = false, has_changing = false;
1880 struct vtable_member key = {};
1881 struct node_vtable *c;
1891 assert(found_interface);
1893 n = hashmap_get(bus->nodes, prefix);
1897 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1901 r = sd_bus_message_append(m, "s", interface);
1905 r = sd_bus_message_open_container(m, 'a', "{sv}");
1910 key.interface = interface;
1912 LIST_FOREACH(vtables, c, n->vtables) {
1913 if (require_fallback && !c->is_fallback)
1916 if (!streq(c->interface, interface))
1919 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1922 if (bus->nodes_modified)
1927 *found_interface = true;
1930 /* If the caller specified a list of
1931 * properties we include exactly those in the
1932 * PropertiesChanged message */
1934 STRV_FOREACH(property, names) {
1935 struct vtable_member *v;
1937 assert_return(member_name_is_valid(*property), -EINVAL);
1939 key.member = *property;
1940 v = hashmap_get(bus->vtable_properties, &key);
1944 /* If there are two vtables for the same
1945 * interface, let's handle this property when
1946 * we come to that vtable. */
1950 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1951 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1953 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1955 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1956 has_invalidating = true;
1960 has_changing = true;
1962 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1965 if (bus->nodes_modified)
1969 const sd_bus_vtable *v;
1971 /* If the caller specified no properties list
1972 * we include all properties that are marked
1973 * as changing in the message. */
1975 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1976 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1979 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1982 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1983 has_invalidating = true;
1987 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1990 has_changing = true;
1992 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1995 if (bus->nodes_modified)
2001 if (!has_invalidating && !has_changing)
2004 r = sd_bus_message_close_container(m);
2008 r = sd_bus_message_open_container(m, 'a', "s");
2012 if (has_invalidating) {
2013 LIST_FOREACH(vtables, c, n->vtables) {
2014 if (require_fallback && !c->is_fallback)
2017 if (!streq(c->interface, interface))
2020 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2023 if (bus->nodes_modified)
2029 STRV_FOREACH(property, names) {
2030 struct vtable_member *v;
2032 key.member = *property;
2033 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2034 assert(c == v->parent);
2036 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2039 r = sd_bus_message_append(m, "s", *property);
2044 const sd_bus_vtable *v;
2046 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2047 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2050 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2053 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2056 r = sd_bus_message_append(m, "s", v->x.property.member);
2064 r = sd_bus_message_close_container(m);
2068 r = sd_bus_send(bus, m, NULL);
2075 _public_ int sd_bus_emit_properties_changed_strv(
2078 const char *interface,
2081 BUS_DONT_DESTROY(bus);
2082 bool found_interface = false;
2086 assert_return(bus, -EINVAL);
2087 assert_return(object_path_is_valid(path), -EINVAL);
2088 assert_return(interface_name_is_valid(interface), -EINVAL);
2089 assert_return(!bus_pid_changed(bus), -ECHILD);
2091 if (!BUS_IS_OPEN(bus->state))
2094 /* A non-NULL but empty names list means nothing needs to be
2095 generated. A NULL list OTOH indicates that all properties
2096 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2097 included in the PropertiesChanged message. */
2098 if (names && names[0] == NULL)
2102 bus->nodes_modified = false;
2104 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2107 if (bus->nodes_modified)
2110 prefix = alloca(strlen(path) + 1);
2111 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2112 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2115 if (bus->nodes_modified)
2119 } while (bus->nodes_modified);
2121 return found_interface ? 0 : -ENOENT;
2124 _public_ int sd_bus_emit_properties_changed(
2127 const char *interface,
2128 const char *name, ...) {
2132 assert_return(bus, -EINVAL);
2133 assert_return(object_path_is_valid(path), -EINVAL);
2134 assert_return(interface_name_is_valid(interface), -EINVAL);
2135 assert_return(!bus_pid_changed(bus), -ECHILD);
2137 if (!BUS_IS_OPEN(bus->state))
2143 names = strv_from_stdarg_alloca(name);
2145 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2148 static int object_added_append_all_prefix(
2154 bool require_fallback) {
2156 const char *previous_interface = NULL;
2157 struct node_vtable *c;
2167 n = hashmap_get(bus->nodes, prefix);
2171 LIST_FOREACH(vtables, c, n->vtables) {
2172 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2175 if (require_fallback && !c->is_fallback)
2178 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2181 if (bus->nodes_modified)
2186 if (!streq_ptr(c->interface, previous_interface)) {
2187 /* If a child-node already handled this interface, we
2188 * skip it on any of its parents. The child vtables
2189 * always fully override any conflicting vtables of
2190 * any parent node. */
2191 if (set_get(s, c->interface))
2194 r = set_put(s, c->interface);
2198 if (previous_interface) {
2199 r = sd_bus_message_close_container(m);
2202 r = sd_bus_message_close_container(m);
2207 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2210 r = sd_bus_message_append(m, "s", c->interface);
2213 r = sd_bus_message_open_container(m, 'a', "{sv}");
2217 previous_interface = c->interface;
2220 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2223 if (bus->nodes_modified)
2227 if (previous_interface) {
2228 r = sd_bus_message_close_container(m);
2231 r = sd_bus_message_close_container(m);
2239 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2240 _cleanup_set_free_ Set *s = NULL;
2249 * This appends all interfaces registered on path @path. We first add
2250 * the builtin interfaces, which are always available and handled by
2251 * sd-bus. Then, we add all interfaces registered on the exact node,
2252 * followed by all fallback interfaces registered on any parent prefix.
2254 * If an interface is registered multiple times on the same node with
2255 * different vtables, we merge all the properties across all vtables.
2256 * However, if a child node has the same interface registered as one of
2257 * its parent nodes has as fallback, we make the child overwrite the
2258 * parent instead of extending it. Therefore, we keep a "Set" of all
2259 * handled interfaces during parent traversal, so we skip interfaces on
2260 * a parent that were overwritten by a child.
2263 s = set_new(&string_hash_ops);
2267 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2270 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2273 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2276 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2280 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2283 if (bus->nodes_modified)
2286 prefix = alloca(strlen(path) + 1);
2287 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2288 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2291 if (bus->nodes_modified)
2298 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2299 BUS_DONT_DESTROY(bus);
2301 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2302 struct node *object_manager;
2306 * This emits an InterfacesAdded signal on the given path, by iterating
2307 * all registered vtables and fallback vtables on the path. All
2308 * properties are queried and included in the signal.
2309 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2310 * explicit list of registered interfaces. However, unlike
2311 * interfaces_added(), this call can figure out the list of supported
2312 * interfaces itself. Furthermore, it properly adds the builtin
2313 * org.freedesktop.DBus.* interfaces.
2316 assert_return(bus, -EINVAL);
2317 assert_return(object_path_is_valid(path), -EINVAL);
2318 assert_return(!bus_pid_changed(bus), -ECHILD);
2320 if (!BUS_IS_OPEN(bus->state))
2323 r = bus_find_parent_object_manager(bus, &object_manager, path);
2330 bus->nodes_modified = false;
2331 m = sd_bus_message_unref(m);
2333 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2337 r = sd_bus_message_append_basic(m, 'o', path);
2341 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2345 r = object_added_append_all(bus, m, path);
2349 if (bus->nodes_modified)
2352 r = sd_bus_message_close_container(m);
2356 } while (bus->nodes_modified);
2358 return sd_bus_send(bus, m, NULL);
2361 static int object_removed_append_all_prefix(
2367 bool require_fallback) {
2369 const char *previous_interface = NULL;
2370 struct node_vtable *c;
2380 n = hashmap_get(bus->nodes, prefix);
2384 LIST_FOREACH(vtables, c, n->vtables) {
2385 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2388 if (require_fallback && !c->is_fallback)
2390 if (streq_ptr(c->interface, previous_interface))
2393 /* If a child-node already handled this interface, we
2394 * skip it on any of its parents. The child vtables
2395 * always fully override any conflicting vtables of
2396 * any parent node. */
2397 if (set_get(s, c->interface))
2400 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2403 if (bus->nodes_modified)
2408 r = set_put(s, c->interface);
2412 r = sd_bus_message_append(m, "s", c->interface);
2416 previous_interface = c->interface;
2422 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2423 _cleanup_set_free_ Set *s = NULL;
2431 /* see sd_bus_emit_object_added() for details */
2433 s = set_new(&string_hash_ops);
2437 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2440 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2443 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2446 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2450 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2453 if (bus->nodes_modified)
2456 prefix = alloca(strlen(path) + 1);
2457 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2458 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2461 if (bus->nodes_modified)
2468 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2469 BUS_DONT_DESTROY(bus);
2471 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2472 struct node *object_manager;
2476 * This is like sd_bus_emit_object_added(), but emits an
2477 * InterfacesRemoved signal on the given path. This only includes any
2478 * registered interfaces but skips the properties. Note that this will
2479 * call into the find() callbacks of any registered vtable. Therefore,
2480 * you must call this function before destroying/unlinking your object.
2481 * Otherwise, the list of interfaces will be incomplete. However, note
2482 * that this will *NOT* call into any property callback. Therefore, the
2483 * object might be in an "destructed" state, as long as we can find it.
2486 assert_return(bus, -EINVAL);
2487 assert_return(object_path_is_valid(path), -EINVAL);
2488 assert_return(!bus_pid_changed(bus), -ECHILD);
2490 if (!BUS_IS_OPEN(bus->state))
2493 r = bus_find_parent_object_manager(bus, &object_manager, path);
2500 bus->nodes_modified = false;
2501 m = sd_bus_message_unref(m);
2503 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2507 r = sd_bus_message_append_basic(m, 'o', path);
2511 r = sd_bus_message_open_container(m, 'a', "s");
2515 r = object_removed_append_all(bus, m, path);
2519 if (bus->nodes_modified)
2522 r = sd_bus_message_close_container(m);
2526 } while (bus->nodes_modified);
2528 return sd_bus_send(bus, m, NULL);
2531 static int interfaces_added_append_one_prefix(
2536 const char *interface,
2537 bool require_fallback) {
2539 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2540 bool found_interface = false;
2541 struct node_vtable *c;
2552 n = hashmap_get(bus->nodes, prefix);
2556 LIST_FOREACH(vtables, c, n->vtables) {
2557 if (require_fallback && !c->is_fallback)
2560 if (!streq(c->interface, interface))
2563 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2566 if (bus->nodes_modified)
2571 if (!found_interface) {
2572 r = sd_bus_message_append_basic(m, 's', interface);
2576 r = sd_bus_message_open_container(m, 'a', "{sv}");
2580 found_interface = true;
2583 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2586 if (bus->nodes_modified)
2590 if (found_interface) {
2591 r = sd_bus_message_close_container(m);
2596 return found_interface;
2599 static int interfaces_added_append_one(
2603 const char *interface) {
2613 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2616 if (bus->nodes_modified)
2619 prefix = alloca(strlen(path) + 1);
2620 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2621 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2624 if (bus->nodes_modified)
2631 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2632 BUS_DONT_DESTROY(bus);
2634 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2635 struct node *object_manager;
2639 assert_return(bus, -EINVAL);
2640 assert_return(object_path_is_valid(path), -EINVAL);
2641 assert_return(!bus_pid_changed(bus), -ECHILD);
2643 if (!BUS_IS_OPEN(bus->state))
2646 if (strv_isempty(interfaces))
2649 r = bus_find_parent_object_manager(bus, &object_manager, path);
2656 bus->nodes_modified = false;
2657 m = sd_bus_message_unref(m);
2659 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2663 r = sd_bus_message_append_basic(m, 'o', path);
2667 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2671 STRV_FOREACH(i, interfaces) {
2672 assert_return(interface_name_is_valid(*i), -EINVAL);
2674 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2678 r = interfaces_added_append_one(bus, m, path, *i);
2682 if (bus->nodes_modified)
2685 r = sd_bus_message_close_container(m);
2690 if (bus->nodes_modified)
2693 r = sd_bus_message_close_container(m);
2697 } while (bus->nodes_modified);
2699 return sd_bus_send(bus, m, NULL);
2702 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2705 assert_return(bus, -EINVAL);
2706 assert_return(object_path_is_valid(path), -EINVAL);
2707 assert_return(!bus_pid_changed(bus), -ECHILD);
2709 if (!BUS_IS_OPEN(bus->state))
2712 interfaces = strv_from_stdarg_alloca(interface);
2714 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2717 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2718 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2719 struct node *object_manager;
2722 assert_return(bus, -EINVAL);
2723 assert_return(object_path_is_valid(path), -EINVAL);
2724 assert_return(!bus_pid_changed(bus), -ECHILD);
2726 if (!BUS_IS_OPEN(bus->state))
2729 if (strv_isempty(interfaces))
2732 r = bus_find_parent_object_manager(bus, &object_manager, path);
2738 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2742 r = sd_bus_message_append_basic(m, 'o', path);
2746 r = sd_bus_message_append_strv(m, interfaces);
2750 return sd_bus_send(bus, m, NULL);
2753 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2756 assert_return(bus, -EINVAL);
2757 assert_return(object_path_is_valid(path), -EINVAL);
2758 assert_return(!bus_pid_changed(bus), -ECHILD);
2760 if (!BUS_IS_OPEN(bus->state))
2763 interfaces = strv_from_stdarg_alloca(interface);
2765 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2768 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2773 assert_return(bus, -EINVAL);
2774 assert_return(object_path_is_valid(path), -EINVAL);
2775 assert_return(!bus_pid_changed(bus), -ECHILD);
2777 n = bus_node_allocate(bus, path);
2781 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2787 s->node_object_manager.node = n;
2788 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2789 bus->nodes_modified = true;
2797 sd_bus_slot_unref(s);
2798 bus_node_gc(bus, n);