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_method_convert_userdata(const sd_bus_vtable *p, void *u) {
74 return (uint8_t*) u + p->x.method.offset;
77 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
80 return (uint8_t*) u + p->x.property.offset;
83 static int vtable_property_get_userdata(
86 struct vtable_member *p,
88 sd_bus_error *error) {
98 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
101 if (bus->nodes_modified)
104 *userdata = vtable_property_convert_userdata(p->vtable, u);
108 static int add_enumerated_to_set(
111 struct node_enumerator *first,
113 sd_bus_error *error) {
115 struct node_enumerator *c;
122 LIST_FOREACH(enumerators, c, first) {
123 char **children = NULL, **k;
126 if (bus->nodes_modified)
129 slot = container_of(c, sd_bus_slot, node_enumerator);
131 bus->current_slot = sd_bus_slot_ref(slot);
132 bus->current_userdata = slot->userdata;
133 r = c->callback(bus, prefix, slot->userdata, &children, error);
134 bus->current_userdata = NULL;
135 bus->current_slot = sd_bus_slot_unref(slot);
139 if (sd_bus_error_is_set(error))
140 return -sd_bus_error_get_errno(error);
142 STRV_FOREACH(k, children) {
148 if (!object_path_is_valid(*k)){
154 if (!object_path_startswith(*k, prefix)) {
159 r = set_consume(s, *k);
173 /* if set, add_subtree() works recursively */
174 CHILDREN_RECURSIVE = (1U << 1),
175 /* if set, add_subtree() scans object-manager hierarchies recursively */
176 CHILDREN_SUBHIERARCHIES = (1U << 0),
179 static int add_subtree_to_set(
185 sd_bus_error *error) {
195 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
198 if (bus->nodes_modified)
201 LIST_FOREACH(siblings, i, n->child) {
204 if (!object_path_startswith(i->path, prefix))
211 r = set_consume(s, t);
212 if (r < 0 && r != -EEXIST)
215 if ((flags & CHILDREN_RECURSIVE) &&
216 ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) {
217 r = add_subtree_to_set(bus, prefix, i, flags, s, error);
220 if (bus->nodes_modified)
228 static int get_child_nodes(
234 sd_bus_error *error) {
244 s = set_new(&string_hash_ops);
248 r = add_subtree_to_set(bus, prefix, n, flags, s, error);
258 static int node_callbacks_run(
261 struct node_callback *first,
262 bool require_fallback,
263 bool *found_object) {
265 struct node_callback *c;
270 assert(found_object);
272 LIST_FOREACH(callbacks, c, first) {
273 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
276 if (bus->nodes_modified)
279 if (require_fallback && !c->is_fallback)
282 *found_object = true;
284 if (c->last_iteration == bus->iteration_counter)
287 c->last_iteration = bus->iteration_counter;
289 r = sd_bus_message_rewind(m, true);
293 slot = container_of(c, sd_bus_slot, node_callback);
295 bus->current_slot = sd_bus_slot_ref(slot);
296 bus->current_handler = c->callback;
297 bus->current_userdata = slot->userdata;
298 r = c->callback(m, slot->userdata, &error_buffer);
299 bus->current_userdata = NULL;
300 bus->current_handler = NULL;
301 bus->current_slot = sd_bus_slot_unref(slot);
303 r = bus_maybe_reply_error(m, r, &error_buffer);
311 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
313 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
321 /* If the entire bus is trusted let's grant access */
325 /* If the member is marked UNPRIVILEGED let's grant access */
326 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
329 /* Check have the caller has the requested capability
330 * set. Note that the flags value contains the capability
331 * number plus one, which we need to subtract here. We do this
332 * so that we have 0 as special value for "default
334 cap = CAPABILITY_SHIFT(c->vtable->flags);
336 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
342 r = sd_bus_query_sender_privilege(m, cap);
348 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
351 static int method_callbacks_run(
354 struct vtable_member *c,
355 bool require_fallback,
356 bool *found_object) {
358 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
359 const char *signature;
366 assert(found_object);
368 if (require_fallback && !c->parent->is_fallback)
371 r = check_access(bus, m, c, &error);
373 return bus_maybe_reply_error(m, r, &error);
375 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
377 return bus_maybe_reply_error(m, r, &error);
378 if (bus->nodes_modified)
381 u = vtable_method_convert_userdata(c->vtable, u);
383 *found_object = true;
385 if (c->last_iteration == bus->iteration_counter)
388 c->last_iteration = bus->iteration_counter;
390 r = sd_bus_message_rewind(m, true);
394 signature = sd_bus_message_get_signature(m, true);
398 if (!streq(strempty(c->vtable->x.method.signature), signature))
399 return sd_bus_reply_method_errorf(
401 SD_BUS_ERROR_INVALID_ARGS,
402 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
403 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
405 /* Keep track what the signature of the reply to this message
406 * should be, so that this can be enforced when sealing the
408 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
410 if (c->vtable->x.method.handler) {
413 slot = container_of(c->parent, sd_bus_slot, node_vtable);
415 bus->current_slot = sd_bus_slot_ref(slot);
416 bus->current_handler = c->vtable->x.method.handler;
417 bus->current_userdata = u;
418 r = c->vtable->x.method.handler(m, u, &error);
419 bus->current_userdata = NULL;
420 bus->current_handler = NULL;
421 bus->current_slot = sd_bus_slot_unref(slot);
423 return bus_maybe_reply_error(m, r, &error);
426 /* If the method callback is NULL, make this a successful NOP */
427 r = sd_bus_reply_method_return(m, NULL);
434 static int invoke_property_get(
437 const sd_bus_vtable *v,
439 const char *interface,
440 const char *property,
441 sd_bus_message *reply,
443 sd_bus_error *error) {
456 if (v->x.property.get) {
458 bus->current_slot = sd_bus_slot_ref(slot);
459 bus->current_userdata = userdata;
460 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
461 bus->current_userdata = NULL;
462 bus->current_slot = sd_bus_slot_unref(slot);
466 if (sd_bus_error_is_set(error))
467 return -sd_bus_error_get_errno(error);
471 /* Automatic handling if no callback is defined. */
473 if (streq(v->x.property.signature, "as"))
474 return sd_bus_message_append_strv(reply, *(char***) userdata);
476 assert(signature_is_single(v->x.property.signature, false));
477 assert(bus_type_is_basic(v->x.property.signature[0]));
479 switch (v->x.property.signature[0]) {
481 case SD_BUS_TYPE_STRING:
482 case SD_BUS_TYPE_SIGNATURE:
483 p = strempty(*(char**) userdata);
486 case SD_BUS_TYPE_OBJECT_PATH:
487 p = *(char**) userdata;
496 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
499 static int invoke_property_set(
502 const sd_bus_vtable *v,
504 const char *interface,
505 const char *property,
506 sd_bus_message *value,
508 sd_bus_error *error) {
520 if (v->x.property.set) {
522 bus->current_slot = sd_bus_slot_ref(slot);
523 bus->current_userdata = userdata;
524 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
525 bus->current_userdata = NULL;
526 bus->current_slot = sd_bus_slot_unref(slot);
530 if (sd_bus_error_is_set(error))
531 return -sd_bus_error_get_errno(error);
535 /* Automatic handling if no callback is defined. */
537 assert(signature_is_single(v->x.property.signature, false));
538 assert(bus_type_is_basic(v->x.property.signature[0]));
540 switch (v->x.property.signature[0]) {
542 case SD_BUS_TYPE_STRING:
543 case SD_BUS_TYPE_OBJECT_PATH:
544 case SD_BUS_TYPE_SIGNATURE: {
548 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
556 free(*(char**) userdata);
557 *(char**) userdata = n;
563 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
573 static int property_get_set_callbacks_run(
576 struct vtable_member *c,
577 bool require_fallback,
579 bool *found_object) {
581 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
582 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
590 assert(found_object);
592 if (require_fallback && !c->parent->is_fallback)
595 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
597 return bus_maybe_reply_error(m, r, &error);
598 if (bus->nodes_modified)
601 slot = container_of(c->parent, sd_bus_slot, node_vtable);
603 *found_object = true;
605 r = sd_bus_message_new_method_return(m, &reply);
610 /* Note that we do not protect against reexecution
611 * here (using the last_iteration check, see below),
612 * should the node tree have changed and we got called
613 * again. We assume that property Get() calls are
614 * ultimately without side-effects or if they aren't
615 * then at least idempotent. */
617 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
621 /* Note that we do not do an access check here. Read
622 * access to properties is always unrestricted, since
623 * PropertiesChanged signals broadcast contents
626 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
628 return bus_maybe_reply_error(m, r, &error);
630 if (bus->nodes_modified)
633 r = sd_bus_message_close_container(reply);
638 const char *signature = NULL;
641 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
642 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
644 /* Avoid that we call the set routine more than once
645 * if the processing of this message got restarted
646 * because the node tree changed. */
647 if (c->last_iteration == bus->iteration_counter)
650 c->last_iteration = bus->iteration_counter;
652 r = sd_bus_message_peek_type(m, &type, &signature);
656 if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
657 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));
659 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
663 r = check_access(bus, m, c, &error);
665 return bus_maybe_reply_error(m, r, &error);
667 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
669 return bus_maybe_reply_error(m, r, &error);
671 if (bus->nodes_modified)
674 r = sd_bus_message_exit_container(m);
679 r = sd_bus_send(bus, reply, NULL);
686 static int vtable_append_one_property(
688 sd_bus_message *reply,
690 struct node_vtable *c,
691 const sd_bus_vtable *v,
693 sd_bus_error *error) {
704 r = sd_bus_message_open_container(reply, 'e', "sv");
708 r = sd_bus_message_append(reply, "s", v->x.property.member);
712 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
716 slot = container_of(c, sd_bus_slot, node_vtable);
718 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
721 if (bus->nodes_modified)
724 r = sd_bus_message_close_container(reply);
728 r = sd_bus_message_close_container(reply);
735 static int vtable_append_all_properties(
737 sd_bus_message *reply,
739 struct node_vtable *c,
741 sd_bus_error *error) {
743 const sd_bus_vtable *v;
751 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
754 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
755 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
758 if (v->flags & SD_BUS_VTABLE_HIDDEN)
761 if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
764 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
767 if (bus->nodes_modified)
774 static int property_get_all_callbacks_run(
777 struct node_vtable *first,
778 bool require_fallback,
780 bool *found_object) {
782 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
783 struct node_vtable *c;
784 bool found_interface;
789 assert(found_object);
791 r = sd_bus_message_new_method_return(m, &reply);
795 r = sd_bus_message_open_container(reply, 'a', "{sv}");
799 found_interface = !iface ||
800 streq(iface, "org.freedesktop.DBus.Properties") ||
801 streq(iface, "org.freedesktop.DBus.Peer") ||
802 streq(iface, "org.freedesktop.DBus.Introspectable");
804 LIST_FOREACH(vtables, c, first) {
805 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
808 if (require_fallback && !c->is_fallback)
811 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
813 return bus_maybe_reply_error(m, r, &error);
814 if (bus->nodes_modified)
819 *found_object = true;
821 if (iface && !streq(c->interface, iface))
823 found_interface = true;
825 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
827 return bus_maybe_reply_error(m, r, &error);
828 if (bus->nodes_modified)
832 if (!found_interface) {
833 r = sd_bus_reply_method_errorf(
835 SD_BUS_ERROR_UNKNOWN_INTERFACE,
836 "Unknown interface '%s'.", iface);
843 r = sd_bus_message_close_container(reply);
847 r = sd_bus_send(bus, reply, NULL);
854 static int bus_node_exists(
858 bool require_fallback) {
860 struct node_vtable *c;
861 struct node_callback *k;
868 /* Tests if there's anything attached directly to this node
869 * for the specified path */
871 if (!require_fallback && (n->enumerators || n->object_managers))
874 LIST_FOREACH(callbacks, k, n->callbacks) {
875 if (require_fallback && !k->is_fallback)
881 LIST_FOREACH(vtables, c, n->vtables) {
882 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
884 if (require_fallback && !c->is_fallback)
887 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
890 if (bus->nodes_modified)
897 static int process_introspect(
901 bool require_fallback,
902 bool *found_object) {
904 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
905 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
906 _cleanup_set_free_free_ Set *s = NULL;
907 const char *previous_interface = NULL;
908 struct introspect intro;
909 struct node_vtable *c;
916 assert(found_object);
918 r = get_child_nodes(bus, m->path, n, 0, &s, &error);
920 return bus_maybe_reply_error(m, r, &error);
921 if (bus->nodes_modified)
924 r = introspect_begin(&intro, bus->trusted);
928 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
932 empty = set_isempty(s);
934 LIST_FOREACH(vtables, c, n->vtables) {
935 if (require_fallback && !c->is_fallback)
938 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
940 r = bus_maybe_reply_error(m, r, &error);
943 if (bus->nodes_modified) {
952 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
955 if (!streq_ptr(previous_interface, c->interface)) {
957 if (previous_interface)
958 fputs(" </interface>\n", intro.f);
960 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
963 r = introspect_write_interface(&intro, c->vtable);
967 previous_interface = c->interface;
970 if (previous_interface)
971 fputs(" </interface>\n", intro.f);
974 /* Nothing?, let's see if we exist at all, and if not
975 * refuse to do anything */
976 r = bus_node_exists(bus, n, m->path, require_fallback);
979 if (bus->nodes_modified) {
985 *found_object = true;
987 r = introspect_write_child_nodes(&intro, s, m->path);
991 r = introspect_finish(&intro, bus, m, &reply);
995 r = sd_bus_send(bus, reply, NULL);
1002 introspect_free(&intro);
1006 static int object_manager_serialize_path(
1008 sd_bus_message *reply,
1011 bool require_fallback,
1012 sd_bus_error *error) {
1014 const char *previous_interface = NULL;
1015 bool found_something = false;
1016 struct node_vtable *i;
1026 n = hashmap_get(bus->nodes, prefix);
1030 LIST_FOREACH(vtables, i, n->vtables) {
1033 if (require_fallback && !i->is_fallback)
1036 r = node_vtable_get_userdata(bus, path, i, &u, error);
1039 if (bus->nodes_modified)
1044 if (!found_something) {
1046 /* Open the object part */
1048 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1052 r = sd_bus_message_append(reply, "o", path);
1056 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1060 found_something = true;
1063 if (!streq_ptr(previous_interface, i->interface)) {
1065 /* Maybe close the previous interface part */
1067 if (previous_interface) {
1068 r = sd_bus_message_close_container(reply);
1072 r = sd_bus_message_close_container(reply);
1077 /* Open the new interface part */
1079 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1083 r = sd_bus_message_append(reply, "s", i->interface);
1087 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1092 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1095 if (bus->nodes_modified)
1098 previous_interface = i->interface;
1101 if (previous_interface) {
1102 r = sd_bus_message_close_container(reply);
1106 r = sd_bus_message_close_container(reply);
1111 if (found_something) {
1112 r = sd_bus_message_close_container(reply);
1116 r = sd_bus_message_close_container(reply);
1124 static int object_manager_serialize_path_and_fallbacks(
1126 sd_bus_message *reply,
1128 sd_bus_error *error) {
1138 /* First, add all vtables registered for this path */
1139 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1142 if (bus->nodes_modified)
1145 /* Second, add fallback vtables registered for any of the prefixes */
1146 prefix = alloca(strlen(path) + 1);
1147 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1148 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1151 if (bus->nodes_modified)
1158 static int process_get_managed_objects(
1162 bool require_fallback,
1163 bool *found_object) {
1165 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1166 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1167 _cleanup_set_free_free_ Set *s = NULL;
1175 assert(found_object);
1177 /* Spec says, GetManagedObjects() is only implemented on the root of a
1178 * sub-tree. Therefore, we require a registered object-manager on
1179 * exactly the queried path, otherwise, we refuse to respond. */
1181 if (require_fallback || !n->object_managers)
1184 r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
1187 if (bus->nodes_modified)
1190 r = sd_bus_message_new_method_return(m, &reply);
1194 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1198 SET_FOREACH(path, s, i) {
1199 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1203 if (bus->nodes_modified)
1207 r = sd_bus_message_close_container(reply);
1211 r = sd_bus_send(bus, reply, NULL);
1218 static int object_find_and_run(
1222 bool require_fallback,
1223 bool *found_object) {
1226 struct vtable_member vtable_key, *v;
1232 assert(found_object);
1234 n = hashmap_get(bus->nodes, p);
1238 /* First, try object callbacks */
1239 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1242 if (bus->nodes_modified)
1245 if (!m->interface || !m->member)
1248 /* Then, look for a known method */
1249 vtable_key.path = (char*) p;
1250 vtable_key.interface = m->interface;
1251 vtable_key.member = m->member;
1253 v = hashmap_get(bus->vtable_methods, &vtable_key);
1255 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1258 if (bus->nodes_modified)
1262 /* Then, look for a known property */
1263 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1266 get = streq(m->member, "Get");
1268 if (get || streq(m->member, "Set")) {
1270 r = sd_bus_message_rewind(m, true);
1274 vtable_key.path = (char*) p;
1276 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1278 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1280 v = hashmap_get(bus->vtable_properties, &vtable_key);
1282 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1287 } else if (streq(m->member, "GetAll")) {
1290 r = sd_bus_message_rewind(m, true);
1294 r = sd_bus_message_read(m, "s", &iface);
1296 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1301 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1306 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1308 if (!isempty(sd_bus_message_get_signature(m, true)))
1309 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1311 r = process_introspect(bus, m, n, require_fallback, found_object);
1315 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1317 if (!isempty(sd_bus_message_get_signature(m, true)))
1318 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1320 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1325 if (bus->nodes_modified)
1328 if (!*found_object) {
1329 r = bus_node_exists(bus, n, m->path, require_fallback);
1332 if (bus->nodes_modified)
1335 *found_object = true;
1341 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1344 bool found_object = false;
1349 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1352 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1355 if (hashmap_isempty(bus->nodes))
1358 /* Never respond to broadcast messages */
1359 if (bus->bus_client && !m->destination)
1365 pl = strlen(m->path);
1369 bus->nodes_modified = false;
1371 r = object_find_and_run(bus, m, m->path, false, &found_object);
1375 /* Look for fallback prefixes */
1376 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1378 if (bus->nodes_modified)
1381 r = object_find_and_run(bus, m, prefix, true, &found_object);
1386 } while (bus->nodes_modified);
1391 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1392 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1393 r = sd_bus_reply_method_errorf(
1395 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1396 "Unknown property or interface.");
1398 r = sd_bus_reply_method_errorf(
1400 SD_BUS_ERROR_UNKNOWN_METHOD,
1401 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1409 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1410 struct node *n, *parent;
1412 _cleanup_free_ char *s = NULL;
1418 assert(path[0] == '/');
1420 n = hashmap_get(bus->nodes, path);
1424 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1432 if (streq(path, "/"))
1435 e = strrchr(path, '/');
1438 p = strndupa(path, MAX(1, e - path));
1440 parent = bus_node_allocate(bus, p);
1445 n = new0(struct node, 1);
1451 s = NULL; /* do not free */
1453 r = hashmap_put(bus->nodes, n->path, n);
1461 LIST_PREPEND(siblings, parent->child, n);
1466 void bus_node_gc(sd_bus *b, struct node *n) {
1479 assert(hashmap_remove(b->nodes, n->path) == n);
1482 LIST_REMOVE(siblings, n->parent->child, n);
1485 bus_node_gc(b, n->parent);
1489 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1495 n = hashmap_get(bus->nodes, path);
1499 prefix = alloca(strlen(path) + 1);
1500 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1501 n = hashmap_get(bus->nodes, prefix);
1507 while (n && !n->object_managers)
1515 static int bus_add_object(
1520 sd_bus_message_handler_t callback,
1527 assert_return(bus, -EINVAL);
1528 assert_return(object_path_is_valid(path), -EINVAL);
1529 assert_return(callback, -EINVAL);
1530 assert_return(!bus_pid_changed(bus), -ECHILD);
1532 n = bus_node_allocate(bus, path);
1536 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1542 s->node_callback.callback = callback;
1543 s->node_callback.is_fallback = fallback;
1545 s->node_callback.node = n;
1546 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1547 bus->nodes_modified = true;
1555 sd_bus_slot_unref(s);
1556 bus_node_gc(bus, n);
1561 _public_ int sd_bus_add_object(
1565 sd_bus_message_handler_t callback,
1568 return bus_add_object(bus, slot, false, path, callback, userdata);
1571 _public_ int sd_bus_add_fallback(
1575 sd_bus_message_handler_t callback,
1578 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1581 static void vtable_member_hash_func(const void *a, struct siphash *state) {
1582 const struct vtable_member *m = a;
1586 string_hash_func(m->path, state);
1587 string_hash_func(m->interface, state);
1588 string_hash_func(m->member, state);
1591 static int vtable_member_compare_func(const void *a, const void *b) {
1592 const struct vtable_member *x = a, *y = b;
1598 r = strcmp(x->path, y->path);
1602 r = strcmp(x->interface, y->interface);
1606 return strcmp(x->member, y->member);
1609 static const struct hash_ops vtable_member_hash_ops = {
1610 .hash = vtable_member_hash_func,
1611 .compare = vtable_member_compare_func
1614 static int add_object_vtable_internal(
1618 const char *interface,
1619 const sd_bus_vtable *vtable,
1621 sd_bus_object_find_t find,
1624 sd_bus_slot *s = NULL;
1625 struct node_vtable *i, *existing = NULL;
1626 const sd_bus_vtable *v;
1630 assert_return(bus, -EINVAL);
1631 assert_return(object_path_is_valid(path), -EINVAL);
1632 assert_return(interface_name_is_valid(interface), -EINVAL);
1633 assert_return(vtable, -EINVAL);
1634 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1635 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1636 assert_return(!bus_pid_changed(bus), -ECHILD);
1637 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1638 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1639 !streq(interface, "org.freedesktop.DBus.Peer") &&
1640 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1642 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1646 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1650 n = bus_node_allocate(bus, path);
1654 LIST_FOREACH(vtables, i, n->vtables) {
1655 if (i->is_fallback != fallback) {
1660 if (streq(i->interface, interface)) {
1662 if (i->vtable == vtable) {
1671 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1677 s->node_vtable.is_fallback = fallback;
1678 s->node_vtable.vtable = vtable;
1679 s->node_vtable.find = find;
1681 s->node_vtable.interface = strdup(interface);
1682 if (!s->node_vtable.interface) {
1687 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1691 case _SD_BUS_VTABLE_METHOD: {
1692 struct vtable_member *m;
1694 if (!member_name_is_valid(v->x.method.member) ||
1695 !signature_is_valid(strempty(v->x.method.signature), false) ||
1696 !signature_is_valid(strempty(v->x.method.result), false) ||
1697 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1698 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1703 m = new0(struct vtable_member, 1);
1709 m->parent = &s->node_vtable;
1711 m->interface = s->node_vtable.interface;
1712 m->member = v->x.method.member;
1715 r = hashmap_put(bus->vtable_methods, m, m);
1724 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1726 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1731 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1738 case _SD_BUS_VTABLE_PROPERTY: {
1739 struct vtable_member *m;
1741 if (!member_name_is_valid(v->x.property.member) ||
1742 !signature_is_single(v->x.property.signature, false) ||
1743 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1744 (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1745 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1746 ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1747 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1752 m = new0(struct vtable_member, 1);
1758 m->parent = &s->node_vtable;
1760 m->interface = s->node_vtable.interface;
1761 m->member = v->x.property.member;
1764 r = hashmap_put(bus->vtable_properties, m, m);
1773 case _SD_BUS_VTABLE_SIGNAL:
1775 if (!member_name_is_valid(v->x.signal.member) ||
1776 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1777 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1790 s->node_vtable.node = n;
1791 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1792 bus->nodes_modified = true;
1800 sd_bus_slot_unref(s);
1801 bus_node_gc(bus, n);
1806 _public_ int sd_bus_add_object_vtable(
1810 const char *interface,
1811 const sd_bus_vtable *vtable,
1814 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1817 _public_ int sd_bus_add_fallback_vtable(
1821 const char *interface,
1822 const sd_bus_vtable *vtable,
1823 sd_bus_object_find_t find,
1826 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1829 _public_ int sd_bus_add_node_enumerator(
1833 sd_bus_node_enumerator_t callback,
1840 assert_return(bus, -EINVAL);
1841 assert_return(object_path_is_valid(path), -EINVAL);
1842 assert_return(callback, -EINVAL);
1843 assert_return(!bus_pid_changed(bus), -ECHILD);
1845 n = bus_node_allocate(bus, path);
1849 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1855 s->node_enumerator.callback = callback;
1857 s->node_enumerator.node = n;
1858 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1859 bus->nodes_modified = true;
1867 sd_bus_slot_unref(s);
1868 bus_node_gc(bus, n);
1873 static int emit_properties_changed_on_interface(
1877 const char *interface,
1878 bool require_fallback,
1879 bool *found_interface,
1882 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1883 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1884 bool has_invalidating = false, has_changing = false;
1885 struct vtable_member key = {};
1886 struct node_vtable *c;
1896 assert(found_interface);
1898 n = hashmap_get(bus->nodes, prefix);
1902 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1906 r = sd_bus_message_append(m, "s", interface);
1910 r = sd_bus_message_open_container(m, 'a', "{sv}");
1915 key.interface = interface;
1917 LIST_FOREACH(vtables, c, n->vtables) {
1918 if (require_fallback && !c->is_fallback)
1921 if (!streq(c->interface, interface))
1924 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1927 if (bus->nodes_modified)
1932 *found_interface = true;
1935 /* If the caller specified a list of
1936 * properties we include exactly those in the
1937 * PropertiesChanged message */
1939 STRV_FOREACH(property, names) {
1940 struct vtable_member *v;
1942 assert_return(member_name_is_valid(*property), -EINVAL);
1944 key.member = *property;
1945 v = hashmap_get(bus->vtable_properties, &key);
1949 /* If there are two vtables for the same
1950 * interface, let's handle this property when
1951 * we come to that vtable. */
1955 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1956 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1958 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1960 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1961 has_invalidating = true;
1965 has_changing = true;
1967 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1970 if (bus->nodes_modified)
1974 const sd_bus_vtable *v;
1976 /* If the caller specified no properties list
1977 * we include all properties that are marked
1978 * as changing in the message. */
1980 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1981 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1984 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1987 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1988 has_invalidating = true;
1992 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1995 has_changing = true;
1997 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2000 if (bus->nodes_modified)
2006 if (!has_invalidating && !has_changing)
2009 r = sd_bus_message_close_container(m);
2013 r = sd_bus_message_open_container(m, 'a', "s");
2017 if (has_invalidating) {
2018 LIST_FOREACH(vtables, c, n->vtables) {
2019 if (require_fallback && !c->is_fallback)
2022 if (!streq(c->interface, interface))
2025 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2028 if (bus->nodes_modified)
2034 STRV_FOREACH(property, names) {
2035 struct vtable_member *v;
2037 key.member = *property;
2038 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2039 assert(c == v->parent);
2041 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2044 r = sd_bus_message_append(m, "s", *property);
2049 const sd_bus_vtable *v;
2051 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2052 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2055 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2058 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2061 r = sd_bus_message_append(m, "s", v->x.property.member);
2069 r = sd_bus_message_close_container(m);
2073 r = sd_bus_send(bus, m, NULL);
2080 _public_ int sd_bus_emit_properties_changed_strv(
2083 const char *interface,
2086 BUS_DONT_DESTROY(bus);
2087 bool found_interface = false;
2091 assert_return(bus, -EINVAL);
2092 assert_return(object_path_is_valid(path), -EINVAL);
2093 assert_return(interface_name_is_valid(interface), -EINVAL);
2094 assert_return(!bus_pid_changed(bus), -ECHILD);
2096 if (!BUS_IS_OPEN(bus->state))
2099 /* A non-NULL but empty names list means nothing needs to be
2100 generated. A NULL list OTOH indicates that all properties
2101 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2102 included in the PropertiesChanged message. */
2103 if (names && names[0] == NULL)
2107 bus->nodes_modified = false;
2109 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2112 if (bus->nodes_modified)
2115 prefix = alloca(strlen(path) + 1);
2116 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2117 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2120 if (bus->nodes_modified)
2124 } while (bus->nodes_modified);
2126 return found_interface ? 0 : -ENOENT;
2129 _public_ int sd_bus_emit_properties_changed(
2132 const char *interface,
2133 const char *name, ...) {
2137 assert_return(bus, -EINVAL);
2138 assert_return(object_path_is_valid(path), -EINVAL);
2139 assert_return(interface_name_is_valid(interface), -EINVAL);
2140 assert_return(!bus_pid_changed(bus), -ECHILD);
2142 if (!BUS_IS_OPEN(bus->state))
2148 names = strv_from_stdarg_alloca(name);
2150 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2153 static int object_added_append_all_prefix(
2159 bool require_fallback) {
2161 const char *previous_interface = NULL;
2162 struct node_vtable *c;
2172 n = hashmap_get(bus->nodes, prefix);
2176 LIST_FOREACH(vtables, c, n->vtables) {
2177 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2180 if (require_fallback && !c->is_fallback)
2183 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2186 if (bus->nodes_modified)
2191 if (!streq_ptr(c->interface, previous_interface)) {
2192 /* If a child-node already handled this interface, we
2193 * skip it on any of its parents. The child vtables
2194 * always fully override any conflicting vtables of
2195 * any parent node. */
2196 if (set_get(s, c->interface))
2199 r = set_put(s, c->interface);
2203 if (previous_interface) {
2204 r = sd_bus_message_close_container(m);
2207 r = sd_bus_message_close_container(m);
2212 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2215 r = sd_bus_message_append(m, "s", c->interface);
2218 r = sd_bus_message_open_container(m, 'a', "{sv}");
2222 previous_interface = c->interface;
2225 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2228 if (bus->nodes_modified)
2232 if (previous_interface) {
2233 r = sd_bus_message_close_container(m);
2236 r = sd_bus_message_close_container(m);
2244 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2245 _cleanup_set_free_ Set *s = NULL;
2254 * This appends all interfaces registered on path @path. We first add
2255 * the builtin interfaces, which are always available and handled by
2256 * sd-bus. Then, we add all interfaces registered on the exact node,
2257 * followed by all fallback interfaces registered on any parent prefix.
2259 * If an interface is registered multiple times on the same node with
2260 * different vtables, we merge all the properties across all vtables.
2261 * However, if a child node has the same interface registered as one of
2262 * its parent nodes has as fallback, we make the child overwrite the
2263 * parent instead of extending it. Therefore, we keep a "Set" of all
2264 * handled interfaces during parent traversal, so we skip interfaces on
2265 * a parent that were overwritten by a child.
2268 s = set_new(&string_hash_ops);
2272 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2275 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2278 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2281 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2285 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2288 if (bus->nodes_modified)
2291 prefix = alloca(strlen(path) + 1);
2292 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2293 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2296 if (bus->nodes_modified)
2303 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2304 BUS_DONT_DESTROY(bus);
2306 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2307 struct node *object_manager;
2311 * This emits an InterfacesAdded signal on the given path, by iterating
2312 * all registered vtables and fallback vtables on the path. All
2313 * properties are queried and included in the signal.
2314 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2315 * explicit list of registered interfaces. However, unlike
2316 * interfaces_added(), this call can figure out the list of supported
2317 * interfaces itself. Furthermore, it properly adds the builtin
2318 * org.freedesktop.DBus.* interfaces.
2321 assert_return(bus, -EINVAL);
2322 assert_return(object_path_is_valid(path), -EINVAL);
2323 assert_return(!bus_pid_changed(bus), -ECHILD);
2325 if (!BUS_IS_OPEN(bus->state))
2328 r = bus_find_parent_object_manager(bus, &object_manager, path);
2335 bus->nodes_modified = false;
2336 m = sd_bus_message_unref(m);
2338 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2342 r = sd_bus_message_append_basic(m, 'o', path);
2346 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2350 r = object_added_append_all(bus, m, path);
2354 if (bus->nodes_modified)
2357 r = sd_bus_message_close_container(m);
2361 } while (bus->nodes_modified);
2363 return sd_bus_send(bus, m, NULL);
2366 /// UNNEEDED by elogind
2368 static int object_removed_append_all_prefix(
2374 bool require_fallback) {
2376 const char *previous_interface = NULL;
2377 struct node_vtable *c;
2387 n = hashmap_get(bus->nodes, prefix);
2391 LIST_FOREACH(vtables, c, n->vtables) {
2392 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2395 if (require_fallback && !c->is_fallback)
2397 if (streq_ptr(c->interface, previous_interface))
2400 /* If a child-node already handled this interface, we
2401 * skip it on any of its parents. The child vtables
2402 * always fully override any conflicting vtables of
2403 * any parent node. */
2404 if (set_get(s, c->interface))
2407 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2410 if (bus->nodes_modified)
2415 r = set_put(s, c->interface);
2419 r = sd_bus_message_append(m, "s", c->interface);
2423 previous_interface = c->interface;
2429 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2430 _cleanup_set_free_ Set *s = NULL;
2438 /* see sd_bus_emit_object_added() for details */
2440 s = set_new(&string_hash_ops);
2444 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2447 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2450 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2453 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2457 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2460 if (bus->nodes_modified)
2463 prefix = alloca(strlen(path) + 1);
2464 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2465 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2468 if (bus->nodes_modified)
2475 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2476 BUS_DONT_DESTROY(bus);
2478 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2479 struct node *object_manager;
2483 * This is like sd_bus_emit_object_added(), but emits an
2484 * InterfacesRemoved signal on the given path. This only includes any
2485 * registered interfaces but skips the properties. Note that this will
2486 * call into the find() callbacks of any registered vtable. Therefore,
2487 * you must call this function before destroying/unlinking your object.
2488 * Otherwise, the list of interfaces will be incomplete. However, note
2489 * that this will *NOT* call into any property callback. Therefore, the
2490 * object might be in an "destructed" state, as long as we can find it.
2493 assert_return(bus, -EINVAL);
2494 assert_return(object_path_is_valid(path), -EINVAL);
2495 assert_return(!bus_pid_changed(bus), -ECHILD);
2497 if (!BUS_IS_OPEN(bus->state))
2500 r = bus_find_parent_object_manager(bus, &object_manager, path);
2507 bus->nodes_modified = false;
2508 m = sd_bus_message_unref(m);
2510 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2514 r = sd_bus_message_append_basic(m, 'o', path);
2518 r = sd_bus_message_open_container(m, 'a', "s");
2522 r = object_removed_append_all(bus, m, path);
2526 if (bus->nodes_modified)
2529 r = sd_bus_message_close_container(m);
2533 } while (bus->nodes_modified);
2535 return sd_bus_send(bus, m, NULL);
2539 static int interfaces_added_append_one_prefix(
2544 const char *interface,
2545 bool require_fallback) {
2547 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2548 bool found_interface = false;
2549 struct node_vtable *c;
2560 n = hashmap_get(bus->nodes, prefix);
2564 LIST_FOREACH(vtables, c, n->vtables) {
2565 if (require_fallback && !c->is_fallback)
2568 if (!streq(c->interface, interface))
2571 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2574 if (bus->nodes_modified)
2579 if (!found_interface) {
2580 r = sd_bus_message_append_basic(m, 's', interface);
2584 r = sd_bus_message_open_container(m, 'a', "{sv}");
2588 found_interface = true;
2591 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2594 if (bus->nodes_modified)
2598 if (found_interface) {
2599 r = sd_bus_message_close_container(m);
2604 return found_interface;
2607 static int interfaces_added_append_one(
2611 const char *interface) {
2621 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2624 if (bus->nodes_modified)
2627 prefix = alloca(strlen(path) + 1);
2628 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2629 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2632 if (bus->nodes_modified)
2639 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2640 BUS_DONT_DESTROY(bus);
2642 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2643 struct node *object_manager;
2647 assert_return(bus, -EINVAL);
2648 assert_return(object_path_is_valid(path), -EINVAL);
2649 assert_return(!bus_pid_changed(bus), -ECHILD);
2651 if (!BUS_IS_OPEN(bus->state))
2654 if (strv_isempty(interfaces))
2657 r = bus_find_parent_object_manager(bus, &object_manager, path);
2664 bus->nodes_modified = false;
2665 m = sd_bus_message_unref(m);
2667 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2671 r = sd_bus_message_append_basic(m, 'o', path);
2675 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2679 STRV_FOREACH(i, interfaces) {
2680 assert_return(interface_name_is_valid(*i), -EINVAL);
2682 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2686 r = interfaces_added_append_one(bus, m, path, *i);
2690 if (bus->nodes_modified)
2693 r = sd_bus_message_close_container(m);
2698 if (bus->nodes_modified)
2701 r = sd_bus_message_close_container(m);
2705 } while (bus->nodes_modified);
2707 return sd_bus_send(bus, m, NULL);
2710 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2713 assert_return(bus, -EINVAL);
2714 assert_return(object_path_is_valid(path), -EINVAL);
2715 assert_return(!bus_pid_changed(bus), -ECHILD);
2717 if (!BUS_IS_OPEN(bus->state))
2720 interfaces = strv_from_stdarg_alloca(interface);
2722 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2725 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2726 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2727 struct node *object_manager;
2730 assert_return(bus, -EINVAL);
2731 assert_return(object_path_is_valid(path), -EINVAL);
2732 assert_return(!bus_pid_changed(bus), -ECHILD);
2734 if (!BUS_IS_OPEN(bus->state))
2737 if (strv_isempty(interfaces))
2740 r = bus_find_parent_object_manager(bus, &object_manager, path);
2746 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2750 r = sd_bus_message_append_basic(m, 'o', path);
2754 r = sd_bus_message_append_strv(m, interfaces);
2758 return sd_bus_send(bus, m, NULL);
2761 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2764 assert_return(bus, -EINVAL);
2765 assert_return(object_path_is_valid(path), -EINVAL);
2766 assert_return(!bus_pid_changed(bus), -ECHILD);
2768 if (!BUS_IS_OPEN(bus->state))
2771 interfaces = strv_from_stdarg_alloca(interface);
2773 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2776 /// UNNEEDED by elogind
2778 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2783 assert_return(bus, -EINVAL);
2784 assert_return(object_path_is_valid(path), -EINVAL);
2785 assert_return(!bus_pid_changed(bus), -ECHILD);
2787 n = bus_node_allocate(bus, path);
2791 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2797 s->node_object_manager.node = n;
2798 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2799 bus->nodes_modified = true;
2807 sd_bus_slot_unref(s);
2808 bus_node_gc(bus, n);