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 r = c->find(bus, path, c->interface, u, &u, error);
55 bus->current_slot = sd_bus_slot_unref(s);
59 if (sd_bus_error_is_set(error))
60 return -sd_bus_error_get_errno(error);
71 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
74 return (uint8_t*) u + p->x.property.offset;
77 static int vtable_property_get_userdata(
80 struct vtable_member *p,
82 sd_bus_error *error) {
92 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
95 if (bus->nodes_modified)
98 *userdata = vtable_property_convert_userdata(p->vtable, u);
102 static int add_enumerated_to_set(
105 struct node_enumerator *first,
107 sd_bus_error *error) {
109 struct node_enumerator *c;
116 LIST_FOREACH(enumerators, c, first) {
117 char **children = NULL, **k;
120 if (bus->nodes_modified)
123 slot = container_of(c, sd_bus_slot, node_enumerator);
125 bus->current_slot = sd_bus_slot_ref(slot);
126 r = c->callback(bus, prefix, slot->userdata, &children, error);
127 bus->current_slot = sd_bus_slot_unref(slot);
131 if (sd_bus_error_is_set(error))
132 return -sd_bus_error_get_errno(error);
134 STRV_FOREACH(k, children) {
140 if (!object_path_is_valid(*k)){
146 if (!object_path_startswith(*k, prefix)) {
151 r = set_consume(s, *k);
164 static int add_subtree_to_set(
169 sd_bus_error *error) {
179 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
182 if (bus->nodes_modified)
185 LIST_FOREACH(siblings, i, n->child) {
188 if (!object_path_startswith(i->path, prefix))
195 r = set_consume(s, t);
196 if (r < 0 && r != -EEXIST)
199 r = add_subtree_to_set(bus, prefix, i, s, error);
202 if (bus->nodes_modified)
209 static int get_child_nodes(
214 sd_bus_error *error) {
224 s = set_new(string_hash_func, string_compare_func);
228 r = add_subtree_to_set(bus, prefix, n, s, error);
238 static int node_callbacks_run(
241 struct node_callback *first,
242 bool require_fallback,
243 bool *found_object) {
245 struct node_callback *c;
250 assert(found_object);
252 LIST_FOREACH(callbacks, c, first) {
253 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
256 if (bus->nodes_modified)
259 if (require_fallback && !c->is_fallback)
262 *found_object = true;
264 if (c->last_iteration == bus->iteration_counter)
267 c->last_iteration = bus->iteration_counter;
269 r = sd_bus_message_rewind(m, true);
273 slot = container_of(c, sd_bus_slot, node_callback);
275 bus->current_slot = sd_bus_slot_ref(slot);
276 r = c->callback(bus, m, slot->userdata, &error_buffer);
277 bus->current_slot = sd_bus_slot_unref(slot);
279 r = bus_maybe_reply_error(m, r, &error_buffer);
287 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
289 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
290 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
299 /* If the entire bus is trusted let's grant access */
303 /* If the member is marked UNPRIVILEGED let's grant access */
304 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
307 /* If we are not connected to kdbus we cannot retrieve the
308 * effective capability set without race. Since we need this
309 * for a security decision we cannot use racy data, hence
310 * don't request it. */
312 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
314 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
318 /* Check have the caller has the requested capability
319 * set. Note that the flags value contains the capability
320 * number plus one, which we need to subtract here. We do this
321 * so that we have 0 as special value for "default
323 cap = CAPABILITY_SHIFT(c->vtable->flags);
325 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
331 r = sd_bus_creds_has_effective_cap(creds, cap);
335 /* Caller has same UID as us, then let's grant access */
336 r = sd_bus_creds_get_uid(creds, &uid);
342 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
345 static int method_callbacks_run(
348 struct vtable_member *c,
349 bool require_fallback,
350 bool *found_object) {
352 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
353 const char *signature;
360 assert(found_object);
362 if (require_fallback && !c->parent->is_fallback)
365 r = check_access(bus, m, c, &error);
367 return bus_maybe_reply_error(m, r, &error);
369 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
371 return bus_maybe_reply_error(m, r, &error);
372 if (bus->nodes_modified)
375 *found_object = true;
377 if (c->last_iteration == bus->iteration_counter)
380 c->last_iteration = bus->iteration_counter;
382 r = sd_bus_message_rewind(m, true);
386 signature = sd_bus_message_get_signature(m, true);
390 if (!streq(strempty(c->vtable->x.method.signature), signature))
391 return sd_bus_reply_method_errorf(
393 SD_BUS_ERROR_INVALID_ARGS,
394 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
395 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
397 /* Keep track what the signature of the reply to this message
398 * should be, so that this can be enforced when sealing the
400 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
402 if (c->vtable->x.method.handler) {
405 slot = container_of(c->parent, sd_bus_slot, node_vtable);
407 bus->current_slot = sd_bus_slot_ref(slot);
408 r = c->vtable->x.method.handler(bus, m, u, &error);
409 bus->current_slot = sd_bus_slot_unref(slot);
411 return bus_maybe_reply_error(m, r, &error);
414 /* If the method callback is NULL, make this a successful NOP */
415 r = sd_bus_reply_method_return(m, NULL);
422 static int invoke_property_get(
425 const sd_bus_vtable *v,
427 const char *interface,
428 const char *property,
429 sd_bus_message *reply,
431 sd_bus_error *error) {
444 if (v->x.property.get) {
446 bus->current_slot = sd_bus_slot_ref(slot);
447 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
448 bus->current_slot = sd_bus_slot_unref(slot);
452 if (sd_bus_error_is_set(error))
453 return -sd_bus_error_get_errno(error);
457 /* Automatic handling if no callback is defined. */
459 if (streq(v->x.property.signature, "as"))
460 return sd_bus_message_append_strv(reply, *(char***) userdata);
462 assert(signature_is_single(v->x.property.signature, false));
463 assert(bus_type_is_basic(v->x.property.signature[0]));
465 switch (v->x.property.signature[0]) {
467 case SD_BUS_TYPE_STRING:
468 case SD_BUS_TYPE_SIGNATURE:
469 p = strempty(*(char**) userdata);
472 case SD_BUS_TYPE_OBJECT_PATH:
473 p = *(char**) userdata;
482 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
485 static int invoke_property_set(
488 const sd_bus_vtable *v,
490 const char *interface,
491 const char *property,
492 sd_bus_message *value,
494 sd_bus_error *error) {
506 if (v->x.property.set) {
508 bus->current_slot = sd_bus_slot_ref(slot);
509 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
510 bus->current_slot = sd_bus_slot_unref(slot);
514 if (sd_bus_error_is_set(error))
515 return -sd_bus_error_get_errno(error);
519 /* Automatic handling if no callback is defined. */
521 assert(signature_is_single(v->x.property.signature, false));
522 assert(bus_type_is_basic(v->x.property.signature[0]));
524 switch (v->x.property.signature[0]) {
526 case SD_BUS_TYPE_STRING:
527 case SD_BUS_TYPE_OBJECT_PATH:
528 case SD_BUS_TYPE_SIGNATURE: {
532 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
540 free(*(char**) userdata);
541 *(char**) userdata = n;
547 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
557 static int property_get_set_callbacks_run(
560 struct vtable_member *c,
561 bool require_fallback,
563 bool *found_object) {
565 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
566 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
574 assert(found_object);
576 if (require_fallback && !c->parent->is_fallback)
579 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
581 return bus_maybe_reply_error(m, r, &error);
582 if (bus->nodes_modified)
585 slot = container_of(c->parent, sd_bus_slot, node_vtable);
587 *found_object = true;
589 r = sd_bus_message_new_method_return(m, &reply);
594 /* Note that we do not protect against reexecution
595 * here (using the last_iteration check, see below),
596 * should the node tree have changed and we got called
597 * again. We assume that property Get() calls are
598 * ultimately without side-effects or if they aren't
599 * then at least idempotent. */
601 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
605 /* Note that we do not do an access check here. Read
606 * access to properties is always unrestricted, since
607 * PropertiesChanged signals broadcast contents
610 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
612 return bus_maybe_reply_error(m, r, &error);
614 if (bus->nodes_modified)
617 r = sd_bus_message_close_container(reply);
622 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
623 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
625 /* Avoid that we call the set routine more than once
626 * if the processing of this message got restarted
627 * because the node tree changed. */
628 if (c->last_iteration == bus->iteration_counter)
631 c->last_iteration = bus->iteration_counter;
633 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
637 r = check_access(bus, m, c, &error);
639 return bus_maybe_reply_error(m, r, &error);
641 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
643 return bus_maybe_reply_error(m, r, &error);
645 if (bus->nodes_modified)
648 r = sd_bus_message_exit_container(m);
653 r = sd_bus_send(bus, reply, NULL);
660 static int vtable_append_one_property(
662 sd_bus_message *reply,
664 struct node_vtable *c,
665 const sd_bus_vtable *v,
667 sd_bus_error *error) {
678 r = sd_bus_message_open_container(reply, 'e', "sv");
682 r = sd_bus_message_append(reply, "s", v->x.property.member);
686 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
690 slot = container_of(c, sd_bus_slot, node_vtable);
692 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
695 if (bus->nodes_modified)
698 r = sd_bus_message_close_container(reply);
702 r = sd_bus_message_close_container(reply);
709 static int vtable_append_all_properties(
711 sd_bus_message *reply,
713 struct node_vtable *c,
715 sd_bus_error *error) {
717 const sd_bus_vtable *v;
725 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
728 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
729 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
732 if (v->flags & SD_BUS_VTABLE_HIDDEN)
735 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
738 if (bus->nodes_modified)
745 static int property_get_all_callbacks_run(
748 struct node_vtable *first,
749 bool require_fallback,
751 bool *found_object) {
753 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
754 struct node_vtable *c;
755 bool found_interface;
760 assert(found_object);
762 r = sd_bus_message_new_method_return(m, &reply);
766 r = sd_bus_message_open_container(reply, 'a', "{sv}");
770 found_interface = !iface ||
771 streq(iface, "org.freedesktop.DBus.Properties") ||
772 streq(iface, "org.freedesktop.DBus.Peer") ||
773 streq(iface, "org.freedesktop.DBus.Introspectable");
775 LIST_FOREACH(vtables, c, first) {
776 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
779 if (require_fallback && !c->is_fallback)
782 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
784 return bus_maybe_reply_error(m, r, &error);
785 if (bus->nodes_modified)
790 *found_object = true;
792 if (iface && !streq(c->interface, iface))
794 found_interface = true;
796 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
798 return bus_maybe_reply_error(m, r, &error);
799 if (bus->nodes_modified)
803 if (!found_interface) {
804 r = sd_bus_reply_method_errorf(
806 SD_BUS_ERROR_UNKNOWN_INTERFACE,
807 "Unknown interface '%s'.", iface);
814 r = sd_bus_message_close_container(reply);
818 r = sd_bus_send(bus, reply, NULL);
825 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
829 if (n->object_managers)
833 return bus_node_with_object_manager(bus, n->parent);
838 static bool bus_node_exists(
842 bool require_fallback) {
844 struct node_vtable *c;
845 struct node_callback *k;
851 /* Tests if there's anything attached directly to this node
852 * for the specified path */
854 LIST_FOREACH(callbacks, k, n->callbacks) {
855 if (require_fallback && !k->is_fallback)
861 LIST_FOREACH(vtables, c, n->vtables) {
862 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
864 if (require_fallback && !c->is_fallback)
867 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
869 if (bus->nodes_modified)
873 return !require_fallback && (n->enumerators || n->object_managers);
876 static int process_introspect(
880 bool require_fallback,
881 bool *found_object) {
883 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
884 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
885 _cleanup_set_free_free_ Set *s = NULL;
886 const char *previous_interface = NULL;
887 struct introspect intro;
888 struct node_vtable *c;
895 assert(found_object);
897 r = get_child_nodes(bus, m->path, n, &s, &error);
899 return bus_maybe_reply_error(m, r, &error);
900 if (bus->nodes_modified)
903 r = introspect_begin(&intro, bus->trusted);
907 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
911 empty = set_isempty(s);
913 LIST_FOREACH(vtables, c, n->vtables) {
914 if (require_fallback && !c->is_fallback)
917 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
919 r = bus_maybe_reply_error(m, r, &error);
922 if (bus->nodes_modified) {
931 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
934 if (!streq_ptr(previous_interface, c->interface)) {
936 if (previous_interface)
937 fputs(" </interface>\n", intro.f);
939 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
942 r = introspect_write_interface(&intro, c->vtable);
946 previous_interface = c->interface;
949 if (previous_interface)
950 fputs(" </interface>\n", intro.f);
953 /* Nothing?, let's see if we exist at all, and if not
954 * refuse to do anything */
955 r = bus_node_exists(bus, n, m->path, require_fallback);
958 if (bus->nodes_modified)
964 *found_object = true;
966 r = introspect_write_child_nodes(&intro, s, m->path);
970 r = introspect_finish(&intro, bus, m, &reply);
974 r = sd_bus_send(bus, reply, NULL);
981 introspect_free(&intro);
985 static int object_manager_serialize_path(
987 sd_bus_message *reply,
990 bool require_fallback,
991 sd_bus_error *error) {
993 const char *previous_interface = NULL;
994 bool found_something = false;
995 struct node_vtable *i;
1005 n = hashmap_get(bus->nodes, prefix);
1009 LIST_FOREACH(vtables, i, n->vtables) {
1012 if (require_fallback && !i->is_fallback)
1015 r = node_vtable_get_userdata(bus, path, i, &u, error);
1018 if (bus->nodes_modified)
1023 if (!found_something) {
1025 /* Open the object part */
1027 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1031 r = sd_bus_message_append(reply, "o", path);
1035 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1039 found_something = true;
1042 if (!streq_ptr(previous_interface, i->interface)) {
1044 /* Maybe close the previous interface part */
1046 if (previous_interface) {
1047 r = sd_bus_message_close_container(reply);
1051 r = sd_bus_message_close_container(reply);
1056 /* Open the new interface part */
1058 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1062 r = sd_bus_message_append(reply, "s", i->interface);
1066 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1071 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1074 if (bus->nodes_modified)
1077 previous_interface = i->interface;
1080 if (previous_interface) {
1081 r = sd_bus_message_close_container(reply);
1085 r = sd_bus_message_close_container(reply);
1090 if (found_something) {
1091 r = sd_bus_message_close_container(reply);
1095 r = sd_bus_message_close_container(reply);
1103 static int object_manager_serialize_path_and_fallbacks(
1105 sd_bus_message *reply,
1107 sd_bus_error *error) {
1117 /* First, add all vtables registered for this path */
1118 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1121 if (bus->nodes_modified)
1124 /* Second, add fallback vtables registered for any of the prefixes */
1125 prefix = alloca(strlen(path) + 1);
1126 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1127 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1130 if (bus->nodes_modified)
1137 static int process_get_managed_objects(
1141 bool require_fallback,
1142 bool *found_object) {
1144 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1145 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1146 _cleanup_set_free_free_ Set *s = NULL;
1153 assert(found_object);
1155 if (!bus_node_with_object_manager(bus, n))
1158 r = get_child_nodes(bus, m->path, n, &s, &error);
1161 if (bus->nodes_modified)
1164 r = sd_bus_message_new_method_return(m, &reply);
1168 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1172 empty = set_isempty(s);
1174 struct node_vtable *c;
1176 /* Hmm, so we have no children? Then let's check
1177 * whether we exist at all, i.e. whether at least one
1180 LIST_FOREACH(vtables, c, n->vtables) {
1182 if (require_fallback && !c->is_fallback)
1200 SET_FOREACH(path, s, i) {
1201 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1205 if (bus->nodes_modified)
1210 r = sd_bus_message_close_container(reply);
1214 r = sd_bus_send(bus, reply, NULL);
1221 static int object_find_and_run(
1225 bool require_fallback,
1226 bool *found_object) {
1229 struct vtable_member vtable_key, *v;
1235 assert(found_object);
1237 n = hashmap_get(bus->nodes, p);
1241 /* First, try object callbacks */
1242 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1245 if (bus->nodes_modified)
1248 if (!m->interface || !m->member)
1251 /* Then, look for a known method */
1252 vtable_key.path = (char*) p;
1253 vtable_key.interface = m->interface;
1254 vtable_key.member = m->member;
1256 v = hashmap_get(bus->vtable_methods, &vtable_key);
1258 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1261 if (bus->nodes_modified)
1265 /* Then, look for a known property */
1266 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1269 get = streq(m->member, "Get");
1271 if (get || streq(m->member, "Set")) {
1273 r = sd_bus_message_rewind(m, true);
1277 vtable_key.path = (char*) p;
1279 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1281 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1283 v = hashmap_get(bus->vtable_properties, &vtable_key);
1285 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1290 } else if (streq(m->member, "GetAll")) {
1293 r = sd_bus_message_rewind(m, true);
1297 r = sd_bus_message_read(m, "s", &iface);
1299 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1304 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1309 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1311 if (!isempty(sd_bus_message_get_signature(m, true)))
1312 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1314 r = process_introspect(bus, m, n, require_fallback, found_object);
1318 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1320 if (!isempty(sd_bus_message_get_signature(m, true)))
1321 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1323 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1328 if (bus->nodes_modified)
1331 if (!*found_object) {
1332 r = bus_node_exists(bus, n, m->path, require_fallback);
1336 *found_object = true;
1342 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1345 bool found_object = false;
1350 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1353 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1356 if (hashmap_isempty(bus->nodes))
1359 /* Never respond to broadcast messages */
1360 if (bus->bus_client && !m->destination)
1366 pl = strlen(m->path);
1370 bus->nodes_modified = false;
1372 r = object_find_and_run(bus, m, m->path, false, &found_object);
1376 /* Look for fallback prefixes */
1377 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1379 if (bus->nodes_modified)
1382 r = object_find_and_run(bus, m, prefix, true, &found_object);
1387 } while (bus->nodes_modified);
1392 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1393 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1394 r = sd_bus_reply_method_errorf(
1396 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1397 "Unknown property or interface.");
1399 r = sd_bus_reply_method_errorf(
1401 SD_BUS_ERROR_UNKNOWN_METHOD,
1402 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1410 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1411 struct node *n, *parent;
1413 _cleanup_free_ char *s = NULL;
1419 assert(path[0] == '/');
1421 n = hashmap_get(bus->nodes, path);
1425 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1433 if (streq(path, "/"))
1436 e = strrchr(path, '/');
1439 p = strndupa(path, MAX(1, path - e));
1441 parent = bus_node_allocate(bus, p);
1446 n = new0(struct node, 1);
1452 s = NULL; /* do not free */
1454 r = hashmap_put(bus->nodes, n->path, n);
1462 LIST_PREPEND(siblings, parent->child, n);
1467 void bus_node_gc(sd_bus *b, struct node *n) {
1480 assert(hashmap_remove(b->nodes, n->path) == n);
1483 LIST_REMOVE(siblings, n->parent->child, n);
1486 bus_node_gc(b, n->parent);
1490 static int bus_add_object(
1495 sd_bus_message_handler_t callback,
1502 assert_return(bus, -EINVAL);
1503 assert_return(object_path_is_valid(path), -EINVAL);
1504 assert_return(callback, -EINVAL);
1505 assert_return(!bus_pid_changed(bus), -ECHILD);
1507 n = bus_node_allocate(bus, path);
1511 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1517 s->node_callback.callback = callback;
1518 s->node_callback.is_fallback = fallback;
1520 s->node_callback.node = n;
1521 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1522 bus->nodes_modified = true;
1530 sd_bus_slot_unref(s);
1531 bus_node_gc(bus, n);
1536 _public_ int sd_bus_add_object(
1540 sd_bus_message_handler_t callback,
1543 return bus_add_object(bus, slot, false, path, callback, userdata);
1546 _public_ int sd_bus_add_fallback(
1550 sd_bus_message_handler_t callback,
1553 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1556 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1557 const struct vtable_member *m = a;
1558 uint8_t hash_key2[HASH_KEY_SIZE];
1563 ret = string_hash_func(m->path, hash_key);
1565 /* Use a slightly different hash key for the interface */
1566 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1568 ret ^= string_hash_func(m->interface, hash_key2);
1570 /* And an even different one for the member */
1572 ret ^= string_hash_func(m->member, hash_key2);
1577 static int vtable_member_compare_func(const void *a, const void *b) {
1578 const struct vtable_member *x = a, *y = b;
1584 r = strcmp(x->path, y->path);
1588 r = strcmp(x->interface, y->interface);
1592 return strcmp(x->member, y->member);
1595 static int add_object_vtable_internal(
1599 const char *interface,
1600 const sd_bus_vtable *vtable,
1602 sd_bus_object_find_t find,
1605 sd_bus_slot *s = NULL;
1606 struct node_vtable *i, *existing = NULL;
1607 const sd_bus_vtable *v;
1611 assert_return(bus, -EINVAL);
1612 assert_return(object_path_is_valid(path), -EINVAL);
1613 assert_return(interface_name_is_valid(interface), -EINVAL);
1614 assert_return(vtable, -EINVAL);
1615 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1616 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1617 assert_return(!bus_pid_changed(bus), -ECHILD);
1618 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1619 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1620 !streq(interface, "org.freedesktop.DBus.Peer") &&
1621 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1623 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1627 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1631 n = bus_node_allocate(bus, path);
1635 LIST_FOREACH(vtables, i, n->vtables) {
1636 if (i->is_fallback != fallback) {
1641 if (streq(i->interface, interface)) {
1643 if (i->vtable == vtable) {
1652 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1658 s->node_vtable.is_fallback = fallback;
1659 s->node_vtable.vtable = vtable;
1660 s->node_vtable.find = find;
1662 s->node_vtable.interface = strdup(interface);
1663 if (!s->node_vtable.interface) {
1668 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1672 case _SD_BUS_VTABLE_METHOD: {
1673 struct vtable_member *m;
1675 if (!member_name_is_valid(v->x.method.member) ||
1676 !signature_is_valid(strempty(v->x.method.signature), false) ||
1677 !signature_is_valid(strempty(v->x.method.result), false) ||
1678 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1679 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1684 m = new0(struct vtable_member, 1);
1690 m->parent = &s->node_vtable;
1692 m->interface = s->node_vtable.interface;
1693 m->member = v->x.method.member;
1696 r = hashmap_put(bus->vtable_methods, m, m);
1705 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1707 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1714 case _SD_BUS_VTABLE_PROPERTY: {
1715 struct vtable_member *m;
1717 if (!member_name_is_valid(v->x.property.member) ||
1718 !signature_is_single(v->x.property.signature, false) ||
1719 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1720 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1721 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1722 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1727 m = new0(struct vtable_member, 1);
1733 m->parent = &s->node_vtable;
1735 m->interface = s->node_vtable.interface;
1736 m->member = v->x.property.member;
1739 r = hashmap_put(bus->vtable_properties, m, m);
1748 case _SD_BUS_VTABLE_SIGNAL:
1750 if (!member_name_is_valid(v->x.signal.member) ||
1751 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1752 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1765 s->node_vtable.node = n;
1766 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1767 bus->nodes_modified = true;
1775 sd_bus_slot_unref(s);
1776 bus_node_gc(bus, n);
1781 _public_ int sd_bus_add_object_vtable(
1785 const char *interface,
1786 const sd_bus_vtable *vtable,
1789 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1792 _public_ int sd_bus_add_fallback_vtable(
1796 const char *interface,
1797 const sd_bus_vtable *vtable,
1798 sd_bus_object_find_t find,
1801 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1804 _public_ int sd_bus_add_node_enumerator(
1808 sd_bus_node_enumerator_t callback,
1815 assert_return(bus, -EINVAL);
1816 assert_return(object_path_is_valid(path), -EINVAL);
1817 assert_return(callback, -EINVAL);
1818 assert_return(!bus_pid_changed(bus), -ECHILD);
1820 n = bus_node_allocate(bus, path);
1824 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1830 s->node_enumerator.callback = callback;
1832 s->node_enumerator.node = n;
1833 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1834 bus->nodes_modified = true;
1842 sd_bus_slot_unref(s);
1843 bus_node_gc(bus, n);
1848 static int emit_properties_changed_on_interface(
1852 const char *interface,
1853 bool require_fallback,
1854 bool *found_interface,
1857 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1858 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1859 bool has_invalidating = false, has_changing = false;
1860 struct vtable_member key = {};
1861 struct node_vtable *c;
1871 assert(found_interface);
1873 n = hashmap_get(bus->nodes, prefix);
1877 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1881 r = sd_bus_message_append(m, "s", interface);
1885 r = sd_bus_message_open_container(m, 'a', "{sv}");
1890 key.interface = interface;
1892 LIST_FOREACH(vtables, c, n->vtables) {
1893 if (require_fallback && !c->is_fallback)
1896 if (!streq(c->interface, interface))
1899 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1902 if (bus->nodes_modified)
1907 *found_interface = true;
1910 /* If the caller specified a list of
1911 * properties we include exactly those in the
1912 * PropertiesChanged message */
1914 STRV_FOREACH(property, names) {
1915 struct vtable_member *v;
1917 assert_return(member_name_is_valid(*property), -EINVAL);
1919 key.member = *property;
1920 v = hashmap_get(bus->vtable_properties, &key);
1924 /* If there are two vtables for the same
1925 * interface, let's handle this property when
1926 * we come to that vtable. */
1930 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1931 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1933 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1935 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1936 has_invalidating = true;
1940 has_changing = true;
1942 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1945 if (bus->nodes_modified)
1949 const sd_bus_vtable *v;
1951 /* If the caller specified no properties list
1952 * we include all properties that are marked
1953 * as changing in the message. */
1955 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1956 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1959 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1962 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1963 has_invalidating = true;
1967 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1970 has_changing = true;
1972 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1975 if (bus->nodes_modified)
1981 if (!has_invalidating && !has_changing)
1984 r = sd_bus_message_close_container(m);
1988 r = sd_bus_message_open_container(m, 'a', "s");
1992 if (has_invalidating) {
1993 LIST_FOREACH(vtables, c, n->vtables) {
1994 if (require_fallback && !c->is_fallback)
1997 if (!streq(c->interface, interface))
2000 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2003 if (bus->nodes_modified)
2009 STRV_FOREACH(property, names) {
2010 struct vtable_member *v;
2012 key.member = *property;
2013 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2014 assert(c == v->parent);
2016 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2019 r = sd_bus_message_append(m, "s", *property);
2024 const sd_bus_vtable *v;
2026 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2027 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2030 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2033 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2036 r = sd_bus_message_append(m, "s", v->x.property.member);
2044 r = sd_bus_message_close_container(m);
2048 r = sd_bus_send(bus, m, NULL);
2055 _public_ int sd_bus_emit_properties_changed_strv(
2058 const char *interface,
2061 BUS_DONT_DESTROY(bus);
2062 bool found_interface = false;
2066 assert_return(bus, -EINVAL);
2067 assert_return(object_path_is_valid(path), -EINVAL);
2068 assert_return(interface_name_is_valid(interface), -EINVAL);
2069 assert_return(!bus_pid_changed(bus), -ECHILD);
2071 if (!BUS_IS_OPEN(bus->state))
2074 /* A non-NULL but empty names list means nothing needs to be
2075 generated. A NULL list OTOH indicates that all properties
2076 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2077 included in the PropertiesChanged message. */
2078 if (names && names[0] == NULL)
2082 bus->nodes_modified = false;
2084 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2087 if (bus->nodes_modified)
2090 prefix = alloca(strlen(path) + 1);
2091 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2092 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2095 if (bus->nodes_modified)
2099 } while (bus->nodes_modified);
2101 return found_interface ? 0 : -ENOENT;
2104 _public_ int sd_bus_emit_properties_changed(
2107 const char *interface,
2108 const char *name, ...) {
2112 assert_return(bus, -EINVAL);
2113 assert_return(object_path_is_valid(path), -EINVAL);
2114 assert_return(interface_name_is_valid(interface), -EINVAL);
2115 assert_return(!bus_pid_changed(bus), -ECHILD);
2117 if (!BUS_IS_OPEN(bus->state))
2123 names = strv_from_stdarg_alloca(name);
2125 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2128 static int interfaces_added_append_one_prefix(
2133 const char *interface,
2134 bool require_fallback) {
2136 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2137 bool found_interface = false;
2138 struct node_vtable *c;
2149 n = hashmap_get(bus->nodes, prefix);
2153 LIST_FOREACH(vtables, c, n->vtables) {
2154 if (require_fallback && !c->is_fallback)
2157 if (!streq(c->interface, interface))
2160 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2163 if (bus->nodes_modified)
2168 if (!found_interface) {
2169 r = sd_bus_message_append_basic(m, 's', interface);
2173 r = sd_bus_message_open_container(m, 'a', "{sv}");
2177 found_interface = true;
2180 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2183 if (bus->nodes_modified)
2187 if (found_interface) {
2188 r = sd_bus_message_close_container(m);
2193 return found_interface;
2196 static int interfaces_added_append_one(
2200 const char *interface) {
2210 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2213 if (bus->nodes_modified)
2216 prefix = alloca(strlen(path) + 1);
2217 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2218 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2221 if (bus->nodes_modified)
2228 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2229 BUS_DONT_DESTROY(bus);
2231 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2235 assert_return(bus, -EINVAL);
2236 assert_return(object_path_is_valid(path), -EINVAL);
2237 assert_return(!bus_pid_changed(bus), -ECHILD);
2239 if (!BUS_IS_OPEN(bus->state))
2242 if (strv_isempty(interfaces))
2246 bus->nodes_modified = false;
2247 m = sd_bus_message_unref(m);
2249 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2253 r = sd_bus_message_append_basic(m, 'o', path);
2257 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2261 STRV_FOREACH(i, interfaces) {
2262 assert_return(interface_name_is_valid(*i), -EINVAL);
2264 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2268 r = interfaces_added_append_one(bus, m, path, *i);
2272 if (bus->nodes_modified)
2275 r = sd_bus_message_close_container(m);
2280 if (bus->nodes_modified)
2283 r = sd_bus_message_close_container(m);
2287 } while (bus->nodes_modified);
2289 return sd_bus_send(bus, m, NULL);
2292 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2295 assert_return(bus, -EINVAL);
2296 assert_return(object_path_is_valid(path), -EINVAL);
2297 assert_return(!bus_pid_changed(bus), -ECHILD);
2299 if (!BUS_IS_OPEN(bus->state))
2302 interfaces = strv_from_stdarg_alloca(interface);
2304 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2307 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2308 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2311 assert_return(bus, -EINVAL);
2312 assert_return(object_path_is_valid(path), -EINVAL);
2313 assert_return(!bus_pid_changed(bus), -ECHILD);
2315 if (!BUS_IS_OPEN(bus->state))
2318 if (strv_isempty(interfaces))
2321 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2325 r = sd_bus_message_append_basic(m, 'o', path);
2329 r = sd_bus_message_append_strv(m, interfaces);
2333 return sd_bus_send(bus, m, NULL);
2336 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2339 assert_return(bus, -EINVAL);
2340 assert_return(object_path_is_valid(path), -EINVAL);
2341 assert_return(!bus_pid_changed(bus), -ECHILD);
2343 if (!BUS_IS_OPEN(bus->state))
2346 interfaces = strv_from_stdarg_alloca(interface);
2348 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2351 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2356 assert_return(bus, -EINVAL);
2357 assert_return(object_path_is_valid(path), -EINVAL);
2358 assert_return(!bus_pid_changed(bus), -ECHILD);
2360 n = bus_node_allocate(bus, path);
2364 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2370 s->node_object_manager.node = n;
2371 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2372 bus->nodes_modified = true;
2380 sd_bus_slot_unref(s);
2381 bus_node_gc(bus, n);