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 /* Keep track what the signature of the reply to this message
316 * should be, so that this can be enforced when sealing the
318 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
320 if (c->vtable->x.method.handler) {
321 r = c->vtable->x.method.handler(bus, m, u, &error);
322 return bus_maybe_reply_error(m, r, &error);
325 /* If the method callback is NULL, make this a successful NOP */
326 r = sd_bus_reply_method_return(m, NULL);
333 static int invoke_property_get(
335 const sd_bus_vtable *v,
337 const char *interface,
338 const char *property,
339 sd_bus_message *reply,
341 sd_bus_error *error) {
353 if (v->x.property.get) {
354 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
357 if (sd_bus_error_is_set(error))
358 return sd_bus_error_get_errno(error);
362 /* Automatic handling if no callback is defined. */
364 if (streq(v->x.property.signature, "as"))
365 return sd_bus_message_append_strv(reply, *(char***) userdata);
367 assert(signature_is_single(v->x.property.signature, false));
368 assert(bus_type_is_basic(v->x.property.signature[0]));
370 switch (v->x.property.signature[0]) {
372 case SD_BUS_TYPE_STRING:
373 case SD_BUS_TYPE_SIGNATURE:
374 p = strempty(*(char**) userdata);
377 case SD_BUS_TYPE_OBJECT_PATH:
378 p = *(char**) userdata;
387 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
390 static int invoke_property_set(
392 const sd_bus_vtable *v,
394 const char *interface,
395 const char *property,
396 sd_bus_message *value,
398 sd_bus_error *error) {
409 if (v->x.property.set) {
410 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
413 if (sd_bus_error_is_set(error))
414 return sd_bus_error_get_errno(error);
418 /* Automatic handling if no callback is defined. */
420 assert(signature_is_single(v->x.property.signature, false));
421 assert(bus_type_is_basic(v->x.property.signature[0]));
423 switch (v->x.property.signature[0]) {
425 case SD_BUS_TYPE_STRING:
426 case SD_BUS_TYPE_OBJECT_PATH:
427 case SD_BUS_TYPE_SIGNATURE: {
431 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
439 free(*(char**) userdata);
440 *(char**) userdata = n;
446 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
456 static int property_get_set_callbacks_run(
459 struct vtable_member *c,
460 bool require_fallback,
462 bool *found_object) {
464 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
465 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
472 assert(found_object);
474 if (require_fallback && !c->parent->is_fallback)
477 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
479 return bus_maybe_reply_error(m, r, &error);
480 if (bus->nodes_modified)
483 *found_object = true;
485 r = sd_bus_message_new_method_return(m, &reply);
490 /* Note that we do not protect against reexecution
491 * here (using the last_iteration check, see below),
492 * should the node tree have changed and we got called
493 * again. We assume that property Get() calls are
494 * ultimately without side-effects or if they aren't
495 * then at least idempotent. */
497 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
501 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
503 return bus_maybe_reply_error(m, r, &error);
505 if (bus->nodes_modified)
508 r = sd_bus_message_close_container(reply);
513 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
514 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
516 /* Avoid that we call the set routine more than once
517 * if the processing of this message got restarted
518 * because the node tree changed. */
519 if (c->last_iteration == bus->iteration_counter)
522 c->last_iteration = bus->iteration_counter;
524 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
528 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
530 return bus_maybe_reply_error(m, r, &error);
532 if (bus->nodes_modified)
535 r = sd_bus_message_exit_container(m);
540 r = sd_bus_send(bus, reply, NULL);
547 static int vtable_append_all_properties(
549 sd_bus_message *reply,
551 struct node_vtable *c,
553 sd_bus_error *error) {
555 const sd_bus_vtable *v;
563 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
564 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
567 r = sd_bus_message_open_container(reply, 'e', "sv");
571 r = sd_bus_message_append(reply, "s", v->x.property.member);
575 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
579 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
582 if (bus->nodes_modified)
585 r = sd_bus_message_close_container(reply);
589 r = sd_bus_message_close_container(reply);
597 static int property_get_all_callbacks_run(
600 struct node_vtable *first,
601 bool require_fallback,
603 bool *found_object) {
605 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
606 struct node_vtable *c;
607 bool found_interface;
612 assert(found_object);
614 r = sd_bus_message_new_method_return(m, &reply);
618 r = sd_bus_message_open_container(reply, 'a', "{sv}");
622 found_interface = !iface ||
623 streq(iface, "org.freedesktop.DBus.Properties") ||
624 streq(iface, "org.freedesktop.DBus.Peer") ||
625 streq(iface, "org.freedesktop.DBus.Introspectable");
627 LIST_FOREACH(vtables, c, first) {
628 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
631 if (require_fallback && !c->is_fallback)
634 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
636 return bus_maybe_reply_error(m, r, &error);
637 if (bus->nodes_modified)
642 *found_object = true;
644 if (iface && !streq(c->interface, iface))
646 found_interface = true;
648 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
650 return bus_maybe_reply_error(m, r, &error);
651 if (bus->nodes_modified)
655 if (!found_interface) {
656 r = sd_bus_reply_method_errorf(
658 SD_BUS_ERROR_UNKNOWN_INTERFACE,
659 "Unknown interface '%s'.", iface);
666 r = sd_bus_message_close_container(reply);
670 r = sd_bus_send(bus, reply, NULL);
677 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
681 if (n->object_manager)
685 return bus_node_with_object_manager(bus, n->parent);
690 static bool bus_node_exists(
694 bool require_fallback) {
696 struct node_vtable *c;
697 struct node_callback *k;
703 /* Tests if there's anything attached directly to this node
704 * for the specified path */
706 LIST_FOREACH(callbacks, k, n->callbacks) {
707 if (require_fallback && !k->is_fallback)
713 LIST_FOREACH(vtables, c, n->vtables) {
714 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
716 if (require_fallback && !c->is_fallback)
719 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
721 if (bus->nodes_modified)
725 return !require_fallback && (n->enumerators || n->object_manager);
728 static int process_introspect(
732 bool require_fallback,
733 bool *found_object) {
735 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
736 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
737 _cleanup_set_free_free_ Set *s = NULL;
738 const char *previous_interface = NULL;
739 struct introspect intro;
740 struct node_vtable *c;
747 assert(found_object);
749 r = get_child_nodes(bus, m->path, n, &s, &error);
751 return bus_maybe_reply_error(m, r, &error);
752 if (bus->nodes_modified)
755 r = introspect_begin(&intro);
759 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
763 empty = set_isempty(s);
765 LIST_FOREACH(vtables, c, n->vtables) {
766 if (require_fallback && !c->is_fallback)
769 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
771 r = bus_maybe_reply_error(m, r, &error);
774 if (bus->nodes_modified) {
783 if (!streq_ptr(previous_interface, c->interface)) {
785 if (previous_interface)
786 fputs(" </interface>\n", intro.f);
788 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
791 r = introspect_write_interface(&intro, c->vtable);
795 previous_interface = c->interface;
798 if (previous_interface)
799 fputs(" </interface>\n", intro.f);
802 /* Nothing?, let's see if we exist at all, and if not
803 * refuse to do anything */
804 r = bus_node_exists(bus, n, m->path, require_fallback);
807 if (bus->nodes_modified)
813 *found_object = true;
815 r = introspect_write_child_nodes(&intro, s, m->path);
819 r = introspect_finish(&intro, bus, m, &reply);
823 r = sd_bus_send(bus, reply, NULL);
830 introspect_free(&intro);
834 static int object_manager_serialize_path(
836 sd_bus_message *reply,
839 bool require_fallback,
840 sd_bus_error *error) {
842 const char *previous_interface = NULL;
843 bool found_something = false;
844 struct node_vtable *i;
854 n = hashmap_get(bus->nodes, prefix);
858 LIST_FOREACH(vtables, i, n->vtables) {
861 if (require_fallback && !i->is_fallback)
864 r = node_vtable_get_userdata(bus, path, i, &u, error);
867 if (bus->nodes_modified)
872 if (!found_something) {
874 /* Open the object part */
876 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
880 r = sd_bus_message_append(reply, "o", path);
884 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
888 found_something = true;
891 if (!streq_ptr(previous_interface, i->interface)) {
893 /* Maybe close the previous interface part */
895 if (previous_interface) {
896 r = sd_bus_message_close_container(reply);
900 r = sd_bus_message_close_container(reply);
905 /* Open the new interface part */
907 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
911 r = sd_bus_message_append(reply, "s", i->interface);
915 r = sd_bus_message_open_container(reply, 'a', "{sv}");
920 r = vtable_append_all_properties(bus, reply, path, i, u, error);
923 if (bus->nodes_modified)
926 previous_interface = i->interface;
929 if (previous_interface) {
930 r = sd_bus_message_close_container(reply);
934 r = sd_bus_message_close_container(reply);
939 if (found_something) {
940 r = sd_bus_message_close_container(reply);
944 r = sd_bus_message_close_container(reply);
952 static int object_manager_serialize_path_and_fallbacks(
954 sd_bus_message *reply,
956 sd_bus_error *error) {
966 /* First, add all vtables registered for this path */
967 r = object_manager_serialize_path(bus, reply, path, path, false, error);
970 if (bus->nodes_modified)
973 /* Second, add fallback vtables registered for any of the prefixes */
974 prefix = alloca(strlen(path) + 1);
975 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
976 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
979 if (bus->nodes_modified)
986 static int process_get_managed_objects(
990 bool require_fallback,
991 bool *found_object) {
993 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
994 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
995 _cleanup_set_free_free_ Set *s = NULL;
1002 assert(found_object);
1004 if (!bus_node_with_object_manager(bus, n))
1007 r = get_child_nodes(bus, m->path, n, &s, &error);
1010 if (bus->nodes_modified)
1013 r = sd_bus_message_new_method_return(m, &reply);
1017 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1021 empty = set_isempty(s);
1023 struct node_vtable *c;
1025 /* Hmm, so we have no children? Then let's check
1026 * whether we exist at all, i.e. whether at least one
1029 LIST_FOREACH(vtables, c, n->vtables) {
1031 if (require_fallback && !c->is_fallback)
1049 SET_FOREACH(path, s, i) {
1050 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1054 if (bus->nodes_modified)
1059 r = sd_bus_message_close_container(reply);
1063 r = sd_bus_send(bus, reply, NULL);
1070 static int object_find_and_run(
1074 bool require_fallback,
1075 bool *found_object) {
1078 struct vtable_member vtable_key, *v;
1084 assert(found_object);
1086 n = hashmap_get(bus->nodes, p);
1090 /* First, try object callbacks */
1091 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1094 if (bus->nodes_modified)
1097 if (!m->interface || !m->member)
1100 /* Then, look for a known method */
1101 vtable_key.path = (char*) p;
1102 vtable_key.interface = m->interface;
1103 vtable_key.member = m->member;
1105 v = hashmap_get(bus->vtable_methods, &vtable_key);
1107 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1110 if (bus->nodes_modified)
1114 /* Then, look for a known property */
1115 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1118 get = streq(m->member, "Get");
1120 if (get || streq(m->member, "Set")) {
1122 r = sd_bus_message_rewind(m, true);
1126 vtable_key.path = (char*) p;
1128 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1130 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1132 v = hashmap_get(bus->vtable_properties, &vtable_key);
1134 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1139 } else if (streq(m->member, "GetAll")) {
1142 r = sd_bus_message_rewind(m, true);
1146 r = sd_bus_message_read(m, "s", &iface);
1148 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1153 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1158 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1160 if (!isempty(sd_bus_message_get_signature(m, true)))
1161 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1163 r = process_introspect(bus, m, n, require_fallback, found_object);
1167 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1169 if (!isempty(sd_bus_message_get_signature(m, true)))
1170 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1172 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1177 if (bus->nodes_modified)
1180 if (!*found_object) {
1181 r = bus_node_exists(bus, n, m->path, require_fallback);
1185 *found_object = true;
1191 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1194 bool found_object = false;
1199 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1205 if (hashmap_isempty(bus->nodes))
1208 pl = strlen(m->path);
1212 bus->nodes_modified = false;
1214 r = object_find_and_run(bus, m, m->path, false, &found_object);
1218 /* Look for fallback prefixes */
1219 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1221 if (bus->nodes_modified)
1224 r = object_find_and_run(bus, m, prefix, true, &found_object);
1229 } while (bus->nodes_modified);
1234 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1235 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1236 r = sd_bus_reply_method_errorf(
1238 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1239 "Unknown property or interface.");
1241 r = sd_bus_reply_method_errorf(
1243 SD_BUS_ERROR_UNKNOWN_METHOD,
1244 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1252 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1253 struct node *n, *parent;
1260 assert(path[0] == '/');
1262 n = hashmap_get(bus->nodes, path);
1266 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1274 if (streq(path, "/"))
1277 e = strrchr(path, '/');
1280 p = strndupa(path, MAX(1, path - e));
1282 parent = bus_node_allocate(bus, p);
1289 n = new0(struct node, 1);
1296 r = hashmap_put(bus->nodes, s, n);
1304 LIST_PREPEND(siblings, parent->child, n);
1309 static void bus_node_gc(sd_bus *b, struct node *n) {
1322 assert(hashmap_remove(b->nodes, n->path) == n);
1325 LIST_REMOVE(siblings, n->parent->child, n);
1328 bus_node_gc(b, n->parent);
1332 static int bus_add_object(
1336 sd_bus_message_handler_t callback,
1339 struct node_callback *c;
1343 assert_return(bus, -EINVAL);
1344 assert_return(object_path_is_valid(path), -EINVAL);
1345 assert_return(callback, -EINVAL);
1346 assert_return(!bus_pid_changed(bus), -ECHILD);
1348 n = bus_node_allocate(bus, path);
1352 c = new0(struct node_callback, 1);
1359 c->callback = callback;
1360 c->userdata = userdata;
1361 c->is_fallback = fallback;
1363 LIST_PREPEND(callbacks, n->callbacks, c);
1364 bus->nodes_modified = true;
1370 bus_node_gc(bus, n);
1374 static int bus_remove_object(
1378 sd_bus_message_handler_t callback,
1381 struct node_callback *c;
1384 assert_return(bus, -EINVAL);
1385 assert_return(object_path_is_valid(path), -EINVAL);
1386 assert_return(callback, -EINVAL);
1387 assert_return(!bus_pid_changed(bus), -ECHILD);
1389 n = hashmap_get(bus->nodes, path);
1393 LIST_FOREACH(callbacks, c, n->callbacks)
1394 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1399 LIST_REMOVE(callbacks, n->callbacks, c);
1402 bus_node_gc(bus, n);
1403 bus->nodes_modified = true;
1408 _public_ int sd_bus_add_object(sd_bus *bus,
1410 sd_bus_message_handler_t callback,
1413 return bus_add_object(bus, false, path, callback, userdata);
1416 _public_ int sd_bus_remove_object(sd_bus *bus,
1418 sd_bus_message_handler_t callback,
1421 return bus_remove_object(bus, false, path, callback, userdata);
1424 _public_ int sd_bus_add_fallback(sd_bus *bus,
1426 sd_bus_message_handler_t callback,
1429 return bus_add_object(bus, true, prefix, callback, userdata);
1432 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1434 sd_bus_message_handler_t callback,
1437 return bus_remove_object(bus, true, prefix, callback, userdata);
1440 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1446 if (w->interface && w->node && w->vtable) {
1447 const sd_bus_vtable *v;
1449 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1450 struct vtable_member *x = NULL;
1454 case _SD_BUS_VTABLE_METHOD: {
1455 struct vtable_member key;
1457 key.path = w->node->path;
1458 key.interface = w->interface;
1459 key.member = v->x.method.member;
1461 x = hashmap_remove(bus->vtable_methods, &key);
1465 case _SD_BUS_VTABLE_PROPERTY:
1466 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1467 struct vtable_member key;
1469 key.path = w->node->path;
1470 key.interface = w->interface;
1471 key.member = v->x.property.member;
1472 x = hashmap_remove(bus->vtable_properties, &key);
1484 static unsigned vtable_member_hash_func(const void *a) {
1485 const struct vtable_member *m = a;
1490 string_hash_func(m->path) ^
1491 string_hash_func(m->interface) ^
1492 string_hash_func(m->member);
1495 static int vtable_member_compare_func(const void *a, const void *b) {
1496 const struct vtable_member *x = a, *y = b;
1502 r = strcmp(x->path, y->path);
1506 r = strcmp(x->interface, y->interface);
1510 return strcmp(x->member, y->member);
1513 static int add_object_vtable_internal(
1516 const char *interface,
1517 const sd_bus_vtable *vtable,
1519 sd_bus_object_find_t find,
1522 struct node_vtable *c = NULL, *i, *existing = NULL;
1523 const sd_bus_vtable *v;
1527 assert_return(bus, -EINVAL);
1528 assert_return(object_path_is_valid(path), -EINVAL);
1529 assert_return(interface_name_is_valid(interface), -EINVAL);
1530 assert_return(vtable, -EINVAL);
1531 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1532 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1533 assert_return(!bus_pid_changed(bus), -ECHILD);
1534 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1535 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1536 !streq(interface, "org.freedesktop.DBus.Peer") &&
1537 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1539 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1543 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1547 n = bus_node_allocate(bus, path);
1551 LIST_FOREACH(vtables, i, n->vtables) {
1552 if (i->is_fallback != fallback) {
1557 if (streq(i->interface, interface)) {
1559 if (i->vtable == vtable) {
1568 c = new0(struct node_vtable, 1);
1575 c->is_fallback = fallback;
1577 c->userdata = userdata;
1580 c->interface = strdup(interface);
1581 if (!c->interface) {
1586 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1590 case _SD_BUS_VTABLE_METHOD: {
1591 struct vtable_member *m;
1593 if (!member_name_is_valid(v->x.method.member) ||
1594 !signature_is_valid(strempty(v->x.method.signature), false) ||
1595 !signature_is_valid(strempty(v->x.method.result), false) ||
1596 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1597 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1602 m = new0(struct vtable_member, 1);
1610 m->interface = c->interface;
1611 m->member = v->x.method.member;
1614 r = hashmap_put(bus->vtable_methods, m, m);
1623 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1625 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1632 case _SD_BUS_VTABLE_PROPERTY: {
1633 struct vtable_member *m;
1635 if (!member_name_is_valid(v->x.property.member) ||
1636 !signature_is_single(v->x.property.signature, false) ||
1637 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1638 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1639 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1645 m = new0(struct vtable_member, 1);
1653 m->interface = c->interface;
1654 m->member = v->x.property.member;
1657 r = hashmap_put(bus->vtable_properties, m, m);
1666 case _SD_BUS_VTABLE_SIGNAL:
1668 if (!member_name_is_valid(v->x.signal.member) ||
1669 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1682 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1683 bus->nodes_modified = true;
1689 free_node_vtable(bus, c);
1691 bus_node_gc(bus, n);
1695 static int remove_object_vtable_internal(
1698 const char *interface,
1699 const sd_bus_vtable *vtable,
1701 sd_bus_object_find_t find,
1704 struct node_vtable *c;
1707 assert_return(bus, -EINVAL);
1708 assert_return(object_path_is_valid(path), -EINVAL);
1709 assert_return(interface_name_is_valid(interface), -EINVAL);
1710 assert_return(!bus_pid_changed(bus), -ECHILD);
1712 n = hashmap_get(bus->nodes, path);
1716 LIST_FOREACH(vtables, c, n->vtables)
1717 if (streq(c->interface, interface) &&
1718 c->is_fallback == fallback &&
1719 c->vtable == vtable &&
1721 c->userdata == userdata)
1727 LIST_REMOVE(vtables, n->vtables, c);
1729 free_node_vtable(bus, c);
1730 bus_node_gc(bus, n);
1732 bus->nodes_modified = true;
1737 _public_ int sd_bus_add_object_vtable(
1740 const char *interface,
1741 const sd_bus_vtable *vtable,
1744 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1747 _public_ int sd_bus_remove_object_vtable(
1750 const char *interface,
1751 const sd_bus_vtable *vtable,
1754 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1757 _public_ int sd_bus_add_fallback_vtable(
1760 const char *interface,
1761 const sd_bus_vtable *vtable,
1762 sd_bus_object_find_t find,
1765 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1768 _public_ int sd_bus_remove_fallback_vtable(
1771 const char *interface,
1772 const sd_bus_vtable *vtable,
1773 sd_bus_object_find_t find,
1776 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1779 _public_ int sd_bus_add_node_enumerator(
1782 sd_bus_node_enumerator_t callback,
1785 struct node_enumerator *c;
1789 assert_return(bus, -EINVAL);
1790 assert_return(object_path_is_valid(path), -EINVAL);
1791 assert_return(callback, -EINVAL);
1792 assert_return(!bus_pid_changed(bus), -ECHILD);
1794 n = bus_node_allocate(bus, path);
1798 c = new0(struct node_enumerator, 1);
1805 c->callback = callback;
1806 c->userdata = userdata;
1808 LIST_PREPEND(enumerators, n->enumerators, c);
1810 bus->nodes_modified = true;
1816 bus_node_gc(bus, n);
1820 _public_ int sd_bus_remove_node_enumerator(
1823 sd_bus_node_enumerator_t callback,
1826 struct node_enumerator *c;
1829 assert_return(bus, -EINVAL);
1830 assert_return(object_path_is_valid(path), -EINVAL);
1831 assert_return(callback, -EINVAL);
1832 assert_return(!bus_pid_changed(bus), -ECHILD);
1834 n = hashmap_get(bus->nodes, path);
1838 LIST_FOREACH(enumerators, c, n->enumerators)
1839 if (c->callback == callback && c->userdata == userdata)
1845 LIST_REMOVE(enumerators, n->enumerators, c);
1848 bus_node_gc(bus, n);
1850 bus->nodes_modified = true;
1855 static int emit_properties_changed_on_interface(
1859 const char *interface,
1860 bool require_fallback,
1863 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1864 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1865 bool has_invalidating = false, has_changing = false;
1866 struct vtable_member key = {};
1867 struct node_vtable *c;
1878 n = hashmap_get(bus->nodes, prefix);
1882 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1886 r = sd_bus_message_append(m, "s", interface);
1890 r = sd_bus_message_open_container(m, 'a', "{sv}");
1895 key.interface = interface;
1897 LIST_FOREACH(vtables, c, n->vtables) {
1898 if (require_fallback && !c->is_fallback)
1901 if (!streq(c->interface, interface))
1904 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1907 if (bus->nodes_modified)
1912 STRV_FOREACH(property, names) {
1913 struct vtable_member *v;
1915 assert_return(member_name_is_valid(*property), -EINVAL);
1917 key.member = *property;
1918 v = hashmap_get(bus->vtable_properties, &key);
1922 /* If there are two vtables for the same
1923 * interface, let's handle this property when
1924 * we come to that vtable. */
1928 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1930 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1931 has_invalidating = true;
1935 has_changing = true;
1937 r = sd_bus_message_open_container(m, 'e', "sv");
1941 r = sd_bus_message_append(m, "s", *property);
1945 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1949 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error);
1952 if (bus->nodes_modified)
1955 r = sd_bus_message_close_container(m);
1959 r = sd_bus_message_close_container(m);
1965 if (!has_invalidating && !has_changing)
1968 r = sd_bus_message_close_container(m);
1972 r = sd_bus_message_open_container(m, 'a', "s");
1976 if (has_invalidating) {
1977 LIST_FOREACH(vtables, c, n->vtables) {
1978 if (require_fallback && !c->is_fallback)
1981 if (!streq(c->interface, interface))
1984 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1987 if (bus->nodes_modified)
1992 STRV_FOREACH(property, names) {
1993 struct vtable_member *v;
1995 key.member = *property;
1996 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1997 assert(c == v->parent);
1999 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
2002 r = sd_bus_message_append(m, "s", *property);
2009 r = sd_bus_message_close_container(m);
2013 r = sd_bus_send(bus, m, NULL);
2020 _public_ int sd_bus_emit_properties_changed_strv(
2023 const char *interface,
2026 BUS_DONT_DESTROY(bus);
2030 assert_return(bus, -EINVAL);
2031 assert_return(object_path_is_valid(path), -EINVAL);
2032 assert_return(interface_name_is_valid(interface), -EINVAL);
2033 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2034 assert_return(!bus_pid_changed(bus), -ECHILD);
2036 if (strv_isempty(names))
2040 bus->nodes_modified = false;
2042 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2045 if (bus->nodes_modified)
2048 prefix = alloca(strlen(path) + 1);
2049 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2050 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2053 if (bus->nodes_modified)
2057 } while (bus->nodes_modified);
2062 _public_ int sd_bus_emit_properties_changed(
2065 const char *interface,
2066 const char *name, ...) {
2070 assert_return(bus, -EINVAL);
2071 assert_return(object_path_is_valid(path), -EINVAL);
2072 assert_return(interface_name_is_valid(interface), -EINVAL);
2073 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2074 assert_return(!bus_pid_changed(bus), -ECHILD);
2079 names = strv_from_stdarg_alloca(name);
2081 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2084 static int interfaces_added_append_one_prefix(
2089 const char *interface,
2090 bool require_fallback) {
2092 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2093 bool found_interface = false;
2094 struct node_vtable *c;
2105 n = hashmap_get(bus->nodes, prefix);
2109 LIST_FOREACH(vtables, c, n->vtables) {
2110 if (require_fallback && !c->is_fallback)
2113 if (!streq(c->interface, interface))
2116 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2119 if (bus->nodes_modified)
2124 if (!found_interface) {
2125 r = sd_bus_message_append_basic(m, 's', interface);
2129 r = sd_bus_message_open_container(m, 'a', "{sv}");
2133 found_interface = true;
2136 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2139 if (bus->nodes_modified)
2143 if (found_interface) {
2144 r = sd_bus_message_close_container(m);
2149 return found_interface;
2152 static int interfaces_added_append_one(
2156 const char *interface) {
2166 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2169 if (bus->nodes_modified)
2172 prefix = alloca(strlen(path) + 1);
2173 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2174 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2177 if (bus->nodes_modified)
2184 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2185 BUS_DONT_DESTROY(bus);
2187 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2191 assert_return(bus, -EINVAL);
2192 assert_return(object_path_is_valid(path), -EINVAL);
2193 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2194 assert_return(!bus_pid_changed(bus), -ECHILD);
2196 if (strv_isempty(interfaces))
2200 bus->nodes_modified = false;
2203 m = sd_bus_message_unref(m);
2205 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2209 r = sd_bus_message_append_basic(m, 'o', path);
2213 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2217 STRV_FOREACH(i, interfaces) {
2218 assert_return(interface_name_is_valid(*i), -EINVAL);
2220 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2224 r = interfaces_added_append_one(bus, m, path, *i);
2228 if (bus->nodes_modified)
2231 r = sd_bus_message_close_container(m);
2236 if (bus->nodes_modified)
2239 r = sd_bus_message_close_container(m);
2243 } while (bus->nodes_modified);
2245 return sd_bus_send(bus, m, NULL);
2248 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2251 assert_return(bus, -EINVAL);
2252 assert_return(object_path_is_valid(path), -EINVAL);
2253 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2254 assert_return(!bus_pid_changed(bus), -ECHILD);
2256 interfaces = strv_from_stdarg_alloca(interface);
2258 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2261 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2262 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2265 assert_return(bus, -EINVAL);
2266 assert_return(object_path_is_valid(path), -EINVAL);
2267 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2268 assert_return(!bus_pid_changed(bus), -ECHILD);
2270 if (strv_isempty(interfaces))
2273 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2277 r = sd_bus_message_append_basic(m, 'o', path);
2281 r = sd_bus_message_append_strv(m, interfaces);
2285 return sd_bus_send(bus, m, NULL);
2288 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2291 assert_return(bus, -EINVAL);
2292 assert_return(object_path_is_valid(path), -EINVAL);
2293 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2294 assert_return(!bus_pid_changed(bus), -ECHILD);
2296 interfaces = strv_from_stdarg_alloca(interface);
2298 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2301 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2304 assert_return(bus, -EINVAL);
2305 assert_return(object_path_is_valid(path), -EINVAL);
2306 assert_return(!bus_pid_changed(bus), -ECHILD);
2308 n = bus_node_allocate(bus, path);
2312 n->object_manager = true;
2313 bus->nodes_modified = true;
2317 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2320 assert_return(bus, -EINVAL);
2321 assert_return(object_path_is_valid(path), -EINVAL);
2322 assert_return(!bus_pid_changed(bus), -ECHILD);
2324 n = hashmap_get(bus->nodes, path);
2328 if (!n->object_manager)
2331 n->object_manager = false;
2332 bus->nodes_modified = true;
2333 bus_node_gc(bus, n);