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(sd_bus *bus, const char *prefix, struct node_enumerator *first, Set *s) {
86 struct node_enumerator *c;
93 LIST_FOREACH(enumerators, c, first) {
94 char **children = NULL, **k;
96 r = c->callback(bus, prefix, &children, c->userdata);
100 STRV_FOREACH(k, children) {
106 if (!object_path_is_valid(*k) && object_path_startswith(*k, prefix)) {
112 r = set_consume(s, *k);
123 static int add_subtree_to_set(sd_bus *bus, const char *prefix, struct node *n, Set *s) {
132 r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
136 LIST_FOREACH(siblings, i, n->child) {
143 r = set_consume(s, t);
144 if (r < 0 && r != -EEXIST)
147 r = add_subtree_to_set(bus, prefix, i, s);
155 static int get_child_nodes(sd_bus *bus, const char *prefix, struct node *n, Set **_s) {
163 s = set_new(string_hash_func, string_compare_func);
167 r = add_subtree_to_set(bus, prefix, n, s);
177 static int node_callbacks_run(
180 struct node_callback *first,
181 bool require_fallback,
182 bool *found_object) {
184 struct node_callback *c;
189 assert(found_object);
191 LIST_FOREACH(callbacks, c, first) {
192 if (require_fallback && !c->is_fallback)
195 *found_object = true;
197 if (c->last_iteration == bus->iteration_counter)
200 r = sd_bus_message_rewind(m, true);
204 r = c->callback(bus, m, c->userdata);
212 static int method_callbacks_run(
215 struct vtable_member *c,
216 bool require_fallback,
217 bool *found_object) {
219 const char *signature;
226 assert(found_object);
228 if (require_fallback && !c->parent->is_fallback)
231 r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
235 *found_object = true;
237 r = sd_bus_message_rewind(m, true);
241 r = sd_bus_message_get_signature(m, true, &signature);
245 if (!streq(strempty(c->vtable->x.method.signature), signature)) {
246 r = sd_bus_reply_method_errorf(bus, m,
247 "org.freedesktop.DBus.Error.InvalidArgs",
248 "Invalid arguments '%s' to call %s:%s, expecting '%s'.",
249 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
256 if (c->vtable->x.method.handler)
257 return c->vtable->x.method.handler(bus, m, u);
259 /* If the method callback is NULL, make this a successful NOP */
260 r = sd_bus_reply_method_return(bus, m, NULL);
267 static int invoke_property_get(
269 const sd_bus_vtable *v,
271 const char *interface,
272 const char *property,
283 if (v->x.property.get)
284 return v->x.property.get(bus, path, interface, property, m, error, userdata);
286 /* Automatic handling if no callback is defined. */
288 assert(signature_is_single(v->x.property.signature, false));
289 assert(bus_type_is_basic(v->x.property.signature[0]));
291 switch (v->x.property.signature[0]) {
293 case SD_BUS_TYPE_STRING:
294 case SD_BUS_TYPE_OBJECT_PATH:
295 case SD_BUS_TYPE_SIGNATURE:
296 p = *(char**) userdata;
304 r = sd_bus_message_append_basic(m, v->x.property.signature[0], p);
311 static int invoke_property_set(
313 const sd_bus_vtable *v,
315 const char *interface,
316 const char *property,
317 sd_bus_message *value,
326 if (v->x.property.set)
327 return v->x.property.set(bus, path, interface, property, value, error, userdata);
329 /* Automatic handling if no callback is defined. */
331 assert(signature_is_single(v->x.property.signature, false));
332 assert(bus_type_is_basic(v->x.property.signature[0]));
334 switch (v->x.property.signature[0]) {
336 case SD_BUS_TYPE_STRING:
337 case SD_BUS_TYPE_OBJECT_PATH:
338 case SD_BUS_TYPE_SIGNATURE: {
342 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
350 free(*(char**) userdata);
351 *(char**) userdata = n;
357 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
367 static int property_get_set_callbacks_run(
370 struct vtable_member *c,
371 bool require_fallback,
373 bool *found_object) {
375 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
376 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
382 assert(found_object);
384 if (require_fallback && !c->parent->is_fallback)
387 r = vtable_property_get_userdata(bus, m->path, c, &u);
391 *found_object = true;
393 r = sd_bus_message_new_method_return(bus, m, &reply);
397 c->last_iteration = bus->iteration_counter;
400 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
404 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
408 if (sd_bus_error_is_set(&error)) {
409 r = sd_bus_reply_method_error(bus, m, &error);
416 r = sd_bus_message_close_container(reply);
421 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
422 sd_bus_error_setf(&error, "org.freedesktop.DBus.Error.PropertyReadOnly", "Property '%s' is not writable.", c->member);
424 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
428 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &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_exit_container(m);
446 r = sd_bus_send(bus, reply, NULL);
453 static int vtable_append_all_properties(
455 sd_bus_message *reply,
457 struct node_vtable *c,
459 sd_bus_error *error) {
461 const sd_bus_vtable *v;
468 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
469 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
472 r = sd_bus_message_open_container(reply, 'e', "sv");
476 r = sd_bus_message_append(reply, "s", c->interface);
480 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
484 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, error, vtable_property_convert_userdata(v, userdata));
488 if (sd_bus_error_is_set(error))
491 r = sd_bus_message_close_container(reply);
495 r = sd_bus_message_close_container(reply);
503 static int property_get_all_callbacks_run(
506 struct node_vtable *first,
507 bool require_fallback,
509 bool *found_object) {
511 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
512 struct node_vtable *c;
513 bool found_interface = false;
518 assert(found_object);
520 r = sd_bus_message_new_method_return(bus, m, &reply);
524 r = sd_bus_message_open_container(reply, 'a', "{sv}");
528 LIST_FOREACH(vtables, c, first) {
529 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
532 if (require_fallback && !c->is_fallback)
535 r = node_vtable_get_userdata(bus, m->path, c, &u);
541 *found_object = true;
543 if (iface && !streq(c->interface, iface))
545 found_interface = true;
547 c->last_iteration = bus->iteration_counter;
549 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
553 if (sd_bus_error_is_set(&error)) {
554 r = sd_bus_reply_method_error(bus, m, &error);
562 if (!found_interface) {
563 r = sd_bus_reply_method_errorf(
565 "org.freedesktop.DBus.Error.UnknownInterface",
566 "Unknown interface '%s'.", iface);
573 r = sd_bus_message_close_container(reply);
577 r = sd_bus_send(bus, reply, NULL);
584 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
587 if (n->object_manager)
591 return bus_node_with_object_manager(bus, n->parent);
596 static bool bus_node_exists(sd_bus *bus, struct node *n, const char *path, bool require_fallback) {
597 struct node_vtable *c;
598 struct node_callback *k;
603 /* Tests if there's anything attached directly to this node
604 * for the specified path */
606 LIST_FOREACH(callbacks, k, n->callbacks) {
607 if (require_fallback && !k->is_fallback)
613 LIST_FOREACH(vtables, c, n->vtables) {
615 if (require_fallback && !c->is_fallback)
618 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
622 return !require_fallback && (n->enumerators || n->object_manager);
625 static int process_introspect(
629 bool require_fallback,
630 bool *found_object) {
632 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
633 _cleanup_set_free_free_ Set *s = NULL;
634 struct introspect intro;
635 struct node_vtable *c;
642 assert(found_object);
644 r = get_child_nodes(bus, m->path, n, &s);
648 r = introspect_begin(&intro);
652 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
656 empty = set_isempty(s);
658 LIST_FOREACH(vtables, c, n->vtables) {
659 if (require_fallback && !c->is_fallback)
662 r = node_vtable_get_userdata(bus, m->path, c, NULL);
670 r = introspect_write_interface(&intro, c->interface, c->vtable);
676 /* Nothing?, let's see if we exist at all, and if not
677 * refuse to do anything */
678 r = bus_node_exists(bus, n, m->path, require_fallback);
686 *found_object = true;
688 r = introspect_write_child_nodes(&intro, s, m->path);
692 r = introspect_finish(&intro, bus, m, &reply);
696 r = sd_bus_send(bus, reply, NULL);
703 introspect_free(&intro);
707 static int object_manager_serialize_vtable(
709 sd_bus_message *reply,
711 struct node_vtable *c,
712 sd_bus_error *error) {
723 r = node_vtable_get_userdata(bus, path, c, &u);
727 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
731 r = sd_bus_message_append(reply, "s", c->interface);
735 r = sd_bus_message_open_container(reply, 'a', "{sv}");
739 r = vtable_append_all_properties(bus, reply, path, c, u, error);
743 r = sd_bus_message_close_container(reply);
747 r = sd_bus_message_close_container(reply);
754 static int object_manager_serialize_path(
756 sd_bus_message *reply,
759 bool require_fallback,
760 sd_bus_error *error) {
762 struct node_vtable *i;
772 n = hashmap_get(bus->nodes, prefix);
776 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
780 r = sd_bus_message_append(reply, "o", path);
784 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
788 LIST_FOREACH(vtables, i, n->vtables) {
790 if (require_fallback && !i->is_fallback)
793 r = object_manager_serialize_vtable(bus, reply, path, i, error);
796 if (sd_bus_error_is_set(error))
800 r = sd_bus_message_close_container(reply);
804 r = sd_bus_message_close_container(reply);
811 static int object_manager_serialize_path_and_fallbacks(
813 sd_bus_message *reply,
815 sd_bus_error *error) {
825 /* First, add all vtables registered for this path */
826 r = object_manager_serialize_path(bus, reply, path, path, false, error);
829 if (sd_bus_error_is_set(error))
832 /* Second, add fallback vtables registered for any of the prefixes */
847 r = object_manager_serialize_path(bus, reply, p, path, true, error);
851 if (sd_bus_error_is_set(error))
859 static int process_get_managed_objects(
863 bool require_fallback,
864 bool *found_object) {
866 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
867 _cleanup_set_free_free_ Set *s = NULL;
874 assert(found_object);
876 if (!bus_node_with_object_manager(bus, n))
879 r = get_child_nodes(bus, m->path, n, &s);
883 r = sd_bus_message_new_method_return(bus, m, &reply);
887 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
891 empty = set_isempty(s);
893 struct node_vtable *c;
895 /* Hmm, so we have no children? Then let's check
896 * whether we exist at all, i.e. whether at least one
899 LIST_FOREACH(vtables, c, n->vtables) {
901 if (require_fallback && !c->is_fallback)
919 SET_FOREACH(path, s, i) {
920 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
922 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
926 if (sd_bus_error_is_set(&error)) {
927 r = sd_bus_reply_method_error(bus, m, &error);
936 r = sd_bus_message_close_container(reply);
940 r = sd_bus_send(bus, reply, NULL);
947 static int object_find_and_run(
951 bool require_fallback,
952 bool *found_object) {
955 struct vtable_member vtable_key, *v;
961 assert(found_object);
963 n = hashmap_get(bus->nodes, p);
967 /* First, try object callbacks */
968 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
972 if (!m->interface || !m->member)
975 /* Then, look for a known method */
976 vtable_key.path = (char*) p;
977 vtable_key.interface = m->interface;
978 vtable_key.member = m->member;
980 v = hashmap_get(bus->vtable_methods, &vtable_key);
982 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
987 /* Then, look for a known property */
988 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
991 get = streq(m->member, "Get");
993 if (get || streq(m->member, "Set")) {
995 r = sd_bus_message_rewind(m, true);
999 vtable_key.path = (char*) p;
1001 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1005 v = hashmap_get(bus->vtable_properties, &vtable_key);
1007 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1012 } else if (streq(m->member, "GetAll")) {
1015 r = sd_bus_message_rewind(m, true);
1019 r = sd_bus_message_read(m, "s", &iface);
1026 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1031 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1033 r = process_introspect(bus, m, n, require_fallback, found_object);
1037 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1039 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1044 if (!*found_object) {
1045 r = bus_node_exists(bus, n, m->path, require_fallback);
1050 *found_object = true;
1056 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1059 bool found_object = false;
1064 if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
1070 if (hashmap_isempty(bus->nodes))
1073 pl = strlen(m->path);
1077 bus->nodes_modified = false;
1079 r = object_find_and_run(bus, m, m->path, false, &found_object);
1083 /* Look for fallback prefixes */
1091 if (bus->nodes_modified)
1094 e = strrchr(p, '/');
1101 r = object_find_and_run(bus, m, p, true, &found_object);
1106 } while (bus->nodes_modified);
1111 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1112 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1113 r = sd_bus_reply_method_errorf(
1115 "org.freedesktop.DBus.Error.UnknownProperty",
1116 "Unknown property or interface.");
1118 r = sd_bus_reply_method_errorf(
1120 "org.freedesktop.DBus.Error.UnknownMethod",
1121 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1129 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1130 struct node *n, *parent;
1137 assert(path[0] == '/');
1139 n = hashmap_get(bus->nodes, path);
1143 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1151 if (streq(path, "/"))
1154 e = strrchr(path, '/');
1157 p = strndupa(path, MAX(1, path - e));
1159 parent = bus_node_allocate(bus, p);
1166 n = new0(struct node, 1);
1173 r = hashmap_put(bus->nodes, s, n);
1181 LIST_PREPEND(struct node, siblings, parent->child, n);
1186 static void bus_node_gc(sd_bus *b, struct node *n) {
1199 assert(hashmap_remove(b->nodes, n->path) == n);
1202 LIST_REMOVE(struct node, siblings, n->parent->child, n);
1205 bus_node_gc(b, n->parent);
1209 static int bus_add_object(
1213 sd_bus_message_handler_t callback,
1216 struct node_callback *c;
1222 if (!object_path_is_valid(path))
1226 if (bus_pid_changed(b))
1229 n = bus_node_allocate(b, path);
1233 c = new0(struct node_callback, 1);
1240 c->callback = callback;
1241 c->userdata = userdata;
1242 c->is_fallback = fallback;
1244 LIST_PREPEND(struct node_callback, callbacks, n->callbacks, c);
1253 static int bus_remove_object(
1257 sd_bus_message_handler_t callback,
1260 struct node_callback *c;
1265 if (!object_path_is_valid(path))
1269 if (bus_pid_changed(bus))
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;
1354 string_hash_func(m->path) ^
1355 string_hash_func(m->interface) ^
1356 string_hash_func(m->member);
1359 static int vtable_member_compare_func(const void *a, const void *b) {
1360 const struct vtable_member *x = a, *y = b;
1363 r = strcmp(x->path, y->path);
1367 r = strcmp(x->interface, y->interface);
1371 return strcmp(x->member, y->member);
1374 static int add_object_vtable_internal(
1377 const char *interface,
1378 const sd_bus_vtable *vtable,
1380 sd_bus_object_find_t find,
1383 struct node_vtable *c = NULL, *i;
1384 const sd_bus_vtable *v;
1390 if (!object_path_is_valid(path))
1392 if (!interface_name_is_valid(interface))
1394 if (!vtable || vtable[0].type != _SD_BUS_VTABLE_START || vtable[0].x.start.element_size != sizeof(struct sd_bus_vtable))
1396 if (bus_pid_changed(bus))
1399 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1403 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1407 n = bus_node_allocate(bus, path);
1411 LIST_FOREACH(vtables, i, n->vtables) {
1412 if (streq(i->interface, interface)) {
1417 if (i->is_fallback != fallback) {
1423 c = new0(struct node_vtable, 1);
1430 c->is_fallback = fallback;
1432 c->userdata = userdata;
1435 c->interface = strdup(interface);
1436 if (!c->interface) {
1441 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1445 case _SD_BUS_VTABLE_METHOD: {
1446 struct vtable_member *m;
1448 if (!member_name_is_valid(v->x.method.member) ||
1449 !signature_is_valid(strempty(v->x.method.signature), false) ||
1450 !signature_is_valid(strempty(v->x.method.result), false) ||
1451 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1452 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1457 m = new0(struct vtable_member, 1);
1465 m->interface = c->interface;
1466 m->member = v->x.method.member;
1469 r = hashmap_put(bus->vtable_methods, m, m);
1478 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1480 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1487 case _SD_BUS_VTABLE_PROPERTY: {
1488 struct vtable_member *m;
1490 if (!member_name_is_valid(v->x.property.member) ||
1491 !signature_is_single(v->x.property.signature, false) ||
1492 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
1493 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1494 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1500 m = new0(struct vtable_member, 1);
1508 m->interface = c->interface;
1509 m->member = v->x.property.member;
1512 r = hashmap_put(bus->vtable_properties, m, m);
1521 case _SD_BUS_VTABLE_SIGNAL:
1523 if (!member_name_is_valid(v->x.signal.member) ||
1524 !signature_is_single(strempty(v->x.signal.signature), false)) {
1537 LIST_PREPEND(struct node_vtable, vtables, n->vtables, c);
1542 free_node_vtable(bus, c);
1544 bus_node_gc(bus, n);
1548 static int remove_object_vtable_internal(
1551 const char *interface,
1554 struct node_vtable *c;
1559 if (!object_path_is_valid(path))
1561 if (!interface_name_is_valid(interface))
1563 if (bus_pid_changed(bus))
1566 n = hashmap_get(bus->nodes, path);
1570 LIST_FOREACH(vtables, c, n->vtables)
1571 if (streq(c->interface, interface) && c->is_fallback == fallback)
1577 LIST_REMOVE(struct node_vtable, vtables, n->vtables, c);
1579 free_node_vtable(bus, c);
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;
1632 if (!object_path_is_valid(path))
1636 if (bus_pid_changed(bus))
1639 n = bus_node_allocate(bus, path);
1643 c = new0(struct node_enumerator, 1);
1650 c->callback = callback;
1651 c->userdata = userdata;
1653 LIST_PREPEND(struct node_enumerator, enumerators, n->enumerators, c);
1658 bus_node_gc(bus, n);
1662 int sd_bus_remove_node_enumerator(
1665 sd_bus_node_enumerator_t callback,
1668 struct node_enumerator *c;
1673 if (!object_path_is_valid(path))
1677 if (bus_pid_changed(bus))
1680 n = hashmap_get(bus->nodes, path);
1684 LIST_FOREACH(enumerators, c, n->enumerators)
1685 if (c->callback == callback && c->userdata == userdata)
1691 LIST_REMOVE(struct node_enumerator, enumerators, n->enumerators, c);
1694 bus_node_gc(bus, n);
1699 static int emit_properties_changed_on_interface(
1703 const char *interface,
1704 bool require_fallback,
1707 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1708 bool has_invalidating = false;
1709 struct vtable_member key;
1710 struct node_vtable *c;
1720 n = hashmap_get(bus->nodes, prefix);
1724 LIST_FOREACH(vtables, c, n->vtables) {
1725 if (require_fallback && !c->is_fallback)
1728 if (streq(c->interface, interface))
1735 r = node_vtable_get_userdata(bus, path, c, &u);
1739 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1743 r = sd_bus_message_append(m, "s", interface);
1747 r = sd_bus_message_open_container(m, 'a', "{sv}");
1752 key.interface = interface;
1754 STRV_FOREACH(property, names) {
1755 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1756 struct vtable_member *v;
1758 key.member = *property;
1759 v = hashmap_get(bus->vtable_properties, &key);
1763 assert(c == v->parent);
1765 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1767 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1768 has_invalidating = true;
1772 r = sd_bus_message_open_container(m, 'e', "sv");
1776 r = sd_bus_message_append(m, "s", *property);
1780 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1784 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1788 if (sd_bus_error_is_set(&error))
1789 return bus_error_to_errno(&error);
1791 r = sd_bus_message_close_container(m);
1795 r = sd_bus_message_close_container(m);
1800 r = sd_bus_message_close_container(m);
1804 r = sd_bus_message_open_container(m, 'a', "s");
1808 if (has_invalidating) {
1809 STRV_FOREACH(property, names) {
1810 struct vtable_member *v;
1812 key.member = *property;
1813 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1814 assert(c == v->parent);
1816 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1819 r = sd_bus_message_append(m, "s", *property);
1825 r = sd_bus_message_close_container(m);
1829 r = sd_bus_send(bus, m, NULL);
1836 int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) {
1842 if (!object_path_is_valid(path))
1844 if (!interface_name_is_valid(interface))
1847 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1862 e = strrchr(p, '/');
1869 r = emit_properties_changed_on_interface(bus, p, path, interface, true, names);
1878 int sd_bus_emit_properties_changed(sd_bus *bus, const char *path, const char *interface, const char *name, ...) {
1879 _cleanup_strv_free_ char **names = NULL;
1883 names = strv_new_ap(name, ap);
1889 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
1892 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interfaces, ...) {
1896 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interfaces, ...) {
1900 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
1905 if (!object_path_is_valid(path))
1907 if (bus_pid_changed(bus))
1910 n = bus_node_allocate(bus, path);
1914 n->object_manager = true;
1918 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
1923 if (!object_path_is_valid(path))
1925 if (bus_pid_changed(bus))
1928 n = hashmap_get(bus->nodes, path);
1932 if (!n->object_manager)
1935 n->object_manager = false;
1936 bus_node_gc(bus, n);