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_OBJECT_PATH:
337 case SD_BUS_TYPE_SIGNATURE:
338 p = *(char**) userdata;
346 r = sd_bus_message_append_basic(m, v->x.property.signature[0], p);
353 static int invoke_property_set(
355 const sd_bus_vtable *v,
357 const char *interface,
358 const char *property,
359 sd_bus_message *value,
372 if (v->x.property.set)
373 return v->x.property.set(bus, path, interface, property, value, error, userdata);
375 /* Automatic handling if no callback is defined. */
377 assert(signature_is_single(v->x.property.signature, false));
378 assert(bus_type_is_basic(v->x.property.signature[0]));
380 switch (v->x.property.signature[0]) {
382 case SD_BUS_TYPE_STRING:
383 case SD_BUS_TYPE_OBJECT_PATH:
384 case SD_BUS_TYPE_SIGNATURE: {
388 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
396 free(*(char**) userdata);
397 *(char**) userdata = n;
403 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
413 static int property_get_set_callbacks_run(
416 struct vtable_member *c,
417 bool require_fallback,
419 bool *found_object) {
421 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
422 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
429 assert(found_object);
431 if (require_fallback && !c->parent->is_fallback)
434 r = vtable_property_get_userdata(bus, m->path, c, &u);
437 if (bus->nodes_modified)
440 *found_object = true;
442 r = sd_bus_message_new_method_return(bus, m, &reply);
447 /* Note that we do not protect against reexecution
448 * here (using the last_iteration check, see below),
449 * should the node tree have changed and we got called
450 * again. We assume that property Get() calls are
451 * ultimately without side-effects or if they aren't
452 * then at least idempotent. */
454 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
458 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
462 if (sd_bus_error_is_set(&error)) {
463 r = sd_bus_reply_method_error(bus, m, &error);
470 if (bus->nodes_modified)
473 r = sd_bus_message_close_container(reply);
478 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
479 sd_bus_error_setf(&error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
481 /* Avoid that we call the set routine more
482 * than once if the processing of this message
483 * got restarted because the node tree
485 if (c->last_iteration == bus->iteration_counter)
488 c->last_iteration = bus->iteration_counter;
490 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
494 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
499 if (sd_bus_error_is_set(&error)) {
500 r = sd_bus_reply_method_error(bus, m, &error);
507 if (bus->nodes_modified)
510 r = sd_bus_message_exit_container(m);
515 r = sd_bus_send(bus, reply, NULL);
522 static int vtable_append_all_properties(
524 sd_bus_message *reply,
526 struct node_vtable *c,
528 sd_bus_error *error) {
530 const sd_bus_vtable *v;
538 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
539 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
542 r = sd_bus_message_open_container(reply, 'e', "sv");
546 r = sd_bus_message_append(reply, "s", v->x.property.member);
550 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
554 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, error, vtable_property_convert_userdata(v, userdata));
557 if (sd_bus_error_is_set(error))
559 if (bus->nodes_modified)
562 r = sd_bus_message_close_container(reply);
566 r = sd_bus_message_close_container(reply);
574 static int property_get_all_callbacks_run(
577 struct node_vtable *first,
578 bool require_fallback,
580 bool *found_object) {
582 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
583 struct node_vtable *c;
584 bool found_interface = false;
589 assert(found_object);
591 r = sd_bus_message_new_method_return(bus, m, &reply);
595 r = sd_bus_message_open_container(reply, 'a', "{sv}");
599 LIST_FOREACH(vtables, c, first) {
600 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
603 if (require_fallback && !c->is_fallback)
606 r = node_vtable_get_userdata(bus, m->path, c, &u);
609 if (bus->nodes_modified)
614 *found_object = true;
616 if (iface && !streq(c->interface, iface))
618 found_interface = true;
620 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
624 if (sd_bus_error_is_set(&error)) {
625 r = sd_bus_reply_method_error(bus, m, &error);
631 if (bus->nodes_modified)
635 if (!found_interface) {
636 r = sd_bus_reply_method_errorf(
638 SD_BUS_ERROR_UNKNOWN_INTERFACE,
639 "Unknown interface '%s'.", iface);
646 r = sd_bus_message_close_container(reply);
650 r = sd_bus_send(bus, reply, NULL);
657 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
661 if (n->object_manager)
665 return bus_node_with_object_manager(bus, n->parent);
670 static bool bus_node_exists(
674 bool require_fallback) {
676 struct node_vtable *c;
677 struct node_callback *k;
683 /* Tests if there's anything attached directly to this node
684 * for the specified path */
686 LIST_FOREACH(callbacks, k, n->callbacks) {
687 if (require_fallback && !k->is_fallback)
693 LIST_FOREACH(vtables, c, n->vtables) {
695 if (require_fallback && !c->is_fallback)
698 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
700 if (bus->nodes_modified)
704 return !require_fallback && (n->enumerators || n->object_manager);
707 static int process_introspect(
711 bool require_fallback,
712 bool *found_object) {
714 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
715 _cleanup_set_free_free_ Set *s = NULL;
716 struct introspect intro;
717 struct node_vtable *c;
724 assert(found_object);
726 r = get_child_nodes(bus, m->path, n, &s);
729 if (bus->nodes_modified)
732 r = introspect_begin(&intro);
736 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
740 empty = set_isempty(s);
742 LIST_FOREACH(vtables, c, n->vtables) {
743 if (require_fallback && !c->is_fallback)
746 r = node_vtable_get_userdata(bus, m->path, c, NULL);
749 if (bus->nodes_modified)
756 r = introspect_write_interface(&intro, c->interface, c->vtable);
762 /* Nothing?, let's see if we exist at all, and if not
763 * refuse to do anything */
764 r = bus_node_exists(bus, n, m->path, require_fallback);
767 if (bus->nodes_modified)
773 *found_object = true;
775 r = introspect_write_child_nodes(&intro, s, m->path);
779 r = introspect_finish(&intro, bus, m, &reply);
783 r = sd_bus_send(bus, reply, NULL);
790 introspect_free(&intro);
794 static int object_manager_serialize_vtable(
796 sd_bus_message *reply,
798 struct node_vtable *c,
810 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
814 r = sd_bus_message_append(reply, "s", c->interface);
818 r = sd_bus_message_open_container(reply, 'a', "{sv}");
822 r = vtable_append_all_properties(bus, reply, path, c, userdata, error);
825 if (bus->nodes_modified)
828 r = sd_bus_message_close_container(reply);
832 r = sd_bus_message_close_container(reply);
839 static int object_manager_serialize_path(
841 sd_bus_message *reply,
844 bool require_fallback,
845 sd_bus_error *error) {
847 struct node_vtable *i;
849 bool found_something = false;
858 n = hashmap_get(bus->nodes, prefix);
862 LIST_FOREACH(vtables, i, n->vtables) {
865 if (require_fallback && !i->is_fallback)
868 r = node_vtable_get_userdata(bus, path, i, &u);
871 if (bus->nodes_modified)
876 if (!found_something) {
877 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
881 r = sd_bus_message_append(reply, "o", path);
885 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
889 found_something = true;
892 r = object_manager_serialize_vtable(bus, reply, path, i, error, u);
895 if (sd_bus_error_is_set(error))
897 if (bus->nodes_modified)
901 if (found_something) {
902 r = sd_bus_message_close_container(reply);
906 r = sd_bus_message_close_container(reply);
914 static int object_manager_serialize_path_and_fallbacks(
916 sd_bus_message *reply,
918 sd_bus_error *error) {
928 /* First, add all vtables registered for this path */
929 r = object_manager_serialize_path(bus, reply, path, path, false, error);
932 if (sd_bus_error_is_set(error))
934 if (bus->nodes_modified)
937 /* Second, add fallback vtables registered for any of the prefixes */
938 prefix = alloca(strlen(path) + 1);
939 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
940 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
943 if (sd_bus_error_is_set(error))
945 if (bus->nodes_modified)
952 static int process_get_managed_objects(
956 bool require_fallback,
957 bool *found_object) {
959 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
960 _cleanup_set_free_free_ Set *s = NULL;
967 assert(found_object);
969 if (!bus_node_with_object_manager(bus, n))
972 r = get_child_nodes(bus, m->path, n, &s);
975 if (bus->nodes_modified)
978 r = sd_bus_message_new_method_return(bus, m, &reply);
982 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
986 empty = set_isempty(s);
988 struct node_vtable *c;
990 /* Hmm, so we have no children? Then let's check
991 * whether we exist at all, i.e. whether at least one
994 LIST_FOREACH(vtables, c, n->vtables) {
996 if (require_fallback && !c->is_fallback)
1014 SET_FOREACH(path, s, i) {
1015 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1017 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1021 if (sd_bus_error_is_set(&error)) {
1022 r = sd_bus_reply_method_error(bus, m, &error);
1029 if (bus->nodes_modified)
1034 r = sd_bus_message_close_container(reply);
1038 r = sd_bus_send(bus, reply, NULL);
1045 static int object_find_and_run(
1049 bool require_fallback,
1050 bool *found_object) {
1053 struct vtable_member vtable_key, *v;
1059 assert(found_object);
1061 n = hashmap_get(bus->nodes, p);
1065 /* First, try object callbacks */
1066 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1069 if (bus->nodes_modified)
1072 if (!m->interface || !m->member)
1075 /* Then, look for a known method */
1076 vtable_key.path = (char*) p;
1077 vtable_key.interface = m->interface;
1078 vtable_key.member = m->member;
1080 v = hashmap_get(bus->vtable_methods, &vtable_key);
1082 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1085 if (bus->nodes_modified)
1089 /* Then, look for a known property */
1090 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1093 get = streq(m->member, "Get");
1095 if (get || streq(m->member, "Set")) {
1097 r = sd_bus_message_rewind(m, true);
1101 vtable_key.path = (char*) p;
1103 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1107 v = hashmap_get(bus->vtable_properties, &vtable_key);
1109 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1114 } else if (streq(m->member, "GetAll")) {
1117 r = sd_bus_message_rewind(m, true);
1121 r = sd_bus_message_read(m, "s", &iface);
1128 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1133 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1135 r = process_introspect(bus, m, n, require_fallback, found_object);
1139 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1141 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1146 if (bus->nodes_modified)
1149 if (!*found_object) {
1150 r = bus_node_exists(bus, n, m->path, require_fallback);
1154 *found_object = true;
1160 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1163 bool found_object = false;
1168 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1174 if (hashmap_isempty(bus->nodes))
1177 pl = strlen(m->path);
1181 bus->nodes_modified = false;
1183 r = object_find_and_run(bus, m, m->path, false, &found_object);
1187 /* Look for fallback prefixes */
1188 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1190 if (bus->nodes_modified)
1193 r = object_find_and_run(bus, m, prefix, true, &found_object);
1198 } while (bus->nodes_modified);
1203 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1204 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1205 r = sd_bus_reply_method_errorf(
1207 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1208 "Unknown property or interface.");
1210 r = sd_bus_reply_method_errorf(
1212 SD_BUS_ERROR_UNKNOWN_METHOD,
1213 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1221 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1222 struct node *n, *parent;
1229 assert(path[0] == '/');
1231 n = hashmap_get(bus->nodes, path);
1235 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1243 if (streq(path, "/"))
1246 e = strrchr(path, '/');
1249 p = strndupa(path, MAX(1, path - e));
1251 parent = bus_node_allocate(bus, p);
1258 n = new0(struct node, 1);
1265 r = hashmap_put(bus->nodes, s, n);
1273 LIST_PREPEND(siblings, parent->child, n);
1278 static void bus_node_gc(sd_bus *b, struct node *n) {
1291 assert(hashmap_remove(b->nodes, n->path) == n);
1294 LIST_REMOVE(siblings, n->parent->child, n);
1297 bus_node_gc(b, n->parent);
1301 static int bus_add_object(
1305 sd_bus_message_handler_t callback,
1308 struct node_callback *c;
1312 assert_return(bus, -EINVAL);
1313 assert_return(object_path_is_valid(path), -EINVAL);
1314 assert_return(callback, -EINVAL);
1315 assert_return(!bus_pid_changed(bus), -ECHILD);
1317 n = bus_node_allocate(bus, path);
1321 c = new0(struct node_callback, 1);
1328 c->callback = callback;
1329 c->userdata = userdata;
1330 c->is_fallback = fallback;
1332 LIST_PREPEND(callbacks, n->callbacks, c);
1333 bus->nodes_modified = true;
1339 bus_node_gc(bus, n);
1343 static int bus_remove_object(
1347 sd_bus_message_handler_t callback,
1350 struct node_callback *c;
1353 assert_return(bus, -EINVAL);
1354 assert_return(object_path_is_valid(path), -EINVAL);
1355 assert_return(callback, -EINVAL);
1356 assert_return(!bus_pid_changed(bus), -ECHILD);
1358 n = hashmap_get(bus->nodes, path);
1362 LIST_FOREACH(callbacks, c, n->callbacks)
1363 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1368 LIST_REMOVE(callbacks, n->callbacks, c);
1371 bus_node_gc(bus, n);
1372 bus->nodes_modified = true;
1377 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1378 return bus_add_object(bus, false, path, callback, userdata);
1381 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1382 return bus_remove_object(bus, false, path, callback, userdata);
1385 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1386 return bus_add_object(bus, true, prefix, callback, userdata);
1389 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1390 return bus_remove_object(bus, true, prefix, callback, userdata);
1393 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1399 if (w->interface && w->node && w->vtable) {
1400 const sd_bus_vtable *v;
1402 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1403 struct vtable_member *x = NULL;
1407 case _SD_BUS_VTABLE_METHOD: {
1408 struct vtable_member key;
1410 key.path = w->node->path;
1411 key.interface = w->interface;
1412 key.member = v->x.method.member;
1414 x = hashmap_remove(bus->vtable_methods, &key);
1418 case _SD_BUS_VTABLE_PROPERTY:
1419 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1420 struct vtable_member key;
1422 key.path = w->node->path;
1423 key.interface = w->interface;
1424 key.member = v->x.property.member;
1425 x = hashmap_remove(bus->vtable_properties, &key);
1437 static unsigned vtable_member_hash_func(const void *a) {
1438 const struct vtable_member *m = a;
1443 string_hash_func(m->path) ^
1444 string_hash_func(m->interface) ^
1445 string_hash_func(m->member);
1448 static int vtable_member_compare_func(const void *a, const void *b) {
1449 const struct vtable_member *x = a, *y = b;
1455 r = strcmp(x->path, y->path);
1459 r = strcmp(x->interface, y->interface);
1463 return strcmp(x->member, y->member);
1466 static int add_object_vtable_internal(
1469 const char *interface,
1470 const sd_bus_vtable *vtable,
1472 sd_bus_object_find_t find,
1475 struct node_vtable *c = NULL, *i;
1476 const sd_bus_vtable *v;
1480 assert_return(bus, -EINVAL);
1481 assert_return(object_path_is_valid(path), -EINVAL);
1482 assert_return(interface_name_is_valid(interface), -EINVAL);
1483 assert_return(vtable, -EINVAL);
1484 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1485 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1486 assert_return(!bus_pid_changed(bus), -ECHILD);
1488 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1492 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1496 n = bus_node_allocate(bus, path);
1500 LIST_FOREACH(vtables, i, n->vtables) {
1501 if (streq(i->interface, interface)) {
1506 if (i->is_fallback != fallback) {
1512 c = new0(struct node_vtable, 1);
1519 c->is_fallback = fallback;
1521 c->userdata = userdata;
1524 c->interface = strdup(interface);
1525 if (!c->interface) {
1530 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1534 case _SD_BUS_VTABLE_METHOD: {
1535 struct vtable_member *m;
1537 if (!member_name_is_valid(v->x.method.member) ||
1538 !signature_is_valid(strempty(v->x.method.signature), false) ||
1539 !signature_is_valid(strempty(v->x.method.result), false) ||
1540 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1541 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1546 m = new0(struct vtable_member, 1);
1554 m->interface = c->interface;
1555 m->member = v->x.method.member;
1558 r = hashmap_put(bus->vtable_methods, m, m);
1567 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1569 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1576 case _SD_BUS_VTABLE_PROPERTY: {
1577 struct vtable_member *m;
1579 if (!member_name_is_valid(v->x.property.member) ||
1580 !signature_is_single(v->x.property.signature, false) ||
1581 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
1582 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1583 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1589 m = new0(struct vtable_member, 1);
1597 m->interface = c->interface;
1598 m->member = v->x.property.member;
1601 r = hashmap_put(bus->vtable_properties, m, m);
1610 case _SD_BUS_VTABLE_SIGNAL:
1612 if (!member_name_is_valid(v->x.signal.member) ||
1613 !signature_is_single(strempty(v->x.signal.signature), false)) {
1626 LIST_PREPEND(vtables, n->vtables, c);
1627 bus->nodes_modified = true;
1633 free_node_vtable(bus, c);
1635 bus_node_gc(bus, n);
1639 static int remove_object_vtable_internal(
1642 const char *interface,
1645 struct node_vtable *c;
1648 assert_return(bus, -EINVAL);
1649 assert_return(object_path_is_valid(path), -EINVAL);
1650 assert_return(interface_name_is_valid(interface), -EINVAL);
1651 assert_return(!bus_pid_changed(bus), -ECHILD);
1653 n = hashmap_get(bus->nodes, path);
1657 LIST_FOREACH(vtables, c, n->vtables)
1658 if (streq(c->interface, interface) && c->is_fallback == fallback)
1664 LIST_REMOVE(vtables, n->vtables, c);
1666 free_node_vtable(bus, c);
1667 bus_node_gc(bus, n);
1669 bus->nodes_modified = true;
1674 int sd_bus_add_object_vtable(
1677 const char *interface,
1678 const sd_bus_vtable *vtable,
1681 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1684 int sd_bus_remove_object_vtable(
1687 const char *interface) {
1689 return remove_object_vtable_internal(bus, path, interface, false);
1692 int sd_bus_add_fallback_vtable(
1695 const char *interface,
1696 const sd_bus_vtable *vtable,
1697 sd_bus_object_find_t find,
1700 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1703 int sd_bus_remove_fallback_vtable(
1706 const char *interface) {
1708 return remove_object_vtable_internal(bus, path, interface, true);
1711 int sd_bus_add_node_enumerator(
1714 sd_bus_node_enumerator_t callback,
1717 struct node_enumerator *c;
1721 assert_return(bus, -EINVAL);
1722 assert_return(object_path_is_valid(path), -EINVAL);
1723 assert_return(callback, -EINVAL);
1724 assert_return(!bus_pid_changed(bus), -ECHILD);
1726 n = bus_node_allocate(bus, path);
1730 c = new0(struct node_enumerator, 1);
1737 c->callback = callback;
1738 c->userdata = userdata;
1740 LIST_PREPEND(enumerators, n->enumerators, c);
1742 bus->nodes_modified = true;
1748 bus_node_gc(bus, n);
1752 int sd_bus_remove_node_enumerator(
1755 sd_bus_node_enumerator_t callback,
1758 struct node_enumerator *c;
1761 assert_return(bus, -EINVAL);
1762 assert_return(object_path_is_valid(path), -EINVAL);
1763 assert_return(callback, -EINVAL);
1764 assert_return(!bus_pid_changed(bus), -ECHILD);
1766 n = hashmap_get(bus->nodes, path);
1770 LIST_FOREACH(enumerators, c, n->enumerators)
1771 if (c->callback == callback && c->userdata == userdata)
1777 LIST_REMOVE(enumerators, n->enumerators, c);
1780 bus_node_gc(bus, n);
1782 bus->nodes_modified = true;
1787 static int emit_properties_changed_on_interface(
1791 const char *interface,
1792 bool require_fallback,
1795 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1796 bool has_invalidating = false;
1797 struct vtable_member key;
1798 struct node_vtable *c;
1809 n = hashmap_get(bus->nodes, prefix);
1813 LIST_FOREACH(vtables, c, n->vtables) {
1814 if (require_fallback && !c->is_fallback)
1817 if (streq(c->interface, interface))
1824 r = node_vtable_get_userdata(bus, path, c, &u);
1827 if (bus->nodes_modified)
1830 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1834 r = sd_bus_message_append(m, "s", interface);
1838 r = sd_bus_message_open_container(m, 'a', "{sv}");
1843 key.interface = interface;
1845 STRV_FOREACH(property, names) {
1846 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1847 struct vtable_member *v;
1849 assert_return(member_name_is_valid(*property), -EINVAL);
1851 key.member = *property;
1852 v = hashmap_get(bus->vtable_properties, &key);
1856 assert(c == v->parent);
1857 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1859 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1860 has_invalidating = true;
1864 r = sd_bus_message_open_container(m, 'e', "sv");
1868 r = sd_bus_message_append(m, "s", *property);
1872 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1876 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1879 if (bus->nodes_modified)
1882 r = sd_bus_message_close_container(m);
1886 r = sd_bus_message_close_container(m);
1891 r = sd_bus_message_close_container(m);
1895 r = sd_bus_message_open_container(m, 'a', "s");
1899 if (has_invalidating) {
1900 STRV_FOREACH(property, names) {
1901 struct vtable_member *v;
1903 key.member = *property;
1904 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1905 assert(c == v->parent);
1907 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1910 r = sd_bus_message_append(m, "s", *property);
1916 r = sd_bus_message_close_container(m);
1920 r = sd_bus_send(bus, m, NULL);
1927 int sd_bus_emit_properties_changed_strv(
1930 const char *interface,
1933 BUS_DONT_DESTROY(bus);
1937 assert_return(bus, -EINVAL);
1938 assert_return(object_path_is_valid(path), -EINVAL);
1939 assert_return(interface_name_is_valid(interface), -EINVAL);
1940 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1941 assert_return(!bus_pid_changed(bus), -ECHILD);
1943 if (strv_isempty(names))
1947 bus->nodes_modified = false;
1949 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1952 if (bus->nodes_modified)
1955 prefix = alloca(strlen(path) + 1);
1956 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1957 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1960 if (bus->nodes_modified)
1964 } while (bus->nodes_modified);
1969 int sd_bus_emit_properties_changed(
1972 const char *interface,
1973 const char *name, ...) {
1975 _cleanup_strv_free_ char **names = NULL;
1978 assert_return(bus, -EINVAL);
1979 assert_return(object_path_is_valid(path), -EINVAL);
1980 assert_return(interface_name_is_valid(interface), -EINVAL);
1981 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1982 assert_return(!bus_pid_changed(bus), -ECHILD);
1988 names = strv_new_ap(name, ap);
1994 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
1997 static int interfaces_added_append_one_prefix(
2002 const char *interface,
2003 bool require_fallback) {
2005 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2006 struct node_vtable *c;
2017 n = hashmap_get(bus->nodes, prefix);
2021 LIST_FOREACH(vtables, c, n->vtables) {
2022 if (require_fallback && !c->is_fallback)
2025 if (streq(c->interface, interface))
2032 r = node_vtable_get_userdata(bus, path, c, &u);
2035 if (bus->nodes_modified)
2038 r = sd_bus_message_append_basic(m, 's', interface);
2042 r = sd_bus_message_open_container(m, 'a', "{sv}");
2046 r = vtable_append_all_properties(bus, m,path, c, u, &error);
2049 if (bus->nodes_modified)
2052 r = sd_bus_message_close_container(m);
2059 static int interfaces_added_append_one(
2063 const char *interface) {
2073 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2076 if (bus->nodes_modified)
2079 prefix = alloca(strlen(path) + 1);
2080 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2081 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2084 if (bus->nodes_modified)
2091 int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2092 BUS_DONT_DESTROY(bus);
2094 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2098 assert_return(bus, -EINVAL);
2099 assert_return(object_path_is_valid(path), -EINVAL);
2100 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2101 assert_return(!bus_pid_changed(bus), -ECHILD);
2103 if (strv_isempty(interfaces))
2107 bus->nodes_modified = false;
2110 m = sd_bus_message_unref(m);
2112 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2116 r = sd_bus_message_append_basic(m, 'o', path);
2120 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2124 STRV_FOREACH(i, interfaces) {
2125 assert_return(interface_name_is_valid(*i), -EINVAL);
2127 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2131 r = interfaces_added_append_one(bus, m, path, *i);
2135 if (bus->nodes_modified)
2138 r = sd_bus_message_close_container(m);
2143 if (bus->nodes_modified)
2146 r = sd_bus_message_close_container(m);
2150 } while (bus->nodes_modified);
2152 return sd_bus_send(bus, m, NULL);
2155 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2156 _cleanup_strv_free_ char **interfaces = NULL;
2159 assert_return(bus, -EINVAL);
2160 assert_return(object_path_is_valid(path), -EINVAL);
2161 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2162 assert_return(!bus_pid_changed(bus), -ECHILD);
2164 va_start(ap, interface);
2165 interfaces = strv_new_ap(interface, ap);
2171 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2174 int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2175 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2178 assert_return(bus, -EINVAL);
2179 assert_return(object_path_is_valid(path), -EINVAL);
2180 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2181 assert_return(!bus_pid_changed(bus), -ECHILD);
2183 if (strv_isempty(interfaces))
2186 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2190 r = sd_bus_message_append_basic(m, 'o', path);
2194 r = sd_bus_message_append_strv(m, interfaces);
2198 return sd_bus_send(bus, m, NULL);
2201 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2202 _cleanup_strv_free_ char **interfaces = NULL;
2205 assert_return(bus, -EINVAL);
2206 assert_return(object_path_is_valid(path), -EINVAL);
2207 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2208 assert_return(!bus_pid_changed(bus), -ECHILD);
2210 va_start(ap, interface);
2211 interfaces = strv_new_ap(interface, ap);
2217 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2220 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2223 assert_return(bus, -EINVAL);
2224 assert_return(object_path_is_valid(path), -EINVAL);
2225 assert_return(!bus_pid_changed(bus), -ECHILD);
2227 n = bus_node_allocate(bus, path);
2231 n->object_manager = true;
2232 bus->nodes_modified = true;
2236 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2239 assert_return(bus, -EINVAL);
2240 assert_return(object_path_is_valid(path), -EINVAL);
2241 assert_return(!bus_pid_changed(bus), -ECHILD);
2243 n = hashmap_get(bus->nodes, path);
2247 if (!n->object_manager)
2250 n->object_manager = false;
2251 bus->nodes_modified = true;
2252 bus_node_gc(bus, n);