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,
745 sd_bus_error *error) {
756 r = node_vtable_get_userdata(bus, path, c, &u);
760 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
764 r = sd_bus_message_append(reply, "s", c->interface);
768 r = sd_bus_message_open_container(reply, 'a', "{sv}");
772 r = vtable_append_all_properties(bus, reply, path, c, u, error);
776 r = sd_bus_message_close_container(reply);
780 r = sd_bus_message_close_container(reply);
787 static int object_manager_serialize_path(
789 sd_bus_message *reply,
792 bool require_fallback,
793 sd_bus_error *error) {
795 struct node_vtable *i;
805 n = hashmap_get(bus->nodes, prefix);
809 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
813 r = sd_bus_message_append(reply, "o", path);
817 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
821 LIST_FOREACH(vtables, i, n->vtables) {
823 if (require_fallback && !i->is_fallback)
826 r = object_manager_serialize_vtable(bus, reply, path, i, error);
829 if (sd_bus_error_is_set(error))
833 r = sd_bus_message_close_container(reply);
837 r = sd_bus_message_close_container(reply);
844 static int object_manager_serialize_path_and_fallbacks(
846 sd_bus_message *reply,
848 sd_bus_error *error) {
858 /* First, add all vtables registered for this path */
859 r = object_manager_serialize_path(bus, reply, path, path, false, error);
862 if (sd_bus_error_is_set(error))
865 /* Second, add fallback vtables registered for any of the prefixes */
866 prefix = alloca(strlen(path) + 1);
867 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
868 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
872 if (sd_bus_error_is_set(error))
879 static int process_get_managed_objects(
883 bool require_fallback,
884 bool *found_object) {
886 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
887 _cleanup_set_free_free_ Set *s = NULL;
894 assert(found_object);
896 if (!bus_node_with_object_manager(bus, n))
899 r = get_child_nodes(bus, m->path, n, &s);
903 r = sd_bus_message_new_method_return(bus, m, &reply);
907 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
911 empty = set_isempty(s);
913 struct node_vtable *c;
915 /* Hmm, so we have no children? Then let's check
916 * whether we exist at all, i.e. whether at least one
919 LIST_FOREACH(vtables, c, n->vtables) {
921 if (require_fallback && !c->is_fallback)
939 SET_FOREACH(path, s, i) {
940 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
942 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
946 if (sd_bus_error_is_set(&error)) {
947 r = sd_bus_reply_method_error(bus, m, &error);
956 r = sd_bus_message_close_container(reply);
960 r = sd_bus_send(bus, reply, NULL);
967 static int object_find_and_run(
971 bool require_fallback,
972 bool *found_object) {
975 struct vtable_member vtable_key, *v;
981 assert(found_object);
983 n = hashmap_get(bus->nodes, p);
987 /* First, try object callbacks */
988 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
992 if (!m->interface || !m->member)
995 /* Then, look for a known method */
996 vtable_key.path = (char*) p;
997 vtable_key.interface = m->interface;
998 vtable_key.member = m->member;
1000 v = hashmap_get(bus->vtable_methods, &vtable_key);
1002 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1007 /* Then, look for a known property */
1008 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1011 get = streq(m->member, "Get");
1013 if (get || streq(m->member, "Set")) {
1015 r = sd_bus_message_rewind(m, true);
1019 vtable_key.path = (char*) p;
1021 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1025 v = hashmap_get(bus->vtable_properties, &vtable_key);
1027 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1032 } else if (streq(m->member, "GetAll")) {
1035 r = sd_bus_message_rewind(m, true);
1039 r = sd_bus_message_read(m, "s", &iface);
1046 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1051 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1053 r = process_introspect(bus, m, n, require_fallback, found_object);
1057 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1059 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1064 if (!*found_object) {
1065 r = bus_node_exists(bus, n, m->path, require_fallback);
1070 *found_object = true;
1076 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1079 bool found_object = false;
1084 if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
1090 if (hashmap_isempty(bus->nodes))
1093 pl = strlen(m->path);
1097 bus->nodes_modified = false;
1099 r = object_find_and_run(bus, m, m->path, false, &found_object);
1103 /* Look for fallback prefixes */
1104 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1106 if (bus->nodes_modified)
1109 r = object_find_and_run(bus, m, prefix, true, &found_object);
1114 } while (bus->nodes_modified);
1119 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1120 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1121 r = sd_bus_reply_method_errorf(
1123 "org.freedesktop.DBus.Error.UnknownProperty",
1124 "Unknown property or interface.");
1126 r = sd_bus_reply_method_errorf(
1128 "org.freedesktop.DBus.Error.UnknownMethod",
1129 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1137 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1138 struct node *n, *parent;
1145 assert(path[0] == '/');
1147 n = hashmap_get(bus->nodes, path);
1151 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1159 if (streq(path, "/"))
1162 e = strrchr(path, '/');
1165 p = strndupa(path, MAX(1, path - e));
1167 parent = bus_node_allocate(bus, p);
1174 n = new0(struct node, 1);
1181 r = hashmap_put(bus->nodes, s, n);
1189 LIST_PREPEND(struct node, siblings, parent->child, n);
1194 static void bus_node_gc(sd_bus *b, struct node *n) {
1207 assert(hashmap_remove(b->nodes, n->path) == n);
1210 LIST_REMOVE(struct node, siblings, n->parent->child, n);
1213 bus_node_gc(b, n->parent);
1217 static int bus_add_object(
1221 sd_bus_message_handler_t callback,
1224 struct node_callback *c;
1228 assert_return(bus, -EINVAL);
1229 assert_return(object_path_is_valid(path), -EINVAL);
1230 assert_return(callback, -EINVAL);
1231 assert_return(!bus_pid_changed(bus), -ECHILD);
1233 n = bus_node_allocate(bus, path);
1237 c = new0(struct node_callback, 1);
1244 c->callback = callback;
1245 c->userdata = userdata;
1246 c->is_fallback = fallback;
1248 LIST_PREPEND(struct node_callback, callbacks, n->callbacks, c);
1253 bus_node_gc(bus, n);
1257 static int bus_remove_object(
1261 sd_bus_message_handler_t callback,
1264 struct node_callback *c;
1267 assert_return(bus, -EINVAL);
1268 assert_return(object_path_is_valid(path), -EINVAL);
1269 assert_return(callback, -EINVAL);
1270 assert_return(!bus_pid_changed(bus), -ECHILD);
1272 n = hashmap_get(bus->nodes, path);
1276 LIST_FOREACH(callbacks, c, n->callbacks)
1277 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1282 LIST_REMOVE(struct node_callback, callbacks, n->callbacks, c);
1285 bus_node_gc(bus, n);
1290 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1291 return bus_add_object(bus, false, path, callback, userdata);
1294 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1295 return bus_remove_object(bus, false, path, callback, userdata);
1298 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1299 return bus_add_object(bus, true, prefix, callback, userdata);
1302 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1303 return bus_remove_object(bus, true, prefix, callback, userdata);
1306 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1312 if (w->interface && w->node && w->vtable) {
1313 const sd_bus_vtable *v;
1315 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1316 struct vtable_member *x = NULL;
1320 case _SD_BUS_VTABLE_METHOD: {
1321 struct vtable_member key;
1323 key.path = w->node->path;
1324 key.interface = w->interface;
1325 key.member = v->x.method.member;
1327 x = hashmap_remove(bus->vtable_methods, &key);
1331 case _SD_BUS_VTABLE_PROPERTY:
1332 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1333 struct vtable_member key;
1335 key.path = w->node->path;
1336 key.interface = w->interface;
1337 key.member = v->x.property.member;
1338 x = hashmap_remove(bus->vtable_properties, &key);
1350 static unsigned vtable_member_hash_func(const void *a) {
1351 const struct vtable_member *m = a;
1356 string_hash_func(m->path) ^
1357 string_hash_func(m->interface) ^
1358 string_hash_func(m->member);
1361 static int vtable_member_compare_func(const void *a, const void *b) {
1362 const struct vtable_member *x = a, *y = b;
1368 r = strcmp(x->path, y->path);
1372 r = strcmp(x->interface, y->interface);
1376 return strcmp(x->member, y->member);
1379 static int add_object_vtable_internal(
1382 const char *interface,
1383 const sd_bus_vtable *vtable,
1385 sd_bus_object_find_t find,
1388 struct node_vtable *c = NULL, *i;
1389 const sd_bus_vtable *v;
1393 assert_return(bus, -EINVAL);
1394 assert_return(object_path_is_valid(path), -EINVAL);
1395 assert_return(interface_name_is_valid(interface), -EINVAL);
1396 assert_return(vtable, -EINVAL);
1397 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1398 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1399 assert_return(!bus_pid_changed(bus), -ECHILD);
1401 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1405 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1409 n = bus_node_allocate(bus, path);
1413 LIST_FOREACH(vtables, i, n->vtables) {
1414 if (streq(i->interface, interface)) {
1419 if (i->is_fallback != fallback) {
1425 c = new0(struct node_vtable, 1);
1432 c->is_fallback = fallback;
1434 c->userdata = userdata;
1437 c->interface = strdup(interface);
1438 if (!c->interface) {
1443 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1447 case _SD_BUS_VTABLE_METHOD: {
1448 struct vtable_member *m;
1450 if (!member_name_is_valid(v->x.method.member) ||
1451 !signature_is_valid(strempty(v->x.method.signature), false) ||
1452 !signature_is_valid(strempty(v->x.method.result), false) ||
1453 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1454 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1459 m = new0(struct vtable_member, 1);
1467 m->interface = c->interface;
1468 m->member = v->x.method.member;
1471 r = hashmap_put(bus->vtable_methods, m, m);
1480 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1482 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1489 case _SD_BUS_VTABLE_PROPERTY: {
1490 struct vtable_member *m;
1492 if (!member_name_is_valid(v->x.property.member) ||
1493 !signature_is_single(v->x.property.signature, false) ||
1494 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
1495 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1496 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1502 m = new0(struct vtable_member, 1);
1510 m->interface = c->interface;
1511 m->member = v->x.property.member;
1514 r = hashmap_put(bus->vtable_properties, m, m);
1523 case _SD_BUS_VTABLE_SIGNAL:
1525 if (!member_name_is_valid(v->x.signal.member) ||
1526 !signature_is_single(strempty(v->x.signal.signature), false)) {
1539 LIST_PREPEND(struct node_vtable, vtables, n->vtables, c);
1544 free_node_vtable(bus, c);
1546 bus_node_gc(bus, n);
1550 static int remove_object_vtable_internal(
1553 const char *interface,
1556 struct node_vtable *c;
1559 assert_return(bus, -EINVAL);
1560 assert_return(object_path_is_valid(path), -EINVAL);
1561 assert_return(interface_name_is_valid(interface), -EINVAL);
1562 assert_return(!bus_pid_changed(bus), -ECHILD);
1564 n = hashmap_get(bus->nodes, path);
1568 LIST_FOREACH(vtables, c, n->vtables)
1569 if (streq(c->interface, interface) && c->is_fallback == fallback)
1575 LIST_REMOVE(struct node_vtable, vtables, n->vtables, c);
1577 free_node_vtable(bus, c);
1578 bus_node_gc(bus, n);
1583 int sd_bus_add_object_vtable(
1586 const char *interface,
1587 const sd_bus_vtable *vtable,
1590 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1593 int sd_bus_remove_object_vtable(
1596 const char *interface) {
1598 return remove_object_vtable_internal(bus, path, interface, false);
1601 int sd_bus_add_fallback_vtable(
1604 const char *interface,
1605 const sd_bus_vtable *vtable,
1606 sd_bus_object_find_t find,
1609 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1612 int sd_bus_remove_fallback_vtable(
1615 const char *interface) {
1617 return remove_object_vtable_internal(bus, path, interface, true);
1620 int sd_bus_add_node_enumerator(
1623 sd_bus_node_enumerator_t callback,
1626 struct node_enumerator *c;
1630 assert_return(bus, -EINVAL);
1631 assert_return(object_path_is_valid(path), -EINVAL);
1632 assert_return(callback, -EINVAL);
1633 assert_return(!bus_pid_changed(bus), -ECHILD);
1635 n = bus_node_allocate(bus, path);
1639 c = new0(struct node_enumerator, 1);
1646 c->callback = callback;
1647 c->userdata = userdata;
1649 LIST_PREPEND(struct node_enumerator, enumerators, n->enumerators, c);
1654 bus_node_gc(bus, n);
1658 int sd_bus_remove_node_enumerator(
1661 sd_bus_node_enumerator_t callback,
1664 struct node_enumerator *c;
1667 assert_return(bus, -EINVAL);
1668 assert_return(object_path_is_valid(path), -EINVAL);
1669 assert_return(callback, -EINVAL);
1670 assert_return(!bus_pid_changed(bus), -ECHILD);
1672 n = hashmap_get(bus->nodes, path);
1676 LIST_FOREACH(enumerators, c, n->enumerators)
1677 if (c->callback == callback && c->userdata == userdata)
1683 LIST_REMOVE(struct node_enumerator, enumerators, n->enumerators, c);
1686 bus_node_gc(bus, n);
1691 static int emit_properties_changed_on_interface(
1695 const char *interface,
1696 bool require_fallback,
1699 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1700 bool has_invalidating = false;
1701 struct vtable_member key;
1702 struct node_vtable *c;
1713 n = hashmap_get(bus->nodes, prefix);
1717 LIST_FOREACH(vtables, c, n->vtables) {
1718 if (require_fallback && !c->is_fallback)
1721 if (streq(c->interface, interface))
1728 r = node_vtable_get_userdata(bus, path, c, &u);
1732 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1736 r = sd_bus_message_append(m, "s", interface);
1740 r = sd_bus_message_open_container(m, 'a', "{sv}");
1745 key.interface = interface;
1747 STRV_FOREACH(property, names) {
1748 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1749 struct vtable_member *v;
1751 assert_return(member_name_is_valid(*property), -EINVAL);
1753 key.member = *property;
1754 v = hashmap_get(bus->vtable_properties, &key);
1758 assert(c == v->parent);
1759 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1761 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1762 has_invalidating = true;
1766 r = sd_bus_message_open_container(m, 'e', "sv");
1770 r = sd_bus_message_append(m, "s", *property);
1774 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1778 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1782 if (sd_bus_error_is_set(&error))
1783 return bus_error_to_errno(&error);
1785 r = sd_bus_message_close_container(m);
1789 r = sd_bus_message_close_container(m);
1794 r = sd_bus_message_close_container(m);
1798 r = sd_bus_message_open_container(m, 'a', "s");
1802 if (has_invalidating) {
1803 STRV_FOREACH(property, names) {
1804 struct vtable_member *v;
1806 key.member = *property;
1807 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1808 assert(c == v->parent);
1810 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1813 r = sd_bus_message_append(m, "s", *property);
1819 r = sd_bus_message_close_container(m);
1823 r = sd_bus_send(bus, m, NULL);
1830 int sd_bus_emit_properties_changed_strv(
1833 const char *interface,
1839 assert_return(bus, -EINVAL);
1840 assert_return(object_path_is_valid(path), -EINVAL);
1841 assert_return(interface_name_is_valid(interface), -EINVAL);
1842 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1843 assert_return(!bus_pid_changed(bus), -ECHILD);
1845 if (strv_isempty(names))
1848 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1852 prefix = alloca(strlen(path) + 1);
1853 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1854 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1862 int sd_bus_emit_properties_changed(
1865 const char *interface,
1866 const char *name, ...) {
1868 _cleanup_strv_free_ char **names = NULL;
1871 assert_return(bus, -EINVAL);
1872 assert_return(object_path_is_valid(path), -EINVAL);
1873 assert_return(interface_name_is_valid(interface), -EINVAL);
1874 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1875 assert_return(!bus_pid_changed(bus), -ECHILD);
1881 names = strv_new_ap(name, ap);
1887 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
1890 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interfaces, ...) {
1894 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interfaces, ...) {
1898 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
1901 assert_return(bus, -EINVAL);
1902 assert_return(object_path_is_valid(path), -EINVAL);
1903 assert_return(!bus_pid_changed(bus), -ECHILD);
1905 n = bus_node_allocate(bus, path);
1909 n->object_manager = true;
1913 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
1916 assert_return(bus, -EINVAL);
1917 assert_return(object_path_is_valid(path), -EINVAL);
1918 assert_return(!bus_pid_changed(bus), -ECHILD);
1920 n = hashmap_get(bus->nodes, path);
1924 if (!n->object_manager)
1927 n->object_manager = false;
1928 bus_node_gc(bus, n);