2 This file is part of systemd.
4 Copyright 2013 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include "alloc-util.h"
21 #include "bus-internal.h"
22 #include "bus-introspect.h"
23 #include "bus-message.h"
24 #include "bus-objects.h"
25 #include "bus-signature.h"
30 #include "string-util.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_(sd_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_(sd_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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
582 _cleanup_(sd_bus_message_unrefp) 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_(sd_bus_message_unrefp) 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_(sd_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_(sd_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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
905 _cleanup_(sd_bus_message_unrefp) 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1166 _cleanup_(sd_bus_message_unrefp) 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_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1883 _cleanup_(sd_bus_message_unrefp) 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_(sd_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_(sd_bus_message_unrefp) 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 #if 0 /// UNNEEDED by elogind
2367 static int object_removed_append_all_prefix(
2373 bool require_fallback) {
2375 const char *previous_interface = NULL;
2376 struct node_vtable *c;
2386 n = hashmap_get(bus->nodes, prefix);
2390 LIST_FOREACH(vtables, c, n->vtables) {
2391 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2394 if (require_fallback && !c->is_fallback)
2396 if (streq_ptr(c->interface, previous_interface))
2399 /* If a child-node already handled this interface, we
2400 * skip it on any of its parents. The child vtables
2401 * always fully override any conflicting vtables of
2402 * any parent node. */
2403 if (set_get(s, c->interface))
2406 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2409 if (bus->nodes_modified)
2414 r = set_put(s, c->interface);
2418 r = sd_bus_message_append(m, "s", c->interface);
2422 previous_interface = c->interface;
2428 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2429 _cleanup_set_free_ Set *s = NULL;
2437 /* see sd_bus_emit_object_added() for details */
2439 s = set_new(&string_hash_ops);
2443 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2446 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2449 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2452 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2456 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2459 if (bus->nodes_modified)
2462 prefix = alloca(strlen(path) + 1);
2463 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2464 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2467 if (bus->nodes_modified)
2474 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2475 BUS_DONT_DESTROY(bus);
2477 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2478 struct node *object_manager;
2482 * This is like sd_bus_emit_object_added(), but emits an
2483 * InterfacesRemoved signal on the given path. This only includes any
2484 * registered interfaces but skips the properties. Note that this will
2485 * call into the find() callbacks of any registered vtable. Therefore,
2486 * you must call this function before destroying/unlinking your object.
2487 * Otherwise, the list of interfaces will be incomplete. However, note
2488 * that this will *NOT* call into any property callback. Therefore, the
2489 * object might be in an "destructed" state, as long as we can find it.
2492 assert_return(bus, -EINVAL);
2493 assert_return(object_path_is_valid(path), -EINVAL);
2494 assert_return(!bus_pid_changed(bus), -ECHILD);
2496 if (!BUS_IS_OPEN(bus->state))
2499 r = bus_find_parent_object_manager(bus, &object_manager, path);
2506 bus->nodes_modified = false;
2507 m = sd_bus_message_unref(m);
2509 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2513 r = sd_bus_message_append_basic(m, 'o', path);
2517 r = sd_bus_message_open_container(m, 'a', "s");
2521 r = object_removed_append_all(bus, m, path);
2525 if (bus->nodes_modified)
2528 r = sd_bus_message_close_container(m);
2532 } while (bus->nodes_modified);
2534 return sd_bus_send(bus, m, NULL);
2538 static int interfaces_added_append_one_prefix(
2543 const char *interface,
2544 bool require_fallback) {
2546 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2547 bool found_interface = false;
2548 struct node_vtable *c;
2559 n = hashmap_get(bus->nodes, prefix);
2563 LIST_FOREACH(vtables, c, n->vtables) {
2564 if (require_fallback && !c->is_fallback)
2567 if (!streq(c->interface, interface))
2570 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2573 if (bus->nodes_modified)
2578 if (!found_interface) {
2579 r = sd_bus_message_append_basic(m, 's', interface);
2583 r = sd_bus_message_open_container(m, 'a', "{sv}");
2587 found_interface = true;
2590 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2593 if (bus->nodes_modified)
2597 if (found_interface) {
2598 r = sd_bus_message_close_container(m);
2603 return found_interface;
2606 static int interfaces_added_append_one(
2610 const char *interface) {
2620 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2623 if (bus->nodes_modified)
2626 prefix = alloca(strlen(path) + 1);
2627 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2628 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2631 if (bus->nodes_modified)
2638 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2639 BUS_DONT_DESTROY(bus);
2641 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2642 struct node *object_manager;
2646 assert_return(bus, -EINVAL);
2647 assert_return(object_path_is_valid(path), -EINVAL);
2648 assert_return(!bus_pid_changed(bus), -ECHILD);
2650 if (!BUS_IS_OPEN(bus->state))
2653 if (strv_isempty(interfaces))
2656 r = bus_find_parent_object_manager(bus, &object_manager, path);
2663 bus->nodes_modified = false;
2664 m = sd_bus_message_unref(m);
2666 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2670 r = sd_bus_message_append_basic(m, 'o', path);
2674 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2678 STRV_FOREACH(i, interfaces) {
2679 assert_return(interface_name_is_valid(*i), -EINVAL);
2681 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2685 r = interfaces_added_append_one(bus, m, path, *i);
2689 if (bus->nodes_modified)
2692 r = sd_bus_message_close_container(m);
2697 if (bus->nodes_modified)
2700 r = sd_bus_message_close_container(m);
2704 } while (bus->nodes_modified);
2706 return sd_bus_send(bus, m, NULL);
2709 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2712 assert_return(bus, -EINVAL);
2713 assert_return(object_path_is_valid(path), -EINVAL);
2714 assert_return(!bus_pid_changed(bus), -ECHILD);
2716 if (!BUS_IS_OPEN(bus->state))
2719 interfaces = strv_from_stdarg_alloca(interface);
2721 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2724 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2725 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2726 struct node *object_manager;
2729 assert_return(bus, -EINVAL);
2730 assert_return(object_path_is_valid(path), -EINVAL);
2731 assert_return(!bus_pid_changed(bus), -ECHILD);
2733 if (!BUS_IS_OPEN(bus->state))
2736 if (strv_isempty(interfaces))
2739 r = bus_find_parent_object_manager(bus, &object_manager, path);
2745 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2749 r = sd_bus_message_append_basic(m, 'o', path);
2753 r = sd_bus_message_append_strv(m, interfaces);
2757 return sd_bus_send(bus, m, NULL);
2760 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2763 assert_return(bus, -EINVAL);
2764 assert_return(object_path_is_valid(path), -EINVAL);
2765 assert_return(!bus_pid_changed(bus), -ECHILD);
2767 if (!BUS_IS_OPEN(bus->state))
2770 interfaces = strv_from_stdarg_alloca(interface);
2772 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2775 #if 0 /// UNNEEDED by elogind
2776 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2781 assert_return(bus, -EINVAL);
2782 assert_return(object_path_is_valid(path), -EINVAL);
2783 assert_return(!bus_pid_changed(bus), -ECHILD);
2785 n = bus_node_allocate(bus, path);
2789 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2795 s->node_object_manager.node = n;
2796 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2797 bus->nodes_modified = true;
2805 sd_bus_slot_unref(s);
2806 bus_node_gc(bus, n);