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"
32 static int node_vtable_get_userdata(
35 struct node_vtable *c,
47 r = c->find(bus, path, c->interface, &u, u);
58 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
61 return (uint8_t*) u + p->x.property.offset;
64 static int vtable_property_get_userdata(
67 struct vtable_member *p,
78 r = node_vtable_get_userdata(bus, path, p->parent, &u);
81 if (bus->nodes_modified)
84 *userdata = vtable_property_convert_userdata(p->vtable, u);
88 static int add_enumerated_to_set(
91 struct node_enumerator *first,
94 struct node_enumerator *c;
101 LIST_FOREACH(enumerators, c, first) {
102 char **children = NULL, **k;
104 if (bus->nodes_modified)
107 r = c->callback(bus, prefix, &children, c->userdata);
111 STRV_FOREACH(k, children) {
117 if (!object_path_is_valid(*k)){
123 if (!object_path_startswith(*k, prefix)) {
128 r = set_consume(s, *k);
139 static int add_subtree_to_set(
153 r = add_enumerated_to_set(bus, prefix, n->enumerators, s);
156 if (bus->nodes_modified)
159 LIST_FOREACH(siblings, i, n->child) {
162 if (!object_path_startswith(i->path, prefix))
169 r = set_consume(s, t);
170 if (r < 0 && r != -EEXIST)
173 r = add_subtree_to_set(bus, prefix, i, s);
176 if (bus->nodes_modified)
183 static int get_child_nodes(
197 s = set_new(string_hash_func, string_compare_func);
201 r = add_subtree_to_set(bus, prefix, n, s);
211 static int node_callbacks_run(
214 struct node_callback *first,
215 bool require_fallback,
216 bool *found_object) {
218 struct node_callback *c;
223 assert(found_object);
225 LIST_FOREACH(callbacks, c, first) {
226 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
228 if (bus->nodes_modified)
231 if (require_fallback && !c->is_fallback)
234 *found_object = true;
236 if (c->last_iteration == bus->iteration_counter)
239 c->last_iteration = bus->iteration_counter;
241 r = sd_bus_message_rewind(m, true);
245 r = c->callback(bus, m, c->userdata, &error_buffer);
246 r = bus_maybe_reply_error(m, r, &error_buffer);
254 static int method_callbacks_run(
257 struct vtable_member *c,
258 bool require_fallback,
259 bool *found_object) {
261 const char *signature;
268 assert(found_object);
270 if (require_fallback && !c->parent->is_fallback)
273 r = node_vtable_get_userdata(bus, m->path, c->parent, &u);
276 if (bus->nodes_modified)
279 *found_object = true;
281 if (c->last_iteration == bus->iteration_counter)
284 c->last_iteration = bus->iteration_counter;
286 r = sd_bus_message_rewind(m, true);
290 signature = sd_bus_message_get_signature(m, true);
294 if (!streq(strempty(c->vtable->x.method.signature), signature)) {
295 return sd_bus_reply_method_errorf(
297 SD_BUS_ERROR_INVALID_ARGS,
298 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
299 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
302 if (c->vtable->x.method.handler) {
303 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
305 r = c->vtable->x.method.handler(bus, m, u, &error_buffer);
307 return bus_maybe_reply_error(m, r, &error_buffer);
310 /* If the method callback is NULL, make this a successful NOP */
311 r = sd_bus_reply_method_return(m, NULL);
318 static int invoke_property_get(
320 const sd_bus_vtable *v,
322 const char *interface,
323 const char *property,
324 sd_bus_message *reply,
326 sd_bus_error *error) {
337 if (v->x.property.get)
338 return v->x.property.get(bus, path, interface, property, reply, userdata, error);
340 /* Automatic handling if no callback is defined. */
342 if (streq(v->x.property.signature, "as"))
343 return sd_bus_message_append_strv(reply, *(char***) userdata);
345 assert(signature_is_single(v->x.property.signature, false));
346 assert(bus_type_is_basic(v->x.property.signature[0]));
348 switch (v->x.property.signature[0]) {
350 case SD_BUS_TYPE_STRING:
351 case SD_BUS_TYPE_SIGNATURE:
352 p = strempty(*(char**) userdata);
355 case SD_BUS_TYPE_OBJECT_PATH:
356 p = *(char**) userdata;
365 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
368 static int invoke_property_set(
370 const sd_bus_vtable *v,
372 const char *interface,
373 const char *property,
374 sd_bus_message *value,
376 sd_bus_error *error) {
387 if (v->x.property.set)
388 return v->x.property.set(bus, path, interface, property, value, userdata, error);
390 /* Automatic handling if no callback is defined. */
392 assert(signature_is_single(v->x.property.signature, false));
393 assert(bus_type_is_basic(v->x.property.signature[0]));
395 switch (v->x.property.signature[0]) {
397 case SD_BUS_TYPE_STRING:
398 case SD_BUS_TYPE_OBJECT_PATH:
399 case SD_BUS_TYPE_SIGNATURE: {
403 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
411 free(*(char**) userdata);
412 *(char**) userdata = n;
418 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
428 static int property_get_set_callbacks_run(
431 struct vtable_member *c,
432 bool require_fallback,
434 bool *found_object) {
436 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
437 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
444 assert(found_object);
446 if (require_fallback && !c->parent->is_fallback)
449 r = vtable_property_get_userdata(bus, m->path, c, &u);
452 if (bus->nodes_modified)
455 *found_object = true;
457 r = sd_bus_message_new_method_return(m, &reply);
462 /* Note that we do not protect against reexecution
463 * here (using the last_iteration check, see below),
464 * should the node tree have changed and we got called
465 * again. We assume that property Get() calls are
466 * ultimately without side-effects or if they aren't
467 * then at least idempotent. */
469 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
473 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
475 return sd_bus_reply_method_errno(m, r, &error);
476 if (sd_bus_error_is_set(&error))
477 return sd_bus_reply_method_error(m, &error);
479 if (bus->nodes_modified)
482 r = sd_bus_message_close_container(reply);
487 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
488 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
490 /* Avoid that we call the set routine more than once
491 * if the processing of this message got restarted
492 * because the node tree changed. */
493 if (c->last_iteration == bus->iteration_counter)
496 c->last_iteration = bus->iteration_counter;
498 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
502 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
504 return sd_bus_reply_method_errno(m, r, &error);
505 if (sd_bus_error_is_set(&error))
506 return sd_bus_reply_method_error(m, &error);
508 if (bus->nodes_modified)
511 r = sd_bus_message_exit_container(m);
516 r = sd_bus_send(bus, reply, NULL);
523 static int vtable_append_all_properties(
525 sd_bus_message *reply,
527 struct node_vtable *c,
529 sd_bus_error *error) {
531 const sd_bus_vtable *v;
539 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
540 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
543 r = sd_bus_message_open_container(reply, 'e', "sv");
547 r = sd_bus_message_append(reply, "s", v->x.property.member);
551 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
555 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
556 if (sd_bus_error_is_set(error))
559 sd_bus_error_set_errno(error, r);
562 if (bus->nodes_modified)
565 r = sd_bus_message_close_container(reply);
569 r = sd_bus_message_close_container(reply);
577 static int property_get_all_callbacks_run(
580 struct node_vtable *first,
581 bool require_fallback,
583 bool *found_object) {
585 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
586 struct node_vtable *c;
587 bool found_interface;
592 assert(found_object);
594 r = sd_bus_message_new_method_return(m, &reply);
598 r = sd_bus_message_open_container(reply, 'a', "{sv}");
602 found_interface = !iface ||
603 streq(iface, "org.freedesktop.DBus.Properties") ||
604 streq(iface, "org.freedesktop.DBus.Peer") ||
605 streq(iface, "org.freedesktop.DBus.Introspectable");
607 LIST_FOREACH(vtables, c, first) {
608 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
611 if (require_fallback && !c->is_fallback)
614 r = node_vtable_get_userdata(bus, m->path, c, &u);
617 if (bus->nodes_modified)
622 *found_object = true;
624 if (iface && !streq(c->interface, iface))
626 found_interface = true;
628 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
632 if (sd_bus_error_is_set(&error)) {
633 r = sd_bus_reply_method_error(m, &error);
639 if (bus->nodes_modified)
643 if (!found_interface) {
644 r = sd_bus_reply_method_errorf(
646 SD_BUS_ERROR_UNKNOWN_INTERFACE,
647 "Unknown interface '%s'.", iface);
654 r = sd_bus_message_close_container(reply);
658 r = sd_bus_send(bus, reply, NULL);
665 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
669 if (n->object_manager)
673 return bus_node_with_object_manager(bus, n->parent);
678 static bool bus_node_exists(
682 bool require_fallback) {
684 struct node_vtable *c;
685 struct node_callback *k;
691 /* Tests if there's anything attached directly to this node
692 * for the specified path */
694 LIST_FOREACH(callbacks, k, n->callbacks) {
695 if (require_fallback && !k->is_fallback)
701 LIST_FOREACH(vtables, c, n->vtables) {
703 if (require_fallback && !c->is_fallback)
706 if (node_vtable_get_userdata(bus, path, c, NULL) > 0)
708 if (bus->nodes_modified)
712 return !require_fallback && (n->enumerators || n->object_manager);
715 static int process_introspect(
719 bool require_fallback,
720 bool *found_object) {
722 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
723 _cleanup_set_free_free_ Set *s = NULL;
724 const char *previous_interface = NULL;
725 struct introspect intro;
726 struct node_vtable *c;
733 assert(found_object);
735 r = get_child_nodes(bus, m->path, n, &s);
738 if (bus->nodes_modified)
741 r = introspect_begin(&intro);
745 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
749 empty = set_isempty(s);
751 LIST_FOREACH(vtables, c, n->vtables) {
752 if (require_fallback && !c->is_fallback)
755 r = node_vtable_get_userdata(bus, m->path, c, NULL);
758 if (bus->nodes_modified)
765 if (!streq_ptr(previous_interface, c->interface)) {
767 if (previous_interface)
768 fputs(" </interface>\n", intro.f);
770 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
773 r = introspect_write_interface(&intro, c->vtable);
777 previous_interface = c->interface;
780 if (previous_interface)
781 fputs(" </interface>\n", intro.f);
784 /* Nothing?, let's see if we exist at all, and if not
785 * refuse to do anything */
786 r = bus_node_exists(bus, n, m->path, require_fallback);
789 if (bus->nodes_modified)
795 *found_object = true;
797 r = introspect_write_child_nodes(&intro, s, m->path);
801 r = introspect_finish(&intro, bus, m, &reply);
805 r = sd_bus_send(bus, reply, NULL);
812 introspect_free(&intro);
816 static int object_manager_serialize_path(
818 sd_bus_message *reply,
821 bool require_fallback,
822 sd_bus_error *error) {
824 const char *previous_interface = NULL;
825 bool found_something = false;
826 struct node_vtable *i;
836 n = hashmap_get(bus->nodes, prefix);
840 LIST_FOREACH(vtables, i, n->vtables) {
843 if (require_fallback && !i->is_fallback)
846 r = node_vtable_get_userdata(bus, path, i, &u);
849 if (bus->nodes_modified)
854 if (!found_something) {
856 /* Open the object part */
858 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
862 r = sd_bus_message_append(reply, "o", path);
866 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
870 found_something = true;
873 if (!streq_ptr(previous_interface, i->interface)) {
875 /* Maybe close the previous interface part */
877 if (previous_interface) {
878 r = sd_bus_message_close_container(reply);
882 r = sd_bus_message_close_container(reply);
887 /* Open the new interface part */
889 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
893 r = sd_bus_message_append(reply, "s", i->interface);
897 r = sd_bus_message_open_container(reply, 'a', "{sv}");
902 r = vtable_append_all_properties(bus, reply, path, i, u, error);
905 if (sd_bus_error_is_set(error))
907 if (bus->nodes_modified)
910 previous_interface = i->interface;
913 if (previous_interface) {
914 r = sd_bus_message_close_container(reply);
918 r = sd_bus_message_close_container(reply);
923 if (found_something) {
924 r = sd_bus_message_close_container(reply);
928 r = sd_bus_message_close_container(reply);
936 static int object_manager_serialize_path_and_fallbacks(
938 sd_bus_message *reply,
940 sd_bus_error *error) {
950 /* First, add all vtables registered for this path */
951 r = object_manager_serialize_path(bus, reply, path, path, false, error);
954 if (sd_bus_error_is_set(error))
956 if (bus->nodes_modified)
959 /* Second, add fallback vtables registered for any of the prefixes */
960 prefix = alloca(strlen(path) + 1);
961 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
962 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
965 if (sd_bus_error_is_set(error))
967 if (bus->nodes_modified)
974 static int process_get_managed_objects(
978 bool require_fallback,
979 bool *found_object) {
981 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
982 _cleanup_set_free_free_ Set *s = NULL;
989 assert(found_object);
991 if (!bus_node_with_object_manager(bus, n))
994 r = get_child_nodes(bus, m->path, n, &s);
997 if (bus->nodes_modified)
1000 r = sd_bus_message_new_method_return(m, &reply);
1004 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1008 empty = set_isempty(s);
1010 struct node_vtable *c;
1012 /* Hmm, so we have no children? Then let's check
1013 * whether we exist at all, i.e. whether at least one
1016 LIST_FOREACH(vtables, c, n->vtables) {
1018 if (require_fallback && !c->is_fallback)
1036 SET_FOREACH(path, s, i) {
1037 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1039 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1043 if (sd_bus_error_is_set(&error)) {
1044 r = sd_bus_reply_method_error(m, &error);
1051 if (bus->nodes_modified)
1056 r = sd_bus_message_close_container(reply);
1060 r = sd_bus_send(bus, reply, NULL);
1067 static int object_find_and_run(
1071 bool require_fallback,
1072 bool *found_object) {
1075 struct vtable_member vtable_key, *v;
1081 assert(found_object);
1083 n = hashmap_get(bus->nodes, p);
1087 /* First, try object callbacks */
1088 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1091 if (bus->nodes_modified)
1094 if (!m->interface || !m->member)
1097 /* Then, look for a known method */
1098 vtable_key.path = (char*) p;
1099 vtable_key.interface = m->interface;
1100 vtable_key.member = m->member;
1102 v = hashmap_get(bus->vtable_methods, &vtable_key);
1104 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1107 if (bus->nodes_modified)
1111 /* Then, look for a known property */
1112 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1115 get = streq(m->member, "Get");
1117 if (get || streq(m->member, "Set")) {
1119 r = sd_bus_message_rewind(m, true);
1123 vtable_key.path = (char*) p;
1125 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1129 v = hashmap_get(bus->vtable_properties, &vtable_key);
1131 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1136 } else if (streq(m->member, "GetAll")) {
1139 r = sd_bus_message_rewind(m, true);
1143 r = sd_bus_message_read(m, "s", &iface);
1150 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1155 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1157 r = process_introspect(bus, m, n, require_fallback, found_object);
1161 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1163 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1168 if (bus->nodes_modified)
1171 if (!*found_object) {
1172 r = bus_node_exists(bus, n, m->path, require_fallback);
1176 *found_object = true;
1182 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1185 bool found_object = false;
1190 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1196 if (hashmap_isempty(bus->nodes))
1199 pl = strlen(m->path);
1203 bus->nodes_modified = false;
1205 r = object_find_and_run(bus, m, m->path, false, &found_object);
1209 /* Look for fallback prefixes */
1210 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1212 if (bus->nodes_modified)
1215 r = object_find_and_run(bus, m, prefix, true, &found_object);
1220 } while (bus->nodes_modified);
1225 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1226 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1227 r = sd_bus_reply_method_errorf(
1229 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1230 "Unknown property or interface.");
1232 r = sd_bus_reply_method_errorf(
1234 SD_BUS_ERROR_UNKNOWN_METHOD,
1235 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1243 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1244 struct node *n, *parent;
1251 assert(path[0] == '/');
1253 n = hashmap_get(bus->nodes, path);
1257 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1265 if (streq(path, "/"))
1268 e = strrchr(path, '/');
1271 p = strndupa(path, MAX(1, path - e));
1273 parent = bus_node_allocate(bus, p);
1280 n = new0(struct node, 1);
1287 r = hashmap_put(bus->nodes, s, n);
1295 LIST_PREPEND(siblings, parent->child, n);
1300 static void bus_node_gc(sd_bus *b, struct node *n) {
1313 assert(hashmap_remove(b->nodes, n->path) == n);
1316 LIST_REMOVE(siblings, n->parent->child, n);
1319 bus_node_gc(b, n->parent);
1323 static int bus_add_object(
1327 sd_bus_message_handler_t callback,
1330 struct node_callback *c;
1334 assert_return(bus, -EINVAL);
1335 assert_return(object_path_is_valid(path), -EINVAL);
1336 assert_return(callback, -EINVAL);
1337 assert_return(!bus_pid_changed(bus), -ECHILD);
1339 n = bus_node_allocate(bus, path);
1343 c = new0(struct node_callback, 1);
1350 c->callback = callback;
1351 c->userdata = userdata;
1352 c->is_fallback = fallback;
1354 LIST_PREPEND(callbacks, n->callbacks, c);
1355 bus->nodes_modified = true;
1361 bus_node_gc(bus, n);
1365 static int bus_remove_object(
1369 sd_bus_message_handler_t callback,
1372 struct node_callback *c;
1375 assert_return(bus, -EINVAL);
1376 assert_return(object_path_is_valid(path), -EINVAL);
1377 assert_return(callback, -EINVAL);
1378 assert_return(!bus_pid_changed(bus), -ECHILD);
1380 n = hashmap_get(bus->nodes, path);
1384 LIST_FOREACH(callbacks, c, n->callbacks)
1385 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1390 LIST_REMOVE(callbacks, n->callbacks, c);
1393 bus_node_gc(bus, n);
1394 bus->nodes_modified = true;
1399 _public_ int sd_bus_add_object(sd_bus *bus,
1401 sd_bus_message_handler_t callback,
1404 return bus_add_object(bus, false, path, callback, userdata);
1407 _public_ int sd_bus_remove_object(sd_bus *bus,
1409 sd_bus_message_handler_t callback,
1412 return bus_remove_object(bus, false, path, callback, userdata);
1415 _public_ int sd_bus_add_fallback(sd_bus *bus,
1417 sd_bus_message_handler_t callback,
1420 return bus_add_object(bus, true, prefix, callback, userdata);
1423 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1425 sd_bus_message_handler_t callback,
1428 return bus_remove_object(bus, true, prefix, callback, userdata);
1431 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1437 if (w->interface && w->node && w->vtable) {
1438 const sd_bus_vtable *v;
1440 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1441 struct vtable_member *x = NULL;
1445 case _SD_BUS_VTABLE_METHOD: {
1446 struct vtable_member key;
1448 key.path = w->node->path;
1449 key.interface = w->interface;
1450 key.member = v->x.method.member;
1452 x = hashmap_remove(bus->vtable_methods, &key);
1456 case _SD_BUS_VTABLE_PROPERTY:
1457 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1458 struct vtable_member key;
1460 key.path = w->node->path;
1461 key.interface = w->interface;
1462 key.member = v->x.property.member;
1463 x = hashmap_remove(bus->vtable_properties, &key);
1475 static unsigned vtable_member_hash_func(const void *a) {
1476 const struct vtable_member *m = a;
1481 string_hash_func(m->path) ^
1482 string_hash_func(m->interface) ^
1483 string_hash_func(m->member);
1486 static int vtable_member_compare_func(const void *a, const void *b) {
1487 const struct vtable_member *x = a, *y = b;
1493 r = strcmp(x->path, y->path);
1497 r = strcmp(x->interface, y->interface);
1501 return strcmp(x->member, y->member);
1504 static int add_object_vtable_internal(
1507 const char *interface,
1508 const sd_bus_vtable *vtable,
1510 sd_bus_object_find_t find,
1513 struct node_vtable *c = NULL, *i, *existing = NULL;
1514 const sd_bus_vtable *v;
1518 assert_return(bus, -EINVAL);
1519 assert_return(object_path_is_valid(path), -EINVAL);
1520 assert_return(interface_name_is_valid(interface), -EINVAL);
1521 assert_return(vtable, -EINVAL);
1522 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1523 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1524 assert_return(!bus_pid_changed(bus), -ECHILD);
1525 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1526 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1527 !streq(interface, "org.freedesktop.DBus.Peer") &&
1528 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1530 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1534 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1538 n = bus_node_allocate(bus, path);
1542 LIST_FOREACH(vtables, i, n->vtables) {
1543 if (i->is_fallback != fallback) {
1548 if (streq(i->interface, interface)) {
1550 if (i->vtable == vtable) {
1559 c = new0(struct node_vtable, 1);
1566 c->is_fallback = fallback;
1568 c->userdata = userdata;
1571 c->interface = strdup(interface);
1572 if (!c->interface) {
1577 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1581 case _SD_BUS_VTABLE_METHOD: {
1582 struct vtable_member *m;
1584 if (!member_name_is_valid(v->x.method.member) ||
1585 !signature_is_valid(strempty(v->x.method.signature), false) ||
1586 !signature_is_valid(strempty(v->x.method.result), false) ||
1587 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1588 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1593 m = new0(struct vtable_member, 1);
1601 m->interface = c->interface;
1602 m->member = v->x.method.member;
1605 r = hashmap_put(bus->vtable_methods, m, m);
1614 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1616 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1623 case _SD_BUS_VTABLE_PROPERTY: {
1624 struct vtable_member *m;
1626 if (!member_name_is_valid(v->x.property.member) ||
1627 !signature_is_single(v->x.property.signature, false) ||
1628 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1629 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1630 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1636 m = new0(struct vtable_member, 1);
1644 m->interface = c->interface;
1645 m->member = v->x.property.member;
1648 r = hashmap_put(bus->vtable_properties, m, m);
1657 case _SD_BUS_VTABLE_SIGNAL:
1659 if (!member_name_is_valid(v->x.signal.member) ||
1660 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1673 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1674 bus->nodes_modified = true;
1680 free_node_vtable(bus, c);
1682 bus_node_gc(bus, n);
1686 static int remove_object_vtable_internal(
1689 const char *interface,
1690 const sd_bus_vtable *vtable,
1692 sd_bus_object_find_t find,
1695 struct node_vtable *c;
1698 assert_return(bus, -EINVAL);
1699 assert_return(object_path_is_valid(path), -EINVAL);
1700 assert_return(interface_name_is_valid(interface), -EINVAL);
1701 assert_return(!bus_pid_changed(bus), -ECHILD);
1703 n = hashmap_get(bus->nodes, path);
1707 LIST_FOREACH(vtables, c, n->vtables)
1708 if (streq(c->interface, interface) &&
1709 c->is_fallback == fallback &&
1710 c->vtable == vtable &&
1712 c->userdata == userdata)
1718 LIST_REMOVE(vtables, n->vtables, c);
1720 free_node_vtable(bus, c);
1721 bus_node_gc(bus, n);
1723 bus->nodes_modified = true;
1728 _public_ int sd_bus_add_object_vtable(
1731 const char *interface,
1732 const sd_bus_vtable *vtable,
1735 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1738 _public_ int sd_bus_remove_object_vtable(
1741 const char *interface,
1742 const sd_bus_vtable *vtable,
1745 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1748 _public_ int sd_bus_add_fallback_vtable(
1751 const char *interface,
1752 const sd_bus_vtable *vtable,
1753 sd_bus_object_find_t find,
1756 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1759 _public_ int sd_bus_remove_fallback_vtable(
1762 const char *interface,
1763 const sd_bus_vtable *vtable,
1764 sd_bus_object_find_t find,
1767 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1770 _public_ int sd_bus_add_node_enumerator(
1773 sd_bus_node_enumerator_t callback,
1776 struct node_enumerator *c;
1780 assert_return(bus, -EINVAL);
1781 assert_return(object_path_is_valid(path), -EINVAL);
1782 assert_return(callback, -EINVAL);
1783 assert_return(!bus_pid_changed(bus), -ECHILD);
1785 n = bus_node_allocate(bus, path);
1789 c = new0(struct node_enumerator, 1);
1796 c->callback = callback;
1797 c->userdata = userdata;
1799 LIST_PREPEND(enumerators, n->enumerators, c);
1801 bus->nodes_modified = true;
1807 bus_node_gc(bus, n);
1811 _public_ int sd_bus_remove_node_enumerator(
1814 sd_bus_node_enumerator_t callback,
1817 struct node_enumerator *c;
1820 assert_return(bus, -EINVAL);
1821 assert_return(object_path_is_valid(path), -EINVAL);
1822 assert_return(callback, -EINVAL);
1823 assert_return(!bus_pid_changed(bus), -ECHILD);
1825 n = hashmap_get(bus->nodes, path);
1829 LIST_FOREACH(enumerators, c, n->enumerators)
1830 if (c->callback == callback && c->userdata == userdata)
1836 LIST_REMOVE(enumerators, n->enumerators, c);
1839 bus_node_gc(bus, n);
1841 bus->nodes_modified = true;
1846 static int emit_properties_changed_on_interface(
1850 const char *interface,
1851 bool require_fallback,
1854 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1855 bool has_invalidating = false, has_changing = false;
1856 struct vtable_member key = {};
1857 struct node_vtable *c;
1868 n = hashmap_get(bus->nodes, prefix);
1872 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1876 r = sd_bus_message_append(m, "s", interface);
1880 r = sd_bus_message_open_container(m, 'a', "{sv}");
1885 key.interface = interface;
1887 LIST_FOREACH(vtables, c, n->vtables) {
1888 if (require_fallback && !c->is_fallback)
1891 if (!streq(c->interface, interface))
1894 r = node_vtable_get_userdata(bus, path, c, &u);
1897 if (bus->nodes_modified)
1902 STRV_FOREACH(property, names) {
1903 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1904 struct vtable_member *v;
1906 assert_return(member_name_is_valid(*property), -EINVAL);
1908 key.member = *property;
1909 v = hashmap_get(bus->vtable_properties, &key);
1913 /* If there are two vtables for the same
1914 * interface, let's handle this property when
1915 * we come to that vtable. */
1919 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1921 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1922 has_invalidating = true;
1926 has_changing = true;
1928 r = sd_bus_message_open_container(m, 'e', "sv");
1932 r = sd_bus_message_append(m, "s", *property);
1936 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1940 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error);
1943 if (sd_bus_error_is_set(&error))
1944 return sd_bus_error_get_errno(&error);
1945 if (bus->nodes_modified)
1948 r = sd_bus_message_close_container(m);
1952 r = sd_bus_message_close_container(m);
1958 if (!has_invalidating && !has_changing)
1961 r = sd_bus_message_close_container(m);
1965 r = sd_bus_message_open_container(m, 'a', "s");
1969 if (has_invalidating) {
1970 LIST_FOREACH(vtables, c, n->vtables) {
1971 if (require_fallback && !c->is_fallback)
1974 if (!streq(c->interface, interface))
1977 r = node_vtable_get_userdata(bus, path, c, &u);
1980 if (bus->nodes_modified)
1985 STRV_FOREACH(property, names) {
1986 struct vtable_member *v;
1988 key.member = *property;
1989 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1990 assert(c == v->parent);
1992 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1995 r = sd_bus_message_append(m, "s", *property);
2002 r = sd_bus_message_close_container(m);
2006 r = sd_bus_send(bus, m, NULL);
2013 _public_ int sd_bus_emit_properties_changed_strv(
2016 const char *interface,
2019 BUS_DONT_DESTROY(bus);
2023 assert_return(bus, -EINVAL);
2024 assert_return(object_path_is_valid(path), -EINVAL);
2025 assert_return(interface_name_is_valid(interface), -EINVAL);
2026 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2027 assert_return(!bus_pid_changed(bus), -ECHILD);
2029 if (strv_isempty(names))
2033 bus->nodes_modified = false;
2035 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2038 if (bus->nodes_modified)
2041 prefix = alloca(strlen(path) + 1);
2042 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2043 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2046 if (bus->nodes_modified)
2050 } while (bus->nodes_modified);
2055 _public_ int sd_bus_emit_properties_changed(
2058 const char *interface,
2059 const char *name, ...) {
2063 assert_return(bus, -EINVAL);
2064 assert_return(object_path_is_valid(path), -EINVAL);
2065 assert_return(interface_name_is_valid(interface), -EINVAL);
2066 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2067 assert_return(!bus_pid_changed(bus), -ECHILD);
2072 names = strv_from_stdarg_alloca(name);
2074 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2077 static int interfaces_added_append_one_prefix(
2082 const char *interface,
2083 bool require_fallback) {
2085 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2086 bool found_interface = false;
2087 struct node_vtable *c;
2098 n = hashmap_get(bus->nodes, prefix);
2102 LIST_FOREACH(vtables, c, n->vtables) {
2103 if (require_fallback && !c->is_fallback)
2106 if (!streq(c->interface, interface))
2109 r = node_vtable_get_userdata(bus, path, c, &u);
2112 if (bus->nodes_modified)
2117 if (!found_interface) {
2118 r = sd_bus_message_append_basic(m, 's', interface);
2122 r = sd_bus_message_open_container(m, 'a', "{sv}");
2126 found_interface = true;
2129 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2132 if (bus->nodes_modified)
2136 if (found_interface) {
2137 r = sd_bus_message_close_container(m);
2142 return found_interface;
2145 static int interfaces_added_append_one(
2149 const char *interface) {
2159 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2162 if (bus->nodes_modified)
2165 prefix = alloca(strlen(path) + 1);
2166 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2167 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2170 if (bus->nodes_modified)
2177 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2178 BUS_DONT_DESTROY(bus);
2180 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2184 assert_return(bus, -EINVAL);
2185 assert_return(object_path_is_valid(path), -EINVAL);
2186 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2187 assert_return(!bus_pid_changed(bus), -ECHILD);
2189 if (strv_isempty(interfaces))
2193 bus->nodes_modified = false;
2196 m = sd_bus_message_unref(m);
2198 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2202 r = sd_bus_message_append_basic(m, 'o', path);
2206 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2210 STRV_FOREACH(i, interfaces) {
2211 assert_return(interface_name_is_valid(*i), -EINVAL);
2213 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2217 r = interfaces_added_append_one(bus, m, path, *i);
2221 if (bus->nodes_modified)
2224 r = sd_bus_message_close_container(m);
2229 if (bus->nodes_modified)
2232 r = sd_bus_message_close_container(m);
2236 } while (bus->nodes_modified);
2238 return sd_bus_send(bus, m, NULL);
2241 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2244 assert_return(bus, -EINVAL);
2245 assert_return(object_path_is_valid(path), -EINVAL);
2246 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2247 assert_return(!bus_pid_changed(bus), -ECHILD);
2249 interfaces = strv_from_stdarg_alloca(interface);
2251 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2254 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2255 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2258 assert_return(bus, -EINVAL);
2259 assert_return(object_path_is_valid(path), -EINVAL);
2260 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2261 assert_return(!bus_pid_changed(bus), -ECHILD);
2263 if (strv_isempty(interfaces))
2266 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2270 r = sd_bus_message_append_basic(m, 'o', path);
2274 r = sd_bus_message_append_strv(m, interfaces);
2278 return sd_bus_send(bus, m, NULL);
2281 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2284 assert_return(bus, -EINVAL);
2285 assert_return(object_path_is_valid(path), -EINVAL);
2286 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2287 assert_return(!bus_pid_changed(bus), -ECHILD);
2289 interfaces = strv_from_stdarg_alloca(interface);
2291 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2294 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2297 assert_return(bus, -EINVAL);
2298 assert_return(object_path_is_valid(path), -EINVAL);
2299 assert_return(!bus_pid_changed(bus), -ECHILD);
2301 n = bus_node_allocate(bus, path);
2305 n->object_manager = true;
2306 bus->nodes_modified = true;
2310 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2313 assert_return(bus, -EINVAL);
2314 assert_return(object_path_is_valid(path), -EINVAL);
2315 assert_return(!bus_pid_changed(bus), -ECHILD);
2317 n = hashmap_get(bus->nodes, path);
2321 if (!n->object_manager)
2324 n->object_manager = false;
2325 bus->nodes_modified = true;
2326 bus_node_gc(bus, n);