1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "bus-internal.h"
25 #include "bus-message.h"
27 #include "bus-signature.h"
28 #include "bus-introspect.h"
31 #include "bus-objects.h"
33 static int node_vtable_get_userdata(
36 struct node_vtable *c,
38 sd_bus_error *error) {
48 s = container_of(c, sd_bus_slot, node_vtable);
51 bus->current_slot = sd_bus_slot_ref(s);
52 bus->current_userdata = u;
53 r = c->find(bus, path, c->interface, u, &u, error);
54 bus->current_userdata = NULL;
55 bus->current_slot = sd_bus_slot_unref(s);
59 if (sd_bus_error_is_set(error))
60 return -sd_bus_error_get_errno(error);
71 static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
74 return (uint8_t*) u + p->x.method.offset;
77 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
80 return (uint8_t*) u + p->x.property.offset;
83 static int vtable_property_get_userdata(
86 struct vtable_member *p,
88 sd_bus_error *error) {
98 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
101 if (bus->nodes_modified)
104 *userdata = vtable_property_convert_userdata(p->vtable, u);
108 static int add_enumerated_to_set(
111 struct node_enumerator *first,
113 sd_bus_error *error) {
115 struct node_enumerator *c;
122 LIST_FOREACH(enumerators, c, first) {
123 char **children = NULL, **k;
126 if (bus->nodes_modified)
129 slot = container_of(c, sd_bus_slot, node_enumerator);
131 bus->current_slot = sd_bus_slot_ref(slot);
132 bus->current_userdata = slot->userdata;
133 r = c->callback(bus, prefix, slot->userdata, &children, error);
134 bus->current_userdata = NULL;
135 bus->current_slot = sd_bus_slot_unref(slot);
139 if (sd_bus_error_is_set(error))
140 return -sd_bus_error_get_errno(error);
142 STRV_FOREACH(k, children) {
148 if (!object_path_is_valid(*k)){
154 if (!object_path_startswith(*k, prefix)) {
159 r = set_consume(s, *k);
172 static int add_subtree_to_set(
176 bool skip_subhierarchies,
178 sd_bus_error *error) {
188 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
191 if (bus->nodes_modified)
194 LIST_FOREACH(siblings, i, n->child) {
197 if (!object_path_startswith(i->path, prefix))
204 r = set_consume(s, t);
205 if (r < 0 && r != -EEXIST)
208 if (!skip_subhierarchies || !i->object_managers) {
209 r = add_subtree_to_set(bus, prefix, i, skip_subhierarchies, s, error);
212 if (bus->nodes_modified)
220 static int get_child_nodes(
224 bool skip_subhierarchies,
226 sd_bus_error *error) {
236 s = set_new(&string_hash_ops);
240 r = add_subtree_to_set(bus, prefix, n, skip_subhierarchies, s, error);
250 static int node_callbacks_run(
253 struct node_callback *first,
254 bool require_fallback,
255 bool *found_object) {
257 struct node_callback *c;
262 assert(found_object);
264 LIST_FOREACH(callbacks, c, first) {
265 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
268 if (bus->nodes_modified)
271 if (require_fallback && !c->is_fallback)
274 *found_object = true;
276 if (c->last_iteration == bus->iteration_counter)
279 c->last_iteration = bus->iteration_counter;
281 r = sd_bus_message_rewind(m, true);
285 slot = container_of(c, sd_bus_slot, node_callback);
287 bus->current_slot = sd_bus_slot_ref(slot);
288 bus->current_handler = c->callback;
289 bus->current_userdata = slot->userdata;
290 r = c->callback(m, slot->userdata, &error_buffer);
291 bus->current_userdata = NULL;
292 bus->current_handler = NULL;
293 bus->current_slot = sd_bus_slot_unref(slot);
295 r = bus_maybe_reply_error(m, r, &error_buffer);
303 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
305 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
313 /* If the entire bus is trusted let's grant access */
317 /* If the member is marked UNPRIVILEGED let's grant access */
318 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
321 /* Check have the caller has the requested capability
322 * set. Note that the flags value contains the capability
323 * number plus one, which we need to subtract here. We do this
324 * so that we have 0 as special value for "default
326 cap = CAPABILITY_SHIFT(c->vtable->flags);
328 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
334 r = sd_bus_query_sender_privilege(m, cap);
340 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
343 static int method_callbacks_run(
346 struct vtable_member *c,
347 bool require_fallback,
348 bool *found_object) {
350 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
351 const char *signature;
358 assert(found_object);
360 if (require_fallback && !c->parent->is_fallback)
363 r = check_access(bus, m, c, &error);
365 return bus_maybe_reply_error(m, r, &error);
367 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
369 return bus_maybe_reply_error(m, r, &error);
370 if (bus->nodes_modified)
373 u = vtable_method_convert_userdata(c->vtable, u);
375 *found_object = true;
377 if (c->last_iteration == bus->iteration_counter)
380 c->last_iteration = bus->iteration_counter;
382 r = sd_bus_message_rewind(m, true);
386 signature = sd_bus_message_get_signature(m, true);
390 if (!streq(strempty(c->vtable->x.method.signature), signature))
391 return sd_bus_reply_method_errorf(
393 SD_BUS_ERROR_INVALID_ARGS,
394 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
395 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
397 /* Keep track what the signature of the reply to this message
398 * should be, so that this can be enforced when sealing the
400 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
402 if (c->vtable->x.method.handler) {
405 slot = container_of(c->parent, sd_bus_slot, node_vtable);
407 bus->current_slot = sd_bus_slot_ref(slot);
408 bus->current_handler = c->vtable->x.method.handler;
409 bus->current_userdata = u;
410 r = c->vtable->x.method.handler(m, u, &error);
411 bus->current_userdata = NULL;
412 bus->current_handler = NULL;
413 bus->current_slot = sd_bus_slot_unref(slot);
415 return bus_maybe_reply_error(m, r, &error);
418 /* If the method callback is NULL, make this a successful NOP */
419 r = sd_bus_reply_method_return(m, NULL);
426 static int invoke_property_get(
429 const sd_bus_vtable *v,
431 const char *interface,
432 const char *property,
433 sd_bus_message *reply,
435 sd_bus_error *error) {
448 if (v->x.property.get) {
450 bus->current_slot = sd_bus_slot_ref(slot);
451 bus->current_userdata = userdata;
452 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
453 bus->current_userdata = NULL;
454 bus->current_slot = sd_bus_slot_unref(slot);
458 if (sd_bus_error_is_set(error))
459 return -sd_bus_error_get_errno(error);
463 /* Automatic handling if no callback is defined. */
465 if (streq(v->x.property.signature, "as"))
466 return sd_bus_message_append_strv(reply, *(char***) userdata);
468 assert(signature_is_single(v->x.property.signature, false));
469 assert(bus_type_is_basic(v->x.property.signature[0]));
471 switch (v->x.property.signature[0]) {
473 case SD_BUS_TYPE_STRING:
474 case SD_BUS_TYPE_SIGNATURE:
475 p = strempty(*(char**) userdata);
478 case SD_BUS_TYPE_OBJECT_PATH:
479 p = *(char**) userdata;
488 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
491 static int invoke_property_set(
494 const sd_bus_vtable *v,
496 const char *interface,
497 const char *property,
498 sd_bus_message *value,
500 sd_bus_error *error) {
512 if (v->x.property.set) {
514 bus->current_slot = sd_bus_slot_ref(slot);
515 bus->current_userdata = userdata;
516 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
517 bus->current_userdata = NULL;
518 bus->current_slot = sd_bus_slot_unref(slot);
522 if (sd_bus_error_is_set(error))
523 return -sd_bus_error_get_errno(error);
527 /* Automatic handling if no callback is defined. */
529 assert(signature_is_single(v->x.property.signature, false));
530 assert(bus_type_is_basic(v->x.property.signature[0]));
532 switch (v->x.property.signature[0]) {
534 case SD_BUS_TYPE_STRING:
535 case SD_BUS_TYPE_OBJECT_PATH:
536 case SD_BUS_TYPE_SIGNATURE: {
540 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
548 free(*(char**) userdata);
549 *(char**) userdata = n;
555 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
565 static int property_get_set_callbacks_run(
568 struct vtable_member *c,
569 bool require_fallback,
571 bool *found_object) {
573 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
574 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
582 assert(found_object);
584 if (require_fallback && !c->parent->is_fallback)
587 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
589 return bus_maybe_reply_error(m, r, &error);
590 if (bus->nodes_modified)
593 slot = container_of(c->parent, sd_bus_slot, node_vtable);
595 *found_object = true;
597 r = sd_bus_message_new_method_return(m, &reply);
602 /* Note that we do not protect against reexecution
603 * here (using the last_iteration check, see below),
604 * should the node tree have changed and we got called
605 * again. We assume that property Get() calls are
606 * ultimately without side-effects or if they aren't
607 * then at least idempotent. */
609 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
613 /* Note that we do not do an access check here. Read
614 * access to properties is always unrestricted, since
615 * PropertiesChanged signals broadcast contents
618 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
620 return bus_maybe_reply_error(m, r, &error);
622 if (bus->nodes_modified)
625 r = sd_bus_message_close_container(reply);
630 const char *signature = NULL;
633 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
634 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
636 /* Avoid that we call the set routine more than once
637 * if the processing of this message got restarted
638 * because the node tree changed. */
639 if (c->last_iteration == bus->iteration_counter)
642 c->last_iteration = bus->iteration_counter;
644 r = sd_bus_message_peek_type(m, &type, &signature);
648 if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
649 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));
651 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
655 r = check_access(bus, m, c, &error);
657 return bus_maybe_reply_error(m, r, &error);
659 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
661 return bus_maybe_reply_error(m, r, &error);
663 if (bus->nodes_modified)
666 r = sd_bus_message_exit_container(m);
671 r = sd_bus_send(bus, reply, NULL);
678 static int vtable_append_one_property(
680 sd_bus_message *reply,
682 struct node_vtable *c,
683 const sd_bus_vtable *v,
685 sd_bus_error *error) {
696 r = sd_bus_message_open_container(reply, 'e', "sv");
700 r = sd_bus_message_append(reply, "s", v->x.property.member);
704 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
708 slot = container_of(c, sd_bus_slot, node_vtable);
710 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
713 if (bus->nodes_modified)
716 r = sd_bus_message_close_container(reply);
720 r = sd_bus_message_close_container(reply);
727 static int vtable_append_all_properties(
729 sd_bus_message *reply,
731 struct node_vtable *c,
733 sd_bus_error *error) {
735 const sd_bus_vtable *v;
743 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
746 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
747 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
750 if (v->flags & SD_BUS_VTABLE_HIDDEN)
753 if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
756 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
759 if (bus->nodes_modified)
766 static int property_get_all_callbacks_run(
769 struct node_vtable *first,
770 bool require_fallback,
772 bool *found_object) {
774 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
775 struct node_vtable *c;
776 bool found_interface;
781 assert(found_object);
783 r = sd_bus_message_new_method_return(m, &reply);
787 r = sd_bus_message_open_container(reply, 'a', "{sv}");
791 found_interface = !iface ||
792 streq(iface, "org.freedesktop.DBus.Properties") ||
793 streq(iface, "org.freedesktop.DBus.Peer") ||
794 streq(iface, "org.freedesktop.DBus.Introspectable");
796 LIST_FOREACH(vtables, c, first) {
797 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
800 if (require_fallback && !c->is_fallback)
803 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
805 return bus_maybe_reply_error(m, r, &error);
806 if (bus->nodes_modified)
811 *found_object = true;
813 if (iface && !streq(c->interface, iface))
815 found_interface = true;
817 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
819 return bus_maybe_reply_error(m, r, &error);
820 if (bus->nodes_modified)
824 if (!found_interface) {
825 r = sd_bus_reply_method_errorf(
827 SD_BUS_ERROR_UNKNOWN_INTERFACE,
828 "Unknown interface '%s'.", iface);
835 r = sd_bus_message_close_container(reply);
839 r = sd_bus_send(bus, reply, NULL);
846 static int bus_node_exists(
850 bool require_fallback) {
852 struct node_vtable *c;
853 struct node_callback *k;
860 /* Tests if there's anything attached directly to this node
861 * for the specified path */
863 if (!require_fallback && (n->enumerators || n->object_managers))
866 LIST_FOREACH(callbacks, k, n->callbacks) {
867 if (require_fallback && !k->is_fallback)
873 LIST_FOREACH(vtables, c, n->vtables) {
874 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
876 if (require_fallback && !c->is_fallback)
879 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
882 if (bus->nodes_modified)
889 static int process_introspect(
893 bool require_fallback,
894 bool *found_object) {
896 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
897 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
898 _cleanup_set_free_free_ Set *s = NULL;
899 const char *previous_interface = NULL;
900 struct introspect intro;
901 struct node_vtable *c;
908 assert(found_object);
910 r = get_child_nodes(bus, m->path, n, false, &s, &error);
912 return bus_maybe_reply_error(m, r, &error);
913 if (bus->nodes_modified)
916 r = introspect_begin(&intro, bus->trusted);
920 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
924 empty = set_isempty(s);
926 LIST_FOREACH(vtables, c, n->vtables) {
927 if (require_fallback && !c->is_fallback)
930 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
932 r = bus_maybe_reply_error(m, r, &error);
935 if (bus->nodes_modified) {
944 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
947 if (!streq_ptr(previous_interface, c->interface)) {
949 if (previous_interface)
950 fputs(" </interface>\n", intro.f);
952 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
955 r = introspect_write_interface(&intro, c->vtable);
959 previous_interface = c->interface;
962 if (previous_interface)
963 fputs(" </interface>\n", intro.f);
966 /* Nothing?, let's see if we exist at all, and if not
967 * refuse to do anything */
968 r = bus_node_exists(bus, n, m->path, require_fallback);
971 if (bus->nodes_modified) {
977 *found_object = true;
979 r = introspect_write_child_nodes(&intro, s, m->path);
983 r = introspect_finish(&intro, bus, m, &reply);
987 r = sd_bus_send(bus, reply, NULL);
994 introspect_free(&intro);
998 static int object_manager_serialize_path(
1000 sd_bus_message *reply,
1003 bool require_fallback,
1004 sd_bus_error *error) {
1006 const char *previous_interface = NULL;
1007 bool found_something = false;
1008 struct node_vtable *i;
1018 n = hashmap_get(bus->nodes, prefix);
1022 LIST_FOREACH(vtables, i, n->vtables) {
1025 if (require_fallback && !i->is_fallback)
1028 r = node_vtable_get_userdata(bus, path, i, &u, error);
1031 if (bus->nodes_modified)
1036 if (!found_something) {
1038 /* Open the object part */
1040 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1044 r = sd_bus_message_append(reply, "o", path);
1048 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1052 found_something = true;
1055 if (!streq_ptr(previous_interface, i->interface)) {
1057 /* Maybe close the previous interface part */
1059 if (previous_interface) {
1060 r = sd_bus_message_close_container(reply);
1064 r = sd_bus_message_close_container(reply);
1069 /* Open the new interface part */
1071 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1075 r = sd_bus_message_append(reply, "s", i->interface);
1079 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1084 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1087 if (bus->nodes_modified)
1090 previous_interface = i->interface;
1093 if (previous_interface) {
1094 r = sd_bus_message_close_container(reply);
1098 r = sd_bus_message_close_container(reply);
1103 if (found_something) {
1104 r = sd_bus_message_close_container(reply);
1108 r = sd_bus_message_close_container(reply);
1116 static int object_manager_serialize_path_and_fallbacks(
1118 sd_bus_message *reply,
1120 sd_bus_error *error) {
1130 /* First, add all vtables registered for this path */
1131 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1134 if (bus->nodes_modified)
1137 /* Second, add fallback vtables registered for any of the prefixes */
1138 prefix = alloca(strlen(path) + 1);
1139 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1140 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1143 if (bus->nodes_modified)
1150 static int process_get_managed_objects(
1154 bool require_fallback,
1155 bool *found_object) {
1157 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1158 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1159 _cleanup_set_free_free_ Set *s = NULL;
1167 assert(found_object);
1169 /* Spec says, GetManagedObjects() is only implemented on the root of a
1170 * sub-tree. Therefore, we require a registered object-manager on
1171 * exactly the queried path, otherwise, we refuse to respond. */
1173 if (require_fallback || !n->object_managers)
1176 r = get_child_nodes(bus, m->path, n, true, &s, &error);
1179 if (bus->nodes_modified)
1182 r = sd_bus_message_new_method_return(m, &reply);
1186 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1190 SET_FOREACH(path, s, i) {
1191 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1195 if (bus->nodes_modified)
1199 r = sd_bus_message_close_container(reply);
1203 r = sd_bus_send(bus, reply, NULL);
1210 static int object_find_and_run(
1214 bool require_fallback,
1215 bool *found_object) {
1218 struct vtable_member vtable_key, *v;
1224 assert(found_object);
1226 n = hashmap_get(bus->nodes, p);
1230 /* First, try object callbacks */
1231 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1234 if (bus->nodes_modified)
1237 if (!m->interface || !m->member)
1240 /* Then, look for a known method */
1241 vtable_key.path = (char*) p;
1242 vtable_key.interface = m->interface;
1243 vtable_key.member = m->member;
1245 v = hashmap_get(bus->vtable_methods, &vtable_key);
1247 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1250 if (bus->nodes_modified)
1254 /* Then, look for a known property */
1255 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1258 get = streq(m->member, "Get");
1260 if (get || streq(m->member, "Set")) {
1262 r = sd_bus_message_rewind(m, true);
1266 vtable_key.path = (char*) p;
1268 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1270 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1272 v = hashmap_get(bus->vtable_properties, &vtable_key);
1274 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1279 } else if (streq(m->member, "GetAll")) {
1282 r = sd_bus_message_rewind(m, true);
1286 r = sd_bus_message_read(m, "s", &iface);
1288 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1293 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1298 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1300 if (!isempty(sd_bus_message_get_signature(m, true)))
1301 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1303 r = process_introspect(bus, m, n, require_fallback, found_object);
1307 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1309 if (!isempty(sd_bus_message_get_signature(m, true)))
1310 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1312 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1317 if (bus->nodes_modified)
1320 if (!*found_object) {
1321 r = bus_node_exists(bus, n, m->path, require_fallback);
1324 if (bus->nodes_modified)
1327 *found_object = true;
1333 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1336 bool found_object = false;
1341 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1344 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1347 if (hashmap_isempty(bus->nodes))
1350 /* Never respond to broadcast messages */
1351 if (bus->bus_client && !m->destination)
1357 pl = strlen(m->path);
1361 bus->nodes_modified = false;
1363 r = object_find_and_run(bus, m, m->path, false, &found_object);
1367 /* Look for fallback prefixes */
1368 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1370 if (bus->nodes_modified)
1373 r = object_find_and_run(bus, m, prefix, true, &found_object);
1378 } while (bus->nodes_modified);
1383 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1384 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1385 r = sd_bus_reply_method_errorf(
1387 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1388 "Unknown property or interface.");
1390 r = sd_bus_reply_method_errorf(
1392 SD_BUS_ERROR_UNKNOWN_METHOD,
1393 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1401 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1402 struct node *n, *parent;
1404 _cleanup_free_ char *s = NULL;
1410 assert(path[0] == '/');
1412 n = hashmap_get(bus->nodes, path);
1416 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1424 if (streq(path, "/"))
1427 e = strrchr(path, '/');
1430 p = strndupa(path, MAX(1, e - path));
1432 parent = bus_node_allocate(bus, p);
1437 n = new0(struct node, 1);
1443 s = NULL; /* do not free */
1445 r = hashmap_put(bus->nodes, n->path, n);
1453 LIST_PREPEND(siblings, parent->child, n);
1458 void bus_node_gc(sd_bus *b, struct node *n) {
1471 assert(hashmap_remove(b->nodes, n->path) == n);
1474 LIST_REMOVE(siblings, n->parent->child, n);
1477 bus_node_gc(b, n->parent);
1481 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1487 n = hashmap_get(bus->nodes, path);
1491 prefix = alloca(strlen(path) + 1);
1492 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1493 n = hashmap_get(bus->nodes, prefix);
1499 while (n && !n->object_managers)
1507 static int bus_add_object(
1512 sd_bus_message_handler_t callback,
1519 assert_return(bus, -EINVAL);
1520 assert_return(object_path_is_valid(path), -EINVAL);
1521 assert_return(callback, -EINVAL);
1522 assert_return(!bus_pid_changed(bus), -ECHILD);
1524 n = bus_node_allocate(bus, path);
1528 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1534 s->node_callback.callback = callback;
1535 s->node_callback.is_fallback = fallback;
1537 s->node_callback.node = n;
1538 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1539 bus->nodes_modified = true;
1547 sd_bus_slot_unref(s);
1548 bus_node_gc(bus, n);
1553 _public_ int sd_bus_add_object(
1557 sd_bus_message_handler_t callback,
1560 return bus_add_object(bus, slot, false, path, callback, userdata);
1563 _public_ int sd_bus_add_fallback(
1567 sd_bus_message_handler_t callback,
1570 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1573 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1574 const struct vtable_member *m = a;
1575 uint8_t hash_key2[HASH_KEY_SIZE];
1580 ret = string_hash_func(m->path, hash_key);
1582 /* Use a slightly different hash key for the interface */
1583 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1585 ret ^= string_hash_func(m->interface, hash_key2);
1587 /* And an even different one for the member */
1589 ret ^= string_hash_func(m->member, hash_key2);
1594 static int vtable_member_compare_func(const void *a, const void *b) {
1595 const struct vtable_member *x = a, *y = b;
1601 r = strcmp(x->path, y->path);
1605 r = strcmp(x->interface, y->interface);
1609 return strcmp(x->member, y->member);
1612 static const struct hash_ops vtable_member_hash_ops = {
1613 .hash = vtable_member_hash_func,
1614 .compare = vtable_member_compare_func
1617 static int add_object_vtable_internal(
1621 const char *interface,
1622 const sd_bus_vtable *vtable,
1624 sd_bus_object_find_t find,
1627 sd_bus_slot *s = NULL;
1628 struct node_vtable *i, *existing = NULL;
1629 const sd_bus_vtable *v;
1633 assert_return(bus, -EINVAL);
1634 assert_return(object_path_is_valid(path), -EINVAL);
1635 assert_return(interface_name_is_valid(interface), -EINVAL);
1636 assert_return(vtable, -EINVAL);
1637 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1638 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1639 assert_return(!bus_pid_changed(bus), -ECHILD);
1640 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1641 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1642 !streq(interface, "org.freedesktop.DBus.Peer") &&
1643 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1645 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1649 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1653 n = bus_node_allocate(bus, path);
1657 LIST_FOREACH(vtables, i, n->vtables) {
1658 if (i->is_fallback != fallback) {
1663 if (streq(i->interface, interface)) {
1665 if (i->vtable == vtable) {
1674 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1680 s->node_vtable.is_fallback = fallback;
1681 s->node_vtable.vtable = vtable;
1682 s->node_vtable.find = find;
1684 s->node_vtable.interface = strdup(interface);
1685 if (!s->node_vtable.interface) {
1690 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1694 case _SD_BUS_VTABLE_METHOD: {
1695 struct vtable_member *m;
1697 if (!member_name_is_valid(v->x.method.member) ||
1698 !signature_is_valid(strempty(v->x.method.signature), false) ||
1699 !signature_is_valid(strempty(v->x.method.result), false) ||
1700 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1701 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1706 m = new0(struct vtable_member, 1);
1712 m->parent = &s->node_vtable;
1714 m->interface = s->node_vtable.interface;
1715 m->member = v->x.method.member;
1718 r = hashmap_put(bus->vtable_methods, m, m);
1727 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1729 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1734 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1741 case _SD_BUS_VTABLE_PROPERTY: {
1742 struct vtable_member *m;
1744 if (!member_name_is_valid(v->x.property.member) ||
1745 !signature_is_single(v->x.property.signature, false) ||
1746 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1747 (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1748 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1749 ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1750 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1755 m = new0(struct vtable_member, 1);
1761 m->parent = &s->node_vtable;
1763 m->interface = s->node_vtable.interface;
1764 m->member = v->x.property.member;
1767 r = hashmap_put(bus->vtable_properties, m, m);
1776 case _SD_BUS_VTABLE_SIGNAL:
1778 if (!member_name_is_valid(v->x.signal.member) ||
1779 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1780 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1793 s->node_vtable.node = n;
1794 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1795 bus->nodes_modified = true;
1803 sd_bus_slot_unref(s);
1804 bus_node_gc(bus, n);
1809 _public_ int sd_bus_add_object_vtable(
1813 const char *interface,
1814 const sd_bus_vtable *vtable,
1817 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1820 _public_ int sd_bus_add_fallback_vtable(
1824 const char *interface,
1825 const sd_bus_vtable *vtable,
1826 sd_bus_object_find_t find,
1829 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1832 _public_ int sd_bus_add_node_enumerator(
1836 sd_bus_node_enumerator_t callback,
1843 assert_return(bus, -EINVAL);
1844 assert_return(object_path_is_valid(path), -EINVAL);
1845 assert_return(callback, -EINVAL);
1846 assert_return(!bus_pid_changed(bus), -ECHILD);
1848 n = bus_node_allocate(bus, path);
1852 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1858 s->node_enumerator.callback = callback;
1860 s->node_enumerator.node = n;
1861 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1862 bus->nodes_modified = true;
1870 sd_bus_slot_unref(s);
1871 bus_node_gc(bus, n);
1876 static int emit_properties_changed_on_interface(
1880 const char *interface,
1881 bool require_fallback,
1882 bool *found_interface,
1885 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1886 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1887 bool has_invalidating = false, has_changing = false;
1888 struct vtable_member key = {};
1889 struct node_vtable *c;
1899 assert(found_interface);
1901 n = hashmap_get(bus->nodes, prefix);
1905 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1909 r = sd_bus_message_append(m, "s", interface);
1913 r = sd_bus_message_open_container(m, 'a', "{sv}");
1918 key.interface = interface;
1920 LIST_FOREACH(vtables, c, n->vtables) {
1921 if (require_fallback && !c->is_fallback)
1924 if (!streq(c->interface, interface))
1927 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1930 if (bus->nodes_modified)
1935 *found_interface = true;
1938 /* If the caller specified a list of
1939 * properties we include exactly those in the
1940 * PropertiesChanged message */
1942 STRV_FOREACH(property, names) {
1943 struct vtable_member *v;
1945 assert_return(member_name_is_valid(*property), -EINVAL);
1947 key.member = *property;
1948 v = hashmap_get(bus->vtable_properties, &key);
1952 /* If there are two vtables for the same
1953 * interface, let's handle this property when
1954 * we come to that vtable. */
1958 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1959 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1961 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1963 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1964 has_invalidating = true;
1968 has_changing = true;
1970 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1973 if (bus->nodes_modified)
1977 const sd_bus_vtable *v;
1979 /* If the caller specified no properties list
1980 * we include all properties that are marked
1981 * as changing in the message. */
1983 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1984 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1987 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1990 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1991 has_invalidating = true;
1995 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1998 has_changing = true;
2000 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2003 if (bus->nodes_modified)
2009 if (!has_invalidating && !has_changing)
2012 r = sd_bus_message_close_container(m);
2016 r = sd_bus_message_open_container(m, 'a', "s");
2020 if (has_invalidating) {
2021 LIST_FOREACH(vtables, c, n->vtables) {
2022 if (require_fallback && !c->is_fallback)
2025 if (!streq(c->interface, interface))
2028 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2031 if (bus->nodes_modified)
2037 STRV_FOREACH(property, names) {
2038 struct vtable_member *v;
2040 key.member = *property;
2041 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2042 assert(c == v->parent);
2044 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2047 r = sd_bus_message_append(m, "s", *property);
2052 const sd_bus_vtable *v;
2054 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2055 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2058 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2061 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2064 r = sd_bus_message_append(m, "s", v->x.property.member);
2072 r = sd_bus_message_close_container(m);
2076 r = sd_bus_send(bus, m, NULL);
2083 _public_ int sd_bus_emit_properties_changed_strv(
2086 const char *interface,
2089 BUS_DONT_DESTROY(bus);
2090 bool found_interface = false;
2094 assert_return(bus, -EINVAL);
2095 assert_return(object_path_is_valid(path), -EINVAL);
2096 assert_return(interface_name_is_valid(interface), -EINVAL);
2097 assert_return(!bus_pid_changed(bus), -ECHILD);
2099 if (!BUS_IS_OPEN(bus->state))
2102 /* A non-NULL but empty names list means nothing needs to be
2103 generated. A NULL list OTOH indicates that all properties
2104 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2105 included in the PropertiesChanged message. */
2106 if (names && names[0] == NULL)
2110 bus->nodes_modified = false;
2112 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2115 if (bus->nodes_modified)
2118 prefix = alloca(strlen(path) + 1);
2119 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2120 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2123 if (bus->nodes_modified)
2127 } while (bus->nodes_modified);
2129 return found_interface ? 0 : -ENOENT;
2132 _public_ int sd_bus_emit_properties_changed(
2135 const char *interface,
2136 const char *name, ...) {
2140 assert_return(bus, -EINVAL);
2141 assert_return(object_path_is_valid(path), -EINVAL);
2142 assert_return(interface_name_is_valid(interface), -EINVAL);
2143 assert_return(!bus_pid_changed(bus), -ECHILD);
2145 if (!BUS_IS_OPEN(bus->state))
2151 names = strv_from_stdarg_alloca(name);
2153 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2156 static int object_added_append_all_prefix(
2162 bool require_fallback) {
2164 const char *previous_interface = NULL;
2165 struct node_vtable *c;
2175 n = hashmap_get(bus->nodes, prefix);
2179 LIST_FOREACH(vtables, c, n->vtables) {
2180 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2183 if (require_fallback && !c->is_fallback)
2186 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2189 if (bus->nodes_modified)
2194 if (!streq_ptr(c->interface, previous_interface)) {
2195 /* If a child-node already handled this interface, we
2196 * skip it on any of its parents. The child vtables
2197 * always fully override any conflicting vtables of
2198 * any parent node. */
2199 if (set_get(s, c->interface))
2202 r = set_put(s, c->interface);
2206 if (previous_interface) {
2207 r = sd_bus_message_close_container(m);
2210 r = sd_bus_message_close_container(m);
2215 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2218 r = sd_bus_message_append(m, "s", c->interface);
2221 r = sd_bus_message_open_container(m, 'a', "{sv}");
2225 previous_interface = c->interface;
2228 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2231 if (bus->nodes_modified)
2235 if (previous_interface) {
2236 r = sd_bus_message_close_container(m);
2239 r = sd_bus_message_close_container(m);
2247 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2248 _cleanup_set_free_ Set *s = NULL;
2257 * This appends all interfaces registered on path @path. We first add
2258 * the builtin interfaces, which are always available and handled by
2259 * sd-bus. Then, we add all interfaces registered on the exact node,
2260 * followed by all fallback interfaces registered on any parent prefix.
2262 * If an interface is registered multiple times on the same node with
2263 * different vtables, we merge all the properties across all vtables.
2264 * However, if a child node has the same interface registered as one of
2265 * its parent nodes has as fallback, we make the child overwrite the
2266 * parent instead of extending it. Therefore, we keep a "Set" of all
2267 * handled interfaces during parent traversal, so we skip interfaces on
2268 * a parent that were overwritten by a child.
2271 s = set_new(&string_hash_ops);
2275 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2278 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2281 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2284 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2288 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2291 if (bus->nodes_modified)
2294 prefix = alloca(strlen(path) + 1);
2295 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2296 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2299 if (bus->nodes_modified)
2306 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2307 BUS_DONT_DESTROY(bus);
2309 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2310 struct node *object_manager;
2314 * This emits an InterfacesAdded signal on the given path, by iterating
2315 * all registered vtables and fallback vtables on the path. All
2316 * properties are queried and included in the signal.
2317 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2318 * explicit list of registered interfaces. However, unlike
2319 * interfaces_added(), this call can figure out the list of supported
2320 * interfaces itself. Furthermore, it properly adds the builtin
2321 * org.freedesktop.DBus.* interfaces.
2324 assert_return(bus, -EINVAL);
2325 assert_return(object_path_is_valid(path), -EINVAL);
2326 assert_return(!bus_pid_changed(bus), -ECHILD);
2328 if (!BUS_IS_OPEN(bus->state))
2331 r = bus_find_parent_object_manager(bus, &object_manager, path);
2338 bus->nodes_modified = false;
2339 m = sd_bus_message_unref(m);
2341 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2345 r = sd_bus_message_append_basic(m, 'o', path);
2349 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2353 r = object_added_append_all(bus, m, path);
2357 if (bus->nodes_modified)
2360 r = sd_bus_message_close_container(m);
2364 } while (bus->nodes_modified);
2366 return sd_bus_send(bus, m, NULL);
2369 /// UNNEEDED by elogind
2371 static int object_removed_append_all_prefix(
2377 bool require_fallback) {
2379 const char *previous_interface = NULL;
2380 struct node_vtable *c;
2390 n = hashmap_get(bus->nodes, prefix);
2394 LIST_FOREACH(vtables, c, n->vtables) {
2395 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2398 if (require_fallback && !c->is_fallback)
2400 if (streq_ptr(c->interface, previous_interface))
2403 /* If a child-node already handled this interface, we
2404 * skip it on any of its parents. The child vtables
2405 * always fully override any conflicting vtables of
2406 * any parent node. */
2407 if (set_get(s, c->interface))
2410 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2413 if (bus->nodes_modified)
2418 r = set_put(s, c->interface);
2422 r = sd_bus_message_append(m, "s", c->interface);
2426 previous_interface = c->interface;
2432 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2433 _cleanup_set_free_ Set *s = NULL;
2441 /* see sd_bus_emit_object_added() for details */
2443 s = set_new(&string_hash_ops);
2447 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2450 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2453 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2456 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2460 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2463 if (bus->nodes_modified)
2466 prefix = alloca(strlen(path) + 1);
2467 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2468 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2471 if (bus->nodes_modified)
2478 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2479 BUS_DONT_DESTROY(bus);
2481 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2482 struct node *object_manager;
2486 * This is like sd_bus_emit_object_added(), but emits an
2487 * InterfacesRemoved signal on the given path. This only includes any
2488 * registered interfaces but skips the properties. Note that this will
2489 * call into the find() callbacks of any registered vtable. Therefore,
2490 * you must call this function before destroying/unlinking your object.
2491 * Otherwise, the list of interfaces will be incomplete. However, note
2492 * that this will *NOT* call into any property callback. Therefore, the
2493 * object might be in an "destructed" state, as long as we can find it.
2496 assert_return(bus, -EINVAL);
2497 assert_return(object_path_is_valid(path), -EINVAL);
2498 assert_return(!bus_pid_changed(bus), -ECHILD);
2500 if (!BUS_IS_OPEN(bus->state))
2503 r = bus_find_parent_object_manager(bus, &object_manager, path);
2510 bus->nodes_modified = false;
2511 m = sd_bus_message_unref(m);
2513 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2517 r = sd_bus_message_append_basic(m, 'o', path);
2521 r = sd_bus_message_open_container(m, 'a', "s");
2525 r = object_removed_append_all(bus, m, path);
2529 if (bus->nodes_modified)
2532 r = sd_bus_message_close_container(m);
2536 } while (bus->nodes_modified);
2538 return sd_bus_send(bus, m, NULL);
2542 static int interfaces_added_append_one_prefix(
2547 const char *interface,
2548 bool require_fallback) {
2550 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2551 bool found_interface = false;
2552 struct node_vtable *c;
2563 n = hashmap_get(bus->nodes, prefix);
2567 LIST_FOREACH(vtables, c, n->vtables) {
2568 if (require_fallback && !c->is_fallback)
2571 if (!streq(c->interface, interface))
2574 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2577 if (bus->nodes_modified)
2582 if (!found_interface) {
2583 r = sd_bus_message_append_basic(m, 's', interface);
2587 r = sd_bus_message_open_container(m, 'a', "{sv}");
2591 found_interface = true;
2594 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2597 if (bus->nodes_modified)
2601 if (found_interface) {
2602 r = sd_bus_message_close_container(m);
2607 return found_interface;
2610 static int interfaces_added_append_one(
2614 const char *interface) {
2624 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2627 if (bus->nodes_modified)
2630 prefix = alloca(strlen(path) + 1);
2631 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2632 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2635 if (bus->nodes_modified)
2642 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2643 BUS_DONT_DESTROY(bus);
2645 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2646 struct node *object_manager;
2650 assert_return(bus, -EINVAL);
2651 assert_return(object_path_is_valid(path), -EINVAL);
2652 assert_return(!bus_pid_changed(bus), -ECHILD);
2654 if (!BUS_IS_OPEN(bus->state))
2657 if (strv_isempty(interfaces))
2660 r = bus_find_parent_object_manager(bus, &object_manager, path);
2667 bus->nodes_modified = false;
2668 m = sd_bus_message_unref(m);
2670 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2674 r = sd_bus_message_append_basic(m, 'o', path);
2678 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2682 STRV_FOREACH(i, interfaces) {
2683 assert_return(interface_name_is_valid(*i), -EINVAL);
2685 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2689 r = interfaces_added_append_one(bus, m, path, *i);
2693 if (bus->nodes_modified)
2696 r = sd_bus_message_close_container(m);
2701 if (bus->nodes_modified)
2704 r = sd_bus_message_close_container(m);
2708 } while (bus->nodes_modified);
2710 return sd_bus_send(bus, m, NULL);
2713 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2716 assert_return(bus, -EINVAL);
2717 assert_return(object_path_is_valid(path), -EINVAL);
2718 assert_return(!bus_pid_changed(bus), -ECHILD);
2720 if (!BUS_IS_OPEN(bus->state))
2723 interfaces = strv_from_stdarg_alloca(interface);
2725 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2728 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2729 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2730 struct node *object_manager;
2733 assert_return(bus, -EINVAL);
2734 assert_return(object_path_is_valid(path), -EINVAL);
2735 assert_return(!bus_pid_changed(bus), -ECHILD);
2737 if (!BUS_IS_OPEN(bus->state))
2740 if (strv_isempty(interfaces))
2743 r = bus_find_parent_object_manager(bus, &object_manager, path);
2749 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2753 r = sd_bus_message_append_basic(m, 'o', path);
2757 r = sd_bus_message_append_strv(m, interfaces);
2761 return sd_bus_send(bus, m, NULL);
2764 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2767 assert_return(bus, -EINVAL);
2768 assert_return(object_path_is_valid(path), -EINVAL);
2769 assert_return(!bus_pid_changed(bus), -ECHILD);
2771 if (!BUS_IS_OPEN(bus->state))
2774 interfaces = strv_from_stdarg_alloca(interface);
2776 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2779 /// UNNEEDED by elogind
2781 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2786 assert_return(bus, -EINVAL);
2787 assert_return(object_path_is_valid(path), -EINVAL);
2788 assert_return(!bus_pid_changed(bus), -ECHILD);
2790 n = bus_node_allocate(bus, path);
2794 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2800 s->node_object_manager.node = n;
2801 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2802 bus->nodes_modified = true;
2810 sd_bus_slot_unref(s);
2811 bus_node_gc(bus, n);