2 This file is part of systemd.
4 Copyright 2013 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include "alloc-util.h"
21 #include "bus-internal.h"
22 #include "bus-introspect.h"
23 #include "bus-message.h"
24 #include "bus-objects.h"
25 #include "bus-signature.h"
30 #include "string-util.h"
33 static int node_vtable_get_userdata(
36 struct node_vtable *c,
38 sd_bus_error *error) {
48 s = container_of(c, sd_bus_slot, node_vtable);
51 bus->current_slot = sd_bus_slot_ref(s);
52 bus->current_userdata = u;
53 r = c->find(bus, path, c->interface, u, &u, error);
54 bus->current_userdata = NULL;
55 bus->current_slot = sd_bus_slot_unref(s);
59 if (sd_bus_error_is_set(error))
60 return -sd_bus_error_get_errno(error);
71 static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
74 return (uint8_t*) u + p->x.method.offset;
77 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
80 return (uint8_t*) u + p->x.property.offset;
83 static int vtable_property_get_userdata(
86 struct vtable_member *p,
88 sd_bus_error *error) {
98 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
101 if (bus->nodes_modified)
104 *userdata = vtable_property_convert_userdata(p->vtable, u);
108 static int add_enumerated_to_set(
111 struct node_enumerator *first,
113 sd_bus_error *error) {
115 struct node_enumerator *c;
122 LIST_FOREACH(enumerators, c, first) {
123 char **children = NULL, **k;
126 if (bus->nodes_modified)
129 slot = container_of(c, sd_bus_slot, node_enumerator);
131 bus->current_slot = sd_bus_slot_ref(slot);
132 bus->current_userdata = slot->userdata;
133 r = c->callback(bus, prefix, slot->userdata, &children, error);
134 bus->current_userdata = NULL;
135 bus->current_slot = sd_bus_slot_unref(slot);
139 if (sd_bus_error_is_set(error))
140 return -sd_bus_error_get_errno(error);
142 STRV_FOREACH(k, children) {
148 if (!object_path_is_valid(*k)) {
154 if (!object_path_startswith(*k, prefix)) {
159 r = set_consume(s, *k);
173 /* if set, add_subtree() works recursively */
174 CHILDREN_RECURSIVE = (1U << 1),
175 /* if set, add_subtree() scans object-manager hierarchies recursively */
176 CHILDREN_SUBHIERARCHIES = (1U << 0),
179 static int add_subtree_to_set(
185 sd_bus_error *error) {
195 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
198 if (bus->nodes_modified)
201 LIST_FOREACH(siblings, i, n->child) {
204 if (!object_path_startswith(i->path, prefix))
211 r = set_consume(s, t);
212 if (r < 0 && r != -EEXIST)
215 if ((flags & CHILDREN_RECURSIVE) &&
216 ((flags & CHILDREN_SUBHIERARCHIES) || !i->object_managers)) {
217 r = add_subtree_to_set(bus, prefix, i, flags, s, error);
220 if (bus->nodes_modified)
228 static int get_child_nodes(
234 sd_bus_error *error) {
244 s = set_new(&string_hash_ops);
248 r = add_subtree_to_set(bus, prefix, n, flags, s, error);
258 static int node_callbacks_run(
261 struct node_callback *first,
262 bool require_fallback,
263 bool *found_object) {
265 struct node_callback *c;
270 assert(found_object);
272 LIST_FOREACH(callbacks, c, first) {
273 _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
276 if (bus->nodes_modified)
279 if (require_fallback && !c->is_fallback)
282 *found_object = true;
284 if (c->last_iteration == bus->iteration_counter)
287 c->last_iteration = bus->iteration_counter;
289 r = sd_bus_message_rewind(m, true);
293 slot = container_of(c, sd_bus_slot, node_callback);
295 bus->current_slot = sd_bus_slot_ref(slot);
296 bus->current_handler = c->callback;
297 bus->current_userdata = slot->userdata;
298 r = c->callback(m, slot->userdata, &error_buffer);
299 bus->current_userdata = NULL;
300 bus->current_handler = NULL;
301 bus->current_slot = sd_bus_slot_unref(slot);
303 r = bus_maybe_reply_error(m, r, &error_buffer);
311 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
313 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
321 /* If the entire bus is trusted let's grant access */
325 /* If the member is marked UNPRIVILEGED let's grant access */
326 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
329 /* Check have the caller has the requested capability
330 * set. Note that the flags value contains the capability
331 * number plus one, which we need to subtract here. We do this
332 * so that we have 0 as special value for "default
334 cap = CAPABILITY_SHIFT(c->vtable->flags);
336 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
342 r = sd_bus_query_sender_privilege(m, cap);
348 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
351 static int method_callbacks_run(
354 struct vtable_member *c,
355 bool require_fallback,
356 bool *found_object) {
358 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
359 const char *signature;
366 assert(found_object);
368 if (require_fallback && !c->parent->is_fallback)
371 r = check_access(bus, m, c, &error);
373 return bus_maybe_reply_error(m, r, &error);
375 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
377 return bus_maybe_reply_error(m, r, &error);
378 if (bus->nodes_modified)
381 u = vtable_method_convert_userdata(c->vtable, u);
383 *found_object = true;
385 if (c->last_iteration == bus->iteration_counter)
388 c->last_iteration = bus->iteration_counter;
390 r = sd_bus_message_rewind(m, true);
394 signature = sd_bus_message_get_signature(m, true);
398 if (!streq(strempty(c->vtable->x.method.signature), signature))
399 return sd_bus_reply_method_errorf(
401 SD_BUS_ERROR_INVALID_ARGS,
402 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
403 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
405 /* Keep track what the signature of the reply to this message
406 * should be, so that this can be enforced when sealing the
408 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
410 if (c->vtable->x.method.handler) {
413 slot = container_of(c->parent, sd_bus_slot, node_vtable);
415 bus->current_slot = sd_bus_slot_ref(slot);
416 bus->current_handler = c->vtable->x.method.handler;
417 bus->current_userdata = u;
418 r = c->vtable->x.method.handler(m, u, &error);
419 bus->current_userdata = NULL;
420 bus->current_handler = NULL;
421 bus->current_slot = sd_bus_slot_unref(slot);
423 return bus_maybe_reply_error(m, r, &error);
426 /* If the method callback is NULL, make this a successful NOP */
427 r = sd_bus_reply_method_return(m, NULL);
434 static int invoke_property_get(
437 const sd_bus_vtable *v,
439 const char *interface,
440 const char *property,
441 sd_bus_message *reply,
443 sd_bus_error *error) {
456 if (v->x.property.get) {
458 bus->current_slot = sd_bus_slot_ref(slot);
459 bus->current_userdata = userdata;
460 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
461 bus->current_userdata = NULL;
462 bus->current_slot = sd_bus_slot_unref(slot);
466 if (sd_bus_error_is_set(error))
467 return -sd_bus_error_get_errno(error);
471 /* Automatic handling if no callback is defined. */
473 if (streq(v->x.property.signature, "as"))
474 return sd_bus_message_append_strv(reply, *(char***) userdata);
476 assert(signature_is_single(v->x.property.signature, false));
477 assert(bus_type_is_basic(v->x.property.signature[0]));
479 switch (v->x.property.signature[0]) {
481 case SD_BUS_TYPE_STRING:
482 case SD_BUS_TYPE_SIGNATURE:
483 p = strempty(*(char**) userdata);
486 case SD_BUS_TYPE_OBJECT_PATH:
487 p = *(char**) userdata;
496 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
499 static int invoke_property_set(
502 const sd_bus_vtable *v,
504 const char *interface,
505 const char *property,
506 sd_bus_message *value,
508 sd_bus_error *error) {
520 if (v->x.property.set) {
522 bus->current_slot = sd_bus_slot_ref(slot);
523 bus->current_userdata = userdata;
524 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
525 bus->current_userdata = NULL;
526 bus->current_slot = sd_bus_slot_unref(slot);
530 if (sd_bus_error_is_set(error))
531 return -sd_bus_error_get_errno(error);
535 /* Automatic handling if no callback is defined. */
537 assert(signature_is_single(v->x.property.signature, false));
538 assert(bus_type_is_basic(v->x.property.signature[0]));
540 switch (v->x.property.signature[0]) {
542 case SD_BUS_TYPE_STRING:
543 case SD_BUS_TYPE_OBJECT_PATH:
544 case SD_BUS_TYPE_SIGNATURE: {
548 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
556 free(*(char**) userdata);
557 *(char**) userdata = n;
563 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
573 static int property_get_set_callbacks_run(
576 struct vtable_member *c,
577 bool require_fallback,
579 bool *found_object) {
581 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
582 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
590 assert(found_object);
592 if (require_fallback && !c->parent->is_fallback)
595 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
597 return bus_maybe_reply_error(m, r, &error);
598 if (bus->nodes_modified)
601 slot = container_of(c->parent, sd_bus_slot, node_vtable);
603 *found_object = true;
605 r = sd_bus_message_new_method_return(m, &reply);
610 /* Note that we do not protect against reexecution
611 * here (using the last_iteration check, see below),
612 * should the node tree have changed and we got called
613 * again. We assume that property Get() calls are
614 * ultimately without side-effects or if they aren't
615 * then at least idempotent. */
617 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
621 /* Note that we do not do an access check here. Read
622 * access to properties is always unrestricted, since
623 * PropertiesChanged signals broadcast contents
626 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
628 return bus_maybe_reply_error(m, r, &error);
630 if (bus->nodes_modified)
633 r = sd_bus_message_close_container(reply);
638 const char *signature = NULL;
641 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
642 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
644 /* Avoid that we call the set routine more than once
645 * if the processing of this message got restarted
646 * because the node tree changed. */
647 if (c->last_iteration == bus->iteration_counter)
650 c->last_iteration = bus->iteration_counter;
652 r = sd_bus_message_peek_type(m, &type, &signature);
656 if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
657 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature));
659 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
663 r = check_access(bus, m, c, &error);
665 return bus_maybe_reply_error(m, r, &error);
667 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
669 return bus_maybe_reply_error(m, r, &error);
671 if (bus->nodes_modified)
674 r = sd_bus_message_exit_container(m);
679 r = sd_bus_send(bus, reply, NULL);
686 static int vtable_append_one_property(
688 sd_bus_message *reply,
690 struct node_vtable *c,
691 const sd_bus_vtable *v,
693 sd_bus_error *error) {
704 r = sd_bus_message_open_container(reply, 'e', "sv");
708 r = sd_bus_message_append(reply, "s", v->x.property.member);
712 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
716 slot = container_of(c, sd_bus_slot, node_vtable);
718 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
721 if (bus->nodes_modified)
724 r = sd_bus_message_close_container(reply);
728 r = sd_bus_message_close_container(reply);
735 static int vtable_append_all_properties(
737 sd_bus_message *reply,
739 struct node_vtable *c,
741 sd_bus_error *error) {
743 const sd_bus_vtable *v;
751 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
754 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
755 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
758 if (v->flags & SD_BUS_VTABLE_HIDDEN)
761 if (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)
764 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
767 if (bus->nodes_modified)
774 static int property_get_all_callbacks_run(
777 struct node_vtable *first,
778 bool require_fallback,
780 bool *found_object) {
782 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
783 struct node_vtable *c;
784 bool found_interface;
789 assert(found_object);
791 r = sd_bus_message_new_method_return(m, &reply);
795 r = sd_bus_message_open_container(reply, 'a', "{sv}");
799 found_interface = !iface ||
800 streq(iface, "org.freedesktop.DBus.Properties") ||
801 streq(iface, "org.freedesktop.DBus.Peer") ||
802 streq(iface, "org.freedesktop.DBus.Introspectable");
804 LIST_FOREACH(vtables, c, first) {
805 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
808 if (require_fallback && !c->is_fallback)
811 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
813 return bus_maybe_reply_error(m, r, &error);
814 if (bus->nodes_modified)
819 *found_object = true;
821 if (iface && !streq(c->interface, iface))
823 found_interface = true;
825 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
827 return bus_maybe_reply_error(m, r, &error);
828 if (bus->nodes_modified)
832 if (!found_interface) {
833 r = sd_bus_reply_method_errorf(
835 SD_BUS_ERROR_UNKNOWN_INTERFACE,
836 "Unknown interface '%s'.", iface);
843 r = sd_bus_message_close_container(reply);
847 r = sd_bus_send(bus, reply, NULL);
854 static int bus_node_exists(
858 bool require_fallback) {
860 struct node_vtable *c;
861 struct node_callback *k;
868 /* Tests if there's anything attached directly to this node
869 * for the specified path */
871 if (!require_fallback && (n->enumerators || n->object_managers))
874 LIST_FOREACH(callbacks, k, n->callbacks) {
875 if (require_fallback && !k->is_fallback)
881 LIST_FOREACH(vtables, c, n->vtables) {
882 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
884 if (require_fallback && !c->is_fallback)
887 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
890 if (bus->nodes_modified)
897 static int process_introspect(
901 bool require_fallback,
902 bool *found_object) {
904 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
905 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
906 _cleanup_set_free_free_ Set *s = NULL;
907 const char *previous_interface = NULL;
908 struct introspect intro;
909 struct node_vtable *c;
916 assert(found_object);
918 r = get_child_nodes(bus, m->path, n, 0, &s, &error);
920 return bus_maybe_reply_error(m, r, &error);
921 if (bus->nodes_modified)
924 r = introspect_begin(&intro, bus->trusted);
928 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
932 empty = set_isempty(s);
934 LIST_FOREACH(vtables, c, n->vtables) {
935 if (require_fallback && !c->is_fallback)
938 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
940 r = bus_maybe_reply_error(m, r, &error);
943 if (bus->nodes_modified) {
952 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
955 if (!streq_ptr(previous_interface, c->interface)) {
957 if (previous_interface)
958 fputs(" </interface>\n", intro.f);
960 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
963 r = introspect_write_interface(&intro, c->vtable);
967 previous_interface = c->interface;
970 if (previous_interface)
971 fputs(" </interface>\n", intro.f);
974 /* Nothing?, let's see if we exist at all, and if not
975 * refuse to do anything */
976 r = bus_node_exists(bus, n, m->path, require_fallback);
979 if (bus->nodes_modified) {
985 *found_object = true;
987 r = introspect_write_child_nodes(&intro, s, m->path);
991 r = introspect_finish(&intro, bus, m, &reply);
995 r = sd_bus_send(bus, reply, NULL);
1002 introspect_free(&intro);
1006 static int object_manager_serialize_path(
1008 sd_bus_message *reply,
1011 bool require_fallback,
1012 sd_bus_error *error) {
1014 const char *previous_interface = NULL;
1015 bool found_something = false;
1016 struct node_vtable *i;
1026 n = hashmap_get(bus->nodes, prefix);
1030 LIST_FOREACH(vtables, i, n->vtables) {
1033 if (require_fallback && !i->is_fallback)
1036 r = node_vtable_get_userdata(bus, path, i, &u, error);
1039 if (bus->nodes_modified)
1044 if (!found_something) {
1046 /* Open the object part */
1048 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1052 r = sd_bus_message_append(reply, "o", path);
1056 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1060 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
1064 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
1068 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
1072 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1076 found_something = true;
1079 if (!streq_ptr(previous_interface, i->interface)) {
1081 /* Maybe close the previous interface part */
1083 if (previous_interface) {
1084 r = sd_bus_message_close_container(reply);
1088 r = sd_bus_message_close_container(reply);
1093 /* Open the new interface part */
1095 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1099 r = sd_bus_message_append(reply, "s", i->interface);
1103 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1108 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1111 if (bus->nodes_modified)
1114 previous_interface = i->interface;
1117 if (previous_interface) {
1118 r = sd_bus_message_close_container(reply);
1122 r = sd_bus_message_close_container(reply);
1127 if (found_something) {
1128 r = sd_bus_message_close_container(reply);
1132 r = sd_bus_message_close_container(reply);
1140 static int object_manager_serialize_path_and_fallbacks(
1142 sd_bus_message *reply,
1144 sd_bus_error *error) {
1154 /* First, add all vtables registered for this path */
1155 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1158 if (bus->nodes_modified)
1161 /* Second, add fallback vtables registered for any of the prefixes */
1162 prefix = alloca(strlen(path) + 1);
1163 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1164 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1167 if (bus->nodes_modified)
1174 static int process_get_managed_objects(
1178 bool require_fallback,
1179 bool *found_object) {
1181 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1182 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
1183 _cleanup_set_free_free_ Set *s = NULL;
1191 assert(found_object);
1193 /* Spec says, GetManagedObjects() is only implemented on the root of a
1194 * sub-tree. Therefore, we require a registered object-manager on
1195 * exactly the queried path, otherwise, we refuse to respond. */
1197 if (require_fallback || !n->object_managers)
1200 r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
1203 if (bus->nodes_modified)
1206 r = sd_bus_message_new_method_return(m, &reply);
1210 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1214 SET_FOREACH(path, s, i) {
1215 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1219 if (bus->nodes_modified)
1223 r = sd_bus_message_close_container(reply);
1227 r = sd_bus_send(bus, reply, NULL);
1234 static int object_find_and_run(
1238 bool require_fallback,
1239 bool *found_object) {
1242 struct vtable_member vtable_key, *v;
1248 assert(found_object);
1250 n = hashmap_get(bus->nodes, p);
1254 /* First, try object callbacks */
1255 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1258 if (bus->nodes_modified)
1261 if (!m->interface || !m->member)
1264 /* Then, look for a known method */
1265 vtable_key.path = (char*) p;
1266 vtable_key.interface = m->interface;
1267 vtable_key.member = m->member;
1269 v = hashmap_get(bus->vtable_methods, &vtable_key);
1271 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1274 if (bus->nodes_modified)
1278 /* Then, look for a known property */
1279 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1282 get = streq(m->member, "Get");
1284 if (get || streq(m->member, "Set")) {
1286 r = sd_bus_message_rewind(m, true);
1290 vtable_key.path = (char*) p;
1292 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1294 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1296 v = hashmap_get(bus->vtable_properties, &vtable_key);
1298 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1303 } else if (streq(m->member, "GetAll")) {
1306 r = sd_bus_message_rewind(m, true);
1310 r = sd_bus_message_read(m, "s", &iface);
1312 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1317 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1322 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1324 if (!isempty(sd_bus_message_get_signature(m, true)))
1325 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1327 r = process_introspect(bus, m, n, require_fallback, found_object);
1331 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1333 if (!isempty(sd_bus_message_get_signature(m, true)))
1334 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1336 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1341 if (bus->nodes_modified)
1344 if (!*found_object) {
1345 r = bus_node_exists(bus, n, m->path, require_fallback);
1348 if (bus->nodes_modified)
1351 *found_object = true;
1357 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1360 bool found_object = false;
1365 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1368 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1371 if (hashmap_isempty(bus->nodes))
1374 /* Never respond to broadcast messages */
1375 if (bus->bus_client && !m->destination)
1381 pl = strlen(m->path);
1385 bus->nodes_modified = false;
1387 r = object_find_and_run(bus, m, m->path, false, &found_object);
1391 /* Look for fallback prefixes */
1392 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1394 if (bus->nodes_modified)
1397 r = object_find_and_run(bus, m, prefix, true, &found_object);
1402 } while (bus->nodes_modified);
1407 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1408 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1409 r = sd_bus_reply_method_errorf(
1411 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1412 "Unknown property or interface.");
1414 r = sd_bus_reply_method_errorf(
1416 SD_BUS_ERROR_UNKNOWN_METHOD,
1417 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1425 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1426 struct node *n, *parent;
1428 _cleanup_free_ char *s = NULL;
1434 assert(path[0] == '/');
1436 n = hashmap_get(bus->nodes, path);
1440 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1448 if (streq(path, "/"))
1451 e = strrchr(path, '/');
1454 p = strndupa(path, MAX(1, e - path));
1456 parent = bus_node_allocate(bus, p);
1461 n = new0(struct node, 1);
1467 s = NULL; /* do not free */
1469 r = hashmap_put(bus->nodes, n->path, n);
1477 LIST_PREPEND(siblings, parent->child, n);
1482 void bus_node_gc(sd_bus *b, struct node *n) {
1495 assert(hashmap_remove(b->nodes, n->path) == n);
1498 LIST_REMOVE(siblings, n->parent->child, n);
1501 bus_node_gc(b, n->parent);
1505 static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1511 n = hashmap_get(bus->nodes, path);
1515 prefix = alloca(strlen(path) + 1);
1516 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1517 n = hashmap_get(bus->nodes, prefix);
1523 while (n && !n->object_managers)
1531 static int bus_add_object(
1536 sd_bus_message_handler_t callback,
1543 assert_return(bus, -EINVAL);
1544 assert_return(object_path_is_valid(path), -EINVAL);
1545 assert_return(callback, -EINVAL);
1546 assert_return(!bus_pid_changed(bus), -ECHILD);
1548 n = bus_node_allocate(bus, path);
1552 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1558 s->node_callback.callback = callback;
1559 s->node_callback.is_fallback = fallback;
1561 s->node_callback.node = n;
1562 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1563 bus->nodes_modified = true;
1571 sd_bus_slot_unref(s);
1572 bus_node_gc(bus, n);
1577 _public_ int sd_bus_add_object(
1581 sd_bus_message_handler_t callback,
1584 return bus_add_object(bus, slot, false, path, callback, userdata);
1587 _public_ int sd_bus_add_fallback(
1591 sd_bus_message_handler_t callback,
1594 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1597 static void vtable_member_hash_func(const void *a, struct siphash *state) {
1598 const struct vtable_member *m = a;
1602 string_hash_func(m->path, state);
1603 string_hash_func(m->interface, state);
1604 string_hash_func(m->member, state);
1607 static int vtable_member_compare_func(const void *a, const void *b) {
1608 const struct vtable_member *x = a, *y = b;
1614 r = strcmp(x->path, y->path);
1618 r = strcmp(x->interface, y->interface);
1622 return strcmp(x->member, y->member);
1625 static const struct hash_ops vtable_member_hash_ops = {
1626 .hash = vtable_member_hash_func,
1627 .compare = vtable_member_compare_func
1630 static int add_object_vtable_internal(
1634 const char *interface,
1635 const sd_bus_vtable *vtable,
1637 sd_bus_object_find_t find,
1640 sd_bus_slot *s = NULL;
1641 struct node_vtable *i, *existing = NULL;
1642 const sd_bus_vtable *v;
1646 assert_return(bus, -EINVAL);
1647 assert_return(object_path_is_valid(path), -EINVAL);
1648 assert_return(interface_name_is_valid(interface), -EINVAL);
1649 assert_return(vtable, -EINVAL);
1650 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1651 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1652 assert_return(!bus_pid_changed(bus), -ECHILD);
1653 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1654 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1655 !streq(interface, "org.freedesktop.DBus.Peer") &&
1656 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1658 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1662 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1666 n = bus_node_allocate(bus, path);
1670 LIST_FOREACH(vtables, i, n->vtables) {
1671 if (i->is_fallback != fallback) {
1676 if (streq(i->interface, interface)) {
1678 if (i->vtable == vtable) {
1687 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1693 s->node_vtable.is_fallback = fallback;
1694 s->node_vtable.vtable = vtable;
1695 s->node_vtable.find = find;
1697 s->node_vtable.interface = strdup(interface);
1698 if (!s->node_vtable.interface) {
1703 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1707 case _SD_BUS_VTABLE_METHOD: {
1708 struct vtable_member *m;
1710 if (!member_name_is_valid(v->x.method.member) ||
1711 !signature_is_valid(strempty(v->x.method.signature), false) ||
1712 !signature_is_valid(strempty(v->x.method.result), false) ||
1713 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1714 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1719 m = new0(struct vtable_member, 1);
1725 m->parent = &s->node_vtable;
1727 m->interface = s->node_vtable.interface;
1728 m->member = v->x.method.member;
1731 r = hashmap_put(bus->vtable_methods, m, m);
1740 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1742 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1747 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1754 case _SD_BUS_VTABLE_PROPERTY: {
1755 struct vtable_member *m;
1757 if (!member_name_is_valid(v->x.property.member) ||
1758 !signature_is_single(v->x.property.signature, false) ||
1759 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1760 (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1761 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1762 ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1763 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1768 m = new0(struct vtable_member, 1);
1774 m->parent = &s->node_vtable;
1776 m->interface = s->node_vtable.interface;
1777 m->member = v->x.property.member;
1780 r = hashmap_put(bus->vtable_properties, m, m);
1789 case _SD_BUS_VTABLE_SIGNAL:
1791 if (!member_name_is_valid(v->x.signal.member) ||
1792 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1793 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1806 s->node_vtable.node = n;
1807 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1808 bus->nodes_modified = true;
1816 sd_bus_slot_unref(s);
1817 bus_node_gc(bus, n);
1822 _public_ int sd_bus_add_object_vtable(
1826 const char *interface,
1827 const sd_bus_vtable *vtable,
1830 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1833 _public_ int sd_bus_add_fallback_vtable(
1837 const char *interface,
1838 const sd_bus_vtable *vtable,
1839 sd_bus_object_find_t find,
1842 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1845 _public_ int sd_bus_add_node_enumerator(
1849 sd_bus_node_enumerator_t callback,
1856 assert_return(bus, -EINVAL);
1857 assert_return(object_path_is_valid(path), -EINVAL);
1858 assert_return(callback, -EINVAL);
1859 assert_return(!bus_pid_changed(bus), -ECHILD);
1861 n = bus_node_allocate(bus, path);
1865 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1871 s->node_enumerator.callback = callback;
1873 s->node_enumerator.node = n;
1874 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1875 bus->nodes_modified = true;
1883 sd_bus_slot_unref(s);
1884 bus_node_gc(bus, n);
1889 static int emit_properties_changed_on_interface(
1893 const char *interface,
1894 bool require_fallback,
1895 bool *found_interface,
1898 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1899 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
1900 bool has_invalidating = false, has_changing = false;
1901 struct vtable_member key = {};
1902 struct node_vtable *c;
1912 assert(found_interface);
1914 n = hashmap_get(bus->nodes, prefix);
1918 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1922 r = sd_bus_message_append(m, "s", interface);
1926 r = sd_bus_message_open_container(m, 'a', "{sv}");
1931 key.interface = interface;
1933 LIST_FOREACH(vtables, c, n->vtables) {
1934 if (require_fallback && !c->is_fallback)
1937 if (!streq(c->interface, interface))
1940 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1943 if (bus->nodes_modified)
1948 *found_interface = true;
1951 /* If the caller specified a list of
1952 * properties we include exactly those in the
1953 * PropertiesChanged message */
1955 STRV_FOREACH(property, names) {
1956 struct vtable_member *v;
1958 assert_return(member_name_is_valid(*property), -EINVAL);
1960 key.member = *property;
1961 v = hashmap_get(bus->vtable_properties, &key);
1965 /* If there are two vtables for the same
1966 * interface, let's handle this property when
1967 * we come to that vtable. */
1971 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1972 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1974 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1976 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1977 has_invalidating = true;
1981 has_changing = true;
1983 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1986 if (bus->nodes_modified)
1990 const sd_bus_vtable *v;
1992 /* If the caller specified no properties list
1993 * we include all properties that are marked
1994 * as changing in the message. */
1996 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1997 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2000 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2003 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2004 has_invalidating = true;
2008 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2011 has_changing = true;
2013 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2016 if (bus->nodes_modified)
2022 if (!has_invalidating && !has_changing)
2025 r = sd_bus_message_close_container(m);
2029 r = sd_bus_message_open_container(m, 'a', "s");
2033 if (has_invalidating) {
2034 LIST_FOREACH(vtables, c, n->vtables) {
2035 if (require_fallback && !c->is_fallback)
2038 if (!streq(c->interface, interface))
2041 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2044 if (bus->nodes_modified)
2050 STRV_FOREACH(property, names) {
2051 struct vtable_member *v;
2053 key.member = *property;
2054 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2055 assert(c == v->parent);
2057 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2060 r = sd_bus_message_append(m, "s", *property);
2065 const sd_bus_vtable *v;
2067 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2068 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2071 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2074 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2077 r = sd_bus_message_append(m, "s", v->x.property.member);
2085 r = sd_bus_message_close_container(m);
2089 r = sd_bus_send(bus, m, NULL);
2096 _public_ int sd_bus_emit_properties_changed_strv(
2099 const char *interface,
2102 BUS_DONT_DESTROY(bus);
2103 bool found_interface = false;
2107 assert_return(bus, -EINVAL);
2108 assert_return(object_path_is_valid(path), -EINVAL);
2109 assert_return(interface_name_is_valid(interface), -EINVAL);
2110 assert_return(!bus_pid_changed(bus), -ECHILD);
2112 if (!BUS_IS_OPEN(bus->state))
2115 /* A non-NULL but empty names list means nothing needs to be
2116 generated. A NULL list OTOH indicates that all properties
2117 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2118 included in the PropertiesChanged message. */
2119 if (names && names[0] == NULL)
2123 bus->nodes_modified = false;
2125 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2128 if (bus->nodes_modified)
2131 prefix = alloca(strlen(path) + 1);
2132 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2133 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2136 if (bus->nodes_modified)
2140 } while (bus->nodes_modified);
2142 return found_interface ? 0 : -ENOENT;
2145 _public_ int sd_bus_emit_properties_changed(
2148 const char *interface,
2149 const char *name, ...) {
2153 assert_return(bus, -EINVAL);
2154 assert_return(object_path_is_valid(path), -EINVAL);
2155 assert_return(interface_name_is_valid(interface), -EINVAL);
2156 assert_return(!bus_pid_changed(bus), -ECHILD);
2158 if (!BUS_IS_OPEN(bus->state))
2164 names = strv_from_stdarg_alloca(name);
2166 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2169 static int object_added_append_all_prefix(
2175 bool require_fallback) {
2177 const char *previous_interface = NULL;
2178 struct node_vtable *c;
2188 n = hashmap_get(bus->nodes, prefix);
2192 LIST_FOREACH(vtables, c, n->vtables) {
2193 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2196 if (require_fallback && !c->is_fallback)
2199 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2202 if (bus->nodes_modified)
2207 if (!streq_ptr(c->interface, previous_interface)) {
2208 /* If a child-node already handled this interface, we
2209 * skip it on any of its parents. The child vtables
2210 * always fully override any conflicting vtables of
2211 * any parent node. */
2212 if (set_get(s, c->interface))
2215 r = set_put(s, c->interface);
2219 if (previous_interface) {
2220 r = sd_bus_message_close_container(m);
2223 r = sd_bus_message_close_container(m);
2228 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2231 r = sd_bus_message_append(m, "s", c->interface);
2234 r = sd_bus_message_open_container(m, 'a', "{sv}");
2238 previous_interface = c->interface;
2241 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2244 if (bus->nodes_modified)
2248 if (previous_interface) {
2249 r = sd_bus_message_close_container(m);
2252 r = sd_bus_message_close_container(m);
2260 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2261 _cleanup_set_free_ Set *s = NULL;
2270 * This appends all interfaces registered on path @path. We first add
2271 * the builtin interfaces, which are always available and handled by
2272 * sd-bus. Then, we add all interfaces registered on the exact node,
2273 * followed by all fallback interfaces registered on any parent prefix.
2275 * If an interface is registered multiple times on the same node with
2276 * different vtables, we merge all the properties across all vtables.
2277 * However, if a child node has the same interface registered as one of
2278 * its parent nodes has as fallback, we make the child overwrite the
2279 * parent instead of extending it. Therefore, we keep a "Set" of all
2280 * handled interfaces during parent traversal, so we skip interfaces on
2281 * a parent that were overwritten by a child.
2284 s = set_new(&string_hash_ops);
2288 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2291 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2294 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2297 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2301 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2304 if (bus->nodes_modified)
2307 prefix = alloca(strlen(path) + 1);
2308 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2309 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2312 if (bus->nodes_modified)
2319 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2320 BUS_DONT_DESTROY(bus);
2322 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2323 struct node *object_manager;
2327 * This emits an InterfacesAdded signal on the given path, by iterating
2328 * all registered vtables and fallback vtables on the path. All
2329 * properties are queried and included in the signal.
2330 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2331 * explicit list of registered interfaces. However, unlike
2332 * interfaces_added(), this call can figure out the list of supported
2333 * interfaces itself. Furthermore, it properly adds the builtin
2334 * org.freedesktop.DBus.* interfaces.
2337 assert_return(bus, -EINVAL);
2338 assert_return(object_path_is_valid(path), -EINVAL);
2339 assert_return(!bus_pid_changed(bus), -ECHILD);
2341 if (!BUS_IS_OPEN(bus->state))
2344 r = bus_find_parent_object_manager(bus, &object_manager, path);
2351 bus->nodes_modified = false;
2352 m = sd_bus_message_unref(m);
2354 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2358 r = sd_bus_message_append_basic(m, 'o', path);
2362 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2366 r = object_added_append_all(bus, m, path);
2370 if (bus->nodes_modified)
2373 r = sd_bus_message_close_container(m);
2377 } while (bus->nodes_modified);
2379 return sd_bus_send(bus, m, NULL);
2382 #if 0 /// UNNEEDED by elogind
2383 static int object_removed_append_all_prefix(
2389 bool require_fallback) {
2391 const char *previous_interface = NULL;
2392 struct node_vtable *c;
2402 n = hashmap_get(bus->nodes, prefix);
2406 LIST_FOREACH(vtables, c, n->vtables) {
2407 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2410 if (require_fallback && !c->is_fallback)
2412 if (streq_ptr(c->interface, previous_interface))
2415 /* If a child-node already handled this interface, we
2416 * skip it on any of its parents. The child vtables
2417 * always fully override any conflicting vtables of
2418 * any parent node. */
2419 if (set_get(s, c->interface))
2422 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2425 if (bus->nodes_modified)
2430 r = set_put(s, c->interface);
2434 r = sd_bus_message_append(m, "s", c->interface);
2438 previous_interface = c->interface;
2444 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2445 _cleanup_set_free_ Set *s = NULL;
2453 /* see sd_bus_emit_object_added() for details */
2455 s = set_new(&string_hash_ops);
2459 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2462 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2465 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2468 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2472 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2475 if (bus->nodes_modified)
2478 prefix = alloca(strlen(path) + 1);
2479 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2480 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2483 if (bus->nodes_modified)
2490 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2491 BUS_DONT_DESTROY(bus);
2493 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2494 struct node *object_manager;
2498 * This is like sd_bus_emit_object_added(), but emits an
2499 * InterfacesRemoved signal on the given path. This only includes any
2500 * registered interfaces but skips the properties. Note that this will
2501 * call into the find() callbacks of any registered vtable. Therefore,
2502 * you must call this function before destroying/unlinking your object.
2503 * Otherwise, the list of interfaces will be incomplete. However, note
2504 * that this will *NOT* call into any property callback. Therefore, the
2505 * object might be in an "destructed" state, as long as we can find it.
2508 assert_return(bus, -EINVAL);
2509 assert_return(object_path_is_valid(path), -EINVAL);
2510 assert_return(!bus_pid_changed(bus), -ECHILD);
2512 if (!BUS_IS_OPEN(bus->state))
2515 r = bus_find_parent_object_manager(bus, &object_manager, path);
2522 bus->nodes_modified = false;
2523 m = sd_bus_message_unref(m);
2525 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2529 r = sd_bus_message_append_basic(m, 'o', path);
2533 r = sd_bus_message_open_container(m, 'a', "s");
2537 r = object_removed_append_all(bus, m, path);
2541 if (bus->nodes_modified)
2544 r = sd_bus_message_close_container(m);
2548 } while (bus->nodes_modified);
2550 return sd_bus_send(bus, m, NULL);
2554 static int interfaces_added_append_one_prefix(
2559 const char *interface,
2560 bool require_fallback) {
2562 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
2563 bool found_interface = false;
2564 struct node_vtable *c;
2575 n = hashmap_get(bus->nodes, prefix);
2579 LIST_FOREACH(vtables, c, n->vtables) {
2580 if (require_fallback && !c->is_fallback)
2583 if (!streq(c->interface, interface))
2586 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2589 if (bus->nodes_modified)
2594 if (!found_interface) {
2595 r = sd_bus_message_append_basic(m, 's', interface);
2599 r = sd_bus_message_open_container(m, 'a', "{sv}");
2603 found_interface = true;
2606 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2609 if (bus->nodes_modified)
2613 if (found_interface) {
2614 r = sd_bus_message_close_container(m);
2619 return found_interface;
2622 static int interfaces_added_append_one(
2626 const char *interface) {
2636 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2639 if (bus->nodes_modified)
2642 prefix = alloca(strlen(path) + 1);
2643 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2644 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2647 if (bus->nodes_modified)
2654 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2655 BUS_DONT_DESTROY(bus);
2657 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2658 struct node *object_manager;
2662 assert_return(bus, -EINVAL);
2663 assert_return(object_path_is_valid(path), -EINVAL);
2664 assert_return(!bus_pid_changed(bus), -ECHILD);
2666 if (!BUS_IS_OPEN(bus->state))
2669 if (strv_isempty(interfaces))
2672 r = bus_find_parent_object_manager(bus, &object_manager, path);
2679 bus->nodes_modified = false;
2680 m = sd_bus_message_unref(m);
2682 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2686 r = sd_bus_message_append_basic(m, 'o', path);
2690 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2694 STRV_FOREACH(i, interfaces) {
2695 assert_return(interface_name_is_valid(*i), -EINVAL);
2697 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2701 r = interfaces_added_append_one(bus, m, path, *i);
2705 if (bus->nodes_modified)
2708 r = sd_bus_message_close_container(m);
2713 if (bus->nodes_modified)
2716 r = sd_bus_message_close_container(m);
2720 } while (bus->nodes_modified);
2722 return sd_bus_send(bus, m, NULL);
2725 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2728 assert_return(bus, -EINVAL);
2729 assert_return(object_path_is_valid(path), -EINVAL);
2730 assert_return(!bus_pid_changed(bus), -ECHILD);
2732 if (!BUS_IS_OPEN(bus->state))
2735 interfaces = strv_from_stdarg_alloca(interface);
2737 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2740 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2741 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2742 struct node *object_manager;
2745 assert_return(bus, -EINVAL);
2746 assert_return(object_path_is_valid(path), -EINVAL);
2747 assert_return(!bus_pid_changed(bus), -ECHILD);
2749 if (!BUS_IS_OPEN(bus->state))
2752 if (strv_isempty(interfaces))
2755 r = bus_find_parent_object_manager(bus, &object_manager, path);
2761 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2765 r = sd_bus_message_append_basic(m, 'o', path);
2769 r = sd_bus_message_append_strv(m, interfaces);
2773 return sd_bus_send(bus, m, NULL);
2776 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2779 assert_return(bus, -EINVAL);
2780 assert_return(object_path_is_valid(path), -EINVAL);
2781 assert_return(!bus_pid_changed(bus), -ECHILD);
2783 if (!BUS_IS_OPEN(bus->state))
2786 interfaces = strv_from_stdarg_alloca(interface);
2788 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2791 #if 0 /// UNNEEDED by elogind
2792 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2797 assert_return(bus, -EINVAL);
2798 assert_return(object_path_is_valid(path), -EINVAL);
2799 assert_return(!bus_pid_changed(bus), -ECHILD);
2801 n = bus_node_allocate(bus, path);
2805 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2811 s->node_object_manager.node = n;
2812 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2813 bus->nodes_modified = true;
2821 sd_bus_slot_unref(s);
2822 bus_node_gc(bus, n);