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);
80 if (bus->nodes_modified)
83 *userdata = vtable_property_convert_userdata(p->vtable, u);
87 static int add_enumerated_to_set(
90 struct node_enumerator *first,
93 struct node_enumerator *c;
100 LIST_FOREACH(enumerators, c, first) {
101 char **children = NULL, **k;
103 if (bus->nodes_modified)
106 r = c->callback(bus, prefix, &children, c->userdata);
110 STRV_FOREACH(k, children) {
116 if (!object_path_is_valid(*k) && object_path_startswith(*k, prefix)) {
122 r = set_consume(s, *k);
133 static int add_subtree_to_set(
147 r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
150 if (bus->nodes_modified)
153 LIST_FOREACH(siblings, i, n->child) {
160 r = set_consume(s, t);
161 if (r < 0 && r != -EEXIST)
164 r = add_subtree_to_set(bus, prefix, i, s);
167 if (bus->nodes_modified)
174 static int get_child_nodes(
188 s = set_new(string_hash_func, string_compare_func);
192 r = add_subtree_to_set(bus, prefix, n, s);
202 static int node_callbacks_run(
205 struct node_callback *first,
206 bool require_fallback,
207 bool *found_object) {
209 struct node_callback *c;
214 assert(found_object);
216 LIST_FOREACH(callbacks, c, first) {
217 if (bus->nodes_modified)
220 if (require_fallback && !c->is_fallback)
223 *found_object = true;
225 if (c->last_iteration == bus->iteration_counter)
228 c->last_iteration = bus->iteration_counter;
230 r = sd_bus_message_rewind(m, true);
234 r = c->callback(bus, m, c->userdata);
242 static int method_callbacks_run(
245 struct vtable_member *c,
246 bool require_fallback,
247 bool *found_object) {
249 const char *signature;
256 assert(found_object);
258 if (require_fallback && !c->parent->is_fallback)
261 r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
264 if (bus->nodes_modified)
267 *found_object = true;
269 if (c->last_iteration == bus->iteration_counter)
272 c->last_iteration = bus->iteration_counter;
274 r = sd_bus_message_rewind(m, true);
278 r = sd_bus_message_get_signature(m, true, &signature);
282 if (!streq(strempty(c->vtable->x.method.signature), signature)) {
283 r = sd_bus_reply_method_errorf(bus, m,
284 "org.freedesktop.DBus.Error.InvalidArgs",
285 "Invalid arguments '%s' to call %s:%s, expecting '%s'.",
286 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
293 if (c->vtable->x.method.handler)
294 return c->vtable->x.method.handler(bus, m, u);
296 /* If the method callback is NULL, make this a successful NOP */
297 r = sd_bus_reply_method_return(bus, m, NULL);
304 static int invoke_property_get(
306 const sd_bus_vtable *v,
308 const char *interface,
309 const char *property,
324 if (v->x.property.get)
325 return v->x.property.get(bus, path, interface, property, m, error, userdata);
327 /* Automatic handling if no callback is defined. */
329 assert(signature_is_single(v->x.property.signature, false));
330 assert(bus_type_is_basic(v->x.property.signature[0]));
332 switch (v->x.property.signature[0]) {
334 case SD_BUS_TYPE_STRING:
335 case SD_BUS_TYPE_OBJECT_PATH:
336 case SD_BUS_TYPE_SIGNATURE:
337 p = *(char**) userdata;
345 r = sd_bus_message_append_basic(m, v->x.property.signature[0], p);
352 static int invoke_property_set(
354 const sd_bus_vtable *v,
356 const char *interface,
357 const char *property,
358 sd_bus_message *value,
371 if (v->x.property.set)
372 return v->x.property.set(bus, path, interface, property, value, error, userdata);
374 /* Automatic handling if no callback is defined. */
376 assert(signature_is_single(v->x.property.signature, false));
377 assert(bus_type_is_basic(v->x.property.signature[0]));
379 switch (v->x.property.signature[0]) {
381 case SD_BUS_TYPE_STRING:
382 case SD_BUS_TYPE_OBJECT_PATH:
383 case SD_BUS_TYPE_SIGNATURE: {
387 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
395 free(*(char**) userdata);
396 *(char**) userdata = n;
402 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
412 static int property_get_set_callbacks_run(
415 struct vtable_member *c,
416 bool require_fallback,
418 bool *found_object) {
420 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
421 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
428 assert(found_object);
430 if (require_fallback && !c->parent->is_fallback)
433 r = vtable_property_get_userdata(bus, m->path, c, &u);
436 if (bus->nodes_modified)
439 *found_object = true;
441 r = sd_bus_message_new_method_return(bus, m, &reply);
446 /* Note that we do not protect against reexecution
447 * here (using the last_iteration check, see below),
448 * should the node tree have changed and we got called
449 * again. We assume that property Get() calls are
450 * ultimately without side-effects or if they aren't
451 * then at least idempotent. */
453 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
457 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, &error, u);
461 if (sd_bus_error_is_set(&error)) {
462 r = sd_bus_reply_method_error(bus, m, &error);
469 if (bus->nodes_modified)
472 r = sd_bus_message_close_container(reply);
477 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
478 sd_bus_error_setf(&error, "org.freedesktop.DBus.Error.PropertyReadOnly", "Property '%s' is not writable.", c->member);
480 /* Avoid that we call the set routine more
481 * than once if the processing of this message
482 * got restarted because the node tree
484 if (c->last_iteration == bus->iteration_counter)
487 c->last_iteration = bus->iteration_counter;
489 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
493 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, &error, u);
498 if (sd_bus_error_is_set(&error)) {
499 r = sd_bus_reply_method_error(bus, m, &error);
506 if (bus->nodes_modified)
509 r = sd_bus_message_exit_container(m);
514 r = sd_bus_send(bus, reply, NULL);
521 static int vtable_append_all_properties(
523 sd_bus_message *reply,
525 struct node_vtable *c,
527 sd_bus_error *error) {
529 const sd_bus_vtable *v;
537 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
538 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
541 r = sd_bus_message_open_container(reply, 'e', "sv");
545 r = sd_bus_message_append(reply, "s", v->x.property.member);
549 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
553 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, error, vtable_property_convert_userdata(v, userdata));
556 if (sd_bus_error_is_set(error))
558 if (bus->nodes_modified)
561 r = sd_bus_message_close_container(reply);
565 r = sd_bus_message_close_container(reply);
573 static int property_get_all_callbacks_run(
576 struct node_vtable *first,
577 bool require_fallback,
579 bool *found_object) {
581 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
582 struct node_vtable *c;
583 bool found_interface = false;
588 assert(found_object);
590 r = sd_bus_message_new_method_return(bus, m, &reply);
594 r = sd_bus_message_open_container(reply, 'a', "{sv}");
598 LIST_FOREACH(vtables, c, first) {
599 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
602 if (require_fallback && !c->is_fallback)
605 r = node_vtable_get_userdata(bus, m->path, c, &u);
608 if (bus->nodes_modified)
613 *found_object = true;
615 if (iface && !streq(c->interface, iface))
617 found_interface = true;
619 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
623 if (sd_bus_error_is_set(&error)) {
624 r = sd_bus_reply_method_error(bus, m, &error);
630 if (bus->nodes_modified)
634 if (!found_interface) {
635 r = sd_bus_reply_method_errorf(
637 "org.freedesktop.DBus.Error.UnknownInterface",
638 "Unknown interface '%s'.", iface);
645 r = sd_bus_message_close_container(reply);
649 r = sd_bus_send(bus, reply, NULL);
656 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
660 if (n->object_manager)
664 return bus_node_with_object_manager(bus, n->parent);
669 static bool bus_node_exists(
673 bool require_fallback) {
675 struct node_vtable *c;
676 struct node_callback *k;
682 /* Tests if there's anything attached directly to this node
683 * for the specified path */
685 LIST_FOREACH(callbacks, k, n->callbacks) {
686 if (require_fallback && !k->is_fallback)
692 LIST_FOREACH(vtables, c, n->vtables) {
694 if (require_fallback && !c->is_fallback)
697 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
699 if (bus->nodes_modified)
703 return !require_fallback && (n->enumerators || n->object_manager);
706 static int process_introspect(
710 bool require_fallback,
711 bool *found_object) {
713 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
714 _cleanup_set_free_free_ Set *s = NULL;
715 struct introspect intro;
716 struct node_vtable *c;
723 assert(found_object);
725 r = get_child_nodes(bus, m->path, n, &s);
728 if (bus->nodes_modified)
731 r = introspect_begin(&intro);
735 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
739 empty = set_isempty(s);
741 LIST_FOREACH(vtables, c, n->vtables) {
742 if (require_fallback && !c->is_fallback)
745 r = node_vtable_get_userdata(bus, m->path, c, NULL);
748 if (bus->nodes_modified)
755 r = introspect_write_interface(&intro, c->interface, c->vtable);
761 /* Nothing?, let's see if we exist at all, and if not
762 * refuse to do anything */
763 r = bus_node_exists(bus, n, m->path, require_fallback);
766 if (bus->nodes_modified)
772 *found_object = true;
774 r = introspect_write_child_nodes(&intro, s, m->path);
778 r = introspect_finish(&intro, bus, m, &reply);
782 r = sd_bus_send(bus, reply, NULL);
789 introspect_free(&intro);
793 static int object_manager_serialize_vtable(
795 sd_bus_message *reply,
797 struct node_vtable *c,
809 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
813 r = sd_bus_message_append(reply, "s", c->interface);
817 r = sd_bus_message_open_container(reply, 'a', "{sv}");
821 r = vtable_append_all_properties(bus, reply, path, c, userdata, error);
824 if (bus->nodes_modified)
827 r = sd_bus_message_close_container(reply);
831 r = sd_bus_message_close_container(reply);
838 static int object_manager_serialize_path(
840 sd_bus_message *reply,
843 bool require_fallback,
844 sd_bus_error *error) {
846 struct node_vtable *i;
848 bool found_something = false;
857 n = hashmap_get(bus->nodes, prefix);
861 LIST_FOREACH(vtables, i, n->vtables) {
864 if (require_fallback && !i->is_fallback)
867 r = node_vtable_get_userdata(bus, path, i, &u);
870 if (bus->nodes_modified)
875 if (!found_something) {
876 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
880 r = sd_bus_message_append(reply, "o", path);
884 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
888 found_something = true;
891 r = object_manager_serialize_vtable(bus, reply, path, i, error, u);
894 if (sd_bus_error_is_set(error))
896 if (bus->nodes_modified)
900 if (found_something) {
901 r = sd_bus_message_close_container(reply);
905 r = sd_bus_message_close_container(reply);
913 static int object_manager_serialize_path_and_fallbacks(
915 sd_bus_message *reply,
917 sd_bus_error *error) {
927 /* First, add all vtables registered for this path */
928 r = object_manager_serialize_path(bus, reply, path, path, false, error);
931 if (sd_bus_error_is_set(error))
933 if (bus->nodes_modified)
936 /* Second, add fallback vtables registered for any of the prefixes */
937 prefix = alloca(strlen(path) + 1);
938 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
939 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
942 if (sd_bus_error_is_set(error))
944 if (bus->nodes_modified)
951 static int process_get_managed_objects(
955 bool require_fallback,
956 bool *found_object) {
958 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
959 _cleanup_set_free_free_ Set *s = NULL;
966 assert(found_object);
968 if (!bus_node_with_object_manager(bus, n))
971 r = get_child_nodes(bus, m->path, n, &s);
974 if (bus->nodes_modified)
977 r = sd_bus_message_new_method_return(bus, m, &reply);
981 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
985 empty = set_isempty(s);
987 struct node_vtable *c;
989 /* Hmm, so we have no children? Then let's check
990 * whether we exist at all, i.e. whether at least one
993 LIST_FOREACH(vtables, c, n->vtables) {
995 if (require_fallback && !c->is_fallback)
1013 SET_FOREACH(path, s, i) {
1014 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1016 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1020 if (sd_bus_error_is_set(&error)) {
1021 r = sd_bus_reply_method_error(bus, m, &error);
1028 if (bus->nodes_modified)
1033 r = sd_bus_message_close_container(reply);
1037 r = sd_bus_send(bus, reply, NULL);
1044 static int object_find_and_run(
1048 bool require_fallback,
1049 bool *found_object) {
1052 struct vtable_member vtable_key, *v;
1058 assert(found_object);
1060 n = hashmap_get(bus->nodes, p);
1064 /* First, try object callbacks */
1065 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1068 if (bus->nodes_modified)
1071 if (!m->interface || !m->member)
1074 /* Then, look for a known method */
1075 vtable_key.path = (char*) p;
1076 vtable_key.interface = m->interface;
1077 vtable_key.member = m->member;
1079 v = hashmap_get(bus->vtable_methods, &vtable_key);
1081 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1084 if (bus->nodes_modified)
1088 /* Then, look for a known property */
1089 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1092 get = streq(m->member, "Get");
1094 if (get || streq(m->member, "Set")) {
1096 r = sd_bus_message_rewind(m, true);
1100 vtable_key.path = (char*) p;
1102 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1106 v = hashmap_get(bus->vtable_properties, &vtable_key);
1108 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1113 } else if (streq(m->member, "GetAll")) {
1116 r = sd_bus_message_rewind(m, true);
1120 r = sd_bus_message_read(m, "s", &iface);
1127 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1132 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1134 r = process_introspect(bus, m, n, require_fallback, found_object);
1138 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1140 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1145 if (bus->nodes_modified)
1148 if (!*found_object) {
1149 r = bus_node_exists(bus, n, m->path, require_fallback);
1153 *found_object = true;
1159 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1162 bool found_object = false;
1167 if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL)
1173 if (hashmap_isempty(bus->nodes))
1176 pl = strlen(m->path);
1180 bus->nodes_modified = false;
1182 r = object_find_and_run(bus, m, m->path, false, &found_object);
1186 /* Look for fallback prefixes */
1187 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1189 if (bus->nodes_modified)
1192 r = object_find_and_run(bus, m, prefix, true, &found_object);
1197 } while (bus->nodes_modified);
1202 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1203 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1204 r = sd_bus_reply_method_errorf(
1206 "org.freedesktop.DBus.Error.UnknownProperty",
1207 "Unknown property or interface.");
1209 r = sd_bus_reply_method_errorf(
1211 "org.freedesktop.DBus.Error.UnknownMethod",
1212 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1220 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1221 struct node *n, *parent;
1228 assert(path[0] == '/');
1230 n = hashmap_get(bus->nodes, path);
1234 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1242 if (streq(path, "/"))
1245 e = strrchr(path, '/');
1248 p = strndupa(path, MAX(1, path - e));
1250 parent = bus_node_allocate(bus, p);
1257 n = new0(struct node, 1);
1264 r = hashmap_put(bus->nodes, s, n);
1272 LIST_PREPEND(siblings, parent->child, n);
1277 static void bus_node_gc(sd_bus *b, struct node *n) {
1290 assert(hashmap_remove(b->nodes, n->path) == n);
1293 LIST_REMOVE(siblings, n->parent->child, n);
1296 bus_node_gc(b, n->parent);
1300 static int bus_add_object(
1304 sd_bus_message_handler_t callback,
1307 struct node_callback *c;
1311 assert_return(bus, -EINVAL);
1312 assert_return(object_path_is_valid(path), -EINVAL);
1313 assert_return(callback, -EINVAL);
1314 assert_return(!bus_pid_changed(bus), -ECHILD);
1316 n = bus_node_allocate(bus, path);
1320 c = new0(struct node_callback, 1);
1327 c->callback = callback;
1328 c->userdata = userdata;
1329 c->is_fallback = fallback;
1331 LIST_PREPEND(callbacks, n->callbacks, c);
1332 bus->nodes_modified = true;
1338 bus_node_gc(bus, n);
1342 static int bus_remove_object(
1346 sd_bus_message_handler_t callback,
1349 struct node_callback *c;
1352 assert_return(bus, -EINVAL);
1353 assert_return(object_path_is_valid(path), -EINVAL);
1354 assert_return(callback, -EINVAL);
1355 assert_return(!bus_pid_changed(bus), -ECHILD);
1357 n = hashmap_get(bus->nodes, path);
1361 LIST_FOREACH(callbacks, c, n->callbacks)
1362 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1367 LIST_REMOVE(callbacks, n->callbacks, c);
1370 bus_node_gc(bus, n);
1371 bus->nodes_modified = true;
1376 int sd_bus_add_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1377 return bus_add_object(bus, false, path, callback, userdata);
1380 int sd_bus_remove_object(sd_bus *bus, const char *path, sd_bus_message_handler_t callback, void *userdata) {
1381 return bus_remove_object(bus, false, path, callback, userdata);
1384 int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1385 return bus_add_object(bus, true, prefix, callback, userdata);
1388 int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_bus_message_handler_t callback, void *userdata) {
1389 return bus_remove_object(bus, true, prefix, callback, userdata);
1392 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1398 if (w->interface && w->node && w->vtable) {
1399 const sd_bus_vtable *v;
1401 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1402 struct vtable_member *x = NULL;
1406 case _SD_BUS_VTABLE_METHOD: {
1407 struct vtable_member key;
1409 key.path = w->node->path;
1410 key.interface = w->interface;
1411 key.member = v->x.method.member;
1413 x = hashmap_remove(bus->vtable_methods, &key);
1417 case _SD_BUS_VTABLE_PROPERTY:
1418 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1419 struct vtable_member key;
1421 key.path = w->node->path;
1422 key.interface = w->interface;
1423 key.member = v->x.property.member;
1424 x = hashmap_remove(bus->vtable_properties, &key);
1436 static unsigned vtable_member_hash_func(const void *a) {
1437 const struct vtable_member *m = a;
1442 string_hash_func(m->path) ^
1443 string_hash_func(m->interface) ^
1444 string_hash_func(m->member);
1447 static int vtable_member_compare_func(const void *a, const void *b) {
1448 const struct vtable_member *x = a, *y = b;
1454 r = strcmp(x->path, y->path);
1458 r = strcmp(x->interface, y->interface);
1462 return strcmp(x->member, y->member);
1465 static int add_object_vtable_internal(
1468 const char *interface,
1469 const sd_bus_vtable *vtable,
1471 sd_bus_object_find_t find,
1474 struct node_vtable *c = NULL, *i;
1475 const sd_bus_vtable *v;
1479 assert_return(bus, -EINVAL);
1480 assert_return(object_path_is_valid(path), -EINVAL);
1481 assert_return(interface_name_is_valid(interface), -EINVAL);
1482 assert_return(vtable, -EINVAL);
1483 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1484 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1485 assert_return(!bus_pid_changed(bus), -ECHILD);
1487 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1491 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1495 n = bus_node_allocate(bus, path);
1499 LIST_FOREACH(vtables, i, n->vtables) {
1500 if (streq(i->interface, interface)) {
1505 if (i->is_fallback != fallback) {
1511 c = new0(struct node_vtable, 1);
1518 c->is_fallback = fallback;
1520 c->userdata = userdata;
1523 c->interface = strdup(interface);
1524 if (!c->interface) {
1529 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1533 case _SD_BUS_VTABLE_METHOD: {
1534 struct vtable_member *m;
1536 if (!member_name_is_valid(v->x.method.member) ||
1537 !signature_is_valid(strempty(v->x.method.signature), false) ||
1538 !signature_is_valid(strempty(v->x.method.result), false) ||
1539 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1540 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1545 m = new0(struct vtable_member, 1);
1553 m->interface = c->interface;
1554 m->member = v->x.method.member;
1557 r = hashmap_put(bus->vtable_methods, m, m);
1566 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1568 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1575 case _SD_BUS_VTABLE_PROPERTY: {
1576 struct vtable_member *m;
1578 if (!member_name_is_valid(v->x.property.member) ||
1579 !signature_is_single(v->x.property.signature, false) ||
1580 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0])) ||
1581 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1582 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1588 m = new0(struct vtable_member, 1);
1596 m->interface = c->interface;
1597 m->member = v->x.property.member;
1600 r = hashmap_put(bus->vtable_properties, m, m);
1609 case _SD_BUS_VTABLE_SIGNAL:
1611 if (!member_name_is_valid(v->x.signal.member) ||
1612 !signature_is_single(strempty(v->x.signal.signature), false)) {
1625 LIST_PREPEND(vtables, n->vtables, c);
1626 bus->nodes_modified = true;
1632 free_node_vtable(bus, c);
1634 bus_node_gc(bus, n);
1638 static int remove_object_vtable_internal(
1641 const char *interface,
1644 struct node_vtable *c;
1647 assert_return(bus, -EINVAL);
1648 assert_return(object_path_is_valid(path), -EINVAL);
1649 assert_return(interface_name_is_valid(interface), -EINVAL);
1650 assert_return(!bus_pid_changed(bus), -ECHILD);
1652 n = hashmap_get(bus->nodes, path);
1656 LIST_FOREACH(vtables, c, n->vtables)
1657 if (streq(c->interface, interface) && c->is_fallback == fallback)
1663 LIST_REMOVE(vtables, n->vtables, c);
1665 free_node_vtable(bus, c);
1666 bus_node_gc(bus, n);
1668 bus->nodes_modified = true;
1673 int sd_bus_add_object_vtable(
1676 const char *interface,
1677 const sd_bus_vtable *vtable,
1680 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1683 int sd_bus_remove_object_vtable(
1686 const char *interface) {
1688 return remove_object_vtable_internal(bus, path, interface, false);
1691 int sd_bus_add_fallback_vtable(
1694 const char *interface,
1695 const sd_bus_vtable *vtable,
1696 sd_bus_object_find_t find,
1699 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1702 int sd_bus_remove_fallback_vtable(
1705 const char *interface) {
1707 return remove_object_vtable_internal(bus, path, interface, true);
1710 int sd_bus_add_node_enumerator(
1713 sd_bus_node_enumerator_t callback,
1716 struct node_enumerator *c;
1720 assert_return(bus, -EINVAL);
1721 assert_return(object_path_is_valid(path), -EINVAL);
1722 assert_return(callback, -EINVAL);
1723 assert_return(!bus_pid_changed(bus), -ECHILD);
1725 n = bus_node_allocate(bus, path);
1729 c = new0(struct node_enumerator, 1);
1736 c->callback = callback;
1737 c->userdata = userdata;
1739 LIST_PREPEND(enumerators, n->enumerators, c);
1741 bus->nodes_modified = true;
1747 bus_node_gc(bus, n);
1751 int sd_bus_remove_node_enumerator(
1754 sd_bus_node_enumerator_t callback,
1757 struct node_enumerator *c;
1760 assert_return(bus, -EINVAL);
1761 assert_return(object_path_is_valid(path), -EINVAL);
1762 assert_return(callback, -EINVAL);
1763 assert_return(!bus_pid_changed(bus), -ECHILD);
1765 n = hashmap_get(bus->nodes, path);
1769 LIST_FOREACH(enumerators, c, n->enumerators)
1770 if (c->callback == callback && c->userdata == userdata)
1776 LIST_REMOVE(enumerators, n->enumerators, c);
1779 bus_node_gc(bus, n);
1781 bus->nodes_modified = true;
1786 static int emit_properties_changed_on_interface(
1790 const char *interface,
1791 bool require_fallback,
1794 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1795 bool has_invalidating = false;
1796 struct vtable_member key;
1797 struct node_vtable *c;
1808 n = hashmap_get(bus->nodes, prefix);
1812 LIST_FOREACH(vtables, c, n->vtables) {
1813 if (require_fallback && !c->is_fallback)
1816 if (streq(c->interface, interface))
1823 r = node_vtable_get_userdata(bus, path, c, &u);
1826 if (bus->nodes_modified)
1829 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1833 r = sd_bus_message_append(m, "s", interface);
1837 r = sd_bus_message_open_container(m, 'a', "{sv}");
1842 key.interface = interface;
1844 STRV_FOREACH(property, names) {
1845 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1846 struct vtable_member *v;
1848 assert_return(member_name_is_valid(*property), -EINVAL);
1850 key.member = *property;
1851 v = hashmap_get(bus->vtable_properties, &key);
1855 assert(c == v->parent);
1856 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1858 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1859 has_invalidating = true;
1863 r = sd_bus_message_open_container(m, 'e', "sv");
1867 r = sd_bus_message_append(m, "s", *property);
1871 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1875 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, &error, vtable_property_convert_userdata(v->vtable, u));
1878 if (sd_bus_error_is_set(&error))
1879 return bus_error_to_errno(&error);
1880 if (bus->nodes_modified)
1883 r = sd_bus_message_close_container(m);
1887 r = sd_bus_message_close_container(m);
1892 r = sd_bus_message_close_container(m);
1896 r = sd_bus_message_open_container(m, 'a', "s");
1900 if (has_invalidating) {
1901 STRV_FOREACH(property, names) {
1902 struct vtable_member *v;
1904 key.member = *property;
1905 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1906 assert(c == v->parent);
1908 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1911 r = sd_bus_message_append(m, "s", *property);
1917 r = sd_bus_message_close_container(m);
1921 r = sd_bus_send(bus, m, NULL);
1928 int sd_bus_emit_properties_changed_strv(
1931 const char *interface,
1934 BUS_DONT_DESTROY(bus);
1938 assert_return(bus, -EINVAL);
1939 assert_return(object_path_is_valid(path), -EINVAL);
1940 assert_return(interface_name_is_valid(interface), -EINVAL);
1941 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1942 assert_return(!bus_pid_changed(bus), -ECHILD);
1944 if (strv_isempty(names))
1948 bus->nodes_modified = false;
1950 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
1953 if (bus->nodes_modified)
1956 prefix = alloca(strlen(path) + 1);
1957 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1958 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
1961 if (bus->nodes_modified)
1965 } while (bus->nodes_modified);
1970 int sd_bus_emit_properties_changed(
1973 const char *interface,
1974 const char *name, ...) {
1976 _cleanup_strv_free_ char **names = NULL;
1979 assert_return(bus, -EINVAL);
1980 assert_return(object_path_is_valid(path), -EINVAL);
1981 assert_return(interface_name_is_valid(interface), -EINVAL);
1982 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
1983 assert_return(!bus_pid_changed(bus), -ECHILD);
1989 names = strv_new_ap(name, ap);
1995 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
1998 static int interfaces_added_append_one_prefix(
2003 const char *interface,
2004 bool require_fallback) {
2006 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2007 struct node_vtable *c;
2018 n = hashmap_get(bus->nodes, prefix);
2022 LIST_FOREACH(vtables, c, n->vtables) {
2023 if (require_fallback && !c->is_fallback)
2026 if (streq(c->interface, interface))
2033 r = node_vtable_get_userdata(bus, path, c, &u);
2036 if (bus->nodes_modified)
2039 r = sd_bus_message_append_basic(m, 's', interface);
2043 r = sd_bus_message_open_container(m, 'a', "{sv}");
2047 r = vtable_append_all_properties(bus, m,path, c, u, &error);
2050 if (sd_bus_error_is_set(&error))
2051 return bus_error_to_errno(&error);
2052 if (bus->nodes_modified)
2055 r = sd_bus_message_close_container(m);
2062 static int interfaces_added_append_one(
2066 const char *interface) {
2076 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2079 if (bus->nodes_modified)
2082 prefix = alloca(strlen(path) + 1);
2083 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2084 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2087 if (bus->nodes_modified)
2094 int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2095 BUS_DONT_DESTROY(bus);
2097 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2101 assert_return(bus, -EINVAL);
2102 assert_return(object_path_is_valid(path), -EINVAL);
2103 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2104 assert_return(!bus_pid_changed(bus), -ECHILD);
2106 if (strv_isempty(interfaces))
2110 bus->nodes_modified = false;
2113 m = sd_bus_message_unref(m);
2115 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2119 r = sd_bus_message_append_basic(m, 'o', path);
2123 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2127 STRV_FOREACH(i, interfaces) {
2128 assert_return(interface_name_is_valid(*i), -EINVAL);
2130 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2134 r = interfaces_added_append_one(bus, m, path, *i);
2138 if (bus->nodes_modified)
2141 r = sd_bus_message_close_container(m);
2146 if (bus->nodes_modified)
2149 r = sd_bus_message_close_container(m);
2153 } while (bus->nodes_modified);
2155 return sd_bus_send(bus, m, NULL);
2158 int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2159 _cleanup_strv_free_ char **interfaces = NULL;
2162 assert_return(bus, -EINVAL);
2163 assert_return(object_path_is_valid(path), -EINVAL);
2164 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2165 assert_return(!bus_pid_changed(bus), -ECHILD);
2167 va_start(ap, interface);
2168 interfaces = strv_new_ap(interface, ap);
2174 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2177 int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2178 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2181 assert_return(bus, -EINVAL);
2182 assert_return(object_path_is_valid(path), -EINVAL);
2183 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2184 assert_return(!bus_pid_changed(bus), -ECHILD);
2186 if (strv_isempty(interfaces))
2189 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2193 r = sd_bus_message_append_basic(m, 'o', path);
2197 r = sd_bus_message_append_strv(m, interfaces);
2201 return sd_bus_send(bus, m, NULL);
2204 int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2205 _cleanup_strv_free_ char **interfaces = NULL;
2208 assert_return(bus, -EINVAL);
2209 assert_return(object_path_is_valid(path), -EINVAL);
2210 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2211 assert_return(!bus_pid_changed(bus), -ECHILD);
2213 va_start(ap, interface);
2214 interfaces = strv_new_ap(interface, ap);
2220 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2223 int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2226 assert_return(bus, -EINVAL);
2227 assert_return(object_path_is_valid(path), -EINVAL);
2228 assert_return(!bus_pid_changed(bus), -ECHILD);
2230 n = bus_node_allocate(bus, path);
2234 n->object_manager = true;
2235 bus->nodes_modified = true;
2239 int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2242 assert_return(bus, -EINVAL);
2243 assert_return(object_path_is_valid(path), -EINVAL);
2244 assert_return(!bus_pid_changed(bus), -ECHILD);
2246 n = hashmap_get(bus->nodes, path);
2250 if (!n->object_manager)
2253 n->object_manager = false;
2254 bus->nodes_modified = true;
2255 bus_node_gc(bus, n);