1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include "alloc-util.h"
22 #include "bus-internal.h"
23 #include "bus-introspect.h"
24 #include "bus-message.h"
25 #include "bus-objects.h"
26 #include "bus-signature.h"
31 #include "string-util.h"
34 static int node_vtable_get_userdata(
37 struct node_vtable *c,
39 sd_bus_error *error) {
49 s = container_of(c, sd_bus_slot, node_vtable);
52 bus->current_slot = sd_bus_slot_ref(s);
53 bus->current_userdata = u;
54 r = c->find(bus, path, c->interface, u, &found_u, error);
55 bus->current_userdata = NULL;
56 bus->current_slot = sd_bus_slot_unref(s);
60 if (sd_bus_error_is_set(error))
61 return -sd_bus_error_get_errno(error);
73 static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
76 return (uint8_t*) u + p->x.method.offset;
79 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
82 return (uint8_t*) u + p->x.property.offset;
85 static int vtable_property_get_userdata(
88 struct vtable_member *p,
90 sd_bus_error *error) {
100 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
103 if (bus->nodes_modified)
106 *userdata = vtable_property_convert_userdata(p->vtable, u);
110 static int add_enumerated_to_set(
113 struct node_enumerator *first,
115 sd_bus_error *error) {
117 struct node_enumerator *c;
124 LIST_FOREACH(enumerators, c, first) {
125 char **children = NULL, **k;
128 if (bus->nodes_modified)
131 slot = container_of(c, sd_bus_slot, node_enumerator);
133 bus->current_slot = sd_bus_slot_ref(slot);
134 bus->current_userdata = slot->userdata;
135 r = c->callback(bus, prefix, slot->userdata, &children, error);
136 bus->current_userdata = NULL;
137 bus->current_slot = sd_bus_slot_unref(slot);
141 if (sd_bus_error_is_set(error))
142 return -sd_bus_error_get_errno(error);
144 STRV_FOREACH(k, children) {
150 if (!object_path_is_valid(*k)) {
156 if (!object_path_startswith(*k, prefix)) {
161 r = set_consume(s, *k);
175 /* if set, add_subtree() works recursively */
176 CHILDREN_RECURSIVE = (1U << 1),
177 /* if set, add_subtree() scans object-manager hierarchies recursively */
178 CHILDREN_SUBHIERARCHIES = (1U << 0),
181 static int add_subtree_to_set(
187 sd_bus_error *error) {
197 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
200 if (bus->nodes_modified)
203 LIST_FOREACH(siblings, i, n->child) {
206 if (!object_path_startswith(i->path, prefix))
213 r = set_consume(s, t);
214 if (r < 0 && r != -EEXIST)
217 if ((flags & CHILDREN_RECURSIVE) &&
218 ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) {
219 r = add_subtree_to_set(bus, prefix, i, flags, s, error);
222 if (bus->nodes_modified)
230 static int get_child_nodes(
236 sd_bus_error *error) {
246 s = set_new(&string_hash_ops);
250 r = add_subtree_to_set(bus, prefix, n, flags, s, error);
260 static int node_callbacks_run(
263 struct node_callback *first,
264 bool require_fallback,
265 bool *found_object) {
267 struct node_callback *c;
272 assert(found_object);
274 LIST_FOREACH(callbacks, c, first) {
275 _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
278 if (bus->nodes_modified)
281 if (require_fallback && !c->is_fallback)
284 *found_object = true;
286 if (c->last_iteration == bus->iteration_counter)
289 c->last_iteration = bus->iteration_counter;
291 r = sd_bus_message_rewind(m, true);
295 slot = container_of(c, sd_bus_slot, node_callback);
297 bus->current_slot = sd_bus_slot_ref(slot);
298 bus->current_handler = c->callback;
299 bus->current_userdata = slot->userdata;
300 r = c->callback(m, slot->userdata, &error_buffer);
301 bus->current_userdata = NULL;
302 bus->current_handler = NULL;
303 bus->current_slot = sd_bus_slot_unref(slot);
305 r = bus_maybe_reply_error(m, r, &error_buffer);
313 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
315 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
323 /* If the entire bus is trusted let's grant access */
327 /* If the member is marked UNPRIVILEGED let's grant access */
328 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
331 /* Check have the caller has the requested capability
332 * set. Note that the flags value contains the capability
333 * number plus one, which we need to subtract here. We do this
334 * so that we have 0 as special value for "default
336 cap = CAPABILITY_SHIFT(c->vtable->flags);
338 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
344 r = sd_bus_query_sender_privilege(m, cap);
350 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
353 static int method_callbacks_run(
356 struct vtable_member *c,
357 bool require_fallback,
358 bool *found_object) {
360 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
361 const char *signature;
368 assert(found_object);
370 if (require_fallback && !c->parent->is_fallback)
373 r = check_access(bus, m, c, &error);
375 return bus_maybe_reply_error(m, r, &error);
377 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
379 return bus_maybe_reply_error(m, r, &error);
380 if (bus->nodes_modified)
383 u = vtable_method_convert_userdata(c->vtable, u);
385 *found_object = true;
387 if (c->last_iteration == bus->iteration_counter)
390 c->last_iteration = bus->iteration_counter;
392 r = sd_bus_message_rewind(m, true);
396 signature = sd_bus_message_get_signature(m, true);
400 if (!streq(strempty(c->vtable->x.method.signature), signature))
401 return sd_bus_reply_method_errorf(
403 SD_BUS_ERROR_INVALID_ARGS,
404 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
405 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
407 /* Keep track what the signature of the reply to this message
408 * should be, so that this can be enforced when sealing the
410 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
412 if (c->vtable->x.method.handler) {
415 slot = container_of(c->parent, sd_bus_slot, node_vtable);
417 bus->current_slot = sd_bus_slot_ref(slot);
418 bus->current_handler = c->vtable->x.method.handler;
419 bus->current_userdata = u;
420 r = c->vtable->x.method.handler(m, u, &error);
421 bus->current_userdata = NULL;
422 bus->current_handler = NULL;
423 bus->current_slot = sd_bus_slot_unref(slot);
425 return bus_maybe_reply_error(m, r, &error);
428 /* If the method callback is NULL, make this a successful NOP */
429 r = sd_bus_reply_method_return(m, NULL);
436 static int invoke_property_get(
439 const sd_bus_vtable *v,
441 const char *interface,
442 const char *property,
443 sd_bus_message *reply,
445 sd_bus_error *error) {
458 if (v->x.property.get) {
460 bus->current_slot = sd_bus_slot_ref(slot);
461 bus->current_userdata = userdata;
462 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
463 bus->current_userdata = NULL;
464 bus->current_slot = sd_bus_slot_unref(slot);
468 if (sd_bus_error_is_set(error))
469 return -sd_bus_error_get_errno(error);
473 /* Automatic handling if no callback is defined. */
475 if (streq(v->x.property.signature, "as"))
476 return sd_bus_message_append_strv(reply, *(char***) userdata);
478 assert(signature_is_single(v->x.property.signature, false));
479 assert(bus_type_is_basic(v->x.property.signature[0]));
481 switch (v->x.property.signature[0]) {
483 case SD_BUS_TYPE_STRING:
484 case SD_BUS_TYPE_SIGNATURE:
485 p = strempty(*(char**) userdata);
488 case SD_BUS_TYPE_OBJECT_PATH:
489 p = *(char**) userdata;
498 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
501 static int invoke_property_set(
504 const sd_bus_vtable *v,
506 const char *interface,
507 const char *property,
508 sd_bus_message *value,
510 sd_bus_error *error) {
522 if (v->x.property.set) {
524 bus->current_slot = sd_bus_slot_ref(slot);
525 bus->current_userdata = userdata;
526 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
527 bus->current_userdata = NULL;
528 bus->current_slot = sd_bus_slot_unref(slot);
532 if (sd_bus_error_is_set(error))
533 return -sd_bus_error_get_errno(error);
537 /* Automatic handling if no callback is defined. */
539 assert(signature_is_single(v->x.property.signature, false));
540 assert(bus_type_is_basic(v->x.property.signature[0]));
542 switch (v->x.property.signature[0]) {
544 case SD_BUS_TYPE_STRING:
545 case SD_BUS_TYPE_OBJECT_PATH:
546 case SD_BUS_TYPE_SIGNATURE: {
550 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
558 free(*(char**) userdata);
559 *(char**) userdata = n;
565 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
575 static int property_get_set_callbacks_run(
578 struct vtable_member *c,
579 bool require_fallback,
581 bool *found_object) {
583 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
584 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
592 assert(found_object);
594 if (require_fallback && !c->parent->is_fallback)
597 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
599 return bus_maybe_reply_error(m, r, &error);
600 if (bus->nodes_modified)
603 slot = container_of(c->parent, sd_bus_slot, node_vtable);
605 *found_object = true;
607 r = sd_bus_message_new_method_return(m, &reply);
612 /* Note that we do not protect against reexecution
613 * here (using the last_iteration check, see below),
614 * should the node tree have changed and we got called
615 * again. We assume that property Get() calls are
616 * ultimately without side-effects or if they aren't
617 * then at least idempotent. */
619 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
623 /* Note that we do not do an access check here. Read
624 * access to properties is always unrestricted, since
625 * PropertiesChanged signals broadcast contents
628 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
630 return bus_maybe_reply_error(m, r, &error);
632 if (bus->nodes_modified)
635 r = sd_bus_message_close_container(reply);
640 const char *signature = NULL;
643 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
644 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
646 /* Avoid that we call the set routine more than once
647 * if the processing of this message got restarted
648 * because the node tree changed. */
649 if (c->last_iteration == bus->iteration_counter)
652 c->last_iteration = bus->iteration_counter;
654 r = sd_bus_message_peek_type(m, &type, &signature);
658 if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
659 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature));
661 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
665 r = check_access(bus, m, c, &error);
667 return bus_maybe_reply_error(m, r, &error);
669 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
671 return bus_maybe_reply_error(m, r, &error);
673 if (bus->nodes_modified)
676 r = sd_bus_message_exit_container(m);
681 r = sd_bus_send(bus, reply, NULL);
688 static int vtable_append_one_property(
690 sd_bus_message *reply,
692 struct node_vtable *c,
693 const sd_bus_vtable *v,
695 sd_bus_error *error) {
706 r = sd_bus_message_open_container(reply, 'e', "sv");
710 r = sd_bus_message_append(reply, "s", v->x.property.member);
714 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
718 slot = container_of(c, sd_bus_slot, node_vtable);
720 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
723 if (bus->nodes_modified)
726 r = sd_bus_message_close_container(reply);
730 r = sd_bus_message_close_container(reply);
737 static int vtable_append_all_properties(
739 sd_bus_message *reply,
741 struct node_vtable *c,
743 sd_bus_error *error) {
745 const sd_bus_vtable *v;
753 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
756 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
757 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
760 if (v->flags & SD_BUS_VTABLE_HIDDEN)
763 if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
766 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
769 if (bus->nodes_modified)
776 static int property_get_all_callbacks_run(
779 struct node_vtable *first,
780 bool require_fallback,
782 bool *found_object) {
784 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
785 struct node_vtable *c;
786 bool found_interface;
791 assert(found_object);
793 r = sd_bus_message_new_method_return(m, &reply);
797 r = sd_bus_message_open_container(reply, 'a', "{sv}");
801 found_interface = !iface ||
802 streq(iface, "org.freedesktop.DBus.Properties") ||
803 streq(iface, "org.freedesktop.DBus.Peer") ||
804 streq(iface, "org.freedesktop.DBus.Introspectable");
806 LIST_FOREACH(vtables, c, first) {
807 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
810 if (require_fallback && !c->is_fallback)
813 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
815 return bus_maybe_reply_error(m, r, &error);
816 if (bus->nodes_modified)
821 *found_object = true;
823 if (iface && !streq(c->interface, iface))
825 found_interface = true;
827 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
829 return bus_maybe_reply_error(m, r, &error);
830 if (bus->nodes_modified)
837 if (!found_interface) {
838 r = sd_bus_reply_method_errorf(
840 SD_BUS_ERROR_UNKNOWN_INTERFACE,
841 "Unknown interface '%s'.", iface);
848 r = sd_bus_message_close_container(reply);
852 r = sd_bus_send(bus, reply, NULL);
859 static int bus_node_exists(
863 bool require_fallback) {
865 struct node_vtable *c;
866 struct node_callback *k;
873 /* Tests if there's anything attached directly to this node
874 * for the specified path */
876 if (!require_fallback && (n->enumerators || n->object_managers))
879 LIST_FOREACH(callbacks, k, n->callbacks) {
880 if (require_fallback && !k->is_fallback)
886 LIST_FOREACH(vtables, c, n->vtables) {
887 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
889 if (require_fallback && !c->is_fallback)
892 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
895 if (bus->nodes_modified)
902 static int process_introspect(
906 bool require_fallback,
907 bool *found_object) {
909 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
910 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
911 _cleanup_set_free_free_ Set *s = NULL;
912 const char *previous_interface = NULL;
913 struct introspect intro;
914 struct node_vtable *c;
921 assert(found_object);
923 r = get_child_nodes(bus, m->path, n, 0, &s, &error);
925 return bus_maybe_reply_error(m, r, &error);
926 if (bus->nodes_modified)
929 r = introspect_begin(&intro, bus->trusted);
933 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
937 empty = set_isempty(s);
939 LIST_FOREACH(vtables, c, n->vtables) {
940 if (require_fallback && !c->is_fallback)
943 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
945 r = bus_maybe_reply_error(m, r, &error);
948 if (bus->nodes_modified) {
957 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
960 if (!streq_ptr(previous_interface, c->interface)) {
962 if (previous_interface)
963 fputs(" </interface>\n", intro.f);
965 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
968 r = introspect_write_interface(&intro, c->vtable);
972 previous_interface = c->interface;
975 if (previous_interface)
976 fputs(" </interface>\n", intro.f);
979 /* Nothing?, let's see if we exist at all, and if not
980 * refuse to do anything */
981 r = bus_node_exists(bus, n, m->path, require_fallback);
983 r = bus_maybe_reply_error(m, r, &error);
986 if (bus->nodes_modified) {
992 *found_object = true;
994 r = introspect_write_child_nodes(&intro, s, m->path);
998 r = introspect_finish(&intro, bus, m, &reply);
1002 r = sd_bus_send(bus, reply, NULL);
1009 introspect_free(&intro);
1013 static int object_manager_serialize_path(
1015 sd_bus_message *reply,
1018 bool require_fallback,
1019 sd_bus_error *error) {
1021 const char *previous_interface = NULL;
1022 bool found_something = false;
1023 struct node_vtable *i;
1033 n = hashmap_get(bus->nodes, prefix);
1037 LIST_FOREACH(vtables, i, n->vtables) {
1040 if (require_fallback && !i->is_fallback)
1043 r = node_vtable_get_userdata(bus, path, i, &u, error);
1046 if (bus->nodes_modified)
1051 if (!found_something) {
1053 /* Open the object part */
1055 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1059 r = sd_bus_message_append(reply, "o", path);
1063 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1067 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
1071 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
1075 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
1079 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1083 found_something = true;
1086 if (!streq_ptr(previous_interface, i->interface)) {
1088 /* Maybe close the previous interface part */
1090 if (previous_interface) {
1091 r = sd_bus_message_close_container(reply);
1095 r = sd_bus_message_close_container(reply);
1100 /* Open the new interface part */
1102 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1106 r = sd_bus_message_append(reply, "s", i->interface);
1110 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1115 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1118 if (bus->nodes_modified)
1121 previous_interface = i->interface;
1124 if (previous_interface) {
1125 r = sd_bus_message_close_container(reply);
1129 r = sd_bus_message_close_container(reply);
1134 if (found_something) {
1135 r = sd_bus_message_close_container(reply);
1139 r = sd_bus_message_close_container(reply);
1147 static int object_manager_serialize_path_and_fallbacks(
1149 sd_bus_message *reply,
1151 sd_bus_error *error) {
1161 /* First, add all vtables registered for this path */
1162 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1165 if (bus->nodes_modified)
1168 /* Second, add fallback vtables registered for any of the prefixes */
1169 prefix = alloca(strlen(path) + 1);
1170 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1171 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1174 if (bus->nodes_modified)
1181 static int process_get_managed_objects(
1185 bool require_fallback,
1186 bool *found_object) {
1188 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1189 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1190 _cleanup_set_free_free_ Set *s = NULL;
1198 assert(found_object);
1200 /* Spec says, GetManagedObjects() is only implemented on the root of a
1201 * sub-tree. Therefore, we require a registered object-manager on
1202 * exactly the queried path, otherwise, we refuse to respond. */
1204 if (require_fallback || !n->object_managers)
1207 r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
1209 return bus_maybe_reply_error(m, r, &error);
1210 if (bus->nodes_modified)
1213 r = sd_bus_message_new_method_return(m, &reply);
1217 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1221 SET_FOREACH(path, s, i) {
1222 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1224 return bus_maybe_reply_error(m, r, &error);
1226 if (bus->nodes_modified)
1230 r = sd_bus_message_close_container(reply);
1234 r = sd_bus_send(bus, reply, NULL);
1241 static int object_find_and_run(
1245 bool require_fallback,
1246 bool *found_object) {
1249 struct vtable_member vtable_key, *v;
1255 assert(found_object);
1257 n = hashmap_get(bus->nodes, p);
1261 /* First, try object callbacks */
1262 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1265 if (bus->nodes_modified)
1268 if (!m->interface || !m->member)
1271 /* Then, look for a known method */
1272 vtable_key.path = (char*) p;
1273 vtable_key.interface = m->interface;
1274 vtable_key.member = m->member;
1276 v = hashmap_get(bus->vtable_methods, &vtable_key);
1278 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1281 if (bus->nodes_modified)
1285 /* Then, look for a known property */
1286 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1289 get = streq(m->member, "Get");
1291 if (get || streq(m->member, "Set")) {
1293 r = sd_bus_message_rewind(m, true);
1297 vtable_key.path = (char*) p;
1299 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1301 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1303 v = hashmap_get(bus->vtable_properties, &vtable_key);
1305 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1310 } else if (streq(m->member, "GetAll")) {
1313 r = sd_bus_message_rewind(m, true);
1317 r = sd_bus_message_read(m, "s", &iface);
1319 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1324 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1329 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1331 if (!isempty(sd_bus_message_get_signature(m, true)))
1332 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1334 r = process_introspect(bus, m, n, require_fallback, found_object);
1338 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1340 if (!isempty(sd_bus_message_get_signature(m, true)))
1341 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1343 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1348 if (bus->nodes_modified)
1351 if (!*found_object) {
1352 r = bus_node_exists(bus, n, m->path, require_fallback);
1354 return bus_maybe_reply_error(m, r, NULL);
1355 if (bus->nodes_modified)
1358 *found_object = true;
1364 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1367 bool found_object = false;
1372 if (bus->is_monitor)
1375 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1378 if (hashmap_isempty(bus->nodes))
1381 /* Never respond to broadcast messages */
1382 if (bus->bus_client && !m->destination)
1388 pl = strlen(m->path);
1392 bus->nodes_modified = false;
1394 r = object_find_and_run(bus, m, m->path, false, &found_object);
1398 /* Look for fallback prefixes */
1399 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1401 if (bus->nodes_modified)
1404 r = object_find_and_run(bus, m, prefix, true, &found_object);
1409 } while (bus->nodes_modified);
1414 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1415 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1416 r = sd_bus_reply_method_errorf(
1418 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1419 "Unknown property or interface.");
1421 r = sd_bus_reply_method_errorf(
1423 SD_BUS_ERROR_UNKNOWN_METHOD,
1424 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1432 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1433 struct node *n, *parent;
1435 _cleanup_free_ char *s = NULL;
1441 assert(path[0] == '/');
1443 n = hashmap_get(bus->nodes, path);
1447 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1455 if (streq(path, "/"))
1458 e = strrchr(path, '/');
1461 p = strndupa(path, MAX(1, e - path));
1463 parent = bus_node_allocate(bus, p);
1468 n = new0(struct node, 1);
1474 s = NULL; /* do not free */
1476 r = hashmap_put(bus->nodes, n->path, n);
1483 LIST_PREPEND(siblings, parent->child, n);
1488 void bus_node_gc(sd_bus *b, struct node *n) {
1501 assert_se(hashmap_remove(b->nodes, n->path) == n);
1504 LIST_REMOVE(siblings, n->parent->child, n);
1507 bus_node_gc(b, n->parent);
1511 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1517 n = hashmap_get(bus->nodes, path);
1521 prefix = alloca(strlen(path) + 1);
1522 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1523 n = hashmap_get(bus->nodes, prefix);
1529 while (n && !n->object_managers)
1537 static int bus_add_object(
1542 sd_bus_message_handler_t callback,
1549 assert_return(bus, -EINVAL);
1550 assert_return(bus = bus_resolve(bus), -ENOPKG);
1551 assert_return(object_path_is_valid(path), -EINVAL);
1552 assert_return(callback, -EINVAL);
1553 assert_return(!bus_pid_changed(bus), -ECHILD);
1555 n = bus_node_allocate(bus, path);
1559 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1565 s->node_callback.callback = callback;
1566 s->node_callback.is_fallback = fallback;
1568 s->node_callback.node = n;
1569 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1570 bus->nodes_modified = true;
1578 sd_bus_slot_unref(s);
1579 bus_node_gc(bus, n);
1584 _public_ int sd_bus_add_object(
1588 sd_bus_message_handler_t callback,
1591 return bus_add_object(bus, slot, false, path, callback, userdata);
1594 _public_ int sd_bus_add_fallback(
1598 sd_bus_message_handler_t callback,
1601 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1604 static void vtable_member_hash_func(const void *a, struct siphash *state) {
1605 const struct vtable_member *m = a;
1609 string_hash_func(m->path, state);
1610 string_hash_func(m->interface, state);
1611 string_hash_func(m->member, state);
1614 static int vtable_member_compare_func(const void *a, const void *b) {
1615 const struct vtable_member *x = a, *y = b;
1621 r = strcmp(x->path, y->path);
1625 r = strcmp(x->interface, y->interface);
1629 return strcmp(x->member, y->member);
1632 static const struct hash_ops vtable_member_hash_ops = {
1633 .hash = vtable_member_hash_func,
1634 .compare = vtable_member_compare_func
1637 static int add_object_vtable_internal(
1641 const char *interface,
1642 const sd_bus_vtable *vtable,
1644 sd_bus_object_find_t find,
1647 sd_bus_slot *s = NULL;
1648 struct node_vtable *i, *existing = NULL;
1649 const sd_bus_vtable *v;
1653 assert_return(bus, -EINVAL);
1654 assert_return(bus = bus_resolve(bus), -ENOPKG);
1655 assert_return(object_path_is_valid(path), -EINVAL);
1656 assert_return(interface_name_is_valid(interface), -EINVAL);
1657 assert_return(vtable, -EINVAL);
1658 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1659 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1660 assert_return(!bus_pid_changed(bus), -ECHILD);
1661 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1662 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1663 !streq(interface, "org.freedesktop.DBus.Peer") &&
1664 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1666 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1670 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1674 n = bus_node_allocate(bus, path);
1678 LIST_FOREACH(vtables, i, n->vtables) {
1679 if (i->is_fallback != fallback) {
1684 if (streq(i->interface, interface)) {
1686 if (i->vtable == vtable) {
1695 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1701 s->node_vtable.is_fallback = fallback;
1702 s->node_vtable.vtable = vtable;
1703 s->node_vtable.find = find;
1705 s->node_vtable.interface = strdup(interface);
1706 if (!s->node_vtable.interface) {
1711 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1715 case _SD_BUS_VTABLE_METHOD: {
1716 struct vtable_member *m;
1718 if (!member_name_is_valid(v->x.method.member) ||
1719 !signature_is_valid(strempty(v->x.method.signature), false) ||
1720 !signature_is_valid(strempty(v->x.method.result), false) ||
1721 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1722 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1727 m = new0(struct vtable_member, 1);
1733 m->parent = &s->node_vtable;
1735 m->interface = s->node_vtable.interface;
1736 m->member = v->x.method.member;
1739 r = hashmap_put(bus->vtable_methods, m, m);
1748 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1750 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1755 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1761 case _SD_BUS_VTABLE_PROPERTY: {
1762 struct vtable_member *m;
1764 if (!member_name_is_valid(v->x.property.member) ||
1765 !signature_is_single(v->x.property.signature, false) ||
1766 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1767 (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1768 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1769 ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1770 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1775 m = new0(struct vtable_member, 1);
1781 m->parent = &s->node_vtable;
1783 m->interface = s->node_vtable.interface;
1784 m->member = v->x.property.member;
1787 r = hashmap_put(bus->vtable_properties, m, m);
1796 case _SD_BUS_VTABLE_SIGNAL:
1798 if (!member_name_is_valid(v->x.signal.member) ||
1799 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1800 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1813 s->node_vtable.node = n;
1814 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1815 bus->nodes_modified = true;
1823 sd_bus_slot_unref(s);
1824 bus_node_gc(bus, n);
1829 _public_ int sd_bus_add_object_vtable(
1833 const char *interface,
1834 const sd_bus_vtable *vtable,
1837 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1840 _public_ int sd_bus_add_fallback_vtable(
1844 const char *interface,
1845 const sd_bus_vtable *vtable,
1846 sd_bus_object_find_t find,
1849 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1852 _public_ int sd_bus_add_node_enumerator(
1856 sd_bus_node_enumerator_t callback,
1863 assert_return(bus, -EINVAL);
1864 assert_return(bus = bus_resolve(bus), -ENOPKG);
1865 assert_return(object_path_is_valid(path), -EINVAL);
1866 assert_return(callback, -EINVAL);
1867 assert_return(!bus_pid_changed(bus), -ECHILD);
1869 n = bus_node_allocate(bus, path);
1873 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1879 s->node_enumerator.callback = callback;
1881 s->node_enumerator.node = n;
1882 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1883 bus->nodes_modified = true;
1891 sd_bus_slot_unref(s);
1892 bus_node_gc(bus, n);
1897 static int emit_properties_changed_on_interface(
1901 const char *interface,
1902 bool require_fallback,
1903 bool *found_interface,
1906 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1907 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1908 bool has_invalidating = false, has_changing = false;
1909 struct vtable_member key = {};
1910 struct node_vtable *c;
1920 assert(found_interface);
1922 n = hashmap_get(bus->nodes, prefix);
1926 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1930 r = sd_bus_message_append(m, "s", interface);
1934 r = sd_bus_message_open_container(m, 'a', "{sv}");
1939 key.interface = interface;
1941 LIST_FOREACH(vtables, c, n->vtables) {
1942 if (require_fallback && !c->is_fallback)
1945 if (!streq(c->interface, interface))
1948 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1951 if (bus->nodes_modified)
1956 *found_interface = true;
1959 /* If the caller specified a list of
1960 * properties we include exactly those in the
1961 * PropertiesChanged message */
1963 STRV_FOREACH(property, names) {
1964 struct vtable_member *v;
1966 assert_return(member_name_is_valid(*property), -EINVAL);
1968 key.member = *property;
1969 v = hashmap_get(bus->vtable_properties, &key);
1973 /* If there are two vtables for the same
1974 * interface, let's handle this property when
1975 * we come to that vtable. */
1979 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1980 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1982 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1984 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1985 has_invalidating = true;
1989 has_changing = true;
1991 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1994 if (bus->nodes_modified)
1998 const sd_bus_vtable *v;
2000 /* If the caller specified no properties list
2001 * we include all properties that are marked
2002 * as changing in the message. */
2004 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2005 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
2008 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2011 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2012 has_invalidating = true;
2016 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2019 has_changing = true;
2021 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2024 if (bus->nodes_modified)
2030 if (!has_invalidating && !has_changing)
2033 r = sd_bus_message_close_container(m);
2037 r = sd_bus_message_open_container(m, 'a', "s");
2041 if (has_invalidating) {
2042 LIST_FOREACH(vtables, c, n->vtables) {
2043 if (require_fallback && !c->is_fallback)
2046 if (!streq(c->interface, interface))
2049 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2052 if (bus->nodes_modified)
2058 STRV_FOREACH(property, names) {
2059 struct vtable_member *v;
2061 key.member = *property;
2062 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2063 assert(c == v->parent);
2065 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2068 r = sd_bus_message_append(m, "s", *property);
2073 const sd_bus_vtable *v;
2075 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2076 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
2079 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2082 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2085 r = sd_bus_message_append(m, "s", v->x.property.member);
2093 r = sd_bus_message_close_container(m);
2097 r = sd_bus_send(bus, m, NULL);
2104 _public_ int sd_bus_emit_properties_changed_strv(
2107 const char *interface,
2110 BUS_DONT_DESTROY(bus);
2111 bool found_interface = false;
2115 assert_return(bus, -EINVAL);
2116 assert_return(bus = bus_resolve(bus), -ENOPKG);
2117 assert_return(object_path_is_valid(path), -EINVAL);
2118 assert_return(interface_name_is_valid(interface), -EINVAL);
2119 assert_return(!bus_pid_changed(bus), -ECHILD);
2121 if (!BUS_IS_OPEN(bus->state))
2124 /* A non-NULL but empty names list means nothing needs to be
2125 generated. A NULL list OTOH indicates that all properties
2126 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2127 included in the PropertiesChanged message. */
2128 if (names && names[0] == NULL)
2132 bus->nodes_modified = false;
2134 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2137 if (bus->nodes_modified)
2140 prefix = alloca(strlen(path) + 1);
2141 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2142 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2145 if (bus->nodes_modified)
2149 } while (bus->nodes_modified);
2151 return found_interface ? 0 : -ENOENT;
2154 _public_ int sd_bus_emit_properties_changed(
2157 const char *interface,
2158 const char *name, ...) {
2162 assert_return(bus, -EINVAL);
2163 assert_return(bus = bus_resolve(bus), -ENOPKG);
2164 assert_return(object_path_is_valid(path), -EINVAL);
2165 assert_return(interface_name_is_valid(interface), -EINVAL);
2166 assert_return(!bus_pid_changed(bus), -ECHILD);
2168 if (!BUS_IS_OPEN(bus->state))
2174 names = strv_from_stdarg_alloca(name);
2176 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2179 static int object_added_append_all_prefix(
2185 bool require_fallback) {
2187 const char *previous_interface = NULL;
2188 struct node_vtable *c;
2198 n = hashmap_get(bus->nodes, prefix);
2202 LIST_FOREACH(vtables, c, n->vtables) {
2203 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2206 if (require_fallback && !c->is_fallback)
2209 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2212 if (bus->nodes_modified)
2217 if (!streq_ptr(c->interface, previous_interface)) {
2218 /* If a child-node already handled this interface, we
2219 * skip it on any of its parents. The child vtables
2220 * always fully override any conflicting vtables of
2221 * any parent node. */
2222 if (set_get(s, c->interface))
2225 r = set_put(s, c->interface);
2229 if (previous_interface) {
2230 r = sd_bus_message_close_container(m);
2233 r = sd_bus_message_close_container(m);
2238 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2241 r = sd_bus_message_append(m, "s", c->interface);
2244 r = sd_bus_message_open_container(m, 'a', "{sv}");
2248 previous_interface = c->interface;
2251 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2254 if (bus->nodes_modified)
2258 if (previous_interface) {
2259 r = sd_bus_message_close_container(m);
2262 r = sd_bus_message_close_container(m);
2270 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2271 _cleanup_set_free_ Set *s = NULL;
2280 * This appends all interfaces registered on path @path. We first add
2281 * the builtin interfaces, which are always available and handled by
2282 * sd-bus. Then, we add all interfaces registered on the exact node,
2283 * followed by all fallback interfaces registered on any parent prefix.
2285 * If an interface is registered multiple times on the same node with
2286 * different vtables, we merge all the properties across all vtables.
2287 * However, if a child node has the same interface registered as one of
2288 * its parent nodes has as fallback, we make the child overwrite the
2289 * parent instead of extending it. Therefore, we keep a "Set" of all
2290 * handled interfaces during parent traversal, so we skip interfaces on
2291 * a parent that were overwritten by a child.
2294 s = set_new(&string_hash_ops);
2298 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2301 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2304 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2307 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2311 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2314 if (bus->nodes_modified)
2317 prefix = alloca(strlen(path) + 1);
2318 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2319 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2322 if (bus->nodes_modified)
2329 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2330 BUS_DONT_DESTROY(bus);
2332 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2333 struct node *object_manager;
2337 * This emits an InterfacesAdded signal on the given path, by iterating
2338 * all registered vtables and fallback vtables on the path. All
2339 * properties are queried and included in the signal.
2340 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2341 * explicit list of registered interfaces. However, unlike
2342 * interfaces_added(), this call can figure out the list of supported
2343 * interfaces itself. Furthermore, it properly adds the builtin
2344 * org.freedesktop.DBus.* interfaces.
2347 assert_return(bus, -EINVAL);
2348 assert_return(bus = bus_resolve(bus), -ENOPKG);
2349 assert_return(object_path_is_valid(path), -EINVAL);
2350 assert_return(!bus_pid_changed(bus), -ECHILD);
2352 if (!BUS_IS_OPEN(bus->state))
2355 r = bus_find_parent_object_manager(bus, &object_manager, path);
2362 bus->nodes_modified = false;
2363 m = sd_bus_message_unref(m);
2365 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2369 r = sd_bus_message_append_basic(m, 'o', path);
2373 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2377 r = object_added_append_all(bus, m, path);
2381 if (bus->nodes_modified)
2384 r = sd_bus_message_close_container(m);
2388 } while (bus->nodes_modified);
2390 return sd_bus_send(bus, m, NULL);
2393 static int object_removed_append_all_prefix(
2399 bool require_fallback) {
2401 const char *previous_interface = NULL;
2402 struct node_vtable *c;
2412 n = hashmap_get(bus->nodes, prefix);
2416 LIST_FOREACH(vtables, c, n->vtables) {
2417 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2420 if (require_fallback && !c->is_fallback)
2422 if (streq_ptr(c->interface, previous_interface))
2425 /* If a child-node already handled this interface, we
2426 * skip it on any of its parents. The child vtables
2427 * always fully override any conflicting vtables of
2428 * any parent node. */
2429 if (set_get(s, c->interface))
2432 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2435 if (bus->nodes_modified)
2440 r = set_put(s, c->interface);
2444 r = sd_bus_message_append(m, "s", c->interface);
2448 previous_interface = c->interface;
2454 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2455 _cleanup_set_free_ Set *s = NULL;
2463 /* see sd_bus_emit_object_added() for details */
2465 s = set_new(&string_hash_ops);
2469 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2472 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2475 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2478 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2482 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2485 if (bus->nodes_modified)
2488 prefix = alloca(strlen(path) + 1);
2489 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2490 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2493 if (bus->nodes_modified)
2500 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2501 BUS_DONT_DESTROY(bus);
2503 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2504 struct node *object_manager;
2508 * This is like sd_bus_emit_object_added(), but emits an
2509 * InterfacesRemoved signal on the given path. This only includes any
2510 * registered interfaces but skips the properties. Note that this will
2511 * call into the find() callbacks of any registered vtable. Therefore,
2512 * you must call this function before destroying/unlinking your object.
2513 * Otherwise, the list of interfaces will be incomplete. However, note
2514 * that this will *NOT* call into any property callback. Therefore, the
2515 * object might be in an "destructed" state, as long as we can find it.
2518 assert_return(bus, -EINVAL);
2519 assert_return(bus = bus_resolve(bus), -ENOPKG);
2520 assert_return(object_path_is_valid(path), -EINVAL);
2521 assert_return(!bus_pid_changed(bus), -ECHILD);
2523 if (!BUS_IS_OPEN(bus->state))
2526 r = bus_find_parent_object_manager(bus, &object_manager, path);
2533 bus->nodes_modified = false;
2534 m = sd_bus_message_unref(m);
2536 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2540 r = sd_bus_message_append_basic(m, 'o', path);
2544 r = sd_bus_message_open_container(m, 'a', "s");
2548 r = object_removed_append_all(bus, m, path);
2552 if (bus->nodes_modified)
2555 r = sd_bus_message_close_container(m);
2559 } while (bus->nodes_modified);
2561 return sd_bus_send(bus, m, NULL);
2564 static int interfaces_added_append_one_prefix(
2569 const char *interface,
2570 bool require_fallback) {
2572 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2573 bool found_interface = false;
2574 struct node_vtable *c;
2585 n = hashmap_get(bus->nodes, prefix);
2589 LIST_FOREACH(vtables, c, n->vtables) {
2590 if (require_fallback && !c->is_fallback)
2593 if (!streq(c->interface, interface))
2596 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2599 if (bus->nodes_modified)
2604 if (!found_interface) {
2605 r = sd_bus_message_append_basic(m, 's', interface);
2609 r = sd_bus_message_open_container(m, 'a', "{sv}");
2613 found_interface = true;
2616 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2619 if (bus->nodes_modified)
2623 if (found_interface) {
2624 r = sd_bus_message_close_container(m);
2629 return found_interface;
2632 static int interfaces_added_append_one(
2636 const char *interface) {
2646 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2649 if (bus->nodes_modified)
2652 prefix = alloca(strlen(path) + 1);
2653 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2654 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2657 if (bus->nodes_modified)
2664 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2665 BUS_DONT_DESTROY(bus);
2667 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2668 struct node *object_manager;
2672 assert_return(bus, -EINVAL);
2673 assert_return(bus = bus_resolve(bus), -ENOPKG);
2674 assert_return(object_path_is_valid(path), -EINVAL);
2675 assert_return(!bus_pid_changed(bus), -ECHILD);
2677 if (!BUS_IS_OPEN(bus->state))
2680 if (strv_isempty(interfaces))
2683 r = bus_find_parent_object_manager(bus, &object_manager, path);
2690 bus->nodes_modified = false;
2691 m = sd_bus_message_unref(m);
2693 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2697 r = sd_bus_message_append_basic(m, 'o', path);
2701 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2705 STRV_FOREACH(i, interfaces) {
2706 assert_return(interface_name_is_valid(*i), -EINVAL);
2708 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2712 r = interfaces_added_append_one(bus, m, path, *i);
2716 if (bus->nodes_modified)
2719 r = sd_bus_message_close_container(m);
2724 if (bus->nodes_modified)
2727 r = sd_bus_message_close_container(m);
2731 } while (bus->nodes_modified);
2733 return sd_bus_send(bus, m, NULL);
2736 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2739 assert_return(bus, -EINVAL);
2740 assert_return(bus = bus_resolve(bus), -ENOPKG);
2741 assert_return(object_path_is_valid(path), -EINVAL);
2742 assert_return(!bus_pid_changed(bus), -ECHILD);
2744 if (!BUS_IS_OPEN(bus->state))
2747 interfaces = strv_from_stdarg_alloca(interface);
2749 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2752 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2753 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2754 struct node *object_manager;
2757 assert_return(bus, -EINVAL);
2758 assert_return(bus = bus_resolve(bus), -ENOPKG);
2759 assert_return(object_path_is_valid(path), -EINVAL);
2760 assert_return(!bus_pid_changed(bus), -ECHILD);
2762 if (!BUS_IS_OPEN(bus->state))
2765 if (strv_isempty(interfaces))
2768 r = bus_find_parent_object_manager(bus, &object_manager, path);
2774 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2778 r = sd_bus_message_append_basic(m, 'o', path);
2782 r = sd_bus_message_append_strv(m, interfaces);
2786 return sd_bus_send(bus, m, NULL);
2789 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2792 assert_return(bus, -EINVAL);
2793 assert_return(bus = bus_resolve(bus), -ENOPKG);
2794 assert_return(object_path_is_valid(path), -EINVAL);
2795 assert_return(!bus_pid_changed(bus), -ECHILD);
2797 if (!BUS_IS_OPEN(bus->state))
2800 interfaces = strv_from_stdarg_alloca(interface);
2802 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2805 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2810 assert_return(bus, -EINVAL);
2811 assert_return(bus = bus_resolve(bus), -ENOPKG);
2812 assert_return(object_path_is_valid(path), -EINVAL);
2813 assert_return(!bus_pid_changed(bus), -ECHILD);
2815 n = bus_node_allocate(bus, path);
2819 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2825 s->node_object_manager.node = n;
2826 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2827 bus->nodes_modified = true;
2835 sd_bus_slot_unref(s);
2836 bus_node_gc(bus, n);