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_unlocked(" </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_unlocked(" </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);
978 r = bus_maybe_reply_error(m, r, &error);
981 if (bus->nodes_modified) {
987 *found_object = true;
989 r = introspect_write_child_nodes(&intro, s, m->path);
993 r = introspect_finish(&intro, bus, m, &reply);
997 r = sd_bus_send(bus, reply, NULL);
1004 introspect_free(&intro);
1008 static int object_manager_serialize_path(
1010 sd_bus_message *reply,
1013 bool require_fallback,
1014 sd_bus_error *error) {
1016 const char *previous_interface = NULL;
1017 bool found_something = false;
1018 struct node_vtable *i;
1028 n = hashmap_get(bus->nodes, prefix);
1032 LIST_FOREACH(vtables, i, n->vtables) {
1035 if (require_fallback && !i->is_fallback)
1038 r = node_vtable_get_userdata(bus, path, i, &u, error);
1041 if (bus->nodes_modified)
1046 if (!found_something) {
1048 /* Open the object part */
1050 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1054 r = sd_bus_message_append(reply, "o", path);
1058 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1062 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
1066 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
1070 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
1074 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1078 found_something = true;
1081 if (!streq_ptr(previous_interface, i->interface)) {
1083 /* Maybe close the previous interface part */
1085 if (previous_interface) {
1086 r = sd_bus_message_close_container(reply);
1090 r = sd_bus_message_close_container(reply);
1095 /* Open the new interface part */
1097 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1101 r = sd_bus_message_append(reply, "s", i->interface);
1105 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1110 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1113 if (bus->nodes_modified)
1116 previous_interface = i->interface;
1119 if (previous_interface) {
1120 r = sd_bus_message_close_container(reply);
1124 r = sd_bus_message_close_container(reply);
1129 if (found_something) {
1130 r = sd_bus_message_close_container(reply);
1134 r = sd_bus_message_close_container(reply);
1142 static int object_manager_serialize_path_and_fallbacks(
1144 sd_bus_message *reply,
1146 sd_bus_error *error) {
1156 /* First, add all vtables registered for this path */
1157 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1160 if (bus->nodes_modified)
1163 /* Second, add fallback vtables registered for any of the prefixes */
1164 prefix = alloca(strlen(path) + 1);
1165 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1166 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1169 if (bus->nodes_modified)
1176 static int process_get_managed_objects(
1180 bool require_fallback,
1181 bool *found_object) {
1183 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1184 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1185 _cleanup_set_free_free_ Set *s = NULL;
1193 assert(found_object);
1195 /* Spec says, GetManagedObjects() is only implemented on the root of a
1196 * sub-tree. Therefore, we require a registered object-manager on
1197 * exactly the queried path, otherwise, we refuse to respond. */
1199 if (require_fallback || !n->object_managers)
1202 r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
1204 return bus_maybe_reply_error(m, r, &error);
1205 if (bus->nodes_modified)
1208 r = sd_bus_message_new_method_return(m, &reply);
1212 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1216 SET_FOREACH(path, s, i) {
1217 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1219 return bus_maybe_reply_error(m, r, &error);
1221 if (bus->nodes_modified)
1225 r = sd_bus_message_close_container(reply);
1229 r = sd_bus_send(bus, reply, NULL);
1236 static int object_find_and_run(
1240 bool require_fallback,
1241 bool *found_object) {
1244 struct vtable_member vtable_key, *v;
1250 assert(found_object);
1252 n = hashmap_get(bus->nodes, p);
1256 /* First, try object callbacks */
1257 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1260 if (bus->nodes_modified)
1263 if (!m->interface || !m->member)
1266 /* Then, look for a known method */
1267 vtable_key.path = (char*) p;
1268 vtable_key.interface = m->interface;
1269 vtable_key.member = m->member;
1271 v = hashmap_get(bus->vtable_methods, &vtable_key);
1273 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1276 if (bus->nodes_modified)
1280 /* Then, look for a known property */
1281 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1284 get = streq(m->member, "Get");
1286 if (get || streq(m->member, "Set")) {
1288 r = sd_bus_message_rewind(m, true);
1292 vtable_key.path = (char*) p;
1294 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1296 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1298 v = hashmap_get(bus->vtable_properties, &vtable_key);
1300 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1305 } else if (streq(m->member, "GetAll")) {
1308 r = sd_bus_message_rewind(m, true);
1312 r = sd_bus_message_read(m, "s", &iface);
1314 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1319 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1324 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1326 if (!isempty(sd_bus_message_get_signature(m, true)))
1327 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1329 r = process_introspect(bus, m, n, require_fallback, found_object);
1333 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1335 if (!isempty(sd_bus_message_get_signature(m, true)))
1336 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1338 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1343 if (bus->nodes_modified)
1346 if (!*found_object) {
1347 r = bus_node_exists(bus, n, m->path, require_fallback);
1349 return bus_maybe_reply_error(m, r, NULL);
1350 if (bus->nodes_modified)
1353 *found_object = true;
1359 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1362 bool found_object = false;
1367 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1370 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1373 if (hashmap_isempty(bus->nodes))
1376 /* Never respond to broadcast messages */
1377 if (bus->bus_client && !m->destination)
1383 pl = strlen(m->path);
1387 bus->nodes_modified = false;
1389 r = object_find_and_run(bus, m, m->path, false, &found_object);
1393 /* Look for fallback prefixes */
1394 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1396 if (bus->nodes_modified)
1399 r = object_find_and_run(bus, m, prefix, true, &found_object);
1404 } while (bus->nodes_modified);
1409 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1410 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1411 r = sd_bus_reply_method_errorf(
1413 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1414 "Unknown property or interface.");
1416 r = sd_bus_reply_method_errorf(
1418 SD_BUS_ERROR_UNKNOWN_METHOD,
1419 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1427 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1428 struct node *n, *parent;
1430 _cleanup_free_ char *s = NULL;
1436 assert(path[0] == '/');
1438 n = hashmap_get(bus->nodes, path);
1442 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1450 if (streq(path, "/"))
1453 e = strrchr(path, '/');
1456 p = strndupa(path, MAX(1, e - path));
1458 parent = bus_node_allocate(bus, p);
1463 n = new0(struct node, 1);
1469 s = NULL; /* do not free */
1471 r = hashmap_put(bus->nodes, n->path, n);
1479 LIST_PREPEND(siblings, parent->child, n);
1484 void bus_node_gc(sd_bus *b, struct node *n) {
1497 assert(hashmap_remove(b->nodes, n->path) == n);
1500 LIST_REMOVE(siblings, n->parent->child, n);
1503 bus_node_gc(b, n->parent);
1507 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1513 n = hashmap_get(bus->nodes, path);
1517 prefix = alloca(strlen(path) + 1);
1518 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1519 n = hashmap_get(bus->nodes, prefix);
1525 while (n && !n->object_managers)
1533 static int bus_add_object(
1538 sd_bus_message_handler_t callback,
1545 assert_return(bus, -EINVAL);
1546 assert_return(object_path_is_valid(path), -EINVAL);
1547 assert_return(callback, -EINVAL);
1548 assert_return(!bus_pid_changed(bus), -ECHILD);
1550 n = bus_node_allocate(bus, path);
1554 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1560 s->node_callback.callback = callback;
1561 s->node_callback.is_fallback = fallback;
1563 s->node_callback.node = n;
1564 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1565 bus->nodes_modified = true;
1573 sd_bus_slot_unref(s);
1574 bus_node_gc(bus, n);
1579 _public_ int sd_bus_add_object(
1583 sd_bus_message_handler_t callback,
1586 return bus_add_object(bus, slot, false, path, callback, userdata);
1589 _public_ int sd_bus_add_fallback(
1593 sd_bus_message_handler_t callback,
1596 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1599 static void vtable_member_hash_func(const void *a, struct siphash *state) {
1600 const struct vtable_member *m = a;
1604 string_hash_func(m->path, state);
1605 string_hash_func(m->interface, state);
1606 string_hash_func(m->member, state);
1609 static int vtable_member_compare_func(const void *a, const void *b) {
1610 const struct vtable_member *x = a, *y = b;
1616 r = strcmp(x->path, y->path);
1620 r = strcmp(x->interface, y->interface);
1624 return strcmp(x->member, y->member);
1627 static const struct hash_ops vtable_member_hash_ops = {
1628 .hash = vtable_member_hash_func,
1629 .compare = vtable_member_compare_func
1632 static int add_object_vtable_internal(
1636 const char *interface,
1637 const sd_bus_vtable *vtable,
1639 sd_bus_object_find_t find,
1642 sd_bus_slot *s = NULL;
1643 struct node_vtable *i, *existing = NULL;
1644 const sd_bus_vtable *v;
1648 assert_return(bus, -EINVAL);
1649 assert_return(object_path_is_valid(path), -EINVAL);
1650 assert_return(interface_name_is_valid(interface), -EINVAL);
1651 assert_return(vtable, -EINVAL);
1652 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1653 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1654 assert_return(!bus_pid_changed(bus), -ECHILD);
1655 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1656 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1657 !streq(interface, "org.freedesktop.DBus.Peer") &&
1658 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1660 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1664 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1668 n = bus_node_allocate(bus, path);
1672 LIST_FOREACH(vtables, i, n->vtables) {
1673 if (i->is_fallback != fallback) {
1678 if (streq(i->interface, interface)) {
1680 if (i->vtable == vtable) {
1689 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1695 s->node_vtable.is_fallback = fallback;
1696 s->node_vtable.vtable = vtable;
1697 s->node_vtable.find = find;
1699 s->node_vtable.interface = strdup(interface);
1700 if (!s->node_vtable.interface) {
1705 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1709 case _SD_BUS_VTABLE_METHOD: {
1710 struct vtable_member *m;
1712 if (!member_name_is_valid(v->x.method.member) ||
1713 !signature_is_valid(strempty(v->x.method.signature), false) ||
1714 !signature_is_valid(strempty(v->x.method.result), false) ||
1715 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1716 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1721 m = new0(struct vtable_member, 1);
1727 m->parent = &s->node_vtable;
1729 m->interface = s->node_vtable.interface;
1730 m->member = v->x.method.member;
1733 r = hashmap_put(bus->vtable_methods, m, m);
1742 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1744 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1749 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1756 case _SD_BUS_VTABLE_PROPERTY: {
1757 struct vtable_member *m;
1759 if (!member_name_is_valid(v->x.property.member) ||
1760 !signature_is_single(v->x.property.signature, false) ||
1761 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1762 (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1763 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1764 ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1765 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1770 m = new0(struct vtable_member, 1);
1776 m->parent = &s->node_vtable;
1778 m->interface = s->node_vtable.interface;
1779 m->member = v->x.property.member;
1782 r = hashmap_put(bus->vtable_properties, m, m);
1791 case _SD_BUS_VTABLE_SIGNAL:
1793 if (!member_name_is_valid(v->x.signal.member) ||
1794 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1795 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1808 s->node_vtable.node = n;
1809 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1810 bus->nodes_modified = true;
1818 sd_bus_slot_unref(s);
1819 bus_node_gc(bus, n);
1824 _public_ int sd_bus_add_object_vtable(
1828 const char *interface,
1829 const sd_bus_vtable *vtable,
1832 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1835 _public_ int sd_bus_add_fallback_vtable(
1839 const char *interface,
1840 const sd_bus_vtable *vtable,
1841 sd_bus_object_find_t find,
1844 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1847 _public_ int sd_bus_add_node_enumerator(
1851 sd_bus_node_enumerator_t callback,
1858 assert_return(bus, -EINVAL);
1859 assert_return(object_path_is_valid(path), -EINVAL);
1860 assert_return(callback, -EINVAL);
1861 assert_return(!bus_pid_changed(bus), -ECHILD);
1863 n = bus_node_allocate(bus, path);
1867 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1873 s->node_enumerator.callback = callback;
1875 s->node_enumerator.node = n;
1876 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1877 bus->nodes_modified = true;
1885 sd_bus_slot_unref(s);
1886 bus_node_gc(bus, n);
1891 static int emit_properties_changed_on_interface(
1895 const char *interface,
1896 bool require_fallback,
1897 bool *found_interface,
1900 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1901 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1902 bool has_invalidating = false, has_changing = false;
1903 struct vtable_member key = {};
1904 struct node_vtable *c;
1914 assert(found_interface);
1916 n = hashmap_get(bus->nodes, prefix);
1920 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1924 r = sd_bus_message_append(m, "s", interface);
1928 r = sd_bus_message_open_container(m, 'a', "{sv}");
1933 key.interface = interface;
1935 LIST_FOREACH(vtables, c, n->vtables) {
1936 if (require_fallback && !c->is_fallback)
1939 if (!streq(c->interface, interface))
1942 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1945 if (bus->nodes_modified)
1950 *found_interface = true;
1953 /* If the caller specified a list of
1954 * properties we include exactly those in the
1955 * PropertiesChanged message */
1957 STRV_FOREACH(property, names) {
1958 struct vtable_member *v;
1960 assert_return(member_name_is_valid(*property), -EINVAL);
1962 key.member = *property;
1963 v = hashmap_get(bus->vtable_properties, &key);
1967 /* If there are two vtables for the same
1968 * interface, let's handle this property when
1969 * we come to that vtable. */
1973 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1974 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1976 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1978 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1979 has_invalidating = true;
1983 has_changing = true;
1985 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1988 if (bus->nodes_modified)
1992 const sd_bus_vtable *v;
1994 /* If the caller specified no properties list
1995 * we include all properties that are marked
1996 * as changing in the message. */
1998 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1999 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2002 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2005 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2006 has_invalidating = true;
2010 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2013 has_changing = true;
2015 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2018 if (bus->nodes_modified)
2024 if (!has_invalidating && !has_changing)
2027 r = sd_bus_message_close_container(m);
2031 r = sd_bus_message_open_container(m, 'a', "s");
2035 if (has_invalidating) {
2036 LIST_FOREACH(vtables, c, n->vtables) {
2037 if (require_fallback && !c->is_fallback)
2040 if (!streq(c->interface, interface))
2043 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2046 if (bus->nodes_modified)
2052 STRV_FOREACH(property, names) {
2053 struct vtable_member *v;
2055 key.member = *property;
2056 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2057 assert(c == v->parent);
2059 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2062 r = sd_bus_message_append(m, "s", *property);
2067 const sd_bus_vtable *v;
2069 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2070 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2073 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2076 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2079 r = sd_bus_message_append(m, "s", v->x.property.member);
2087 r = sd_bus_message_close_container(m);
2091 r = sd_bus_send(bus, m, NULL);
2098 _public_ int sd_bus_emit_properties_changed_strv(
2101 const char *interface,
2104 BUS_DONT_DESTROY(bus);
2105 bool found_interface = false;
2109 assert_return(bus, -EINVAL);
2110 assert_return(object_path_is_valid(path), -EINVAL);
2111 assert_return(interface_name_is_valid(interface), -EINVAL);
2112 assert_return(!bus_pid_changed(bus), -ECHILD);
2114 if (!BUS_IS_OPEN(bus->state))
2117 /* A non-NULL but empty names list means nothing needs to be
2118 generated. A NULL list OTOH indicates that all properties
2119 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2120 included in the PropertiesChanged message. */
2121 if (names && names[0] == NULL)
2125 bus->nodes_modified = false;
2127 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2130 if (bus->nodes_modified)
2133 prefix = alloca(strlen(path) + 1);
2134 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2135 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2138 if (bus->nodes_modified)
2142 } while (bus->nodes_modified);
2144 return found_interface ? 0 : -ENOENT;
2147 _public_ int sd_bus_emit_properties_changed(
2150 const char *interface,
2151 const char *name, ...) {
2155 assert_return(bus, -EINVAL);
2156 assert_return(object_path_is_valid(path), -EINVAL);
2157 assert_return(interface_name_is_valid(interface), -EINVAL);
2158 assert_return(!bus_pid_changed(bus), -ECHILD);
2160 if (!BUS_IS_OPEN(bus->state))
2166 names = strv_from_stdarg_alloca(name);
2168 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2171 static int object_added_append_all_prefix(
2177 bool require_fallback) {
2179 const char *previous_interface = NULL;
2180 struct node_vtable *c;
2190 n = hashmap_get(bus->nodes, prefix);
2194 LIST_FOREACH(vtables, c, n->vtables) {
2195 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2198 if (require_fallback && !c->is_fallback)
2201 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2204 if (bus->nodes_modified)
2209 if (!streq_ptr(c->interface, previous_interface)) {
2210 /* If a child-node already handled this interface, we
2211 * skip it on any of its parents. The child vtables
2212 * always fully override any conflicting vtables of
2213 * any parent node. */
2214 if (set_get(s, c->interface))
2217 r = set_put(s, c->interface);
2221 if (previous_interface) {
2222 r = sd_bus_message_close_container(m);
2225 r = sd_bus_message_close_container(m);
2230 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2233 r = sd_bus_message_append(m, "s", c->interface);
2236 r = sd_bus_message_open_container(m, 'a', "{sv}");
2240 previous_interface = c->interface;
2243 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2246 if (bus->nodes_modified)
2250 if (previous_interface) {
2251 r = sd_bus_message_close_container(m);
2254 r = sd_bus_message_close_container(m);
2262 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2263 _cleanup_set_free_ Set *s = NULL;
2272 * This appends all interfaces registered on path @path. We first add
2273 * the builtin interfaces, which are always available and handled by
2274 * sd-bus. Then, we add all interfaces registered on the exact node,
2275 * followed by all fallback interfaces registered on any parent prefix.
2277 * If an interface is registered multiple times on the same node with
2278 * different vtables, we merge all the properties across all vtables.
2279 * However, if a child node has the same interface registered as one of
2280 * its parent nodes has as fallback, we make the child overwrite the
2281 * parent instead of extending it. Therefore, we keep a "Set" of all
2282 * handled interfaces during parent traversal, so we skip interfaces on
2283 * a parent that were overwritten by a child.
2286 s = set_new(&string_hash_ops);
2290 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2293 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2296 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2299 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2303 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2306 if (bus->nodes_modified)
2309 prefix = alloca(strlen(path) + 1);
2310 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2311 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2314 if (bus->nodes_modified)
2321 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2322 BUS_DONT_DESTROY(bus);
2324 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2325 struct node *object_manager;
2329 * This emits an InterfacesAdded signal on the given path, by iterating
2330 * all registered vtables and fallback vtables on the path. All
2331 * properties are queried and included in the signal.
2332 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2333 * explicit list of registered interfaces. However, unlike
2334 * interfaces_added(), this call can figure out the list of supported
2335 * interfaces itself. Furthermore, it properly adds the builtin
2336 * org.freedesktop.DBus.* interfaces.
2339 assert_return(bus, -EINVAL);
2340 assert_return(object_path_is_valid(path), -EINVAL);
2341 assert_return(!bus_pid_changed(bus), -ECHILD);
2343 if (!BUS_IS_OPEN(bus->state))
2346 r = bus_find_parent_object_manager(bus, &object_manager, path);
2353 bus->nodes_modified = false;
2354 m = sd_bus_message_unref(m);
2356 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2360 r = sd_bus_message_append_basic(m, 'o', path);
2364 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2368 r = object_added_append_all(bus, m, path);
2372 if (bus->nodes_modified)
2375 r = sd_bus_message_close_container(m);
2379 } while (bus->nodes_modified);
2381 return sd_bus_send(bus, m, NULL);
2384 #if 0 /// UNNEEDED by elogind
2385 static int object_removed_append_all_prefix(
2391 bool require_fallback) {
2393 const char *previous_interface = NULL;
2394 struct node_vtable *c;
2404 n = hashmap_get(bus->nodes, prefix);
2408 LIST_FOREACH(vtables, c, n->vtables) {
2409 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2412 if (require_fallback && !c->is_fallback)
2414 if (streq_ptr(c->interface, previous_interface))
2417 /* If a child-node already handled this interface, we
2418 * skip it on any of its parents. The child vtables
2419 * always fully override any conflicting vtables of
2420 * any parent node. */
2421 if (set_get(s, c->interface))
2424 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2427 if (bus->nodes_modified)
2432 r = set_put(s, c->interface);
2436 r = sd_bus_message_append(m, "s", c->interface);
2440 previous_interface = c->interface;
2446 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2447 _cleanup_set_free_ Set *s = NULL;
2455 /* see sd_bus_emit_object_added() for details */
2457 s = set_new(&string_hash_ops);
2461 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2464 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2467 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2470 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2474 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2477 if (bus->nodes_modified)
2480 prefix = alloca(strlen(path) + 1);
2481 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2482 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2485 if (bus->nodes_modified)
2492 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2493 BUS_DONT_DESTROY(bus);
2495 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2496 struct node *object_manager;
2500 * This is like sd_bus_emit_object_added(), but emits an
2501 * InterfacesRemoved signal on the given path. This only includes any
2502 * registered interfaces but skips the properties. Note that this will
2503 * call into the find() callbacks of any registered vtable. Therefore,
2504 * you must call this function before destroying/unlinking your object.
2505 * Otherwise, the list of interfaces will be incomplete. However, note
2506 * that this will *NOT* call into any property callback. Therefore, the
2507 * object might be in an "destructed" state, as long as we can find it.
2510 assert_return(bus, -EINVAL);
2511 assert_return(object_path_is_valid(path), -EINVAL);
2512 assert_return(!bus_pid_changed(bus), -ECHILD);
2514 if (!BUS_IS_OPEN(bus->state))
2517 r = bus_find_parent_object_manager(bus, &object_manager, path);
2524 bus->nodes_modified = false;
2525 m = sd_bus_message_unref(m);
2527 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2531 r = sd_bus_message_append_basic(m, 'o', path);
2535 r = sd_bus_message_open_container(m, 'a', "s");
2539 r = object_removed_append_all(bus, m, path);
2543 if (bus->nodes_modified)
2546 r = sd_bus_message_close_container(m);
2550 } while (bus->nodes_modified);
2552 return sd_bus_send(bus, m, NULL);
2556 static int interfaces_added_append_one_prefix(
2561 const char *interface,
2562 bool require_fallback) {
2564 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2565 bool found_interface = false;
2566 struct node_vtable *c;
2577 n = hashmap_get(bus->nodes, prefix);
2581 LIST_FOREACH(vtables, c, n->vtables) {
2582 if (require_fallback && !c->is_fallback)
2585 if (!streq(c->interface, interface))
2588 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2591 if (bus->nodes_modified)
2596 if (!found_interface) {
2597 r = sd_bus_message_append_basic(m, 's', interface);
2601 r = sd_bus_message_open_container(m, 'a', "{sv}");
2605 found_interface = true;
2608 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2611 if (bus->nodes_modified)
2615 if (found_interface) {
2616 r = sd_bus_message_close_container(m);
2621 return found_interface;
2624 static int interfaces_added_append_one(
2628 const char *interface) {
2638 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2641 if (bus->nodes_modified)
2644 prefix = alloca(strlen(path) + 1);
2645 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2646 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2649 if (bus->nodes_modified)
2656 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2657 BUS_DONT_DESTROY(bus);
2659 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2660 struct node *object_manager;
2664 assert_return(bus, -EINVAL);
2665 assert_return(object_path_is_valid(path), -EINVAL);
2666 assert_return(!bus_pid_changed(bus), -ECHILD);
2668 if (!BUS_IS_OPEN(bus->state))
2671 if (strv_isempty(interfaces))
2674 r = bus_find_parent_object_manager(bus, &object_manager, path);
2681 bus->nodes_modified = false;
2682 m = sd_bus_message_unref(m);
2684 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2688 r = sd_bus_message_append_basic(m, 'o', path);
2692 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2696 STRV_FOREACH(i, interfaces) {
2697 assert_return(interface_name_is_valid(*i), -EINVAL);
2699 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2703 r = interfaces_added_append_one(bus, m, path, *i);
2707 if (bus->nodes_modified)
2710 r = sd_bus_message_close_container(m);
2715 if (bus->nodes_modified)
2718 r = sd_bus_message_close_container(m);
2722 } while (bus->nodes_modified);
2724 return sd_bus_send(bus, m, NULL);
2727 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
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 interfaces = strv_from_stdarg_alloca(interface);
2739 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2742 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2743 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2744 struct node *object_manager;
2747 assert_return(bus, -EINVAL);
2748 assert_return(object_path_is_valid(path), -EINVAL);
2749 assert_return(!bus_pid_changed(bus), -ECHILD);
2751 if (!BUS_IS_OPEN(bus->state))
2754 if (strv_isempty(interfaces))
2757 r = bus_find_parent_object_manager(bus, &object_manager, path);
2763 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2767 r = sd_bus_message_append_basic(m, 'o', path);
2771 r = sd_bus_message_append_strv(m, interfaces);
2775 return sd_bus_send(bus, m, NULL);
2778 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2781 assert_return(bus, -EINVAL);
2782 assert_return(object_path_is_valid(path), -EINVAL);
2783 assert_return(!bus_pid_changed(bus), -ECHILD);
2785 if (!BUS_IS_OPEN(bus->state))
2788 interfaces = strv_from_stdarg_alloca(interface);
2790 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2793 #if 0 /// UNNEEDED by elogind
2794 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2799 assert_return(bus, -EINVAL);
2800 assert_return(object_path_is_valid(path), -EINVAL);
2801 assert_return(!bus_pid_changed(bus), -ECHILD);
2803 n = bus_node_allocate(bus, path);
2807 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2813 s->node_object_manager.node = n;
2814 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2815 bus->nodes_modified = true;
2823 sd_bus_slot_unref(s);
2824 bus_node_gc(bus, n);