1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "bus-internal.h"
25 #include "bus-message.h"
27 #include "bus-signature.h"
28 #include "bus-introspect.h"
29 #include "bus-objects.h"
32 static int node_vtable_get_userdata(
35 struct node_vtable *c,
47 r = c->find(bus, path, c->interface, &u, u);
58 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
61 return (uint8_t*) u + p->x.property.offset;
64 static int vtable_property_get_userdata(
67 struct vtable_member *p,
78 r = node_vtable_get_userdata(bus, path, p->parent, &u);
81 if (bus->nodes_modified)
84 *userdata = vtable_property_convert_userdata(p->vtable, u);
88 static int add_enumerated_to_set(
91 struct node_enumerator *first,
94 struct node_enumerator *c;
101 LIST_FOREACH(enumerators, c, first) {
102 char **children = NULL, **k;
104 if (bus->nodes_modified)
107 r = c->callback(bus, prefix, &children, c->userdata);
111 STRV_FOREACH(k, children) {
117 if (!object_path_is_valid(*k) && object_path_startswith(*k, prefix)) {
123 r = set_consume(s, *k);
134 static int add_subtree_to_set(
148 r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
151 if (bus->nodes_modified)
154 LIST_FOREACH(siblings, i, n->child) {
161 r = set_consume(s, t);
162 if (r < 0 && r != -EEXIST)
165 r = add_subtree_to_set(bus, prefix, i, s);
168 if (bus->nodes_modified)
175 static int get_child_nodes(
189 s = set_new(string_hash_func, string_compare_func);
193 r = add_subtree_to_set(bus, prefix, n, s);
203 static int node_callbacks_run(
206 struct node_callback *first,
207 bool require_fallback,
208 bool *found_object) {
210 struct node_callback *c;
215 assert(found_object);
217 LIST_FOREACH(callbacks, c, first) {
218 if (bus->nodes_modified)
221 if (require_fallback && !c->is_fallback)
224 *found_object = true;
226 if (c->last_iteration == bus->iteration_counter)
229 c->last_iteration = bus->iteration_counter;
231 r = sd_bus_message_rewind(m, true);
235 r = c->callback(bus, m, c->userdata);
243 static int method_callbacks_run(
246 struct vtable_member *c,
247 bool require_fallback,
248 bool *found_object) {
250 const char *signature;
257 assert(found_object);
259 if (require_fallback && !c->parent->is_fallback)
262 r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
265 if (bus->nodes_modified)
268 *found_object = true;
270 if (c->last_iteration == bus->iteration_counter)
273 c->last_iteration = bus->iteration_counter;
275 r = sd_bus_message_rewind(m, true);
279 signature = sd_bus_message_get_signature(m, true);
283 if (!streq(strempty(c->vtable->x.method.signature), signature)) {
284 r = sd_bus_reply_method_errorf(bus, m,
285 SD_BUS_ERROR_INVALID_ARGS,
286 "Invalid arguments '%s' to call %s:%s, expecting '%s'.",
287 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
294 if (c->vtable->x.method.handler)
295 return c->vtable->x.method.handler(bus, m, u);
297 /* If the method callback is NULL, make this a successful NOP */
298 r = sd_bus_reply_method_return(bus, m, NULL);
305 static int invoke_property_get(
307 const sd_bus_vtable *v,
309 const char *interface,
310 const char *property,
324 if (v->x.property.get)
325 return v->x.property.get(bus, path, interface, property, m, error, userdata);
327 /* Automatic handling if no callback is defined. */
329 if (streq(v->x.property.signature, "as"))
330 return sd_bus_message_append_strv(m, *(char***) userdata);
332 assert(signature_is_single(v->x.property.signature, false));
333 assert(bus_type_is_basic(v->x.property.signature[0]));
335 switch (v->x.property.signature[0]) {
337 case SD_BUS_TYPE_STRING:
338 case SD_BUS_TYPE_SIGNATURE:
339 p = strempty(*(char**) userdata);
342 case SD_BUS_TYPE_OBJECT_PATH:
343 p = *(char**) userdata;
352 return sd_bus_message_append_basic(m, v->x.property.signature[0], p);
355 static int invoke_property_set(
357 const sd_bus_vtable *v,
359 const char *interface,
360 const char *property,
361 sd_bus_message *value,
374 if (v->x.property.set)
375 return v->x.property.set(bus, path, interface, property, value, error, userdata);
377 /* Automatic handling if no callback is defined. */
379 assert(signature_is_single(v->x.property.signature, false));
380 assert(bus_type_is_basic(v->x.property.signature[0]));
382 switch (v->x.property.signature[0]) {
384 case SD_BUS_TYPE_STRING:
385 case SD_BUS_TYPE_OBJECT_PATH:
386 case SD_BUS_TYPE_SIGNATURE: {
390 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
398 free(*(char**) userdata);
399 *(char**) userdata = n;
405 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
415 static int property_get_set_callbacks_run(
418 struct vtable_member *c,
419 bool require_fallback,
421 bool *found_object) {
423 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
424 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
431 assert(found_object);
433 if (require_fallback && !c->parent->is_fallback)
436 r = vtable_property_get_userdata(bus, m->path, c, &u);
439 if (bus->nodes_modified)
442 *found_object = true;
444 r = sd_bus_message_new_method_return(bus, m, &reply);
449 /* Note that we do not protect against reexecution
450 * here (using the last_iteration check, see below),
451 * should the node tree have changed and we got called
452 * again. We assume that property Get() calls are
453 * ultimately without side-effects or if they aren't
454 * then at least idempotent. */
456 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
460 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
464 if (sd_bus_error_is_set(&error)) {
465 r = sd_bus_reply_method_error(bus, m, &error);
472 if (bus->nodes_modified)
475 r = sd_bus_message_close_container(reply);
480 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
481 sd_bus_error_setf(&error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
483 /* Avoid that we call the set routine more
484 * than once if the processing of this message
485 * got restarted because the node tree
487 if (c->last_iteration == bus->iteration_counter)
490 c->last_iteration = bus->iteration_counter;
492 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
496 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
501 if (sd_bus_error_is_set(&error)) {
502 r = sd_bus_reply_method_error(bus, m, &error);
509 if (bus->nodes_modified)
512 r = sd_bus_message_exit_container(m);
517 r = sd_bus_send(bus, reply, NULL);
524 static int vtable_append_all_properties(
526 sd_bus_message *reply,
528 struct node_vtable *c,
530 sd_bus_error *error) {
532 const sd_bus_vtable *v;
540 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
541 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
544 r = sd_bus_message_open_container(reply, 'e', "sv");
548 r = sd_bus_message_append(reply, "s", v->x.property.member);
552 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
556 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, error, vtable_property_convert_userdata(v, userdata));
559 if (sd_bus_error_is_set(error))
561 if (bus->nodes_modified)
564 r = sd_bus_message_close_container(reply);
568 r = sd_bus_message_close_container(reply);
576 static int property_get_all_callbacks_run(
579 struct node_vtable *first,
580 bool require_fallback,
582 bool *found_object) {
584 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
585 struct node_vtable *c;
586 bool found_interface;
591 assert(found_object);
593 r = sd_bus_message_new_method_return(bus, m, &reply);
597 r = sd_bus_message_open_container(reply, 'a', "{sv}");
601 found_interface = !iface ||
602 streq(iface, "org.freedesktop.DBus.Properties") ||
603 streq(iface, "org.freedesktop.DBus.Peer") ||
604 streq(iface, "org.freedesktop.DBus.Introspectable");
606 LIST_FOREACH(vtables, c, first) {
607 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
610 if (require_fallback && !c->is_fallback)
613 r = node_vtable_get_userdata(bus, m->path, c, &u);
616 if (bus->nodes_modified)
621 *found_object = true;
623 if (iface && !streq(c->interface, iface))
625 found_interface = true;
627 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
631 if (sd_bus_error_is_set(&error)) {
632 r = sd_bus_reply_method_error(bus, m, &error);
638 if (bus->nodes_modified)
642 if (!found_interface) {
643 r = sd_bus_reply_method_errorf(
645 SD_BUS_ERROR_UNKNOWN_INTERFACE,
646 "Unknown interface '%s'.", iface);
653 r = sd_bus_message_close_container(reply);
657 r = sd_bus_send(bus, reply, NULL);
664 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
668 if (n->object_manager)
672 return bus_node_with_object_manager(bus, n->parent);
677 static bool bus_node_exists(
681 bool require_fallback) {
683 struct node_vtable *c;
684 struct node_callback *k;
690 /* Tests if there's anything attached directly to this node
691 * for the specified path */
693 LIST_FOREACH(callbacks, k, n->callbacks) {
694 if (require_fallback && !k->is_fallback)
700 LIST_FOREACH(vtables, c, n->vtables) {
702 if (require_fallback && !c->is_fallback)
705 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
707 if (bus->nodes_modified)
711 return !require_fallback && (n->enumerators || n->object_manager);
714 static int process_introspect(
718 bool require_fallback,
719 bool *found_object) {
721 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
722 _cleanup_set_free_free_ Set *s = NULL;
723 struct introspect intro;
724 struct node_vtable *c;
731 assert(found_object);
733 r = get_child_nodes(bus, m->path, n, &s);
736 if (bus->nodes_modified)
739 r = introspect_begin(&intro);
743 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
747 empty = set_isempty(s);
749 LIST_FOREACH(vtables, c, n->vtables) {
750 if (require_fallback && !c->is_fallback)
753 r = node_vtable_get_userdata(bus, m->path, c, NULL);
756 if (bus->nodes_modified)
763 r = introspect_write_interface(&intro, c->interface, c->vtable);
769 /* Nothing?, let's see if we exist at all, and if not
770 * refuse to do anything */
771 r = bus_node_exists(bus, n, m->path, require_fallback);
774 if (bus->nodes_modified)
780 *found_object = true;
782 r = introspect_write_child_nodes(&intro, s, m->path);
786 r = introspect_finish(&intro, bus, m, &reply);
790 r = sd_bus_send(bus, reply, NULL);
797 introspect_free(&intro);
801 static int object_manager_serialize_vtable(
803 sd_bus_message *reply,
805 struct node_vtable *c,
817 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
821 r = sd_bus_message_append(reply, "s", c->interface);
825 r = sd_bus_message_open_container(reply, 'a', "{sv}");
829 r = vtable_append_all_properties(bus, reply, path, c, userdata, error);
832 if (bus->nodes_modified)
835 r = sd_bus_message_close_container(reply);
839 r = sd_bus_message_close_container(reply);
846 static int object_manager_serialize_path(
848 sd_bus_message *reply,
851 bool require_fallback,
852 sd_bus_error *error) {
854 struct node_vtable *i;
856 bool found_something = false;
865 n = hashmap_get(bus->nodes, prefix);
869 LIST_FOREACH(vtables, i, n->vtables) {
872 if (require_fallback && !i->is_fallback)
875 r = node_vtable_get_userdata(bus, path, i, &u);
878 if (bus->nodes_modified)
883 if (!found_something) {
884 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
888 r = sd_bus_message_append(reply, "o", path);
892 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
896 found_something = true;
899 r = object_manager_serialize_vtable(bus, reply, path, i, error, u);
902 if (sd_bus_error_is_set(error))
904 if (bus->nodes_modified)
908 if (found_something) {
909 r = sd_bus_message_close_container(reply);
913 r = sd_bus_message_close_container(reply);
921 static int object_manager_serialize_path_and_fallbacks(
923 sd_bus_message *reply,
925 sd_bus_error *error) {
935 /* First, add all vtables registered for this path */
936 r = object_manager_serialize_path(bus, reply, path, path, false, error);
939 if (sd_bus_error_is_set(error))
941 if (bus->nodes_modified)
944 /* Second, add fallback vtables registered for any of the prefixes */
945 prefix = alloca(strlen(path) + 1);
946 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
947 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
950 if (sd_bus_error_is_set(error))
952 if (bus->nodes_modified)
959 static int process_get_managed_objects(
963 bool require_fallback,
964 bool *found_object) {
966 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
967 _cleanup_set_free_free_ Set *s = NULL;
974 assert(found_object);
976 if (!bus_node_with_object_manager(bus, n))
979 r = get_child_nodes(bus, m->path, n, &s);
982 if (bus->nodes_modified)
985 r = sd_bus_message_new_method_return(bus, m, &reply);
989 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
993 empty = set_isempty(s);
995 struct node_vtable *c;
997 /* Hmm, so we have no children? Then let's check
998 * whether we exist at all, i.e. whether at least one
1001 LIST_FOREACH(vtables, c, n->vtables) {
1003 if (require_fallback && !c->is_fallback)
1021 SET_FOREACH(path, s, i) {
1022 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1024 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1028 if (sd_bus_error_is_set(&error)) {
1029 r = sd_bus_reply_method_error(bus, m, &error);
1036 if (bus->nodes_modified)
1041 r = sd_bus_message_close_container(reply);
1045 r = sd_bus_send(bus, reply, NULL);
1052 static int object_find_and_run(
1056 bool require_fallback,
1057 bool *found_object) {
1060 struct vtable_member vtable_key, *v;
1066 assert(found_object);
1068 n = hashmap_get(bus->nodes, p);
1072 /* First, try object callbacks */
1073 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1076 if (bus->nodes_modified)
1079 if (!m->interface || !m->member)
1082 /* Then, look for a known method */
1083 vtable_key.path = (char*) p;
1084 vtable_key.interface = m->interface;
1085 vtable_key.member = m->member;
1087 v = hashmap_get(bus->vtable_methods, &vtable_key);
1089 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1092 if (bus->nodes_modified)
1096 /* Then, look for a known property */
1097 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1100 get = streq(m->member, "Get");
1102 if (get || streq(m->member, "Set")) {
1104 r = sd_bus_message_rewind(m, true);
1108 vtable_key.path = (char*) p;
1110 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1114 v = hashmap_get(bus->vtable_properties, &vtable_key);
1116 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1121 } else if (streq(m->member, "GetAll")) {
1124 r = sd_bus_message_rewind(m, true);
1128 r = sd_bus_message_read(m, "s", &iface);
1135 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1140 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1142 r = process_introspect(bus, m, n, require_fallback, found_object);
1146 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1148 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1153 if (bus->nodes_modified)
1156 if (!*found_object) {
1157 r = bus_node_exists(bus, n, m->path, require_fallback);
1161 *found_object = true;
1167 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1170 bool found_object = false;
1175 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1181 if (hashmap_isempty(bus->nodes))
1184 pl = strlen(m->path);
1188 bus->nodes_modified = false;
1190 r = object_find_and_run(bus, m, m->path, false, &found_object);
1194 /* Look for fallback prefixes */
1195 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1197 if (bus->nodes_modified)
1200 r = object_find_and_run(bus, m, prefix, true, &found_object);
1205 } while (bus->nodes_modified);
1210 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1211 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1212 r = sd_bus_reply_method_errorf(
1214 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1215 "Unknown property or interface.");
1217 r = sd_bus_reply_method_errorf(
1219 SD_BUS_ERROR_UNKNOWN_METHOD,
1220 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1228 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1229 struct node *n, *parent;
1236 assert(path[0] == '/');
1238 n = hashmap_get(bus->nodes, path);
1242 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1250 if (streq(path, "/"))
1253 e = strrchr(path, '/');
1256 p = strndupa(path, MAX(1, path - e));
1258 parent = bus_node_allocate(bus, p);
1265 n = new0(struct node, 1);
1272 r = hashmap_put(bus->nodes, s, n);
1280 LIST_PREPEND(siblings, parent->child, n);
1285 static void bus_node_gc(sd_bus *b, struct node *n) {
1298 assert(hashmap_remove(b->nodes, n->path) == n);
1301 LIST_REMOVE(siblings, n->parent->child, n);
1304 bus_node_gc(b, n->parent);
1308 static int bus_add_object(
1312 sd_bus_message_handler_t callback,
1315 struct node_callback *c;
1319 assert_return(bus, -EINVAL);
1320 assert_return(object_path_is_valid(path), -EINVAL);
1321 assert_return(callback, -EINVAL);
1322 assert_return(!bus_pid_changed(bus), -ECHILD);
1324 n = bus_node_allocate(bus, path);
1328 c = new0(struct node_callback, 1);
1335 c->callback = callback;
1336 c->userdata = userdata;
1337 c->is_fallback = fallback;
1339 LIST_PREPEND(callbacks, n->callbacks, c);
1340 bus->nodes_modified = true;
1346 bus_node_gc(bus, n);
1350 static int bus_remove_object(
1354 sd_bus_message_handler_t callback,
1357 struct node_callback *c;
1360 assert_return(bus, -EINVAL);
1361 assert_return(object_path_is_valid(path), -EINVAL);
1362 assert_return(callback, -EINVAL);
1363 assert_return(!bus_pid_changed(bus), -ECHILD);
1365 n = hashmap_get(bus->nodes, path);
1369 LIST_FOREACH(callbacks, c, n->callbacks)
1370 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1375 LIST_REMOVE(callbacks, n->callbacks, c);
1378 bus_node_gc(bus, n);
1379 bus->nodes_modified = true;
1384 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1385 return bus_add_object(bus, false, path, callback, userdata);
1388 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1389 return bus_remove_object(bus, false, path, callback, userdata);
1392 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1393 return bus_add_object(bus, true, prefix, callback, userdata);
1396 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1397 return bus_remove_object(bus, true, prefix, callback, userdata);
1400 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1406 if (w->interface && w->node && w->vtable) {
1407 const sd_bus_vtable *v;
1409 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1410 struct vtable_member *x = NULL;
1414 case _SD_BUS_VTABLE_METHOD: {
1415 struct vtable_member key;
1417 key.path = w->node->path;
1418 key.interface = w->interface;
1419 key.member = v->x.method.member;
1421 x = hashmap_remove(bus->vtable_methods, &key);
1425 case _SD_BUS_VTABLE_PROPERTY:
1426 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1427 struct vtable_member key;
1429 key.path = w->node->path;
1430 key.interface = w->interface;
1431 key.member = v->x.property.member;
1432 x = hashmap_remove(bus->vtable_properties, &key);
1444 static unsigned vtable_member_hash_func(const void *a) {
1445 const struct vtable_member *m = a;
1450 string_hash_func(m->path) ^
1451 string_hash_func(m->interface) ^
1452 string_hash_func(m->member);
1455 static int vtable_member_compare_func(const void *a, const void *b) {
1456 const struct vtable_member *x = a, *y = b;
1462 r = strcmp(x->path, y->path);
1466 r = strcmp(x->interface, y->interface);
1470 return strcmp(x->member, y->member);
1473 static int add_object_vtable_internal(
1476 const char *interface,
1477 const sd_bus_vtable *vtable,
1479 sd_bus_object_find_t find,
1482 struct node_vtable *c = NULL, *i;
1483 const sd_bus_vtable *v;
1487 assert_return(bus, -EINVAL);
1488 assert_return(object_path_is_valid(path), -EINVAL);
1489 assert_return(interface_name_is_valid(interface), -EINVAL);
1490 assert_return(vtable, -EINVAL);
1491 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1492 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1493 assert_return(!bus_pid_changed(bus), -ECHILD);
1495 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1499 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1503 n = bus_node_allocate(bus, path);
1507 LIST_FOREACH(vtables, i, n->vtables) {
1508 if (streq(i->interface, interface)) {
1513 if (i->is_fallback != fallback) {
1519 c = new0(struct node_vtable, 1);
1526 c->is_fallback = fallback;
1528 c->userdata = userdata;
1531 c->interface = strdup(interface);
1532 if (!c->interface) {
1537 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1541 case _SD_BUS_VTABLE_METHOD: {
1542 struct vtable_member *m;
1544 if (!member_name_is_valid(v->x.method.member) ||
1545 !signature_is_valid(strempty(v->x.method.signature), false) ||
1546 !signature_is_valid(strempty(v->x.method.result), false) ||
1547 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1548 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1553 m = new0(struct vtable_member, 1);
1561 m->interface = c->interface;
1562 m->member = v->x.method.member;
1565 r = hashmap_put(bus->vtable_methods, m, m);
1574 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1576 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1583 case _SD_BUS_VTABLE_PROPERTY: {
1584 struct vtable_member *m;
1586 if (!member_name_is_valid(v->x.property.member) ||
1587 !signature_is_single(v->x.property.signature, false) ||
1588 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1589 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1590 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1596 m = new0(struct vtable_member, 1);
1604 m->interface = c->interface;
1605 m->member = v->x.property.member;
1608 r = hashmap_put(bus->vtable_properties, m, m);
1617 case _SD_BUS_VTABLE_SIGNAL:
1619 if (!member_name_is_valid(v->x.signal.member) ||
1620 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1633 LIST_PREPEND(vtables, n->vtables, c);
1634 bus->nodes_modified = true;
1640 free_node_vtable(bus, c);
1642 bus_node_gc(bus, n);
1646 static int remove_object_vtable_internal(
1649 const char *interface,
1652 struct node_vtable *c;
1655 assert_return(bus, -EINVAL);
1656 assert_return(object_path_is_valid(path), -EINVAL);
1657 assert_return(interface_name_is_valid(interface), -EINVAL);
1658 assert_return(!bus_pid_changed(bus), -ECHILD);
1660 n = hashmap_get(bus->nodes, path);
1664 LIST_FOREACH(vtables, c, n->vtables)
1665 if (streq(c->interface, interface) && c->is_fallback == fallback)
1671 LIST_REMOVE(vtables, n->vtables, c);
1673 free_node_vtable(bus, c);
1674 bus_node_gc(bus, n);
1676 bus->nodes_modified = true;
1681 int sd_bus_add_object_vtable(
1684 const char *interface,
1685 const sd_bus_vtable *vtable,
1688 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1691 int sd_bus_remove_object_vtable(
1694 const char *interface) {
1696 return remove_object_vtable_internal(bus, path, interface, false);
1699 int sd_bus_add_fallback_vtable(
1702 const char *interface,
1703 const sd_bus_vtable *vtable,
1704 sd_bus_object_find_t find,
1707 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1710 int sd_bus_remove_fallback_vtable(
1713 const char *interface) {
1715 return remove_object_vtable_internal(bus, path, interface, true);
1718 int sd_bus_add_node_enumerator(
1721 sd_bus_node_enumerator_t callback,
1724 struct node_enumerator *c;
1728 assert_return(bus, -EINVAL);
1729 assert_return(object_path_is_valid(path), -EINVAL);
1730 assert_return(callback, -EINVAL);
1731 assert_return(!bus_pid_changed(bus), -ECHILD);
1733 n = bus_node_allocate(bus, path);
1737 c = new0(struct node_enumerator, 1);
1744 c->callback = callback;
1745 c->userdata = userdata;
1747 LIST_PREPEND(enumerators, n->enumerators, c);
1749 bus->nodes_modified = true;
1755 bus_node_gc(bus, n);
1759 int sd_bus_remove_node_enumerator(
1762 sd_bus_node_enumerator_t callback,
1765 struct node_enumerator *c;
1768 assert_return(bus, -EINVAL);
1769 assert_return(object_path_is_valid(path), -EINVAL);
1770 assert_return(callback, -EINVAL);
1771 assert_return(!bus_pid_changed(bus), -ECHILD);
1773 n = hashmap_get(bus->nodes, path);
1777 LIST_FOREACH(enumerators, c, n->enumerators)
1778 if (c->callback == callback && c->userdata == userdata)
1784 LIST_REMOVE(enumerators, n->enumerators, c);
1787 bus_node_gc(bus, n);
1789 bus->nodes_modified = true;
1794 static int emit_properties_changed_on_interface(
1798 const char *interface,
1799 bool require_fallback,
1802 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1803 bool has_invalidating = false;
1804 struct vtable_member key;
1805 struct node_vtable *c;
1816 n = hashmap_get(bus->nodes, prefix);
1820 LIST_FOREACH(vtables, c, n->vtables) {
1821 if (require_fallback && !c->is_fallback)
1824 if (streq(c->interface, interface))
1831 r = node_vtable_get_userdata(bus, path, c, &u);
1834 if (bus->nodes_modified)
1837 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1841 r = sd_bus_message_append(m, "s", interface);
1845 r = sd_bus_message_open_container(m, 'a', "{sv}");
1850 key.interface = interface;
1852 STRV_FOREACH(property, names) {
1853 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1854 struct vtable_member *v;
1856 assert_return(member_name_is_valid(*property), -EINVAL);
1858 key.member = *property;
1859 v = hashmap_get(bus->vtable_properties, &key);
1863 assert(c == v->parent);
1864 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1866 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1867 has_invalidating = true;
1871 r = sd_bus_message_open_container(m, 'e', "sv");
1875 r = sd_bus_message_append(m, "s", *property);
1879 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1883 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1886 if (bus->nodes_modified)
1889 r = sd_bus_message_close_container(m);
1893 r = sd_bus_message_close_container(m);
1898 r = sd_bus_message_close_container(m);
1902 r = sd_bus_message_open_container(m, 'a', "s");
1906 if (has_invalidating) {
1907 STRV_FOREACH(property, names) {
1908 struct vtable_member *v;
1910 key.member = *property;
1911 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1912 assert(c == v->parent);
1914 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1917 r = sd_bus_message_append(m, "s", *property);
1923 r = sd_bus_message_close_container(m);
1927 r = sd_bus_send(bus, m, NULL);
1934 int sd_bus_emit_properties_changed_strv(
1937 const char *interface,
1940 BUS_DONT_DESTROY(bus);
1944 assert_return(bus, -EINVAL);
1945 assert_return(object_path_is_valid(path), -EINVAL);
1946 assert_return(interface_name_is_valid(interface), -EINVAL);
1947 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1948 assert_return(!bus_pid_changed(bus), -ECHILD);
1950 if (strv_isempty(names))
1954 bus->nodes_modified = false;
1956 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1959 if (bus->nodes_modified)
1962 prefix = alloca(strlen(path) + 1);
1963 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1964 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1967 if (bus->nodes_modified)
1971 } while (bus->nodes_modified);
1976 int sd_bus_emit_properties_changed(
1979 const char *interface,
1980 const char *name, ...) {
1984 assert_return(bus, -EINVAL);
1985 assert_return(object_path_is_valid(path), -EINVAL);
1986 assert_return(interface_name_is_valid(interface), -EINVAL);
1987 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1988 assert_return(!bus_pid_changed(bus), -ECHILD);
1993 names = strv_from_stdarg_alloca(name);
1995 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
1998 static int interfaces_added_append_one_prefix(
2003 const char *interface,
2004 bool require_fallback) {
2006 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2007 struct node_vtable *c;
2018 n = hashmap_get(bus->nodes, prefix);
2022 LIST_FOREACH(vtables, c, n->vtables) {
2023 if (require_fallback && !c->is_fallback)
2026 if (streq(c->interface, interface))
2033 r = node_vtable_get_userdata(bus, path, c, &u);
2036 if (bus->nodes_modified)
2039 r = sd_bus_message_append_basic(m, 's', interface);
2043 r = sd_bus_message_open_container(m, 'a', "{sv}");
2047 r = vtable_append_all_properties(bus, m,path, c, u, &error);
2050 if (bus->nodes_modified)
2053 r = sd_bus_message_close_container(m);
2060 static int interfaces_added_append_one(
2064 const char *interface) {
2074 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2077 if (bus->nodes_modified)
2080 prefix = alloca(strlen(path) + 1);
2081 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2082 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2085 if (bus->nodes_modified)
2092 int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2093 BUS_DONT_DESTROY(bus);
2095 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2099 assert_return(bus, -EINVAL);
2100 assert_return(object_path_is_valid(path), -EINVAL);
2101 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2102 assert_return(!bus_pid_changed(bus), -ECHILD);
2104 if (strv_isempty(interfaces))
2108 bus->nodes_modified = false;
2111 m = sd_bus_message_unref(m);
2113 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2117 r = sd_bus_message_append_basic(m, 'o', path);
2121 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2125 STRV_FOREACH(i, interfaces) {
2126 assert_return(interface_name_is_valid(*i), -EINVAL);
2128 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2132 r = interfaces_added_append_one(bus, m, path, *i);
2136 if (bus->nodes_modified)
2139 r = sd_bus_message_close_container(m);
2144 if (bus->nodes_modified)
2147 r = sd_bus_message_close_container(m);
2151 } while (bus->nodes_modified);
2153 return sd_bus_send(bus, m, NULL);
2156 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2159 assert_return(bus, -EINVAL);
2160 assert_return(object_path_is_valid(path), -EINVAL);
2161 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2162 assert_return(!bus_pid_changed(bus), -ECHILD);
2164 interfaces = strv_from_stdarg_alloca(interface);
2166 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2169 int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2170 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2173 assert_return(bus, -EINVAL);
2174 assert_return(object_path_is_valid(path), -EINVAL);
2175 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2176 assert_return(!bus_pid_changed(bus), -ECHILD);
2178 if (strv_isempty(interfaces))
2181 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2185 r = sd_bus_message_append_basic(m, 'o', path);
2189 r = sd_bus_message_append_strv(m, interfaces);
2193 return sd_bus_send(bus, m, NULL);
2196 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2199 assert_return(bus, -EINVAL);
2200 assert_return(object_path_is_valid(path), -EINVAL);
2201 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2202 assert_return(!bus_pid_changed(bus), -ECHILD);
2204 interfaces = strv_from_stdarg_alloca(interface);
2206 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2209 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2212 assert_return(bus, -EINVAL);
2213 assert_return(object_path_is_valid(path), -EINVAL);
2214 assert_return(!bus_pid_changed(bus), -ECHILD);
2216 n = bus_node_allocate(bus, path);
2220 n->object_manager = true;
2221 bus->nodes_modified = true;
2225 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2228 assert_return(bus, -EINVAL);
2229 assert_return(object_path_is_valid(path), -EINVAL);
2230 assert_return(!bus_pid_changed(bus), -ECHILD);
2232 n = hashmap_get(bus->nodes, path);
2236 if (!n->object_manager)
2239 n->object_manager = false;
2240 bus->nodes_modified = true;
2241 bus_node_gc(bus, n);