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->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->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->method.signature));
256 if (c->vtable->method.handler)
257 return c->vtable->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,
284 return v->property.get(bus, path, interface, property, m, error, userdata);
286 /* Automatic handling if no callback is defined. */
288 assert(bus_type_is_basic(v->property.signature[0]));
290 switch (v->property.signature[0]) {
292 case SD_BUS_TYPE_STRING:
293 case SD_BUS_TYPE_OBJECT_PATH:
294 case SD_BUS_TYPE_SIGNATURE:
295 p = *(char**) userdata;
303 r = sd_bus_message_append_basic(m, v->property.signature[0], p);
310 static int invoke_property_set(
312 const sd_bus_vtable *v,
314 const char *interface,
315 const char *property,
316 sd_bus_message *value,
326 return v->property.set(bus, path, interface, property, value, error, userdata);
328 /* Automatic handling if no callback is defined. */
330 assert(signature_is_single(v->property.signature, false));
331 assert(bus_type_is_basic(v->property.signature[0]));
333 switch (v->property.signature[0]) {
335 case SD_BUS_TYPE_STRING:
336 case SD_BUS_TYPE_OBJECT_PATH:
337 case SD_BUS_TYPE_SIGNATURE: {
341 r = sd_bus_message_read_basic(value, v->property.signature[0], &p);
349 free(*(char**) userdata);
350 *(char**) userdata = n;
356 r = sd_bus_message_read_basic(value, v->property.signature[0], userdata);
366 static int property_get_set_callbacks_run(
369 struct vtable_member *c,
370 bool require_fallback,
372 bool *found_object) {
374 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
375 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
381 assert(found_object);
383 if (require_fallback && !c->parent->is_fallback)
386 r = vtable_property_get_userdata(bus, m->path, c, &u);
390 *found_object = true;
392 r = sd_bus_message_new_method_return(bus, m, &reply);
396 c->last_iteration = bus->iteration_counter;
399 r = sd_bus_message_open_container(reply, 'v', c->vtable->property.signature);
403 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
407 if (sd_bus_error_is_set(&error)) {
408 r = sd_bus_reply_method_error(bus, m, &error);
415 r = sd_bus_message_close_container(reply);
420 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
421 sd_bus_error_setf(&error, "org.freedesktop.DBus.Error.PropertyReadOnly", "Property '%s' is not writable.", c->member);
423 r = sd_bus_message_enter_container(m, 'v', c->vtable->property.signature);
427 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
432 if (sd_bus_error_is_set(&error)) {
433 r = sd_bus_reply_method_error(bus, m, &error);
440 r = sd_bus_message_exit_container(m);
445 r = sd_bus_send(bus, reply, NULL);
452 static int vtable_append_all_properties(
454 sd_bus_message *reply,
456 struct node_vtable *c,
458 sd_bus_error *error) {
460 const sd_bus_vtable *v;
467 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
468 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
471 r = sd_bus_message_open_container(reply, 'e', "sv");
475 r = sd_bus_message_append(reply, "s", c->interface);
479 r = sd_bus_message_open_container(reply, 'v', v->property.signature);
483 r = invoke_property_get(bus, v, path, c->interface, v->property.member, reply, error, vtable_property_convert_userdata(v, userdata));
487 if (sd_bus_error_is_set(error))
490 r = sd_bus_message_close_container(reply);
494 r = sd_bus_message_close_container(reply);
502 static int property_get_all_callbacks_run(
505 struct node_vtable *first,
506 bool require_fallback,
508 bool *found_object) {
510 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
511 struct node_vtable *c;
512 bool found_interface = false;
517 assert(found_object);
519 r = sd_bus_message_new_method_return(bus, m, &reply);
523 r = sd_bus_message_open_container(reply, 'a', "{sv}");
527 LIST_FOREACH(vtables, c, first) {
528 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
531 if (require_fallback && !c->is_fallback)
534 r = node_vtable_get_userdata(bus, m->path, c, &u);
540 *found_object = true;
542 if (iface && !streq(c->interface, iface))
544 found_interface = true;
546 c->last_iteration = bus->iteration_counter;
548 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
552 if (sd_bus_error_is_set(&error)) {
553 r = sd_bus_reply_method_error(bus, m, &error);
561 if (!found_interface) {
562 r = sd_bus_reply_method_errorf(
564 "org.freedesktop.DBus.Error.UnknownInterface",
565 "Unknown interface '%s'.", iface);
572 r = sd_bus_message_close_container(reply);
576 r = sd_bus_send(bus, reply, NULL);
583 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
586 if (n->object_manager)
590 return bus_node_with_object_manager(bus, n->parent);
595 static bool bus_node_exists(sd_bus *bus, struct node *n, const char *path, bool require_fallback) {
596 struct node_vtable *c;
597 struct node_callback *k;
602 /* Tests if there's anything attached directly to this node
603 * for the specified path */
605 LIST_FOREACH(callbacks, k, n->callbacks) {
606 if (require_fallback && !k->is_fallback)
612 LIST_FOREACH(vtables, c, n->vtables) {
614 if (require_fallback && !c->is_fallback)
617 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
621 return !require_fallback && (n->enumerators || n->object_manager);
624 static int process_introspect(
628 bool require_fallback,
629 bool *found_object) {
631 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
632 _cleanup_set_free_free_ Set *s = NULL;
633 struct introspect intro;
634 struct node_vtable *c;
641 assert(found_object);
643 r = get_child_nodes(bus, m->path, n, &s);
647 r = introspect_begin(&intro);
651 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
655 empty = set_isempty(s);
657 LIST_FOREACH(vtables, c, n->vtables) {
658 if (require_fallback && !c->is_fallback)
661 r = node_vtable_get_userdata(bus, m->path, c, NULL);
669 r = introspect_write_interface(&intro, c->interface, c->vtable);
675 /* Nothing?, let's see if we exist at all, and if not
676 * refuse to do anything */
677 r = bus_node_exists(bus, n, m->path, require_fallback);
685 *found_object = true;
687 r = introspect_write_child_nodes(&intro, s, m->path);
691 r = introspect_finish(&intro, bus, m, &reply);
695 r = sd_bus_send(bus, reply, NULL);
702 introspect_free(&intro);
706 static int object_manager_serialize_vtable(
708 sd_bus_message *reply,
710 struct node_vtable *c,
711 sd_bus_error *error) {
722 r = node_vtable_get_userdata(bus, path, c, &u);
726 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
730 r = sd_bus_message_append(reply, "s", c->interface);
734 r = sd_bus_message_open_container(reply, 'a', "{sv}");
738 r = vtable_append_all_properties(bus, reply, path, c, u, error);
742 r = sd_bus_message_close_container(reply);
746 r = sd_bus_message_close_container(reply);
753 static int object_manager_serialize_path(
755 sd_bus_message *reply,
758 bool require_fallback,
759 sd_bus_error *error) {
761 struct node_vtable *i;
771 n = hashmap_get(bus->nodes, prefix);
775 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
779 r = sd_bus_message_append(reply, "o", path);
783 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
787 LIST_FOREACH(vtables, i, n->vtables) {
789 if (require_fallback && !i->is_fallback)
792 r = object_manager_serialize_vtable(bus, reply, path, i, error);
795 if (sd_bus_error_is_set(error))
799 r = sd_bus_message_close_container(reply);
803 r = sd_bus_message_close_container(reply);
810 static int object_manager_serialize_path_and_fallbacks(
812 sd_bus_message *reply,
814 sd_bus_error *error) {
824 /* First, add all vtables registered for this path */
825 r = object_manager_serialize_path(bus, reply, path, path, false, error);
828 if (sd_bus_error_is_set(error))
831 /* Second, add fallback vtables registered for any of the prefixes */
846 r = object_manager_serialize_path(bus, reply, p, path, true, error);
850 if (sd_bus_error_is_set(error))
858 static int process_get_managed_objects(
862 bool require_fallback,
863 bool *found_object) {
865 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
866 _cleanup_set_free_free_ Set *s = NULL;
873 assert(found_object);
875 if (!bus_node_with_object_manager(bus, n))
878 r = get_child_nodes(bus, m->path, n, &s);
882 r = sd_bus_message_new_method_return(bus, m, &reply);
886 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
890 empty = set_isempty(s);
892 struct node_vtable *c;
894 /* Hmm, so we have no children? Then let's check
895 * whether we exist at all, i.e. whether at least one
898 LIST_FOREACH(vtables, c, n->vtables) {
900 if (require_fallback && !c->is_fallback)
918 SET_FOREACH(path, s, i) {
919 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
921 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
925 if (sd_bus_error_is_set(&error)) {
926 r = sd_bus_reply_method_error(bus, m, &error);
935 r = sd_bus_message_close_container(reply);
939 r = sd_bus_send(bus, reply, NULL);
946 static int object_find_and_run(
950 bool require_fallback,
951 bool *found_object) {
954 struct vtable_member vtable_key, *v;
960 assert(found_object);
962 n = hashmap_get(bus->nodes, p);
966 /* First, try object callbacks */
967 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
971 if (!m->interface || !m->member)
974 /* Then, look for a known method */
975 vtable_key.path = (char*) p;
976 vtable_key.interface = m->interface;
977 vtable_key.member = m->member;
979 v = hashmap_get(bus->vtable_methods, &vtable_key);
981 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
986 /* Then, look for a known property */
987 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
990 get = streq(m->member, "Get");
992 if (get || streq(m->member, "Set")) {
994 r = sd_bus_message_rewind(m, true);
998 vtable_key.path = (char*) p;
1000 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1004 v = hashmap_get(bus->vtable_properties, &vtable_key);
1006 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1011 } else if (streq(m->member, "GetAll")) {
1014 r = sd_bus_message_rewind(m, true);
1018 r = sd_bus_message_read(m, "s", &iface);
1025 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1030 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1032 r = process_introspect(bus, m, n, require_fallback, found_object);
1036 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1038 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1043 if (!*found_object) {
1044 r = bus_node_exists(bus, n, m->path, require_fallback);
1049 *found_object = true;
1055 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1058 bool found_object = false;
1063 if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
1069 if (hashmap_isempty(bus->nodes))
1072 pl = strlen(m->path);
1076 bus->nodes_modified = false;
1078 r = object_find_and_run(bus, m, m->path, false, &found_object);
1082 /* Look for fallback prefixes */
1090 if (bus->nodes_modified)
1093 e = strrchr(p, '/');
1100 r = object_find_and_run(bus, m, p, true, &found_object);
1105 } while (bus->nodes_modified);
1110 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1111 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1112 r = sd_bus_reply_method_errorf(
1114 "org.freedesktop.DBus.Error.UnknownProperty",
1115 "Unknown property or interface.");
1117 r = sd_bus_reply_method_errorf(
1119 "org.freedesktop.DBus.Error.UnknownMethod",
1120 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1128 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1129 struct node *n, *parent;
1136 assert(path[0] == '/');
1138 n = hashmap_get(bus->nodes, path);
1142 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1150 if (streq(path, "/"))
1153 e = strrchr(path, '/');
1156 p = strndupa(path, MAX(1, path - e));
1158 parent = bus_node_allocate(bus, p);
1165 n = new0(struct node, 1);
1172 r = hashmap_put(bus->nodes, s, n);
1180 LIST_PREPEND(struct node, siblings, parent->child, n);
1185 static void bus_node_gc(sd_bus *b, struct node *n) {
1198 assert(hashmap_remove(b->nodes, n->path) == n);
1201 LIST_REMOVE(struct node, siblings, n->parent->child, n);
1204 bus_node_gc(b, n->parent);
1208 static int bus_add_object(
1212 sd_bus_message_handler_t callback,
1215 struct node_callback *c;
1221 if (!object_path_is_valid(path))
1225 if (bus_pid_changed(b))
1228 n = bus_node_allocate(b, path);
1232 c = new0(struct node_callback, 1);
1239 c->callback = callback;
1240 c->userdata = userdata;
1241 c->is_fallback = fallback;
1243 LIST_PREPEND(struct node_callback, callbacks, n->callbacks, c);
1252 static int bus_remove_object(
1256 sd_bus_message_handler_t callback,
1259 struct node_callback *c;
1264 if (!object_path_is_valid(path))
1268 if (bus_pid_changed(bus))
1271 n = hashmap_get(bus->nodes, path);
1275 LIST_FOREACH(callbacks, c, n->callbacks)
1276 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1281 LIST_REMOVE(struct node_callback, callbacks, n->callbacks, c);
1284 bus_node_gc(bus, n);
1289 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1290 return bus_add_object(bus, false, path, callback, userdata);
1293 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1294 return bus_remove_object(bus, false, path, callback, userdata);
1297 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1298 return bus_add_object(bus, true, prefix, callback, userdata);
1301 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1302 return bus_remove_object(bus, true, prefix, callback, userdata);
1305 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1311 if (w->interface && w->node && w->vtable) {
1312 const sd_bus_vtable *v;
1314 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1315 struct vtable_member *x = NULL;
1319 case _SD_BUS_VTABLE_METHOD: {
1320 struct vtable_member key;
1322 key.path = w->node->path;
1323 key.interface = w->interface;
1324 key.member = v->method.member;
1326 x = hashmap_remove(bus->vtable_methods, &key);
1330 case _SD_BUS_VTABLE_PROPERTY:
1331 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1332 struct vtable_member key;
1334 key.path = w->node->path;
1335 key.interface = w->interface;
1336 key.member = v->property.member;
1337 x = hashmap_remove(bus->vtable_properties, &key);
1349 static unsigned vtable_member_hash_func(const void *a) {
1350 const struct vtable_member *m = a;
1353 string_hash_func(m->path) ^
1354 string_hash_func(m->interface) ^
1355 string_hash_func(m->member);
1358 static int vtable_member_compare_func(const void *a, const void *b) {
1359 const struct vtable_member *x = a, *y = b;
1362 r = strcmp(x->path, y->path);
1366 r = strcmp(x->interface, y->interface);
1370 return strcmp(x->member, y->member);
1373 static int add_object_vtable_internal(
1376 const char *interface,
1377 const sd_bus_vtable *vtable,
1379 sd_bus_object_find_t find,
1382 struct node_vtable *c = NULL, *i;
1383 const sd_bus_vtable *v;
1389 if (!object_path_is_valid(path))
1391 if (!interface_name_is_valid(interface))
1393 if (!vtable || vtable[0].type != _SD_BUS_VTABLE_START || vtable[0].start.element_size != sizeof(struct sd_bus_vtable))
1395 if (bus_pid_changed(bus))
1398 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1402 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1406 n = bus_node_allocate(bus, path);
1410 LIST_FOREACH(vtables, i, n->vtables) {
1411 if (streq(i->interface, interface)) {
1416 if (i->is_fallback != fallback) {
1422 c = new0(struct node_vtable, 1);
1429 c->is_fallback = fallback;
1431 c->userdata = userdata;
1434 c->interface = strdup(interface);
1435 if (!c->interface) {
1440 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1444 case _SD_BUS_VTABLE_METHOD: {
1445 struct vtable_member *m;
1447 if (!member_name_is_valid(v->method.member) ||
1448 !signature_is_valid(strempty(v->method.signature), false) ||
1449 !signature_is_valid(strempty(v->method.result), false) ||
1450 !(v->method.handler || (isempty(v->method.signature) && isempty(v->method.result))) ||
1451 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1456 m = new0(struct vtable_member, 1);
1464 m->interface = c->interface;
1465 m->member = v->method.member;
1468 r = hashmap_put(bus->vtable_methods, m, m);
1477 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1479 if (!(v->property.set || bus_type_is_basic(v->property.signature[0]))) {
1486 case _SD_BUS_VTABLE_PROPERTY: {
1487 struct vtable_member *m;
1489 if (!member_name_is_valid(v->property.member) ||
1490 !signature_is_single(v->property.signature, false) ||
1491 !(v->property.get || bus_type_is_basic(v->property.signature[0])) ||
1492 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1493 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1499 m = new0(struct vtable_member, 1);
1507 m->interface = c->interface;
1508 m->member = v->property.member;
1511 r = hashmap_put(bus->vtable_properties, m, m);
1520 case _SD_BUS_VTABLE_SIGNAL:
1522 if (!member_name_is_valid(v->signal.member) ||
1523 !signature_is_single(strempty(v->signal.signature), false)) {
1536 LIST_PREPEND(struct node_vtable, vtables, n->vtables, c);
1541 free_node_vtable(bus, c);
1543 bus_node_gc(bus, n);
1547 static int remove_object_vtable_internal(
1550 const char *interface,
1553 struct node_vtable *c;
1558 if (!object_path_is_valid(path))
1560 if (!interface_name_is_valid(interface))
1562 if (bus_pid_changed(bus))
1565 n = hashmap_get(bus->nodes, path);
1569 LIST_FOREACH(vtables, c, n->vtables)
1570 if (streq(c->interface, interface) && c->is_fallback == fallback)
1576 LIST_REMOVE(struct node_vtable, vtables, n->vtables, c);
1578 free_node_vtable(bus, c);
1582 int sd_bus_add_object_vtable(
1585 const char *interface,
1586 const sd_bus_vtable *vtable,
1589 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1592 int sd_bus_remove_object_vtable(
1595 const char *interface) {
1597 return remove_object_vtable_internal(bus, path, interface, false);
1600 int sd_bus_add_fallback_vtable(
1603 const char *interface,
1604 const sd_bus_vtable *vtable,
1605 sd_bus_object_find_t find,
1608 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1611 int sd_bus_remove_fallback_vtable(
1614 const char *interface) {
1616 return remove_object_vtable_internal(bus, path, interface, true);
1619 int sd_bus_add_node_enumerator(
1622 sd_bus_node_enumerator_t callback,
1625 struct node_enumerator *c;
1631 if (!object_path_is_valid(path))
1635 if (bus_pid_changed(bus))
1638 n = bus_node_allocate(bus, path);
1642 c = new0(struct node_enumerator, 1);
1649 c->callback = callback;
1650 c->userdata = userdata;
1652 LIST_PREPEND(struct node_enumerator, enumerators, n->enumerators, c);
1657 bus_node_gc(bus, n);
1661 int sd_bus_remove_node_enumerator(
1664 sd_bus_node_enumerator_t callback,
1667 struct node_enumerator *c;
1672 if (!object_path_is_valid(path))
1676 if (bus_pid_changed(bus))
1679 n = hashmap_get(bus->nodes, path);
1683 LIST_FOREACH(enumerators, c, n->enumerators)
1684 if (c->callback == callback && c->userdata == userdata)
1690 LIST_REMOVE(struct node_enumerator, enumerators, n->enumerators, c);
1693 bus_node_gc(bus, n);
1698 static int emit_properties_changed_on_interface(
1702 const char *interface,
1703 bool require_fallback,
1706 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1707 bool has_invalidating = false;
1708 struct vtable_member key;
1709 struct node_vtable *c;
1719 n = hashmap_get(bus->nodes, prefix);
1723 LIST_FOREACH(vtables, c, n->vtables) {
1724 if (require_fallback && !c->is_fallback)
1727 if (streq(c->interface, interface))
1734 r = node_vtable_get_userdata(bus, path, c, &u);
1738 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1742 r = sd_bus_message_append(m, "s", interface);
1746 r = sd_bus_message_open_container(m, 'a', "{sv}");
1751 key.interface = interface;
1753 STRV_FOREACH(property, names) {
1754 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1755 struct vtable_member *v;
1757 key.member = *property;
1758 v = hashmap_get(bus->vtable_properties, &key);
1762 assert(c == v->parent);
1764 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1766 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1767 has_invalidating = true;
1771 r = sd_bus_message_open_container(m, 'e', "sv");
1775 r = sd_bus_message_append(m, "s", *property);
1779 r = sd_bus_message_open_container(m, 'v', v->vtable->property.signature);
1783 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1787 if (sd_bus_error_is_set(&error))
1788 return bus_error_to_errno(&error);
1790 r = sd_bus_message_close_container(m);
1794 r = sd_bus_message_close_container(m);
1799 r = sd_bus_message_close_container(m);
1803 r = sd_bus_message_open_container(m, 'a', "s");
1807 if (has_invalidating) {
1808 STRV_FOREACH(property, names) {
1809 struct vtable_member *v;
1811 key.member = *property;
1812 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1813 assert(c == v->parent);
1815 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1818 r = sd_bus_message_append(m, "s", *property);
1824 r = sd_bus_message_close_container(m);
1828 r = sd_bus_send(bus, m, NULL);
1835 int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names) {
1841 if (!object_path_is_valid(path))
1843 if (!interface_name_is_valid(interface))
1846 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1861 e = strrchr(p, '/');
1868 r = emit_properties_changed_on_interface(bus, p, path, interface, true, names);
1877 int sd_bus_emit_properties_changed(sd_bus *bus, const char *path, const char *interface, const char *name, ...) {
1878 _cleanup_strv_free_ char **names = NULL;
1882 names = strv_new_ap(name, ap);
1888 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
1891 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interfaces, ...) {
1895 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interfaces, ...) {
1899 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
1904 if (!object_path_is_valid(path))
1906 if (bus_pid_changed(bus))
1909 n = bus_node_allocate(bus, path);
1913 n->object_manager = true;
1917 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
1922 if (!object_path_is_valid(path))
1924 if (bus_pid_changed(bus))
1927 n = hashmap_get(bus->nodes, path);
1931 if (!n->object_manager)
1934 n->object_manager = false;
1935 bus_node_gc(bus, n);