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"
29 #include "bus-objects.h"
32 static int node_vtable_get_userdata(
35 struct node_vtable *c,
37 sd_bus_error *error) {
48 r = c->find(bus, path, c->interface, u, &u, error);
51 if (sd_bus_error_is_set(error))
52 return sd_bus_error_get_errno(error);
63 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
66 return (uint8_t*) u + p->x.property.offset;
69 static int vtable_property_get_userdata(
72 struct vtable_member *p,
74 sd_bus_error *error) {
84 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
87 if (bus->nodes_modified)
90 *userdata = vtable_property_convert_userdata(p->vtable, u);
94 static int add_enumerated_to_set(
97 struct node_enumerator *first,
99 sd_bus_error *error) {
101 struct node_enumerator *c;
108 LIST_FOREACH(enumerators, c, first) {
109 char **children = NULL, **k;
111 if (bus->nodes_modified)
114 r = c->callback(bus, prefix, c->userdata, &children, error);
117 if (sd_bus_error_is_set(error))
118 return sd_bus_error_get_errno(error);
120 STRV_FOREACH(k, children) {
126 if (!object_path_is_valid(*k)){
132 if (!object_path_startswith(*k, prefix)) {
137 r = set_consume(s, *k);
150 static int add_subtree_to_set(
155 sd_bus_error *error) {
165 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
168 if (bus->nodes_modified)
171 LIST_FOREACH(siblings, i, n->child) {
174 if (!object_path_startswith(i->path, prefix))
181 r = set_consume(s, t);
182 if (r < 0 && r != -EEXIST)
185 r = add_subtree_to_set(bus, prefix, i, s, error);
188 if (bus->nodes_modified)
195 static int get_child_nodes(
200 sd_bus_error *error) {
210 s = set_new(string_hash_func, string_compare_func);
214 r = add_subtree_to_set(bus, prefix, n, s, error);
224 static int node_callbacks_run(
227 struct node_callback *first,
228 bool require_fallback,
229 bool *found_object) {
231 struct node_callback *c;
236 assert(found_object);
238 LIST_FOREACH(callbacks, c, first) {
239 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
241 if (bus->nodes_modified)
244 if (require_fallback && !c->is_fallback)
247 *found_object = true;
249 if (c->last_iteration == bus->iteration_counter)
252 c->last_iteration = bus->iteration_counter;
254 r = sd_bus_message_rewind(m, true);
258 r = c->callback(bus, m, c->userdata, &error_buffer);
259 r = bus_maybe_reply_error(m, r, &error_buffer);
267 static int method_callbacks_run(
270 struct vtable_member *c,
271 bool require_fallback,
272 bool *found_object) {
274 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
275 const char *signature;
282 assert(found_object);
284 if (require_fallback && !c->parent->is_fallback)
287 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
289 return bus_maybe_reply_error(m, r, &error);
290 if (bus->nodes_modified)
293 *found_object = true;
295 if (c->last_iteration == bus->iteration_counter)
298 c->last_iteration = bus->iteration_counter;
300 r = sd_bus_message_rewind(m, true);
304 signature = sd_bus_message_get_signature(m, true);
308 if (!streq(strempty(c->vtable->x.method.signature), signature))
309 return sd_bus_reply_method_errorf(
311 SD_BUS_ERROR_INVALID_ARGS,
312 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
313 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
315 if (c->vtable->x.method.handler) {
316 r = c->vtable->x.method.handler(bus, m, u, &error);
317 return bus_maybe_reply_error(m, r, &error);
320 /* If the method callback is NULL, make this a successful NOP */
321 r = sd_bus_reply_method_return(m, NULL);
328 static int invoke_property_get(
330 const sd_bus_vtable *v,
332 const char *interface,
333 const char *property,
334 sd_bus_message *reply,
336 sd_bus_error *error) {
348 if (v->x.property.get) {
349 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
352 if (sd_bus_error_is_set(error))
353 return sd_bus_error_get_errno(error);
357 /* Automatic handling if no callback is defined. */
359 if (streq(v->x.property.signature, "as"))
360 return sd_bus_message_append_strv(reply, *(char***) userdata);
362 assert(signature_is_single(v->x.property.signature, false));
363 assert(bus_type_is_basic(v->x.property.signature[0]));
365 switch (v->x.property.signature[0]) {
367 case SD_BUS_TYPE_STRING:
368 case SD_BUS_TYPE_SIGNATURE:
369 p = strempty(*(char**) userdata);
372 case SD_BUS_TYPE_OBJECT_PATH:
373 p = *(char**) userdata;
382 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
385 static int invoke_property_set(
387 const sd_bus_vtable *v,
389 const char *interface,
390 const char *property,
391 sd_bus_message *value,
393 sd_bus_error *error) {
404 if (v->x.property.set) {
405 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
408 if (sd_bus_error_is_set(error))
409 return sd_bus_error_get_errno(error);
413 /* Automatic handling if no callback is defined. */
415 assert(signature_is_single(v->x.property.signature, false));
416 assert(bus_type_is_basic(v->x.property.signature[0]));
418 switch (v->x.property.signature[0]) {
420 case SD_BUS_TYPE_STRING:
421 case SD_BUS_TYPE_OBJECT_PATH:
422 case SD_BUS_TYPE_SIGNATURE: {
426 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
434 free(*(char**) userdata);
435 *(char**) userdata = n;
441 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
451 static int property_get_set_callbacks_run(
454 struct vtable_member *c,
455 bool require_fallback,
457 bool *found_object) {
459 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
460 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
467 assert(found_object);
469 if (require_fallback && !c->parent->is_fallback)
472 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
474 return bus_maybe_reply_error(m, r, &error);
475 if (bus->nodes_modified)
478 *found_object = true;
480 r = sd_bus_message_new_method_return(m, &reply);
485 /* Note that we do not protect against reexecution
486 * here (using the last_iteration check, see below),
487 * should the node tree have changed and we got called
488 * again. We assume that property Get() calls are
489 * ultimately without side-effects or if they aren't
490 * then at least idempotent. */
492 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
496 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
498 return bus_maybe_reply_error(m, r, &error);
500 if (bus->nodes_modified)
503 r = sd_bus_message_close_container(reply);
508 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
509 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
511 /* Avoid that we call the set routine more than once
512 * if the processing of this message got restarted
513 * because the node tree changed. */
514 if (c->last_iteration == bus->iteration_counter)
517 c->last_iteration = bus->iteration_counter;
519 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
523 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
525 return bus_maybe_reply_error(m, r, &error);
527 if (bus->nodes_modified)
530 r = sd_bus_message_exit_container(m);
535 r = sd_bus_send(bus, reply, NULL);
542 static int vtable_append_all_properties(
544 sd_bus_message *reply,
546 struct node_vtable *c,
548 sd_bus_error *error) {
550 const sd_bus_vtable *v;
558 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
559 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
562 r = sd_bus_message_open_container(reply, 'e', "sv");
566 r = sd_bus_message_append(reply, "s", v->x.property.member);
570 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
574 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
577 if (bus->nodes_modified)
580 r = sd_bus_message_close_container(reply);
584 r = sd_bus_message_close_container(reply);
592 static int property_get_all_callbacks_run(
595 struct node_vtable *first,
596 bool require_fallback,
598 bool *found_object) {
600 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
601 struct node_vtable *c;
602 bool found_interface;
607 assert(found_object);
609 r = sd_bus_message_new_method_return(m, &reply);
613 r = sd_bus_message_open_container(reply, 'a', "{sv}");
617 found_interface = !iface ||
618 streq(iface, "org.freedesktop.DBus.Properties") ||
619 streq(iface, "org.freedesktop.DBus.Peer") ||
620 streq(iface, "org.freedesktop.DBus.Introspectable");
622 LIST_FOREACH(vtables, c, first) {
623 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
626 if (require_fallback && !c->is_fallback)
629 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
631 return bus_maybe_reply_error(m, r, &error);
632 if (bus->nodes_modified)
637 *found_object = true;
639 if (iface && !streq(c->interface, iface))
641 found_interface = true;
643 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
645 return bus_maybe_reply_error(m, r, &error);
646 if (bus->nodes_modified)
650 if (!found_interface) {
651 r = sd_bus_reply_method_errorf(
653 SD_BUS_ERROR_UNKNOWN_INTERFACE,
654 "Unknown interface '%s'.", iface);
661 r = sd_bus_message_close_container(reply);
665 r = sd_bus_send(bus, reply, NULL);
672 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
676 if (n->object_manager)
680 return bus_node_with_object_manager(bus, n->parent);
685 static bool bus_node_exists(
689 bool require_fallback) {
691 struct node_vtable *c;
692 struct node_callback *k;
698 /* Tests if there's anything attached directly to this node
699 * for the specified path */
701 LIST_FOREACH(callbacks, k, n->callbacks) {
702 if (require_fallback && !k->is_fallback)
708 LIST_FOREACH(vtables, c, n->vtables) {
709 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
711 if (require_fallback && !c->is_fallback)
714 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
716 if (bus->nodes_modified)
720 return !require_fallback && (n->enumerators || n->object_manager);
723 static int process_introspect(
727 bool require_fallback,
728 bool *found_object) {
730 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
731 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
732 _cleanup_set_free_free_ Set *s = NULL;
733 const char *previous_interface = NULL;
734 struct introspect intro;
735 struct node_vtable *c;
742 assert(found_object);
744 r = get_child_nodes(bus, m->path, n, &s, &error);
746 return bus_maybe_reply_error(m, r, &error);
747 if (bus->nodes_modified)
750 r = introspect_begin(&intro);
754 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
758 empty = set_isempty(s);
760 LIST_FOREACH(vtables, c, n->vtables) {
761 if (require_fallback && !c->is_fallback)
764 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
766 r = bus_maybe_reply_error(m, r, &error);
769 if (bus->nodes_modified) {
778 if (!streq_ptr(previous_interface, c->interface)) {
780 if (previous_interface)
781 fputs(" </interface>\n", intro.f);
783 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
786 r = introspect_write_interface(&intro, c->vtable);
790 previous_interface = c->interface;
793 if (previous_interface)
794 fputs(" </interface>\n", intro.f);
797 /* Nothing?, let's see if we exist at all, and if not
798 * refuse to do anything */
799 r = bus_node_exists(bus, n, m->path, require_fallback);
802 if (bus->nodes_modified)
808 *found_object = true;
810 r = introspect_write_child_nodes(&intro, s, m->path);
814 r = introspect_finish(&intro, bus, m, &reply);
818 r = sd_bus_send(bus, reply, NULL);
825 introspect_free(&intro);
829 static int object_manager_serialize_path(
831 sd_bus_message *reply,
834 bool require_fallback,
835 sd_bus_error *error) {
837 const char *previous_interface = NULL;
838 bool found_something = false;
839 struct node_vtable *i;
849 n = hashmap_get(bus->nodes, prefix);
853 LIST_FOREACH(vtables, i, n->vtables) {
856 if (require_fallback && !i->is_fallback)
859 r = node_vtable_get_userdata(bus, path, i, &u, error);
862 if (bus->nodes_modified)
867 if (!found_something) {
869 /* Open the object part */
871 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
875 r = sd_bus_message_append(reply, "o", path);
879 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
883 found_something = true;
886 if (!streq_ptr(previous_interface, i->interface)) {
888 /* Maybe close the previous interface part */
890 if (previous_interface) {
891 r = sd_bus_message_close_container(reply);
895 r = sd_bus_message_close_container(reply);
900 /* Open the new interface part */
902 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
906 r = sd_bus_message_append(reply, "s", i->interface);
910 r = sd_bus_message_open_container(reply, 'a', "{sv}");
915 r = vtable_append_all_properties(bus, reply, path, i, u, error);
918 if (bus->nodes_modified)
921 previous_interface = i->interface;
924 if (previous_interface) {
925 r = sd_bus_message_close_container(reply);
929 r = sd_bus_message_close_container(reply);
934 if (found_something) {
935 r = sd_bus_message_close_container(reply);
939 r = sd_bus_message_close_container(reply);
947 static int object_manager_serialize_path_and_fallbacks(
949 sd_bus_message *reply,
951 sd_bus_error *error) {
961 /* First, add all vtables registered for this path */
962 r = object_manager_serialize_path(bus, reply, path, path, false, error);
965 if (bus->nodes_modified)
968 /* Second, add fallback vtables registered for any of the prefixes */
969 prefix = alloca(strlen(path) + 1);
970 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
971 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
974 if (bus->nodes_modified)
981 static int process_get_managed_objects(
985 bool require_fallback,
986 bool *found_object) {
988 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
989 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
990 _cleanup_set_free_free_ Set *s = NULL;
997 assert(found_object);
999 if (!bus_node_with_object_manager(bus, n))
1002 r = get_child_nodes(bus, m->path, n, &s, &error);
1005 if (bus->nodes_modified)
1008 r = sd_bus_message_new_method_return(m, &reply);
1012 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1016 empty = set_isempty(s);
1018 struct node_vtable *c;
1020 /* Hmm, so we have no children? Then let's check
1021 * whether we exist at all, i.e. whether at least one
1024 LIST_FOREACH(vtables, c, n->vtables) {
1026 if (require_fallback && !c->is_fallback)
1044 SET_FOREACH(path, s, i) {
1045 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1049 if (bus->nodes_modified)
1054 r = sd_bus_message_close_container(reply);
1058 r = sd_bus_send(bus, reply, NULL);
1065 static int object_find_and_run(
1069 bool require_fallback,
1070 bool *found_object) {
1073 struct vtable_member vtable_key, *v;
1079 assert(found_object);
1081 n = hashmap_get(bus->nodes, p);
1085 /* First, try object callbacks */
1086 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1089 if (bus->nodes_modified)
1092 if (!m->interface || !m->member)
1095 /* Then, look for a known method */
1096 vtable_key.path = (char*) p;
1097 vtable_key.interface = m->interface;
1098 vtable_key.member = m->member;
1100 v = hashmap_get(bus->vtable_methods, &vtable_key);
1102 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1105 if (bus->nodes_modified)
1109 /* Then, look for a known property */
1110 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1113 get = streq(m->member, "Get");
1115 if (get || streq(m->member, "Set")) {
1117 r = sd_bus_message_rewind(m, true);
1121 vtable_key.path = (char*) p;
1123 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1125 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1127 v = hashmap_get(bus->vtable_properties, &vtable_key);
1129 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1134 } else if (streq(m->member, "GetAll")) {
1137 r = sd_bus_message_rewind(m, true);
1141 r = sd_bus_message_read(m, "s", &iface);
1143 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1148 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1153 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1155 if (!isempty(sd_bus_message_get_signature(m, true)))
1156 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1158 r = process_introspect(bus, m, n, require_fallback, found_object);
1162 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1164 if (!isempty(sd_bus_message_get_signature(m, true)))
1165 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1167 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1172 if (bus->nodes_modified)
1175 if (!*found_object) {
1176 r = bus_node_exists(bus, n, m->path, require_fallback);
1180 *found_object = true;
1186 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1189 bool found_object = false;
1194 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1200 if (hashmap_isempty(bus->nodes))
1203 pl = strlen(m->path);
1207 bus->nodes_modified = false;
1209 r = object_find_and_run(bus, m, m->path, false, &found_object);
1213 /* Look for fallback prefixes */
1214 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1216 if (bus->nodes_modified)
1219 r = object_find_and_run(bus, m, prefix, true, &found_object);
1224 } while (bus->nodes_modified);
1229 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1230 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1231 r = sd_bus_reply_method_errorf(
1233 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1234 "Unknown property or interface.");
1236 r = sd_bus_reply_method_errorf(
1238 SD_BUS_ERROR_UNKNOWN_METHOD,
1239 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1247 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1248 struct node *n, *parent;
1255 assert(path[0] == '/');
1257 n = hashmap_get(bus->nodes, path);
1261 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1269 if (streq(path, "/"))
1272 e = strrchr(path, '/');
1275 p = strndupa(path, MAX(1, path - e));
1277 parent = bus_node_allocate(bus, p);
1284 n = new0(struct node, 1);
1291 r = hashmap_put(bus->nodes, s, n);
1299 LIST_PREPEND(siblings, parent->child, n);
1304 static void bus_node_gc(sd_bus *b, struct node *n) {
1317 assert(hashmap_remove(b->nodes, n->path) == n);
1320 LIST_REMOVE(siblings, n->parent->child, n);
1323 bus_node_gc(b, n->parent);
1327 static int bus_add_object(
1331 sd_bus_message_handler_t callback,
1334 struct node_callback *c;
1338 assert_return(bus, -EINVAL);
1339 assert_return(object_path_is_valid(path), -EINVAL);
1340 assert_return(callback, -EINVAL);
1341 assert_return(!bus_pid_changed(bus), -ECHILD);
1343 n = bus_node_allocate(bus, path);
1347 c = new0(struct node_callback, 1);
1354 c->callback = callback;
1355 c->userdata = userdata;
1356 c->is_fallback = fallback;
1358 LIST_PREPEND(callbacks, n->callbacks, c);
1359 bus->nodes_modified = true;
1365 bus_node_gc(bus, n);
1369 static int bus_remove_object(
1373 sd_bus_message_handler_t callback,
1376 struct node_callback *c;
1379 assert_return(bus, -EINVAL);
1380 assert_return(object_path_is_valid(path), -EINVAL);
1381 assert_return(callback, -EINVAL);
1382 assert_return(!bus_pid_changed(bus), -ECHILD);
1384 n = hashmap_get(bus->nodes, path);
1388 LIST_FOREACH(callbacks, c, n->callbacks)
1389 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1394 LIST_REMOVE(callbacks, n->callbacks, c);
1397 bus_node_gc(bus, n);
1398 bus->nodes_modified = true;
1403 _public_ int sd_bus_add_object(sd_bus *bus,
1405 sd_bus_message_handler_t callback,
1408 return bus_add_object(bus, false, path, callback, userdata);
1411 _public_ int sd_bus_remove_object(sd_bus *bus,
1413 sd_bus_message_handler_t callback,
1416 return bus_remove_object(bus, false, path, callback, userdata);
1419 _public_ int sd_bus_add_fallback(sd_bus *bus,
1421 sd_bus_message_handler_t callback,
1424 return bus_add_object(bus, true, prefix, callback, userdata);
1427 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1429 sd_bus_message_handler_t callback,
1432 return bus_remove_object(bus, true, prefix, callback, userdata);
1435 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1441 if (w->interface && w->node && w->vtable) {
1442 const sd_bus_vtable *v;
1444 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1445 struct vtable_member *x = NULL;
1449 case _SD_BUS_VTABLE_METHOD: {
1450 struct vtable_member key;
1452 key.path = w->node->path;
1453 key.interface = w->interface;
1454 key.member = v->x.method.member;
1456 x = hashmap_remove(bus->vtable_methods, &key);
1460 case _SD_BUS_VTABLE_PROPERTY:
1461 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1462 struct vtable_member key;
1464 key.path = w->node->path;
1465 key.interface = w->interface;
1466 key.member = v->x.property.member;
1467 x = hashmap_remove(bus->vtable_properties, &key);
1479 static unsigned vtable_member_hash_func(const void *a) {
1480 const struct vtable_member *m = a;
1485 string_hash_func(m->path) ^
1486 string_hash_func(m->interface) ^
1487 string_hash_func(m->member);
1490 static int vtable_member_compare_func(const void *a, const void *b) {
1491 const struct vtable_member *x = a, *y = b;
1497 r = strcmp(x->path, y->path);
1501 r = strcmp(x->interface, y->interface);
1505 return strcmp(x->member, y->member);
1508 static int add_object_vtable_internal(
1511 const char *interface,
1512 const sd_bus_vtable *vtable,
1514 sd_bus_object_find_t find,
1517 struct node_vtable *c = NULL, *i, *existing = NULL;
1518 const sd_bus_vtable *v;
1522 assert_return(bus, -EINVAL);
1523 assert_return(object_path_is_valid(path), -EINVAL);
1524 assert_return(interface_name_is_valid(interface), -EINVAL);
1525 assert_return(vtable, -EINVAL);
1526 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1527 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1528 assert_return(!bus_pid_changed(bus), -ECHILD);
1529 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1530 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1531 !streq(interface, "org.freedesktop.DBus.Peer") &&
1532 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1534 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1538 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1542 n = bus_node_allocate(bus, path);
1546 LIST_FOREACH(vtables, i, n->vtables) {
1547 if (i->is_fallback != fallback) {
1552 if (streq(i->interface, interface)) {
1554 if (i->vtable == vtable) {
1563 c = new0(struct node_vtable, 1);
1570 c->is_fallback = fallback;
1572 c->userdata = userdata;
1575 c->interface = strdup(interface);
1576 if (!c->interface) {
1581 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1585 case _SD_BUS_VTABLE_METHOD: {
1586 struct vtable_member *m;
1588 if (!member_name_is_valid(v->x.method.member) ||
1589 !signature_is_valid(strempty(v->x.method.signature), false) ||
1590 !signature_is_valid(strempty(v->x.method.result), false) ||
1591 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1592 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1597 m = new0(struct vtable_member, 1);
1605 m->interface = c->interface;
1606 m->member = v->x.method.member;
1609 r = hashmap_put(bus->vtable_methods, m, m);
1618 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1620 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1627 case _SD_BUS_VTABLE_PROPERTY: {
1628 struct vtable_member *m;
1630 if (!member_name_is_valid(v->x.property.member) ||
1631 !signature_is_single(v->x.property.signature, false) ||
1632 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1633 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1634 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1640 m = new0(struct vtable_member, 1);
1648 m->interface = c->interface;
1649 m->member = v->x.property.member;
1652 r = hashmap_put(bus->vtable_properties, m, m);
1661 case _SD_BUS_VTABLE_SIGNAL:
1663 if (!member_name_is_valid(v->x.signal.member) ||
1664 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1677 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1678 bus->nodes_modified = true;
1684 free_node_vtable(bus, c);
1686 bus_node_gc(bus, n);
1690 static int remove_object_vtable_internal(
1693 const char *interface,
1694 const sd_bus_vtable *vtable,
1696 sd_bus_object_find_t find,
1699 struct node_vtable *c;
1702 assert_return(bus, -EINVAL);
1703 assert_return(object_path_is_valid(path), -EINVAL);
1704 assert_return(interface_name_is_valid(interface), -EINVAL);
1705 assert_return(!bus_pid_changed(bus), -ECHILD);
1707 n = hashmap_get(bus->nodes, path);
1711 LIST_FOREACH(vtables, c, n->vtables)
1712 if (streq(c->interface, interface) &&
1713 c->is_fallback == fallback &&
1714 c->vtable == vtable &&
1716 c->userdata == userdata)
1722 LIST_REMOVE(vtables, n->vtables, c);
1724 free_node_vtable(bus, c);
1725 bus_node_gc(bus, n);
1727 bus->nodes_modified = true;
1732 _public_ int sd_bus_add_object_vtable(
1735 const char *interface,
1736 const sd_bus_vtable *vtable,
1739 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1742 _public_ int sd_bus_remove_object_vtable(
1745 const char *interface,
1746 const sd_bus_vtable *vtable,
1749 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1752 _public_ int sd_bus_add_fallback_vtable(
1755 const char *interface,
1756 const sd_bus_vtable *vtable,
1757 sd_bus_object_find_t find,
1760 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1763 _public_ int sd_bus_remove_fallback_vtable(
1766 const char *interface,
1767 const sd_bus_vtable *vtable,
1768 sd_bus_object_find_t find,
1771 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1774 _public_ int sd_bus_add_node_enumerator(
1777 sd_bus_node_enumerator_t callback,
1780 struct node_enumerator *c;
1784 assert_return(bus, -EINVAL);
1785 assert_return(object_path_is_valid(path), -EINVAL);
1786 assert_return(callback, -EINVAL);
1787 assert_return(!bus_pid_changed(bus), -ECHILD);
1789 n = bus_node_allocate(bus, path);
1793 c = new0(struct node_enumerator, 1);
1800 c->callback = callback;
1801 c->userdata = userdata;
1803 LIST_PREPEND(enumerators, n->enumerators, c);
1805 bus->nodes_modified = true;
1811 bus_node_gc(bus, n);
1815 _public_ int sd_bus_remove_node_enumerator(
1818 sd_bus_node_enumerator_t callback,
1821 struct node_enumerator *c;
1824 assert_return(bus, -EINVAL);
1825 assert_return(object_path_is_valid(path), -EINVAL);
1826 assert_return(callback, -EINVAL);
1827 assert_return(!bus_pid_changed(bus), -ECHILD);
1829 n = hashmap_get(bus->nodes, path);
1833 LIST_FOREACH(enumerators, c, n->enumerators)
1834 if (c->callback == callback && c->userdata == userdata)
1840 LIST_REMOVE(enumerators, n->enumerators, c);
1843 bus_node_gc(bus, n);
1845 bus->nodes_modified = true;
1850 static int emit_properties_changed_on_interface(
1854 const char *interface,
1855 bool require_fallback,
1858 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1859 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1860 bool has_invalidating = false, has_changing = false;
1861 struct vtable_member key = {};
1862 struct node_vtable *c;
1873 n = hashmap_get(bus->nodes, prefix);
1877 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1881 r = sd_bus_message_append(m, "s", interface);
1885 r = sd_bus_message_open_container(m, 'a', "{sv}");
1890 key.interface = interface;
1892 LIST_FOREACH(vtables, c, n->vtables) {
1893 if (require_fallback && !c->is_fallback)
1896 if (!streq(c->interface, interface))
1899 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1902 if (bus->nodes_modified)
1907 STRV_FOREACH(property, names) {
1908 struct vtable_member *v;
1910 assert_return(member_name_is_valid(*property), -EINVAL);
1912 key.member = *property;
1913 v = hashmap_get(bus->vtable_properties, &key);
1917 /* If there are two vtables for the same
1918 * interface, let's handle this property when
1919 * we come to that vtable. */
1923 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1925 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1926 has_invalidating = true;
1930 has_changing = true;
1932 r = sd_bus_message_open_container(m, 'e', "sv");
1936 r = sd_bus_message_append(m, "s", *property);
1940 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1944 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error);
1947 if (bus->nodes_modified)
1950 r = sd_bus_message_close_container(m);
1954 r = sd_bus_message_close_container(m);
1960 if (!has_invalidating && !has_changing)
1963 r = sd_bus_message_close_container(m);
1967 r = sd_bus_message_open_container(m, 'a', "s");
1971 if (has_invalidating) {
1972 LIST_FOREACH(vtables, c, n->vtables) {
1973 if (require_fallback && !c->is_fallback)
1976 if (!streq(c->interface, interface))
1979 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1982 if (bus->nodes_modified)
1987 STRV_FOREACH(property, names) {
1988 struct vtable_member *v;
1990 key.member = *property;
1991 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1992 assert(c == v->parent);
1994 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1997 r = sd_bus_message_append(m, "s", *property);
2004 r = sd_bus_message_close_container(m);
2008 r = sd_bus_send(bus, m, NULL);
2015 _public_ int sd_bus_emit_properties_changed_strv(
2018 const char *interface,
2021 BUS_DONT_DESTROY(bus);
2025 assert_return(bus, -EINVAL);
2026 assert_return(object_path_is_valid(path), -EINVAL);
2027 assert_return(interface_name_is_valid(interface), -EINVAL);
2028 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2029 assert_return(!bus_pid_changed(bus), -ECHILD);
2031 if (strv_isempty(names))
2035 bus->nodes_modified = false;
2037 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2040 if (bus->nodes_modified)
2043 prefix = alloca(strlen(path) + 1);
2044 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2045 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2048 if (bus->nodes_modified)
2052 } while (bus->nodes_modified);
2057 _public_ int sd_bus_emit_properties_changed(
2060 const char *interface,
2061 const char *name, ...) {
2065 assert_return(bus, -EINVAL);
2066 assert_return(object_path_is_valid(path), -EINVAL);
2067 assert_return(interface_name_is_valid(interface), -EINVAL);
2068 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2069 assert_return(!bus_pid_changed(bus), -ECHILD);
2074 names = strv_from_stdarg_alloca(name);
2076 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2079 static int interfaces_added_append_one_prefix(
2084 const char *interface,
2085 bool require_fallback) {
2087 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2088 bool found_interface = false;
2089 struct node_vtable *c;
2100 n = hashmap_get(bus->nodes, prefix);
2104 LIST_FOREACH(vtables, c, n->vtables) {
2105 if (require_fallback && !c->is_fallback)
2108 if (!streq(c->interface, interface))
2111 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2114 if (bus->nodes_modified)
2119 if (!found_interface) {
2120 r = sd_bus_message_append_basic(m, 's', interface);
2124 r = sd_bus_message_open_container(m, 'a', "{sv}");
2128 found_interface = true;
2131 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2134 if (bus->nodes_modified)
2138 if (found_interface) {
2139 r = sd_bus_message_close_container(m);
2144 return found_interface;
2147 static int interfaces_added_append_one(
2151 const char *interface) {
2161 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2164 if (bus->nodes_modified)
2167 prefix = alloca(strlen(path) + 1);
2168 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2169 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2172 if (bus->nodes_modified)
2179 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2180 BUS_DONT_DESTROY(bus);
2182 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2186 assert_return(bus, -EINVAL);
2187 assert_return(object_path_is_valid(path), -EINVAL);
2188 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2189 assert_return(!bus_pid_changed(bus), -ECHILD);
2191 if (strv_isempty(interfaces))
2195 bus->nodes_modified = false;
2198 m = sd_bus_message_unref(m);
2200 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2204 r = sd_bus_message_append_basic(m, 'o', path);
2208 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2212 STRV_FOREACH(i, interfaces) {
2213 assert_return(interface_name_is_valid(*i), -EINVAL);
2215 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2219 r = interfaces_added_append_one(bus, m, path, *i);
2223 if (bus->nodes_modified)
2226 r = sd_bus_message_close_container(m);
2231 if (bus->nodes_modified)
2234 r = sd_bus_message_close_container(m);
2238 } while (bus->nodes_modified);
2240 return sd_bus_send(bus, m, NULL);
2243 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2246 assert_return(bus, -EINVAL);
2247 assert_return(object_path_is_valid(path), -EINVAL);
2248 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2249 assert_return(!bus_pid_changed(bus), -ECHILD);
2251 interfaces = strv_from_stdarg_alloca(interface);
2253 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2256 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2257 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2260 assert_return(bus, -EINVAL);
2261 assert_return(object_path_is_valid(path), -EINVAL);
2262 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2263 assert_return(!bus_pid_changed(bus), -ECHILD);
2265 if (strv_isempty(interfaces))
2268 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2272 r = sd_bus_message_append_basic(m, 'o', path);
2276 r = sd_bus_message_append_strv(m, interfaces);
2280 return sd_bus_send(bus, m, NULL);
2283 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2286 assert_return(bus, -EINVAL);
2287 assert_return(object_path_is_valid(path), -EINVAL);
2288 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2289 assert_return(!bus_pid_changed(bus), -ECHILD);
2291 interfaces = strv_from_stdarg_alloca(interface);
2293 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2296 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2299 assert_return(bus, -EINVAL);
2300 assert_return(object_path_is_valid(path), -EINVAL);
2301 assert_return(!bus_pid_changed(bus), -ECHILD);
2303 n = bus_node_allocate(bus, path);
2307 n->object_manager = true;
2308 bus->nodes_modified = true;
2312 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2315 assert_return(bus, -EINVAL);
2316 assert_return(object_path_is_valid(path), -EINVAL);
2317 assert_return(!bus_pid_changed(bus), -ECHILD);
2319 n = hashmap_get(bus->nodes, path);
2323 if (!n->object_manager)
2326 n->object_manager = false;
2327 bus->nodes_modified = true;
2328 bus_node_gc(bus, n);