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(
171 sd_bus_error *error) {
181 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
184 if (bus->nodes_modified)
187 LIST_FOREACH(siblings, i, n->child) {
190 if (!object_path_startswith(i->path, prefix))
197 r = set_consume(s, t);
198 if (r < 0 && r != -EEXIST)
201 r = add_subtree_to_set(bus, prefix, i, s, error);
204 if (bus->nodes_modified)
211 static int get_child_nodes(
216 sd_bus_error *error) {
226 s = set_new(&string_hash_ops);
230 r = add_subtree_to_set(bus, prefix, n, s, error);
240 static int node_callbacks_run(
243 struct node_callback *first,
244 bool require_fallback,
245 bool *found_object) {
247 struct node_callback *c;
252 assert(found_object);
254 LIST_FOREACH(callbacks, c, first) {
255 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
258 if (bus->nodes_modified)
261 if (require_fallback && !c->is_fallback)
264 *found_object = true;
266 if (c->last_iteration == bus->iteration_counter)
269 c->last_iteration = bus->iteration_counter;
271 r = sd_bus_message_rewind(m, true);
275 slot = container_of(c, sd_bus_slot, node_callback);
277 bus->current_slot = sd_bus_slot_ref(slot);
278 bus->current_handler = c->callback;
279 bus->current_userdata = slot->userdata;
280 r = c->callback(bus, m, slot->userdata, &error_buffer);
281 bus->current_userdata = NULL;
282 bus->current_handler = NULL;
283 bus->current_slot = sd_bus_slot_unref(slot);
285 r = bus_maybe_reply_error(m, r, &error_buffer);
293 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
295 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
303 /* If the entire bus is trusted let's grant access */
307 /* If the member is marked UNPRIVILEGED let's grant access */
308 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
311 /* Check have the caller has the requested capability
312 * set. Note that the flags value contains the capability
313 * number plus one, which we need to subtract here. We do this
314 * so that we have 0 as special value for "default
316 cap = CAPABILITY_SHIFT(c->vtable->flags);
318 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
324 r = sd_bus_query_sender_privilege(m, cap);
330 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
333 static int method_callbacks_run(
336 struct vtable_member *c,
337 bool require_fallback,
338 bool *found_object) {
340 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
341 const char *signature;
348 assert(found_object);
350 if (require_fallback && !c->parent->is_fallback)
353 r = check_access(bus, m, c, &error);
355 return bus_maybe_reply_error(m, r, &error);
357 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
359 return bus_maybe_reply_error(m, r, &error);
360 if (bus->nodes_modified)
363 *found_object = true;
365 if (c->last_iteration == bus->iteration_counter)
368 c->last_iteration = bus->iteration_counter;
370 r = sd_bus_message_rewind(m, true);
374 signature = sd_bus_message_get_signature(m, true);
378 if (!streq(strempty(c->vtable->x.method.signature), signature))
379 return sd_bus_reply_method_errorf(
381 SD_BUS_ERROR_INVALID_ARGS,
382 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
383 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
385 /* Keep track what the signature of the reply to this message
386 * should be, so that this can be enforced when sealing the
388 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
390 if (c->vtable->x.method.handler) {
393 slot = container_of(c->parent, sd_bus_slot, node_vtable);
395 bus->current_slot = sd_bus_slot_ref(slot);
396 bus->current_handler = c->vtable->x.method.handler;
397 bus->current_userdata = u;
398 r = c->vtable->x.method.handler(bus, m, u, &error);
399 bus->current_userdata = NULL;
400 bus->current_handler = NULL;
401 bus->current_slot = sd_bus_slot_unref(slot);
403 return bus_maybe_reply_error(m, r, &error);
406 /* If the method callback is NULL, make this a successful NOP */
407 r = sd_bus_reply_method_return(m, NULL);
414 static int invoke_property_get(
417 const sd_bus_vtable *v,
419 const char *interface,
420 const char *property,
421 sd_bus_message *reply,
423 sd_bus_error *error) {
436 if (v->x.property.get) {
438 bus->current_slot = sd_bus_slot_ref(slot);
439 bus->current_userdata = userdata;
440 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
441 bus->current_userdata = NULL;
442 bus->current_slot = sd_bus_slot_unref(slot);
446 if (sd_bus_error_is_set(error))
447 return -sd_bus_error_get_errno(error);
451 /* Automatic handling if no callback is defined. */
453 if (streq(v->x.property.signature, "as"))
454 return sd_bus_message_append_strv(reply, *(char***) userdata);
456 assert(signature_is_single(v->x.property.signature, false));
457 assert(bus_type_is_basic(v->x.property.signature[0]));
459 switch (v->x.property.signature[0]) {
461 case SD_BUS_TYPE_STRING:
462 case SD_BUS_TYPE_SIGNATURE:
463 p = strempty(*(char**) userdata);
466 case SD_BUS_TYPE_OBJECT_PATH:
467 p = *(char**) userdata;
476 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
479 static int invoke_property_set(
482 const sd_bus_vtable *v,
484 const char *interface,
485 const char *property,
486 sd_bus_message *value,
488 sd_bus_error *error) {
500 if (v->x.property.set) {
502 bus->current_slot = sd_bus_slot_ref(slot);
503 bus->current_userdata = userdata;
504 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
505 bus->current_userdata = NULL;
506 bus->current_slot = sd_bus_slot_unref(slot);
510 if (sd_bus_error_is_set(error))
511 return -sd_bus_error_get_errno(error);
515 /* Automatic handling if no callback is defined. */
517 assert(signature_is_single(v->x.property.signature, false));
518 assert(bus_type_is_basic(v->x.property.signature[0]));
520 switch (v->x.property.signature[0]) {
522 case SD_BUS_TYPE_STRING:
523 case SD_BUS_TYPE_OBJECT_PATH:
524 case SD_BUS_TYPE_SIGNATURE: {
528 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
536 free(*(char**) userdata);
537 *(char**) userdata = n;
543 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
553 static int property_get_set_callbacks_run(
556 struct vtable_member *c,
557 bool require_fallback,
559 bool *found_object) {
561 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
562 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
570 assert(found_object);
572 if (require_fallback && !c->parent->is_fallback)
575 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
577 return bus_maybe_reply_error(m, r, &error);
578 if (bus->nodes_modified)
581 slot = container_of(c->parent, sd_bus_slot, node_vtable);
583 *found_object = true;
585 r = sd_bus_message_new_method_return(m, &reply);
590 /* Note that we do not protect against reexecution
591 * here (using the last_iteration check, see below),
592 * should the node tree have changed and we got called
593 * again. We assume that property Get() calls are
594 * ultimately without side-effects or if they aren't
595 * then at least idempotent. */
597 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
601 /* Note that we do not do an access check here. Read
602 * access to properties is always unrestricted, since
603 * PropertiesChanged signals broadcast contents
606 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
608 return bus_maybe_reply_error(m, r, &error);
610 if (bus->nodes_modified)
613 r = sd_bus_message_close_container(reply);
618 const char *signature = NULL;
621 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
622 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
624 /* Avoid that we call the set routine more than once
625 * if the processing of this message got restarted
626 * because the node tree changed. */
627 if (c->last_iteration == bus->iteration_counter)
630 c->last_iteration = bus->iteration_counter;
632 r = sd_bus_message_peek_type(m, &type, &signature);
636 if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
637 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));
639 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
643 r = check_access(bus, m, c, &error);
645 return bus_maybe_reply_error(m, r, &error);
647 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
649 return bus_maybe_reply_error(m, r, &error);
651 if (bus->nodes_modified)
654 r = sd_bus_message_exit_container(m);
659 r = sd_bus_send(bus, reply, NULL);
666 static int vtable_append_one_property(
668 sd_bus_message *reply,
670 struct node_vtable *c,
671 const sd_bus_vtable *v,
673 sd_bus_error *error) {
684 r = sd_bus_message_open_container(reply, 'e', "sv");
688 r = sd_bus_message_append(reply, "s", v->x.property.member);
692 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
696 slot = container_of(c, sd_bus_slot, node_vtable);
698 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
701 if (bus->nodes_modified)
704 r = sd_bus_message_close_container(reply);
708 r = sd_bus_message_close_container(reply);
715 static int vtable_append_all_properties(
717 sd_bus_message *reply,
719 struct node_vtable *c,
721 sd_bus_error *error) {
723 const sd_bus_vtable *v;
731 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
734 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
735 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
738 if (v->flags & SD_BUS_VTABLE_HIDDEN)
741 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
744 if (bus->nodes_modified)
751 static int property_get_all_callbacks_run(
754 struct node_vtable *first,
755 bool require_fallback,
757 bool *found_object) {
759 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
760 struct node_vtable *c;
761 bool found_interface;
766 assert(found_object);
768 r = sd_bus_message_new_method_return(m, &reply);
772 r = sd_bus_message_open_container(reply, 'a', "{sv}");
776 found_interface = !iface ||
777 streq(iface, "org.freedesktop.DBus.Properties") ||
778 streq(iface, "org.freedesktop.DBus.Peer") ||
779 streq(iface, "org.freedesktop.DBus.Introspectable");
781 LIST_FOREACH(vtables, c, first) {
782 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
785 if (require_fallback && !c->is_fallback)
788 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
790 return bus_maybe_reply_error(m, r, &error);
791 if (bus->nodes_modified)
796 *found_object = true;
798 if (iface && !streq(c->interface, iface))
800 found_interface = true;
802 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
804 return bus_maybe_reply_error(m, r, &error);
805 if (bus->nodes_modified)
809 if (!found_interface) {
810 r = sd_bus_reply_method_errorf(
812 SD_BUS_ERROR_UNKNOWN_INTERFACE,
813 "Unknown interface '%s'.", iface);
820 r = sd_bus_message_close_container(reply);
824 r = sd_bus_send(bus, reply, NULL);
831 static int bus_node_exists(
835 bool require_fallback) {
837 struct node_vtable *c;
838 struct node_callback *k;
845 /* Tests if there's anything attached directly to this node
846 * for the specified path */
848 if (!require_fallback && (n->enumerators || n->object_managers))
851 LIST_FOREACH(callbacks, k, n->callbacks) {
852 if (require_fallback && !k->is_fallback)
858 LIST_FOREACH(vtables, c, n->vtables) {
859 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
861 if (require_fallback && !c->is_fallback)
864 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
867 if (bus->nodes_modified)
874 static int process_introspect(
878 bool require_fallback,
879 bool *found_object) {
881 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
882 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
883 _cleanup_set_free_free_ Set *s = NULL;
884 const char *previous_interface = NULL;
885 struct introspect intro;
886 struct node_vtable *c;
893 assert(found_object);
895 r = get_child_nodes(bus, m->path, n, &s, &error);
897 return bus_maybe_reply_error(m, r, &error);
898 if (bus->nodes_modified)
901 r = introspect_begin(&intro, bus->trusted);
905 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
909 empty = set_isempty(s);
911 LIST_FOREACH(vtables, c, n->vtables) {
912 if (require_fallback && !c->is_fallback)
915 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
917 r = bus_maybe_reply_error(m, r, &error);
920 if (bus->nodes_modified) {
929 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
932 if (!streq_ptr(previous_interface, c->interface)) {
934 if (previous_interface)
935 fputs(" </interface>\n", intro.f);
937 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
940 r = introspect_write_interface(&intro, c->vtable);
944 previous_interface = c->interface;
947 if (previous_interface)
948 fputs(" </interface>\n", intro.f);
951 /* Nothing?, let's see if we exist at all, and if not
952 * refuse to do anything */
953 r = bus_node_exists(bus, n, m->path, require_fallback);
956 if (bus->nodes_modified) {
962 *found_object = true;
964 r = introspect_write_child_nodes(&intro, s, m->path);
968 r = introspect_finish(&intro, bus, m, &reply);
972 r = sd_bus_send(bus, reply, NULL);
979 introspect_free(&intro);
983 static int object_manager_serialize_path(
985 sd_bus_message *reply,
988 bool require_fallback,
989 sd_bus_error *error) {
991 const char *previous_interface = NULL;
992 bool found_something = false;
993 struct node_vtable *i;
1003 n = hashmap_get(bus->nodes, prefix);
1007 LIST_FOREACH(vtables, i, n->vtables) {
1010 if (require_fallback && !i->is_fallback)
1013 r = node_vtable_get_userdata(bus, path, i, &u, error);
1016 if (bus->nodes_modified)
1021 if (!found_something) {
1023 /* Open the object part */
1025 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1029 r = sd_bus_message_append(reply, "o", path);
1033 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1037 found_something = true;
1040 if (!streq_ptr(previous_interface, i->interface)) {
1042 /* Maybe close the previous interface part */
1044 if (previous_interface) {
1045 r = sd_bus_message_close_container(reply);
1049 r = sd_bus_message_close_container(reply);
1054 /* Open the new interface part */
1056 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1060 r = sd_bus_message_append(reply, "s", i->interface);
1064 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1069 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1072 if (bus->nodes_modified)
1075 previous_interface = i->interface;
1078 if (previous_interface) {
1079 r = sd_bus_message_close_container(reply);
1083 r = sd_bus_message_close_container(reply);
1088 if (found_something) {
1089 r = sd_bus_message_close_container(reply);
1093 r = sd_bus_message_close_container(reply);
1101 static int object_manager_serialize_path_and_fallbacks(
1103 sd_bus_message *reply,
1105 sd_bus_error *error) {
1115 /* First, add all vtables registered for this path */
1116 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1119 if (bus->nodes_modified)
1122 /* Second, add fallback vtables registered for any of the prefixes */
1123 prefix = alloca(strlen(path) + 1);
1124 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1125 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1128 if (bus->nodes_modified)
1135 static int process_get_managed_objects(
1139 bool require_fallback,
1140 bool *found_object) {
1142 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1143 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1144 _cleanup_set_free_free_ Set *s = NULL;
1152 assert(found_object);
1154 /* Spec says, GetManagedObjects() is only implemented on the root of a
1155 * sub-tree. Therefore, we require a registered object-manager on
1156 * exactly the queried path, otherwise, we refuse to respond. */
1158 if (require_fallback || !n->object_managers)
1161 r = get_child_nodes(bus, m->path, n, &s, &error);
1164 if (bus->nodes_modified)
1167 r = sd_bus_message_new_method_return(m, &reply);
1171 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1175 SET_FOREACH(path, s, i) {
1176 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1180 if (bus->nodes_modified)
1184 r = sd_bus_message_close_container(reply);
1188 r = sd_bus_send(bus, reply, NULL);
1195 static int object_find_and_run(
1199 bool require_fallback,
1200 bool *found_object) {
1203 struct vtable_member vtable_key, *v;
1209 assert(found_object);
1211 n = hashmap_get(bus->nodes, p);
1215 /* First, try object callbacks */
1216 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1219 if (bus->nodes_modified)
1222 if (!m->interface || !m->member)
1225 /* Then, look for a known method */
1226 vtable_key.path = (char*) p;
1227 vtable_key.interface = m->interface;
1228 vtable_key.member = m->member;
1230 v = hashmap_get(bus->vtable_methods, &vtable_key);
1232 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1235 if (bus->nodes_modified)
1239 /* Then, look for a known property */
1240 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1243 get = streq(m->member, "Get");
1245 if (get || streq(m->member, "Set")) {
1247 r = sd_bus_message_rewind(m, true);
1251 vtable_key.path = (char*) p;
1253 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1255 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1257 v = hashmap_get(bus->vtable_properties, &vtable_key);
1259 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1264 } else if (streq(m->member, "GetAll")) {
1267 r = sd_bus_message_rewind(m, true);
1271 r = sd_bus_message_read(m, "s", &iface);
1273 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1278 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1283 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1285 if (!isempty(sd_bus_message_get_signature(m, true)))
1286 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1288 r = process_introspect(bus, m, n, require_fallback, found_object);
1292 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1294 if (!isempty(sd_bus_message_get_signature(m, true)))
1295 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1297 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1302 if (bus->nodes_modified)
1305 if (!*found_object) {
1306 r = bus_node_exists(bus, n, m->path, require_fallback);
1309 if (bus->nodes_modified)
1312 *found_object = true;
1318 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1321 bool found_object = false;
1326 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1329 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1332 if (hashmap_isempty(bus->nodes))
1335 /* Never respond to broadcast messages */
1336 if (bus->bus_client && !m->destination)
1342 pl = strlen(m->path);
1346 bus->nodes_modified = false;
1348 r = object_find_and_run(bus, m, m->path, false, &found_object);
1352 /* Look for fallback prefixes */
1353 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1355 if (bus->nodes_modified)
1358 r = object_find_and_run(bus, m, prefix, true, &found_object);
1363 } while (bus->nodes_modified);
1368 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1369 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1370 r = sd_bus_reply_method_errorf(
1372 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1373 "Unknown property or interface.");
1375 r = sd_bus_reply_method_errorf(
1377 SD_BUS_ERROR_UNKNOWN_METHOD,
1378 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1386 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1387 struct node *n, *parent;
1389 _cleanup_free_ char *s = NULL;
1395 assert(path[0] == '/');
1397 n = hashmap_get(bus->nodes, path);
1401 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1409 if (streq(path, "/"))
1412 e = strrchr(path, '/');
1415 p = strndupa(path, MAX(1, path - e));
1417 parent = bus_node_allocate(bus, p);
1422 n = new0(struct node, 1);
1428 s = NULL; /* do not free */
1430 r = hashmap_put(bus->nodes, n->path, n);
1438 LIST_PREPEND(siblings, parent->child, n);
1443 void bus_node_gc(sd_bus *b, struct node *n) {
1456 assert(hashmap_remove(b->nodes, n->path) == n);
1459 LIST_REMOVE(siblings, n->parent->child, n);
1462 bus_node_gc(b, n->parent);
1466 static int bus_add_object(
1471 sd_bus_message_handler_t callback,
1478 assert_return(bus, -EINVAL);
1479 assert_return(object_path_is_valid(path), -EINVAL);
1480 assert_return(callback, -EINVAL);
1481 assert_return(!bus_pid_changed(bus), -ECHILD);
1483 n = bus_node_allocate(bus, path);
1487 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1493 s->node_callback.callback = callback;
1494 s->node_callback.is_fallback = fallback;
1496 s->node_callback.node = n;
1497 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1498 bus->nodes_modified = true;
1506 sd_bus_slot_unref(s);
1507 bus_node_gc(bus, n);
1512 _public_ int sd_bus_add_object(
1516 sd_bus_message_handler_t callback,
1519 return bus_add_object(bus, slot, false, path, callback, userdata);
1522 _public_ int sd_bus_add_fallback(
1526 sd_bus_message_handler_t callback,
1529 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1532 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1533 const struct vtable_member *m = a;
1534 uint8_t hash_key2[HASH_KEY_SIZE];
1539 ret = string_hash_func(m->path, hash_key);
1541 /* Use a slightly different hash key for the interface */
1542 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1544 ret ^= string_hash_func(m->interface, hash_key2);
1546 /* And an even different one for the member */
1548 ret ^= string_hash_func(m->member, hash_key2);
1553 static int vtable_member_compare_func(const void *a, const void *b) {
1554 const struct vtable_member *x = a, *y = b;
1560 r = strcmp(x->path, y->path);
1564 r = strcmp(x->interface, y->interface);
1568 return strcmp(x->member, y->member);
1571 static const struct hash_ops vtable_member_hash_ops = {
1572 .hash = vtable_member_hash_func,
1573 .compare = vtable_member_compare_func
1576 static int add_object_vtable_internal(
1580 const char *interface,
1581 const sd_bus_vtable *vtable,
1583 sd_bus_object_find_t find,
1586 sd_bus_slot *s = NULL;
1587 struct node_vtable *i, *existing = NULL;
1588 const sd_bus_vtable *v;
1592 assert_return(bus, -EINVAL);
1593 assert_return(object_path_is_valid(path), -EINVAL);
1594 assert_return(interface_name_is_valid(interface), -EINVAL);
1595 assert_return(vtable, -EINVAL);
1596 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1597 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1598 assert_return(!bus_pid_changed(bus), -ECHILD);
1599 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1600 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1601 !streq(interface, "org.freedesktop.DBus.Peer") &&
1602 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1604 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1608 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1612 n = bus_node_allocate(bus, path);
1616 LIST_FOREACH(vtables, i, n->vtables) {
1617 if (i->is_fallback != fallback) {
1622 if (streq(i->interface, interface)) {
1624 if (i->vtable == vtable) {
1633 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1639 s->node_vtable.is_fallback = fallback;
1640 s->node_vtable.vtable = vtable;
1641 s->node_vtable.find = find;
1643 s->node_vtable.interface = strdup(interface);
1644 if (!s->node_vtable.interface) {
1649 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1653 case _SD_BUS_VTABLE_METHOD: {
1654 struct vtable_member *m;
1656 if (!member_name_is_valid(v->x.method.member) ||
1657 !signature_is_valid(strempty(v->x.method.signature), false) ||
1658 !signature_is_valid(strempty(v->x.method.result), false) ||
1659 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1660 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1665 m = new0(struct vtable_member, 1);
1671 m->parent = &s->node_vtable;
1673 m->interface = s->node_vtable.interface;
1674 m->member = v->x.method.member;
1677 r = hashmap_put(bus->vtable_methods, m, m);
1686 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1688 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1693 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1700 case _SD_BUS_VTABLE_PROPERTY: {
1701 struct vtable_member *m;
1703 if (!member_name_is_valid(v->x.property.member) ||
1704 !signature_is_single(v->x.property.signature, false) ||
1705 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1706 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1707 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1708 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1713 m = new0(struct vtable_member, 1);
1719 m->parent = &s->node_vtable;
1721 m->interface = s->node_vtable.interface;
1722 m->member = v->x.property.member;
1725 r = hashmap_put(bus->vtable_properties, m, m);
1734 case _SD_BUS_VTABLE_SIGNAL:
1736 if (!member_name_is_valid(v->x.signal.member) ||
1737 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1738 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1751 s->node_vtable.node = n;
1752 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1753 bus->nodes_modified = true;
1761 sd_bus_slot_unref(s);
1762 bus_node_gc(bus, n);
1767 _public_ int sd_bus_add_object_vtable(
1771 const char *interface,
1772 const sd_bus_vtable *vtable,
1775 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1778 _public_ int sd_bus_add_fallback_vtable(
1782 const char *interface,
1783 const sd_bus_vtable *vtable,
1784 sd_bus_object_find_t find,
1787 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1790 _public_ int sd_bus_add_node_enumerator(
1794 sd_bus_node_enumerator_t callback,
1801 assert_return(bus, -EINVAL);
1802 assert_return(object_path_is_valid(path), -EINVAL);
1803 assert_return(callback, -EINVAL);
1804 assert_return(!bus_pid_changed(bus), -ECHILD);
1806 n = bus_node_allocate(bus, path);
1810 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1816 s->node_enumerator.callback = callback;
1818 s->node_enumerator.node = n;
1819 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1820 bus->nodes_modified = true;
1828 sd_bus_slot_unref(s);
1829 bus_node_gc(bus, n);
1834 static int emit_properties_changed_on_interface(
1838 const char *interface,
1839 bool require_fallback,
1840 bool *found_interface,
1843 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1844 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1845 bool has_invalidating = false, has_changing = false;
1846 struct vtable_member key = {};
1847 struct node_vtable *c;
1857 assert(found_interface);
1859 n = hashmap_get(bus->nodes, prefix);
1863 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1867 r = sd_bus_message_append(m, "s", interface);
1871 r = sd_bus_message_open_container(m, 'a', "{sv}");
1876 key.interface = interface;
1878 LIST_FOREACH(vtables, c, n->vtables) {
1879 if (require_fallback && !c->is_fallback)
1882 if (!streq(c->interface, interface))
1885 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1888 if (bus->nodes_modified)
1893 *found_interface = true;
1896 /* If the caller specified a list of
1897 * properties we include exactly those in the
1898 * PropertiesChanged message */
1900 STRV_FOREACH(property, names) {
1901 struct vtable_member *v;
1903 assert_return(member_name_is_valid(*property), -EINVAL);
1905 key.member = *property;
1906 v = hashmap_get(bus->vtable_properties, &key);
1910 /* If there are two vtables for the same
1911 * interface, let's handle this property when
1912 * we come to that vtable. */
1916 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1917 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1919 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1921 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1922 has_invalidating = true;
1926 has_changing = true;
1928 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1931 if (bus->nodes_modified)
1935 const sd_bus_vtable *v;
1937 /* If the caller specified no properties list
1938 * we include all properties that are marked
1939 * as changing in the message. */
1941 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1942 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1945 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1948 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1949 has_invalidating = true;
1953 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1956 has_changing = true;
1958 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1961 if (bus->nodes_modified)
1967 if (!has_invalidating && !has_changing)
1970 r = sd_bus_message_close_container(m);
1974 r = sd_bus_message_open_container(m, 'a', "s");
1978 if (has_invalidating) {
1979 LIST_FOREACH(vtables, c, n->vtables) {
1980 if (require_fallback && !c->is_fallback)
1983 if (!streq(c->interface, interface))
1986 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1989 if (bus->nodes_modified)
1995 STRV_FOREACH(property, names) {
1996 struct vtable_member *v;
1998 key.member = *property;
1999 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2000 assert(c == v->parent);
2002 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2005 r = sd_bus_message_append(m, "s", *property);
2010 const sd_bus_vtable *v;
2012 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2013 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2016 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2019 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2022 r = sd_bus_message_append(m, "s", v->x.property.member);
2030 r = sd_bus_message_close_container(m);
2034 r = sd_bus_send(bus, m, NULL);
2041 _public_ int sd_bus_emit_properties_changed_strv(
2044 const char *interface,
2047 BUS_DONT_DESTROY(bus);
2048 bool found_interface = false;
2052 assert_return(bus, -EINVAL);
2053 assert_return(object_path_is_valid(path), -EINVAL);
2054 assert_return(interface_name_is_valid(interface), -EINVAL);
2055 assert_return(!bus_pid_changed(bus), -ECHILD);
2057 if (!BUS_IS_OPEN(bus->state))
2060 /* A non-NULL but empty names list means nothing needs to be
2061 generated. A NULL list OTOH indicates that all properties
2062 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2063 included in the PropertiesChanged message. */
2064 if (names && names[0] == NULL)
2068 bus->nodes_modified = false;
2070 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2073 if (bus->nodes_modified)
2076 prefix = alloca(strlen(path) + 1);
2077 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2078 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2081 if (bus->nodes_modified)
2085 } while (bus->nodes_modified);
2087 return found_interface ? 0 : -ENOENT;
2090 _public_ int sd_bus_emit_properties_changed(
2093 const char *interface,
2094 const char *name, ...) {
2098 assert_return(bus, -EINVAL);
2099 assert_return(object_path_is_valid(path), -EINVAL);
2100 assert_return(interface_name_is_valid(interface), -EINVAL);
2101 assert_return(!bus_pid_changed(bus), -ECHILD);
2103 if (!BUS_IS_OPEN(bus->state))
2109 names = strv_from_stdarg_alloca(name);
2111 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2114 static int object_added_append_all_prefix(
2120 bool require_fallback) {
2122 const char *previous_interface = NULL;
2123 struct node_vtable *c;
2133 n = hashmap_get(bus->nodes, prefix);
2137 LIST_FOREACH(vtables, c, n->vtables) {
2138 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2141 if (require_fallback && !c->is_fallback)
2144 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2147 if (bus->nodes_modified)
2152 if (!streq_ptr(c->interface, previous_interface)) {
2153 /* If a child-node already handled this interface, we
2154 * skip it on any of its parents. The child vtables
2155 * always fully override any conflicting vtables of
2156 * any parent node. */
2157 if (set_get(s, c->interface))
2160 r = set_put(s, c->interface);
2164 if (previous_interface) {
2165 r = sd_bus_message_close_container(m);
2168 r = sd_bus_message_close_container(m);
2173 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2176 r = sd_bus_message_append(m, "s", c->interface);
2179 r = sd_bus_message_open_container(m, 'a', "{sv}");
2183 previous_interface = c->interface;
2186 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2189 if (bus->nodes_modified)
2193 if (previous_interface) {
2194 r = sd_bus_message_close_container(m);
2197 r = sd_bus_message_close_container(m);
2205 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2206 _cleanup_set_free_ Set *s = NULL;
2215 * This appends all interfaces registered on path @path. We first add
2216 * the builtin interfaces, which are always available and handled by
2217 * sd-bus. Then, we add all interfaces registered on the exact node,
2218 * followed by all fallback interfaces registered on any parent prefix.
2220 * If an interface is registered multiple times on the same node with
2221 * different vtables, we merge all the properties across all vtables.
2222 * However, if a child node has the same interface registered as one of
2223 * its parent nodes has as fallback, we make the child overwrite the
2224 * parent instead of extending it. Therefore, we keep a "Set" of all
2225 * handled interfaces during parent traversal, so we skip interfaces on
2226 * a parent that were overwritten by a child.
2229 s = set_new(&string_hash_ops);
2233 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2236 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2239 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2242 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2246 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2249 if (bus->nodes_modified)
2252 prefix = alloca(strlen(path) + 1);
2253 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2254 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2257 if (bus->nodes_modified)
2264 int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2265 BUS_DONT_DESTROY(bus);
2267 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2271 * This emits an InterfacesAdded signal on the given path, by iterating
2272 * all registered vtables and fallback vtables on the path. All
2273 * properties are queried and included in the signal.
2274 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2275 * explicit list of registered interfaces. However, unlike
2276 * interfaces_added(), this call can figure out the list of supported
2277 * interfaces itself. Furthermore, it properly adds the builtin
2278 * org.freedesktop.DBus.* interfaces.
2281 assert_return(bus, -EINVAL);
2282 assert_return(object_path_is_valid(path), -EINVAL);
2283 assert_return(!bus_pid_changed(bus), -ECHILD);
2285 if (!BUS_IS_OPEN(bus->state))
2289 bus->nodes_modified = false;
2290 m = sd_bus_message_unref(m);
2292 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2296 r = sd_bus_message_append_basic(m, 'o', path);
2300 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2304 r = object_added_append_all(bus, m, path);
2308 if (bus->nodes_modified)
2311 r = sd_bus_message_close_container(m);
2315 } while (bus->nodes_modified);
2317 return sd_bus_send(bus, m, NULL);
2320 static int object_removed_append_all_prefix(
2326 bool require_fallback) {
2328 const char *previous_interface = NULL;
2329 struct node_vtable *c;
2339 n = hashmap_get(bus->nodes, prefix);
2343 LIST_FOREACH(vtables, c, n->vtables) {
2344 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2347 if (require_fallback && !c->is_fallback)
2349 if (streq_ptr(c->interface, previous_interface))
2352 /* If a child-node already handled this interface, we
2353 * skip it on any of its parents. The child vtables
2354 * always fully override any conflicting vtables of
2355 * any parent node. */
2356 if (set_get(s, c->interface))
2359 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2362 if (bus->nodes_modified)
2367 r = set_put(s, c->interface);
2371 r = sd_bus_message_append(m, "s", c->interface);
2375 previous_interface = c->interface;
2381 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2382 _cleanup_set_free_ Set *s = NULL;
2390 /* see sd_bus_emit_object_added() for details */
2392 s = set_new(&string_hash_ops);
2396 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2399 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2402 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2405 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2409 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2412 if (bus->nodes_modified)
2415 prefix = alloca(strlen(path) + 1);
2416 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2417 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2420 if (bus->nodes_modified)
2427 int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2428 BUS_DONT_DESTROY(bus);
2430 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2434 * This is like sd_bus_emit_object_added(), but emits an
2435 * InterfacesRemoved signal on the given path. This only includes any
2436 * registered interfaces but skips the properties. Note that this will
2437 * call into the find() callbacks of any registered vtable. Therefore,
2438 * you must call this function before destroying/unlinking your object.
2439 * Otherwise, the list of interfaces will be incomplete. However, note
2440 * that this will *NOT* call into any property callback. Therefore, the
2441 * object might be in an "destructed" state, as long as we can find it.
2444 assert_return(bus, -EINVAL);
2445 assert_return(object_path_is_valid(path), -EINVAL);
2446 assert_return(!bus_pid_changed(bus), -ECHILD);
2448 if (!BUS_IS_OPEN(bus->state))
2452 bus->nodes_modified = false;
2453 m = sd_bus_message_unref(m);
2455 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2459 r = sd_bus_message_append_basic(m, 'o', path);
2463 r = sd_bus_message_open_container(m, 'a', "s");
2467 r = object_removed_append_all(bus, m, path);
2471 if (bus->nodes_modified)
2474 r = sd_bus_message_close_container(m);
2478 } while (bus->nodes_modified);
2480 return sd_bus_send(bus, m, NULL);
2483 static int interfaces_added_append_one_prefix(
2488 const char *interface,
2489 bool require_fallback) {
2491 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2492 bool found_interface = false;
2493 struct node_vtable *c;
2504 n = hashmap_get(bus->nodes, prefix);
2508 LIST_FOREACH(vtables, c, n->vtables) {
2509 if (require_fallback && !c->is_fallback)
2512 if (!streq(c->interface, interface))
2515 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2518 if (bus->nodes_modified)
2523 if (!found_interface) {
2524 r = sd_bus_message_append_basic(m, 's', interface);
2528 r = sd_bus_message_open_container(m, 'a', "{sv}");
2532 found_interface = true;
2535 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2538 if (bus->nodes_modified)
2542 if (found_interface) {
2543 r = sd_bus_message_close_container(m);
2548 return found_interface;
2551 static int interfaces_added_append_one(
2555 const char *interface) {
2565 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2568 if (bus->nodes_modified)
2571 prefix = alloca(strlen(path) + 1);
2572 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2573 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2576 if (bus->nodes_modified)
2583 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2584 BUS_DONT_DESTROY(bus);
2586 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2590 assert_return(bus, -EINVAL);
2591 assert_return(object_path_is_valid(path), -EINVAL);
2592 assert_return(!bus_pid_changed(bus), -ECHILD);
2594 if (!BUS_IS_OPEN(bus->state))
2597 if (strv_isempty(interfaces))
2601 bus->nodes_modified = false;
2602 m = sd_bus_message_unref(m);
2604 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2608 r = sd_bus_message_append_basic(m, 'o', path);
2612 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2616 STRV_FOREACH(i, interfaces) {
2617 assert_return(interface_name_is_valid(*i), -EINVAL);
2619 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2623 r = interfaces_added_append_one(bus, m, path, *i);
2627 if (bus->nodes_modified)
2630 r = sd_bus_message_close_container(m);
2635 if (bus->nodes_modified)
2638 r = sd_bus_message_close_container(m);
2642 } while (bus->nodes_modified);
2644 return sd_bus_send(bus, m, NULL);
2647 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2650 assert_return(bus, -EINVAL);
2651 assert_return(object_path_is_valid(path), -EINVAL);
2652 assert_return(!bus_pid_changed(bus), -ECHILD);
2654 if (!BUS_IS_OPEN(bus->state))
2657 interfaces = strv_from_stdarg_alloca(interface);
2659 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2662 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2663 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2666 assert_return(bus, -EINVAL);
2667 assert_return(object_path_is_valid(path), -EINVAL);
2668 assert_return(!bus_pid_changed(bus), -ECHILD);
2670 if (!BUS_IS_OPEN(bus->state))
2673 if (strv_isempty(interfaces))
2676 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2680 r = sd_bus_message_append_basic(m, 'o', path);
2684 r = sd_bus_message_append_strv(m, interfaces);
2688 return sd_bus_send(bus, m, NULL);
2691 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2694 assert_return(bus, -EINVAL);
2695 assert_return(object_path_is_valid(path), -EINVAL);
2696 assert_return(!bus_pid_changed(bus), -ECHILD);
2698 if (!BUS_IS_OPEN(bus->state))
2701 interfaces = strv_from_stdarg_alloca(interface);
2703 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2706 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2711 assert_return(bus, -EINVAL);
2712 assert_return(object_path_is_valid(path), -EINVAL);
2713 assert_return(!bus_pid_changed(bus), -ECHILD);
2715 n = bus_node_allocate(bus, path);
2719 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2725 s->node_object_manager.node = n;
2726 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2727 bus->nodes_modified = true;
2735 sd_bus_slot_unref(s);
2736 bus_node_gc(bus, n);