1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/capability.h>
26 #include "bus-internal.h"
27 #include "bus-message.h"
29 #include "bus-signature.h"
30 #include "bus-introspect.h"
31 #include "bus-objects.h"
34 static int node_vtable_get_userdata(
37 struct node_vtable *c,
39 sd_bus_error *error) {
50 r = c->find(bus, path, c->interface, u, &u, error);
53 if (sd_bus_error_is_set(error))
54 return -sd_bus_error_get_errno(error);
65 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
68 return (uint8_t*) u + p->x.property.offset;
71 static int vtable_property_get_userdata(
74 struct vtable_member *p,
76 sd_bus_error *error) {
86 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
89 if (bus->nodes_modified)
92 *userdata = vtable_property_convert_userdata(p->vtable, u);
96 static int add_enumerated_to_set(
99 struct node_enumerator *first,
101 sd_bus_error *error) {
103 struct node_enumerator *c;
110 LIST_FOREACH(enumerators, c, first) {
111 char **children = NULL, **k;
113 if (bus->nodes_modified)
116 r = c->callback(bus, prefix, c->userdata, &children, error);
119 if (sd_bus_error_is_set(error))
120 return -sd_bus_error_get_errno(error);
122 STRV_FOREACH(k, children) {
128 if (!object_path_is_valid(*k)){
134 if (!object_path_startswith(*k, prefix)) {
139 r = set_consume(s, *k);
152 static int add_subtree_to_set(
157 sd_bus_error *error) {
167 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
170 if (bus->nodes_modified)
173 LIST_FOREACH(siblings, i, n->child) {
176 if (!object_path_startswith(i->path, prefix))
183 r = set_consume(s, t);
184 if (r < 0 && r != -EEXIST)
187 r = add_subtree_to_set(bus, prefix, i, s, error);
190 if (bus->nodes_modified)
197 static int get_child_nodes(
202 sd_bus_error *error) {
212 s = set_new(string_hash_func, string_compare_func);
216 r = add_subtree_to_set(bus, prefix, n, s, error);
226 static int node_callbacks_run(
229 struct node_callback *first,
230 bool require_fallback,
231 bool *found_object) {
233 struct node_callback *c;
238 assert(found_object);
240 LIST_FOREACH(callbacks, c, first) {
241 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
243 if (bus->nodes_modified)
246 if (require_fallback && !c->is_fallback)
249 *found_object = true;
251 if (c->last_iteration == bus->iteration_counter)
254 c->last_iteration = bus->iteration_counter;
256 r = sd_bus_message_rewind(m, true);
260 r = c->callback(bus, m, c->userdata, &error_buffer);
261 r = bus_maybe_reply_error(m, r, &error_buffer);
269 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
271 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
272 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
281 /* If the entire bus is trusted let's grant access */
285 /* If the member is marked UNPRIVILEGED let's grant access */
286 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
289 /* If we are not connected to kdbus we cannot retrieve the
290 * effective capability set without race. Since we need this
291 * for a security decision we cannot use racy data, hence
292 * don't request it. */
294 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
296 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
300 /* Check have the caller has the requested capability
301 * set. Note that the flags value contains the capability
302 * number plus one, which we need to subtract here. We do this
303 * so that we have 0 as special value for "default
305 cap = CAPABILITY_SHIFT(c->vtable->flags);
307 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
313 r = sd_bus_creds_has_effective_cap(creds, cap);
317 /* Caller has same UID as us, then let's grant access */
318 r = sd_bus_creds_get_uid(creds, &uid);
324 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
327 static int method_callbacks_run(
330 struct vtable_member *c,
331 bool require_fallback,
332 bool *found_object) {
334 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
335 const char *signature;
342 assert(found_object);
344 if (require_fallback && !c->parent->is_fallback)
347 r = check_access(bus, m, c, &error);
349 return bus_maybe_reply_error(m, r, &error);
351 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
353 return bus_maybe_reply_error(m, r, &error);
354 if (bus->nodes_modified)
357 *found_object = true;
359 if (c->last_iteration == bus->iteration_counter)
362 c->last_iteration = bus->iteration_counter;
364 r = sd_bus_message_rewind(m, true);
368 signature = sd_bus_message_get_signature(m, true);
372 if (!streq(strempty(c->vtable->x.method.signature), signature))
373 return sd_bus_reply_method_errorf(
375 SD_BUS_ERROR_INVALID_ARGS,
376 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
377 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
379 /* Keep track what the signature of the reply to this message
380 * should be, so that this can be enforced when sealing the
382 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
384 if (c->vtable->x.method.handler) {
385 r = c->vtable->x.method.handler(bus, m, u, &error);
386 return bus_maybe_reply_error(m, r, &error);
389 /* If the method callback is NULL, make this a successful NOP */
390 r = sd_bus_reply_method_return(m, NULL);
397 static int invoke_property_get(
399 const sd_bus_vtable *v,
401 const char *interface,
402 const char *property,
403 sd_bus_message *reply,
405 sd_bus_error *error) {
417 if (v->x.property.get) {
418 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
421 if (sd_bus_error_is_set(error))
422 return -sd_bus_error_get_errno(error);
426 /* Automatic handling if no callback is defined. */
428 if (streq(v->x.property.signature, "as"))
429 return sd_bus_message_append_strv(reply, *(char***) userdata);
431 assert(signature_is_single(v->x.property.signature, false));
432 assert(bus_type_is_basic(v->x.property.signature[0]));
434 switch (v->x.property.signature[0]) {
436 case SD_BUS_TYPE_STRING:
437 case SD_BUS_TYPE_SIGNATURE:
438 p = strempty(*(char**) userdata);
441 case SD_BUS_TYPE_OBJECT_PATH:
442 p = *(char**) userdata;
451 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
454 static int invoke_property_set(
456 const sd_bus_vtable *v,
458 const char *interface,
459 const char *property,
460 sd_bus_message *value,
462 sd_bus_error *error) {
473 if (v->x.property.set) {
474 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
477 if (sd_bus_error_is_set(error))
478 return -sd_bus_error_get_errno(error);
482 /* Automatic handling if no callback is defined. */
484 assert(signature_is_single(v->x.property.signature, false));
485 assert(bus_type_is_basic(v->x.property.signature[0]));
487 switch (v->x.property.signature[0]) {
489 case SD_BUS_TYPE_STRING:
490 case SD_BUS_TYPE_OBJECT_PATH:
491 case SD_BUS_TYPE_SIGNATURE: {
495 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
503 free(*(char**) userdata);
504 *(char**) userdata = n;
510 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
520 static int property_get_set_callbacks_run(
523 struct vtable_member *c,
524 bool require_fallback,
526 bool *found_object) {
528 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
529 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
536 assert(found_object);
538 if (require_fallback && !c->parent->is_fallback)
541 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
543 return bus_maybe_reply_error(m, r, &error);
544 if (bus->nodes_modified)
547 *found_object = true;
549 r = sd_bus_message_new_method_return(m, &reply);
554 /* Note that we do not protect against reexecution
555 * here (using the last_iteration check, see below),
556 * should the node tree have changed and we got called
557 * again. We assume that property Get() calls are
558 * ultimately without side-effects or if they aren't
559 * then at least idempotent. */
561 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
565 /* Note that we do not do an access check here. Read
566 * access to properties is always unrestricted, since
567 * PropertiesChanged signals broadcast contents
570 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
572 return bus_maybe_reply_error(m, r, &error);
574 if (bus->nodes_modified)
577 r = sd_bus_message_close_container(reply);
582 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
583 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
585 /* Avoid that we call the set routine more than once
586 * if the processing of this message got restarted
587 * because the node tree changed. */
588 if (c->last_iteration == bus->iteration_counter)
591 c->last_iteration = bus->iteration_counter;
593 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
597 r = check_access(bus, m, c, &error);
599 return bus_maybe_reply_error(m, r, &error);
601 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
603 return bus_maybe_reply_error(m, r, &error);
605 if (bus->nodes_modified)
608 r = sd_bus_message_exit_container(m);
613 r = sd_bus_send(bus, reply, NULL);
620 static int vtable_append_one_property(
622 sd_bus_message *reply,
624 struct node_vtable *c,
625 const sd_bus_vtable *v,
627 sd_bus_error *error) {
637 r = sd_bus_message_open_container(reply, 'e', "sv");
641 r = sd_bus_message_append(reply, "s", v->x.property.member);
645 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
649 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
652 if (bus->nodes_modified)
655 r = sd_bus_message_close_container(reply);
659 r = sd_bus_message_close_container(reply);
666 static int vtable_append_all_properties(
668 sd_bus_message *reply,
670 struct node_vtable *c,
672 sd_bus_error *error) {
674 const sd_bus_vtable *v;
682 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
685 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
686 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
689 if (v->flags & SD_BUS_VTABLE_HIDDEN)
692 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
695 if (bus->nodes_modified)
702 static int property_get_all_callbacks_run(
705 struct node_vtable *first,
706 bool require_fallback,
708 bool *found_object) {
710 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
711 struct node_vtable *c;
712 bool found_interface;
717 assert(found_object);
719 r = sd_bus_message_new_method_return(m, &reply);
723 r = sd_bus_message_open_container(reply, 'a', "{sv}");
727 found_interface = !iface ||
728 streq(iface, "org.freedesktop.DBus.Properties") ||
729 streq(iface, "org.freedesktop.DBus.Peer") ||
730 streq(iface, "org.freedesktop.DBus.Introspectable");
732 LIST_FOREACH(vtables, c, first) {
733 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
736 if (require_fallback && !c->is_fallback)
739 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
741 return bus_maybe_reply_error(m, r, &error);
742 if (bus->nodes_modified)
747 *found_object = true;
749 if (iface && !streq(c->interface, iface))
751 found_interface = true;
753 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
755 return bus_maybe_reply_error(m, r, &error);
756 if (bus->nodes_modified)
760 if (!found_interface) {
761 r = sd_bus_reply_method_errorf(
763 SD_BUS_ERROR_UNKNOWN_INTERFACE,
764 "Unknown interface '%s'.", iface);
771 r = sd_bus_message_close_container(reply);
775 r = sd_bus_send(bus, reply, NULL);
782 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
786 if (n->object_manager)
790 return bus_node_with_object_manager(bus, n->parent);
795 static bool bus_node_exists(
799 bool require_fallback) {
801 struct node_vtable *c;
802 struct node_callback *k;
808 /* Tests if there's anything attached directly to this node
809 * for the specified path */
811 LIST_FOREACH(callbacks, k, n->callbacks) {
812 if (require_fallback && !k->is_fallback)
818 LIST_FOREACH(vtables, c, n->vtables) {
819 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
821 if (require_fallback && !c->is_fallback)
824 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
826 if (bus->nodes_modified)
830 return !require_fallback && (n->enumerators || n->object_manager);
833 static int process_introspect(
837 bool require_fallback,
838 bool *found_object) {
840 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
841 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
842 _cleanup_set_free_free_ Set *s = NULL;
843 const char *previous_interface = NULL;
844 struct introspect intro;
845 struct node_vtable *c;
852 assert(found_object);
854 r = get_child_nodes(bus, m->path, n, &s, &error);
856 return bus_maybe_reply_error(m, r, &error);
857 if (bus->nodes_modified)
860 r = introspect_begin(&intro, bus->trusted);
864 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
868 empty = set_isempty(s);
870 LIST_FOREACH(vtables, c, n->vtables) {
871 if (require_fallback && !c->is_fallback)
874 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
876 r = bus_maybe_reply_error(m, r, &error);
879 if (bus->nodes_modified) {
888 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
891 if (!streq_ptr(previous_interface, c->interface)) {
893 if (previous_interface)
894 fputs(" </interface>\n", intro.f);
896 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
899 r = introspect_write_interface(&intro, c->vtable);
903 previous_interface = c->interface;
906 if (previous_interface)
907 fputs(" </interface>\n", intro.f);
910 /* Nothing?, let's see if we exist at all, and if not
911 * refuse to do anything */
912 r = bus_node_exists(bus, n, m->path, require_fallback);
915 if (bus->nodes_modified)
921 *found_object = true;
923 r = introspect_write_child_nodes(&intro, s, m->path);
927 r = introspect_finish(&intro, bus, m, &reply);
931 r = sd_bus_send(bus, reply, NULL);
938 introspect_free(&intro);
942 static int object_manager_serialize_path(
944 sd_bus_message *reply,
947 bool require_fallback,
948 sd_bus_error *error) {
950 const char *previous_interface = NULL;
951 bool found_something = false;
952 struct node_vtable *i;
962 n = hashmap_get(bus->nodes, prefix);
966 LIST_FOREACH(vtables, i, n->vtables) {
969 if (require_fallback && !i->is_fallback)
972 r = node_vtable_get_userdata(bus, path, i, &u, error);
975 if (bus->nodes_modified)
980 if (!found_something) {
982 /* Open the object part */
984 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
988 r = sd_bus_message_append(reply, "o", path);
992 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
996 found_something = true;
999 if (!streq_ptr(previous_interface, i->interface)) {
1001 /* Maybe close the previous interface part */
1003 if (previous_interface) {
1004 r = sd_bus_message_close_container(reply);
1008 r = sd_bus_message_close_container(reply);
1013 /* Open the new interface part */
1015 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1019 r = sd_bus_message_append(reply, "s", i->interface);
1023 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1028 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1031 if (bus->nodes_modified)
1034 previous_interface = i->interface;
1037 if (previous_interface) {
1038 r = sd_bus_message_close_container(reply);
1042 r = sd_bus_message_close_container(reply);
1047 if (found_something) {
1048 r = sd_bus_message_close_container(reply);
1052 r = sd_bus_message_close_container(reply);
1060 static int object_manager_serialize_path_and_fallbacks(
1062 sd_bus_message *reply,
1064 sd_bus_error *error) {
1074 /* First, add all vtables registered for this path */
1075 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1078 if (bus->nodes_modified)
1081 /* Second, add fallback vtables registered for any of the prefixes */
1082 prefix = alloca(strlen(path) + 1);
1083 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1084 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1087 if (bus->nodes_modified)
1094 static int process_get_managed_objects(
1098 bool require_fallback,
1099 bool *found_object) {
1101 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1102 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1103 _cleanup_set_free_free_ Set *s = NULL;
1110 assert(found_object);
1112 if (!bus_node_with_object_manager(bus, n))
1115 r = get_child_nodes(bus, m->path, n, &s, &error);
1118 if (bus->nodes_modified)
1121 r = sd_bus_message_new_method_return(m, &reply);
1125 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1129 empty = set_isempty(s);
1131 struct node_vtable *c;
1133 /* Hmm, so we have no children? Then let's check
1134 * whether we exist at all, i.e. whether at least one
1137 LIST_FOREACH(vtables, c, n->vtables) {
1139 if (require_fallback && !c->is_fallback)
1157 SET_FOREACH(path, s, i) {
1158 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1162 if (bus->nodes_modified)
1167 r = sd_bus_message_close_container(reply);
1171 r = sd_bus_send(bus, reply, NULL);
1178 static int object_find_and_run(
1182 bool require_fallback,
1183 bool *found_object) {
1186 struct vtable_member vtable_key, *v;
1192 assert(found_object);
1194 n = hashmap_get(bus->nodes, p);
1198 /* First, try object callbacks */
1199 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1202 if (bus->nodes_modified)
1205 if (!m->interface || !m->member)
1208 /* Then, look for a known method */
1209 vtable_key.path = (char*) p;
1210 vtable_key.interface = m->interface;
1211 vtable_key.member = m->member;
1213 v = hashmap_get(bus->vtable_methods, &vtable_key);
1215 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1218 if (bus->nodes_modified)
1222 /* Then, look for a known property */
1223 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1226 get = streq(m->member, "Get");
1228 if (get || streq(m->member, "Set")) {
1230 r = sd_bus_message_rewind(m, true);
1234 vtable_key.path = (char*) p;
1236 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1238 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1240 v = hashmap_get(bus->vtable_properties, &vtable_key);
1242 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1247 } else if (streq(m->member, "GetAll")) {
1250 r = sd_bus_message_rewind(m, true);
1254 r = sd_bus_message_read(m, "s", &iface);
1256 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1261 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1266 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1268 if (!isempty(sd_bus_message_get_signature(m, true)))
1269 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1271 r = process_introspect(bus, m, n, require_fallback, found_object);
1275 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1277 if (!isempty(sd_bus_message_get_signature(m, true)))
1278 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1280 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1285 if (bus->nodes_modified)
1288 if (!*found_object) {
1289 r = bus_node_exists(bus, n, m->path, require_fallback);
1293 *found_object = true;
1299 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1302 bool found_object = false;
1307 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1310 if (hashmap_isempty(bus->nodes))
1313 /* Never respond to broadcast messages */
1314 if (bus->bus_client && !m->destination)
1320 pl = strlen(m->path);
1324 bus->nodes_modified = false;
1326 r = object_find_and_run(bus, m, m->path, false, &found_object);
1330 /* Look for fallback prefixes */
1331 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1333 if (bus->nodes_modified)
1336 r = object_find_and_run(bus, m, prefix, true, &found_object);
1341 } while (bus->nodes_modified);
1346 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1347 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1348 r = sd_bus_reply_method_errorf(
1350 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1351 "Unknown property or interface.");
1353 r = sd_bus_reply_method_errorf(
1355 SD_BUS_ERROR_UNKNOWN_METHOD,
1356 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1364 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1365 struct node *n, *parent;
1367 _cleanup_free_ char *s = NULL;
1373 assert(path[0] == '/');
1375 n = hashmap_get(bus->nodes, path);
1379 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1387 if (streq(path, "/"))
1390 e = strrchr(path, '/');
1393 p = strndupa(path, MAX(1, path - e));
1395 parent = bus_node_allocate(bus, p);
1400 n = new0(struct node, 1);
1406 s = NULL; /* do not free */
1408 r = hashmap_put(bus->nodes, n->path, n);
1416 LIST_PREPEND(siblings, parent->child, n);
1421 static void bus_node_gc(sd_bus *b, struct node *n) {
1434 assert(hashmap_remove(b->nodes, n->path) == n);
1437 LIST_REMOVE(siblings, n->parent->child, n);
1440 bus_node_gc(b, n->parent);
1444 static int bus_add_object(
1448 sd_bus_message_handler_t callback,
1451 struct node_callback *c;
1455 assert_return(bus, -EINVAL);
1456 assert_return(object_path_is_valid(path), -EINVAL);
1457 assert_return(callback, -EINVAL);
1458 assert_return(!bus_pid_changed(bus), -ECHILD);
1460 n = bus_node_allocate(bus, path);
1464 c = new0(struct node_callback, 1);
1471 c->callback = callback;
1472 c->userdata = userdata;
1473 c->is_fallback = fallback;
1475 LIST_PREPEND(callbacks, n->callbacks, c);
1476 bus->nodes_modified = true;
1482 bus_node_gc(bus, n);
1486 static int bus_remove_object(
1490 sd_bus_message_handler_t callback,
1493 struct node_callback *c;
1496 assert_return(bus, -EINVAL);
1497 assert_return(object_path_is_valid(path), -EINVAL);
1498 assert_return(callback, -EINVAL);
1499 assert_return(!bus_pid_changed(bus), -ECHILD);
1501 n = hashmap_get(bus->nodes, path);
1505 LIST_FOREACH(callbacks, c, n->callbacks)
1506 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1511 LIST_REMOVE(callbacks, n->callbacks, c);
1514 bus_node_gc(bus, n);
1515 bus->nodes_modified = true;
1520 _public_ int sd_bus_add_object(sd_bus *bus,
1522 sd_bus_message_handler_t callback,
1525 return bus_add_object(bus, false, path, callback, userdata);
1528 _public_ int sd_bus_remove_object(sd_bus *bus,
1530 sd_bus_message_handler_t callback,
1533 return bus_remove_object(bus, false, path, callback, userdata);
1536 _public_ int sd_bus_add_fallback(sd_bus *bus,
1538 sd_bus_message_handler_t callback,
1541 return bus_add_object(bus, true, prefix, callback, userdata);
1544 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1546 sd_bus_message_handler_t callback,
1549 return bus_remove_object(bus, true, prefix, callback, userdata);
1552 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1558 if (w->interface && w->node && w->vtable) {
1559 const sd_bus_vtable *v;
1561 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1562 struct vtable_member *x = NULL;
1566 case _SD_BUS_VTABLE_METHOD: {
1567 struct vtable_member key;
1569 key.path = w->node->path;
1570 key.interface = w->interface;
1571 key.member = v->x.method.member;
1573 x = hashmap_remove(bus->vtable_methods, &key);
1577 case _SD_BUS_VTABLE_PROPERTY:
1578 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1579 struct vtable_member key;
1581 key.path = w->node->path;
1582 key.interface = w->interface;
1583 key.member = v->x.property.member;
1584 x = hashmap_remove(bus->vtable_properties, &key);
1596 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1597 const struct vtable_member *m = a;
1598 uint8_t hash_key2[HASH_KEY_SIZE];
1603 ret = string_hash_func(m->path, hash_key);
1605 /* Use a slightly different hash key for the interface */
1606 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1608 ret ^= string_hash_func(m->interface, hash_key2);
1610 /* And an even different one for the member */
1612 ret ^= string_hash_func(m->member, hash_key2);
1617 static int vtable_member_compare_func(const void *a, const void *b) {
1618 const struct vtable_member *x = a, *y = b;
1624 r = strcmp(x->path, y->path);
1628 r = strcmp(x->interface, y->interface);
1632 return strcmp(x->member, y->member);
1635 static int add_object_vtable_internal(
1638 const char *interface,
1639 const sd_bus_vtable *vtable,
1641 sd_bus_object_find_t find,
1644 struct node_vtable *c = NULL, *i, *existing = NULL;
1645 const sd_bus_vtable *v;
1649 assert_return(bus, -EINVAL);
1650 assert_return(object_path_is_valid(path), -EINVAL);
1651 assert_return(interface_name_is_valid(interface), -EINVAL);
1652 assert_return(vtable, -EINVAL);
1653 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1654 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1655 assert_return(!bus_pid_changed(bus), -ECHILD);
1656 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1657 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1658 !streq(interface, "org.freedesktop.DBus.Peer") &&
1659 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1661 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1665 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1669 n = bus_node_allocate(bus, path);
1673 LIST_FOREACH(vtables, i, n->vtables) {
1674 if (i->is_fallback != fallback) {
1679 if (streq(i->interface, interface)) {
1681 if (i->vtable == vtable) {
1690 c = new0(struct node_vtable, 1);
1697 c->is_fallback = fallback;
1699 c->userdata = userdata;
1702 c->interface = strdup(interface);
1703 if (!c->interface) {
1708 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1712 case _SD_BUS_VTABLE_METHOD: {
1713 struct vtable_member *m;
1715 if (!member_name_is_valid(v->x.method.member) ||
1716 !signature_is_valid(strempty(v->x.method.signature), false) ||
1717 !signature_is_valid(strempty(v->x.method.result), false) ||
1718 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1719 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1724 m = new0(struct vtable_member, 1);
1732 m->interface = c->interface;
1733 m->member = v->x.method.member;
1736 r = hashmap_put(bus->vtable_methods, m, m);
1745 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1747 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1754 case _SD_BUS_VTABLE_PROPERTY: {
1755 struct vtable_member *m;
1757 if (!member_name_is_valid(v->x.property.member) ||
1758 !signature_is_single(v->x.property.signature, false) ||
1759 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1760 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1761 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1762 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1767 m = new0(struct vtable_member, 1);
1775 m->interface = c->interface;
1776 m->member = v->x.property.member;
1779 r = hashmap_put(bus->vtable_properties, m, m);
1788 case _SD_BUS_VTABLE_SIGNAL:
1790 if (!member_name_is_valid(v->x.signal.member) ||
1791 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1792 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1805 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1806 bus->nodes_modified = true;
1812 free_node_vtable(bus, c);
1814 bus_node_gc(bus, n);
1818 static int remove_object_vtable_internal(
1821 const char *interface,
1822 const sd_bus_vtable *vtable,
1824 sd_bus_object_find_t find,
1827 struct node_vtable *c;
1830 assert_return(bus, -EINVAL);
1831 assert_return(object_path_is_valid(path), -EINVAL);
1832 assert_return(interface_name_is_valid(interface), -EINVAL);
1833 assert_return(!bus_pid_changed(bus), -ECHILD);
1835 n = hashmap_get(bus->nodes, path);
1839 LIST_FOREACH(vtables, c, n->vtables)
1840 if (streq(c->interface, interface) &&
1841 c->is_fallback == fallback &&
1842 c->vtable == vtable &&
1844 c->userdata == userdata)
1850 LIST_REMOVE(vtables, n->vtables, c);
1852 free_node_vtable(bus, c);
1853 bus_node_gc(bus, n);
1855 bus->nodes_modified = true;
1860 _public_ int sd_bus_add_object_vtable(
1863 const char *interface,
1864 const sd_bus_vtable *vtable,
1867 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1870 _public_ int sd_bus_remove_object_vtable(
1873 const char *interface,
1874 const sd_bus_vtable *vtable,
1877 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1880 _public_ int sd_bus_add_fallback_vtable(
1883 const char *interface,
1884 const sd_bus_vtable *vtable,
1885 sd_bus_object_find_t find,
1888 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1891 _public_ int sd_bus_remove_fallback_vtable(
1894 const char *interface,
1895 const sd_bus_vtable *vtable,
1896 sd_bus_object_find_t find,
1899 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1902 _public_ int sd_bus_add_node_enumerator(
1905 sd_bus_node_enumerator_t callback,
1908 struct node_enumerator *c;
1912 assert_return(bus, -EINVAL);
1913 assert_return(object_path_is_valid(path), -EINVAL);
1914 assert_return(callback, -EINVAL);
1915 assert_return(!bus_pid_changed(bus), -ECHILD);
1917 n = bus_node_allocate(bus, path);
1921 c = new0(struct node_enumerator, 1);
1928 c->callback = callback;
1929 c->userdata = userdata;
1931 LIST_PREPEND(enumerators, n->enumerators, c);
1933 bus->nodes_modified = true;
1939 bus_node_gc(bus, n);
1943 _public_ int sd_bus_remove_node_enumerator(
1946 sd_bus_node_enumerator_t callback,
1949 struct node_enumerator *c;
1952 assert_return(bus, -EINVAL);
1953 assert_return(object_path_is_valid(path), -EINVAL);
1954 assert_return(callback, -EINVAL);
1955 assert_return(!bus_pid_changed(bus), -ECHILD);
1957 n = hashmap_get(bus->nodes, path);
1961 LIST_FOREACH(enumerators, c, n->enumerators)
1962 if (c->callback == callback && c->userdata == userdata)
1968 LIST_REMOVE(enumerators, n->enumerators, c);
1971 bus_node_gc(bus, n);
1973 bus->nodes_modified = true;
1978 static int emit_properties_changed_on_interface(
1982 const char *interface,
1983 bool require_fallback,
1984 bool *found_interface,
1987 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1988 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1989 bool has_invalidating = false, has_changing = false;
1990 struct vtable_member key = {};
1991 struct node_vtable *c;
2001 assert(found_interface);
2003 n = hashmap_get(bus->nodes, prefix);
2007 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
2011 r = sd_bus_message_append(m, "s", interface);
2015 r = sd_bus_message_open_container(m, 'a', "{sv}");
2020 key.interface = interface;
2022 LIST_FOREACH(vtables, c, n->vtables) {
2023 if (require_fallback && !c->is_fallback)
2026 if (!streq(c->interface, interface))
2029 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2032 if (bus->nodes_modified)
2037 *found_interface = true;
2040 /* If the caller specified a list of
2041 * properties we include exactly those in the
2042 * PropertiesChanged message */
2044 STRV_FOREACH(property, names) {
2045 struct vtable_member *v;
2047 assert_return(member_name_is_valid(*property), -EINVAL);
2049 key.member = *property;
2050 v = hashmap_get(bus->vtable_properties, &key);
2054 /* If there are two vtables for the same
2055 * interface, let's handle this property when
2056 * we come to that vtable. */
2060 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
2061 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
2063 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
2065 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2066 has_invalidating = true;
2070 has_changing = true;
2072 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
2075 if (bus->nodes_modified)
2079 const sd_bus_vtable *v;
2081 /* If the caller specified no properties list
2082 * we include all properties that are marked
2083 * as changing in the message. */
2085 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2086 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2089 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2092 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2093 has_invalidating = true;
2097 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2100 has_changing = true;
2102 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2105 if (bus->nodes_modified)
2111 if (!has_invalidating && !has_changing)
2114 r = sd_bus_message_close_container(m);
2118 r = sd_bus_message_open_container(m, 'a', "s");
2122 if (has_invalidating) {
2123 LIST_FOREACH(vtables, c, n->vtables) {
2124 if (require_fallback && !c->is_fallback)
2127 if (!streq(c->interface, interface))
2130 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2133 if (bus->nodes_modified)
2139 STRV_FOREACH(property, names) {
2140 struct vtable_member *v;
2142 key.member = *property;
2143 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2144 assert(c == v->parent);
2146 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2149 r = sd_bus_message_append(m, "s", *property);
2154 const sd_bus_vtable *v;
2156 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2157 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2160 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2163 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2166 r = sd_bus_message_append(m, "s", v->x.property.member);
2174 r = sd_bus_message_close_container(m);
2178 r = sd_bus_send(bus, m, NULL);
2185 _public_ int sd_bus_emit_properties_changed_strv(
2188 const char *interface,
2191 BUS_DONT_DESTROY(bus);
2192 bool found_interface = false;
2196 assert_return(bus, -EINVAL);
2197 assert_return(object_path_is_valid(path), -EINVAL);
2198 assert_return(interface_name_is_valid(interface), -EINVAL);
2199 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2200 assert_return(!bus_pid_changed(bus), -ECHILD);
2203 /* A non-NULL but empty names list means nothing needs to be
2204 generated. A NULL list OTOH indicates that all properties
2205 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2206 included in the PropertiesChanged message. */
2207 if (names && names[0] == NULL)
2211 bus->nodes_modified = false;
2213 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2216 if (bus->nodes_modified)
2219 prefix = alloca(strlen(path) + 1);
2220 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2221 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2224 if (bus->nodes_modified)
2228 } while (bus->nodes_modified);
2230 return found_interface ? 0 : -ENOENT;
2233 _public_ int sd_bus_emit_properties_changed(
2236 const char *interface,
2237 const char *name, ...) {
2241 assert_return(bus, -EINVAL);
2242 assert_return(object_path_is_valid(path), -EINVAL);
2243 assert_return(interface_name_is_valid(interface), -EINVAL);
2244 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2245 assert_return(!bus_pid_changed(bus), -ECHILD);
2250 names = strv_from_stdarg_alloca(name);
2252 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2255 static int interfaces_added_append_one_prefix(
2260 const char *interface,
2261 bool require_fallback) {
2263 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2264 bool found_interface = false;
2265 struct node_vtable *c;
2276 n = hashmap_get(bus->nodes, prefix);
2280 LIST_FOREACH(vtables, c, n->vtables) {
2281 if (require_fallback && !c->is_fallback)
2284 if (!streq(c->interface, interface))
2287 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2290 if (bus->nodes_modified)
2295 if (!found_interface) {
2296 r = sd_bus_message_append_basic(m, 's', interface);
2300 r = sd_bus_message_open_container(m, 'a', "{sv}");
2304 found_interface = true;
2307 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2310 if (bus->nodes_modified)
2314 if (found_interface) {
2315 r = sd_bus_message_close_container(m);
2320 return found_interface;
2323 static int interfaces_added_append_one(
2327 const char *interface) {
2337 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2340 if (bus->nodes_modified)
2343 prefix = alloca(strlen(path) + 1);
2344 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2345 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2348 if (bus->nodes_modified)
2355 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2356 BUS_DONT_DESTROY(bus);
2358 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2362 assert_return(bus, -EINVAL);
2363 assert_return(object_path_is_valid(path), -EINVAL);
2364 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2365 assert_return(!bus_pid_changed(bus), -ECHILD);
2367 if (strv_isempty(interfaces))
2371 bus->nodes_modified = false;
2374 m = sd_bus_message_unref(m);
2376 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2380 r = sd_bus_message_append_basic(m, 'o', path);
2384 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2388 STRV_FOREACH(i, interfaces) {
2389 assert_return(interface_name_is_valid(*i), -EINVAL);
2391 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2395 r = interfaces_added_append_one(bus, m, path, *i);
2399 if (bus->nodes_modified)
2402 r = sd_bus_message_close_container(m);
2407 if (bus->nodes_modified)
2410 r = sd_bus_message_close_container(m);
2414 } while (bus->nodes_modified);
2416 return sd_bus_send(bus, m, NULL);
2419 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2422 assert_return(bus, -EINVAL);
2423 assert_return(object_path_is_valid(path), -EINVAL);
2424 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2425 assert_return(!bus_pid_changed(bus), -ECHILD);
2427 interfaces = strv_from_stdarg_alloca(interface);
2429 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2432 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2433 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2436 assert_return(bus, -EINVAL);
2437 assert_return(object_path_is_valid(path), -EINVAL);
2438 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2439 assert_return(!bus_pid_changed(bus), -ECHILD);
2441 if (strv_isempty(interfaces))
2444 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2448 r = sd_bus_message_append_basic(m, 'o', path);
2452 r = sd_bus_message_append_strv(m, interfaces);
2456 return sd_bus_send(bus, m, NULL);
2459 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2462 assert_return(bus, -EINVAL);
2463 assert_return(object_path_is_valid(path), -EINVAL);
2464 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2465 assert_return(!bus_pid_changed(bus), -ECHILD);
2467 interfaces = strv_from_stdarg_alloca(interface);
2469 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2472 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2475 assert_return(bus, -EINVAL);
2476 assert_return(object_path_is_valid(path), -EINVAL);
2477 assert_return(!bus_pid_changed(bus), -ECHILD);
2479 n = bus_node_allocate(bus, path);
2483 n->object_manager = true;
2484 bus->nodes_modified = true;
2488 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2491 assert_return(bus, -EINVAL);
2492 assert_return(object_path_is_valid(path), -EINVAL);
2493 assert_return(!bus_pid_changed(bus), -ECHILD);
2495 n = hashmap_get(bus->nodes, path);
2499 if (!n->object_manager)
2502 n->object_manager = false;
2503 bus->nodes_modified = true;
2504 bus_node_gc(bus, n);