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 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1393 return bus_add_object(bus, false, path, callback, userdata);
1396 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1397 return bus_remove_object(bus, false, path, callback, userdata);
1400 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1401 return bus_add_object(bus, true, prefix, callback, userdata);
1404 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1405 return bus_remove_object(bus, true, prefix, callback, userdata);
1408 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1414 if (w->interface && w->node && w->vtable) {
1415 const sd_bus_vtable *v;
1417 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1418 struct vtable_member *x = NULL;
1422 case _SD_BUS_VTABLE_METHOD: {
1423 struct vtable_member key;
1425 key.path = w->node->path;
1426 key.interface = w->interface;
1427 key.member = v->x.method.member;
1429 x = hashmap_remove(bus->vtable_methods, &key);
1433 case _SD_BUS_VTABLE_PROPERTY:
1434 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1435 struct vtable_member key;
1437 key.path = w->node->path;
1438 key.interface = w->interface;
1439 key.member = v->x.property.member;
1440 x = hashmap_remove(bus->vtable_properties, &key);
1452 static unsigned vtable_member_hash_func(const void *a) {
1453 const struct vtable_member *m = a;
1458 string_hash_func(m->path) ^
1459 string_hash_func(m->interface) ^
1460 string_hash_func(m->member);
1463 static int vtable_member_compare_func(const void *a, const void *b) {
1464 const struct vtable_member *x = a, *y = b;
1470 r = strcmp(x->path, y->path);
1474 r = strcmp(x->interface, y->interface);
1478 return strcmp(x->member, y->member);
1481 static int add_object_vtable_internal(
1484 const char *interface,
1485 const sd_bus_vtable *vtable,
1487 sd_bus_object_find_t find,
1490 struct node_vtable *c = NULL, *i;
1491 const sd_bus_vtable *v;
1495 assert_return(bus, -EINVAL);
1496 assert_return(object_path_is_valid(path), -EINVAL);
1497 assert_return(interface_name_is_valid(interface), -EINVAL);
1498 assert_return(vtable, -EINVAL);
1499 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1500 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1501 assert_return(!bus_pid_changed(bus), -ECHILD);
1503 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1507 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1511 n = bus_node_allocate(bus, path);
1515 LIST_FOREACH(vtables, i, n->vtables) {
1516 if (streq(i->interface, interface)) {
1521 if (i->is_fallback != fallback) {
1527 c = new0(struct node_vtable, 1);
1534 c->is_fallback = fallback;
1536 c->userdata = userdata;
1539 c->interface = strdup(interface);
1540 if (!c->interface) {
1545 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1549 case _SD_BUS_VTABLE_METHOD: {
1550 struct vtable_member *m;
1552 if (!member_name_is_valid(v->x.method.member) ||
1553 !signature_is_valid(strempty(v->x.method.signature), false) ||
1554 !signature_is_valid(strempty(v->x.method.result), false) ||
1555 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1556 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1561 m = new0(struct vtable_member, 1);
1569 m->interface = c->interface;
1570 m->member = v->x.method.member;
1573 r = hashmap_put(bus->vtable_methods, m, m);
1582 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1584 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1591 case _SD_BUS_VTABLE_PROPERTY: {
1592 struct vtable_member *m;
1594 if (!member_name_is_valid(v->x.property.member) ||
1595 !signature_is_single(v->x.property.signature, false) ||
1596 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1597 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1598 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1604 m = new0(struct vtable_member, 1);
1612 m->interface = c->interface;
1613 m->member = v->x.property.member;
1616 r = hashmap_put(bus->vtable_properties, m, m);
1625 case _SD_BUS_VTABLE_SIGNAL:
1627 if (!member_name_is_valid(v->x.signal.member) ||
1628 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1641 LIST_PREPEND(vtables, n->vtables, c);
1642 bus->nodes_modified = true;
1648 free_node_vtable(bus, c);
1650 bus_node_gc(bus, n);
1654 static int remove_object_vtable_internal(
1657 const char *interface,
1660 struct node_vtable *c;
1663 assert_return(bus, -EINVAL);
1664 assert_return(object_path_is_valid(path), -EINVAL);
1665 assert_return(interface_name_is_valid(interface), -EINVAL);
1666 assert_return(!bus_pid_changed(bus), -ECHILD);
1668 n = hashmap_get(bus->nodes, path);
1672 LIST_FOREACH(vtables, c, n->vtables)
1673 if (streq(c->interface, interface) && c->is_fallback == fallback)
1679 LIST_REMOVE(vtables, n->vtables, c);
1681 free_node_vtable(bus, c);
1682 bus_node_gc(bus, n);
1684 bus->nodes_modified = true;
1689 int sd_bus_add_object_vtable(
1692 const char *interface,
1693 const sd_bus_vtable *vtable,
1696 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1699 int sd_bus_remove_object_vtable(
1702 const char *interface) {
1704 return remove_object_vtable_internal(bus, path, interface, false);
1707 int sd_bus_add_fallback_vtable(
1710 const char *interface,
1711 const sd_bus_vtable *vtable,
1712 sd_bus_object_find_t find,
1715 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1718 int sd_bus_remove_fallback_vtable(
1721 const char *interface) {
1723 return remove_object_vtable_internal(bus, path, interface, true);
1726 int sd_bus_add_node_enumerator(
1729 sd_bus_node_enumerator_t callback,
1732 struct node_enumerator *c;
1736 assert_return(bus, -EINVAL);
1737 assert_return(object_path_is_valid(path), -EINVAL);
1738 assert_return(callback, -EINVAL);
1739 assert_return(!bus_pid_changed(bus), -ECHILD);
1741 n = bus_node_allocate(bus, path);
1745 c = new0(struct node_enumerator, 1);
1752 c->callback = callback;
1753 c->userdata = userdata;
1755 LIST_PREPEND(enumerators, n->enumerators, c);
1757 bus->nodes_modified = true;
1763 bus_node_gc(bus, n);
1767 int sd_bus_remove_node_enumerator(
1770 sd_bus_node_enumerator_t callback,
1773 struct node_enumerator *c;
1776 assert_return(bus, -EINVAL);
1777 assert_return(object_path_is_valid(path), -EINVAL);
1778 assert_return(callback, -EINVAL);
1779 assert_return(!bus_pid_changed(bus), -ECHILD);
1781 n = hashmap_get(bus->nodes, path);
1785 LIST_FOREACH(enumerators, c, n->enumerators)
1786 if (c->callback == callback && c->userdata == userdata)
1792 LIST_REMOVE(enumerators, n->enumerators, c);
1795 bus_node_gc(bus, n);
1797 bus->nodes_modified = true;
1802 static int emit_properties_changed_on_interface(
1806 const char *interface,
1807 bool require_fallback,
1810 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1811 bool has_invalidating = false;
1812 struct vtable_member key;
1813 struct node_vtable *c;
1824 n = hashmap_get(bus->nodes, prefix);
1828 LIST_FOREACH(vtables, c, n->vtables) {
1829 if (require_fallback && !c->is_fallback)
1832 if (streq(c->interface, interface))
1839 r = node_vtable_get_userdata(bus, path, c, &u);
1842 if (bus->nodes_modified)
1845 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1849 r = sd_bus_message_append(m, "s", interface);
1853 r = sd_bus_message_open_container(m, 'a', "{sv}");
1858 key.interface = interface;
1860 STRV_FOREACH(property, names) {
1861 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1862 struct vtable_member *v;
1864 assert_return(member_name_is_valid(*property), -EINVAL);
1866 key.member = *property;
1867 v = hashmap_get(bus->vtable_properties, &key);
1871 assert(c == v->parent);
1872 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1874 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1875 has_invalidating = true;
1879 r = sd_bus_message_open_container(m, 'e', "sv");
1883 r = sd_bus_message_append(m, "s", *property);
1887 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1891 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1894 if (bus->nodes_modified)
1897 r = sd_bus_message_close_container(m);
1901 r = sd_bus_message_close_container(m);
1906 r = sd_bus_message_close_container(m);
1910 r = sd_bus_message_open_container(m, 'a', "s");
1914 if (has_invalidating) {
1915 STRV_FOREACH(property, names) {
1916 struct vtable_member *v;
1918 key.member = *property;
1919 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1920 assert(c == v->parent);
1922 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1925 r = sd_bus_message_append(m, "s", *property);
1931 r = sd_bus_message_close_container(m);
1935 r = sd_bus_send(bus, m, NULL);
1942 int sd_bus_emit_properties_changed_strv(
1945 const char *interface,
1948 BUS_DONT_DESTROY(bus);
1952 assert_return(bus, -EINVAL);
1953 assert_return(object_path_is_valid(path), -EINVAL);
1954 assert_return(interface_name_is_valid(interface), -EINVAL);
1955 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1956 assert_return(!bus_pid_changed(bus), -ECHILD);
1958 if (strv_isempty(names))
1962 bus->nodes_modified = false;
1964 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1967 if (bus->nodes_modified)
1970 prefix = alloca(strlen(path) + 1);
1971 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1972 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1975 if (bus->nodes_modified)
1979 } while (bus->nodes_modified);
1984 int sd_bus_emit_properties_changed(
1987 const char *interface,
1988 const char *name, ...) {
1992 assert_return(bus, -EINVAL);
1993 assert_return(object_path_is_valid(path), -EINVAL);
1994 assert_return(interface_name_is_valid(interface), -EINVAL);
1995 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1996 assert_return(!bus_pid_changed(bus), -ECHILD);
2001 names = strv_from_stdarg_alloca(name);
2003 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2006 static int interfaces_added_append_one_prefix(
2011 const char *interface,
2012 bool require_fallback) {
2014 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2015 struct node_vtable *c;
2026 n = hashmap_get(bus->nodes, prefix);
2030 LIST_FOREACH(vtables, c, n->vtables) {
2031 if (require_fallback && !c->is_fallback)
2034 if (streq(c->interface, interface))
2041 r = node_vtable_get_userdata(bus, path, c, &u);
2044 if (bus->nodes_modified)
2047 r = sd_bus_message_append_basic(m, 's', interface);
2051 r = sd_bus_message_open_container(m, 'a', "{sv}");
2055 r = vtable_append_all_properties(bus, m,path, c, u, &error);
2058 if (bus->nodes_modified)
2061 r = sd_bus_message_close_container(m);
2068 static int interfaces_added_append_one(
2072 const char *interface) {
2082 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2085 if (bus->nodes_modified)
2088 prefix = alloca(strlen(path) + 1);
2089 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2090 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2093 if (bus->nodes_modified)
2100 int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2101 BUS_DONT_DESTROY(bus);
2103 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2107 assert_return(bus, -EINVAL);
2108 assert_return(object_path_is_valid(path), -EINVAL);
2109 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2110 assert_return(!bus_pid_changed(bus), -ECHILD);
2112 if (strv_isempty(interfaces))
2116 bus->nodes_modified = false;
2119 m = sd_bus_message_unref(m);
2121 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2125 r = sd_bus_message_append_basic(m, 'o', path);
2129 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2133 STRV_FOREACH(i, interfaces) {
2134 assert_return(interface_name_is_valid(*i), -EINVAL);
2136 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2140 r = interfaces_added_append_one(bus, m, path, *i);
2144 if (bus->nodes_modified)
2147 r = sd_bus_message_close_container(m);
2152 if (bus->nodes_modified)
2155 r = sd_bus_message_close_container(m);
2159 } while (bus->nodes_modified);
2161 return sd_bus_send(bus, m, NULL);
2164 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2167 assert_return(bus, -EINVAL);
2168 assert_return(object_path_is_valid(path), -EINVAL);
2169 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2170 assert_return(!bus_pid_changed(bus), -ECHILD);
2172 interfaces = strv_from_stdarg_alloca(interface);
2174 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2177 int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2178 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2181 assert_return(bus, -EINVAL);
2182 assert_return(object_path_is_valid(path), -EINVAL);
2183 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2184 assert_return(!bus_pid_changed(bus), -ECHILD);
2186 if (strv_isempty(interfaces))
2189 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2193 r = sd_bus_message_append_basic(m, 'o', path);
2197 r = sd_bus_message_append_strv(m, interfaces);
2201 return sd_bus_send(bus, m, NULL);
2204 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2207 assert_return(bus, -EINVAL);
2208 assert_return(object_path_is_valid(path), -EINVAL);
2209 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2210 assert_return(!bus_pid_changed(bus), -ECHILD);
2212 interfaces = strv_from_stdarg_alloca(interface);
2214 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2217 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2220 assert_return(bus, -EINVAL);
2221 assert_return(object_path_is_valid(path), -EINVAL);
2222 assert_return(!bus_pid_changed(bus), -ECHILD);
2224 n = bus_node_allocate(bus, path);
2228 n->object_manager = true;
2229 bus->nodes_modified = true;
2233 int sd_bus_remove_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 = hashmap_get(bus->nodes, path);
2244 if (!n->object_manager)
2247 n->object_manager = false;
2248 bus->nodes_modified = true;
2249 bus_node_gc(bus, n);