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"
31 static int node_vtable_get_userdata(
34 struct node_vtable *c,
46 r = c->find(bus, path, c->interface, &u, u);
57 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
60 return (uint8_t*) u + p->x.property.offset;
63 static int vtable_property_get_userdata(
66 struct vtable_member *p,
77 r = node_vtable_get_userdata(bus, path, p->parent, &u);
81 *userdata = vtable_property_convert_userdata(p->vtable, u);
85 static int add_enumerated_to_set(
88 struct node_enumerator *first,
91 struct node_enumerator *c;
98 LIST_FOREACH(enumerators, c, first) {
99 char **children = NULL, **k;
101 r = c->callback(bus, prefix, &children, c->userdata);
105 STRV_FOREACH(k, children) {
111 if (!object_path_is_valid(*k) && object_path_startswith(*k, prefix)) {
117 r = set_consume(s, *k);
128 static int add_subtree_to_set(
142 r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
146 LIST_FOREACH(siblings, i, n->child) {
153 r = set_consume(s, t);
154 if (r < 0 && r != -EEXIST)
157 r = add_subtree_to_set(bus, prefix, i, s);
165 static int get_child_nodes(
179 s = set_new(string_hash_func, string_compare_func);
183 r = add_subtree_to_set(bus, prefix, n, s);
193 static int node_callbacks_run(
196 struct node_callback *first,
197 bool require_fallback,
198 bool *found_object) {
200 struct node_callback *c;
205 assert(found_object);
207 LIST_FOREACH(callbacks, c, first) {
208 if (require_fallback && !c->is_fallback)
211 *found_object = true;
213 if (c->last_iteration == bus->iteration_counter)
216 r = sd_bus_message_rewind(m, true);
220 r = c->callback(bus, m, c->userdata);
228 static int method_callbacks_run(
231 struct vtable_member *c,
232 bool require_fallback,
233 bool *found_object) {
235 const char *signature;
242 assert(found_object);
244 if (require_fallback && !c->parent->is_fallback)
247 r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
251 *found_object = true;
253 r = sd_bus_message_rewind(m, true);
257 r = sd_bus_message_get_signature(m, true, &signature);
261 if (!streq(strempty(c->vtable->x.method.signature), signature)) {
262 r = sd_bus_reply_method_errorf(bus, m,
263 "org.freedesktop.DBus.Error.InvalidArgs",
264 "Invalid arguments '%s' to call %s:%s, expecting '%s'.",
265 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
272 if (c->vtable->x.method.handler)
273 return c->vtable->x.method.handler(bus, m, u);
275 /* If the method callback is NULL, make this a successful NOP */
276 r = sd_bus_reply_method_return(bus, m, NULL);
283 static int invoke_property_get(
285 const sd_bus_vtable *v,
287 const char *interface,
288 const char *property,
303 if (v->x.property.get)
304 return v->x.property.get(bus, path, interface, property, m, error, userdata);
306 /* Automatic handling if no callback is defined. */
308 assert(signature_is_single(v->x.property.signature, false));
309 assert(bus_type_is_basic(v->x.property.signature[0]));
311 switch (v->x.property.signature[0]) {
313 case SD_BUS_TYPE_STRING:
314 case SD_BUS_TYPE_OBJECT_PATH:
315 case SD_BUS_TYPE_SIGNATURE:
316 p = *(char**) userdata;
324 r = sd_bus_message_append_basic(m, v->x.property.signature[0], p);
331 static int invoke_property_set(
333 const sd_bus_vtable *v,
335 const char *interface,
336 const char *property,
337 sd_bus_message *value,
350 if (v->x.property.set)
351 return v->x.property.set(bus, path, interface, property, value, error, userdata);
353 /* Automatic handling if no callback is defined. */
355 assert(signature_is_single(v->x.property.signature, false));
356 assert(bus_type_is_basic(v->x.property.signature[0]));
358 switch (v->x.property.signature[0]) {
360 case SD_BUS_TYPE_STRING:
361 case SD_BUS_TYPE_OBJECT_PATH:
362 case SD_BUS_TYPE_SIGNATURE: {
366 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
374 free(*(char**) userdata);
375 *(char**) userdata = n;
381 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
391 static int property_get_set_callbacks_run(
394 struct vtable_member *c,
395 bool require_fallback,
397 bool *found_object) {
399 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
400 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
407 assert(found_object);
409 if (require_fallback && !c->parent->is_fallback)
412 r = vtable_property_get_userdata(bus, m->path, c, &u);
416 *found_object = true;
418 r = sd_bus_message_new_method_return(bus, m, &reply);
422 c->last_iteration = bus->iteration_counter;
425 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
429 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
433 if (sd_bus_error_is_set(&error)) {
434 r = sd_bus_reply_method_error(bus, m, &error);
441 r = sd_bus_message_close_container(reply);
446 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
447 sd_bus_error_setf(&error, "org.freedesktop.DBus.Error.PropertyReadOnly", "Property '%s' is not writable.", c->member);
449 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
453 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
458 if (sd_bus_error_is_set(&error)) {
459 r = sd_bus_reply_method_error(bus, m, &error);
466 r = sd_bus_message_exit_container(m);
471 r = sd_bus_send(bus, reply, NULL);
478 static int vtable_append_all_properties(
480 sd_bus_message *reply,
482 struct node_vtable *c,
484 sd_bus_error *error) {
486 const sd_bus_vtable *v;
494 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
495 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
498 r = sd_bus_message_open_container(reply, 'e', "sv");
502 r = sd_bus_message_append(reply, "s", v->x.property.member);
506 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
510 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, error, vtable_property_convert_userdata(v, userdata));
514 if (sd_bus_error_is_set(error))
517 r = sd_bus_message_close_container(reply);
521 r = sd_bus_message_close_container(reply);
529 static int property_get_all_callbacks_run(
532 struct node_vtable *first,
533 bool require_fallback,
535 bool *found_object) {
537 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
538 struct node_vtable *c;
539 bool found_interface = false;
544 assert(found_object);
546 r = sd_bus_message_new_method_return(bus, m, &reply);
550 r = sd_bus_message_open_container(reply, 'a', "{sv}");
554 LIST_FOREACH(vtables, c, first) {
555 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
558 if (require_fallback && !c->is_fallback)
561 r = node_vtable_get_userdata(bus, m->path, c, &u);
567 *found_object = true;
569 if (iface && !streq(c->interface, iface))
571 found_interface = true;
573 c->last_iteration = bus->iteration_counter;
575 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
579 if (sd_bus_error_is_set(&error)) {
580 r = sd_bus_reply_method_error(bus, m, &error);
588 if (!found_interface) {
589 r = sd_bus_reply_method_errorf(
591 "org.freedesktop.DBus.Error.UnknownInterface",
592 "Unknown interface '%s'.", iface);
599 r = sd_bus_message_close_container(reply);
603 r = sd_bus_send(bus, reply, NULL);
610 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
614 if (n->object_manager)
618 return bus_node_with_object_manager(bus, n->parent);
623 static bool bus_node_exists(
627 bool require_fallback) {
629 struct node_vtable *c;
630 struct node_callback *k;
636 /* Tests if there's anything attached directly to this node
637 * for the specified path */
639 LIST_FOREACH(callbacks, k, n->callbacks) {
640 if (require_fallback && !k->is_fallback)
646 LIST_FOREACH(vtables, c, n->vtables) {
648 if (require_fallback && !c->is_fallback)
651 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
655 return !require_fallback && (n->enumerators || n->object_manager);
658 static int process_introspect(
662 bool require_fallback,
663 bool *found_object) {
665 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
666 _cleanup_set_free_free_ Set *s = NULL;
667 struct introspect intro;
668 struct node_vtable *c;
675 assert(found_object);
677 r = get_child_nodes(bus, m->path, n, &s);
681 r = introspect_begin(&intro);
685 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
689 empty = set_isempty(s);
691 LIST_FOREACH(vtables, c, n->vtables) {
692 if (require_fallback && !c->is_fallback)
695 r = node_vtable_get_userdata(bus, m->path, c, NULL);
703 r = introspect_write_interface(&intro, c->interface, c->vtable);
709 /* Nothing?, let's see if we exist at all, and if not
710 * refuse to do anything */
711 r = bus_node_exists(bus, n, m->path, require_fallback);
719 *found_object = true;
721 r = introspect_write_child_nodes(&intro, s, m->path);
725 r = introspect_finish(&intro, bus, m, &reply);
729 r = sd_bus_send(bus, reply, NULL);
736 introspect_free(&intro);
740 static int object_manager_serialize_vtable(
742 sd_bus_message *reply,
744 struct node_vtable *c,
756 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
760 r = sd_bus_message_append(reply, "s", c->interface);
764 r = sd_bus_message_open_container(reply, 'a', "{sv}");
768 r = vtable_append_all_properties(bus, reply, path, c, userdata, error);
772 r = sd_bus_message_close_container(reply);
776 r = sd_bus_message_close_container(reply);
783 static int object_manager_serialize_path(
785 sd_bus_message *reply,
788 bool require_fallback,
789 sd_bus_error *error) {
791 struct node_vtable *i;
793 bool found_something = false;
802 n = hashmap_get(bus->nodes, prefix);
806 LIST_FOREACH(vtables, i, n->vtables) {
809 if (require_fallback && !i->is_fallback)
812 r = node_vtable_get_userdata(bus, path, i, &u);
818 if (!found_something) {
819 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
823 r = sd_bus_message_append(reply, "o", path);
827 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
831 found_something = true;
834 r = object_manager_serialize_vtable(bus, reply, path, i, error, u);
837 if (sd_bus_error_is_set(error))
841 if (found_something) {
842 r = sd_bus_message_close_container(reply);
846 r = sd_bus_message_close_container(reply);
854 static int object_manager_serialize_path_and_fallbacks(
856 sd_bus_message *reply,
858 sd_bus_error *error) {
868 /* First, add all vtables registered for this path */
869 r = object_manager_serialize_path(bus, reply, path, path, false, error);
872 if (sd_bus_error_is_set(error))
875 /* Second, add fallback vtables registered for any of the prefixes */
876 prefix = alloca(strlen(path) + 1);
877 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
878 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
882 if (sd_bus_error_is_set(error))
889 static int process_get_managed_objects(
893 bool require_fallback,
894 bool *found_object) {
896 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
897 _cleanup_set_free_free_ Set *s = NULL;
904 assert(found_object);
906 if (!bus_node_with_object_manager(bus, n))
909 r = get_child_nodes(bus, m->path, n, &s);
913 r = sd_bus_message_new_method_return(bus, m, &reply);
917 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
921 empty = set_isempty(s);
923 struct node_vtable *c;
925 /* Hmm, so we have no children? Then let's check
926 * whether we exist at all, i.e. whether at least one
929 LIST_FOREACH(vtables, c, n->vtables) {
931 if (require_fallback && !c->is_fallback)
949 SET_FOREACH(path, s, i) {
950 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
952 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
956 if (sd_bus_error_is_set(&error)) {
957 r = sd_bus_reply_method_error(bus, m, &error);
966 r = sd_bus_message_close_container(reply);
970 r = sd_bus_send(bus, reply, NULL);
977 static int object_find_and_run(
981 bool require_fallback,
982 bool *found_object) {
985 struct vtable_member vtable_key, *v;
991 assert(found_object);
993 n = hashmap_get(bus->nodes, p);
997 /* First, try object callbacks */
998 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1002 if (!m->interface || !m->member)
1005 /* Then, look for a known method */
1006 vtable_key.path = (char*) p;
1007 vtable_key.interface = m->interface;
1008 vtable_key.member = m->member;
1010 v = hashmap_get(bus->vtable_methods, &vtable_key);
1012 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1017 /* Then, look for a known property */
1018 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1021 get = streq(m->member, "Get");
1023 if (get || streq(m->member, "Set")) {
1025 r = sd_bus_message_rewind(m, true);
1029 vtable_key.path = (char*) p;
1031 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1035 v = hashmap_get(bus->vtable_properties, &vtable_key);
1037 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1042 } else if (streq(m->member, "GetAll")) {
1045 r = sd_bus_message_rewind(m, true);
1049 r = sd_bus_message_read(m, "s", &iface);
1056 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1061 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1063 r = process_introspect(bus, m, n, require_fallback, found_object);
1067 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1069 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1074 if (!*found_object) {
1075 r = bus_node_exists(bus, n, m->path, require_fallback);
1080 *found_object = true;
1086 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1089 bool found_object = false;
1094 if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
1100 if (hashmap_isempty(bus->nodes))
1103 pl = strlen(m->path);
1107 bus->nodes_modified = false;
1109 r = object_find_and_run(bus, m, m->path, false, &found_object);
1113 /* Look for fallback prefixes */
1114 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1116 if (bus->nodes_modified)
1119 r = object_find_and_run(bus, m, prefix, true, &found_object);
1124 } while (bus->nodes_modified);
1129 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1130 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1131 r = sd_bus_reply_method_errorf(
1133 "org.freedesktop.DBus.Error.UnknownProperty",
1134 "Unknown property or interface.");
1136 r = sd_bus_reply_method_errorf(
1138 "org.freedesktop.DBus.Error.UnknownMethod",
1139 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1147 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1148 struct node *n, *parent;
1155 assert(path[0] == '/');
1157 n = hashmap_get(bus->nodes, path);
1161 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1169 if (streq(path, "/"))
1172 e = strrchr(path, '/');
1175 p = strndupa(path, MAX(1, path - e));
1177 parent = bus_node_allocate(bus, p);
1184 n = new0(struct node, 1);
1191 r = hashmap_put(bus->nodes, s, n);
1199 LIST_PREPEND(struct node, siblings, parent->child, n);
1204 static void bus_node_gc(sd_bus *b, struct node *n) {
1217 assert(hashmap_remove(b->nodes, n->path) == n);
1220 LIST_REMOVE(struct node, siblings, n->parent->child, n);
1223 bus_node_gc(b, n->parent);
1227 static int bus_add_object(
1231 sd_bus_message_handler_t callback,
1234 struct node_callback *c;
1238 assert_return(bus, -EINVAL);
1239 assert_return(object_path_is_valid(path), -EINVAL);
1240 assert_return(callback, -EINVAL);
1241 assert_return(!bus_pid_changed(bus), -ECHILD);
1243 n = bus_node_allocate(bus, path);
1247 c = new0(struct node_callback, 1);
1254 c->callback = callback;
1255 c->userdata = userdata;
1256 c->is_fallback = fallback;
1258 LIST_PREPEND(struct node_callback, callbacks, n->callbacks, c);
1263 bus_node_gc(bus, n);
1267 static int bus_remove_object(
1271 sd_bus_message_handler_t callback,
1274 struct node_callback *c;
1277 assert_return(bus, -EINVAL);
1278 assert_return(object_path_is_valid(path), -EINVAL);
1279 assert_return(callback, -EINVAL);
1280 assert_return(!bus_pid_changed(bus), -ECHILD);
1282 n = hashmap_get(bus->nodes, path);
1286 LIST_FOREACH(callbacks, c, n->callbacks)
1287 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1292 LIST_REMOVE(struct node_callback, callbacks, n->callbacks, c);
1295 bus_node_gc(bus, n);
1300 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1301 return bus_add_object(bus, false, path, callback, userdata);
1304 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1305 return bus_remove_object(bus, false, path, callback, userdata);
1308 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1309 return bus_add_object(bus, true, prefix, callback, userdata);
1312 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1313 return bus_remove_object(bus, true, prefix, callback, userdata);
1316 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1322 if (w->interface && w->node && w->vtable) {
1323 const sd_bus_vtable *v;
1325 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1326 struct vtable_member *x = NULL;
1330 case _SD_BUS_VTABLE_METHOD: {
1331 struct vtable_member key;
1333 key.path = w->node->path;
1334 key.interface = w->interface;
1335 key.member = v->x.method.member;
1337 x = hashmap_remove(bus->vtable_methods, &key);
1341 case _SD_BUS_VTABLE_PROPERTY:
1342 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1343 struct vtable_member key;
1345 key.path = w->node->path;
1346 key.interface = w->interface;
1347 key.member = v->x.property.member;
1348 x = hashmap_remove(bus->vtable_properties, &key);
1360 static unsigned vtable_member_hash_func(const void *a) {
1361 const struct vtable_member *m = a;
1366 string_hash_func(m->path) ^
1367 string_hash_func(m->interface) ^
1368 string_hash_func(m->member);
1371 static int vtable_member_compare_func(const void *a, const void *b) {
1372 const struct vtable_member *x = a, *y = b;
1378 r = strcmp(x->path, y->path);
1382 r = strcmp(x->interface, y->interface);
1386 return strcmp(x->member, y->member);
1389 static int add_object_vtable_internal(
1392 const char *interface,
1393 const sd_bus_vtable *vtable,
1395 sd_bus_object_find_t find,
1398 struct node_vtable *c = NULL, *i;
1399 const sd_bus_vtable *v;
1403 assert_return(bus, -EINVAL);
1404 assert_return(object_path_is_valid(path), -EINVAL);
1405 assert_return(interface_name_is_valid(interface), -EINVAL);
1406 assert_return(vtable, -EINVAL);
1407 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1408 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1409 assert_return(!bus_pid_changed(bus), -ECHILD);
1411 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1415 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1419 n = bus_node_allocate(bus, path);
1423 LIST_FOREACH(vtables, i, n->vtables) {
1424 if (streq(i->interface, interface)) {
1429 if (i->is_fallback != fallback) {
1435 c = new0(struct node_vtable, 1);
1442 c->is_fallback = fallback;
1444 c->userdata = userdata;
1447 c->interface = strdup(interface);
1448 if (!c->interface) {
1453 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1457 case _SD_BUS_VTABLE_METHOD: {
1458 struct vtable_member *m;
1460 if (!member_name_is_valid(v->x.method.member) ||
1461 !signature_is_valid(strempty(v->x.method.signature), false) ||
1462 !signature_is_valid(strempty(v->x.method.result), false) ||
1463 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1464 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1469 m = new0(struct vtable_member, 1);
1477 m->interface = c->interface;
1478 m->member = v->x.method.member;
1481 r = hashmap_put(bus->vtable_methods, m, m);
1490 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1492 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1499 case _SD_BUS_VTABLE_PROPERTY: {
1500 struct vtable_member *m;
1502 if (!member_name_is_valid(v->x.property.member) ||
1503 !signature_is_single(v->x.property.signature, false) ||
1504 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
1505 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1506 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1512 m = new0(struct vtable_member, 1);
1520 m->interface = c->interface;
1521 m->member = v->x.property.member;
1524 r = hashmap_put(bus->vtable_properties, m, m);
1533 case _SD_BUS_VTABLE_SIGNAL:
1535 if (!member_name_is_valid(v->x.signal.member) ||
1536 !signature_is_single(strempty(v->x.signal.signature), false)) {
1549 LIST_PREPEND(struct node_vtable, vtables, n->vtables, c);
1554 free_node_vtable(bus, c);
1556 bus_node_gc(bus, n);
1560 static int remove_object_vtable_internal(
1563 const char *interface,
1566 struct node_vtable *c;
1569 assert_return(bus, -EINVAL);
1570 assert_return(object_path_is_valid(path), -EINVAL);
1571 assert_return(interface_name_is_valid(interface), -EINVAL);
1572 assert_return(!bus_pid_changed(bus), -ECHILD);
1574 n = hashmap_get(bus->nodes, path);
1578 LIST_FOREACH(vtables, c, n->vtables)
1579 if (streq(c->interface, interface) && c->is_fallback == fallback)
1585 LIST_REMOVE(struct node_vtable, vtables, n->vtables, c);
1587 free_node_vtable(bus, c);
1588 bus_node_gc(bus, n);
1593 int sd_bus_add_object_vtable(
1596 const char *interface,
1597 const sd_bus_vtable *vtable,
1600 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1603 int sd_bus_remove_object_vtable(
1606 const char *interface) {
1608 return remove_object_vtable_internal(bus, path, interface, false);
1611 int sd_bus_add_fallback_vtable(
1614 const char *interface,
1615 const sd_bus_vtable *vtable,
1616 sd_bus_object_find_t find,
1619 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1622 int sd_bus_remove_fallback_vtable(
1625 const char *interface) {
1627 return remove_object_vtable_internal(bus, path, interface, true);
1630 int sd_bus_add_node_enumerator(
1633 sd_bus_node_enumerator_t callback,
1636 struct node_enumerator *c;
1640 assert_return(bus, -EINVAL);
1641 assert_return(object_path_is_valid(path), -EINVAL);
1642 assert_return(callback, -EINVAL);
1643 assert_return(!bus_pid_changed(bus), -ECHILD);
1645 n = bus_node_allocate(bus, path);
1649 c = new0(struct node_enumerator, 1);
1656 c->callback = callback;
1657 c->userdata = userdata;
1659 LIST_PREPEND(struct node_enumerator, enumerators, n->enumerators, c);
1664 bus_node_gc(bus, n);
1668 int sd_bus_remove_node_enumerator(
1671 sd_bus_node_enumerator_t callback,
1674 struct node_enumerator *c;
1677 assert_return(bus, -EINVAL);
1678 assert_return(object_path_is_valid(path), -EINVAL);
1679 assert_return(callback, -EINVAL);
1680 assert_return(!bus_pid_changed(bus), -ECHILD);
1682 n = hashmap_get(bus->nodes, path);
1686 LIST_FOREACH(enumerators, c, n->enumerators)
1687 if (c->callback == callback && c->userdata == userdata)
1693 LIST_REMOVE(struct node_enumerator, enumerators, n->enumerators, c);
1696 bus_node_gc(bus, n);
1701 static int emit_properties_changed_on_interface(
1705 const char *interface,
1706 bool require_fallback,
1709 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1710 bool has_invalidating = false;
1711 struct vtable_member key;
1712 struct node_vtable *c;
1723 n = hashmap_get(bus->nodes, prefix);
1727 LIST_FOREACH(vtables, c, n->vtables) {
1728 if (require_fallback && !c->is_fallback)
1731 if (streq(c->interface, interface))
1738 r = node_vtable_get_userdata(bus, path, c, &u);
1742 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1746 r = sd_bus_message_append(m, "s", interface);
1750 r = sd_bus_message_open_container(m, 'a', "{sv}");
1755 key.interface = interface;
1757 STRV_FOREACH(property, names) {
1758 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1759 struct vtable_member *v;
1761 assert_return(member_name_is_valid(*property), -EINVAL);
1763 key.member = *property;
1764 v = hashmap_get(bus->vtable_properties, &key);
1768 assert(c == v->parent);
1769 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1771 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1772 has_invalidating = true;
1776 r = sd_bus_message_open_container(m, 'e', "sv");
1780 r = sd_bus_message_append(m, "s", *property);
1784 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1788 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1792 if (sd_bus_error_is_set(&error))
1793 return bus_error_to_errno(&error);
1795 r = sd_bus_message_close_container(m);
1799 r = sd_bus_message_close_container(m);
1804 r = sd_bus_message_close_container(m);
1808 r = sd_bus_message_open_container(m, 'a', "s");
1812 if (has_invalidating) {
1813 STRV_FOREACH(property, names) {
1814 struct vtable_member *v;
1816 key.member = *property;
1817 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1818 assert(c == v->parent);
1820 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1823 r = sd_bus_message_append(m, "s", *property);
1829 r = sd_bus_message_close_container(m);
1833 r = sd_bus_send(bus, m, NULL);
1840 int sd_bus_emit_properties_changed_strv(
1843 const char *interface,
1849 assert_return(bus, -EINVAL);
1850 assert_return(object_path_is_valid(path), -EINVAL);
1851 assert_return(interface_name_is_valid(interface), -EINVAL);
1852 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1853 assert_return(!bus_pid_changed(bus), -ECHILD);
1855 if (strv_isempty(names))
1858 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1862 prefix = alloca(strlen(path) + 1);
1863 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1864 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1872 int sd_bus_emit_properties_changed(
1875 const char *interface,
1876 const char *name, ...) {
1878 _cleanup_strv_free_ char **names = NULL;
1881 assert_return(bus, -EINVAL);
1882 assert_return(object_path_is_valid(path), -EINVAL);
1883 assert_return(interface_name_is_valid(interface), -EINVAL);
1884 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1885 assert_return(!bus_pid_changed(bus), -ECHILD);
1891 names = strv_new_ap(name, ap);
1897 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
1900 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interfaces, ...) {
1904 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interfaces, ...) {
1908 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
1911 assert_return(bus, -EINVAL);
1912 assert_return(object_path_is_valid(path), -EINVAL);
1913 assert_return(!bus_pid_changed(bus), -ECHILD);
1915 n = bus_node_allocate(bus, path);
1919 n->object_manager = true;
1923 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
1926 assert_return(bus, -EINVAL);
1927 assert_return(object_path_is_valid(path), -EINVAL);
1928 assert_return(!bus_pid_changed(bus), -ECHILD);
1930 n = hashmap_get(bus->nodes, path);
1934 if (!n->object_manager)
1937 n->object_manager = false;
1938 bus_node_gc(bus, n);