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) && object_path_startswith(*k, prefix)) {
123 r = set_consume(s, *k);
134 static int add_subtree_to_set(
148 r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
151 if (bus->nodes_modified)
154 LIST_FOREACH(siblings, i, n->child) {
161 r = set_consume(s, t);
162 if (r < 0 && r != -EEXIST)
165 r = add_subtree_to_set(bus, prefix, i, s);
168 if (bus->nodes_modified)
175 static int get_child_nodes(
189 s = set_new(string_hash_func, string_compare_func);
193 r = add_subtree_to_set(bus, prefix, n, s);
203 static int node_callbacks_run(
206 struct node_callback *first,
207 bool require_fallback,
208 bool *found_object) {
210 struct node_callback *c;
215 assert(found_object);
217 LIST_FOREACH(callbacks, c, first) {
218 if (bus->nodes_modified)
221 if (require_fallback && !c->is_fallback)
224 *found_object = true;
226 if (c->last_iteration == bus->iteration_counter)
229 c->last_iteration = bus->iteration_counter;
231 r = sd_bus_message_rewind(m, true);
235 r = c->callback(bus, m, c->userdata);
243 static int method_callbacks_run(
246 struct vtable_member *c,
247 bool require_fallback,
248 bool *found_object) {
250 const char *signature;
257 assert(found_object);
259 if (require_fallback && !c->parent->is_fallback)
262 r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
265 if (bus->nodes_modified)
268 *found_object = true;
270 if (c->last_iteration == bus->iteration_counter)
273 c->last_iteration = bus->iteration_counter;
275 r = sd_bus_message_rewind(m, true);
279 signature = sd_bus_message_get_signature(m, true);
283 if (!streq(strempty(c->vtable->x.method.signature), signature)) {
284 r = sd_bus_reply_method_errorf(bus, m,
285 SD_BUS_ERROR_INVALID_ARGS,
286 "Invalid arguments '%s' to call %s:%s, expecting '%s'.",
287 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
294 if (c->vtable->x.method.handler)
295 return c->vtable->x.method.handler(bus, m, u);
297 /* If the method callback is NULL, make this a successful NOP */
298 r = sd_bus_reply_method_return(bus, m, NULL);
305 static int invoke_property_get(
307 const sd_bus_vtable *v,
309 const char *interface,
310 const char *property,
325 if (v->x.property.get)
326 return v->x.property.get(bus, path, interface, property, m, error, userdata);
328 /* Automatic handling if no callback is defined. */
330 assert(signature_is_single(v->x.property.signature, false));
331 assert(bus_type_is_basic(v->x.property.signature[0]));
333 switch (v->x.property.signature[0]) {
335 case SD_BUS_TYPE_STRING:
336 case SD_BUS_TYPE_SIGNATURE:
337 p = strempty(*(char**) userdata);
340 case SD_BUS_TYPE_OBJECT_PATH:
341 p = *(char**) userdata;
350 r = sd_bus_message_append_basic(m, v->x.property.signature[0], p);
357 static int invoke_property_set(
359 const sd_bus_vtable *v,
361 const char *interface,
362 const char *property,
363 sd_bus_message *value,
376 if (v->x.property.set)
377 return v->x.property.set(bus, path, interface, property, value, error, userdata);
379 /* Automatic handling if no callback is defined. */
381 assert(signature_is_single(v->x.property.signature, false));
382 assert(bus_type_is_basic(v->x.property.signature[0]));
384 switch (v->x.property.signature[0]) {
386 case SD_BUS_TYPE_STRING:
387 case SD_BUS_TYPE_OBJECT_PATH:
388 case SD_BUS_TYPE_SIGNATURE: {
392 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
400 free(*(char**) userdata);
401 *(char**) userdata = n;
407 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
417 static int property_get_set_callbacks_run(
420 struct vtable_member *c,
421 bool require_fallback,
423 bool *found_object) {
425 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
426 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
433 assert(found_object);
435 if (require_fallback && !c->parent->is_fallback)
438 r = vtable_property_get_userdata(bus, m->path, c, &u);
441 if (bus->nodes_modified)
444 *found_object = true;
446 r = sd_bus_message_new_method_return(bus, m, &reply);
451 /* Note that we do not protect against reexecution
452 * here (using the last_iteration check, see below),
453 * should the node tree have changed and we got called
454 * again. We assume that property Get() calls are
455 * ultimately without side-effects or if they aren't
456 * then at least idempotent. */
458 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
462 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
466 if (sd_bus_error_is_set(&error)) {
467 r = sd_bus_reply_method_error(bus, m, &error);
474 if (bus->nodes_modified)
477 r = sd_bus_message_close_container(reply);
482 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
483 sd_bus_error_setf(&error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
485 /* Avoid that we call the set routine more
486 * than once if the processing of this message
487 * got restarted because the node tree
489 if (c->last_iteration == bus->iteration_counter)
492 c->last_iteration = bus->iteration_counter;
494 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
498 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
503 if (sd_bus_error_is_set(&error)) {
504 r = sd_bus_reply_method_error(bus, 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, error, vtable_property_convert_userdata(v, userdata));
561 if (sd_bus_error_is_set(error))
563 if (bus->nodes_modified)
566 r = sd_bus_message_close_container(reply);
570 r = sd_bus_message_close_container(reply);
578 static int property_get_all_callbacks_run(
581 struct node_vtable *first,
582 bool require_fallback,
584 bool *found_object) {
586 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
587 struct node_vtable *c;
588 bool found_interface = false;
593 assert(found_object);
595 r = sd_bus_message_new_method_return(bus, m, &reply);
599 r = sd_bus_message_open_container(reply, 'a', "{sv}");
603 LIST_FOREACH(vtables, c, first) {
604 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
607 if (require_fallback && !c->is_fallback)
610 r = node_vtable_get_userdata(bus, m->path, c, &u);
613 if (bus->nodes_modified)
618 *found_object = true;
620 if (iface && !streq(c->interface, iface))
622 found_interface = true;
624 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
628 if (sd_bus_error_is_set(&error)) {
629 r = sd_bus_reply_method_error(bus, m, &error);
635 if (bus->nodes_modified)
639 if (!found_interface) {
640 r = sd_bus_reply_method_errorf(
642 SD_BUS_ERROR_UNKNOWN_INTERFACE,
643 "Unknown interface '%s'.", iface);
650 r = sd_bus_message_close_container(reply);
654 r = sd_bus_send(bus, reply, NULL);
661 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
665 if (n->object_manager)
669 return bus_node_with_object_manager(bus, n->parent);
674 static bool bus_node_exists(
678 bool require_fallback) {
680 struct node_vtable *c;
681 struct node_callback *k;
687 /* Tests if there's anything attached directly to this node
688 * for the specified path */
690 LIST_FOREACH(callbacks, k, n->callbacks) {
691 if (require_fallback && !k->is_fallback)
697 LIST_FOREACH(vtables, c, n->vtables) {
699 if (require_fallback && !c->is_fallback)
702 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
704 if (bus->nodes_modified)
708 return !require_fallback && (n->enumerators || n->object_manager);
711 static int process_introspect(
715 bool require_fallback,
716 bool *found_object) {
718 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
719 _cleanup_set_free_free_ Set *s = NULL;
720 struct introspect intro;
721 struct node_vtable *c;
728 assert(found_object);
730 r = get_child_nodes(bus, m->path, n, &s);
733 if (bus->nodes_modified)
736 r = introspect_begin(&intro);
740 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
744 empty = set_isempty(s);
746 LIST_FOREACH(vtables, c, n->vtables) {
747 if (require_fallback && !c->is_fallback)
750 r = node_vtable_get_userdata(bus, m->path, c, NULL);
753 if (bus->nodes_modified)
760 r = introspect_write_interface(&intro, c->interface, c->vtable);
766 /* Nothing?, let's see if we exist at all, and if not
767 * refuse to do anything */
768 r = bus_node_exists(bus, n, m->path, require_fallback);
771 if (bus->nodes_modified)
777 *found_object = true;
779 r = introspect_write_child_nodes(&intro, s, m->path);
783 r = introspect_finish(&intro, bus, m, &reply);
787 r = sd_bus_send(bus, reply, NULL);
794 introspect_free(&intro);
798 static int object_manager_serialize_vtable(
800 sd_bus_message *reply,
802 struct node_vtable *c,
814 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
818 r = sd_bus_message_append(reply, "s", c->interface);
822 r = sd_bus_message_open_container(reply, 'a', "{sv}");
826 r = vtable_append_all_properties(bus, reply, path, c, userdata, error);
829 if (bus->nodes_modified)
832 r = sd_bus_message_close_container(reply);
836 r = sd_bus_message_close_container(reply);
843 static int object_manager_serialize_path(
845 sd_bus_message *reply,
848 bool require_fallback,
849 sd_bus_error *error) {
851 struct node_vtable *i;
853 bool found_something = false;
862 n = hashmap_get(bus->nodes, prefix);
866 LIST_FOREACH(vtables, i, n->vtables) {
869 if (require_fallback && !i->is_fallback)
872 r = node_vtable_get_userdata(bus, path, i, &u);
875 if (bus->nodes_modified)
880 if (!found_something) {
881 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
885 r = sd_bus_message_append(reply, "o", path);
889 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
893 found_something = true;
896 r = object_manager_serialize_vtable(bus, reply, path, i, error, u);
899 if (sd_bus_error_is_set(error))
901 if (bus->nodes_modified)
905 if (found_something) {
906 r = sd_bus_message_close_container(reply);
910 r = sd_bus_message_close_container(reply);
918 static int object_manager_serialize_path_and_fallbacks(
920 sd_bus_message *reply,
922 sd_bus_error *error) {
932 /* First, add all vtables registered for this path */
933 r = object_manager_serialize_path(bus, reply, path, path, false, error);
936 if (sd_bus_error_is_set(error))
938 if (bus->nodes_modified)
941 /* Second, add fallback vtables registered for any of the prefixes */
942 prefix = alloca(strlen(path) + 1);
943 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
944 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
947 if (sd_bus_error_is_set(error))
949 if (bus->nodes_modified)
956 static int process_get_managed_objects(
960 bool require_fallback,
961 bool *found_object) {
963 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
964 _cleanup_set_free_free_ Set *s = NULL;
971 assert(found_object);
973 if (!bus_node_with_object_manager(bus, n))
976 r = get_child_nodes(bus, m->path, n, &s);
979 if (bus->nodes_modified)
982 r = sd_bus_message_new_method_return(bus, m, &reply);
986 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
990 empty = set_isempty(s);
992 struct node_vtable *c;
994 /* Hmm, so we have no children? Then let's check
995 * whether we exist at all, i.e. whether at least one
998 LIST_FOREACH(vtables, c, n->vtables) {
1000 if (require_fallback && !c->is_fallback)
1018 SET_FOREACH(path, s, i) {
1019 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1021 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1025 if (sd_bus_error_is_set(&error)) {
1026 r = sd_bus_reply_method_error(bus, m, &error);
1033 if (bus->nodes_modified)
1038 r = sd_bus_message_close_container(reply);
1042 r = sd_bus_send(bus, reply, NULL);
1049 static int object_find_and_run(
1053 bool require_fallback,
1054 bool *found_object) {
1057 struct vtable_member vtable_key, *v;
1063 assert(found_object);
1065 n = hashmap_get(bus->nodes, p);
1069 /* First, try object callbacks */
1070 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1073 if (bus->nodes_modified)
1076 if (!m->interface || !m->member)
1079 /* Then, look for a known method */
1080 vtable_key.path = (char*) p;
1081 vtable_key.interface = m->interface;
1082 vtable_key.member = m->member;
1084 v = hashmap_get(bus->vtable_methods, &vtable_key);
1086 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1089 if (bus->nodes_modified)
1093 /* Then, look for a known property */
1094 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1097 get = streq(m->member, "Get");
1099 if (get || streq(m->member, "Set")) {
1101 r = sd_bus_message_rewind(m, true);
1105 vtable_key.path = (char*) p;
1107 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1111 v = hashmap_get(bus->vtable_properties, &vtable_key);
1113 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1118 } else if (streq(m->member, "GetAll")) {
1121 r = sd_bus_message_rewind(m, true);
1125 r = sd_bus_message_read(m, "s", &iface);
1132 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1137 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1139 r = process_introspect(bus, m, n, require_fallback, found_object);
1143 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1145 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1150 if (bus->nodes_modified)
1153 if (!*found_object) {
1154 r = bus_node_exists(bus, n, m->path, require_fallback);
1158 *found_object = true;
1164 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1167 bool found_object = false;
1172 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1178 if (hashmap_isempty(bus->nodes))
1181 pl = strlen(m->path);
1185 bus->nodes_modified = false;
1187 r = object_find_and_run(bus, m, m->path, false, &found_object);
1191 /* Look for fallback prefixes */
1192 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1194 if (bus->nodes_modified)
1197 r = object_find_and_run(bus, m, prefix, true, &found_object);
1202 } while (bus->nodes_modified);
1207 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1208 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1209 r = sd_bus_reply_method_errorf(
1211 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1212 "Unknown property or interface.");
1214 r = sd_bus_reply_method_errorf(
1216 SD_BUS_ERROR_UNKNOWN_METHOD,
1217 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1225 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1226 struct node *n, *parent;
1233 assert(path[0] == '/');
1235 n = hashmap_get(bus->nodes, path);
1239 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1247 if (streq(path, "/"))
1250 e = strrchr(path, '/');
1253 p = strndupa(path, MAX(1, path - e));
1255 parent = bus_node_allocate(bus, p);
1262 n = new0(struct node, 1);
1269 r = hashmap_put(bus->nodes, s, n);
1277 LIST_PREPEND(siblings, parent->child, n);
1282 static void bus_node_gc(sd_bus *b, struct node *n) {
1295 assert(hashmap_remove(b->nodes, n->path) == n);
1298 LIST_REMOVE(siblings, n->parent->child, n);
1301 bus_node_gc(b, n->parent);
1305 static int bus_add_object(
1309 sd_bus_message_handler_t callback,
1312 struct node_callback *c;
1316 assert_return(bus, -EINVAL);
1317 assert_return(object_path_is_valid(path), -EINVAL);
1318 assert_return(callback, -EINVAL);
1319 assert_return(!bus_pid_changed(bus), -ECHILD);
1321 n = bus_node_allocate(bus, path);
1325 c = new0(struct node_callback, 1);
1332 c->callback = callback;
1333 c->userdata = userdata;
1334 c->is_fallback = fallback;
1336 LIST_PREPEND(callbacks, n->callbacks, c);
1337 bus->nodes_modified = true;
1343 bus_node_gc(bus, n);
1347 static int bus_remove_object(
1351 sd_bus_message_handler_t callback,
1354 struct node_callback *c;
1357 assert_return(bus, -EINVAL);
1358 assert_return(object_path_is_valid(path), -EINVAL);
1359 assert_return(callback, -EINVAL);
1360 assert_return(!bus_pid_changed(bus), -ECHILD);
1362 n = hashmap_get(bus->nodes, path);
1366 LIST_FOREACH(callbacks, c, n->callbacks)
1367 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1372 LIST_REMOVE(callbacks, n->callbacks, c);
1375 bus_node_gc(bus, n);
1376 bus->nodes_modified = true;
1381 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1382 return bus_add_object(bus, false, path, callback, userdata);
1385 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1386 return bus_remove_object(bus, false, path, callback, userdata);
1389 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1390 return bus_add_object(bus, true, prefix, callback, userdata);
1393 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1394 return bus_remove_object(bus, true, prefix, callback, userdata);
1397 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1403 if (w->interface && w->node && w->vtable) {
1404 const sd_bus_vtable *v;
1406 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1407 struct vtable_member *x = NULL;
1411 case _SD_BUS_VTABLE_METHOD: {
1412 struct vtable_member key;
1414 key.path = w->node->path;
1415 key.interface = w->interface;
1416 key.member = v->x.method.member;
1418 x = hashmap_remove(bus->vtable_methods, &key);
1422 case _SD_BUS_VTABLE_PROPERTY:
1423 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1424 struct vtable_member key;
1426 key.path = w->node->path;
1427 key.interface = w->interface;
1428 key.member = v->x.property.member;
1429 x = hashmap_remove(bus->vtable_properties, &key);
1441 static unsigned vtable_member_hash_func(const void *a) {
1442 const struct vtable_member *m = a;
1447 string_hash_func(m->path) ^
1448 string_hash_func(m->interface) ^
1449 string_hash_func(m->member);
1452 static int vtable_member_compare_func(const void *a, const void *b) {
1453 const struct vtable_member *x = a, *y = b;
1459 r = strcmp(x->path, y->path);
1463 r = strcmp(x->interface, y->interface);
1467 return strcmp(x->member, y->member);
1470 static int add_object_vtable_internal(
1473 const char *interface,
1474 const sd_bus_vtable *vtable,
1476 sd_bus_object_find_t find,
1479 struct node_vtable *c = NULL, *i;
1480 const sd_bus_vtable *v;
1484 assert_return(bus, -EINVAL);
1485 assert_return(object_path_is_valid(path), -EINVAL);
1486 assert_return(interface_name_is_valid(interface), -EINVAL);
1487 assert_return(vtable, -EINVAL);
1488 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1489 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1490 assert_return(!bus_pid_changed(bus), -ECHILD);
1492 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1496 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1500 n = bus_node_allocate(bus, path);
1504 LIST_FOREACH(vtables, i, n->vtables) {
1505 if (streq(i->interface, interface)) {
1510 if (i->is_fallback != fallback) {
1516 c = new0(struct node_vtable, 1);
1523 c->is_fallback = fallback;
1525 c->userdata = userdata;
1528 c->interface = strdup(interface);
1529 if (!c->interface) {
1534 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1538 case _SD_BUS_VTABLE_METHOD: {
1539 struct vtable_member *m;
1541 if (!member_name_is_valid(v->x.method.member) ||
1542 !signature_is_valid(strempty(v->x.method.signature), false) ||
1543 !signature_is_valid(strempty(v->x.method.result), false) ||
1544 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1545 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1550 m = new0(struct vtable_member, 1);
1558 m->interface = c->interface;
1559 m->member = v->x.method.member;
1562 r = hashmap_put(bus->vtable_methods, m, m);
1571 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1573 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1580 case _SD_BUS_VTABLE_PROPERTY: {
1581 struct vtable_member *m;
1583 if (!member_name_is_valid(v->x.property.member) ||
1584 !signature_is_single(v->x.property.signature, false) ||
1585 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
1586 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1587 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1593 m = new0(struct vtable_member, 1);
1601 m->interface = c->interface;
1602 m->member = v->x.property.member;
1605 r = hashmap_put(bus->vtable_properties, m, m);
1614 case _SD_BUS_VTABLE_SIGNAL:
1616 if (!member_name_is_valid(v->x.signal.member) ||
1617 !signature_is_single(strempty(v->x.signal.signature), false)) {
1630 LIST_PREPEND(vtables, n->vtables, c);
1631 bus->nodes_modified = true;
1637 free_node_vtable(bus, c);
1639 bus_node_gc(bus, n);
1643 static int remove_object_vtable_internal(
1646 const char *interface,
1649 struct node_vtable *c;
1652 assert_return(bus, -EINVAL);
1653 assert_return(object_path_is_valid(path), -EINVAL);
1654 assert_return(interface_name_is_valid(interface), -EINVAL);
1655 assert_return(!bus_pid_changed(bus), -ECHILD);
1657 n = hashmap_get(bus->nodes, path);
1661 LIST_FOREACH(vtables, c, n->vtables)
1662 if (streq(c->interface, interface) && c->is_fallback == fallback)
1668 LIST_REMOVE(vtables, n->vtables, c);
1670 free_node_vtable(bus, c);
1671 bus_node_gc(bus, n);
1673 bus->nodes_modified = true;
1678 int sd_bus_add_object_vtable(
1681 const char *interface,
1682 const sd_bus_vtable *vtable,
1685 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1688 int sd_bus_remove_object_vtable(
1691 const char *interface) {
1693 return remove_object_vtable_internal(bus, path, interface, false);
1696 int sd_bus_add_fallback_vtable(
1699 const char *interface,
1700 const sd_bus_vtable *vtable,
1701 sd_bus_object_find_t find,
1704 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1707 int sd_bus_remove_fallback_vtable(
1710 const char *interface) {
1712 return remove_object_vtable_internal(bus, path, interface, true);
1715 int sd_bus_add_node_enumerator(
1718 sd_bus_node_enumerator_t callback,
1721 struct node_enumerator *c;
1725 assert_return(bus, -EINVAL);
1726 assert_return(object_path_is_valid(path), -EINVAL);
1727 assert_return(callback, -EINVAL);
1728 assert_return(!bus_pid_changed(bus), -ECHILD);
1730 n = bus_node_allocate(bus, path);
1734 c = new0(struct node_enumerator, 1);
1741 c->callback = callback;
1742 c->userdata = userdata;
1744 LIST_PREPEND(enumerators, n->enumerators, c);
1746 bus->nodes_modified = true;
1752 bus_node_gc(bus, n);
1756 int sd_bus_remove_node_enumerator(
1759 sd_bus_node_enumerator_t callback,
1762 struct node_enumerator *c;
1765 assert_return(bus, -EINVAL);
1766 assert_return(object_path_is_valid(path), -EINVAL);
1767 assert_return(callback, -EINVAL);
1768 assert_return(!bus_pid_changed(bus), -ECHILD);
1770 n = hashmap_get(bus->nodes, path);
1774 LIST_FOREACH(enumerators, c, n->enumerators)
1775 if (c->callback == callback && c->userdata == userdata)
1781 LIST_REMOVE(enumerators, n->enumerators, c);
1784 bus_node_gc(bus, n);
1786 bus->nodes_modified = true;
1791 static int emit_properties_changed_on_interface(
1795 const char *interface,
1796 bool require_fallback,
1799 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1800 bool has_invalidating = false;
1801 struct vtable_member key;
1802 struct node_vtable *c;
1813 n = hashmap_get(bus->nodes, prefix);
1817 LIST_FOREACH(vtables, c, n->vtables) {
1818 if (require_fallback && !c->is_fallback)
1821 if (streq(c->interface, interface))
1828 r = node_vtable_get_userdata(bus, path, c, &u);
1831 if (bus->nodes_modified)
1834 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1838 r = sd_bus_message_append(m, "s", interface);
1842 r = sd_bus_message_open_container(m, 'a', "{sv}");
1847 key.interface = interface;
1849 STRV_FOREACH(property, names) {
1850 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1851 struct vtable_member *v;
1853 assert_return(member_name_is_valid(*property), -EINVAL);
1855 key.member = *property;
1856 v = hashmap_get(bus->vtable_properties, &key);
1860 assert(c == v->parent);
1861 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1863 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1864 has_invalidating = true;
1868 r = sd_bus_message_open_container(m, 'e', "sv");
1872 r = sd_bus_message_append(m, "s", *property);
1876 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1880 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1883 if (bus->nodes_modified)
1886 r = sd_bus_message_close_container(m);
1890 r = sd_bus_message_close_container(m);
1895 r = sd_bus_message_close_container(m);
1899 r = sd_bus_message_open_container(m, 'a', "s");
1903 if (has_invalidating) {
1904 STRV_FOREACH(property, names) {
1905 struct vtable_member *v;
1907 key.member = *property;
1908 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1909 assert(c == v->parent);
1911 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1914 r = sd_bus_message_append(m, "s", *property);
1920 r = sd_bus_message_close_container(m);
1924 r = sd_bus_send(bus, m, NULL);
1931 int sd_bus_emit_properties_changed_strv(
1934 const char *interface,
1937 BUS_DONT_DESTROY(bus);
1941 assert_return(bus, -EINVAL);
1942 assert_return(object_path_is_valid(path), -EINVAL);
1943 assert_return(interface_name_is_valid(interface), -EINVAL);
1944 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1945 assert_return(!bus_pid_changed(bus), -ECHILD);
1947 if (strv_isempty(names))
1951 bus->nodes_modified = false;
1953 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1956 if (bus->nodes_modified)
1959 prefix = alloca(strlen(path) + 1);
1960 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1961 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1964 if (bus->nodes_modified)
1968 } while (bus->nodes_modified);
1973 int sd_bus_emit_properties_changed(
1976 const char *interface,
1977 const char *name, ...) {
1981 assert_return(bus, -EINVAL);
1982 assert_return(object_path_is_valid(path), -EINVAL);
1983 assert_return(interface_name_is_valid(interface), -EINVAL);
1984 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1985 assert_return(!bus_pid_changed(bus), -ECHILD);
1990 names = strv_from_stdarg_alloca(name);
1992 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
1995 static int interfaces_added_append_one_prefix(
2000 const char *interface,
2001 bool require_fallback) {
2003 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2004 struct node_vtable *c;
2015 n = hashmap_get(bus->nodes, prefix);
2019 LIST_FOREACH(vtables, c, n->vtables) {
2020 if (require_fallback && !c->is_fallback)
2023 if (streq(c->interface, interface))
2030 r = node_vtable_get_userdata(bus, path, c, &u);
2033 if (bus->nodes_modified)
2036 r = sd_bus_message_append_basic(m, 's', interface);
2040 r = sd_bus_message_open_container(m, 'a', "{sv}");
2044 r = vtable_append_all_properties(bus, m,path, c, u, &error);
2047 if (bus->nodes_modified)
2050 r = sd_bus_message_close_container(m);
2057 static int interfaces_added_append_one(
2061 const char *interface) {
2071 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2074 if (bus->nodes_modified)
2077 prefix = alloca(strlen(path) + 1);
2078 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2079 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2082 if (bus->nodes_modified)
2089 int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2090 BUS_DONT_DESTROY(bus);
2092 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2096 assert_return(bus, -EINVAL);
2097 assert_return(object_path_is_valid(path), -EINVAL);
2098 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2099 assert_return(!bus_pid_changed(bus), -ECHILD);
2101 if (strv_isempty(interfaces))
2105 bus->nodes_modified = false;
2108 m = sd_bus_message_unref(m);
2110 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2114 r = sd_bus_message_append_basic(m, 'o', path);
2118 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2122 STRV_FOREACH(i, interfaces) {
2123 assert_return(interface_name_is_valid(*i), -EINVAL);
2125 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2129 r = interfaces_added_append_one(bus, m, path, *i);
2133 if (bus->nodes_modified)
2136 r = sd_bus_message_close_container(m);
2141 if (bus->nodes_modified)
2144 r = sd_bus_message_close_container(m);
2148 } while (bus->nodes_modified);
2150 return sd_bus_send(bus, m, NULL);
2153 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2156 assert_return(bus, -EINVAL);
2157 assert_return(object_path_is_valid(path), -EINVAL);
2158 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2159 assert_return(!bus_pid_changed(bus), -ECHILD);
2161 interfaces = strv_from_stdarg_alloca(interface);
2163 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2166 int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2167 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2170 assert_return(bus, -EINVAL);
2171 assert_return(object_path_is_valid(path), -EINVAL);
2172 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2173 assert_return(!bus_pid_changed(bus), -ECHILD);
2175 if (strv_isempty(interfaces))
2178 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2182 r = sd_bus_message_append_basic(m, 'o', path);
2186 r = sd_bus_message_append_strv(m, interfaces);
2190 return sd_bus_send(bus, m, NULL);
2193 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2196 assert_return(bus, -EINVAL);
2197 assert_return(object_path_is_valid(path), -EINVAL);
2198 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2199 assert_return(!bus_pid_changed(bus), -ECHILD);
2201 interfaces = strv_from_stdarg_alloca(interface);
2203 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2206 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2209 assert_return(bus, -EINVAL);
2210 assert_return(object_path_is_valid(path), -EINVAL);
2211 assert_return(!bus_pid_changed(bus), -ECHILD);
2213 n = bus_node_allocate(bus, path);
2217 n->object_manager = true;
2218 bus->nodes_modified = true;
2222 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2225 assert_return(bus, -EINVAL);
2226 assert_return(object_path_is_valid(path), -EINVAL);
2227 assert_return(!bus_pid_changed(bus), -ECHILD);
2229 n = hashmap_get(bus->nodes, path);
2233 if (!n->object_manager)
2236 n->object_manager = false;
2237 bus->nodes_modified = true;
2238 bus_node_gc(bus, n);