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 */
880 r = object_manager_serialize_path(bus, reply, p, path, true, error);
884 if (sd_bus_error_is_set(error))
892 static int process_get_managed_objects(
896 bool require_fallback,
897 bool *found_object) {
899 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
900 _cleanup_set_free_free_ Set *s = NULL;
907 assert(found_object);
909 if (!bus_node_with_object_manager(bus, n))
912 r = get_child_nodes(bus, m->path, n, &s);
916 r = sd_bus_message_new_method_return(bus, m, &reply);
920 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
924 empty = set_isempty(s);
926 struct node_vtable *c;
928 /* Hmm, so we have no children? Then let's check
929 * whether we exist at all, i.e. whether at least one
932 LIST_FOREACH(vtables, c, n->vtables) {
934 if (require_fallback && !c->is_fallback)
952 SET_FOREACH(path, s, i) {
953 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
955 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
959 if (sd_bus_error_is_set(&error)) {
960 r = sd_bus_reply_method_error(bus, m, &error);
969 r = sd_bus_message_close_container(reply);
973 r = sd_bus_send(bus, reply, NULL);
980 static int object_find_and_run(
984 bool require_fallback,
985 bool *found_object) {
988 struct vtable_member vtable_key, *v;
994 assert(found_object);
996 n = hashmap_get(bus->nodes, p);
1000 /* First, try object callbacks */
1001 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1005 if (!m->interface || !m->member)
1008 /* Then, look for a known method */
1009 vtable_key.path = (char*) p;
1010 vtable_key.interface = m->interface;
1011 vtable_key.member = m->member;
1013 v = hashmap_get(bus->vtable_methods, &vtable_key);
1015 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1020 /* Then, look for a known property */
1021 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1024 get = streq(m->member, "Get");
1026 if (get || streq(m->member, "Set")) {
1028 r = sd_bus_message_rewind(m, true);
1032 vtable_key.path = (char*) p;
1034 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1038 v = hashmap_get(bus->vtable_properties, &vtable_key);
1040 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1045 } else if (streq(m->member, "GetAll")) {
1048 r = sd_bus_message_rewind(m, true);
1052 r = sd_bus_message_read(m, "s", &iface);
1059 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1064 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1066 r = process_introspect(bus, m, n, require_fallback, found_object);
1070 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1072 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1077 if (!*found_object) {
1078 r = bus_node_exists(bus, n, m->path, require_fallback);
1083 *found_object = true;
1089 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1092 bool found_object = false;
1097 if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
1103 if (hashmap_isempty(bus->nodes))
1106 pl = strlen(m->path);
1110 bus->nodes_modified = false;
1112 r = object_find_and_run(bus, m, m->path, false, &found_object);
1116 /* Look for fallback prefixes */
1124 if (bus->nodes_modified)
1127 e = strrchr(p, '/');
1134 r = object_find_and_run(bus, m, p, true, &found_object);
1139 } while (bus->nodes_modified);
1144 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1145 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1146 r = sd_bus_reply_method_errorf(
1148 "org.freedesktop.DBus.Error.UnknownProperty",
1149 "Unknown property or interface.");
1151 r = sd_bus_reply_method_errorf(
1153 "org.freedesktop.DBus.Error.UnknownMethod",
1154 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1162 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1163 struct node *n, *parent;
1170 assert(path[0] == '/');
1172 n = hashmap_get(bus->nodes, path);
1176 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1184 if (streq(path, "/"))
1187 e = strrchr(path, '/');
1190 p = strndupa(path, MAX(1, path - e));
1192 parent = bus_node_allocate(bus, p);
1199 n = new0(struct node, 1);
1206 r = hashmap_put(bus->nodes, s, n);
1214 LIST_PREPEND(struct node, siblings, parent->child, n);
1219 static void bus_node_gc(sd_bus *b, struct node *n) {
1232 assert(hashmap_remove(b->nodes, n->path) == n);
1235 LIST_REMOVE(struct node, siblings, n->parent->child, n);
1238 bus_node_gc(b, n->parent);
1242 static int bus_add_object(
1246 sd_bus_message_handler_t callback,
1249 struct node_callback *c;
1253 assert_return(bus, -EINVAL);
1254 assert_return(object_path_is_valid(path), -EINVAL);
1255 assert_return(callback, -EINVAL);
1256 assert_return(!bus_pid_changed(bus), -ECHILD);
1258 n = bus_node_allocate(bus, path);
1262 c = new0(struct node_callback, 1);
1269 c->callback = callback;
1270 c->userdata = userdata;
1271 c->is_fallback = fallback;
1273 LIST_PREPEND(struct node_callback, callbacks, n->callbacks, c);
1278 bus_node_gc(bus, n);
1282 static int bus_remove_object(
1286 sd_bus_message_handler_t callback,
1289 struct node_callback *c;
1292 assert_return(bus, -EINVAL);
1293 assert_return(object_path_is_valid(path), -EINVAL);
1294 assert_return(callback, -EINVAL);
1295 assert_return(!bus_pid_changed(bus), -ECHILD);
1297 n = hashmap_get(bus->nodes, path);
1301 LIST_FOREACH(callbacks, c, n->callbacks)
1302 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1307 LIST_REMOVE(struct node_callback, callbacks, n->callbacks, c);
1310 bus_node_gc(bus, n);
1315 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1316 return bus_add_object(bus, false, path, callback, userdata);
1319 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1320 return bus_remove_object(bus, false, path, callback, userdata);
1323 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1324 return bus_add_object(bus, true, prefix, callback, userdata);
1327 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1328 return bus_remove_object(bus, true, prefix, callback, userdata);
1331 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1337 if (w->interface && w->node && w->vtable) {
1338 const sd_bus_vtable *v;
1340 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1341 struct vtable_member *x = NULL;
1345 case _SD_BUS_VTABLE_METHOD: {
1346 struct vtable_member key;
1348 key.path = w->node->path;
1349 key.interface = w->interface;
1350 key.member = v->x.method.member;
1352 x = hashmap_remove(bus->vtable_methods, &key);
1356 case _SD_BUS_VTABLE_PROPERTY:
1357 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1358 struct vtable_member key;
1360 key.path = w->node->path;
1361 key.interface = w->interface;
1362 key.member = v->x.property.member;
1363 x = hashmap_remove(bus->vtable_properties, &key);
1375 static unsigned vtable_member_hash_func(const void *a) {
1376 const struct vtable_member *m = a;
1381 string_hash_func(m->path) ^
1382 string_hash_func(m->interface) ^
1383 string_hash_func(m->member);
1386 static int vtable_member_compare_func(const void *a, const void *b) {
1387 const struct vtable_member *x = a, *y = b;
1393 r = strcmp(x->path, y->path);
1397 r = strcmp(x->interface, y->interface);
1401 return strcmp(x->member, y->member);
1404 static int add_object_vtable_internal(
1407 const char *interface,
1408 const sd_bus_vtable *vtable,
1410 sd_bus_object_find_t find,
1413 struct node_vtable *c = NULL, *i;
1414 const sd_bus_vtable *v;
1418 assert_return(bus, -EINVAL);
1419 assert_return(object_path_is_valid(path), -EINVAL);
1420 assert_return(interface_name_is_valid(interface), -EINVAL);
1421 assert_return(vtable, -EINVAL);
1422 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1423 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1424 assert_return(!bus_pid_changed(bus), -ECHILD);
1426 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1430 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1434 n = bus_node_allocate(bus, path);
1438 LIST_FOREACH(vtables, i, n->vtables) {
1439 if (streq(i->interface, interface)) {
1444 if (i->is_fallback != fallback) {
1450 c = new0(struct node_vtable, 1);
1457 c->is_fallback = fallback;
1459 c->userdata = userdata;
1462 c->interface = strdup(interface);
1463 if (!c->interface) {
1468 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1472 case _SD_BUS_VTABLE_METHOD: {
1473 struct vtable_member *m;
1475 if (!member_name_is_valid(v->x.method.member) ||
1476 !signature_is_valid(strempty(v->x.method.signature), false) ||
1477 !signature_is_valid(strempty(v->x.method.result), false) ||
1478 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1479 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1484 m = new0(struct vtable_member, 1);
1492 m->interface = c->interface;
1493 m->member = v->x.method.member;
1496 r = hashmap_put(bus->vtable_methods, m, m);
1505 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1507 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1514 case _SD_BUS_VTABLE_PROPERTY: {
1515 struct vtable_member *m;
1517 if (!member_name_is_valid(v->x.property.member) ||
1518 !signature_is_single(v->x.property.signature, false) ||
1519 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
1520 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1521 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1527 m = new0(struct vtable_member, 1);
1535 m->interface = c->interface;
1536 m->member = v->x.property.member;
1539 r = hashmap_put(bus->vtable_properties, m, m);
1548 case _SD_BUS_VTABLE_SIGNAL:
1550 if (!member_name_is_valid(v->x.signal.member) ||
1551 !signature_is_single(strempty(v->x.signal.signature), false)) {
1564 LIST_PREPEND(struct node_vtable, vtables, n->vtables, c);
1569 free_node_vtable(bus, c);
1571 bus_node_gc(bus, n);
1575 static int remove_object_vtable_internal(
1578 const char *interface,
1581 struct node_vtable *c;
1584 assert_return(bus, -EINVAL);
1585 assert_return(object_path_is_valid(path), -EINVAL);
1586 assert_return(interface_name_is_valid(interface), -EINVAL);
1587 assert_return(!bus_pid_changed(bus), -ECHILD);
1589 n = hashmap_get(bus->nodes, path);
1593 LIST_FOREACH(vtables, c, n->vtables)
1594 if (streq(c->interface, interface) && c->is_fallback == fallback)
1600 LIST_REMOVE(struct node_vtable, vtables, n->vtables, c);
1602 free_node_vtable(bus, c);
1603 bus_node_gc(bus, n);
1608 int sd_bus_add_object_vtable(
1611 const char *interface,
1612 const sd_bus_vtable *vtable,
1615 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1618 int sd_bus_remove_object_vtable(
1621 const char *interface) {
1623 return remove_object_vtable_internal(bus, path, interface, false);
1626 int sd_bus_add_fallback_vtable(
1629 const char *interface,
1630 const sd_bus_vtable *vtable,
1631 sd_bus_object_find_t find,
1634 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1637 int sd_bus_remove_fallback_vtable(
1640 const char *interface) {
1642 return remove_object_vtable_internal(bus, path, interface, true);
1645 int sd_bus_add_node_enumerator(
1648 sd_bus_node_enumerator_t callback,
1651 struct node_enumerator *c;
1655 assert_return(bus, -EINVAL);
1656 assert_return(object_path_is_valid(path), -EINVAL);
1657 assert_return(callback, -EINVAL);
1658 assert_return(!bus_pid_changed(bus), -ECHILD);
1660 n = bus_node_allocate(bus, path);
1664 c = new0(struct node_enumerator, 1);
1671 c->callback = callback;
1672 c->userdata = userdata;
1674 LIST_PREPEND(struct node_enumerator, enumerators, n->enumerators, c);
1679 bus_node_gc(bus, n);
1683 int sd_bus_remove_node_enumerator(
1686 sd_bus_node_enumerator_t callback,
1689 struct node_enumerator *c;
1692 assert_return(bus, -EINVAL);
1693 assert_return(object_path_is_valid(path), -EINVAL);
1694 assert_return(callback, -EINVAL);
1695 assert_return(!bus_pid_changed(bus), -ECHILD);
1697 n = hashmap_get(bus->nodes, path);
1701 LIST_FOREACH(enumerators, c, n->enumerators)
1702 if (c->callback == callback && c->userdata == userdata)
1708 LIST_REMOVE(struct node_enumerator, enumerators, n->enumerators, c);
1711 bus_node_gc(bus, n);
1716 static int emit_properties_changed_on_interface(
1720 const char *interface,
1721 bool require_fallback,
1724 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1725 bool has_invalidating = false;
1726 struct vtable_member key;
1727 struct node_vtable *c;
1738 n = hashmap_get(bus->nodes, prefix);
1742 LIST_FOREACH(vtables, c, n->vtables) {
1743 if (require_fallback && !c->is_fallback)
1746 if (streq(c->interface, interface))
1753 r = node_vtable_get_userdata(bus, path, c, &u);
1757 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1761 r = sd_bus_message_append(m, "s", interface);
1765 r = sd_bus_message_open_container(m, 'a', "{sv}");
1770 key.interface = interface;
1772 STRV_FOREACH(property, names) {
1773 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1774 struct vtable_member *v;
1776 assert_return(member_name_is_valid(*property), -EINVAL);
1778 key.member = *property;
1779 v = hashmap_get(bus->vtable_properties, &key);
1783 assert(c == v->parent);
1784 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1786 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1787 has_invalidating = true;
1791 r = sd_bus_message_open_container(m, 'e', "sv");
1795 r = sd_bus_message_append(m, "s", *property);
1799 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1803 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1807 if (sd_bus_error_is_set(&error))
1808 return bus_error_to_errno(&error);
1810 r = sd_bus_message_close_container(m);
1814 r = sd_bus_message_close_container(m);
1819 r = sd_bus_message_close_container(m);
1823 r = sd_bus_message_open_container(m, 'a', "s");
1827 if (has_invalidating) {
1828 STRV_FOREACH(property, names) {
1829 struct vtable_member *v;
1831 key.member = *property;
1832 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1833 assert(c == v->parent);
1835 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1838 r = sd_bus_message_append(m, "s", *property);
1844 r = sd_bus_message_close_container(m);
1848 r = sd_bus_send(bus, m, NULL);
1855 int sd_bus_emit_properties_changed_strv(
1858 const char *interface,
1864 assert_return(bus, -EINVAL);
1865 assert_return(object_path_is_valid(path), -EINVAL);
1866 assert_return(interface_name_is_valid(interface), -EINVAL);
1867 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1868 assert_return(!bus_pid_changed(bus), -ECHILD);
1870 if (strv_isempty(names))
1873 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1888 e = strrchr(p, '/');
1895 r = emit_properties_changed_on_interface(bus, p, path, interface, true, names);
1904 int sd_bus_emit_properties_changed(
1907 const char *interface,
1908 const char *name, ...) {
1910 _cleanup_strv_free_ char **names = NULL;
1913 assert_return(bus, -EINVAL);
1914 assert_return(object_path_is_valid(path), -EINVAL);
1915 assert_return(interface_name_is_valid(interface), -EINVAL);
1916 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1917 assert_return(!bus_pid_changed(bus), -ECHILD);
1923 names = strv_new_ap(name, ap);
1929 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
1932 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interfaces, ...) {
1936 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interfaces, ...) {
1940 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
1943 assert_return(bus, -EINVAL);
1944 assert_return(object_path_is_valid(path), -EINVAL);
1945 assert_return(!bus_pid_changed(bus), -ECHILD);
1947 n = bus_node_allocate(bus, path);
1951 n->object_manager = true;
1955 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
1958 assert_return(bus, -EINVAL);
1959 assert_return(object_path_is_valid(path), -EINVAL);
1960 assert_return(!bus_pid_changed(bus), -ECHILD);
1962 n = hashmap_get(bus->nodes, path);
1966 if (!n->object_manager)
1969 n->object_manager = false;
1970 bus_node_gc(bus, n);