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 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
756 if (bus->nodes_modified)
763 static int property_get_all_callbacks_run(
766 struct node_vtable *first,
767 bool require_fallback,
769 bool *found_object) {
771 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
772 struct node_vtable *c;
773 bool found_interface;
778 assert(found_object);
780 r = sd_bus_message_new_method_return(m, &reply);
784 r = sd_bus_message_open_container(reply, 'a', "{sv}");
788 found_interface = !iface ||
789 streq(iface, "org.freedesktop.DBus.Properties") ||
790 streq(iface, "org.freedesktop.DBus.Peer") ||
791 streq(iface, "org.freedesktop.DBus.Introspectable");
793 LIST_FOREACH(vtables, c, first) {
794 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
797 if (require_fallback && !c->is_fallback)
800 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
802 return bus_maybe_reply_error(m, r, &error);
803 if (bus->nodes_modified)
808 *found_object = true;
810 if (iface && !streq(c->interface, iface))
812 found_interface = true;
814 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
816 return bus_maybe_reply_error(m, r, &error);
817 if (bus->nodes_modified)
821 if (!found_interface) {
822 r = sd_bus_reply_method_errorf(
824 SD_BUS_ERROR_UNKNOWN_INTERFACE,
825 "Unknown interface '%s'.", iface);
832 r = sd_bus_message_close_container(reply);
836 r = sd_bus_send(bus, reply, NULL);
843 static int bus_node_exists(
847 bool require_fallback) {
849 struct node_vtable *c;
850 struct node_callback *k;
857 /* Tests if there's anything attached directly to this node
858 * for the specified path */
860 if (!require_fallback && (n->enumerators || n->object_managers))
863 LIST_FOREACH(callbacks, k, n->callbacks) {
864 if (require_fallback && !k->is_fallback)
870 LIST_FOREACH(vtables, c, n->vtables) {
871 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
873 if (require_fallback && !c->is_fallback)
876 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
879 if (bus->nodes_modified)
886 static int process_introspect(
890 bool require_fallback,
891 bool *found_object) {
893 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
894 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
895 _cleanup_set_free_free_ Set *s = NULL;
896 const char *previous_interface = NULL;
897 struct introspect intro;
898 struct node_vtable *c;
905 assert(found_object);
907 r = get_child_nodes(bus, m->path, n, false, &s, &error);
909 return bus_maybe_reply_error(m, r, &error);
910 if (bus->nodes_modified)
913 r = introspect_begin(&intro, bus->trusted);
917 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
921 empty = set_isempty(s);
923 LIST_FOREACH(vtables, c, n->vtables) {
924 if (require_fallback && !c->is_fallback)
927 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
929 r = bus_maybe_reply_error(m, r, &error);
932 if (bus->nodes_modified) {
941 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
944 if (!streq_ptr(previous_interface, c->interface)) {
946 if (previous_interface)
947 fputs(" </interface>\n", intro.f);
949 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
952 r = introspect_write_interface(&intro, c->vtable);
956 previous_interface = c->interface;
959 if (previous_interface)
960 fputs(" </interface>\n", intro.f);
963 /* Nothing?, let's see if we exist at all, and if not
964 * refuse to do anything */
965 r = bus_node_exists(bus, n, m->path, require_fallback);
968 if (bus->nodes_modified) {
974 *found_object = true;
976 r = introspect_write_child_nodes(&intro, s, m->path);
980 r = introspect_finish(&intro, bus, m, &reply);
984 r = sd_bus_send(bus, reply, NULL);
991 introspect_free(&intro);
995 static int object_manager_serialize_path(
997 sd_bus_message *reply,
1000 bool require_fallback,
1001 sd_bus_error *error) {
1003 const char *previous_interface = NULL;
1004 bool found_something = false;
1005 struct node_vtable *i;
1015 n = hashmap_get(bus->nodes, prefix);
1019 LIST_FOREACH(vtables, i, n->vtables) {
1022 if (require_fallback && !i->is_fallback)
1025 r = node_vtable_get_userdata(bus, path, i, &u, error);
1028 if (bus->nodes_modified)
1033 if (!found_something) {
1035 /* Open the object part */
1037 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1041 r = sd_bus_message_append(reply, "o", path);
1045 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1049 found_something = true;
1052 if (!streq_ptr(previous_interface, i->interface)) {
1054 /* Maybe close the previous interface part */
1056 if (previous_interface) {
1057 r = sd_bus_message_close_container(reply);
1061 r = sd_bus_message_close_container(reply);
1066 /* Open the new interface part */
1068 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1072 r = sd_bus_message_append(reply, "s", i->interface);
1076 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1081 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1084 if (bus->nodes_modified)
1087 previous_interface = i->interface;
1090 if (previous_interface) {
1091 r = sd_bus_message_close_container(reply);
1095 r = sd_bus_message_close_container(reply);
1100 if (found_something) {
1101 r = sd_bus_message_close_container(reply);
1105 r = sd_bus_message_close_container(reply);
1113 static int object_manager_serialize_path_and_fallbacks(
1115 sd_bus_message *reply,
1117 sd_bus_error *error) {
1127 /* First, add all vtables registered for this path */
1128 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1131 if (bus->nodes_modified)
1134 /* Second, add fallback vtables registered for any of the prefixes */
1135 prefix = alloca(strlen(path) + 1);
1136 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1137 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1140 if (bus->nodes_modified)
1147 static int process_get_managed_objects(
1151 bool require_fallback,
1152 bool *found_object) {
1154 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1155 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1156 _cleanup_set_free_free_ Set *s = NULL;
1164 assert(found_object);
1166 /* Spec says, GetManagedObjects() is only implemented on the root of a
1167 * sub-tree. Therefore, we require a registered object-manager on
1168 * exactly the queried path, otherwise, we refuse to respond. */
1170 if (require_fallback || !n->object_managers)
1173 r = get_child_nodes(bus, m->path, n, true, &s, &error);
1176 if (bus->nodes_modified)
1179 r = set_put_strdup(s, m->path);
1183 r = sd_bus_message_new_method_return(m, &reply);
1187 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1191 SET_FOREACH(path, s, i) {
1192 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1196 if (bus->nodes_modified)
1200 r = sd_bus_message_close_container(reply);
1204 r = sd_bus_send(bus, reply, NULL);
1211 static int object_find_and_run(
1215 bool require_fallback,
1216 bool *found_object) {
1219 struct vtable_member vtable_key, *v;
1225 assert(found_object);
1227 n = hashmap_get(bus->nodes, p);
1231 /* First, try object callbacks */
1232 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1235 if (bus->nodes_modified)
1238 if (!m->interface || !m->member)
1241 /* Then, look for a known method */
1242 vtable_key.path = (char*) p;
1243 vtable_key.interface = m->interface;
1244 vtable_key.member = m->member;
1246 v = hashmap_get(bus->vtable_methods, &vtable_key);
1248 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1251 if (bus->nodes_modified)
1255 /* Then, look for a known property */
1256 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1259 get = streq(m->member, "Get");
1261 if (get || streq(m->member, "Set")) {
1263 r = sd_bus_message_rewind(m, true);
1267 vtable_key.path = (char*) p;
1269 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1271 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1273 v = hashmap_get(bus->vtable_properties, &vtable_key);
1275 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1280 } else if (streq(m->member, "GetAll")) {
1283 r = sd_bus_message_rewind(m, true);
1287 r = sd_bus_message_read(m, "s", &iface);
1289 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1294 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1299 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1301 if (!isempty(sd_bus_message_get_signature(m, true)))
1302 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1304 r = process_introspect(bus, m, n, require_fallback, found_object);
1308 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1310 if (!isempty(sd_bus_message_get_signature(m, true)))
1311 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1313 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1318 if (bus->nodes_modified)
1321 if (!*found_object) {
1322 r = bus_node_exists(bus, n, m->path, require_fallback);
1325 if (bus->nodes_modified)
1328 *found_object = true;
1334 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1337 bool found_object = false;
1342 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1345 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1348 if (hashmap_isempty(bus->nodes))
1351 /* Never respond to broadcast messages */
1352 if (bus->bus_client && !m->destination)
1358 pl = strlen(m->path);
1362 bus->nodes_modified = false;
1364 r = object_find_and_run(bus, m, m->path, false, &found_object);
1368 /* Look for fallback prefixes */
1369 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1371 if (bus->nodes_modified)
1374 r = object_find_and_run(bus, m, prefix, true, &found_object);
1379 } while (bus->nodes_modified);
1384 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1385 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1386 r = sd_bus_reply_method_errorf(
1388 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1389 "Unknown property or interface.");
1391 r = sd_bus_reply_method_errorf(
1393 SD_BUS_ERROR_UNKNOWN_METHOD,
1394 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1402 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1403 struct node *n, *parent;
1405 _cleanup_free_ char *s = NULL;
1411 assert(path[0] == '/');
1413 n = hashmap_get(bus->nodes, path);
1417 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1425 if (streq(path, "/"))
1428 e = strrchr(path, '/');
1431 p = strndupa(path, MAX(1, e - path));
1433 parent = bus_node_allocate(bus, p);
1438 n = new0(struct node, 1);
1444 s = NULL; /* do not free */
1446 r = hashmap_put(bus->nodes, n->path, n);
1454 LIST_PREPEND(siblings, parent->child, n);
1459 void bus_node_gc(sd_bus *b, struct node *n) {
1472 assert(hashmap_remove(b->nodes, n->path) == n);
1475 LIST_REMOVE(siblings, n->parent->child, n);
1478 bus_node_gc(b, n->parent);
1482 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1488 n = hashmap_get(bus->nodes, path);
1492 prefix = alloca(strlen(path) + 1);
1493 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1494 n = hashmap_get(bus->nodes, prefix);
1500 while (n && !n->object_managers)
1508 static int bus_add_object(
1513 sd_bus_message_handler_t callback,
1520 assert_return(bus, -EINVAL);
1521 assert_return(object_path_is_valid(path), -EINVAL);
1522 assert_return(callback, -EINVAL);
1523 assert_return(!bus_pid_changed(bus), -ECHILD);
1525 n = bus_node_allocate(bus, path);
1529 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1535 s->node_callback.callback = callback;
1536 s->node_callback.is_fallback = fallback;
1538 s->node_callback.node = n;
1539 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1540 bus->nodes_modified = true;
1548 sd_bus_slot_unref(s);
1549 bus_node_gc(bus, n);
1554 _public_ int sd_bus_add_object(
1558 sd_bus_message_handler_t callback,
1561 return bus_add_object(bus, slot, false, path, callback, userdata);
1564 _public_ int sd_bus_add_fallback(
1568 sd_bus_message_handler_t callback,
1571 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1574 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1575 const struct vtable_member *m = a;
1576 uint8_t hash_key2[HASH_KEY_SIZE];
1581 ret = string_hash_func(m->path, hash_key);
1583 /* Use a slightly different hash key for the interface */
1584 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1586 ret ^= string_hash_func(m->interface, hash_key2);
1588 /* And an even different one for the member */
1590 ret ^= string_hash_func(m->member, hash_key2);
1595 static int vtable_member_compare_func(const void *a, const void *b) {
1596 const struct vtable_member *x = a, *y = b;
1602 r = strcmp(x->path, y->path);
1606 r = strcmp(x->interface, y->interface);
1610 return strcmp(x->member, y->member);
1613 static const struct hash_ops vtable_member_hash_ops = {
1614 .hash = vtable_member_hash_func,
1615 .compare = vtable_member_compare_func
1618 static int add_object_vtable_internal(
1622 const char *interface,
1623 const sd_bus_vtable *vtable,
1625 sd_bus_object_find_t find,
1628 sd_bus_slot *s = NULL;
1629 struct node_vtable *i, *existing = NULL;
1630 const sd_bus_vtable *v;
1634 assert_return(bus, -EINVAL);
1635 assert_return(object_path_is_valid(path), -EINVAL);
1636 assert_return(interface_name_is_valid(interface), -EINVAL);
1637 assert_return(vtable, -EINVAL);
1638 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1639 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1640 assert_return(!bus_pid_changed(bus), -ECHILD);
1641 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1642 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1643 !streq(interface, "org.freedesktop.DBus.Peer") &&
1644 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1646 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1650 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1654 n = bus_node_allocate(bus, path);
1658 LIST_FOREACH(vtables, i, n->vtables) {
1659 if (i->is_fallback != fallback) {
1664 if (streq(i->interface, interface)) {
1666 if (i->vtable == vtable) {
1675 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1681 s->node_vtable.is_fallback = fallback;
1682 s->node_vtable.vtable = vtable;
1683 s->node_vtable.find = find;
1685 s->node_vtable.interface = strdup(interface);
1686 if (!s->node_vtable.interface) {
1691 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1695 case _SD_BUS_VTABLE_METHOD: {
1696 struct vtable_member *m;
1698 if (!member_name_is_valid(v->x.method.member) ||
1699 !signature_is_valid(strempty(v->x.method.signature), false) ||
1700 !signature_is_valid(strempty(v->x.method.result), false) ||
1701 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1702 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1707 m = new0(struct vtable_member, 1);
1713 m->parent = &s->node_vtable;
1715 m->interface = s->node_vtable.interface;
1716 m->member = v->x.method.member;
1719 r = hashmap_put(bus->vtable_methods, m, m);
1728 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1730 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1735 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1742 case _SD_BUS_VTABLE_PROPERTY: {
1743 struct vtable_member *m;
1745 if (!member_name_is_valid(v->x.property.member) ||
1746 !signature_is_single(v->x.property.signature, false) ||
1747 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1748 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1749 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
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);