1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "bus-internal.h"
25 #include "bus-message.h"
27 #include "bus-signature.h"
28 #include "bus-introspect.h"
29 #include "bus-objects.h"
32 static int node_vtable_get_userdata(
35 struct node_vtable *c,
47 r = c->find(bus, path, c->interface, &u, u);
58 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
61 return (uint8_t*) u + p->x.property.offset;
64 static int vtable_property_get_userdata(
67 struct vtable_member *p,
78 r = node_vtable_get_userdata(bus, path, p->parent, &u);
81 if (bus->nodes_modified)
84 *userdata = vtable_property_convert_userdata(p->vtable, u);
88 static int add_enumerated_to_set(
91 struct node_enumerator *first,
94 struct node_enumerator *c;
101 LIST_FOREACH(enumerators, c, first) {
102 char **children = NULL, **k;
104 if (bus->nodes_modified)
107 r = c->callback(bus, prefix, &children, c->userdata);
111 STRV_FOREACH(k, children) {
117 if (!object_path_is_valid(*k)){
123 if (!object_path_startswith(*k, prefix)) {
128 r = set_consume(s, *k);
139 static int add_subtree_to_set(
153 r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
156 if (bus->nodes_modified)
159 LIST_FOREACH(siblings, i, n->child) {
162 if (!object_path_startswith(i->path, prefix))
169 r = set_consume(s, t);
170 if (r < 0 && r != -EEXIST)
173 r = add_subtree_to_set(bus, prefix, i, s);
176 if (bus->nodes_modified)
183 static int get_child_nodes(
197 s = set_new(string_hash_func, string_compare_func);
201 r = add_subtree_to_set(bus, prefix, n, s);
211 static int node_callbacks_run(
214 struct node_callback *first,
215 bool require_fallback,
216 bool *found_object) {
218 struct node_callback *c;
223 assert(found_object);
225 LIST_FOREACH(callbacks, c, first) {
226 if (bus->nodes_modified)
229 if (require_fallback && !c->is_fallback)
232 *found_object = true;
234 if (c->last_iteration == bus->iteration_counter)
237 c->last_iteration = bus->iteration_counter;
239 r = sd_bus_message_rewind(m, true);
243 r = c->callback(bus, m, c->userdata);
251 static int method_callbacks_run(
254 struct vtable_member *c,
255 bool require_fallback,
256 bool *found_object) {
258 const char *signature;
265 assert(found_object);
267 if (require_fallback && !c->parent->is_fallback)
270 r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
273 if (bus->nodes_modified)
276 *found_object = true;
278 if (c->last_iteration == bus->iteration_counter)
281 c->last_iteration = bus->iteration_counter;
283 r = sd_bus_message_rewind(m, true);
287 signature = sd_bus_message_get_signature(m, true);
291 if (!streq(strempty(c->vtable->x.method.signature), signature)) {
292 r = sd_bus_reply_method_errorf(bus, m,
293 SD_BUS_ERROR_INVALID_ARGS,
294 "Invalid arguments '%s' to call %s:%s, expecting '%s'.",
295 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
302 if (c->vtable->x.method.handler)
303 return c->vtable->x.method.handler(bus, m, u);
305 /* If the method callback is NULL, make this a successful NOP */
306 r = sd_bus_reply_method_return(bus, m, NULL);
313 static int invoke_property_get(
315 const sd_bus_vtable *v,
317 const char *interface,
318 const char *property,
332 if (v->x.property.get)
333 return v->x.property.get(bus, path, interface, property, m, error, userdata);
335 /* Automatic handling if no callback is defined. */
337 if (streq(v->x.property.signature, "as"))
338 return sd_bus_message_append_strv(m, *(char***) userdata);
340 assert(signature_is_single(v->x.property.signature, false));
341 assert(bus_type_is_basic(v->x.property.signature[0]));
343 switch (v->x.property.signature[0]) {
345 case SD_BUS_TYPE_STRING:
346 case SD_BUS_TYPE_SIGNATURE:
347 p = strempty(*(char**) userdata);
350 case SD_BUS_TYPE_OBJECT_PATH:
351 p = *(char**) userdata;
360 return sd_bus_message_append_basic(m, v->x.property.signature[0], p);
363 static int invoke_property_set(
365 const sd_bus_vtable *v,
367 const char *interface,
368 const char *property,
369 sd_bus_message *value,
382 if (v->x.property.set)
383 return v->x.property.set(bus, path, interface, property, value, error, userdata);
385 /* Automatic handling if no callback is defined. */
387 assert(signature_is_single(v->x.property.signature, false));
388 assert(bus_type_is_basic(v->x.property.signature[0]));
390 switch (v->x.property.signature[0]) {
392 case SD_BUS_TYPE_STRING:
393 case SD_BUS_TYPE_OBJECT_PATH:
394 case SD_BUS_TYPE_SIGNATURE: {
398 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
406 free(*(char**) userdata);
407 *(char**) userdata = n;
413 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
423 static int property_get_set_callbacks_run(
426 struct vtable_member *c,
427 bool require_fallback,
429 bool *found_object) {
431 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
432 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
439 assert(found_object);
441 if (require_fallback && !c->parent->is_fallback)
444 r = vtable_property_get_userdata(bus, m->path, c, &u);
447 if (bus->nodes_modified)
450 *found_object = true;
452 r = sd_bus_message_new_method_return(bus, m, &reply);
457 /* Note that we do not protect against reexecution
458 * here (using the last_iteration check, see below),
459 * should the node tree have changed and we got called
460 * again. We assume that property Get() calls are
461 * ultimately without side-effects or if they aren't
462 * then at least idempotent. */
464 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
468 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
472 if (sd_bus_error_is_set(&error)) {
473 r = sd_bus_reply_method_error(bus, m, &error);
480 if (bus->nodes_modified)
483 r = sd_bus_message_close_container(reply);
488 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
489 sd_bus_error_setf(&error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
491 /* Avoid that we call the set routine more
492 * than once if the processing of this message
493 * got restarted because the node tree
495 if (c->last_iteration == bus->iteration_counter)
498 c->last_iteration = bus->iteration_counter;
500 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
504 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
509 if (sd_bus_error_is_set(&error)) {
510 r = sd_bus_reply_method_error(bus, m, &error);
517 if (bus->nodes_modified)
520 r = sd_bus_message_exit_container(m);
525 r = sd_bus_send(bus, reply, NULL);
532 static int vtable_append_all_properties(
534 sd_bus_message *reply,
536 struct node_vtable *c,
538 sd_bus_error *error) {
540 const sd_bus_vtable *v;
548 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
549 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
552 r = sd_bus_message_open_container(reply, 'e', "sv");
556 r = sd_bus_message_append(reply, "s", v->x.property.member);
560 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
564 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, error, vtable_property_convert_userdata(v, userdata));
567 if (sd_bus_error_is_set(error))
569 if (bus->nodes_modified)
572 r = sd_bus_message_close_container(reply);
576 r = sd_bus_message_close_container(reply);
584 static int property_get_all_callbacks_run(
587 struct node_vtable *first,
588 bool require_fallback,
590 bool *found_object) {
592 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
593 struct node_vtable *c;
594 bool found_interface;
599 assert(found_object);
601 r = sd_bus_message_new_method_return(bus, m, &reply);
605 r = sd_bus_message_open_container(reply, 'a', "{sv}");
609 found_interface = !iface ||
610 streq(iface, "org.freedesktop.DBus.Properties") ||
611 streq(iface, "org.freedesktop.DBus.Peer") ||
612 streq(iface, "org.freedesktop.DBus.Introspectable");
614 LIST_FOREACH(vtables, c, first) {
615 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
618 if (require_fallback && !c->is_fallback)
621 r = node_vtable_get_userdata(bus, m->path, c, &u);
624 if (bus->nodes_modified)
629 *found_object = true;
631 if (iface && !streq(c->interface, iface))
633 found_interface = true;
635 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
639 if (sd_bus_error_is_set(&error)) {
640 r = sd_bus_reply_method_error(bus, m, &error);
646 if (bus->nodes_modified)
650 if (!found_interface) {
651 r = sd_bus_reply_method_errorf(
653 SD_BUS_ERROR_UNKNOWN_INTERFACE,
654 "Unknown interface '%s'.", iface);
661 r = sd_bus_message_close_container(reply);
665 r = sd_bus_send(bus, reply, NULL);
672 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
676 if (n->object_manager)
680 return bus_node_with_object_manager(bus, n->parent);
685 static bool bus_node_exists(
689 bool require_fallback) {
691 struct node_vtable *c;
692 struct node_callback *k;
698 /* Tests if there's anything attached directly to this node
699 * for the specified path */
701 LIST_FOREACH(callbacks, k, n->callbacks) {
702 if (require_fallback && !k->is_fallback)
708 LIST_FOREACH(vtables, c, n->vtables) {
710 if (require_fallback && !c->is_fallback)
713 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
715 if (bus->nodes_modified)
719 return !require_fallback && (n->enumerators || n->object_manager);
722 static int process_introspect(
726 bool require_fallback,
727 bool *found_object) {
729 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
730 _cleanup_set_free_free_ Set *s = NULL;
731 struct introspect intro;
732 struct node_vtable *c;
739 assert(found_object);
741 r = get_child_nodes(bus, m->path, n, &s);
744 if (bus->nodes_modified)
747 r = introspect_begin(&intro);
751 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
755 empty = set_isempty(s);
757 LIST_FOREACH(vtables, c, n->vtables) {
758 if (require_fallback && !c->is_fallback)
761 r = node_vtable_get_userdata(bus, m->path, c, NULL);
764 if (bus->nodes_modified)
771 r = introspect_write_interface(&intro, c->interface, c->vtable);
777 /* Nothing?, let's see if we exist at all, and if not
778 * refuse to do anything */
779 r = bus_node_exists(bus, n, m->path, require_fallback);
782 if (bus->nodes_modified)
788 *found_object = true;
790 r = introspect_write_child_nodes(&intro, s, m->path);
794 r = introspect_finish(&intro, bus, m, &reply);
798 r = sd_bus_send(bus, reply, NULL);
805 introspect_free(&intro);
809 static int object_manager_serialize_vtable(
811 sd_bus_message *reply,
813 struct node_vtable *c,
825 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
829 r = sd_bus_message_append(reply, "s", c->interface);
833 r = sd_bus_message_open_container(reply, 'a', "{sv}");
837 r = vtable_append_all_properties(bus, reply, path, c, userdata, error);
840 if (bus->nodes_modified)
843 r = sd_bus_message_close_container(reply);
847 r = sd_bus_message_close_container(reply);
854 static int object_manager_serialize_path(
856 sd_bus_message *reply,
859 bool require_fallback,
860 sd_bus_error *error) {
862 struct node_vtable *i;
864 bool found_something = false;
873 n = hashmap_get(bus->nodes, prefix);
877 LIST_FOREACH(vtables, i, n->vtables) {
880 if (require_fallback && !i->is_fallback)
883 r = node_vtable_get_userdata(bus, path, i, &u);
886 if (bus->nodes_modified)
891 if (!found_something) {
892 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
896 r = sd_bus_message_append(reply, "o", path);
900 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
904 found_something = true;
907 r = object_manager_serialize_vtable(bus, reply, path, i, error, u);
910 if (sd_bus_error_is_set(error))
912 if (bus->nodes_modified)
916 if (found_something) {
917 r = sd_bus_message_close_container(reply);
921 r = sd_bus_message_close_container(reply);
929 static int object_manager_serialize_path_and_fallbacks(
931 sd_bus_message *reply,
933 sd_bus_error *error) {
943 /* First, add all vtables registered for this path */
944 r = object_manager_serialize_path(bus, reply, path, path, false, error);
947 if (sd_bus_error_is_set(error))
949 if (bus->nodes_modified)
952 /* Second, add fallback vtables registered for any of the prefixes */
953 prefix = alloca(strlen(path) + 1);
954 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
955 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
958 if (sd_bus_error_is_set(error))
960 if (bus->nodes_modified)
967 static int process_get_managed_objects(
971 bool require_fallback,
972 bool *found_object) {
974 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
975 _cleanup_set_free_free_ Set *s = NULL;
982 assert(found_object);
984 if (!bus_node_with_object_manager(bus, n))
987 r = get_child_nodes(bus, m->path, n, &s);
990 if (bus->nodes_modified)
993 r = sd_bus_message_new_method_return(bus, m, &reply);
997 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1001 empty = set_isempty(s);
1003 struct node_vtable *c;
1005 /* Hmm, so we have no children? Then let's check
1006 * whether we exist at all, i.e. whether at least one
1009 LIST_FOREACH(vtables, c, n->vtables) {
1011 if (require_fallback && !c->is_fallback)
1029 SET_FOREACH(path, s, i) {
1030 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1032 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1036 if (sd_bus_error_is_set(&error)) {
1037 r = sd_bus_reply_method_error(bus, m, &error);
1044 if (bus->nodes_modified)
1049 r = sd_bus_message_close_container(reply);
1053 r = sd_bus_send(bus, reply, NULL);
1060 static int object_find_and_run(
1064 bool require_fallback,
1065 bool *found_object) {
1068 struct vtable_member vtable_key, *v;
1074 assert(found_object);
1076 n = hashmap_get(bus->nodes, p);
1080 /* First, try object callbacks */
1081 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1084 if (bus->nodes_modified)
1087 if (!m->interface || !m->member)
1090 /* Then, look for a known method */
1091 vtable_key.path = (char*) p;
1092 vtable_key.interface = m->interface;
1093 vtable_key.member = m->member;
1095 v = hashmap_get(bus->vtable_methods, &vtable_key);
1097 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1100 if (bus->nodes_modified)
1104 /* Then, look for a known property */
1105 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1108 get = streq(m->member, "Get");
1110 if (get || streq(m->member, "Set")) {
1112 r = sd_bus_message_rewind(m, true);
1116 vtable_key.path = (char*) p;
1118 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1122 v = hashmap_get(bus->vtable_properties, &vtable_key);
1124 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1129 } else if (streq(m->member, "GetAll")) {
1132 r = sd_bus_message_rewind(m, true);
1136 r = sd_bus_message_read(m, "s", &iface);
1143 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1148 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1150 r = process_introspect(bus, m, n, require_fallback, found_object);
1154 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1156 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1161 if (bus->nodes_modified)
1164 if (!*found_object) {
1165 r = bus_node_exists(bus, n, m->path, require_fallback);
1169 *found_object = true;
1175 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1178 bool found_object = false;
1183 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1189 if (hashmap_isempty(bus->nodes))
1192 pl = strlen(m->path);
1196 bus->nodes_modified = false;
1198 r = object_find_and_run(bus, m, m->path, false, &found_object);
1202 /* Look for fallback prefixes */
1203 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1205 if (bus->nodes_modified)
1208 r = object_find_and_run(bus, m, prefix, true, &found_object);
1213 } while (bus->nodes_modified);
1218 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1219 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1220 r = sd_bus_reply_method_errorf(
1222 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1223 "Unknown property or interface.");
1225 r = sd_bus_reply_method_errorf(
1227 SD_BUS_ERROR_UNKNOWN_METHOD,
1228 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1236 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1237 struct node *n, *parent;
1244 assert(path[0] == '/');
1246 n = hashmap_get(bus->nodes, path);
1250 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1258 if (streq(path, "/"))
1261 e = strrchr(path, '/');
1264 p = strndupa(path, MAX(1, path - e));
1266 parent = bus_node_allocate(bus, p);
1273 n = new0(struct node, 1);
1280 r = hashmap_put(bus->nodes, s, n);
1288 LIST_PREPEND(siblings, parent->child, n);
1293 static void bus_node_gc(sd_bus *b, struct node *n) {
1306 assert(hashmap_remove(b->nodes, n->path) == n);
1309 LIST_REMOVE(siblings, n->parent->child, n);
1312 bus_node_gc(b, n->parent);
1316 static int bus_add_object(
1320 sd_bus_message_handler_t callback,
1323 struct node_callback *c;
1327 assert_return(bus, -EINVAL);
1328 assert_return(object_path_is_valid(path), -EINVAL);
1329 assert_return(callback, -EINVAL);
1330 assert_return(!bus_pid_changed(bus), -ECHILD);
1332 n = bus_node_allocate(bus, path);
1336 c = new0(struct node_callback, 1);
1343 c->callback = callback;
1344 c->userdata = userdata;
1345 c->is_fallback = fallback;
1347 LIST_PREPEND(callbacks, n->callbacks, c);
1348 bus->nodes_modified = true;
1354 bus_node_gc(bus, n);
1358 static int bus_remove_object(
1362 sd_bus_message_handler_t callback,
1365 struct node_callback *c;
1368 assert_return(bus, -EINVAL);
1369 assert_return(object_path_is_valid(path), -EINVAL);
1370 assert_return(callback, -EINVAL);
1371 assert_return(!bus_pid_changed(bus), -ECHILD);
1373 n = hashmap_get(bus->nodes, path);
1377 LIST_FOREACH(callbacks, c, n->callbacks)
1378 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1383 LIST_REMOVE(callbacks, n->callbacks, c);
1386 bus_node_gc(bus, n);
1387 bus->nodes_modified = true;
1392 _public_ int sd_bus_add_object(sd_bus *bus,
1394 sd_bus_message_handler_t callback,
1397 return bus_add_object(bus, false, path, callback, userdata);
1400 _public_ int sd_bus_remove_object(sd_bus *bus,
1402 sd_bus_message_handler_t callback,
1405 return bus_remove_object(bus, false, path, callback, userdata);
1408 _public_ int sd_bus_add_fallback(sd_bus *bus,
1410 sd_bus_message_handler_t callback,
1413 return bus_add_object(bus, true, prefix, callback, userdata);
1416 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1418 sd_bus_message_handler_t callback,
1421 return bus_remove_object(bus, true, prefix, callback, userdata);
1424 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1430 if (w->interface && w->node && w->vtable) {
1431 const sd_bus_vtable *v;
1433 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1434 struct vtable_member *x = NULL;
1438 case _SD_BUS_VTABLE_METHOD: {
1439 struct vtable_member key;
1441 key.path = w->node->path;
1442 key.interface = w->interface;
1443 key.member = v->x.method.member;
1445 x = hashmap_remove(bus->vtable_methods, &key);
1449 case _SD_BUS_VTABLE_PROPERTY:
1450 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1451 struct vtable_member key;
1453 key.path = w->node->path;
1454 key.interface = w->interface;
1455 key.member = v->x.property.member;
1456 x = hashmap_remove(bus->vtable_properties, &key);
1468 static unsigned vtable_member_hash_func(const void *a) {
1469 const struct vtable_member *m = a;
1474 string_hash_func(m->path) ^
1475 string_hash_func(m->interface) ^
1476 string_hash_func(m->member);
1479 static int vtable_member_compare_func(const void *a, const void *b) {
1480 const struct vtable_member *x = a, *y = b;
1486 r = strcmp(x->path, y->path);
1490 r = strcmp(x->interface, y->interface);
1494 return strcmp(x->member, y->member);
1497 static int add_object_vtable_internal(
1500 const char *interface,
1501 const sd_bus_vtable *vtable,
1503 sd_bus_object_find_t find,
1506 struct node_vtable *c = NULL, *i;
1507 const sd_bus_vtable *v;
1511 assert_return(bus, -EINVAL);
1512 assert_return(object_path_is_valid(path), -EINVAL);
1513 assert_return(interface_name_is_valid(interface), -EINVAL);
1514 assert_return(vtable, -EINVAL);
1515 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1516 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1517 assert_return(!bus_pid_changed(bus), -ECHILD);
1519 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1523 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1527 n = bus_node_allocate(bus, path);
1531 LIST_FOREACH(vtables, i, n->vtables) {
1532 if (streq(i->interface, interface)) {
1537 if (i->is_fallback != fallback) {
1543 c = new0(struct node_vtable, 1);
1550 c->is_fallback = fallback;
1552 c->userdata = userdata;
1555 c->interface = strdup(interface);
1556 if (!c->interface) {
1561 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1565 case _SD_BUS_VTABLE_METHOD: {
1566 struct vtable_member *m;
1568 if (!member_name_is_valid(v->x.method.member) ||
1569 !signature_is_valid(strempty(v->x.method.signature), false) ||
1570 !signature_is_valid(strempty(v->x.method.result), false) ||
1571 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1572 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1577 m = new0(struct vtable_member, 1);
1585 m->interface = c->interface;
1586 m->member = v->x.method.member;
1589 r = hashmap_put(bus->vtable_methods, m, m);
1598 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1600 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1607 case _SD_BUS_VTABLE_PROPERTY: {
1608 struct vtable_member *m;
1610 if (!member_name_is_valid(v->x.property.member) ||
1611 !signature_is_single(v->x.property.signature, false) ||
1612 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1613 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1614 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1620 m = new0(struct vtable_member, 1);
1628 m->interface = c->interface;
1629 m->member = v->x.property.member;
1632 r = hashmap_put(bus->vtable_properties, m, m);
1641 case _SD_BUS_VTABLE_SIGNAL:
1643 if (!member_name_is_valid(v->x.signal.member) ||
1644 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1657 LIST_PREPEND(vtables, n->vtables, c);
1658 bus->nodes_modified = true;
1664 free_node_vtable(bus, c);
1666 bus_node_gc(bus, n);
1670 static int remove_object_vtable_internal(
1673 const char *interface,
1676 struct node_vtable *c;
1679 assert_return(bus, -EINVAL);
1680 assert_return(object_path_is_valid(path), -EINVAL);
1681 assert_return(interface_name_is_valid(interface), -EINVAL);
1682 assert_return(!bus_pid_changed(bus), -ECHILD);
1684 n = hashmap_get(bus->nodes, path);
1688 LIST_FOREACH(vtables, c, n->vtables)
1689 if (streq(c->interface, interface) && c->is_fallback == fallback)
1695 LIST_REMOVE(vtables, n->vtables, c);
1697 free_node_vtable(bus, c);
1698 bus_node_gc(bus, n);
1700 bus->nodes_modified = true;
1705 _public_ int sd_bus_add_object_vtable(
1708 const char *interface,
1709 const sd_bus_vtable *vtable,
1712 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1715 _public_ int sd_bus_remove_object_vtable(
1718 const char *interface) {
1720 return remove_object_vtable_internal(bus, path, interface, false);
1723 _public_ int sd_bus_add_fallback_vtable(
1726 const char *interface,
1727 const sd_bus_vtable *vtable,
1728 sd_bus_object_find_t find,
1731 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1734 _public_ int sd_bus_remove_fallback_vtable(
1737 const char *interface) {
1739 return remove_object_vtable_internal(bus, path, interface, true);
1742 _public_ int sd_bus_add_node_enumerator(
1745 sd_bus_node_enumerator_t callback,
1748 struct node_enumerator *c;
1752 assert_return(bus, -EINVAL);
1753 assert_return(object_path_is_valid(path), -EINVAL);
1754 assert_return(callback, -EINVAL);
1755 assert_return(!bus_pid_changed(bus), -ECHILD);
1757 n = bus_node_allocate(bus, path);
1761 c = new0(struct node_enumerator, 1);
1768 c->callback = callback;
1769 c->userdata = userdata;
1771 LIST_PREPEND(enumerators, n->enumerators, c);
1773 bus->nodes_modified = true;
1779 bus_node_gc(bus, n);
1783 _public_ int sd_bus_remove_node_enumerator(
1786 sd_bus_node_enumerator_t callback,
1789 struct node_enumerator *c;
1792 assert_return(bus, -EINVAL);
1793 assert_return(object_path_is_valid(path), -EINVAL);
1794 assert_return(callback, -EINVAL);
1795 assert_return(!bus_pid_changed(bus), -ECHILD);
1797 n = hashmap_get(bus->nodes, path);
1801 LIST_FOREACH(enumerators, c, n->enumerators)
1802 if (c->callback == callback && c->userdata == userdata)
1808 LIST_REMOVE(enumerators, n->enumerators, c);
1811 bus_node_gc(bus, n);
1813 bus->nodes_modified = true;
1818 static int emit_properties_changed_on_interface(
1822 const char *interface,
1823 bool require_fallback,
1826 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1827 bool has_invalidating = false;
1828 struct vtable_member key;
1829 struct node_vtable *c;
1840 n = hashmap_get(bus->nodes, prefix);
1844 LIST_FOREACH(vtables, c, n->vtables) {
1845 if (require_fallback && !c->is_fallback)
1848 if (streq(c->interface, interface))
1855 r = node_vtable_get_userdata(bus, path, c, &u);
1858 if (bus->nodes_modified)
1861 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1865 r = sd_bus_message_append(m, "s", interface);
1869 r = sd_bus_message_open_container(m, 'a', "{sv}");
1874 key.interface = interface;
1876 STRV_FOREACH(property, names) {
1877 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1878 struct vtable_member *v;
1880 assert_return(member_name_is_valid(*property), -EINVAL);
1882 key.member = *property;
1883 v = hashmap_get(bus->vtable_properties, &key);
1887 assert(c == v->parent);
1888 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1890 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1891 has_invalidating = true;
1895 r = sd_bus_message_open_container(m, 'e', "sv");
1899 r = sd_bus_message_append(m, "s", *property);
1903 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1907 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1910 if (bus->nodes_modified)
1913 r = sd_bus_message_close_container(m);
1917 r = sd_bus_message_close_container(m);
1922 r = sd_bus_message_close_container(m);
1926 r = sd_bus_message_open_container(m, 'a', "s");
1930 if (has_invalidating) {
1931 STRV_FOREACH(property, names) {
1932 struct vtable_member *v;
1934 key.member = *property;
1935 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1936 assert(c == v->parent);
1938 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1941 r = sd_bus_message_append(m, "s", *property);
1947 r = sd_bus_message_close_container(m);
1951 r = sd_bus_send(bus, m, NULL);
1958 _public_ int sd_bus_emit_properties_changed_strv(
1961 const char *interface,
1964 BUS_DONT_DESTROY(bus);
1968 assert_return(bus, -EINVAL);
1969 assert_return(object_path_is_valid(path), -EINVAL);
1970 assert_return(interface_name_is_valid(interface), -EINVAL);
1971 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1972 assert_return(!bus_pid_changed(bus), -ECHILD);
1974 if (strv_isempty(names))
1978 bus->nodes_modified = false;
1980 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1983 if (bus->nodes_modified)
1986 prefix = alloca(strlen(path) + 1);
1987 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1988 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1991 if (bus->nodes_modified)
1995 } while (bus->nodes_modified);
2000 _public_ int sd_bus_emit_properties_changed(
2003 const char *interface,
2004 const char *name, ...) {
2008 assert_return(bus, -EINVAL);
2009 assert_return(object_path_is_valid(path), -EINVAL);
2010 assert_return(interface_name_is_valid(interface), -EINVAL);
2011 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2012 assert_return(!bus_pid_changed(bus), -ECHILD);
2017 names = strv_from_stdarg_alloca(name);
2019 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2022 static int interfaces_added_append_one_prefix(
2027 const char *interface,
2028 bool require_fallback) {
2030 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2031 struct node_vtable *c;
2042 n = hashmap_get(bus->nodes, prefix);
2046 LIST_FOREACH(vtables, c, n->vtables) {
2047 if (require_fallback && !c->is_fallback)
2050 if (streq(c->interface, interface))
2057 r = node_vtable_get_userdata(bus, path, c, &u);
2060 if (bus->nodes_modified)
2063 r = sd_bus_message_append_basic(m, 's', interface);
2067 r = sd_bus_message_open_container(m, 'a', "{sv}");
2071 r = vtable_append_all_properties(bus, m,path, c, u, &error);
2074 if (bus->nodes_modified)
2077 r = sd_bus_message_close_container(m);
2084 static int interfaces_added_append_one(
2088 const char *interface) {
2098 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2101 if (bus->nodes_modified)
2104 prefix = alloca(strlen(path) + 1);
2105 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2106 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2109 if (bus->nodes_modified)
2116 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2117 BUS_DONT_DESTROY(bus);
2119 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2123 assert_return(bus, -EINVAL);
2124 assert_return(object_path_is_valid(path), -EINVAL);
2125 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2126 assert_return(!bus_pid_changed(bus), -ECHILD);
2128 if (strv_isempty(interfaces))
2132 bus->nodes_modified = false;
2135 m = sd_bus_message_unref(m);
2137 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2141 r = sd_bus_message_append_basic(m, 'o', path);
2145 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2149 STRV_FOREACH(i, interfaces) {
2150 assert_return(interface_name_is_valid(*i), -EINVAL);
2152 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2156 r = interfaces_added_append_one(bus, m, path, *i);
2160 if (bus->nodes_modified)
2163 r = sd_bus_message_close_container(m);
2168 if (bus->nodes_modified)
2171 r = sd_bus_message_close_container(m);
2175 } while (bus->nodes_modified);
2177 return sd_bus_send(bus, m, NULL);
2180 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
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 interfaces = strv_from_stdarg_alloca(interface);
2190 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2193 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2194 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2197 assert_return(bus, -EINVAL);
2198 assert_return(object_path_is_valid(path), -EINVAL);
2199 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2200 assert_return(!bus_pid_changed(bus), -ECHILD);
2202 if (strv_isempty(interfaces))
2205 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2209 r = sd_bus_message_append_basic(m, 'o', path);
2213 r = sd_bus_message_append_strv(m, interfaces);
2217 return sd_bus_send(bus, m, NULL);
2220 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2223 assert_return(bus, -EINVAL);
2224 assert_return(object_path_is_valid(path), -EINVAL);
2225 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2226 assert_return(!bus_pid_changed(bus), -ECHILD);
2228 interfaces = strv_from_stdarg_alloca(interface);
2230 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2233 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2236 assert_return(bus, -EINVAL);
2237 assert_return(object_path_is_valid(path), -EINVAL);
2238 assert_return(!bus_pid_changed(bus), -ECHILD);
2240 n = bus_node_allocate(bus, path);
2244 n->object_manager = true;
2245 bus->nodes_modified = true;
2249 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2252 assert_return(bus, -EINVAL);
2253 assert_return(object_path_is_valid(path), -EINVAL);
2254 assert_return(!bus_pid_changed(bus), -ECHILD);
2256 n = hashmap_get(bus->nodes, path);
2260 if (!n->object_manager)
2263 n->object_manager = false;
2264 bus->nodes_modified = true;
2265 bus_node_gc(bus, n);