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 int bus_node_exists(
827 bool require_fallback) {
829 struct node_vtable *c;
830 struct node_callback *k;
837 /* Tests if there's anything attached directly to this node
838 * for the specified path */
840 if (!require_fallback && (n->enumerators || n->object_managers))
843 LIST_FOREACH(callbacks, k, n->callbacks) {
844 if (require_fallback && !k->is_fallback)
850 LIST_FOREACH(vtables, c, n->vtables) {
851 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
853 if (require_fallback && !c->is_fallback)
856 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
859 if (bus->nodes_modified)
866 static int process_introspect(
870 bool require_fallback,
871 bool *found_object) {
873 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
874 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
875 _cleanup_set_free_free_ Set *s = NULL;
876 const char *previous_interface = NULL;
877 struct introspect intro;
878 struct node_vtable *c;
885 assert(found_object);
887 r = get_child_nodes(bus, m->path, n, &s, &error);
889 return bus_maybe_reply_error(m, r, &error);
890 if (bus->nodes_modified)
893 r = introspect_begin(&intro, bus->trusted);
897 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
901 empty = set_isempty(s);
903 LIST_FOREACH(vtables, c, n->vtables) {
904 if (require_fallback && !c->is_fallback)
907 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
909 r = bus_maybe_reply_error(m, r, &error);
912 if (bus->nodes_modified) {
921 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
924 if (!streq_ptr(previous_interface, c->interface)) {
926 if (previous_interface)
927 fputs(" </interface>\n", intro.f);
929 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
932 r = introspect_write_interface(&intro, c->vtable);
936 previous_interface = c->interface;
939 if (previous_interface)
940 fputs(" </interface>\n", intro.f);
943 /* Nothing?, let's see if we exist at all, and if not
944 * refuse to do anything */
945 r = bus_node_exists(bus, n, m->path, require_fallback);
948 if (bus->nodes_modified) {
954 *found_object = true;
956 r = introspect_write_child_nodes(&intro, s, m->path);
960 r = introspect_finish(&intro, bus, m, &reply);
964 r = sd_bus_send(bus, reply, NULL);
971 introspect_free(&intro);
975 static int object_manager_serialize_path(
977 sd_bus_message *reply,
980 bool require_fallback,
981 sd_bus_error *error) {
983 const char *previous_interface = NULL;
984 bool found_something = false;
985 struct node_vtable *i;
995 n = hashmap_get(bus->nodes, prefix);
999 LIST_FOREACH(vtables, i, n->vtables) {
1002 if (require_fallback && !i->is_fallback)
1005 r = node_vtable_get_userdata(bus, path, i, &u, error);
1008 if (bus->nodes_modified)
1013 if (!found_something) {
1015 /* Open the object part */
1017 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1021 r = sd_bus_message_append(reply, "o", path);
1025 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1029 found_something = true;
1032 if (!streq_ptr(previous_interface, i->interface)) {
1034 /* Maybe close the previous interface part */
1036 if (previous_interface) {
1037 r = sd_bus_message_close_container(reply);
1041 r = sd_bus_message_close_container(reply);
1046 /* Open the new interface part */
1048 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1052 r = sd_bus_message_append(reply, "s", i->interface);
1056 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1061 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1064 if (bus->nodes_modified)
1067 previous_interface = i->interface;
1070 if (previous_interface) {
1071 r = sd_bus_message_close_container(reply);
1075 r = sd_bus_message_close_container(reply);
1080 if (found_something) {
1081 r = sd_bus_message_close_container(reply);
1085 r = sd_bus_message_close_container(reply);
1093 static int object_manager_serialize_path_and_fallbacks(
1095 sd_bus_message *reply,
1097 sd_bus_error *error) {
1107 /* First, add all vtables registered for this path */
1108 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1111 if (bus->nodes_modified)
1114 /* Second, add fallback vtables registered for any of the prefixes */
1115 prefix = alloca(strlen(path) + 1);
1116 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1117 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1120 if (bus->nodes_modified)
1127 static int process_get_managed_objects(
1131 bool require_fallback,
1132 bool *found_object) {
1134 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1135 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1136 _cleanup_set_free_free_ Set *s = NULL;
1144 assert(found_object);
1146 /* Spec says, GetManagedObjects() is only implemented on the root of a
1147 * sub-tree. Therefore, we require a registered object-manager on
1148 * exactly the queried path, otherwise, we refuse to respond. */
1150 if (require_fallback || !n->object_managers)
1153 r = get_child_nodes(bus, m->path, n, &s, &error);
1156 if (bus->nodes_modified)
1159 r = sd_bus_message_new_method_return(m, &reply);
1163 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1167 SET_FOREACH(path, s, i) {
1168 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1172 if (bus->nodes_modified)
1176 r = sd_bus_message_close_container(reply);
1180 r = sd_bus_send(bus, reply, NULL);
1187 static int object_find_and_run(
1191 bool require_fallback,
1192 bool *found_object) {
1195 struct vtable_member vtable_key, *v;
1201 assert(found_object);
1203 n = hashmap_get(bus->nodes, p);
1207 /* First, try object callbacks */
1208 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1211 if (bus->nodes_modified)
1214 if (!m->interface || !m->member)
1217 /* Then, look for a known method */
1218 vtable_key.path = (char*) p;
1219 vtable_key.interface = m->interface;
1220 vtable_key.member = m->member;
1222 v = hashmap_get(bus->vtable_methods, &vtable_key);
1224 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1227 if (bus->nodes_modified)
1231 /* Then, look for a known property */
1232 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1235 get = streq(m->member, "Get");
1237 if (get || streq(m->member, "Set")) {
1239 r = sd_bus_message_rewind(m, true);
1243 vtable_key.path = (char*) p;
1245 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1247 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1249 v = hashmap_get(bus->vtable_properties, &vtable_key);
1251 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1256 } else if (streq(m->member, "GetAll")) {
1259 r = sd_bus_message_rewind(m, true);
1263 r = sd_bus_message_read(m, "s", &iface);
1265 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1270 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1275 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1277 if (!isempty(sd_bus_message_get_signature(m, true)))
1278 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1280 r = process_introspect(bus, m, n, require_fallback, found_object);
1284 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1286 if (!isempty(sd_bus_message_get_signature(m, true)))
1287 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1289 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1294 if (bus->nodes_modified)
1297 if (!*found_object) {
1298 r = bus_node_exists(bus, n, m->path, require_fallback);
1301 if (bus->nodes_modified)
1304 *found_object = true;
1310 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1313 bool found_object = false;
1318 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1321 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1324 if (hashmap_isempty(bus->nodes))
1327 /* Never respond to broadcast messages */
1328 if (bus->bus_client && !m->destination)
1334 pl = strlen(m->path);
1338 bus->nodes_modified = false;
1340 r = object_find_and_run(bus, m, m->path, false, &found_object);
1344 /* Look for fallback prefixes */
1345 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1347 if (bus->nodes_modified)
1350 r = object_find_and_run(bus, m, prefix, true, &found_object);
1355 } while (bus->nodes_modified);
1360 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1361 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1362 r = sd_bus_reply_method_errorf(
1364 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1365 "Unknown property or interface.");
1367 r = sd_bus_reply_method_errorf(
1369 SD_BUS_ERROR_UNKNOWN_METHOD,
1370 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1378 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1379 struct node *n, *parent;
1381 _cleanup_free_ char *s = NULL;
1387 assert(path[0] == '/');
1389 n = hashmap_get(bus->nodes, path);
1393 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1401 if (streq(path, "/"))
1404 e = strrchr(path, '/');
1407 p = strndupa(path, MAX(1, path - e));
1409 parent = bus_node_allocate(bus, p);
1414 n = new0(struct node, 1);
1420 s = NULL; /* do not free */
1422 r = hashmap_put(bus->nodes, n->path, n);
1430 LIST_PREPEND(siblings, parent->child, n);
1435 void bus_node_gc(sd_bus *b, struct node *n) {
1448 assert(hashmap_remove(b->nodes, n->path) == n);
1451 LIST_REMOVE(siblings, n->parent->child, n);
1454 bus_node_gc(b, n->parent);
1458 static int bus_add_object(
1463 sd_bus_message_handler_t callback,
1470 assert_return(bus, -EINVAL);
1471 assert_return(object_path_is_valid(path), -EINVAL);
1472 assert_return(callback, -EINVAL);
1473 assert_return(!bus_pid_changed(bus), -ECHILD);
1475 n = bus_node_allocate(bus, path);
1479 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1485 s->node_callback.callback = callback;
1486 s->node_callback.is_fallback = fallback;
1488 s->node_callback.node = n;
1489 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1490 bus->nodes_modified = true;
1498 sd_bus_slot_unref(s);
1499 bus_node_gc(bus, n);
1504 _public_ int sd_bus_add_object(
1508 sd_bus_message_handler_t callback,
1511 return bus_add_object(bus, slot, false, path, callback, userdata);
1514 _public_ int sd_bus_add_fallback(
1518 sd_bus_message_handler_t callback,
1521 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1524 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1525 const struct vtable_member *m = a;
1526 uint8_t hash_key2[HASH_KEY_SIZE];
1531 ret = string_hash_func(m->path, hash_key);
1533 /* Use a slightly different hash key for the interface */
1534 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1536 ret ^= string_hash_func(m->interface, hash_key2);
1538 /* And an even different one for the member */
1540 ret ^= string_hash_func(m->member, hash_key2);
1545 static int vtable_member_compare_func(const void *a, const void *b) {
1546 const struct vtable_member *x = a, *y = b;
1552 r = strcmp(x->path, y->path);
1556 r = strcmp(x->interface, y->interface);
1560 return strcmp(x->member, y->member);
1563 static const struct hash_ops vtable_member_hash_ops = {
1564 .hash = vtable_member_hash_func,
1565 .compare = vtable_member_compare_func
1568 static int add_object_vtable_internal(
1572 const char *interface,
1573 const sd_bus_vtable *vtable,
1575 sd_bus_object_find_t find,
1578 sd_bus_slot *s = NULL;
1579 struct node_vtable *i, *existing = NULL;
1580 const sd_bus_vtable *v;
1584 assert_return(bus, -EINVAL);
1585 assert_return(object_path_is_valid(path), -EINVAL);
1586 assert_return(interface_name_is_valid(interface), -EINVAL);
1587 assert_return(vtable, -EINVAL);
1588 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1589 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1590 assert_return(!bus_pid_changed(bus), -ECHILD);
1591 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1592 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1593 !streq(interface, "org.freedesktop.DBus.Peer") &&
1594 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1596 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1600 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1604 n = bus_node_allocate(bus, path);
1608 LIST_FOREACH(vtables, i, n->vtables) {
1609 if (i->is_fallback != fallback) {
1614 if (streq(i->interface, interface)) {
1616 if (i->vtable == vtable) {
1625 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1631 s->node_vtable.is_fallback = fallback;
1632 s->node_vtable.vtable = vtable;
1633 s->node_vtable.find = find;
1635 s->node_vtable.interface = strdup(interface);
1636 if (!s->node_vtable.interface) {
1641 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1645 case _SD_BUS_VTABLE_METHOD: {
1646 struct vtable_member *m;
1648 if (!member_name_is_valid(v->x.method.member) ||
1649 !signature_is_valid(strempty(v->x.method.signature), false) ||
1650 !signature_is_valid(strempty(v->x.method.result), false) ||
1651 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1652 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1657 m = new0(struct vtable_member, 1);
1663 m->parent = &s->node_vtable;
1665 m->interface = s->node_vtable.interface;
1666 m->member = v->x.method.member;
1669 r = hashmap_put(bus->vtable_methods, m, m);
1678 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1680 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1685 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1692 case _SD_BUS_VTABLE_PROPERTY: {
1693 struct vtable_member *m;
1695 if (!member_name_is_valid(v->x.property.member) ||
1696 !signature_is_single(v->x.property.signature, false) ||
1697 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1698 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1699 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1700 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1705 m = new0(struct vtable_member, 1);
1711 m->parent = &s->node_vtable;
1713 m->interface = s->node_vtable.interface;
1714 m->member = v->x.property.member;
1717 r = hashmap_put(bus->vtable_properties, m, m);
1726 case _SD_BUS_VTABLE_SIGNAL:
1728 if (!member_name_is_valid(v->x.signal.member) ||
1729 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1730 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1743 s->node_vtable.node = n;
1744 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1745 bus->nodes_modified = true;
1753 sd_bus_slot_unref(s);
1754 bus_node_gc(bus, n);
1759 _public_ int sd_bus_add_object_vtable(
1763 const char *interface,
1764 const sd_bus_vtable *vtable,
1767 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1770 _public_ int sd_bus_add_fallback_vtable(
1774 const char *interface,
1775 const sd_bus_vtable *vtable,
1776 sd_bus_object_find_t find,
1779 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1782 _public_ int sd_bus_add_node_enumerator(
1786 sd_bus_node_enumerator_t callback,
1793 assert_return(bus, -EINVAL);
1794 assert_return(object_path_is_valid(path), -EINVAL);
1795 assert_return(callback, -EINVAL);
1796 assert_return(!bus_pid_changed(bus), -ECHILD);
1798 n = bus_node_allocate(bus, path);
1802 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1808 s->node_enumerator.callback = callback;
1810 s->node_enumerator.node = n;
1811 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1812 bus->nodes_modified = true;
1820 sd_bus_slot_unref(s);
1821 bus_node_gc(bus, n);
1826 static int emit_properties_changed_on_interface(
1830 const char *interface,
1831 bool require_fallback,
1832 bool *found_interface,
1835 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1836 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1837 bool has_invalidating = false, has_changing = false;
1838 struct vtable_member key = {};
1839 struct node_vtable *c;
1849 assert(found_interface);
1851 n = hashmap_get(bus->nodes, prefix);
1855 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1859 r = sd_bus_message_append(m, "s", interface);
1863 r = sd_bus_message_open_container(m, 'a', "{sv}");
1868 key.interface = interface;
1870 LIST_FOREACH(vtables, c, n->vtables) {
1871 if (require_fallback && !c->is_fallback)
1874 if (!streq(c->interface, interface))
1877 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1880 if (bus->nodes_modified)
1885 *found_interface = true;
1888 /* If the caller specified a list of
1889 * properties we include exactly those in the
1890 * PropertiesChanged message */
1892 STRV_FOREACH(property, names) {
1893 struct vtable_member *v;
1895 assert_return(member_name_is_valid(*property), -EINVAL);
1897 key.member = *property;
1898 v = hashmap_get(bus->vtable_properties, &key);
1902 /* If there are two vtables for the same
1903 * interface, let's handle this property when
1904 * we come to that vtable. */
1908 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1909 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1911 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1913 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1914 has_invalidating = true;
1918 has_changing = true;
1920 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1923 if (bus->nodes_modified)
1927 const sd_bus_vtable *v;
1929 /* If the caller specified no properties list
1930 * we include all properties that are marked
1931 * as changing in the message. */
1933 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1934 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1937 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1940 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1941 has_invalidating = true;
1945 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1948 has_changing = true;
1950 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1953 if (bus->nodes_modified)
1959 if (!has_invalidating && !has_changing)
1962 r = sd_bus_message_close_container(m);
1966 r = sd_bus_message_open_container(m, 'a', "s");
1970 if (has_invalidating) {
1971 LIST_FOREACH(vtables, c, n->vtables) {
1972 if (require_fallback && !c->is_fallback)
1975 if (!streq(c->interface, interface))
1978 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1981 if (bus->nodes_modified)
1987 STRV_FOREACH(property, names) {
1988 struct vtable_member *v;
1990 key.member = *property;
1991 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1992 assert(c == v->parent);
1994 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
1997 r = sd_bus_message_append(m, "s", *property);
2002 const sd_bus_vtable *v;
2004 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2005 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2008 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2011 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2014 r = sd_bus_message_append(m, "s", v->x.property.member);
2022 r = sd_bus_message_close_container(m);
2026 r = sd_bus_send(bus, m, NULL);
2033 _public_ int sd_bus_emit_properties_changed_strv(
2036 const char *interface,
2039 BUS_DONT_DESTROY(bus);
2040 bool found_interface = false;
2044 assert_return(bus, -EINVAL);
2045 assert_return(object_path_is_valid(path), -EINVAL);
2046 assert_return(interface_name_is_valid(interface), -EINVAL);
2047 assert_return(!bus_pid_changed(bus), -ECHILD);
2049 if (!BUS_IS_OPEN(bus->state))
2052 /* A non-NULL but empty names list means nothing needs to be
2053 generated. A NULL list OTOH indicates that all properties
2054 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2055 included in the PropertiesChanged message. */
2056 if (names && names[0] == NULL)
2060 bus->nodes_modified = false;
2062 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2065 if (bus->nodes_modified)
2068 prefix = alloca(strlen(path) + 1);
2069 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2070 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2073 if (bus->nodes_modified)
2077 } while (bus->nodes_modified);
2079 return found_interface ? 0 : -ENOENT;
2082 _public_ int sd_bus_emit_properties_changed(
2085 const char *interface,
2086 const char *name, ...) {
2090 assert_return(bus, -EINVAL);
2091 assert_return(object_path_is_valid(path), -EINVAL);
2092 assert_return(interface_name_is_valid(interface), -EINVAL);
2093 assert_return(!bus_pid_changed(bus), -ECHILD);
2095 if (!BUS_IS_OPEN(bus->state))
2101 names = strv_from_stdarg_alloca(name);
2103 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2106 static int interfaces_added_append_one_prefix(
2111 const char *interface,
2112 bool require_fallback) {
2114 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2115 bool found_interface = false;
2116 struct node_vtable *c;
2127 n = hashmap_get(bus->nodes, prefix);
2131 LIST_FOREACH(vtables, c, n->vtables) {
2132 if (require_fallback && !c->is_fallback)
2135 if (!streq(c->interface, interface))
2138 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2141 if (bus->nodes_modified)
2146 if (!found_interface) {
2147 r = sd_bus_message_append_basic(m, 's', interface);
2151 r = sd_bus_message_open_container(m, 'a', "{sv}");
2155 found_interface = true;
2158 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2161 if (bus->nodes_modified)
2165 if (found_interface) {
2166 r = sd_bus_message_close_container(m);
2171 return found_interface;
2174 static int interfaces_added_append_one(
2178 const char *interface) {
2188 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2191 if (bus->nodes_modified)
2194 prefix = alloca(strlen(path) + 1);
2195 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2196 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2199 if (bus->nodes_modified)
2206 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2207 BUS_DONT_DESTROY(bus);
2209 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2213 assert_return(bus, -EINVAL);
2214 assert_return(object_path_is_valid(path), -EINVAL);
2215 assert_return(!bus_pid_changed(bus), -ECHILD);
2217 if (!BUS_IS_OPEN(bus->state))
2220 if (strv_isempty(interfaces))
2224 bus->nodes_modified = false;
2225 m = sd_bus_message_unref(m);
2227 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2231 r = sd_bus_message_append_basic(m, 'o', path);
2235 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2239 STRV_FOREACH(i, interfaces) {
2240 assert_return(interface_name_is_valid(*i), -EINVAL);
2242 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2246 r = interfaces_added_append_one(bus, m, path, *i);
2250 if (bus->nodes_modified)
2253 r = sd_bus_message_close_container(m);
2258 if (bus->nodes_modified)
2261 r = sd_bus_message_close_container(m);
2265 } while (bus->nodes_modified);
2267 return sd_bus_send(bus, m, NULL);
2270 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2273 assert_return(bus, -EINVAL);
2274 assert_return(object_path_is_valid(path), -EINVAL);
2275 assert_return(!bus_pid_changed(bus), -ECHILD);
2277 if (!BUS_IS_OPEN(bus->state))
2280 interfaces = strv_from_stdarg_alloca(interface);
2282 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2285 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2286 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2289 assert_return(bus, -EINVAL);
2290 assert_return(object_path_is_valid(path), -EINVAL);
2291 assert_return(!bus_pid_changed(bus), -ECHILD);
2293 if (!BUS_IS_OPEN(bus->state))
2296 if (strv_isempty(interfaces))
2299 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2303 r = sd_bus_message_append_basic(m, 'o', path);
2307 r = sd_bus_message_append_strv(m, interfaces);
2311 return sd_bus_send(bus, m, NULL);
2314 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2317 assert_return(bus, -EINVAL);
2318 assert_return(object_path_is_valid(path), -EINVAL);
2319 assert_return(!bus_pid_changed(bus), -ECHILD);
2321 if (!BUS_IS_OPEN(bus->state))
2324 interfaces = strv_from_stdarg_alloca(interface);
2326 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2329 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2334 assert_return(bus, -EINVAL);
2335 assert_return(object_path_is_valid(path), -EINVAL);
2336 assert_return(!bus_pid_changed(bus), -ECHILD);
2338 n = bus_node_allocate(bus, path);
2342 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2348 s->node_object_manager.node = n;
2349 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2350 bus->nodes_modified = true;
2358 sd_bus_slot_unref(s);
2359 bus_node_gc(bus, n);