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/>.
22 #include <sys/capability.h>
26 #include "bus-internal.h"
27 #include "bus-message.h"
29 #include "bus-signature.h"
30 #include "bus-introspect.h"
33 #include "bus-objects.h"
35 static int node_vtable_get_userdata(
38 struct node_vtable *c,
40 sd_bus_error *error) {
50 s = container_of(c, sd_bus_slot, node_vtable);
53 bus->current_slot = sd_bus_slot_ref(s);
54 bus->current_userdata = u;
55 r = c->find(bus, path, c->interface, u, &u, error);
56 bus->current_userdata = NULL;
57 bus->current_slot = sd_bus_slot_unref(s);
61 if (sd_bus_error_is_set(error))
62 return -sd_bus_error_get_errno(error);
73 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
76 return (uint8_t*) u + p->x.property.offset;
79 static int vtable_property_get_userdata(
82 struct vtable_member *p,
84 sd_bus_error *error) {
94 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
97 if (bus->nodes_modified)
100 *userdata = vtable_property_convert_userdata(p->vtable, u);
104 static int add_enumerated_to_set(
107 struct node_enumerator *first,
109 sd_bus_error *error) {
111 struct node_enumerator *c;
118 LIST_FOREACH(enumerators, c, first) {
119 char **children = NULL, **k;
122 if (bus->nodes_modified)
125 slot = container_of(c, sd_bus_slot, node_enumerator);
127 bus->current_slot = sd_bus_slot_ref(slot);
128 bus->current_userdata = slot->userdata;
129 r = c->callback(bus, prefix, slot->userdata, &children, error);
130 bus->current_userdata = NULL;
131 bus->current_slot = sd_bus_slot_unref(slot);
135 if (sd_bus_error_is_set(error))
136 return -sd_bus_error_get_errno(error);
138 STRV_FOREACH(k, children) {
144 if (!object_path_is_valid(*k)){
150 if (!object_path_startswith(*k, prefix)) {
155 r = set_consume(s, *k);
168 static int add_subtree_to_set(
173 sd_bus_error *error) {
183 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
186 if (bus->nodes_modified)
189 LIST_FOREACH(siblings, i, n->child) {
192 if (!object_path_startswith(i->path, prefix))
199 r = set_consume(s, t);
200 if (r < 0 && r != -EEXIST)
203 r = add_subtree_to_set(bus, prefix, i, s, error);
206 if (bus->nodes_modified)
213 static int get_child_nodes(
218 sd_bus_error *error) {
228 s = set_new(&string_hash_ops);
232 r = add_subtree_to_set(bus, prefix, n, s, error);
242 static int node_callbacks_run(
245 struct node_callback *first,
246 bool require_fallback,
247 bool *found_object) {
249 struct node_callback *c;
254 assert(found_object);
256 LIST_FOREACH(callbacks, c, first) {
257 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
260 if (bus->nodes_modified)
263 if (require_fallback && !c->is_fallback)
266 *found_object = true;
268 if (c->last_iteration == bus->iteration_counter)
271 c->last_iteration = bus->iteration_counter;
273 r = sd_bus_message_rewind(m, true);
277 slot = container_of(c, sd_bus_slot, node_callback);
279 bus->current_slot = sd_bus_slot_ref(slot);
280 bus->current_handler = c->callback;
281 bus->current_userdata = slot->userdata;
282 r = c->callback(bus, m, slot->userdata, &error_buffer);
283 bus->current_userdata = NULL;
284 bus->current_handler = NULL;
285 bus->current_slot = sd_bus_slot_unref(slot);
287 r = bus_maybe_reply_error(m, r, &error_buffer);
295 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
297 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
305 /* If the entire bus is trusted let's grant access */
309 /* If the member is marked UNPRIVILEGED let's grant access */
310 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
313 /* Check have the caller has the requested capability
314 * set. Note that the flags value contains the capability
315 * number plus one, which we need to subtract here. We do this
316 * so that we have 0 as special value for "default
318 cap = CAPABILITY_SHIFT(c->vtable->flags);
320 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
326 r = sd_bus_query_sender_privilege(m, cap);
332 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
335 static int method_callbacks_run(
338 struct vtable_member *c,
339 bool require_fallback,
340 bool *found_object) {
342 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
343 const char *signature;
350 assert(found_object);
352 if (require_fallback && !c->parent->is_fallback)
355 r = check_access(bus, m, c, &error);
357 return bus_maybe_reply_error(m, r, &error);
359 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
361 return bus_maybe_reply_error(m, r, &error);
362 if (bus->nodes_modified)
365 *found_object = true;
367 if (c->last_iteration == bus->iteration_counter)
370 c->last_iteration = bus->iteration_counter;
372 r = sd_bus_message_rewind(m, true);
376 signature = sd_bus_message_get_signature(m, true);
380 if (!streq(strempty(c->vtable->x.method.signature), signature))
381 return sd_bus_reply_method_errorf(
383 SD_BUS_ERROR_INVALID_ARGS,
384 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
385 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
387 /* Keep track what the signature of the reply to this message
388 * should be, so that this can be enforced when sealing the
390 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
392 if (c->vtable->x.method.handler) {
395 slot = container_of(c->parent, sd_bus_slot, node_vtable);
397 bus->current_slot = sd_bus_slot_ref(slot);
398 bus->current_handler = c->vtable->x.method.handler;
399 bus->current_userdata = u;
400 r = c->vtable->x.method.handler(bus, m, u, &error);
401 bus->current_userdata = NULL;
402 bus->current_handler = NULL;
403 bus->current_slot = sd_bus_slot_unref(slot);
405 return bus_maybe_reply_error(m, r, &error);
408 /* If the method callback is NULL, make this a successful NOP */
409 r = sd_bus_reply_method_return(m, NULL);
416 static int invoke_property_get(
419 const sd_bus_vtable *v,
421 const char *interface,
422 const char *property,
423 sd_bus_message *reply,
425 sd_bus_error *error) {
438 if (v->x.property.get) {
440 bus->current_slot = sd_bus_slot_ref(slot);
441 bus->current_userdata = userdata;
442 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
443 bus->current_userdata = NULL;
444 bus->current_slot = sd_bus_slot_unref(slot);
448 if (sd_bus_error_is_set(error))
449 return -sd_bus_error_get_errno(error);
453 /* Automatic handling if no callback is defined. */
455 if (streq(v->x.property.signature, "as"))
456 return sd_bus_message_append_strv(reply, *(char***) userdata);
458 assert(signature_is_single(v->x.property.signature, false));
459 assert(bus_type_is_basic(v->x.property.signature[0]));
461 switch (v->x.property.signature[0]) {
463 case SD_BUS_TYPE_STRING:
464 case SD_BUS_TYPE_SIGNATURE:
465 p = strempty(*(char**) userdata);
468 case SD_BUS_TYPE_OBJECT_PATH:
469 p = *(char**) userdata;
478 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
481 static int invoke_property_set(
484 const sd_bus_vtable *v,
486 const char *interface,
487 const char *property,
488 sd_bus_message *value,
490 sd_bus_error *error) {
502 if (v->x.property.set) {
504 bus->current_slot = sd_bus_slot_ref(slot);
505 bus->current_userdata = userdata;
506 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
507 bus->current_userdata = NULL;
508 bus->current_slot = sd_bus_slot_unref(slot);
512 if (sd_bus_error_is_set(error))
513 return -sd_bus_error_get_errno(error);
517 /* Automatic handling if no callback is defined. */
519 assert(signature_is_single(v->x.property.signature, false));
520 assert(bus_type_is_basic(v->x.property.signature[0]));
522 switch (v->x.property.signature[0]) {
524 case SD_BUS_TYPE_STRING:
525 case SD_BUS_TYPE_OBJECT_PATH:
526 case SD_BUS_TYPE_SIGNATURE: {
530 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
538 free(*(char**) userdata);
539 *(char**) userdata = n;
545 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
555 static int property_get_set_callbacks_run(
558 struct vtable_member *c,
559 bool require_fallback,
561 bool *found_object) {
563 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
564 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
572 assert(found_object);
574 if (require_fallback && !c->parent->is_fallback)
577 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
579 return bus_maybe_reply_error(m, r, &error);
580 if (bus->nodes_modified)
583 slot = container_of(c->parent, sd_bus_slot, node_vtable);
585 *found_object = true;
587 r = sd_bus_message_new_method_return(m, &reply);
592 /* Note that we do not protect against reexecution
593 * here (using the last_iteration check, see below),
594 * should the node tree have changed and we got called
595 * again. We assume that property Get() calls are
596 * ultimately without side-effects or if they aren't
597 * then at least idempotent. */
599 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
603 /* Note that we do not do an access check here. Read
604 * access to properties is always unrestricted, since
605 * PropertiesChanged signals broadcast contents
608 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
610 return bus_maybe_reply_error(m, r, &error);
612 if (bus->nodes_modified)
615 r = sd_bus_message_close_container(reply);
620 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
621 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
623 /* Avoid that we call the set routine more than once
624 * if the processing of this message got restarted
625 * because the node tree changed. */
626 if (c->last_iteration == bus->iteration_counter)
629 c->last_iteration = bus->iteration_counter;
631 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
635 r = check_access(bus, m, c, &error);
637 return bus_maybe_reply_error(m, r, &error);
639 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
641 return bus_maybe_reply_error(m, r, &error);
643 if (bus->nodes_modified)
646 r = sd_bus_message_exit_container(m);
651 r = sd_bus_send(bus, reply, NULL);
658 static int vtable_append_one_property(
660 sd_bus_message *reply,
662 struct node_vtable *c,
663 const sd_bus_vtable *v,
665 sd_bus_error *error) {
676 r = sd_bus_message_open_container(reply, 'e', "sv");
680 r = sd_bus_message_append(reply, "s", v->x.property.member);
684 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
688 slot = container_of(c, sd_bus_slot, node_vtable);
690 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
693 if (bus->nodes_modified)
696 r = sd_bus_message_close_container(reply);
700 r = sd_bus_message_close_container(reply);
707 static int vtable_append_all_properties(
709 sd_bus_message *reply,
711 struct node_vtable *c,
713 sd_bus_error *error) {
715 const sd_bus_vtable *v;
723 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
726 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
727 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
730 if (v->flags & SD_BUS_VTABLE_HIDDEN)
733 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
736 if (bus->nodes_modified)
743 static int property_get_all_callbacks_run(
746 struct node_vtable *first,
747 bool require_fallback,
749 bool *found_object) {
751 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
752 struct node_vtable *c;
753 bool found_interface;
758 assert(found_object);
760 r = sd_bus_message_new_method_return(m, &reply);
764 r = sd_bus_message_open_container(reply, 'a', "{sv}");
768 found_interface = !iface ||
769 streq(iface, "org.freedesktop.DBus.Properties") ||
770 streq(iface, "org.freedesktop.DBus.Peer") ||
771 streq(iface, "org.freedesktop.DBus.Introspectable");
773 LIST_FOREACH(vtables, c, first) {
774 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
777 if (require_fallback && !c->is_fallback)
780 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
782 return bus_maybe_reply_error(m, r, &error);
783 if (bus->nodes_modified)
788 *found_object = true;
790 if (iface && !streq(c->interface, iface))
792 found_interface = true;
794 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
796 return bus_maybe_reply_error(m, r, &error);
797 if (bus->nodes_modified)
801 if (!found_interface) {
802 r = sd_bus_reply_method_errorf(
804 SD_BUS_ERROR_UNKNOWN_INTERFACE,
805 "Unknown interface '%s'.", iface);
812 r = sd_bus_message_close_container(reply);
816 r = sd_bus_send(bus, reply, NULL);
823 static bool bus_node_exists(
827 bool require_fallback) {
829 struct node_vtable *c;
830 struct node_callback *k;
836 /* Tests if there's anything attached directly to this node
837 * for the specified path */
839 LIST_FOREACH(callbacks, k, n->callbacks) {
840 if (require_fallback && !k->is_fallback)
846 LIST_FOREACH(vtables, c, n->vtables) {
847 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
849 if (require_fallback && !c->is_fallback)
852 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
854 if (bus->nodes_modified)
858 return !require_fallback && (n->enumerators || n->object_managers);
861 static int process_introspect(
865 bool require_fallback,
866 bool *found_object) {
868 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
869 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
870 _cleanup_set_free_free_ Set *s = NULL;
871 const char *previous_interface = NULL;
872 struct introspect intro;
873 struct node_vtable *c;
880 assert(found_object);
882 r = get_child_nodes(bus, m->path, n, &s, &error);
884 return bus_maybe_reply_error(m, r, &error);
885 if (bus->nodes_modified)
888 r = introspect_begin(&intro, bus->trusted);
892 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
896 empty = set_isempty(s);
898 LIST_FOREACH(vtables, c, n->vtables) {
899 if (require_fallback && !c->is_fallback)
902 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
904 r = bus_maybe_reply_error(m, r, &error);
907 if (bus->nodes_modified) {
916 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
919 if (!streq_ptr(previous_interface, c->interface)) {
921 if (previous_interface)
922 fputs(" </interface>\n", intro.f);
924 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
927 r = introspect_write_interface(&intro, c->vtable);
931 previous_interface = c->interface;
934 if (previous_interface)
935 fputs(" </interface>\n", intro.f);
938 /* Nothing?, let's see if we exist at all, and if not
939 * refuse to do anything */
940 r = bus_node_exists(bus, n, m->path, require_fallback);
943 if (bus->nodes_modified)
949 *found_object = true;
951 r = introspect_write_child_nodes(&intro, s, m->path);
955 r = introspect_finish(&intro, bus, m, &reply);
959 r = sd_bus_send(bus, reply, NULL);
966 introspect_free(&intro);
970 static int object_manager_serialize_path(
972 sd_bus_message *reply,
975 bool require_fallback,
976 sd_bus_error *error) {
978 const char *previous_interface = NULL;
979 bool found_something = false;
980 struct node_vtable *i;
990 n = hashmap_get(bus->nodes, prefix);
994 LIST_FOREACH(vtables, i, n->vtables) {
997 if (require_fallback && !i->is_fallback)
1000 r = node_vtable_get_userdata(bus, path, i, &u, error);
1003 if (bus->nodes_modified)
1008 if (!found_something) {
1010 /* Open the object part */
1012 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1016 r = sd_bus_message_append(reply, "o", path);
1020 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1024 found_something = true;
1027 if (!streq_ptr(previous_interface, i->interface)) {
1029 /* Maybe close the previous interface part */
1031 if (previous_interface) {
1032 r = sd_bus_message_close_container(reply);
1036 r = sd_bus_message_close_container(reply);
1041 /* Open the new interface part */
1043 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1047 r = sd_bus_message_append(reply, "s", i->interface);
1051 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1056 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1059 if (bus->nodes_modified)
1062 previous_interface = i->interface;
1065 if (previous_interface) {
1066 r = sd_bus_message_close_container(reply);
1070 r = sd_bus_message_close_container(reply);
1075 if (found_something) {
1076 r = sd_bus_message_close_container(reply);
1080 r = sd_bus_message_close_container(reply);
1088 static int object_manager_serialize_path_and_fallbacks(
1090 sd_bus_message *reply,
1092 sd_bus_error *error) {
1102 /* First, add all vtables registered for this path */
1103 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1106 if (bus->nodes_modified)
1109 /* Second, add fallback vtables registered for any of the prefixes */
1110 prefix = alloca(strlen(path) + 1);
1111 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1112 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1115 if (bus->nodes_modified)
1122 static int process_get_managed_objects(
1126 bool require_fallback,
1127 bool *found_object) {
1129 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1130 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1131 _cleanup_set_free_free_ Set *s = NULL;
1139 assert(found_object);
1141 /* Spec says, GetManagedObjects() is only implemented on the root of a
1142 * sub-tree. Therefore, we require a registered object-manager on
1143 * exactly the queried path, otherwise, we refuse to respond. */
1145 if (require_fallback || !n->object_managers)
1148 r = get_child_nodes(bus, m->path, n, &s, &error);
1151 if (bus->nodes_modified)
1154 r = sd_bus_message_new_method_return(m, &reply);
1158 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1162 SET_FOREACH(path, s, i) {
1163 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1167 if (bus->nodes_modified)
1171 r = sd_bus_message_close_container(reply);
1175 r = sd_bus_send(bus, reply, NULL);
1182 static int object_find_and_run(
1186 bool require_fallback,
1187 bool *found_object) {
1190 struct vtable_member vtable_key, *v;
1196 assert(found_object);
1198 n = hashmap_get(bus->nodes, p);
1202 /* First, try object callbacks */
1203 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1206 if (bus->nodes_modified)
1209 if (!m->interface || !m->member)
1212 /* Then, look for a known method */
1213 vtable_key.path = (char*) p;
1214 vtable_key.interface = m->interface;
1215 vtable_key.member = m->member;
1217 v = hashmap_get(bus->vtable_methods, &vtable_key);
1219 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1222 if (bus->nodes_modified)
1226 /* Then, look for a known property */
1227 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1230 get = streq(m->member, "Get");
1232 if (get || streq(m->member, "Set")) {
1234 r = sd_bus_message_rewind(m, true);
1238 vtable_key.path = (char*) p;
1240 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1242 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1244 v = hashmap_get(bus->vtable_properties, &vtable_key);
1246 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1251 } else if (streq(m->member, "GetAll")) {
1254 r = sd_bus_message_rewind(m, true);
1258 r = sd_bus_message_read(m, "s", &iface);
1260 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1265 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1270 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1272 if (!isempty(sd_bus_message_get_signature(m, true)))
1273 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1275 r = process_introspect(bus, m, n, require_fallback, found_object);
1279 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1281 if (!isempty(sd_bus_message_get_signature(m, true)))
1282 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1284 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1289 if (bus->nodes_modified)
1292 if (!*found_object) {
1293 r = bus_node_exists(bus, n, m->path, require_fallback);
1297 *found_object = true;
1303 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1306 bool found_object = false;
1311 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1314 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1317 if (hashmap_isempty(bus->nodes))
1320 /* Never respond to broadcast messages */
1321 if (bus->bus_client && !m->destination)
1327 pl = strlen(m->path);
1331 bus->nodes_modified = false;
1333 r = object_find_and_run(bus, m, m->path, false, &found_object);
1337 /* Look for fallback prefixes */
1338 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1340 if (bus->nodes_modified)
1343 r = object_find_and_run(bus, m, prefix, true, &found_object);
1348 } while (bus->nodes_modified);
1353 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1354 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1355 r = sd_bus_reply_method_errorf(
1357 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1358 "Unknown property or interface.");
1360 r = sd_bus_reply_method_errorf(
1362 SD_BUS_ERROR_UNKNOWN_METHOD,
1363 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1371 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1372 struct node *n, *parent;
1374 _cleanup_free_ char *s = NULL;
1380 assert(path[0] == '/');
1382 n = hashmap_get(bus->nodes, path);
1386 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1394 if (streq(path, "/"))
1397 e = strrchr(path, '/');
1400 p = strndupa(path, MAX(1, path - e));
1402 parent = bus_node_allocate(bus, p);
1407 n = new0(struct node, 1);
1413 s = NULL; /* do not free */
1415 r = hashmap_put(bus->nodes, n->path, n);
1423 LIST_PREPEND(siblings, parent->child, n);
1428 void bus_node_gc(sd_bus *b, struct node *n) {
1441 assert(hashmap_remove(b->nodes, n->path) == n);
1444 LIST_REMOVE(siblings, n->parent->child, n);
1447 bus_node_gc(b, n->parent);
1451 static int bus_add_object(
1456 sd_bus_message_handler_t callback,
1463 assert_return(bus, -EINVAL);
1464 assert_return(object_path_is_valid(path), -EINVAL);
1465 assert_return(callback, -EINVAL);
1466 assert_return(!bus_pid_changed(bus), -ECHILD);
1468 n = bus_node_allocate(bus, path);
1472 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1478 s->node_callback.callback = callback;
1479 s->node_callback.is_fallback = fallback;
1481 s->node_callback.node = n;
1482 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1483 bus->nodes_modified = true;
1491 sd_bus_slot_unref(s);
1492 bus_node_gc(bus, n);
1497 _public_ int sd_bus_add_object(
1501 sd_bus_message_handler_t callback,
1504 return bus_add_object(bus, slot, false, path, callback, userdata);
1507 _public_ int sd_bus_add_fallback(
1511 sd_bus_message_handler_t callback,
1514 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1517 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1518 const struct vtable_member *m = a;
1519 uint8_t hash_key2[HASH_KEY_SIZE];
1524 ret = string_hash_func(m->path, hash_key);
1526 /* Use a slightly different hash key for the interface */
1527 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1529 ret ^= string_hash_func(m->interface, hash_key2);
1531 /* And an even different one for the member */
1533 ret ^= string_hash_func(m->member, hash_key2);
1538 static int vtable_member_compare_func(const void *a, const void *b) {
1539 const struct vtable_member *x = a, *y = b;
1545 r = strcmp(x->path, y->path);
1549 r = strcmp(x->interface, y->interface);
1553 return strcmp(x->member, y->member);
1556 static const struct hash_ops vtable_member_hash_ops = {
1557 .hash = vtable_member_hash_func,
1558 .compare = vtable_member_compare_func
1561 static int add_object_vtable_internal(
1565 const char *interface,
1566 const sd_bus_vtable *vtable,
1568 sd_bus_object_find_t find,
1571 sd_bus_slot *s = NULL;
1572 struct node_vtable *i, *existing = NULL;
1573 const sd_bus_vtable *v;
1577 assert_return(bus, -EINVAL);
1578 assert_return(object_path_is_valid(path), -EINVAL);
1579 assert_return(interface_name_is_valid(interface), -EINVAL);
1580 assert_return(vtable, -EINVAL);
1581 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1582 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1583 assert_return(!bus_pid_changed(bus), -ECHILD);
1584 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1585 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1586 !streq(interface, "org.freedesktop.DBus.Peer") &&
1587 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1589 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1593 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1597 n = bus_node_allocate(bus, path);
1601 LIST_FOREACH(vtables, i, n->vtables) {
1602 if (i->is_fallback != fallback) {
1607 if (streq(i->interface, interface)) {
1609 if (i->vtable == vtable) {
1618 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1624 s->node_vtable.is_fallback = fallback;
1625 s->node_vtable.vtable = vtable;
1626 s->node_vtable.find = find;
1628 s->node_vtable.interface = strdup(interface);
1629 if (!s->node_vtable.interface) {
1634 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1638 case _SD_BUS_VTABLE_METHOD: {
1639 struct vtable_member *m;
1641 if (!member_name_is_valid(v->x.method.member) ||
1642 !signature_is_valid(strempty(v->x.method.signature), false) ||
1643 !signature_is_valid(strempty(v->x.method.result), false) ||
1644 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1645 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1650 m = new0(struct vtable_member, 1);
1656 m->parent = &s->node_vtable;
1658 m->interface = s->node_vtable.interface;
1659 m->member = v->x.method.member;
1662 r = hashmap_put(bus->vtable_methods, m, m);
1671 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1673 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1680 case _SD_BUS_VTABLE_PROPERTY: {
1681 struct vtable_member *m;
1683 if (!member_name_is_valid(v->x.property.member) ||
1684 !signature_is_single(v->x.property.signature, false) ||
1685 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1686 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1687 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1688 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1693 m = new0(struct vtable_member, 1);
1699 m->parent = &s->node_vtable;
1701 m->interface = s->node_vtable.interface;
1702 m->member = v->x.property.member;
1705 r = hashmap_put(bus->vtable_properties, m, m);
1714 case _SD_BUS_VTABLE_SIGNAL:
1716 if (!member_name_is_valid(v->x.signal.member) ||
1717 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1718 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1731 s->node_vtable.node = n;
1732 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1733 bus->nodes_modified = true;
1741 sd_bus_slot_unref(s);
1742 bus_node_gc(bus, n);
1747 _public_ int sd_bus_add_object_vtable(
1751 const char *interface,
1752 const sd_bus_vtable *vtable,
1755 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1758 _public_ int sd_bus_add_fallback_vtable(
1762 const char *interface,
1763 const sd_bus_vtable *vtable,
1764 sd_bus_object_find_t find,
1767 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1770 _public_ int sd_bus_add_node_enumerator(
1774 sd_bus_node_enumerator_t callback,
1781 assert_return(bus, -EINVAL);
1782 assert_return(object_path_is_valid(path), -EINVAL);
1783 assert_return(callback, -EINVAL);
1784 assert_return(!bus_pid_changed(bus), -ECHILD);
1786 n = bus_node_allocate(bus, path);
1790 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1796 s->node_enumerator.callback = callback;
1798 s->node_enumerator.node = n;
1799 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1800 bus->nodes_modified = true;
1808 sd_bus_slot_unref(s);
1809 bus_node_gc(bus, n);
1814 static int emit_properties_changed_on_interface(
1818 const char *interface,
1819 bool require_fallback,
1820 bool *found_interface,
1823 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1824 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1825 bool has_invalidating = false, has_changing = false;
1826 struct vtable_member key = {};
1827 struct node_vtable *c;
1837 assert(found_interface);
1839 n = hashmap_get(bus->nodes, prefix);
1843 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1847 r = sd_bus_message_append(m, "s", interface);
1851 r = sd_bus_message_open_container(m, 'a', "{sv}");
1856 key.interface = interface;
1858 LIST_FOREACH(vtables, c, n->vtables) {
1859 if (require_fallback && !c->is_fallback)
1862 if (!streq(c->interface, interface))
1865 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1868 if (bus->nodes_modified)
1873 *found_interface = true;
1876 /* If the caller specified a list of
1877 * properties we include exactly those in the
1878 * PropertiesChanged message */
1880 STRV_FOREACH(property, names) {
1881 struct vtable_member *v;
1883 assert_return(member_name_is_valid(*property), -EINVAL);
1885 key.member = *property;
1886 v = hashmap_get(bus->vtable_properties, &key);
1890 /* If there are two vtables for the same
1891 * interface, let's handle this property when
1892 * we come to that vtable. */
1896 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1897 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1899 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1901 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1902 has_invalidating = true;
1906 has_changing = true;
1908 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1911 if (bus->nodes_modified)
1915 const sd_bus_vtable *v;
1917 /* If the caller specified no properties list
1918 * we include all properties that are marked
1919 * as changing in the message. */
1921 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1922 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1925 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1928 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1929 has_invalidating = true;
1933 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1936 has_changing = true;
1938 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1941 if (bus->nodes_modified)
1947 if (!has_invalidating && !has_changing)
1950 r = sd_bus_message_close_container(m);
1954 r = sd_bus_message_open_container(m, 'a', "s");
1958 if (has_invalidating) {
1959 LIST_FOREACH(vtables, c, n->vtables) {
1960 if (require_fallback && !c->is_fallback)
1963 if (!streq(c->interface, interface))
1966 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1969 if (bus->nodes_modified)
1975 STRV_FOREACH(property, names) {
1976 struct vtable_member *v;
1978 key.member = *property;
1979 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1980 assert(c == v->parent);
1982 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
1985 r = sd_bus_message_append(m, "s", *property);
1990 const sd_bus_vtable *v;
1992 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1993 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1996 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1999 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2002 r = sd_bus_message_append(m, "s", v->x.property.member);
2010 r = sd_bus_message_close_container(m);
2014 r = sd_bus_send(bus, m, NULL);
2021 _public_ int sd_bus_emit_properties_changed_strv(
2024 const char *interface,
2027 BUS_DONT_DESTROY(bus);
2028 bool found_interface = false;
2032 assert_return(bus, -EINVAL);
2033 assert_return(object_path_is_valid(path), -EINVAL);
2034 assert_return(interface_name_is_valid(interface), -EINVAL);
2035 assert_return(!bus_pid_changed(bus), -ECHILD);
2037 if (!BUS_IS_OPEN(bus->state))
2040 /* A non-NULL but empty names list means nothing needs to be
2041 generated. A NULL list OTOH indicates that all properties
2042 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2043 included in the PropertiesChanged message. */
2044 if (names && names[0] == NULL)
2048 bus->nodes_modified = false;
2050 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2053 if (bus->nodes_modified)
2056 prefix = alloca(strlen(path) + 1);
2057 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2058 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2061 if (bus->nodes_modified)
2065 } while (bus->nodes_modified);
2067 return found_interface ? 0 : -ENOENT;
2070 _public_ int sd_bus_emit_properties_changed(
2073 const char *interface,
2074 const char *name, ...) {
2078 assert_return(bus, -EINVAL);
2079 assert_return(object_path_is_valid(path), -EINVAL);
2080 assert_return(interface_name_is_valid(interface), -EINVAL);
2081 assert_return(!bus_pid_changed(bus), -ECHILD);
2083 if (!BUS_IS_OPEN(bus->state))
2089 names = strv_from_stdarg_alloca(name);
2091 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2094 static int interfaces_added_append_one_prefix(
2099 const char *interface,
2100 bool require_fallback) {
2102 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2103 bool found_interface = false;
2104 struct node_vtable *c;
2115 n = hashmap_get(bus->nodes, prefix);
2119 LIST_FOREACH(vtables, c, n->vtables) {
2120 if (require_fallback && !c->is_fallback)
2123 if (!streq(c->interface, interface))
2126 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2129 if (bus->nodes_modified)
2134 if (!found_interface) {
2135 r = sd_bus_message_append_basic(m, 's', interface);
2139 r = sd_bus_message_open_container(m, 'a', "{sv}");
2143 found_interface = true;
2146 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2149 if (bus->nodes_modified)
2153 if (found_interface) {
2154 r = sd_bus_message_close_container(m);
2159 return found_interface;
2162 static int interfaces_added_append_one(
2166 const char *interface) {
2176 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2179 if (bus->nodes_modified)
2182 prefix = alloca(strlen(path) + 1);
2183 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2184 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2187 if (bus->nodes_modified)
2194 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2195 BUS_DONT_DESTROY(bus);
2197 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2201 assert_return(bus, -EINVAL);
2202 assert_return(object_path_is_valid(path), -EINVAL);
2203 assert_return(!bus_pid_changed(bus), -ECHILD);
2205 if (!BUS_IS_OPEN(bus->state))
2208 if (strv_isempty(interfaces))
2212 bus->nodes_modified = false;
2213 m = sd_bus_message_unref(m);
2215 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2219 r = sd_bus_message_append_basic(m, 'o', path);
2223 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2227 STRV_FOREACH(i, interfaces) {
2228 assert_return(interface_name_is_valid(*i), -EINVAL);
2230 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2234 r = interfaces_added_append_one(bus, m, path, *i);
2238 if (bus->nodes_modified)
2241 r = sd_bus_message_close_container(m);
2246 if (bus->nodes_modified)
2249 r = sd_bus_message_close_container(m);
2253 } while (bus->nodes_modified);
2255 return sd_bus_send(bus, m, NULL);
2258 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2261 assert_return(bus, -EINVAL);
2262 assert_return(object_path_is_valid(path), -EINVAL);
2263 assert_return(!bus_pid_changed(bus), -ECHILD);
2265 if (!BUS_IS_OPEN(bus->state))
2268 interfaces = strv_from_stdarg_alloca(interface);
2270 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2273 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2274 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2277 assert_return(bus, -EINVAL);
2278 assert_return(object_path_is_valid(path), -EINVAL);
2279 assert_return(!bus_pid_changed(bus), -ECHILD);
2281 if (!BUS_IS_OPEN(bus->state))
2284 if (strv_isempty(interfaces))
2287 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2291 r = sd_bus_message_append_basic(m, 'o', path);
2295 r = sd_bus_message_append_strv(m, interfaces);
2299 return sd_bus_send(bus, m, NULL);
2302 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2305 assert_return(bus, -EINVAL);
2306 assert_return(object_path_is_valid(path), -EINVAL);
2307 assert_return(!bus_pid_changed(bus), -ECHILD);
2309 if (!BUS_IS_OPEN(bus->state))
2312 interfaces = strv_from_stdarg_alloca(interface);
2314 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2317 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2322 assert_return(bus, -EINVAL);
2323 assert_return(object_path_is_valid(path), -EINVAL);
2324 assert_return(!bus_pid_changed(bus), -ECHILD);
2326 n = bus_node_allocate(bus, path);
2330 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2336 s->node_object_manager.node = n;
2337 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2338 bus->nodes_modified = true;
2346 sd_bus_slot_unref(s);
2347 bus_node_gc(bus, n);