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,
37 sd_bus_error *error) {
48 r = c->find(bus, path, c->interface, u, &u, error);
51 if (sd_bus_error_is_set(error))
52 return sd_bus_error_get_errno(error);
63 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
66 return (uint8_t*) u + p->x.property.offset;
69 static int vtable_property_get_userdata(
72 struct vtable_member *p,
74 sd_bus_error *error) {
84 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
87 if (bus->nodes_modified)
90 *userdata = vtable_property_convert_userdata(p->vtable, u);
94 static int add_enumerated_to_set(
97 struct node_enumerator *first,
99 sd_bus_error *error) {
101 struct node_enumerator *c;
108 LIST_FOREACH(enumerators, c, first) {
109 char **children = NULL, **k;
111 if (bus->nodes_modified)
114 r = c->callback(bus, prefix, c->userdata, &children, error);
117 if (sd_bus_error_is_set(error))
118 return sd_bus_error_get_errno(error);
120 STRV_FOREACH(k, children) {
126 if (!object_path_is_valid(*k)){
132 if (!object_path_startswith(*k, prefix)) {
137 r = set_consume(s, *k);
150 static int add_subtree_to_set(
155 sd_bus_error *error) {
165 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
168 if (bus->nodes_modified)
171 LIST_FOREACH(siblings, i, n->child) {
174 if (!object_path_startswith(i->path, prefix))
181 r = set_consume(s, t);
182 if (r < 0 && r != -EEXIST)
185 r = add_subtree_to_set(bus, prefix, i, s, error);
188 if (bus->nodes_modified)
195 static int get_child_nodes(
200 sd_bus_error *error) {
210 s = set_new(string_hash_func, string_compare_func);
214 r = add_subtree_to_set(bus, prefix, n, s, error);
224 static int node_callbacks_run(
227 struct node_callback *first,
228 bool require_fallback,
229 bool *found_object) {
231 struct node_callback *c;
236 assert(found_object);
238 LIST_FOREACH(callbacks, c, first) {
239 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
241 if (bus->nodes_modified)
244 if (require_fallback && !c->is_fallback)
247 *found_object = true;
249 if (c->last_iteration == bus->iteration_counter)
252 c->last_iteration = bus->iteration_counter;
254 r = sd_bus_message_rewind(m, true);
258 r = c->callback(bus, m, c->userdata, &error_buffer);
259 r = bus_maybe_reply_error(m, r, &error_buffer);
267 static int method_callbacks_run(
270 struct vtable_member *c,
271 bool require_fallback,
272 bool *found_object) {
274 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
275 const char *signature;
282 assert(found_object);
284 if (require_fallback && !c->parent->is_fallback)
287 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
289 return bus_maybe_reply_error(m, r, &error);
290 if (bus->nodes_modified)
293 *found_object = true;
295 if (c->last_iteration == bus->iteration_counter)
298 c->last_iteration = bus->iteration_counter;
300 r = sd_bus_message_rewind(m, true);
304 signature = sd_bus_message_get_signature(m, true);
308 if (!streq(strempty(c->vtable->x.method.signature), signature))
309 return sd_bus_reply_method_errorf(
311 SD_BUS_ERROR_INVALID_ARGS,
312 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
313 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
315 if (c->vtable->x.method.handler) {
316 r = c->vtable->x.method.handler(bus, m, u, &error);
317 return bus_maybe_reply_error(m, r, &error);
320 /* If the method callback is NULL, make this a successful NOP */
321 r = sd_bus_reply_method_return(m, NULL);
328 static int invoke_property_get(
330 const sd_bus_vtable *v,
332 const char *interface,
333 const char *property,
334 sd_bus_message *reply,
336 sd_bus_error *error) {
348 if (v->x.property.get) {
349 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
352 if (sd_bus_error_is_set(error))
353 return sd_bus_error_get_errno(error);
357 /* Automatic handling if no callback is defined. */
359 if (streq(v->x.property.signature, "as"))
360 return sd_bus_message_append_strv(reply, *(char***) userdata);
362 assert(signature_is_single(v->x.property.signature, false));
363 assert(bus_type_is_basic(v->x.property.signature[0]));
365 switch (v->x.property.signature[0]) {
367 case SD_BUS_TYPE_STRING:
368 case SD_BUS_TYPE_SIGNATURE:
369 p = strempty(*(char**) userdata);
372 case SD_BUS_TYPE_OBJECT_PATH:
373 p = *(char**) userdata;
382 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
385 static int invoke_property_set(
387 const sd_bus_vtable *v,
389 const char *interface,
390 const char *property,
391 sd_bus_message *value,
393 sd_bus_error *error) {
404 if (v->x.property.set) {
405 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
408 if (sd_bus_error_is_set(error))
409 return sd_bus_error_get_errno(error);
413 /* Automatic handling if no callback is defined. */
415 assert(signature_is_single(v->x.property.signature, false));
416 assert(bus_type_is_basic(v->x.property.signature[0]));
418 switch (v->x.property.signature[0]) {
420 case SD_BUS_TYPE_STRING:
421 case SD_BUS_TYPE_OBJECT_PATH:
422 case SD_BUS_TYPE_SIGNATURE: {
426 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
434 free(*(char**) userdata);
435 *(char**) userdata = n;
441 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
451 static int property_get_set_callbacks_run(
454 struct vtable_member *c,
455 bool require_fallback,
457 bool *found_object) {
459 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
460 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
467 assert(found_object);
469 if (require_fallback && !c->parent->is_fallback)
472 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
474 return bus_maybe_reply_error(m, r, &error);
475 if (bus->nodes_modified)
478 *found_object = true;
480 r = sd_bus_message_new_method_return(m, &reply);
485 /* Note that we do not protect against reexecution
486 * here (using the last_iteration check, see below),
487 * should the node tree have changed and we got called
488 * again. We assume that property Get() calls are
489 * ultimately without side-effects or if they aren't
490 * then at least idempotent. */
492 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
496 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
498 return bus_maybe_reply_error(m, r, &error);
500 if (bus->nodes_modified)
503 r = sd_bus_message_close_container(reply);
508 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
509 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
511 /* Avoid that we call the set routine more than once
512 * if the processing of this message got restarted
513 * because the node tree changed. */
514 if (c->last_iteration == bus->iteration_counter)
517 c->last_iteration = bus->iteration_counter;
519 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
523 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
525 return bus_maybe_reply_error(m, r, &error);
527 if (bus->nodes_modified)
530 r = sd_bus_message_exit_container(m);
535 r = sd_bus_send(bus, reply, NULL);
542 static int vtable_append_all_properties(
544 sd_bus_message *reply,
546 struct node_vtable *c,
548 sd_bus_error *error) {
550 const sd_bus_vtable *v;
558 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
559 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
562 r = sd_bus_message_open_container(reply, 'e', "sv");
566 r = sd_bus_message_append(reply, "s", v->x.property.member);
570 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
574 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
577 if (bus->nodes_modified)
580 r = sd_bus_message_close_container(reply);
584 r = sd_bus_message_close_container(reply);
592 static int property_get_all_callbacks_run(
595 struct node_vtable *first,
596 bool require_fallback,
598 bool *found_object) {
600 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
601 struct node_vtable *c;
602 bool found_interface;
607 assert(found_object);
609 r = sd_bus_message_new_method_return(m, &reply);
613 r = sd_bus_message_open_container(reply, 'a', "{sv}");
617 found_interface = !iface ||
618 streq(iface, "org.freedesktop.DBus.Properties") ||
619 streq(iface, "org.freedesktop.DBus.Peer") ||
620 streq(iface, "org.freedesktop.DBus.Introspectable");
622 LIST_FOREACH(vtables, c, first) {
623 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
626 if (require_fallback && !c->is_fallback)
629 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
631 return bus_maybe_reply_error(m, r, &error);
632 if (bus->nodes_modified)
637 *found_object = true;
639 if (iface && !streq(c->interface, iface))
641 found_interface = true;
643 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
645 return bus_maybe_reply_error(m, r, &error);
646 if (bus->nodes_modified)
650 if (!found_interface) {
651 r = sd_bus_reply_method_errorf(
653 SD_BUS_ERROR_UNKNOWN_INTERFACE,
654 "Unknown interface '%s'.", iface);
661 r = sd_bus_message_close_container(reply);
665 r = sd_bus_send(bus, reply, NULL);
672 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
676 if (n->object_manager)
680 return bus_node_with_object_manager(bus, n->parent);
685 static bool bus_node_exists(
689 bool require_fallback) {
691 struct node_vtable *c;
692 struct node_callback *k;
698 /* Tests if there's anything attached directly to this node
699 * for the specified path */
701 LIST_FOREACH(callbacks, k, n->callbacks) {
702 if (require_fallback && !k->is_fallback)
708 LIST_FOREACH(vtables, c, n->vtables) {
709 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
711 if (require_fallback && !c->is_fallback)
714 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
716 if (bus->nodes_modified)
720 return !require_fallback && (n->enumerators || n->object_manager);
723 static int process_introspect(
727 bool require_fallback,
728 bool *found_object) {
730 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
731 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
732 _cleanup_set_free_free_ Set *s = NULL;
733 const char *previous_interface = NULL;
734 struct introspect intro;
735 struct node_vtable *c;
742 assert(found_object);
744 r = get_child_nodes(bus, m->path, n, &s, &error);
746 return bus_maybe_reply_error(m, r, &error);
747 if (bus->nodes_modified)
750 r = introspect_begin(&intro);
754 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
758 empty = set_isempty(s);
760 LIST_FOREACH(vtables, c, n->vtables) {
761 if (require_fallback && !c->is_fallback)
764 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
766 r = bus_maybe_reply_error(m, r, &error);
769 if (bus->nodes_modified) {
778 if (!streq_ptr(previous_interface, c->interface)) {
780 if (previous_interface)
781 fputs(" </interface>\n", intro.f);
783 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
786 r = introspect_write_interface(&intro, c->vtable);
790 previous_interface = c->interface;
793 if (previous_interface)
794 fputs(" </interface>\n", intro.f);
797 /* Nothing?, let's see if we exist at all, and if not
798 * refuse to do anything */
799 r = bus_node_exists(bus, n, m->path, require_fallback);
802 if (bus->nodes_modified)
808 *found_object = true;
810 r = introspect_write_child_nodes(&intro, s, m->path);
814 r = introspect_finish(&intro, bus, m, &reply);
818 r = sd_bus_send(bus, reply, NULL);
825 introspect_free(&intro);
829 static int object_manager_serialize_path(
831 sd_bus_message *reply,
834 bool require_fallback,
835 sd_bus_error *error) {
837 const char *previous_interface = NULL;
838 bool found_something = false;
839 struct node_vtable *i;
849 n = hashmap_get(bus->nodes, prefix);
853 LIST_FOREACH(vtables, i, n->vtables) {
856 if (require_fallback && !i->is_fallback)
859 r = node_vtable_get_userdata(bus, path, i, &u, error);
862 if (bus->nodes_modified)
867 if (!found_something) {
869 /* Open the object part */
871 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
875 r = sd_bus_message_append(reply, "o", path);
879 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
883 found_something = true;
886 if (!streq_ptr(previous_interface, i->interface)) {
888 /* Maybe close the previous interface part */
890 if (previous_interface) {
891 r = sd_bus_message_close_container(reply);
895 r = sd_bus_message_close_container(reply);
900 /* Open the new interface part */
902 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
906 r = sd_bus_message_append(reply, "s", i->interface);
910 r = sd_bus_message_open_container(reply, 'a', "{sv}");
915 r = vtable_append_all_properties(bus, reply, path, i, u, error);
918 if (bus->nodes_modified)
921 previous_interface = i->interface;
924 if (previous_interface) {
925 r = sd_bus_message_close_container(reply);
929 r = sd_bus_message_close_container(reply);
934 if (found_something) {
935 r = sd_bus_message_close_container(reply);
939 r = sd_bus_message_close_container(reply);
947 static int object_manager_serialize_path_and_fallbacks(
949 sd_bus_message *reply,
951 sd_bus_error *error) {
961 /* First, add all vtables registered for this path */
962 r = object_manager_serialize_path(bus, reply, path, path, false, error);
965 if (bus->nodes_modified)
968 /* Second, add fallback vtables registered for any of the prefixes */
969 prefix = alloca(strlen(path) + 1);
970 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
971 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
974 if (bus->nodes_modified)
981 static int process_get_managed_objects(
985 bool require_fallback,
986 bool *found_object) {
988 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
989 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
990 _cleanup_set_free_free_ Set *s = NULL;
997 assert(found_object);
999 if (!bus_node_with_object_manager(bus, n))
1002 r = get_child_nodes(bus, m->path, n, &s, &error);
1005 if (bus->nodes_modified)
1008 r = sd_bus_message_new_method_return(m, &reply);
1012 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1016 empty = set_isempty(s);
1018 struct node_vtable *c;
1020 /* Hmm, so we have no children? Then let's check
1021 * whether we exist at all, i.e. whether at least one
1024 LIST_FOREACH(vtables, c, n->vtables) {
1026 if (require_fallback && !c->is_fallback)
1044 SET_FOREACH(path, s, i) {
1045 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1049 if (bus->nodes_modified)
1054 r = sd_bus_message_close_container(reply);
1058 r = sd_bus_send(bus, reply, NULL);
1065 static int object_find_and_run(
1069 bool require_fallback,
1070 bool *found_object) {
1073 struct vtable_member vtable_key, *v;
1079 assert(found_object);
1081 n = hashmap_get(bus->nodes, p);
1085 /* First, try object callbacks */
1086 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1089 if (bus->nodes_modified)
1092 if (!m->interface || !m->member)
1095 /* Then, look for a known method */
1096 vtable_key.path = (char*) p;
1097 vtable_key.interface = m->interface;
1098 vtable_key.member = m->member;
1100 v = hashmap_get(bus->vtable_methods, &vtable_key);
1102 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1105 if (bus->nodes_modified)
1109 /* Then, look for a known property */
1110 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1113 get = streq(m->member, "Get");
1115 if (get || streq(m->member, "Set")) {
1117 r = sd_bus_message_rewind(m, true);
1121 vtable_key.path = (char*) p;
1123 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1127 v = hashmap_get(bus->vtable_properties, &vtable_key);
1129 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1134 } else if (streq(m->member, "GetAll")) {
1137 r = sd_bus_message_rewind(m, true);
1141 r = sd_bus_message_read(m, "s", &iface);
1148 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1153 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1155 r = process_introspect(bus, m, n, require_fallback, found_object);
1159 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1161 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1166 if (bus->nodes_modified)
1169 if (!*found_object) {
1170 r = bus_node_exists(bus, n, m->path, require_fallback);
1174 *found_object = true;
1180 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1183 bool found_object = false;
1188 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1194 if (hashmap_isempty(bus->nodes))
1197 pl = strlen(m->path);
1201 bus->nodes_modified = false;
1203 r = object_find_and_run(bus, m, m->path, false, &found_object);
1207 /* Look for fallback prefixes */
1208 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1210 if (bus->nodes_modified)
1213 r = object_find_and_run(bus, m, prefix, true, &found_object);
1218 } while (bus->nodes_modified);
1223 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1224 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1225 r = sd_bus_reply_method_errorf(
1227 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1228 "Unknown property or interface.");
1230 r = sd_bus_reply_method_errorf(
1232 SD_BUS_ERROR_UNKNOWN_METHOD,
1233 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1241 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1242 struct node *n, *parent;
1249 assert(path[0] == '/');
1251 n = hashmap_get(bus->nodes, path);
1255 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1263 if (streq(path, "/"))
1266 e = strrchr(path, '/');
1269 p = strndupa(path, MAX(1, path - e));
1271 parent = bus_node_allocate(bus, p);
1278 n = new0(struct node, 1);
1285 r = hashmap_put(bus->nodes, s, n);
1293 LIST_PREPEND(siblings, parent->child, n);
1298 static void bus_node_gc(sd_bus *b, struct node *n) {
1311 assert(hashmap_remove(b->nodes, n->path) == n);
1314 LIST_REMOVE(siblings, n->parent->child, n);
1317 bus_node_gc(b, n->parent);
1321 static int bus_add_object(
1325 sd_bus_message_handler_t callback,
1328 struct node_callback *c;
1332 assert_return(bus, -EINVAL);
1333 assert_return(object_path_is_valid(path), -EINVAL);
1334 assert_return(callback, -EINVAL);
1335 assert_return(!bus_pid_changed(bus), -ECHILD);
1337 n = bus_node_allocate(bus, path);
1341 c = new0(struct node_callback, 1);
1348 c->callback = callback;
1349 c->userdata = userdata;
1350 c->is_fallback = fallback;
1352 LIST_PREPEND(callbacks, n->callbacks, c);
1353 bus->nodes_modified = true;
1359 bus_node_gc(bus, n);
1363 static int bus_remove_object(
1367 sd_bus_message_handler_t callback,
1370 struct node_callback *c;
1373 assert_return(bus, -EINVAL);
1374 assert_return(object_path_is_valid(path), -EINVAL);
1375 assert_return(callback, -EINVAL);
1376 assert_return(!bus_pid_changed(bus), -ECHILD);
1378 n = hashmap_get(bus->nodes, path);
1382 LIST_FOREACH(callbacks, c, n->callbacks)
1383 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1388 LIST_REMOVE(callbacks, n->callbacks, c);
1391 bus_node_gc(bus, n);
1392 bus->nodes_modified = true;
1397 _public_ int sd_bus_add_object(sd_bus *bus,
1399 sd_bus_message_handler_t callback,
1402 return bus_add_object(bus, false, path, callback, userdata);
1405 _public_ int sd_bus_remove_object(sd_bus *bus,
1407 sd_bus_message_handler_t callback,
1410 return bus_remove_object(bus, false, path, callback, userdata);
1413 _public_ int sd_bus_add_fallback(sd_bus *bus,
1415 sd_bus_message_handler_t callback,
1418 return bus_add_object(bus, true, prefix, callback, userdata);
1421 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1423 sd_bus_message_handler_t callback,
1426 return bus_remove_object(bus, true, prefix, callback, userdata);
1429 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1435 if (w->interface && w->node && w->vtable) {
1436 const sd_bus_vtable *v;
1438 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1439 struct vtable_member *x = NULL;
1443 case _SD_BUS_VTABLE_METHOD: {
1444 struct vtable_member key;
1446 key.path = w->node->path;
1447 key.interface = w->interface;
1448 key.member = v->x.method.member;
1450 x = hashmap_remove(bus->vtable_methods, &key);
1454 case _SD_BUS_VTABLE_PROPERTY:
1455 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1456 struct vtable_member key;
1458 key.path = w->node->path;
1459 key.interface = w->interface;
1460 key.member = v->x.property.member;
1461 x = hashmap_remove(bus->vtable_properties, &key);
1473 static unsigned vtable_member_hash_func(const void *a) {
1474 const struct vtable_member *m = a;
1479 string_hash_func(m->path) ^
1480 string_hash_func(m->interface) ^
1481 string_hash_func(m->member);
1484 static int vtable_member_compare_func(const void *a, const void *b) {
1485 const struct vtable_member *x = a, *y = b;
1491 r = strcmp(x->path, y->path);
1495 r = strcmp(x->interface, y->interface);
1499 return strcmp(x->member, y->member);
1502 static int add_object_vtable_internal(
1505 const char *interface,
1506 const sd_bus_vtable *vtable,
1508 sd_bus_object_find_t find,
1511 struct node_vtable *c = NULL, *i, *existing = NULL;
1512 const sd_bus_vtable *v;
1516 assert_return(bus, -EINVAL);
1517 assert_return(object_path_is_valid(path), -EINVAL);
1518 assert_return(interface_name_is_valid(interface), -EINVAL);
1519 assert_return(vtable, -EINVAL);
1520 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1521 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1522 assert_return(!bus_pid_changed(bus), -ECHILD);
1523 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1524 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1525 !streq(interface, "org.freedesktop.DBus.Peer") &&
1526 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1528 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1532 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1536 n = bus_node_allocate(bus, path);
1540 LIST_FOREACH(vtables, i, n->vtables) {
1541 if (i->is_fallback != fallback) {
1546 if (streq(i->interface, interface)) {
1548 if (i->vtable == vtable) {
1557 c = new0(struct node_vtable, 1);
1564 c->is_fallback = fallback;
1566 c->userdata = userdata;
1569 c->interface = strdup(interface);
1570 if (!c->interface) {
1575 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1579 case _SD_BUS_VTABLE_METHOD: {
1580 struct vtable_member *m;
1582 if (!member_name_is_valid(v->x.method.member) ||
1583 !signature_is_valid(strempty(v->x.method.signature), false) ||
1584 !signature_is_valid(strempty(v->x.method.result), false) ||
1585 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1586 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1591 m = new0(struct vtable_member, 1);
1599 m->interface = c->interface;
1600 m->member = v->x.method.member;
1603 r = hashmap_put(bus->vtable_methods, m, m);
1612 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1614 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1621 case _SD_BUS_VTABLE_PROPERTY: {
1622 struct vtable_member *m;
1624 if (!member_name_is_valid(v->x.property.member) ||
1625 !signature_is_single(v->x.property.signature, false) ||
1626 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1627 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1628 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))) {
1634 m = new0(struct vtable_member, 1);
1642 m->interface = c->interface;
1643 m->member = v->x.property.member;
1646 r = hashmap_put(bus->vtable_properties, m, m);
1655 case _SD_BUS_VTABLE_SIGNAL:
1657 if (!member_name_is_valid(v->x.signal.member) ||
1658 !signature_is_valid(strempty(v->x.signal.signature), false)) {
1671 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1672 bus->nodes_modified = true;
1678 free_node_vtable(bus, c);
1680 bus_node_gc(bus, n);
1684 static int remove_object_vtable_internal(
1687 const char *interface,
1688 const sd_bus_vtable *vtable,
1690 sd_bus_object_find_t find,
1693 struct node_vtable *c;
1696 assert_return(bus, -EINVAL);
1697 assert_return(object_path_is_valid(path), -EINVAL);
1698 assert_return(interface_name_is_valid(interface), -EINVAL);
1699 assert_return(!bus_pid_changed(bus), -ECHILD);
1701 n = hashmap_get(bus->nodes, path);
1705 LIST_FOREACH(vtables, c, n->vtables)
1706 if (streq(c->interface, interface) &&
1707 c->is_fallback == fallback &&
1708 c->vtable == vtable &&
1710 c->userdata == userdata)
1716 LIST_REMOVE(vtables, n->vtables, c);
1718 free_node_vtable(bus, c);
1719 bus_node_gc(bus, n);
1721 bus->nodes_modified = true;
1726 _public_ int sd_bus_add_object_vtable(
1729 const char *interface,
1730 const sd_bus_vtable *vtable,
1733 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1736 _public_ int sd_bus_remove_object_vtable(
1739 const char *interface,
1740 const sd_bus_vtable *vtable,
1743 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1746 _public_ int sd_bus_add_fallback_vtable(
1749 const char *interface,
1750 const sd_bus_vtable *vtable,
1751 sd_bus_object_find_t find,
1754 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1757 _public_ int sd_bus_remove_fallback_vtable(
1760 const char *interface,
1761 const sd_bus_vtable *vtable,
1762 sd_bus_object_find_t find,
1765 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1768 _public_ int sd_bus_add_node_enumerator(
1771 sd_bus_node_enumerator_t callback,
1774 struct node_enumerator *c;
1778 assert_return(bus, -EINVAL);
1779 assert_return(object_path_is_valid(path), -EINVAL);
1780 assert_return(callback, -EINVAL);
1781 assert_return(!bus_pid_changed(bus), -ECHILD);
1783 n = bus_node_allocate(bus, path);
1787 c = new0(struct node_enumerator, 1);
1794 c->callback = callback;
1795 c->userdata = userdata;
1797 LIST_PREPEND(enumerators, n->enumerators, c);
1799 bus->nodes_modified = true;
1805 bus_node_gc(bus, n);
1809 _public_ int sd_bus_remove_node_enumerator(
1812 sd_bus_node_enumerator_t callback,
1815 struct node_enumerator *c;
1818 assert_return(bus, -EINVAL);
1819 assert_return(object_path_is_valid(path), -EINVAL);
1820 assert_return(callback, -EINVAL);
1821 assert_return(!bus_pid_changed(bus), -ECHILD);
1823 n = hashmap_get(bus->nodes, path);
1827 LIST_FOREACH(enumerators, c, n->enumerators)
1828 if (c->callback == callback && c->userdata == userdata)
1834 LIST_REMOVE(enumerators, n->enumerators, c);
1837 bus_node_gc(bus, n);
1839 bus->nodes_modified = true;
1844 static int emit_properties_changed_on_interface(
1848 const char *interface,
1849 bool require_fallback,
1852 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1853 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1854 bool has_invalidating = false, has_changing = false;
1855 struct vtable_member key = {};
1856 struct node_vtable *c;
1867 n = hashmap_get(bus->nodes, prefix);
1871 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1875 r = sd_bus_message_append(m, "s", interface);
1879 r = sd_bus_message_open_container(m, 'a', "{sv}");
1884 key.interface = interface;
1886 LIST_FOREACH(vtables, c, n->vtables) {
1887 if (require_fallback && !c->is_fallback)
1890 if (!streq(c->interface, interface))
1893 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1896 if (bus->nodes_modified)
1901 STRV_FOREACH(property, names) {
1902 struct vtable_member *v;
1904 assert_return(member_name_is_valid(*property), -EINVAL);
1906 key.member = *property;
1907 v = hashmap_get(bus->vtable_properties, &key);
1911 /* If there are two vtables for the same
1912 * interface, let's handle this property when
1913 * we come to that vtable. */
1917 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
1919 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
1920 has_invalidating = true;
1924 has_changing = true;
1926 r = sd_bus_message_open_container(m, 'e', "sv");
1930 r = sd_bus_message_append(m, "s", *property);
1934 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
1938 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error);
1941 if (bus->nodes_modified)
1944 r = sd_bus_message_close_container(m);
1948 r = sd_bus_message_close_container(m);
1954 if (!has_invalidating && !has_changing)
1957 r = sd_bus_message_close_container(m);
1961 r = sd_bus_message_open_container(m, 'a', "s");
1965 if (has_invalidating) {
1966 LIST_FOREACH(vtables, c, n->vtables) {
1967 if (require_fallback && !c->is_fallback)
1970 if (!streq(c->interface, interface))
1973 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1976 if (bus->nodes_modified)
1981 STRV_FOREACH(property, names) {
1982 struct vtable_member *v;
1984 key.member = *property;
1985 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1986 assert(c == v->parent);
1988 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
1991 r = sd_bus_message_append(m, "s", *property);
1998 r = sd_bus_message_close_container(m);
2002 r = sd_bus_send(bus, m, NULL);
2009 _public_ int sd_bus_emit_properties_changed_strv(
2012 const char *interface,
2015 BUS_DONT_DESTROY(bus);
2019 assert_return(bus, -EINVAL);
2020 assert_return(object_path_is_valid(path), -EINVAL);
2021 assert_return(interface_name_is_valid(interface), -EINVAL);
2022 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2023 assert_return(!bus_pid_changed(bus), -ECHILD);
2025 if (strv_isempty(names))
2029 bus->nodes_modified = false;
2031 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2034 if (bus->nodes_modified)
2037 prefix = alloca(strlen(path) + 1);
2038 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2039 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2042 if (bus->nodes_modified)
2046 } while (bus->nodes_modified);
2051 _public_ int sd_bus_emit_properties_changed(
2054 const char *interface,
2055 const char *name, ...) {
2059 assert_return(bus, -EINVAL);
2060 assert_return(object_path_is_valid(path), -EINVAL);
2061 assert_return(interface_name_is_valid(interface), -EINVAL);
2062 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2063 assert_return(!bus_pid_changed(bus), -ECHILD);
2068 names = strv_from_stdarg_alloca(name);
2070 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2073 static int interfaces_added_append_one_prefix(
2078 const char *interface,
2079 bool require_fallback) {
2081 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2082 bool found_interface = false;
2083 struct node_vtable *c;
2094 n = hashmap_get(bus->nodes, prefix);
2098 LIST_FOREACH(vtables, c, n->vtables) {
2099 if (require_fallback && !c->is_fallback)
2102 if (!streq(c->interface, interface))
2105 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2108 if (bus->nodes_modified)
2113 if (!found_interface) {
2114 r = sd_bus_message_append_basic(m, 's', interface);
2118 r = sd_bus_message_open_container(m, 'a', "{sv}");
2122 found_interface = true;
2125 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2128 if (bus->nodes_modified)
2132 if (found_interface) {
2133 r = sd_bus_message_close_container(m);
2138 return found_interface;
2141 static int interfaces_added_append_one(
2145 const char *interface) {
2155 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2158 if (bus->nodes_modified)
2161 prefix = alloca(strlen(path) + 1);
2162 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2163 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2166 if (bus->nodes_modified)
2173 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2174 BUS_DONT_DESTROY(bus);
2176 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2180 assert_return(bus, -EINVAL);
2181 assert_return(object_path_is_valid(path), -EINVAL);
2182 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2183 assert_return(!bus_pid_changed(bus), -ECHILD);
2185 if (strv_isempty(interfaces))
2189 bus->nodes_modified = false;
2192 m = sd_bus_message_unref(m);
2194 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2198 r = sd_bus_message_append_basic(m, 'o', path);
2202 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2206 STRV_FOREACH(i, interfaces) {
2207 assert_return(interface_name_is_valid(*i), -EINVAL);
2209 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2213 r = interfaces_added_append_one(bus, m, path, *i);
2217 if (bus->nodes_modified)
2220 r = sd_bus_message_close_container(m);
2225 if (bus->nodes_modified)
2228 r = sd_bus_message_close_container(m);
2232 } while (bus->nodes_modified);
2234 return sd_bus_send(bus, m, NULL);
2237 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2240 assert_return(bus, -EINVAL);
2241 assert_return(object_path_is_valid(path), -EINVAL);
2242 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2243 assert_return(!bus_pid_changed(bus), -ECHILD);
2245 interfaces = strv_from_stdarg_alloca(interface);
2247 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2250 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2251 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2254 assert_return(bus, -EINVAL);
2255 assert_return(object_path_is_valid(path), -EINVAL);
2256 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2257 assert_return(!bus_pid_changed(bus), -ECHILD);
2259 if (strv_isempty(interfaces))
2262 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2266 r = sd_bus_message_append_basic(m, 'o', path);
2270 r = sd_bus_message_append_strv(m, interfaces);
2274 return sd_bus_send(bus, m, NULL);
2277 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2280 assert_return(bus, -EINVAL);
2281 assert_return(object_path_is_valid(path), -EINVAL);
2282 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2283 assert_return(!bus_pid_changed(bus), -ECHILD);
2285 interfaces = strv_from_stdarg_alloca(interface);
2287 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2290 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2293 assert_return(bus, -EINVAL);
2294 assert_return(object_path_is_valid(path), -EINVAL);
2295 assert_return(!bus_pid_changed(bus), -ECHILD);
2297 n = bus_node_allocate(bus, path);
2301 n->object_manager = true;
2302 bus->nodes_modified = true;
2306 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2309 assert_return(bus, -EINVAL);
2310 assert_return(object_path_is_valid(path), -EINVAL);
2311 assert_return(!bus_pid_changed(bus), -ECHILD);
2313 n = hashmap_get(bus->nodes, path);
2317 if (!n->object_manager)
2320 n->object_manager = false;
2321 bus->nodes_modified = true;
2322 bus_node_gc(bus, n);