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 p = *(char**) userdata;
341 case SD_BUS_TYPE_OBJECT_PATH:
342 case SD_BUS_TYPE_SIGNATURE:
343 p = *(char**) userdata;
351 r = sd_bus_message_append_basic(m, v->x.property.signature[0], p);
358 static int invoke_property_set(
360 const sd_bus_vtable *v,
362 const char *interface,
363 const char *property,
364 sd_bus_message *value,
377 if (v->x.property.set)
378 return v->x.property.set(bus, path, interface, property, value, error, userdata);
380 /* Automatic handling if no callback is defined. */
382 assert(signature_is_single(v->x.property.signature, false));
383 assert(bus_type_is_basic(v->x.property.signature[0]));
385 switch (v->x.property.signature[0]) {
387 case SD_BUS_TYPE_STRING:
388 case SD_BUS_TYPE_OBJECT_PATH:
389 case SD_BUS_TYPE_SIGNATURE: {
393 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
401 free(*(char**) userdata);
402 *(char**) userdata = n;
408 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
418 static int property_get_set_callbacks_run(
421 struct vtable_member *c,
422 bool require_fallback,
424 bool *found_object) {
426 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
427 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
434 assert(found_object);
436 if (require_fallback && !c->parent->is_fallback)
439 r = vtable_property_get_userdata(bus, m->path, c, &u);
442 if (bus->nodes_modified)
445 *found_object = true;
447 r = sd_bus_message_new_method_return(bus, m, &reply);
452 /* Note that we do not protect against reexecution
453 * here (using the last_iteration check, see below),
454 * should the node tree have changed and we got called
455 * again. We assume that property Get() calls are
456 * ultimately without side-effects or if they aren't
457 * then at least idempotent. */
459 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
463 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
467 if (sd_bus_error_is_set(&error)) {
468 r = sd_bus_reply_method_error(bus, m, &error);
475 if (bus->nodes_modified)
478 r = sd_bus_message_close_container(reply);
483 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
484 sd_bus_error_setf(&error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
486 /* Avoid that we call the set routine more
487 * than once if the processing of this message
488 * got restarted because the node tree
490 if (c->last_iteration == bus->iteration_counter)
493 c->last_iteration = bus->iteration_counter;
495 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
499 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
504 if (sd_bus_error_is_set(&error)) {
505 r = sd_bus_reply_method_error(bus, m, &error);
512 if (bus->nodes_modified)
515 r = sd_bus_message_exit_container(m);
520 r = sd_bus_send(bus, reply, NULL);
527 static int vtable_append_all_properties(
529 sd_bus_message *reply,
531 struct node_vtable *c,
533 sd_bus_error *error) {
535 const sd_bus_vtable *v;
543 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
544 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
547 r = sd_bus_message_open_container(reply, 'e', "sv");
551 r = sd_bus_message_append(reply, "s", v->x.property.member);
555 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
559 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, error, vtable_property_convert_userdata(v, userdata));
562 if (sd_bus_error_is_set(error))
564 if (bus->nodes_modified)
567 r = sd_bus_message_close_container(reply);
571 r = sd_bus_message_close_container(reply);
579 static int property_get_all_callbacks_run(
582 struct node_vtable *first,
583 bool require_fallback,
585 bool *found_object) {
587 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
588 struct node_vtable *c;
589 bool found_interface = false;
594 assert(found_object);
596 r = sd_bus_message_new_method_return(bus, m, &reply);
600 r = sd_bus_message_open_container(reply, 'a', "{sv}");
604 LIST_FOREACH(vtables, c, first) {
605 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
608 if (require_fallback && !c->is_fallback)
611 r = node_vtable_get_userdata(bus, m->path, c, &u);
614 if (bus->nodes_modified)
619 *found_object = true;
621 if (iface && !streq(c->interface, iface))
623 found_interface = true;
625 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
629 if (sd_bus_error_is_set(&error)) {
630 r = sd_bus_reply_method_error(bus, m, &error);
636 if (bus->nodes_modified)
640 if (!found_interface) {
641 r = sd_bus_reply_method_errorf(
643 SD_BUS_ERROR_UNKNOWN_INTERFACE,
644 "Unknown interface '%s'.", iface);
651 r = sd_bus_message_close_container(reply);
655 r = sd_bus_send(bus, reply, NULL);
662 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
666 if (n->object_manager)
670 return bus_node_with_object_manager(bus, n->parent);
675 static bool bus_node_exists(
679 bool require_fallback) {
681 struct node_vtable *c;
682 struct node_callback *k;
688 /* Tests if there's anything attached directly to this node
689 * for the specified path */
691 LIST_FOREACH(callbacks, k, n->callbacks) {
692 if (require_fallback && !k->is_fallback)
698 LIST_FOREACH(vtables, c, n->vtables) {
700 if (require_fallback && !c->is_fallback)
703 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
705 if (bus->nodes_modified)
709 return !require_fallback && (n->enumerators || n->object_manager);
712 static int process_introspect(
716 bool require_fallback,
717 bool *found_object) {
719 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
720 _cleanup_set_free_free_ Set *s = NULL;
721 struct introspect intro;
722 struct node_vtable *c;
729 assert(found_object);
731 r = get_child_nodes(bus, m->path, n, &s);
734 if (bus->nodes_modified)
737 r = introspect_begin(&intro);
741 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
745 empty = set_isempty(s);
747 LIST_FOREACH(vtables, c, n->vtables) {
748 if (require_fallback && !c->is_fallback)
751 r = node_vtable_get_userdata(bus, m->path, c, NULL);
754 if (bus->nodes_modified)
761 r = introspect_write_interface(&intro, c->interface, c->vtable);
767 /* Nothing?, let's see if we exist at all, and if not
768 * refuse to do anything */
769 r = bus_node_exists(bus, n, m->path, require_fallback);
772 if (bus->nodes_modified)
778 *found_object = true;
780 r = introspect_write_child_nodes(&intro, s, m->path);
784 r = introspect_finish(&intro, bus, m, &reply);
788 r = sd_bus_send(bus, reply, NULL);
795 introspect_free(&intro);
799 static int object_manager_serialize_vtable(
801 sd_bus_message *reply,
803 struct node_vtable *c,
815 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
819 r = sd_bus_message_append(reply, "s", c->interface);
823 r = sd_bus_message_open_container(reply, 'a', "{sv}");
827 r = vtable_append_all_properties(bus, reply, path, c, userdata, error);
830 if (bus->nodes_modified)
833 r = sd_bus_message_close_container(reply);
837 r = sd_bus_message_close_container(reply);
844 static int object_manager_serialize_path(
846 sd_bus_message *reply,
849 bool require_fallback,
850 sd_bus_error *error) {
852 struct node_vtable *i;
854 bool found_something = false;
863 n = hashmap_get(bus->nodes, prefix);
867 LIST_FOREACH(vtables, i, n->vtables) {
870 if (require_fallback && !i->is_fallback)
873 r = node_vtable_get_userdata(bus, path, i, &u);
876 if (bus->nodes_modified)
881 if (!found_something) {
882 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
886 r = sd_bus_message_append(reply, "o", path);
890 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
894 found_something = true;
897 r = object_manager_serialize_vtable(bus, reply, path, i, error, u);
900 if (sd_bus_error_is_set(error))
902 if (bus->nodes_modified)
906 if (found_something) {
907 r = sd_bus_message_close_container(reply);
911 r = sd_bus_message_close_container(reply);
919 static int object_manager_serialize_path_and_fallbacks(
921 sd_bus_message *reply,
923 sd_bus_error *error) {
933 /* First, add all vtables registered for this path */
934 r = object_manager_serialize_path(bus, reply, path, path, false, error);
937 if (sd_bus_error_is_set(error))
939 if (bus->nodes_modified)
942 /* Second, add fallback vtables registered for any of the prefixes */
943 prefix = alloca(strlen(path) + 1);
944 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
945 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
948 if (sd_bus_error_is_set(error))
950 if (bus->nodes_modified)
957 static int process_get_managed_objects(
961 bool require_fallback,
962 bool *found_object) {
964 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
965 _cleanup_set_free_free_ Set *s = NULL;
972 assert(found_object);
974 if (!bus_node_with_object_manager(bus, n))
977 r = get_child_nodes(bus, m->path, n, &s);
980 if (bus->nodes_modified)
983 r = sd_bus_message_new_method_return(bus, m, &reply);
987 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
991 empty = set_isempty(s);
993 struct node_vtable *c;
995 /* Hmm, so we have no children? Then let's check
996 * whether we exist at all, i.e. whether at least one
999 LIST_FOREACH(vtables, c, n->vtables) {
1001 if (require_fallback && !c->is_fallback)
1019 SET_FOREACH(path, s, i) {
1020 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1022 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1026 if (sd_bus_error_is_set(&error)) {
1027 r = sd_bus_reply_method_error(bus, m, &error);
1034 if (bus->nodes_modified)
1039 r = sd_bus_message_close_container(reply);
1043 r = sd_bus_send(bus, reply, NULL);
1050 static int object_find_and_run(
1054 bool require_fallback,
1055 bool *found_object) {
1058 struct vtable_member vtable_key, *v;
1064 assert(found_object);
1066 n = hashmap_get(bus->nodes, p);
1070 /* First, try object callbacks */
1071 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1074 if (bus->nodes_modified)
1077 if (!m->interface || !m->member)
1080 /* Then, look for a known method */
1081 vtable_key.path = (char*) p;
1082 vtable_key.interface = m->interface;
1083 vtable_key.member = m->member;
1085 v = hashmap_get(bus->vtable_methods, &vtable_key);
1087 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1090 if (bus->nodes_modified)
1094 /* Then, look for a known property */
1095 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1098 get = streq(m->member, "Get");
1100 if (get || streq(m->member, "Set")) {
1102 r = sd_bus_message_rewind(m, true);
1106 vtable_key.path = (char*) p;
1108 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1112 v = hashmap_get(bus->vtable_properties, &vtable_key);
1114 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1119 } else if (streq(m->member, "GetAll")) {
1122 r = sd_bus_message_rewind(m, true);
1126 r = sd_bus_message_read(m, "s", &iface);
1133 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1138 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1140 r = process_introspect(bus, m, n, require_fallback, found_object);
1144 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1146 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1151 if (bus->nodes_modified)
1154 if (!*found_object) {
1155 r = bus_node_exists(bus, n, m->path, require_fallback);
1159 *found_object = true;
1165 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1168 bool found_object = false;
1173 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1179 if (hashmap_isempty(bus->nodes))
1182 pl = strlen(m->path);
1186 bus->nodes_modified = false;
1188 r = object_find_and_run(bus, m, m->path, false, &found_object);
1192 /* Look for fallback prefixes */
1193 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1195 if (bus->nodes_modified)
1198 r = object_find_and_run(bus, m, prefix, true, &found_object);
1203 } while (bus->nodes_modified);
1208 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1209 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1210 r = sd_bus_reply_method_errorf(
1212 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1213 "Unknown property or interface.");
1215 r = sd_bus_reply_method_errorf(
1217 SD_BUS_ERROR_UNKNOWN_METHOD,
1218 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1226 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1227 struct node *n, *parent;
1234 assert(path[0] == '/');
1236 n = hashmap_get(bus->nodes, path);
1240 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1248 if (streq(path, "/"))
1251 e = strrchr(path, '/');
1254 p = strndupa(path, MAX(1, path - e));
1256 parent = bus_node_allocate(bus, p);
1263 n = new0(struct node, 1);
1270 r = hashmap_put(bus->nodes, s, n);
1278 LIST_PREPEND(siblings, parent->child, n);
1283 static void bus_node_gc(sd_bus *b, struct node *n) {
1296 assert(hashmap_remove(b->nodes, n->path) == n);
1299 LIST_REMOVE(siblings, n->parent->child, n);
1302 bus_node_gc(b, n->parent);
1306 static int bus_add_object(
1310 sd_bus_message_handler_t callback,
1313 struct node_callback *c;
1317 assert_return(bus, -EINVAL);
1318 assert_return(object_path_is_valid(path), -EINVAL);
1319 assert_return(callback, -EINVAL);
1320 assert_return(!bus_pid_changed(bus), -ECHILD);
1322 n = bus_node_allocate(bus, path);
1326 c = new0(struct node_callback, 1);
1333 c->callback = callback;
1334 c->userdata = userdata;
1335 c->is_fallback = fallback;
1337 LIST_PREPEND(callbacks, n->callbacks, c);
1338 bus->nodes_modified = true;
1344 bus_node_gc(bus, n);
1348 static int bus_remove_object(
1352 sd_bus_message_handler_t callback,
1355 struct node_callback *c;
1358 assert_return(bus, -EINVAL);
1359 assert_return(object_path_is_valid(path), -EINVAL);
1360 assert_return(callback, -EINVAL);
1361 assert_return(!bus_pid_changed(bus), -ECHILD);
1363 n = hashmap_get(bus->nodes, path);
1367 LIST_FOREACH(callbacks, c, n->callbacks)
1368 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1373 LIST_REMOVE(callbacks, n->callbacks, c);
1376 bus_node_gc(bus, n);
1377 bus->nodes_modified = true;
1382 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1383 return bus_add_object(bus, false, path, callback, userdata);
1386 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1387 return bus_remove_object(bus, false, path, callback, userdata);
1390 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1391 return bus_add_object(bus, true, prefix, callback, userdata);
1394 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1395 return bus_remove_object(bus, true, prefix, callback, userdata);
1398 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1404 if (w->interface && w->node && w->vtable) {
1405 const sd_bus_vtable *v;
1407 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1408 struct vtable_member *x = NULL;
1412 case _SD_BUS_VTABLE_METHOD: {
1413 struct vtable_member key;
1415 key.path = w->node->path;
1416 key.interface = w->interface;
1417 key.member = v->x.method.member;
1419 x = hashmap_remove(bus->vtable_methods, &key);
1423 case _SD_BUS_VTABLE_PROPERTY:
1424 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1425 struct vtable_member key;
1427 key.path = w->node->path;
1428 key.interface = w->interface;
1429 key.member = v->x.property.member;
1430 x = hashmap_remove(bus->vtable_properties, &key);
1442 static unsigned vtable_member_hash_func(const void *a) {
1443 const struct vtable_member *m = a;
1448 string_hash_func(m->path) ^
1449 string_hash_func(m->interface) ^
1450 string_hash_func(m->member);
1453 static int vtable_member_compare_func(const void *a, const void *b) {
1454 const struct vtable_member *x = a, *y = b;
1460 r = strcmp(x->path, y->path);
1464 r = strcmp(x->interface, y->interface);
1468 return strcmp(x->member, y->member);
1471 static int add_object_vtable_internal(
1474 const char *interface,
1475 const sd_bus_vtable *vtable,
1477 sd_bus_object_find_t find,
1480 struct node_vtable *c = NULL, *i;
1481 const sd_bus_vtable *v;
1485 assert_return(bus, -EINVAL);
1486 assert_return(object_path_is_valid(path), -EINVAL);
1487 assert_return(interface_name_is_valid(interface), -EINVAL);
1488 assert_return(vtable, -EINVAL);
1489 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1490 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1491 assert_return(!bus_pid_changed(bus), -ECHILD);
1493 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1497 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1501 n = bus_node_allocate(bus, path);
1505 LIST_FOREACH(vtables, i, n->vtables) {
1506 if (streq(i->interface, interface)) {
1511 if (i->is_fallback != fallback) {
1517 c = new0(struct node_vtable, 1);
1524 c->is_fallback = fallback;
1526 c->userdata = userdata;
1529 c->interface = strdup(interface);
1530 if (!c->interface) {
1535 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1539 case _SD_BUS_VTABLE_METHOD: {
1540 struct vtable_member *m;
1542 if (!member_name_is_valid(v->x.method.member) ||
1543 !signature_is_valid(strempty(v->x.method.signature), false) ||
1544 !signature_is_valid(strempty(v->x.method.result), false) ||
1545 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1546 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1551 m = new0(struct vtable_member, 1);
1559 m->interface = c->interface;
1560 m->member = v->x.method.member;
1563 r = hashmap_put(bus->vtable_methods, m, m);
1572 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1574 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1581 case _SD_BUS_VTABLE_PROPERTY: {
1582 struct vtable_member *m;
1584 if (!member_name_is_valid(v->x.property.member) ||
1585 !signature_is_single(v->x.property.signature, false) ||
1586 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
1587 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1588 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1594 m = new0(struct vtable_member, 1);
1602 m->interface = c->interface;
1603 m->member = v->x.property.member;
1606 r = hashmap_put(bus->vtable_properties, m, m);
1615 case _SD_BUS_VTABLE_SIGNAL:
1617 if (!member_name_is_valid(v->x.signal.member) ||
1618 !signature_is_single(strempty(v->x.signal.signature), false)) {
1631 LIST_PREPEND(vtables, n->vtables, c);
1632 bus->nodes_modified = true;
1638 free_node_vtable(bus, c);
1640 bus_node_gc(bus, n);
1644 static int remove_object_vtable_internal(
1647 const char *interface,
1650 struct node_vtable *c;
1653 assert_return(bus, -EINVAL);
1654 assert_return(object_path_is_valid(path), -EINVAL);
1655 assert_return(interface_name_is_valid(interface), -EINVAL);
1656 assert_return(!bus_pid_changed(bus), -ECHILD);
1658 n = hashmap_get(bus->nodes, path);
1662 LIST_FOREACH(vtables, c, n->vtables)
1663 if (streq(c->interface, interface) && c->is_fallback == fallback)
1669 LIST_REMOVE(vtables, n->vtables, c);
1671 free_node_vtable(bus, c);
1672 bus_node_gc(bus, n);
1674 bus->nodes_modified = true;
1679 int sd_bus_add_object_vtable(
1682 const char *interface,
1683 const sd_bus_vtable *vtable,
1686 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1689 int sd_bus_remove_object_vtable(
1692 const char *interface) {
1694 return remove_object_vtable_internal(bus, path, interface, false);
1697 int sd_bus_add_fallback_vtable(
1700 const char *interface,
1701 const sd_bus_vtable *vtable,
1702 sd_bus_object_find_t find,
1705 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1708 int sd_bus_remove_fallback_vtable(
1711 const char *interface) {
1713 return remove_object_vtable_internal(bus, path, interface, true);
1716 int sd_bus_add_node_enumerator(
1719 sd_bus_node_enumerator_t callback,
1722 struct node_enumerator *c;
1726 assert_return(bus, -EINVAL);
1727 assert_return(object_path_is_valid(path), -EINVAL);
1728 assert_return(callback, -EINVAL);
1729 assert_return(!bus_pid_changed(bus), -ECHILD);
1731 n = bus_node_allocate(bus, path);
1735 c = new0(struct node_enumerator, 1);
1742 c->callback = callback;
1743 c->userdata = userdata;
1745 LIST_PREPEND(enumerators, n->enumerators, c);
1747 bus->nodes_modified = true;
1753 bus_node_gc(bus, n);
1757 int sd_bus_remove_node_enumerator(
1760 sd_bus_node_enumerator_t callback,
1763 struct node_enumerator *c;
1766 assert_return(bus, -EINVAL);
1767 assert_return(object_path_is_valid(path), -EINVAL);
1768 assert_return(callback, -EINVAL);
1769 assert_return(!bus_pid_changed(bus), -ECHILD);
1771 n = hashmap_get(bus->nodes, path);
1775 LIST_FOREACH(enumerators, c, n->enumerators)
1776 if (c->callback == callback && c->userdata == userdata)
1782 LIST_REMOVE(enumerators, n->enumerators, c);
1785 bus_node_gc(bus, n);
1787 bus->nodes_modified = true;
1792 static int emit_properties_changed_on_interface(
1796 const char *interface,
1797 bool require_fallback,
1800 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1801 bool has_invalidating = false;
1802 struct vtable_member key;
1803 struct node_vtable *c;
1814 n = hashmap_get(bus->nodes, prefix);
1818 LIST_FOREACH(vtables, c, n->vtables) {
1819 if (require_fallback && !c->is_fallback)
1822 if (streq(c->interface, interface))
1829 r = node_vtable_get_userdata(bus, path, c, &u);
1832 if (bus->nodes_modified)
1835 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1839 r = sd_bus_message_append(m, "s", interface);
1843 r = sd_bus_message_open_container(m, 'a', "{sv}");
1848 key.interface = interface;
1850 STRV_FOREACH(property, names) {
1851 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1852 struct vtable_member *v;
1854 assert_return(member_name_is_valid(*property), -EINVAL);
1856 key.member = *property;
1857 v = hashmap_get(bus->vtable_properties, &key);
1861 assert(c == v->parent);
1862 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1864 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1865 has_invalidating = true;
1869 r = sd_bus_message_open_container(m, 'e', "sv");
1873 r = sd_bus_message_append(m, "s", *property);
1877 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1881 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1884 if (bus->nodes_modified)
1887 r = sd_bus_message_close_container(m);
1891 r = sd_bus_message_close_container(m);
1896 r = sd_bus_message_close_container(m);
1900 r = sd_bus_message_open_container(m, 'a', "s");
1904 if (has_invalidating) {
1905 STRV_FOREACH(property, names) {
1906 struct vtable_member *v;
1908 key.member = *property;
1909 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1910 assert(c == v->parent);
1912 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1915 r = sd_bus_message_append(m, "s", *property);
1921 r = sd_bus_message_close_container(m);
1925 r = sd_bus_send(bus, m, NULL);
1932 int sd_bus_emit_properties_changed_strv(
1935 const char *interface,
1938 BUS_DONT_DESTROY(bus);
1942 assert_return(bus, -EINVAL);
1943 assert_return(object_path_is_valid(path), -EINVAL);
1944 assert_return(interface_name_is_valid(interface), -EINVAL);
1945 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1946 assert_return(!bus_pid_changed(bus), -ECHILD);
1948 if (strv_isempty(names))
1952 bus->nodes_modified = false;
1954 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1957 if (bus->nodes_modified)
1960 prefix = alloca(strlen(path) + 1);
1961 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1962 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1965 if (bus->nodes_modified)
1969 } while (bus->nodes_modified);
1974 int sd_bus_emit_properties_changed(
1977 const char *interface,
1978 const char *name, ...) {
1980 _cleanup_strv_free_ char **names = NULL;
1983 assert_return(bus, -EINVAL);
1984 assert_return(object_path_is_valid(path), -EINVAL);
1985 assert_return(interface_name_is_valid(interface), -EINVAL);
1986 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1987 assert_return(!bus_pid_changed(bus), -ECHILD);
1993 names = strv_new_ap(name, ap);
1999 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2002 static int interfaces_added_append_one_prefix(
2007 const char *interface,
2008 bool require_fallback) {
2010 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2011 struct node_vtable *c;
2022 n = hashmap_get(bus->nodes, prefix);
2026 LIST_FOREACH(vtables, c, n->vtables) {
2027 if (require_fallback && !c->is_fallback)
2030 if (streq(c->interface, interface))
2037 r = node_vtable_get_userdata(bus, path, c, &u);
2040 if (bus->nodes_modified)
2043 r = sd_bus_message_append_basic(m, 's', interface);
2047 r = sd_bus_message_open_container(m, 'a', "{sv}");
2051 r = vtable_append_all_properties(bus, m,path, c, u, &error);
2054 if (bus->nodes_modified)
2057 r = sd_bus_message_close_container(m);
2064 static int interfaces_added_append_one(
2068 const char *interface) {
2078 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2081 if (bus->nodes_modified)
2084 prefix = alloca(strlen(path) + 1);
2085 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2086 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2089 if (bus->nodes_modified)
2096 int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2097 BUS_DONT_DESTROY(bus);
2099 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2103 assert_return(bus, -EINVAL);
2104 assert_return(object_path_is_valid(path), -EINVAL);
2105 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2106 assert_return(!bus_pid_changed(bus), -ECHILD);
2108 if (strv_isempty(interfaces))
2112 bus->nodes_modified = false;
2115 m = sd_bus_message_unref(m);
2117 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2121 r = sd_bus_message_append_basic(m, 'o', path);
2125 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2129 STRV_FOREACH(i, interfaces) {
2130 assert_return(interface_name_is_valid(*i), -EINVAL);
2132 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2136 r = interfaces_added_append_one(bus, m, path, *i);
2140 if (bus->nodes_modified)
2143 r = sd_bus_message_close_container(m);
2148 if (bus->nodes_modified)
2151 r = sd_bus_message_close_container(m);
2155 } while (bus->nodes_modified);
2157 return sd_bus_send(bus, m, NULL);
2160 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2161 _cleanup_strv_free_ char **interfaces = NULL;
2164 assert_return(bus, -EINVAL);
2165 assert_return(object_path_is_valid(path), -EINVAL);
2166 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2167 assert_return(!bus_pid_changed(bus), -ECHILD);
2169 va_start(ap, interface);
2170 interfaces = strv_new_ap(interface, ap);
2176 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2179 int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2180 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2183 assert_return(bus, -EINVAL);
2184 assert_return(object_path_is_valid(path), -EINVAL);
2185 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2186 assert_return(!bus_pid_changed(bus), -ECHILD);
2188 if (strv_isempty(interfaces))
2191 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2195 r = sd_bus_message_append_basic(m, 'o', path);
2199 r = sd_bus_message_append_strv(m, interfaces);
2203 return sd_bus_send(bus, m, NULL);
2206 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2207 _cleanup_strv_free_ char **interfaces = NULL;
2210 assert_return(bus, -EINVAL);
2211 assert_return(object_path_is_valid(path), -EINVAL);
2212 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2213 assert_return(!bus_pid_changed(bus), -ECHILD);
2215 va_start(ap, interface);
2216 interfaces = strv_new_ap(interface, ap);
2222 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2225 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2228 assert_return(bus, -EINVAL);
2229 assert_return(object_path_is_valid(path), -EINVAL);
2230 assert_return(!bus_pid_changed(bus), -ECHILD);
2232 n = bus_node_allocate(bus, path);
2236 n->object_manager = true;
2237 bus->nodes_modified = true;
2241 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2244 assert_return(bus, -EINVAL);
2245 assert_return(object_path_is_valid(path), -EINVAL);
2246 assert_return(!bus_pid_changed(bus), -ECHILD);
2248 n = hashmap_get(bus->nodes, path);
2252 if (!n->object_manager)
2255 n->object_manager = false;
2256 bus->nodes_modified = true;
2257 bus_node_gc(bus, n);