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,
47 r = c->find(bus, path, c->interface, &u, u);
58 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
61 return (uint8_t*) u + p->x.property.offset;
64 static int vtable_property_get_userdata(
67 struct vtable_member *p,
78 r = node_vtable_get_userdata(bus, path, p->parent, &u);
81 if (bus->nodes_modified)
84 *userdata = vtable_property_convert_userdata(p->vtable, u);
88 static int add_enumerated_to_set(
91 struct node_enumerator *first,
94 struct node_enumerator *c;
101 LIST_FOREACH(enumerators, c, first) {
102 char **children = NULL, **k;
104 if (bus->nodes_modified)
107 r = c->callback(bus, prefix, &children, c->userdata);
111 STRV_FOREACH(k, children) {
117 if (!object_path_is_valid(*k)){
123 if (!object_path_startswith(*k, prefix)) {
128 r = set_consume(s, *k);
139 static int add_subtree_to_set(
153 r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
156 if (bus->nodes_modified)
159 LIST_FOREACH(siblings, i, n->child) {
162 if (!object_path_startswith(i->path, prefix))
169 r = set_consume(s, t);
170 if (r < 0 && r != -EEXIST)
173 r = add_subtree_to_set(bus, prefix, i, s);
176 if (bus->nodes_modified)
183 static int get_child_nodes(
197 s = set_new(string_hash_func, string_compare_func);
201 r = add_subtree_to_set(bus, prefix, n, s);
211 static int node_callbacks_run(
214 struct node_callback *first,
215 bool require_fallback,
216 bool *found_object) {
218 struct node_callback *c;
223 assert(found_object);
225 LIST_FOREACH(callbacks, c, first) {
226 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
228 if (bus->nodes_modified)
231 if (require_fallback && !c->is_fallback)
234 *found_object = true;
236 if (c->last_iteration == bus->iteration_counter)
239 c->last_iteration = bus->iteration_counter;
241 r = sd_bus_message_rewind(m, true);
245 r = c->callback(bus, m, c->userdata, &error_buffer);
246 r = bus_maybe_reply_error(m, r, &error_buffer);
254 static int method_callbacks_run(
257 struct vtable_member *c,
258 bool require_fallback,
259 bool *found_object) {
261 const char *signature;
268 assert(found_object);
270 if (require_fallback && !c->parent->is_fallback)
273 r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
276 if (bus->nodes_modified)
279 *found_object = true;
281 if (c->last_iteration == bus->iteration_counter)
284 c->last_iteration = bus->iteration_counter;
286 r = sd_bus_message_rewind(m, true);
290 signature = sd_bus_message_get_signature(m, true);
294 if (!streq(strempty(c->vtable->x.method.signature), signature)) {
295 r = sd_bus_reply_method_errorf(m,
296 SD_BUS_ERROR_INVALID_ARGS,
297 "Invalid arguments '%s' to call %s:%s, expecting '%s'.",
298 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
305 if (c->vtable->x.method.handler) {
306 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
308 r = c->vtable->x.method.handler(bus, m, u, &error_buffer);
310 return bus_maybe_reply_error(m, r, &error_buffer);
313 /* If the method callback is NULL, make this a successful NOP */
314 r = sd_bus_reply_method_return(m, NULL);
321 static int invoke_property_get(
323 const sd_bus_vtable *v,
325 const char *interface,
326 const char *property,
327 sd_bus_message *reply,
329 sd_bus_error *error) {
340 if (v->x.property.get)
341 return v->x.property.get(bus, path, interface, property, reply, userdata, error);
343 /* Automatic handling if no callback is defined. */
345 if (streq(v->x.property.signature, "as"))
346 return sd_bus_message_append_strv(reply, *(char***) userdata);
348 assert(signature_is_single(v->x.property.signature, false));
349 assert(bus_type_is_basic(v->x.property.signature[0]));
351 switch (v->x.property.signature[0]) {
353 case SD_BUS_TYPE_STRING:
354 case SD_BUS_TYPE_SIGNATURE:
355 p = strempty(*(char**) userdata);
358 case SD_BUS_TYPE_OBJECT_PATH:
359 p = *(char**) userdata;
368 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
371 static int invoke_property_set(
373 const sd_bus_vtable *v,
375 const char *interface,
376 const char *property,
377 sd_bus_message *value,
379 sd_bus_error *error) {
390 if (v->x.property.set)
391 return v->x.property.set(bus, path, interface, property, value, userdata, error);
393 /* Automatic handling if no callback is defined. */
395 assert(signature_is_single(v->x.property.signature, false));
396 assert(bus_type_is_basic(v->x.property.signature[0]));
398 switch (v->x.property.signature[0]) {
400 case SD_BUS_TYPE_STRING:
401 case SD_BUS_TYPE_OBJECT_PATH:
402 case SD_BUS_TYPE_SIGNATURE: {
406 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
414 free(*(char**) userdata);
415 *(char**) userdata = n;
421 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
431 static int property_get_set_callbacks_run(
434 struct vtable_member *c,
435 bool require_fallback,
437 bool *found_object) {
439 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
440 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
447 assert(found_object);
449 if (require_fallback && !c->parent->is_fallback)
452 r = vtable_property_get_userdata(bus, m->path, c, &u);
455 if (bus->nodes_modified)
458 *found_object = true;
460 r = sd_bus_message_new_method_return(m, &reply);
465 /* Note that we do not protect against reexecution
466 * here (using the last_iteration check, see below),
467 * should the node tree have changed and we got called
468 * again. We assume that property Get() calls are
469 * ultimately without side-effects or if they aren't
470 * then at least idempotent. */
472 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
476 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
478 return sd_bus_reply_method_errno(m, r, &error);
479 if (sd_bus_error_is_set(&error))
480 return sd_bus_reply_method_error(m, &error);
482 if (bus->nodes_modified)
485 r = sd_bus_message_close_container(reply);
490 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
491 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
493 /* Avoid that we call the set routine more than once
494 * if the processing of this message got restarted
495 * because the node tree changed. */
496 if (c->last_iteration == bus->iteration_counter)
499 c->last_iteration = bus->iteration_counter;
501 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
505 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
507 return sd_bus_reply_method_errno(m, r, &error);
508 if (sd_bus_error_is_set(&error))
509 return sd_bus_reply_method_error(m, &error);
511 if (bus->nodes_modified)
514 r = sd_bus_message_exit_container(m);
519 r = sd_bus_send(bus, reply, NULL);
526 static int vtable_append_all_properties(
528 sd_bus_message *reply,
530 struct node_vtable *c,
532 sd_bus_error *error) {
534 const sd_bus_vtable *v;
542 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
543 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
546 r = sd_bus_message_open_container(reply, 'e', "sv");
550 r = sd_bus_message_append(reply, "s", v->x.property.member);
554 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
558 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
559 if (sd_bus_error_is_set(error))
562 sd_bus_error_set_errno(error, r);
565 if (bus->nodes_modified)
568 r = sd_bus_message_close_container(reply);
572 r = sd_bus_message_close_container(reply);
580 static int property_get_all_callbacks_run(
583 struct node_vtable *first,
584 bool require_fallback,
586 bool *found_object) {
588 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
589 struct node_vtable *c;
590 bool found_interface;
595 assert(found_object);
597 r = sd_bus_message_new_method_return(m, &reply);
601 r = sd_bus_message_open_container(reply, 'a', "{sv}");
605 found_interface = !iface ||
606 streq(iface, "org.freedesktop.DBus.Properties") ||
607 streq(iface, "org.freedesktop.DBus.Peer") ||
608 streq(iface, "org.freedesktop.DBus.Introspectable");
610 LIST_FOREACH(vtables, c, first) {
611 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
614 if (require_fallback && !c->is_fallback)
617 r = node_vtable_get_userdata(bus, m->path, c, &u);
620 if (bus->nodes_modified)
625 *found_object = true;
627 if (iface && !streq(c->interface, iface))
629 found_interface = true;
631 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
635 if (sd_bus_error_is_set(&error)) {
636 r = sd_bus_reply_method_error(m, &error);
642 if (bus->nodes_modified)
646 if (!found_interface) {
647 r = sd_bus_reply_method_errorf(
649 SD_BUS_ERROR_UNKNOWN_INTERFACE,
650 "Unknown interface '%s'.", iface);
657 r = sd_bus_message_close_container(reply);
661 r = sd_bus_send(bus, reply, NULL);
668 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
672 if (n->object_manager)
676 return bus_node_with_object_manager(bus, n->parent);
681 static bool bus_node_exists(
685 bool require_fallback) {
687 struct node_vtable *c;
688 struct node_callback *k;
694 /* Tests if there's anything attached directly to this node
695 * for the specified path */
697 LIST_FOREACH(callbacks, k, n->callbacks) {
698 if (require_fallback && !k->is_fallback)
704 LIST_FOREACH(vtables, c, n->vtables) {
706 if (require_fallback && !c->is_fallback)
709 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
711 if (bus->nodes_modified)
715 return !require_fallback && (n->enumerators || n->object_manager);
718 static int process_introspect(
722 bool require_fallback,
723 bool *found_object) {
725 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
726 _cleanup_set_free_free_ Set *s = NULL;
727 const char *previous_interface = NULL;
728 struct introspect intro;
729 struct node_vtable *c;
736 assert(found_object);
738 r = get_child_nodes(bus, m->path, n, &s);
741 if (bus->nodes_modified)
744 r = introspect_begin(&intro);
748 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
752 empty = set_isempty(s);
754 LIST_FOREACH(vtables, c, n->vtables) {
755 if (require_fallback && !c->is_fallback)
758 r = node_vtable_get_userdata(bus, m->path, c, NULL);
761 if (bus->nodes_modified)
768 if (!streq_ptr(previous_interface, c->interface)) {
770 if (previous_interface)
771 fputs(" </interface>\n", intro.f);
773 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
776 r = introspect_write_interface(&intro, c->vtable);
780 previous_interface = c->interface;
783 if (previous_interface)
784 fputs(" </interface>\n", intro.f);
787 /* Nothing?, let's see if we exist at all, and if not
788 * refuse to do anything */
789 r = bus_node_exists(bus, n, m->path, require_fallback);
792 if (bus->nodes_modified)
798 *found_object = true;
800 r = introspect_write_child_nodes(&intro, s, m->path);
804 r = introspect_finish(&intro, bus, m, &reply);
808 r = sd_bus_send(bus, reply, NULL);
815 introspect_free(&intro);
819 static int object_manager_serialize_path(
821 sd_bus_message *reply,
824 bool require_fallback,
825 sd_bus_error *error) {
827 const char *previous_interface = NULL;
828 bool found_something = false;
829 struct node_vtable *i;
839 n = hashmap_get(bus->nodes, prefix);
843 LIST_FOREACH(vtables, i, n->vtables) {
846 if (require_fallback && !i->is_fallback)
849 r = node_vtable_get_userdata(bus, path, i, &u);
852 if (bus->nodes_modified)
857 if (!found_something) {
859 /* Open the object part */
861 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
865 r = sd_bus_message_append(reply, "o", path);
869 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
873 found_something = true;
876 if (!streq_ptr(previous_interface, i->interface)) {
878 /* Maybe close the previous interface part */
880 if (previous_interface) {
881 r = sd_bus_message_close_container(reply);
885 r = sd_bus_message_close_container(reply);
890 /* Open the new interface part */
892 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
896 r = sd_bus_message_append(reply, "s", i->interface);
900 r = sd_bus_message_open_container(reply, 'a', "{sv}");
905 r = vtable_append_all_properties(bus, reply, path, i, u, error);
908 if (sd_bus_error_is_set(error))
910 if (bus->nodes_modified)
913 previous_interface = i->interface;
916 if (previous_interface) {
917 r = sd_bus_message_close_container(reply);
921 r = sd_bus_message_close_container(reply);
926 if (found_something) {
927 r = sd_bus_message_close_container(reply);
931 r = sd_bus_message_close_container(reply);
939 static int object_manager_serialize_path_and_fallbacks(
941 sd_bus_message *reply,
943 sd_bus_error *error) {
953 /* First, add all vtables registered for this path */
954 r = object_manager_serialize_path(bus, reply, path, path, false, error);
957 if (sd_bus_error_is_set(error))
959 if (bus->nodes_modified)
962 /* Second, add fallback vtables registered for any of the prefixes */
963 prefix = alloca(strlen(path) + 1);
964 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
965 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
968 if (sd_bus_error_is_set(error))
970 if (bus->nodes_modified)
977 static int process_get_managed_objects(
981 bool require_fallback,
982 bool *found_object) {
984 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
985 _cleanup_set_free_free_ Set *s = NULL;
992 assert(found_object);
994 if (!bus_node_with_object_manager(bus, n))
997 r = get_child_nodes(bus, m->path, n, &s);
1000 if (bus->nodes_modified)
1003 r = sd_bus_message_new_method_return(m, &reply);
1007 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1011 empty = set_isempty(s);
1013 struct node_vtable *c;
1015 /* Hmm, so we have no children? Then let's check
1016 * whether we exist at all, i.e. whether at least one
1019 LIST_FOREACH(vtables, c, n->vtables) {
1021 if (require_fallback && !c->is_fallback)
1039 SET_FOREACH(path, s, i) {
1040 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1042 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1046 if (sd_bus_error_is_set(&error)) {
1047 r = sd_bus_reply_method_error(m, &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);
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);
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 r = process_introspect(bus, m, n, require_fallback, found_object);
1164 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1166 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1171 if (bus->nodes_modified)
1174 if (!*found_object) {
1175 r = bus_node_exists(bus, n, m->path, require_fallback);
1179 *found_object = true;
1185 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1188 bool found_object = false;
1193 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1199 if (hashmap_isempty(bus->nodes))
1202 pl = strlen(m->path);
1206 bus->nodes_modified = false;
1208 r = object_find_and_run(bus, m, m->path, false, &found_object);
1212 /* Look for fallback prefixes */
1213 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1215 if (bus->nodes_modified)
1218 r = object_find_and_run(bus, m, prefix, true, &found_object);
1223 } while (bus->nodes_modified);
1228 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1229 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1230 r = sd_bus_reply_method_errorf(
1232 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1233 "Unknown property or interface.");
1235 r = sd_bus_reply_method_errorf(
1237 SD_BUS_ERROR_UNKNOWN_METHOD,
1238 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1246 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1247 struct node *n, *parent;
1254 assert(path[0] == '/');
1256 n = hashmap_get(bus->nodes, path);
1260 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1268 if (streq(path, "/"))
1271 e = strrchr(path, '/');
1274 p = strndupa(path, MAX(1, path - e));
1276 parent = bus_node_allocate(bus, p);
1283 n = new0(struct node, 1);
1290 r = hashmap_put(bus->nodes, s, n);
1298 LIST_PREPEND(siblings, parent->child, n);
1303 static void bus_node_gc(sd_bus *b, struct node *n) {
1316 assert(hashmap_remove(b->nodes, n->path) == n);
1319 LIST_REMOVE(siblings, n->parent->child, n);
1322 bus_node_gc(b, n->parent);
1326 static int bus_add_object(
1330 sd_bus_message_handler_t callback,
1333 struct node_callback *c;
1337 assert_return(bus, -EINVAL);
1338 assert_return(object_path_is_valid(path), -EINVAL);
1339 assert_return(callback, -EINVAL);
1340 assert_return(!bus_pid_changed(bus), -ECHILD);
1342 n = bus_node_allocate(bus, path);
1346 c = new0(struct node_callback, 1);
1353 c->callback = callback;
1354 c->userdata = userdata;
1355 c->is_fallback = fallback;
1357 LIST_PREPEND(callbacks, n->callbacks, c);
1358 bus->nodes_modified = true;
1364 bus_node_gc(bus, n);
1368 static int bus_remove_object(
1372 sd_bus_message_handler_t callback,
1375 struct node_callback *c;
1378 assert_return(bus, -EINVAL);
1379 assert_return(object_path_is_valid(path), -EINVAL);
1380 assert_return(callback, -EINVAL);
1381 assert_return(!bus_pid_changed(bus), -ECHILD);
1383 n = hashmap_get(bus->nodes, path);
1387 LIST_FOREACH(callbacks, c, n->callbacks)
1388 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1393 LIST_REMOVE(callbacks, n->callbacks, c);
1396 bus_node_gc(bus, n);
1397 bus->nodes_modified = true;
1402 _public_ int sd_bus_add_object(sd_bus *bus,
1404 sd_bus_message_handler_t callback,
1407 return bus_add_object(bus, false, path, callback, userdata);
1410 _public_ int sd_bus_remove_object(sd_bus *bus,
1412 sd_bus_message_handler_t callback,
1415 return bus_remove_object(bus, false, path, callback, userdata);
1418 _public_ int sd_bus_add_fallback(sd_bus *bus,
1420 sd_bus_message_handler_t callback,
1423 return bus_add_object(bus, true, prefix, callback, userdata);
1426 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1428 sd_bus_message_handler_t callback,
1431 return bus_remove_object(bus, true, prefix, callback, userdata);
1434 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1440 if (w->interface && w->node && w->vtable) {
1441 const sd_bus_vtable *v;
1443 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1444 struct vtable_member *x = NULL;
1448 case _SD_BUS_VTABLE_METHOD: {
1449 struct vtable_member key;
1451 key.path = w->node->path;
1452 key.interface = w->interface;
1453 key.member = v->x.method.member;
1455 x = hashmap_remove(bus->vtable_methods, &key);
1459 case _SD_BUS_VTABLE_PROPERTY:
1460 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1461 struct vtable_member key;
1463 key.path = w->node->path;
1464 key.interface = w->interface;
1465 key.member = v->x.property.member;
1466 x = hashmap_remove(bus->vtable_properties, &key);
1478 static unsigned vtable_member_hash_func(const void *a) {
1479 const struct vtable_member *m = a;
1484 string_hash_func(m->path) ^
1485 string_hash_func(m->interface) ^
1486 string_hash_func(m->member);
1489 static int vtable_member_compare_func(const void *a, const void *b) {
1490 const struct vtable_member *x = a, *y = b;
1496 r = strcmp(x->path, y->path);
1500 r = strcmp(x->interface, y->interface);
1504 return strcmp(x->member, y->member);
1507 static int add_object_vtable_internal(
1510 const char *interface,
1511 const sd_bus_vtable *vtable,
1513 sd_bus_object_find_t find,
1516 struct node_vtable *c = NULL, *i, *existing = NULL;
1517 const sd_bus_vtable *v;
1521 assert_return(bus, -EINVAL);
1522 assert_return(object_path_is_valid(path), -EINVAL);
1523 assert_return(interface_name_is_valid(interface), -EINVAL);
1524 assert_return(vtable, -EINVAL);
1525 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1526 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1527 assert_return(!bus_pid_changed(bus), -ECHILD);
1528 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1529 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1530 !streq(interface, "org.freedesktop.DBus.Peer") &&
1531 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1533 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1537 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1541 n = bus_node_allocate(bus, path);
1545 LIST_FOREACH(vtables, i, n->vtables) {
1546 if (i->is_fallback != fallback) {
1551 if (streq(i->interface, interface)) {
1553 if (i->vtable == vtable) {
1562 c = new0(struct node_vtable, 1);
1569 c->is_fallback = fallback;
1571 c->userdata = userdata;
1574 c->interface = strdup(interface);
1575 if (!c->interface) {
1580 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1584 case _SD_BUS_VTABLE_METHOD: {
1585 struct vtable_member *m;
1587 if (!member_name_is_valid(v->x.method.member) ||
1588 !signature_is_valid(strempty(v->x.method.signature), false) ||
1589 !signature_is_valid(strempty(v->x.method.result), false) ||
1590 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1591 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1596 m = new0(struct vtable_member, 1);
1604 m->interface = c->interface;
1605 m->member = v->x.method.member;
1608 r = hashmap_put(bus->vtable_methods, m, m);
1617 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1619 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1626 case _SD_BUS_VTABLE_PROPERTY: {
1627 struct vtable_member *m;
1629 if (!member_name_is_valid(v->x.property.member) ||
1630 !signature_is_single(v->x.property.signature, false) ||
1631 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1632 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1633 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1639 m = new0(struct vtable_member, 1);
1647 m->interface = c->interface;
1648 m->member = v->x.property.member;
1651 r = hashmap_put(bus->vtable_properties, m, m);
1660 case _SD_BUS_VTABLE_SIGNAL:
1662 if (!member_name_is_valid(v->x.signal.member) ||
1663 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1676 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1677 bus->nodes_modified = true;
1683 free_node_vtable(bus, c);
1685 bus_node_gc(bus, n);
1689 static int remove_object_vtable_internal(
1692 const char *interface,
1693 const sd_bus_vtable *vtable,
1695 sd_bus_object_find_t find,
1698 struct node_vtable *c;
1701 assert_return(bus, -EINVAL);
1702 assert_return(object_path_is_valid(path), -EINVAL);
1703 assert_return(interface_name_is_valid(interface), -EINVAL);
1704 assert_return(!bus_pid_changed(bus), -ECHILD);
1706 n = hashmap_get(bus->nodes, path);
1710 LIST_FOREACH(vtables, c, n->vtables)
1711 if (streq(c->interface, interface) &&
1712 c->is_fallback == fallback &&
1713 c->vtable == vtable &&
1715 c->userdata == userdata)
1721 LIST_REMOVE(vtables, n->vtables, c);
1723 free_node_vtable(bus, c);
1724 bus_node_gc(bus, n);
1726 bus->nodes_modified = true;
1731 _public_ int sd_bus_add_object_vtable(
1734 const char *interface,
1735 const sd_bus_vtable *vtable,
1738 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1741 _public_ int sd_bus_remove_object_vtable(
1744 const char *interface,
1745 const sd_bus_vtable *vtable,
1748 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1751 _public_ int sd_bus_add_fallback_vtable(
1754 const char *interface,
1755 const sd_bus_vtable *vtable,
1756 sd_bus_object_find_t find,
1759 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1762 _public_ int sd_bus_remove_fallback_vtable(
1765 const char *interface,
1766 const sd_bus_vtable *vtable,
1767 sd_bus_object_find_t find,
1770 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1773 _public_ int sd_bus_add_node_enumerator(
1776 sd_bus_node_enumerator_t callback,
1779 struct node_enumerator *c;
1783 assert_return(bus, -EINVAL);
1784 assert_return(object_path_is_valid(path), -EINVAL);
1785 assert_return(callback, -EINVAL);
1786 assert_return(!bus_pid_changed(bus), -ECHILD);
1788 n = bus_node_allocate(bus, path);
1792 c = new0(struct node_enumerator, 1);
1799 c->callback = callback;
1800 c->userdata = userdata;
1802 LIST_PREPEND(enumerators, n->enumerators, c);
1804 bus->nodes_modified = true;
1810 bus_node_gc(bus, n);
1814 _public_ int sd_bus_remove_node_enumerator(
1817 sd_bus_node_enumerator_t callback,
1820 struct node_enumerator *c;
1823 assert_return(bus, -EINVAL);
1824 assert_return(object_path_is_valid(path), -EINVAL);
1825 assert_return(callback, -EINVAL);
1826 assert_return(!bus_pid_changed(bus), -ECHILD);
1828 n = hashmap_get(bus->nodes, path);
1832 LIST_FOREACH(enumerators, c, n->enumerators)
1833 if (c->callback == callback && c->userdata == userdata)
1839 LIST_REMOVE(enumerators, n->enumerators, c);
1842 bus_node_gc(bus, n);
1844 bus->nodes_modified = true;
1849 static int emit_properties_changed_on_interface(
1853 const char *interface,
1854 bool require_fallback,
1857 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1858 bool has_invalidating = false, has_changing = false;
1859 struct vtable_member key = {};
1860 struct node_vtable *c;
1871 n = hashmap_get(bus->nodes, prefix);
1875 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1879 r = sd_bus_message_append(m, "s", interface);
1883 r = sd_bus_message_open_container(m, 'a', "{sv}");
1888 key.interface = interface;
1890 LIST_FOREACH(vtables, c, n->vtables) {
1891 if (require_fallback && !c->is_fallback)
1894 if (!streq(c->interface, interface))
1897 r = node_vtable_get_userdata(bus, path, c, &u);
1900 if (bus->nodes_modified)
1905 STRV_FOREACH(property, names) {
1906 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1907 struct vtable_member *v;
1909 assert_return(member_name_is_valid(*property), -EINVAL);
1911 key.member = *property;
1912 v = hashmap_get(bus->vtable_properties, &key);
1916 /* If there are two vtables for the same
1917 * interface, let's handle this property when
1918 * we come to that vtable. */
1922 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1924 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1925 has_invalidating = true;
1929 has_changing = true;
1931 r = sd_bus_message_open_container(m, 'e', "sv");
1935 r = sd_bus_message_append(m, "s", *property);
1939 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1943 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error);
1946 if (sd_bus_error_is_set(&error))
1947 return sd_bus_error_get_errno(&error);
1948 if (bus->nodes_modified)
1951 r = sd_bus_message_close_container(m);
1955 r = sd_bus_message_close_container(m);
1961 if (!has_invalidating && !has_changing)
1964 r = sd_bus_message_close_container(m);
1968 r = sd_bus_message_open_container(m, 'a', "s");
1972 if (has_invalidating) {
1973 LIST_FOREACH(vtables, c, n->vtables) {
1974 if (require_fallback && !c->is_fallback)
1977 if (!streq(c->interface, interface))
1980 r = node_vtable_get_userdata(bus, path, c, &u);
1983 if (bus->nodes_modified)
1988 STRV_FOREACH(property, names) {
1989 struct vtable_member *v;
1991 key.member = *property;
1992 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1993 assert(c == v->parent);
1995 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1998 r = sd_bus_message_append(m, "s", *property);
2005 r = sd_bus_message_close_container(m);
2009 r = sd_bus_send(bus, m, NULL);
2016 _public_ int sd_bus_emit_properties_changed_strv(
2019 const char *interface,
2022 BUS_DONT_DESTROY(bus);
2026 assert_return(bus, -EINVAL);
2027 assert_return(object_path_is_valid(path), -EINVAL);
2028 assert_return(interface_name_is_valid(interface), -EINVAL);
2029 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2030 assert_return(!bus_pid_changed(bus), -ECHILD);
2032 if (strv_isempty(names))
2036 bus->nodes_modified = false;
2038 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2041 if (bus->nodes_modified)
2044 prefix = alloca(strlen(path) + 1);
2045 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2046 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2049 if (bus->nodes_modified)
2053 } while (bus->nodes_modified);
2058 _public_ int sd_bus_emit_properties_changed(
2061 const char *interface,
2062 const char *name, ...) {
2066 assert_return(bus, -EINVAL);
2067 assert_return(object_path_is_valid(path), -EINVAL);
2068 assert_return(interface_name_is_valid(interface), -EINVAL);
2069 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2070 assert_return(!bus_pid_changed(bus), -ECHILD);
2075 names = strv_from_stdarg_alloca(name);
2077 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2080 static int interfaces_added_append_one_prefix(
2085 const char *interface,
2086 bool require_fallback) {
2088 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2089 bool found_interface = false;
2090 struct node_vtable *c;
2101 n = hashmap_get(bus->nodes, prefix);
2105 LIST_FOREACH(vtables, c, n->vtables) {
2106 if (require_fallback && !c->is_fallback)
2109 if (!streq(c->interface, interface))
2112 r = node_vtable_get_userdata(bus, path, c, &u);
2115 if (bus->nodes_modified)
2120 if (!found_interface) {
2121 r = sd_bus_message_append_basic(m, 's', interface);
2125 r = sd_bus_message_open_container(m, 'a', "{sv}");
2129 found_interface = true;
2132 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2135 if (bus->nodes_modified)
2139 if (found_interface) {
2140 r = sd_bus_message_close_container(m);
2145 return found_interface;
2148 static int interfaces_added_append_one(
2152 const char *interface) {
2162 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2165 if (bus->nodes_modified)
2168 prefix = alloca(strlen(path) + 1);
2169 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2170 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2173 if (bus->nodes_modified)
2180 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2181 BUS_DONT_DESTROY(bus);
2183 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2187 assert_return(bus, -EINVAL);
2188 assert_return(object_path_is_valid(path), -EINVAL);
2189 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2190 assert_return(!bus_pid_changed(bus), -ECHILD);
2192 if (strv_isempty(interfaces))
2196 bus->nodes_modified = false;
2199 m = sd_bus_message_unref(m);
2201 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2205 r = sd_bus_message_append_basic(m, 'o', path);
2209 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2213 STRV_FOREACH(i, interfaces) {
2214 assert_return(interface_name_is_valid(*i), -EINVAL);
2216 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2220 r = interfaces_added_append_one(bus, m, path, *i);
2224 if (bus->nodes_modified)
2227 r = sd_bus_message_close_container(m);
2232 if (bus->nodes_modified)
2235 r = sd_bus_message_close_container(m);
2239 } while (bus->nodes_modified);
2241 return sd_bus_send(bus, m, NULL);
2244 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2247 assert_return(bus, -EINVAL);
2248 assert_return(object_path_is_valid(path), -EINVAL);
2249 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2250 assert_return(!bus_pid_changed(bus), -ECHILD);
2252 interfaces = strv_from_stdarg_alloca(interface);
2254 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2257 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2258 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2261 assert_return(bus, -EINVAL);
2262 assert_return(object_path_is_valid(path), -EINVAL);
2263 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2264 assert_return(!bus_pid_changed(bus), -ECHILD);
2266 if (strv_isempty(interfaces))
2269 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2273 r = sd_bus_message_append_basic(m, 'o', path);
2277 r = sd_bus_message_append_strv(m, interfaces);
2281 return sd_bus_send(bus, m, NULL);
2284 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2287 assert_return(bus, -EINVAL);
2288 assert_return(object_path_is_valid(path), -EINVAL);
2289 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2290 assert_return(!bus_pid_changed(bus), -ECHILD);
2292 interfaces = strv_from_stdarg_alloca(interface);
2294 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2297 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2300 assert_return(bus, -EINVAL);
2301 assert_return(object_path_is_valid(path), -EINVAL);
2302 assert_return(!bus_pid_changed(bus), -ECHILD);
2304 n = bus_node_allocate(bus, path);
2308 n->object_manager = true;
2309 bus->nodes_modified = true;
2313 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2316 assert_return(bus, -EINVAL);
2317 assert_return(object_path_is_valid(path), -EINVAL);
2318 assert_return(!bus_pid_changed(bus), -ECHILD);
2320 n = hashmap_get(bus->nodes, path);
2324 if (!n->object_manager)
2327 n->object_manager = false;
2328 bus->nodes_modified = true;
2329 bus_node_gc(bus, n);