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,
37 sd_bus_error *error) {
48 r = c->find(bus, path, c->interface, u, &u, error);
51 if (sd_bus_error_is_set(error))
52 return sd_bus_error_get_errno(error);
63 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
66 return (uint8_t*) u + p->x.property.offset;
69 static int vtable_property_get_userdata(
72 struct vtable_member *p,
74 sd_bus_error *error) {
84 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
87 if (bus->nodes_modified)
90 *userdata = vtable_property_convert_userdata(p->vtable, u);
94 static int add_enumerated_to_set(
97 struct node_enumerator *first,
99 sd_bus_error *error) {
101 struct node_enumerator *c;
108 LIST_FOREACH(enumerators, c, first) {
109 char **children = NULL, **k;
111 if (bus->nodes_modified)
114 r = c->callback(bus, prefix, c->userdata, &children, error);
117 if (sd_bus_error_is_set(error))
118 return sd_bus_error_get_errno(error);
120 STRV_FOREACH(k, children) {
126 if (!object_path_is_valid(*k)){
132 if (!object_path_startswith(*k, prefix)) {
137 r = set_consume(s, *k);
148 static int add_subtree_to_set(
153 sd_bus_error *error) {
163 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
166 if (bus->nodes_modified)
169 LIST_FOREACH(siblings, i, n->child) {
172 if (!object_path_startswith(i->path, prefix))
179 r = set_consume(s, t);
180 if (r < 0 && r != -EEXIST)
183 r = add_subtree_to_set(bus, prefix, i, s, error);
186 if (bus->nodes_modified)
193 static int get_child_nodes(
198 sd_bus_error *error) {
208 s = set_new(string_hash_func, string_compare_func);
212 r = add_subtree_to_set(bus, prefix, n, s, error);
222 static int node_callbacks_run(
225 struct node_callback *first,
226 bool require_fallback,
227 bool *found_object) {
229 struct node_callback *c;
234 assert(found_object);
236 LIST_FOREACH(callbacks, c, first) {
237 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
239 if (bus->nodes_modified)
242 if (require_fallback && !c->is_fallback)
245 *found_object = true;
247 if (c->last_iteration == bus->iteration_counter)
250 c->last_iteration = bus->iteration_counter;
252 r = sd_bus_message_rewind(m, true);
256 r = c->callback(bus, m, c->userdata, &error_buffer);
257 r = bus_maybe_reply_error(m, r, &error_buffer);
265 static int method_callbacks_run(
268 struct vtable_member *c,
269 bool require_fallback,
270 bool *found_object) {
272 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
273 const char *signature;
280 assert(found_object);
282 if (require_fallback && !c->parent->is_fallback)
285 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
287 return bus_maybe_reply_error(m, r, &error);
288 if (bus->nodes_modified)
291 *found_object = true;
293 if (c->last_iteration == bus->iteration_counter)
296 c->last_iteration = bus->iteration_counter;
298 r = sd_bus_message_rewind(m, true);
302 signature = sd_bus_message_get_signature(m, true);
306 if (!streq(strempty(c->vtable->x.method.signature), signature))
307 return sd_bus_reply_method_errorf(
309 SD_BUS_ERROR_INVALID_ARGS,
310 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
311 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
313 if (c->vtable->x.method.handler) {
314 r = c->vtable->x.method.handler(bus, m, u, &error);
315 return bus_maybe_reply_error(m, r, &error);
318 /* If the method callback is NULL, make this a successful NOP */
319 r = sd_bus_reply_method_return(m, NULL);
326 static int invoke_property_get(
328 const sd_bus_vtable *v,
330 const char *interface,
331 const char *property,
332 sd_bus_message *reply,
334 sd_bus_error *error) {
346 if (v->x.property.get) {
347 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
350 if (sd_bus_error_is_set(error))
351 return sd_bus_error_get_errno(error);
355 /* Automatic handling if no callback is defined. */
357 if (streq(v->x.property.signature, "as"))
358 return sd_bus_message_append_strv(reply, *(char***) userdata);
360 assert(signature_is_single(v->x.property.signature, false));
361 assert(bus_type_is_basic(v->x.property.signature[0]));
363 switch (v->x.property.signature[0]) {
365 case SD_BUS_TYPE_STRING:
366 case SD_BUS_TYPE_SIGNATURE:
367 p = strempty(*(char**) userdata);
370 case SD_BUS_TYPE_OBJECT_PATH:
371 p = *(char**) userdata;
380 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
383 static int invoke_property_set(
385 const sd_bus_vtable *v,
387 const char *interface,
388 const char *property,
389 sd_bus_message *value,
391 sd_bus_error *error) {
402 if (v->x.property.set) {
403 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
406 if (sd_bus_error_is_set(error))
407 return sd_bus_error_get_errno(error);
411 /* Automatic handling if no callback is defined. */
413 assert(signature_is_single(v->x.property.signature, false));
414 assert(bus_type_is_basic(v->x.property.signature[0]));
416 switch (v->x.property.signature[0]) {
418 case SD_BUS_TYPE_STRING:
419 case SD_BUS_TYPE_OBJECT_PATH:
420 case SD_BUS_TYPE_SIGNATURE: {
424 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
432 free(*(char**) userdata);
433 *(char**) userdata = n;
439 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
449 static int property_get_set_callbacks_run(
452 struct vtable_member *c,
453 bool require_fallback,
455 bool *found_object) {
457 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
458 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
465 assert(found_object);
467 if (require_fallback && !c->parent->is_fallback)
470 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
472 return bus_maybe_reply_error(m, r, &error);
473 if (bus->nodes_modified)
476 *found_object = true;
478 r = sd_bus_message_new_method_return(m, &reply);
483 /* Note that we do not protect against reexecution
484 * here (using the last_iteration check, see below),
485 * should the node tree have changed and we got called
486 * again. We assume that property Get() calls are
487 * ultimately without side-effects or if they aren't
488 * then at least idempotent. */
490 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
494 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
496 return bus_maybe_reply_error(m, r, &error);
498 if (bus->nodes_modified)
501 r = sd_bus_message_close_container(reply);
506 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
507 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
509 /* Avoid that we call the set routine more than once
510 * if the processing of this message got restarted
511 * because the node tree changed. */
512 if (c->last_iteration == bus->iteration_counter)
515 c->last_iteration = bus->iteration_counter;
517 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
521 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
523 return bus_maybe_reply_error(m, r, &error);
525 if (bus->nodes_modified)
528 r = sd_bus_message_exit_container(m);
533 r = sd_bus_send(bus, reply, NULL);
540 static int vtable_append_all_properties(
542 sd_bus_message *reply,
544 struct node_vtable *c,
546 sd_bus_error *error) {
548 const sd_bus_vtable *v;
556 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
557 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
560 r = sd_bus_message_open_container(reply, 'e', "sv");
564 r = sd_bus_message_append(reply, "s", v->x.property.member);
568 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
572 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
575 if (bus->nodes_modified)
578 r = sd_bus_message_close_container(reply);
582 r = sd_bus_message_close_container(reply);
590 static int property_get_all_callbacks_run(
593 struct node_vtable *first,
594 bool require_fallback,
596 bool *found_object) {
598 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
599 struct node_vtable *c;
600 bool found_interface;
605 assert(found_object);
607 r = sd_bus_message_new_method_return(m, &reply);
611 r = sd_bus_message_open_container(reply, 'a', "{sv}");
615 found_interface = !iface ||
616 streq(iface, "org.freedesktop.DBus.Properties") ||
617 streq(iface, "org.freedesktop.DBus.Peer") ||
618 streq(iface, "org.freedesktop.DBus.Introspectable");
620 LIST_FOREACH(vtables, c, first) {
621 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
624 if (require_fallback && !c->is_fallback)
627 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
629 return bus_maybe_reply_error(m, r, &error);
630 if (bus->nodes_modified)
635 *found_object = true;
637 if (iface && !streq(c->interface, iface))
639 found_interface = true;
641 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
643 return bus_maybe_reply_error(m, r, &error);
644 if (bus->nodes_modified)
648 if (!found_interface) {
649 r = sd_bus_reply_method_errorf(
651 SD_BUS_ERROR_UNKNOWN_INTERFACE,
652 "Unknown interface '%s'.", iface);
659 r = sd_bus_message_close_container(reply);
663 r = sd_bus_send(bus, reply, NULL);
670 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
674 if (n->object_manager)
678 return bus_node_with_object_manager(bus, n->parent);
683 static bool bus_node_exists(
687 bool require_fallback) {
689 struct node_vtable *c;
690 struct node_callback *k;
696 /* Tests if there's anything attached directly to this node
697 * for the specified path */
699 LIST_FOREACH(callbacks, k, n->callbacks) {
700 if (require_fallback && !k->is_fallback)
706 LIST_FOREACH(vtables, c, n->vtables) {
707 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
709 if (require_fallback && !c->is_fallback)
712 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
714 if (bus->nodes_modified)
718 return !require_fallback && (n->enumerators || n->object_manager);
721 static int process_introspect(
725 bool require_fallback,
726 bool *found_object) {
728 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
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, &error);
744 return bus_maybe_reply_error(m, r, &error);
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, &error);
764 r = bus_maybe_reply_error(m, r, &error);
767 if (bus->nodes_modified) {
776 if (!streq_ptr(previous_interface, c->interface)) {
778 if (previous_interface)
779 fputs(" </interface>\n", intro.f);
781 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
784 r = introspect_write_interface(&intro, c->vtable);
788 previous_interface = c->interface;
791 if (previous_interface)
792 fputs(" </interface>\n", intro.f);
795 /* Nothing?, let's see if we exist at all, and if not
796 * refuse to do anything */
797 r = bus_node_exists(bus, n, m->path, require_fallback);
800 if (bus->nodes_modified)
806 *found_object = true;
808 r = introspect_write_child_nodes(&intro, s, m->path);
812 r = introspect_finish(&intro, bus, m, &reply);
816 r = sd_bus_send(bus, reply, NULL);
823 introspect_free(&intro);
827 static int object_manager_serialize_path(
829 sd_bus_message *reply,
832 bool require_fallback,
833 sd_bus_error *error) {
835 const char *previous_interface = NULL;
836 bool found_something = false;
837 struct node_vtable *i;
847 n = hashmap_get(bus->nodes, prefix);
851 LIST_FOREACH(vtables, i, n->vtables) {
854 if (require_fallback && !i->is_fallback)
857 r = node_vtable_get_userdata(bus, path, i, &u, error);
860 if (bus->nodes_modified)
865 if (!found_something) {
867 /* Open the object part */
869 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
873 r = sd_bus_message_append(reply, "o", path);
877 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
881 found_something = true;
884 if (!streq_ptr(previous_interface, i->interface)) {
886 /* Maybe close the previous interface part */
888 if (previous_interface) {
889 r = sd_bus_message_close_container(reply);
893 r = sd_bus_message_close_container(reply);
898 /* Open the new interface part */
900 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
904 r = sd_bus_message_append(reply, "s", i->interface);
908 r = sd_bus_message_open_container(reply, 'a', "{sv}");
913 r = vtable_append_all_properties(bus, reply, path, i, u, error);
916 if (bus->nodes_modified)
919 previous_interface = i->interface;
922 if (previous_interface) {
923 r = sd_bus_message_close_container(reply);
927 r = sd_bus_message_close_container(reply);
932 if (found_something) {
933 r = sd_bus_message_close_container(reply);
937 r = sd_bus_message_close_container(reply);
945 static int object_manager_serialize_path_and_fallbacks(
947 sd_bus_message *reply,
949 sd_bus_error *error) {
959 /* First, add all vtables registered for this path */
960 r = object_manager_serialize_path(bus, reply, path, path, false, 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 (bus->nodes_modified)
979 static int process_get_managed_objects(
983 bool require_fallback,
984 bool *found_object) {
986 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
987 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
988 _cleanup_set_free_free_ Set *s = NULL;
995 assert(found_object);
997 if (!bus_node_with_object_manager(bus, n))
1000 r = get_child_nodes(bus, m->path, n, &s, &error);
1003 if (bus->nodes_modified)
1006 r = sd_bus_message_new_method_return(m, &reply);
1010 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1014 empty = set_isempty(s);
1016 struct node_vtable *c;
1018 /* Hmm, so we have no children? Then let's check
1019 * whether we exist at all, i.e. whether at least one
1022 LIST_FOREACH(vtables, c, n->vtables) {
1024 if (require_fallback && !c->is_fallback)
1042 SET_FOREACH(path, s, i) {
1043 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1047 if (bus->nodes_modified)
1052 r = sd_bus_message_close_container(reply);
1056 r = sd_bus_send(bus, reply, NULL);
1063 static int object_find_and_run(
1067 bool require_fallback,
1068 bool *found_object) {
1071 struct vtable_member vtable_key, *v;
1077 assert(found_object);
1079 n = hashmap_get(bus->nodes, p);
1083 /* First, try object callbacks */
1084 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1087 if (bus->nodes_modified)
1090 if (!m->interface || !m->member)
1093 /* Then, look for a known method */
1094 vtable_key.path = (char*) p;
1095 vtable_key.interface = m->interface;
1096 vtable_key.member = m->member;
1098 v = hashmap_get(bus->vtable_methods, &vtable_key);
1100 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1103 if (bus->nodes_modified)
1107 /* Then, look for a known property */
1108 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1111 get = streq(m->member, "Get");
1113 if (get || streq(m->member, "Set")) {
1115 r = sd_bus_message_rewind(m, true);
1119 vtable_key.path = (char*) p;
1121 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1125 v = hashmap_get(bus->vtable_properties, &vtable_key);
1127 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1132 } else if (streq(m->member, "GetAll")) {
1135 r = sd_bus_message_rewind(m, true);
1139 r = sd_bus_message_read(m, "s", &iface);
1146 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1151 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1153 r = process_introspect(bus, m, n, require_fallback, found_object);
1157 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1159 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1164 if (bus->nodes_modified)
1167 if (!*found_object) {
1168 r = bus_node_exists(bus, n, m->path, require_fallback);
1172 *found_object = true;
1178 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1181 bool found_object = false;
1186 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1192 if (hashmap_isempty(bus->nodes))
1195 pl = strlen(m->path);
1199 bus->nodes_modified = false;
1201 r = object_find_and_run(bus, m, m->path, false, &found_object);
1205 /* Look for fallback prefixes */
1206 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1208 if (bus->nodes_modified)
1211 r = object_find_and_run(bus, m, prefix, true, &found_object);
1216 } while (bus->nodes_modified);
1221 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1222 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1223 r = sd_bus_reply_method_errorf(
1225 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1226 "Unknown property or interface.");
1228 r = sd_bus_reply_method_errorf(
1230 SD_BUS_ERROR_UNKNOWN_METHOD,
1231 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1239 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1240 struct node *n, *parent;
1247 assert(path[0] == '/');
1249 n = hashmap_get(bus->nodes, path);
1253 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1261 if (streq(path, "/"))
1264 e = strrchr(path, '/');
1267 p = strndupa(path, MAX(1, path - e));
1269 parent = bus_node_allocate(bus, p);
1276 n = new0(struct node, 1);
1283 r = hashmap_put(bus->nodes, s, n);
1291 LIST_PREPEND(siblings, parent->child, n);
1296 static void bus_node_gc(sd_bus *b, struct node *n) {
1309 assert(hashmap_remove(b->nodes, n->path) == n);
1312 LIST_REMOVE(siblings, n->parent->child, n);
1315 bus_node_gc(b, n->parent);
1319 static int bus_add_object(
1323 sd_bus_message_handler_t callback,
1326 struct node_callback *c;
1330 assert_return(bus, -EINVAL);
1331 assert_return(object_path_is_valid(path), -EINVAL);
1332 assert_return(callback, -EINVAL);
1333 assert_return(!bus_pid_changed(bus), -ECHILD);
1335 n = bus_node_allocate(bus, path);
1339 c = new0(struct node_callback, 1);
1346 c->callback = callback;
1347 c->userdata = userdata;
1348 c->is_fallback = fallback;
1350 LIST_PREPEND(callbacks, n->callbacks, c);
1351 bus->nodes_modified = true;
1357 bus_node_gc(bus, n);
1361 static int bus_remove_object(
1365 sd_bus_message_handler_t callback,
1368 struct node_callback *c;
1371 assert_return(bus, -EINVAL);
1372 assert_return(object_path_is_valid(path), -EINVAL);
1373 assert_return(callback, -EINVAL);
1374 assert_return(!bus_pid_changed(bus), -ECHILD);
1376 n = hashmap_get(bus->nodes, path);
1380 LIST_FOREACH(callbacks, c, n->callbacks)
1381 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1386 LIST_REMOVE(callbacks, n->callbacks, c);
1389 bus_node_gc(bus, n);
1390 bus->nodes_modified = true;
1395 _public_ int sd_bus_add_object(sd_bus *bus,
1397 sd_bus_message_handler_t callback,
1400 return bus_add_object(bus, false, path, callback, userdata);
1403 _public_ int sd_bus_remove_object(sd_bus *bus,
1405 sd_bus_message_handler_t callback,
1408 return bus_remove_object(bus, false, path, callback, userdata);
1411 _public_ int sd_bus_add_fallback(sd_bus *bus,
1413 sd_bus_message_handler_t callback,
1416 return bus_add_object(bus, true, prefix, callback, userdata);
1419 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1421 sd_bus_message_handler_t callback,
1424 return bus_remove_object(bus, true, prefix, callback, userdata);
1427 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1433 if (w->interface && w->node && w->vtable) {
1434 const sd_bus_vtable *v;
1436 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1437 struct vtable_member *x = NULL;
1441 case _SD_BUS_VTABLE_METHOD: {
1442 struct vtable_member key;
1444 key.path = w->node->path;
1445 key.interface = w->interface;
1446 key.member = v->x.method.member;
1448 x = hashmap_remove(bus->vtable_methods, &key);
1452 case _SD_BUS_VTABLE_PROPERTY:
1453 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1454 struct vtable_member key;
1456 key.path = w->node->path;
1457 key.interface = w->interface;
1458 key.member = v->x.property.member;
1459 x = hashmap_remove(bus->vtable_properties, &key);
1471 static unsigned vtable_member_hash_func(const void *a) {
1472 const struct vtable_member *m = a;
1477 string_hash_func(m->path) ^
1478 string_hash_func(m->interface) ^
1479 string_hash_func(m->member);
1482 static int vtable_member_compare_func(const void *a, const void *b) {
1483 const struct vtable_member *x = a, *y = b;
1489 r = strcmp(x->path, y->path);
1493 r = strcmp(x->interface, y->interface);
1497 return strcmp(x->member, y->member);
1500 static int add_object_vtable_internal(
1503 const char *interface,
1504 const sd_bus_vtable *vtable,
1506 sd_bus_object_find_t find,
1509 struct node_vtable *c = NULL, *i, *existing = NULL;
1510 const sd_bus_vtable *v;
1514 assert_return(bus, -EINVAL);
1515 assert_return(object_path_is_valid(path), -EINVAL);
1516 assert_return(interface_name_is_valid(interface), -EINVAL);
1517 assert_return(vtable, -EINVAL);
1518 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1519 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1520 assert_return(!bus_pid_changed(bus), -ECHILD);
1521 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1522 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1523 !streq(interface, "org.freedesktop.DBus.Peer") &&
1524 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1526 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1530 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1534 n = bus_node_allocate(bus, path);
1538 LIST_FOREACH(vtables, i, n->vtables) {
1539 if (i->is_fallback != fallback) {
1544 if (streq(i->interface, interface)) {
1546 if (i->vtable == vtable) {
1555 c = new0(struct node_vtable, 1);
1562 c->is_fallback = fallback;
1564 c->userdata = userdata;
1567 c->interface = strdup(interface);
1568 if (!c->interface) {
1573 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1577 case _SD_BUS_VTABLE_METHOD: {
1578 struct vtable_member *m;
1580 if (!member_name_is_valid(v->x.method.member) ||
1581 !signature_is_valid(strempty(v->x.method.signature), false) ||
1582 !signature_is_valid(strempty(v->x.method.result), false) ||
1583 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1584 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1589 m = new0(struct vtable_member, 1);
1597 m->interface = c->interface;
1598 m->member = v->x.method.member;
1601 r = hashmap_put(bus->vtable_methods, m, m);
1610 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1612 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1619 case _SD_BUS_VTABLE_PROPERTY: {
1620 struct vtable_member *m;
1622 if (!member_name_is_valid(v->x.property.member) ||
1623 !signature_is_single(v->x.property.signature, false) ||
1624 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1625 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1626 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1632 m = new0(struct vtable_member, 1);
1640 m->interface = c->interface;
1641 m->member = v->x.property.member;
1644 r = hashmap_put(bus->vtable_properties, m, m);
1653 case _SD_BUS_VTABLE_SIGNAL:
1655 if (!member_name_is_valid(v->x.signal.member) ||
1656 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1669 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1670 bus->nodes_modified = true;
1676 free_node_vtable(bus, c);
1678 bus_node_gc(bus, n);
1682 static int remove_object_vtable_internal(
1685 const char *interface,
1686 const sd_bus_vtable *vtable,
1688 sd_bus_object_find_t find,
1691 struct node_vtable *c;
1694 assert_return(bus, -EINVAL);
1695 assert_return(object_path_is_valid(path), -EINVAL);
1696 assert_return(interface_name_is_valid(interface), -EINVAL);
1697 assert_return(!bus_pid_changed(bus), -ECHILD);
1699 n = hashmap_get(bus->nodes, path);
1703 LIST_FOREACH(vtables, c, n->vtables)
1704 if (streq(c->interface, interface) &&
1705 c->is_fallback == fallback &&
1706 c->vtable == vtable &&
1708 c->userdata == userdata)
1714 LIST_REMOVE(vtables, n->vtables, c);
1716 free_node_vtable(bus, c);
1717 bus_node_gc(bus, n);
1719 bus->nodes_modified = true;
1724 _public_ int sd_bus_add_object_vtable(
1727 const char *interface,
1728 const sd_bus_vtable *vtable,
1731 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1734 _public_ int sd_bus_remove_object_vtable(
1737 const char *interface,
1738 const sd_bus_vtable *vtable,
1741 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1744 _public_ int sd_bus_add_fallback_vtable(
1747 const char *interface,
1748 const sd_bus_vtable *vtable,
1749 sd_bus_object_find_t find,
1752 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1755 _public_ int sd_bus_remove_fallback_vtable(
1758 const char *interface,
1759 const sd_bus_vtable *vtable,
1760 sd_bus_object_find_t find,
1763 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1766 _public_ int sd_bus_add_node_enumerator(
1769 sd_bus_node_enumerator_t callback,
1772 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 = bus_node_allocate(bus, path);
1785 c = new0(struct node_enumerator, 1);
1792 c->callback = callback;
1793 c->userdata = userdata;
1795 LIST_PREPEND(enumerators, n->enumerators, c);
1797 bus->nodes_modified = true;
1803 bus_node_gc(bus, n);
1807 _public_ int sd_bus_remove_node_enumerator(
1810 sd_bus_node_enumerator_t callback,
1813 struct node_enumerator *c;
1816 assert_return(bus, -EINVAL);
1817 assert_return(object_path_is_valid(path), -EINVAL);
1818 assert_return(callback, -EINVAL);
1819 assert_return(!bus_pid_changed(bus), -ECHILD);
1821 n = hashmap_get(bus->nodes, path);
1825 LIST_FOREACH(enumerators, c, n->enumerators)
1826 if (c->callback == callback && c->userdata == userdata)
1832 LIST_REMOVE(enumerators, n->enumerators, c);
1835 bus_node_gc(bus, n);
1837 bus->nodes_modified = true;
1842 static int emit_properties_changed_on_interface(
1846 const char *interface,
1847 bool require_fallback,
1850 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1851 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1852 bool has_invalidating = false, has_changing = false;
1853 struct vtable_member key = {};
1854 struct node_vtable *c;
1865 n = hashmap_get(bus->nodes, prefix);
1869 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1873 r = sd_bus_message_append(m, "s", interface);
1877 r = sd_bus_message_open_container(m, 'a', "{sv}");
1882 key.interface = interface;
1884 LIST_FOREACH(vtables, c, n->vtables) {
1885 if (require_fallback && !c->is_fallback)
1888 if (!streq(c->interface, interface))
1891 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1894 if (bus->nodes_modified)
1899 STRV_FOREACH(property, names) {
1900 struct vtable_member *v;
1902 assert_return(member_name_is_valid(*property), -EINVAL);
1904 key.member = *property;
1905 v = hashmap_get(bus->vtable_properties, &key);
1909 /* If there are two vtables for the same
1910 * interface, let's handle this property when
1911 * we come to that vtable. */
1915 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1917 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1918 has_invalidating = true;
1922 has_changing = true;
1924 r = sd_bus_message_open_container(m, 'e', "sv");
1928 r = sd_bus_message_append(m, "s", *property);
1932 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1936 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error);
1939 if (bus->nodes_modified)
1942 r = sd_bus_message_close_container(m);
1946 r = sd_bus_message_close_container(m);
1952 if (!has_invalidating && !has_changing)
1955 r = sd_bus_message_close_container(m);
1959 r = sd_bus_message_open_container(m, 'a', "s");
1963 if (has_invalidating) {
1964 LIST_FOREACH(vtables, c, n->vtables) {
1965 if (require_fallback && !c->is_fallback)
1968 if (!streq(c->interface, interface))
1971 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1974 if (bus->nodes_modified)
1979 STRV_FOREACH(property, names) {
1980 struct vtable_member *v;
1982 key.member = *property;
1983 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1984 assert(c == v->parent);
1986 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1989 r = sd_bus_message_append(m, "s", *property);
1996 r = sd_bus_message_close_container(m);
2000 r = sd_bus_send(bus, m, NULL);
2007 _public_ int sd_bus_emit_properties_changed_strv(
2010 const char *interface,
2013 BUS_DONT_DESTROY(bus);
2017 assert_return(bus, -EINVAL);
2018 assert_return(object_path_is_valid(path), -EINVAL);
2019 assert_return(interface_name_is_valid(interface), -EINVAL);
2020 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2021 assert_return(!bus_pid_changed(bus), -ECHILD);
2023 if (strv_isempty(names))
2027 bus->nodes_modified = false;
2029 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2032 if (bus->nodes_modified)
2035 prefix = alloca(strlen(path) + 1);
2036 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2037 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2040 if (bus->nodes_modified)
2044 } while (bus->nodes_modified);
2049 _public_ int sd_bus_emit_properties_changed(
2052 const char *interface,
2053 const char *name, ...) {
2057 assert_return(bus, -EINVAL);
2058 assert_return(object_path_is_valid(path), -EINVAL);
2059 assert_return(interface_name_is_valid(interface), -EINVAL);
2060 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2061 assert_return(!bus_pid_changed(bus), -ECHILD);
2066 names = strv_from_stdarg_alloca(name);
2068 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2071 static int interfaces_added_append_one_prefix(
2076 const char *interface,
2077 bool require_fallback) {
2079 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2080 bool found_interface = false;
2081 struct node_vtable *c;
2092 n = hashmap_get(bus->nodes, prefix);
2096 LIST_FOREACH(vtables, c, n->vtables) {
2097 if (require_fallback && !c->is_fallback)
2100 if (!streq(c->interface, interface))
2103 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2106 if (bus->nodes_modified)
2111 if (!found_interface) {
2112 r = sd_bus_message_append_basic(m, 's', interface);
2116 r = sd_bus_message_open_container(m, 'a', "{sv}");
2120 found_interface = true;
2123 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2126 if (bus->nodes_modified)
2130 if (found_interface) {
2131 r = sd_bus_message_close_container(m);
2136 return found_interface;
2139 static int interfaces_added_append_one(
2143 const char *interface) {
2153 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2156 if (bus->nodes_modified)
2159 prefix = alloca(strlen(path) + 1);
2160 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2161 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2164 if (bus->nodes_modified)
2171 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2172 BUS_DONT_DESTROY(bus);
2174 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2178 assert_return(bus, -EINVAL);
2179 assert_return(object_path_is_valid(path), -EINVAL);
2180 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2181 assert_return(!bus_pid_changed(bus), -ECHILD);
2183 if (strv_isempty(interfaces))
2187 bus->nodes_modified = false;
2190 m = sd_bus_message_unref(m);
2192 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2196 r = sd_bus_message_append_basic(m, 'o', path);
2200 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2204 STRV_FOREACH(i, interfaces) {
2205 assert_return(interface_name_is_valid(*i), -EINVAL);
2207 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2211 r = interfaces_added_append_one(bus, m, path, *i);
2215 if (bus->nodes_modified)
2218 r = sd_bus_message_close_container(m);
2223 if (bus->nodes_modified)
2226 r = sd_bus_message_close_container(m);
2230 } while (bus->nodes_modified);
2232 return sd_bus_send(bus, m, NULL);
2235 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2238 assert_return(bus, -EINVAL);
2239 assert_return(object_path_is_valid(path), -EINVAL);
2240 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2241 assert_return(!bus_pid_changed(bus), -ECHILD);
2243 interfaces = strv_from_stdarg_alloca(interface);
2245 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2248 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2249 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2252 assert_return(bus, -EINVAL);
2253 assert_return(object_path_is_valid(path), -EINVAL);
2254 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2255 assert_return(!bus_pid_changed(bus), -ECHILD);
2257 if (strv_isempty(interfaces))
2260 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2264 r = sd_bus_message_append_basic(m, 'o', path);
2268 r = sd_bus_message_append_strv(m, interfaces);
2272 return sd_bus_send(bus, m, NULL);
2275 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2278 assert_return(bus, -EINVAL);
2279 assert_return(object_path_is_valid(path), -EINVAL);
2280 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2281 assert_return(!bus_pid_changed(bus), -ECHILD);
2283 interfaces = strv_from_stdarg_alloca(interface);
2285 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2288 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2291 assert_return(bus, -EINVAL);
2292 assert_return(object_path_is_valid(path), -EINVAL);
2293 assert_return(!bus_pid_changed(bus), -ECHILD);
2295 n = bus_node_allocate(bus, path);
2299 n->object_manager = true;
2300 bus->nodes_modified = true;
2304 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2307 assert_return(bus, -EINVAL);
2308 assert_return(object_path_is_valid(path), -EINVAL);
2309 assert_return(!bus_pid_changed(bus), -ECHILD);
2311 n = hashmap_get(bus->nodes, path);
2315 if (!n->object_manager)
2318 n->object_manager = false;
2319 bus->nodes_modified = true;
2320 bus_node_gc(bus, n);