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;
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}");
604 streq(iface, "org.freedesktop.DBus.Properties") ||
605 streq(iface, "org.freedesktop.DBus.Peer") ||
606 streq(iface, "org.freedesktop.DBus.Introspectable");
608 LIST_FOREACH(vtables, c, first) {
609 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
612 if (require_fallback && !c->is_fallback)
615 r = node_vtable_get_userdata(bus, m->path, c, &u);
618 if (bus->nodes_modified)
623 *found_object = true;
625 if (iface && !streq(c->interface, iface))
627 found_interface = true;
629 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
633 if (sd_bus_error_is_set(&error)) {
634 r = sd_bus_reply_method_error(bus, m, &error);
640 if (bus->nodes_modified)
644 if (!found_interface) {
645 r = sd_bus_reply_method_errorf(
647 SD_BUS_ERROR_UNKNOWN_INTERFACE,
648 "Unknown interface '%s'.", iface);
655 r = sd_bus_message_close_container(reply);
659 r = sd_bus_send(bus, reply, NULL);
666 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
670 if (n->object_manager)
674 return bus_node_with_object_manager(bus, n->parent);
679 static bool bus_node_exists(
683 bool require_fallback) {
685 struct node_vtable *c;
686 struct node_callback *k;
692 /* Tests if there's anything attached directly to this node
693 * for the specified path */
695 LIST_FOREACH(callbacks, k, n->callbacks) {
696 if (require_fallback && !k->is_fallback)
702 LIST_FOREACH(vtables, c, n->vtables) {
704 if (require_fallback && !c->is_fallback)
707 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
709 if (bus->nodes_modified)
713 return !require_fallback && (n->enumerators || n->object_manager);
716 static int process_introspect(
720 bool require_fallback,
721 bool *found_object) {
723 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
724 _cleanup_set_free_free_ Set *s = NULL;
725 struct introspect intro;
726 struct node_vtable *c;
733 assert(found_object);
735 r = get_child_nodes(bus, m->path, n, &s);
738 if (bus->nodes_modified)
741 r = introspect_begin(&intro);
745 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
749 empty = set_isempty(s);
751 LIST_FOREACH(vtables, c, n->vtables) {
752 if (require_fallback && !c->is_fallback)
755 r = node_vtable_get_userdata(bus, m->path, c, NULL);
758 if (bus->nodes_modified)
765 r = introspect_write_interface(&intro, c->interface, c->vtable);
771 /* Nothing?, let's see if we exist at all, and if not
772 * refuse to do anything */
773 r = bus_node_exists(bus, n, m->path, require_fallback);
776 if (bus->nodes_modified)
782 *found_object = true;
784 r = introspect_write_child_nodes(&intro, s, m->path);
788 r = introspect_finish(&intro, bus, m, &reply);
792 r = sd_bus_send(bus, reply, NULL);
799 introspect_free(&intro);
803 static int object_manager_serialize_vtable(
805 sd_bus_message *reply,
807 struct node_vtable *c,
819 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
823 r = sd_bus_message_append(reply, "s", c->interface);
827 r = sd_bus_message_open_container(reply, 'a', "{sv}");
831 r = vtable_append_all_properties(bus, reply, path, c, userdata, error);
834 if (bus->nodes_modified)
837 r = sd_bus_message_close_container(reply);
841 r = sd_bus_message_close_container(reply);
848 static int object_manager_serialize_path(
850 sd_bus_message *reply,
853 bool require_fallback,
854 sd_bus_error *error) {
856 struct node_vtable *i;
858 bool found_something = false;
867 n = hashmap_get(bus->nodes, prefix);
871 LIST_FOREACH(vtables, i, n->vtables) {
874 if (require_fallback && !i->is_fallback)
877 r = node_vtable_get_userdata(bus, path, i, &u);
880 if (bus->nodes_modified)
885 if (!found_something) {
886 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
890 r = sd_bus_message_append(reply, "o", path);
894 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
898 found_something = true;
901 r = object_manager_serialize_vtable(bus, reply, path, i, error, u);
904 if (sd_bus_error_is_set(error))
906 if (bus->nodes_modified)
910 if (found_something) {
911 r = sd_bus_message_close_container(reply);
915 r = sd_bus_message_close_container(reply);
923 static int object_manager_serialize_path_and_fallbacks(
925 sd_bus_message *reply,
927 sd_bus_error *error) {
937 /* First, add all vtables registered for this path */
938 r = object_manager_serialize_path(bus, reply, path, path, false, error);
941 if (sd_bus_error_is_set(error))
943 if (bus->nodes_modified)
946 /* Second, add fallback vtables registered for any of the prefixes */
947 prefix = alloca(strlen(path) + 1);
948 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
949 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
952 if (sd_bus_error_is_set(error))
954 if (bus->nodes_modified)
961 static int process_get_managed_objects(
965 bool require_fallback,
966 bool *found_object) {
968 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
969 _cleanup_set_free_free_ Set *s = NULL;
976 assert(found_object);
978 if (!bus_node_with_object_manager(bus, n))
981 r = get_child_nodes(bus, m->path, n, &s);
984 if (bus->nodes_modified)
987 r = sd_bus_message_new_method_return(bus, m, &reply);
991 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
995 empty = set_isempty(s);
997 struct node_vtable *c;
999 /* Hmm, so we have no children? Then let's check
1000 * whether we exist at all, i.e. whether at least one
1003 LIST_FOREACH(vtables, c, n->vtables) {
1005 if (require_fallback && !c->is_fallback)
1023 SET_FOREACH(path, s, i) {
1024 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1026 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1030 if (sd_bus_error_is_set(&error)) {
1031 r = sd_bus_reply_method_error(bus, m, &error);
1038 if (bus->nodes_modified)
1043 r = sd_bus_message_close_container(reply);
1047 r = sd_bus_send(bus, reply, NULL);
1054 static int object_find_and_run(
1058 bool require_fallback,
1059 bool *found_object) {
1062 struct vtable_member vtable_key, *v;
1068 assert(found_object);
1070 n = hashmap_get(bus->nodes, p);
1074 /* First, try object callbacks */
1075 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1078 if (bus->nodes_modified)
1081 if (!m->interface || !m->member)
1084 /* Then, look for a known method */
1085 vtable_key.path = (char*) p;
1086 vtable_key.interface = m->interface;
1087 vtable_key.member = m->member;
1089 v = hashmap_get(bus->vtable_methods, &vtable_key);
1091 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1094 if (bus->nodes_modified)
1098 /* Then, look for a known property */
1099 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1102 get = streq(m->member, "Get");
1104 if (get || streq(m->member, "Set")) {
1106 r = sd_bus_message_rewind(m, true);
1110 vtable_key.path = (char*) p;
1112 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1116 v = hashmap_get(bus->vtable_properties, &vtable_key);
1118 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1123 } else if (streq(m->member, "GetAll")) {
1126 r = sd_bus_message_rewind(m, true);
1130 r = sd_bus_message_read(m, "s", &iface);
1137 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1142 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1144 r = process_introspect(bus, m, n, require_fallback, found_object);
1148 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1150 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1155 if (bus->nodes_modified)
1158 if (!*found_object) {
1159 r = bus_node_exists(bus, n, m->path, require_fallback);
1163 *found_object = true;
1169 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1172 bool found_object = false;
1177 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1183 if (hashmap_isempty(bus->nodes))
1186 pl = strlen(m->path);
1190 bus->nodes_modified = false;
1192 r = object_find_and_run(bus, m, m->path, false, &found_object);
1196 /* Look for fallback prefixes */
1197 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1199 if (bus->nodes_modified)
1202 r = object_find_and_run(bus, m, prefix, true, &found_object);
1207 } while (bus->nodes_modified);
1212 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1213 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1214 r = sd_bus_reply_method_errorf(
1216 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1217 "Unknown property or interface.");
1219 r = sd_bus_reply_method_errorf(
1221 SD_BUS_ERROR_UNKNOWN_METHOD,
1222 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1230 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1231 struct node *n, *parent;
1238 assert(path[0] == '/');
1240 n = hashmap_get(bus->nodes, path);
1244 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1252 if (streq(path, "/"))
1255 e = strrchr(path, '/');
1258 p = strndupa(path, MAX(1, path - e));
1260 parent = bus_node_allocate(bus, p);
1267 n = new0(struct node, 1);
1274 r = hashmap_put(bus->nodes, s, n);
1282 LIST_PREPEND(siblings, parent->child, n);
1287 static void bus_node_gc(sd_bus *b, struct node *n) {
1300 assert(hashmap_remove(b->nodes, n->path) == n);
1303 LIST_REMOVE(siblings, n->parent->child, n);
1306 bus_node_gc(b, n->parent);
1310 static int bus_add_object(
1314 sd_bus_message_handler_t callback,
1317 struct node_callback *c;
1321 assert_return(bus, -EINVAL);
1322 assert_return(object_path_is_valid(path), -EINVAL);
1323 assert_return(callback, -EINVAL);
1324 assert_return(!bus_pid_changed(bus), -ECHILD);
1326 n = bus_node_allocate(bus, path);
1330 c = new0(struct node_callback, 1);
1337 c->callback = callback;
1338 c->userdata = userdata;
1339 c->is_fallback = fallback;
1341 LIST_PREPEND(callbacks, n->callbacks, c);
1342 bus->nodes_modified = true;
1348 bus_node_gc(bus, n);
1352 static int bus_remove_object(
1356 sd_bus_message_handler_t callback,
1359 struct node_callback *c;
1362 assert_return(bus, -EINVAL);
1363 assert_return(object_path_is_valid(path), -EINVAL);
1364 assert_return(callback, -EINVAL);
1365 assert_return(!bus_pid_changed(bus), -ECHILD);
1367 n = hashmap_get(bus->nodes, path);
1371 LIST_FOREACH(callbacks, c, n->callbacks)
1372 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1377 LIST_REMOVE(callbacks, n->callbacks, c);
1380 bus_node_gc(bus, n);
1381 bus->nodes_modified = true;
1386 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1387 return bus_add_object(bus, false, path, callback, userdata);
1390 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1391 return bus_remove_object(bus, false, path, callback, userdata);
1394 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1395 return bus_add_object(bus, true, prefix, callback, userdata);
1398 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1399 return bus_remove_object(bus, true, prefix, callback, userdata);
1402 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1408 if (w->interface && w->node && w->vtable) {
1409 const sd_bus_vtable *v;
1411 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1412 struct vtable_member *x = NULL;
1416 case _SD_BUS_VTABLE_METHOD: {
1417 struct vtable_member key;
1419 key.path = w->node->path;
1420 key.interface = w->interface;
1421 key.member = v->x.method.member;
1423 x = hashmap_remove(bus->vtable_methods, &key);
1427 case _SD_BUS_VTABLE_PROPERTY:
1428 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1429 struct vtable_member key;
1431 key.path = w->node->path;
1432 key.interface = w->interface;
1433 key.member = v->x.property.member;
1434 x = hashmap_remove(bus->vtable_properties, &key);
1446 static unsigned vtable_member_hash_func(const void *a) {
1447 const struct vtable_member *m = a;
1452 string_hash_func(m->path) ^
1453 string_hash_func(m->interface) ^
1454 string_hash_func(m->member);
1457 static int vtable_member_compare_func(const void *a, const void *b) {
1458 const struct vtable_member *x = a, *y = b;
1464 r = strcmp(x->path, y->path);
1468 r = strcmp(x->interface, y->interface);
1472 return strcmp(x->member, y->member);
1475 static int add_object_vtable_internal(
1478 const char *interface,
1479 const sd_bus_vtable *vtable,
1481 sd_bus_object_find_t find,
1484 struct node_vtable *c = NULL, *i;
1485 const sd_bus_vtable *v;
1489 assert_return(bus, -EINVAL);
1490 assert_return(object_path_is_valid(path), -EINVAL);
1491 assert_return(interface_name_is_valid(interface), -EINVAL);
1492 assert_return(vtable, -EINVAL);
1493 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1494 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1495 assert_return(!bus_pid_changed(bus), -ECHILD);
1497 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1501 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1505 n = bus_node_allocate(bus, path);
1509 LIST_FOREACH(vtables, i, n->vtables) {
1510 if (streq(i->interface, interface)) {
1515 if (i->is_fallback != fallback) {
1521 c = new0(struct node_vtable, 1);
1528 c->is_fallback = fallback;
1530 c->userdata = userdata;
1533 c->interface = strdup(interface);
1534 if (!c->interface) {
1539 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1543 case _SD_BUS_VTABLE_METHOD: {
1544 struct vtable_member *m;
1546 if (!member_name_is_valid(v->x.method.member) ||
1547 !signature_is_valid(strempty(v->x.method.signature), false) ||
1548 !signature_is_valid(strempty(v->x.method.result), false) ||
1549 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1550 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1555 m = new0(struct vtable_member, 1);
1563 m->interface = c->interface;
1564 m->member = v->x.method.member;
1567 r = hashmap_put(bus->vtable_methods, m, m);
1576 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1578 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1585 case _SD_BUS_VTABLE_PROPERTY: {
1586 struct vtable_member *m;
1588 if (!member_name_is_valid(v->x.property.member) ||
1589 !signature_is_single(v->x.property.signature, false) ||
1590 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
1591 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1592 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1598 m = new0(struct vtable_member, 1);
1606 m->interface = c->interface;
1607 m->member = v->x.property.member;
1610 r = hashmap_put(bus->vtable_properties, m, m);
1619 case _SD_BUS_VTABLE_SIGNAL:
1621 if (!member_name_is_valid(v->x.signal.member) ||
1622 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1635 LIST_PREPEND(vtables, n->vtables, c);
1636 bus->nodes_modified = true;
1642 free_node_vtable(bus, c);
1644 bus_node_gc(bus, n);
1648 static int remove_object_vtable_internal(
1651 const char *interface,
1654 struct node_vtable *c;
1657 assert_return(bus, -EINVAL);
1658 assert_return(object_path_is_valid(path), -EINVAL);
1659 assert_return(interface_name_is_valid(interface), -EINVAL);
1660 assert_return(!bus_pid_changed(bus), -ECHILD);
1662 n = hashmap_get(bus->nodes, path);
1666 LIST_FOREACH(vtables, c, n->vtables)
1667 if (streq(c->interface, interface) && c->is_fallback == fallback)
1673 LIST_REMOVE(vtables, n->vtables, c);
1675 free_node_vtable(bus, c);
1676 bus_node_gc(bus, n);
1678 bus->nodes_modified = true;
1683 int sd_bus_add_object_vtable(
1686 const char *interface,
1687 const sd_bus_vtable *vtable,
1690 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1693 int sd_bus_remove_object_vtable(
1696 const char *interface) {
1698 return remove_object_vtable_internal(bus, path, interface, false);
1701 int sd_bus_add_fallback_vtable(
1704 const char *interface,
1705 const sd_bus_vtable *vtable,
1706 sd_bus_object_find_t find,
1709 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1712 int sd_bus_remove_fallback_vtable(
1715 const char *interface) {
1717 return remove_object_vtable_internal(bus, path, interface, true);
1720 int sd_bus_add_node_enumerator(
1723 sd_bus_node_enumerator_t callback,
1726 struct node_enumerator *c;
1730 assert_return(bus, -EINVAL);
1731 assert_return(object_path_is_valid(path), -EINVAL);
1732 assert_return(callback, -EINVAL);
1733 assert_return(!bus_pid_changed(bus), -ECHILD);
1735 n = bus_node_allocate(bus, path);
1739 c = new0(struct node_enumerator, 1);
1746 c->callback = callback;
1747 c->userdata = userdata;
1749 LIST_PREPEND(enumerators, n->enumerators, c);
1751 bus->nodes_modified = true;
1757 bus_node_gc(bus, n);
1761 int sd_bus_remove_node_enumerator(
1764 sd_bus_node_enumerator_t callback,
1767 struct node_enumerator *c;
1770 assert_return(bus, -EINVAL);
1771 assert_return(object_path_is_valid(path), -EINVAL);
1772 assert_return(callback, -EINVAL);
1773 assert_return(!bus_pid_changed(bus), -ECHILD);
1775 n = hashmap_get(bus->nodes, path);
1779 LIST_FOREACH(enumerators, c, n->enumerators)
1780 if (c->callback == callback && c->userdata == userdata)
1786 LIST_REMOVE(enumerators, n->enumerators, c);
1789 bus_node_gc(bus, n);
1791 bus->nodes_modified = true;
1796 static int emit_properties_changed_on_interface(
1800 const char *interface,
1801 bool require_fallback,
1804 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1805 bool has_invalidating = false;
1806 struct vtable_member key;
1807 struct node_vtable *c;
1818 n = hashmap_get(bus->nodes, prefix);
1822 LIST_FOREACH(vtables, c, n->vtables) {
1823 if (require_fallback && !c->is_fallback)
1826 if (streq(c->interface, interface))
1833 r = node_vtable_get_userdata(bus, path, c, &u);
1836 if (bus->nodes_modified)
1839 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1843 r = sd_bus_message_append(m, "s", interface);
1847 r = sd_bus_message_open_container(m, 'a', "{sv}");
1852 key.interface = interface;
1854 STRV_FOREACH(property, names) {
1855 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1856 struct vtable_member *v;
1858 assert_return(member_name_is_valid(*property), -EINVAL);
1860 key.member = *property;
1861 v = hashmap_get(bus->vtable_properties, &key);
1865 assert(c == v->parent);
1866 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1868 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1869 has_invalidating = true;
1873 r = sd_bus_message_open_container(m, 'e', "sv");
1877 r = sd_bus_message_append(m, "s", *property);
1881 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1885 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1888 if (bus->nodes_modified)
1891 r = sd_bus_message_close_container(m);
1895 r = sd_bus_message_close_container(m);
1900 r = sd_bus_message_close_container(m);
1904 r = sd_bus_message_open_container(m, 'a', "s");
1908 if (has_invalidating) {
1909 STRV_FOREACH(property, names) {
1910 struct vtable_member *v;
1912 key.member = *property;
1913 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1914 assert(c == v->parent);
1916 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1919 r = sd_bus_message_append(m, "s", *property);
1925 r = sd_bus_message_close_container(m);
1929 r = sd_bus_send(bus, m, NULL);
1936 int sd_bus_emit_properties_changed_strv(
1939 const char *interface,
1942 BUS_DONT_DESTROY(bus);
1946 assert_return(bus, -EINVAL);
1947 assert_return(object_path_is_valid(path), -EINVAL);
1948 assert_return(interface_name_is_valid(interface), -EINVAL);
1949 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1950 assert_return(!bus_pid_changed(bus), -ECHILD);
1952 if (strv_isempty(names))
1956 bus->nodes_modified = false;
1958 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1961 if (bus->nodes_modified)
1964 prefix = alloca(strlen(path) + 1);
1965 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1966 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1969 if (bus->nodes_modified)
1973 } while (bus->nodes_modified);
1978 int sd_bus_emit_properties_changed(
1981 const char *interface,
1982 const char *name, ...) {
1986 assert_return(bus, -EINVAL);
1987 assert_return(object_path_is_valid(path), -EINVAL);
1988 assert_return(interface_name_is_valid(interface), -EINVAL);
1989 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1990 assert_return(!bus_pid_changed(bus), -ECHILD);
1995 names = strv_from_stdarg_alloca(name);
1997 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2000 static int interfaces_added_append_one_prefix(
2005 const char *interface,
2006 bool require_fallback) {
2008 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2009 struct node_vtable *c;
2020 n = hashmap_get(bus->nodes, prefix);
2024 LIST_FOREACH(vtables, c, n->vtables) {
2025 if (require_fallback && !c->is_fallback)
2028 if (streq(c->interface, interface))
2035 r = node_vtable_get_userdata(bus, path, c, &u);
2038 if (bus->nodes_modified)
2041 r = sd_bus_message_append_basic(m, 's', interface);
2045 r = sd_bus_message_open_container(m, 'a', "{sv}");
2049 r = vtable_append_all_properties(bus, m,path, c, u, &error);
2052 if (bus->nodes_modified)
2055 r = sd_bus_message_close_container(m);
2062 static int interfaces_added_append_one(
2066 const char *interface) {
2076 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2079 if (bus->nodes_modified)
2082 prefix = alloca(strlen(path) + 1);
2083 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2084 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2087 if (bus->nodes_modified)
2094 int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2095 BUS_DONT_DESTROY(bus);
2097 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2101 assert_return(bus, -EINVAL);
2102 assert_return(object_path_is_valid(path), -EINVAL);
2103 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2104 assert_return(!bus_pid_changed(bus), -ECHILD);
2106 if (strv_isempty(interfaces))
2110 bus->nodes_modified = false;
2113 m = sd_bus_message_unref(m);
2115 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2119 r = sd_bus_message_append_basic(m, 'o', path);
2123 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2127 STRV_FOREACH(i, interfaces) {
2128 assert_return(interface_name_is_valid(*i), -EINVAL);
2130 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2134 r = interfaces_added_append_one(bus, m, path, *i);
2138 if (bus->nodes_modified)
2141 r = sd_bus_message_close_container(m);
2146 if (bus->nodes_modified)
2149 r = sd_bus_message_close_container(m);
2153 } while (bus->nodes_modified);
2155 return sd_bus_send(bus, m, NULL);
2158 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2161 assert_return(bus, -EINVAL);
2162 assert_return(object_path_is_valid(path), -EINVAL);
2163 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2164 assert_return(!bus_pid_changed(bus), -ECHILD);
2166 interfaces = strv_from_stdarg_alloca(interface);
2168 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2171 int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2172 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2175 assert_return(bus, -EINVAL);
2176 assert_return(object_path_is_valid(path), -EINVAL);
2177 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2178 assert_return(!bus_pid_changed(bus), -ECHILD);
2180 if (strv_isempty(interfaces))
2183 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2187 r = sd_bus_message_append_basic(m, 'o', path);
2191 r = sd_bus_message_append_strv(m, interfaces);
2195 return sd_bus_send(bus, m, NULL);
2198 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2201 assert_return(bus, -EINVAL);
2202 assert_return(object_path_is_valid(path), -EINVAL);
2203 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2204 assert_return(!bus_pid_changed(bus), -ECHILD);
2206 interfaces = strv_from_stdarg_alloca(interface);
2208 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2211 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2214 assert_return(bus, -EINVAL);
2215 assert_return(object_path_is_valid(path), -EINVAL);
2216 assert_return(!bus_pid_changed(bus), -ECHILD);
2218 n = bus_node_allocate(bus, path);
2222 n->object_manager = true;
2223 bus->nodes_modified = true;
2227 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2230 assert_return(bus, -EINVAL);
2231 assert_return(object_path_is_valid(path), -EINVAL);
2232 assert_return(!bus_pid_changed(bus), -ECHILD);
2234 n = hashmap_get(bus->nodes, path);
2238 if (!n->object_manager)
2241 n->object_manager = false;
2242 bus->nodes_modified = true;
2243 bus_node_gc(bus, n);