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(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(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(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(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(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(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(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 const char *previous_interface = NULL;
732 struct introspect intro;
733 struct node_vtable *c;
740 assert(found_object);
742 r = get_child_nodes(bus, m->path, n, &s);
745 if (bus->nodes_modified)
748 r = introspect_begin(&intro);
752 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
756 empty = set_isempty(s);
758 LIST_FOREACH(vtables, c, n->vtables) {
759 if (require_fallback && !c->is_fallback)
762 r = node_vtable_get_userdata(bus, m->path, c, NULL);
765 if (bus->nodes_modified)
772 if (!streq_ptr(previous_interface, c->interface)) {
774 if (previous_interface)
775 fputs(" </interface>\n", intro.f);
777 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
780 r = introspect_write_interface(&intro, c->vtable);
784 previous_interface = c->interface;
787 if (previous_interface)
788 fputs(" </interface>\n", intro.f);
791 /* Nothing?, let's see if we exist at all, and if not
792 * refuse to do anything */
793 r = bus_node_exists(bus, n, m->path, require_fallback);
796 if (bus->nodes_modified)
802 *found_object = true;
804 r = introspect_write_child_nodes(&intro, s, m->path);
808 r = introspect_finish(&intro, bus, m, &reply);
812 r = sd_bus_send(bus, reply, NULL);
819 introspect_free(&intro);
823 static int object_manager_serialize_path(
825 sd_bus_message *reply,
828 bool require_fallback,
829 sd_bus_error *error) {
831 const char *previous_interface = NULL;
832 bool found_something = false;
833 struct node_vtable *i;
843 n = hashmap_get(bus->nodes, prefix);
847 LIST_FOREACH(vtables, i, n->vtables) {
850 if (require_fallback && !i->is_fallback)
853 r = node_vtable_get_userdata(bus, path, i, &u);
856 if (bus->nodes_modified)
861 if (!found_something) {
863 /* Open the object part */
865 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
869 r = sd_bus_message_append(reply, "o", path);
873 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
877 found_something = true;
880 if (!streq_ptr(previous_interface, i->interface)) {
882 /* Maybe close the previous interface part */
884 if (previous_interface) {
885 r = sd_bus_message_close_container(reply);
889 r = sd_bus_message_close_container(reply);
894 /* Open the new interface part */
896 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
900 r = sd_bus_message_append(reply, "s", i->interface);
904 r = sd_bus_message_open_container(reply, 'a', "{sv}");
909 r = vtable_append_all_properties(bus, reply, path, i, u, error);
912 if (sd_bus_error_is_set(error))
914 if (bus->nodes_modified)
917 previous_interface = i->interface;
920 if (previous_interface) {
921 r = sd_bus_message_close_container(reply);
925 r = sd_bus_message_close_container(reply);
930 if (found_something) {
931 r = sd_bus_message_close_container(reply);
935 r = sd_bus_message_close_container(reply);
943 static int object_manager_serialize_path_and_fallbacks(
945 sd_bus_message *reply,
947 sd_bus_error *error) {
957 /* First, add all vtables registered for this path */
958 r = object_manager_serialize_path(bus, reply, path, path, false, error);
961 if (sd_bus_error_is_set(error))
963 if (bus->nodes_modified)
966 /* Second, add fallback vtables registered for any of the prefixes */
967 prefix = alloca(strlen(path) + 1);
968 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
969 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
972 if (sd_bus_error_is_set(error))
974 if (bus->nodes_modified)
981 static int process_get_managed_objects(
985 bool require_fallback,
986 bool *found_object) {
988 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
989 _cleanup_set_free_free_ Set *s = NULL;
996 assert(found_object);
998 if (!bus_node_with_object_manager(bus, n))
1001 r = get_child_nodes(bus, m->path, n, &s);
1004 if (bus->nodes_modified)
1007 r = sd_bus_message_new_method_return(m, &reply);
1011 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1015 empty = set_isempty(s);
1017 struct node_vtable *c;
1019 /* Hmm, so we have no children? Then let's check
1020 * whether we exist at all, i.e. whether at least one
1023 LIST_FOREACH(vtables, c, n->vtables) {
1025 if (require_fallback && !c->is_fallback)
1043 SET_FOREACH(path, s, i) {
1044 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1046 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1050 if (sd_bus_error_is_set(&error)) {
1051 r = sd_bus_reply_method_error(m, &error);
1058 if (bus->nodes_modified)
1063 r = sd_bus_message_close_container(reply);
1067 r = sd_bus_send(bus, reply, NULL);
1074 static int object_find_and_run(
1078 bool require_fallback,
1079 bool *found_object) {
1082 struct vtable_member vtable_key, *v;
1088 assert(found_object);
1090 n = hashmap_get(bus->nodes, p);
1094 /* First, try object callbacks */
1095 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1098 if (bus->nodes_modified)
1101 if (!m->interface || !m->member)
1104 /* Then, look for a known method */
1105 vtable_key.path = (char*) p;
1106 vtable_key.interface = m->interface;
1107 vtable_key.member = m->member;
1109 v = hashmap_get(bus->vtable_methods, &vtable_key);
1111 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1114 if (bus->nodes_modified)
1118 /* Then, look for a known property */
1119 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1122 get = streq(m->member, "Get");
1124 if (get || streq(m->member, "Set")) {
1126 r = sd_bus_message_rewind(m, true);
1130 vtable_key.path = (char*) p;
1132 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1136 v = hashmap_get(bus->vtable_properties, &vtable_key);
1138 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1143 } else if (streq(m->member, "GetAll")) {
1146 r = sd_bus_message_rewind(m, true);
1150 r = sd_bus_message_read(m, "s", &iface);
1157 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1162 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1164 r = process_introspect(bus, m, n, require_fallback, found_object);
1168 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1170 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1175 if (bus->nodes_modified)
1178 if (!*found_object) {
1179 r = bus_node_exists(bus, n, m->path, require_fallback);
1183 *found_object = true;
1189 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1192 bool found_object = false;
1197 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1203 if (hashmap_isempty(bus->nodes))
1206 pl = strlen(m->path);
1210 bus->nodes_modified = false;
1212 r = object_find_and_run(bus, m, m->path, false, &found_object);
1216 /* Look for fallback prefixes */
1217 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1219 if (bus->nodes_modified)
1222 r = object_find_and_run(bus, m, prefix, true, &found_object);
1227 } while (bus->nodes_modified);
1232 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1233 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1234 r = sd_bus_reply_method_errorf(
1236 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1237 "Unknown property or interface.");
1239 r = sd_bus_reply_method_errorf(
1241 SD_BUS_ERROR_UNKNOWN_METHOD,
1242 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1250 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1251 struct node *n, *parent;
1258 assert(path[0] == '/');
1260 n = hashmap_get(bus->nodes, path);
1264 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1272 if (streq(path, "/"))
1275 e = strrchr(path, '/');
1278 p = strndupa(path, MAX(1, path - e));
1280 parent = bus_node_allocate(bus, p);
1287 n = new0(struct node, 1);
1294 r = hashmap_put(bus->nodes, s, n);
1302 LIST_PREPEND(siblings, parent->child, n);
1307 static void bus_node_gc(sd_bus *b, struct node *n) {
1320 assert(hashmap_remove(b->nodes, n->path) == n);
1323 LIST_REMOVE(siblings, n->parent->child, n);
1326 bus_node_gc(b, n->parent);
1330 static int bus_add_object(
1334 sd_bus_message_handler_t callback,
1337 struct node_callback *c;
1341 assert_return(bus, -EINVAL);
1342 assert_return(object_path_is_valid(path), -EINVAL);
1343 assert_return(callback, -EINVAL);
1344 assert_return(!bus_pid_changed(bus), -ECHILD);
1346 n = bus_node_allocate(bus, path);
1350 c = new0(struct node_callback, 1);
1357 c->callback = callback;
1358 c->userdata = userdata;
1359 c->is_fallback = fallback;
1361 LIST_PREPEND(callbacks, n->callbacks, c);
1362 bus->nodes_modified = true;
1368 bus_node_gc(bus, n);
1372 static int bus_remove_object(
1376 sd_bus_message_handler_t callback,
1379 struct node_callback *c;
1382 assert_return(bus, -EINVAL);
1383 assert_return(object_path_is_valid(path), -EINVAL);
1384 assert_return(callback, -EINVAL);
1385 assert_return(!bus_pid_changed(bus), -ECHILD);
1387 n = hashmap_get(bus->nodes, path);
1391 LIST_FOREACH(callbacks, c, n->callbacks)
1392 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1397 LIST_REMOVE(callbacks, n->callbacks, c);
1400 bus_node_gc(bus, n);
1401 bus->nodes_modified = true;
1406 _public_ int sd_bus_add_object(sd_bus *bus,
1408 sd_bus_message_handler_t callback,
1411 return bus_add_object(bus, false, path, callback, userdata);
1414 _public_ int sd_bus_remove_object(sd_bus *bus,
1416 sd_bus_message_handler_t callback,
1419 return bus_remove_object(bus, false, path, callback, userdata);
1422 _public_ int sd_bus_add_fallback(sd_bus *bus,
1424 sd_bus_message_handler_t callback,
1427 return bus_add_object(bus, true, prefix, callback, userdata);
1430 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1432 sd_bus_message_handler_t callback,
1435 return bus_remove_object(bus, true, prefix, callback, userdata);
1438 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1444 if (w->interface && w->node && w->vtable) {
1445 const sd_bus_vtable *v;
1447 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1448 struct vtable_member *x = NULL;
1452 case _SD_BUS_VTABLE_METHOD: {
1453 struct vtable_member key;
1455 key.path = w->node->path;
1456 key.interface = w->interface;
1457 key.member = v->x.method.member;
1459 x = hashmap_remove(bus->vtable_methods, &key);
1463 case _SD_BUS_VTABLE_PROPERTY:
1464 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1465 struct vtable_member key;
1467 key.path = w->node->path;
1468 key.interface = w->interface;
1469 key.member = v->x.property.member;
1470 x = hashmap_remove(bus->vtable_properties, &key);
1482 static unsigned vtable_member_hash_func(const void *a) {
1483 const struct vtable_member *m = a;
1488 string_hash_func(m->path) ^
1489 string_hash_func(m->interface) ^
1490 string_hash_func(m->member);
1493 static int vtable_member_compare_func(const void *a, const void *b) {
1494 const struct vtable_member *x = a, *y = b;
1500 r = strcmp(x->path, y->path);
1504 r = strcmp(x->interface, y->interface);
1508 return strcmp(x->member, y->member);
1511 static int add_object_vtable_internal(
1514 const char *interface,
1515 const sd_bus_vtable *vtable,
1517 sd_bus_object_find_t find,
1520 struct node_vtable *c = NULL, *i, *existing = NULL;
1521 const sd_bus_vtable *v;
1525 assert_return(bus, -EINVAL);
1526 assert_return(object_path_is_valid(path), -EINVAL);
1527 assert_return(interface_name_is_valid(interface), -EINVAL);
1528 assert_return(vtable, -EINVAL);
1529 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1530 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1531 assert_return(!bus_pid_changed(bus), -ECHILD);
1532 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1533 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1534 !streq(interface, "org.freedesktop.DBus.Peer") &&
1535 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1537 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1541 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1545 n = bus_node_allocate(bus, path);
1549 LIST_FOREACH(vtables, i, n->vtables) {
1550 if (i->is_fallback != fallback) {
1555 if (streq(i->interface, interface)) {
1557 if (i->vtable == vtable) {
1566 c = new0(struct node_vtable, 1);
1573 c->is_fallback = fallback;
1575 c->userdata = userdata;
1578 c->interface = strdup(interface);
1579 if (!c->interface) {
1584 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1588 case _SD_BUS_VTABLE_METHOD: {
1589 struct vtable_member *m;
1591 if (!member_name_is_valid(v->x.method.member) ||
1592 !signature_is_valid(strempty(v->x.method.signature), false) ||
1593 !signature_is_valid(strempty(v->x.method.result), false) ||
1594 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1595 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1600 m = new0(struct vtable_member, 1);
1608 m->interface = c->interface;
1609 m->member = v->x.method.member;
1612 r = hashmap_put(bus->vtable_methods, m, m);
1621 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1623 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1630 case _SD_BUS_VTABLE_PROPERTY: {
1631 struct vtable_member *m;
1633 if (!member_name_is_valid(v->x.property.member) ||
1634 !signature_is_single(v->x.property.signature, false) ||
1635 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1636 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1637 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1643 m = new0(struct vtable_member, 1);
1651 m->interface = c->interface;
1652 m->member = v->x.property.member;
1655 r = hashmap_put(bus->vtable_properties, m, m);
1664 case _SD_BUS_VTABLE_SIGNAL:
1666 if (!member_name_is_valid(v->x.signal.member) ||
1667 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1680 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1681 bus->nodes_modified = true;
1687 free_node_vtable(bus, c);
1689 bus_node_gc(bus, n);
1693 static int remove_object_vtable_internal(
1696 const char *interface,
1697 const sd_bus_vtable *vtable,
1699 sd_bus_object_find_t find,
1702 struct node_vtable *c;
1705 assert_return(bus, -EINVAL);
1706 assert_return(object_path_is_valid(path), -EINVAL);
1707 assert_return(interface_name_is_valid(interface), -EINVAL);
1708 assert_return(!bus_pid_changed(bus), -ECHILD);
1710 n = hashmap_get(bus->nodes, path);
1714 LIST_FOREACH(vtables, c, n->vtables)
1715 if (streq(c->interface, interface) &&
1716 c->is_fallback == fallback &&
1717 c->vtable == vtable &&
1719 c->userdata == userdata)
1725 LIST_REMOVE(vtables, n->vtables, c);
1727 free_node_vtable(bus, c);
1728 bus_node_gc(bus, n);
1730 bus->nodes_modified = true;
1735 _public_ int sd_bus_add_object_vtable(
1738 const char *interface,
1739 const sd_bus_vtable *vtable,
1742 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1745 _public_ int sd_bus_remove_object_vtable(
1748 const char *interface,
1749 const sd_bus_vtable *vtable,
1752 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1755 _public_ int sd_bus_add_fallback_vtable(
1758 const char *interface,
1759 const sd_bus_vtable *vtable,
1760 sd_bus_object_find_t find,
1763 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1766 _public_ int sd_bus_remove_fallback_vtable(
1769 const char *interface,
1770 const sd_bus_vtable *vtable,
1771 sd_bus_object_find_t find,
1774 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1777 _public_ int sd_bus_add_node_enumerator(
1780 sd_bus_node_enumerator_t callback,
1783 struct node_enumerator *c;
1787 assert_return(bus, -EINVAL);
1788 assert_return(object_path_is_valid(path), -EINVAL);
1789 assert_return(callback, -EINVAL);
1790 assert_return(!bus_pid_changed(bus), -ECHILD);
1792 n = bus_node_allocate(bus, path);
1796 c = new0(struct node_enumerator, 1);
1803 c->callback = callback;
1804 c->userdata = userdata;
1806 LIST_PREPEND(enumerators, n->enumerators, c);
1808 bus->nodes_modified = true;
1814 bus_node_gc(bus, n);
1818 _public_ int sd_bus_remove_node_enumerator(
1821 sd_bus_node_enumerator_t callback,
1824 struct node_enumerator *c;
1827 assert_return(bus, -EINVAL);
1828 assert_return(object_path_is_valid(path), -EINVAL);
1829 assert_return(callback, -EINVAL);
1830 assert_return(!bus_pid_changed(bus), -ECHILD);
1832 n = hashmap_get(bus->nodes, path);
1836 LIST_FOREACH(enumerators, c, n->enumerators)
1837 if (c->callback == callback && c->userdata == userdata)
1843 LIST_REMOVE(enumerators, n->enumerators, c);
1846 bus_node_gc(bus, n);
1848 bus->nodes_modified = true;
1853 static int emit_properties_changed_on_interface(
1857 const char *interface,
1858 bool require_fallback,
1861 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1862 bool has_invalidating = false, has_changing = false;
1863 struct vtable_member key = {};
1864 struct node_vtable *c;
1875 n = hashmap_get(bus->nodes, prefix);
1879 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1883 r = sd_bus_message_append(m, "s", interface);
1887 r = sd_bus_message_open_container(m, 'a', "{sv}");
1892 key.interface = interface;
1894 LIST_FOREACH(vtables, c, n->vtables) {
1895 if (require_fallback && !c->is_fallback)
1898 if (!streq(c->interface, interface))
1901 r = node_vtable_get_userdata(bus, path, c, &u);
1904 if (bus->nodes_modified)
1909 STRV_FOREACH(property, names) {
1910 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1911 struct vtable_member *v;
1913 assert_return(member_name_is_valid(*property), -EINVAL);
1915 key.member = *property;
1916 v = hashmap_get(bus->vtable_properties, &key);
1920 /* If there are two vtables for the same
1921 * interface, let's handle this property when
1922 * we come to that vtable. */
1926 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1928 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1929 has_invalidating = true;
1933 has_changing = true;
1935 r = sd_bus_message_open_container(m, 'e', "sv");
1939 r = sd_bus_message_append(m, "s", *property);
1943 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1947 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1950 if (bus->nodes_modified)
1953 r = sd_bus_message_close_container(m);
1957 r = sd_bus_message_close_container(m);
1963 if (!has_invalidating && !has_changing)
1966 r = sd_bus_message_close_container(m);
1970 r = sd_bus_message_open_container(m, 'a', "s");
1974 if (has_invalidating) {
1975 LIST_FOREACH(vtables, c, n->vtables) {
1976 if (require_fallback && !c->is_fallback)
1979 if (!streq(c->interface, interface))
1982 r = node_vtable_get_userdata(bus, path, c, &u);
1985 if (bus->nodes_modified)
1990 STRV_FOREACH(property, names) {
1991 struct vtable_member *v;
1993 key.member = *property;
1994 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1995 assert(c == v->parent);
1997 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
2000 r = sd_bus_message_append(m, "s", *property);
2007 r = sd_bus_message_close_container(m);
2011 r = sd_bus_send(bus, m, NULL);
2018 _public_ int sd_bus_emit_properties_changed_strv(
2021 const char *interface,
2024 BUS_DONT_DESTROY(bus);
2028 assert_return(bus, -EINVAL);
2029 assert_return(object_path_is_valid(path), -EINVAL);
2030 assert_return(interface_name_is_valid(interface), -EINVAL);
2031 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2032 assert_return(!bus_pid_changed(bus), -ECHILD);
2034 if (strv_isempty(names))
2038 bus->nodes_modified = false;
2040 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2043 if (bus->nodes_modified)
2046 prefix = alloca(strlen(path) + 1);
2047 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2048 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2051 if (bus->nodes_modified)
2055 } while (bus->nodes_modified);
2060 _public_ int sd_bus_emit_properties_changed(
2063 const char *interface,
2064 const char *name, ...) {
2068 assert_return(bus, -EINVAL);
2069 assert_return(object_path_is_valid(path), -EINVAL);
2070 assert_return(interface_name_is_valid(interface), -EINVAL);
2071 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2072 assert_return(!bus_pid_changed(bus), -ECHILD);
2077 names = strv_from_stdarg_alloca(name);
2079 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2082 static int interfaces_added_append_one_prefix(
2087 const char *interface,
2088 bool require_fallback) {
2090 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2091 bool found_interface = false;
2092 struct node_vtable *c;
2103 n = hashmap_get(bus->nodes, prefix);
2107 LIST_FOREACH(vtables, c, n->vtables) {
2108 if (require_fallback && !c->is_fallback)
2111 if (!streq(c->interface, interface))
2114 r = node_vtable_get_userdata(bus, path, c, &u);
2117 if (bus->nodes_modified)
2122 if (!found_interface) {
2123 r = sd_bus_message_append_basic(m, 's', interface);
2127 r = sd_bus_message_open_container(m, 'a', "{sv}");
2131 found_interface = true;
2134 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2137 if (bus->nodes_modified)
2141 if (found_interface) {
2142 r = sd_bus_message_close_container(m);
2147 return found_interface;
2150 static int interfaces_added_append_one(
2154 const char *interface) {
2164 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2167 if (bus->nodes_modified)
2170 prefix = alloca(strlen(path) + 1);
2171 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2172 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2175 if (bus->nodes_modified)
2182 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2183 BUS_DONT_DESTROY(bus);
2185 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2189 assert_return(bus, -EINVAL);
2190 assert_return(object_path_is_valid(path), -EINVAL);
2191 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2192 assert_return(!bus_pid_changed(bus), -ECHILD);
2194 if (strv_isempty(interfaces))
2198 bus->nodes_modified = false;
2201 m = sd_bus_message_unref(m);
2203 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2207 r = sd_bus_message_append_basic(m, 'o', path);
2211 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2215 STRV_FOREACH(i, interfaces) {
2216 assert_return(interface_name_is_valid(*i), -EINVAL);
2218 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2222 r = interfaces_added_append_one(bus, m, path, *i);
2226 if (bus->nodes_modified)
2229 r = sd_bus_message_close_container(m);
2234 if (bus->nodes_modified)
2237 r = sd_bus_message_close_container(m);
2241 } while (bus->nodes_modified);
2243 return sd_bus_send(bus, m, NULL);
2246 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2249 assert_return(bus, -EINVAL);
2250 assert_return(object_path_is_valid(path), -EINVAL);
2251 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2252 assert_return(!bus_pid_changed(bus), -ECHILD);
2254 interfaces = strv_from_stdarg_alloca(interface);
2256 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2259 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2260 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2263 assert_return(bus, -EINVAL);
2264 assert_return(object_path_is_valid(path), -EINVAL);
2265 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2266 assert_return(!bus_pid_changed(bus), -ECHILD);
2268 if (strv_isempty(interfaces))
2271 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2275 r = sd_bus_message_append_basic(m, 'o', path);
2279 r = sd_bus_message_append_strv(m, interfaces);
2283 return sd_bus_send(bus, m, NULL);
2286 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2289 assert_return(bus, -EINVAL);
2290 assert_return(object_path_is_valid(path), -EINVAL);
2291 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2292 assert_return(!bus_pid_changed(bus), -ECHILD);
2294 interfaces = strv_from_stdarg_alloca(interface);
2296 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2299 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2302 assert_return(bus, -EINVAL);
2303 assert_return(object_path_is_valid(path), -EINVAL);
2304 assert_return(!bus_pid_changed(bus), -ECHILD);
2306 n = bus_node_allocate(bus, path);
2310 n->object_manager = true;
2311 bus->nodes_modified = true;
2315 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2318 assert_return(bus, -EINVAL);
2319 assert_return(object_path_is_valid(path), -EINVAL);
2320 assert_return(!bus_pid_changed(bus), -ECHILD);
2322 n = hashmap_get(bus->nodes, path);
2326 if (!n->object_manager)
2329 n->object_manager = false;
2330 bus->nodes_modified = true;
2331 bus_node_gc(bus, n);