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_func, string_compare_func);
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) {
298 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
306 /* If the entire bus is trusted let's grant access */
310 /* If the member is marked UNPRIVILEGED let's grant access */
311 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
314 /* Check have the caller has the requested capability
315 * set. Note that the flags value contains the capability
316 * number plus one, which we need to subtract here. We do this
317 * so that we have 0 as special value for "default
319 cap = CAPABILITY_SHIFT(c->vtable->flags);
321 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
327 r = sd_bus_query_sender_privilege(m, cap);
333 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
336 static int method_callbacks_run(
339 struct vtable_member *c,
340 bool require_fallback,
341 bool *found_object) {
343 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
344 const char *signature;
351 assert(found_object);
353 if (require_fallback && !c->parent->is_fallback)
356 r = check_access(bus, m, c, &error);
358 return bus_maybe_reply_error(m, r, &error);
360 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
362 return bus_maybe_reply_error(m, r, &error);
363 if (bus->nodes_modified)
366 *found_object = true;
368 if (c->last_iteration == bus->iteration_counter)
371 c->last_iteration = bus->iteration_counter;
373 r = sd_bus_message_rewind(m, true);
377 signature = sd_bus_message_get_signature(m, true);
381 if (!streq(strempty(c->vtable->x.method.signature), signature))
382 return sd_bus_reply_method_errorf(
384 SD_BUS_ERROR_INVALID_ARGS,
385 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
386 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
388 /* Keep track what the signature of the reply to this message
389 * should be, so that this can be enforced when sealing the
391 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
393 if (c->vtable->x.method.handler) {
396 slot = container_of(c->parent, sd_bus_slot, node_vtable);
398 bus->current_slot = sd_bus_slot_ref(slot);
399 bus->current_handler = c->vtable->x.method.handler;
400 bus->current_userdata = u;
401 r = c->vtable->x.method.handler(bus, m, u, &error);
402 bus->current_userdata = NULL;
403 bus->current_handler = NULL;
404 bus->current_slot = sd_bus_slot_unref(slot);
406 return bus_maybe_reply_error(m, r, &error);
409 /* If the method callback is NULL, make this a successful NOP */
410 r = sd_bus_reply_method_return(m, NULL);
417 static int invoke_property_get(
420 const sd_bus_vtable *v,
422 const char *interface,
423 const char *property,
424 sd_bus_message *reply,
426 sd_bus_error *error) {
439 if (v->x.property.get) {
441 bus->current_slot = sd_bus_slot_ref(slot);
442 bus->current_userdata = userdata;
443 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
444 bus->current_userdata = NULL;
445 bus->current_slot = sd_bus_slot_unref(slot);
449 if (sd_bus_error_is_set(error))
450 return -sd_bus_error_get_errno(error);
454 /* Automatic handling if no callback is defined. */
456 if (streq(v->x.property.signature, "as"))
457 return sd_bus_message_append_strv(reply, *(char***) userdata);
459 assert(signature_is_single(v->x.property.signature, false));
460 assert(bus_type_is_basic(v->x.property.signature[0]));
462 switch (v->x.property.signature[0]) {
464 case SD_BUS_TYPE_STRING:
465 case SD_BUS_TYPE_SIGNATURE:
466 p = strempty(*(char**) userdata);
469 case SD_BUS_TYPE_OBJECT_PATH:
470 p = *(char**) userdata;
479 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
482 static int invoke_property_set(
485 const sd_bus_vtable *v,
487 const char *interface,
488 const char *property,
489 sd_bus_message *value,
491 sd_bus_error *error) {
503 if (v->x.property.set) {
505 bus->current_slot = sd_bus_slot_ref(slot);
506 bus->current_userdata = userdata;
507 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
508 bus->current_userdata = NULL;
509 bus->current_slot = sd_bus_slot_unref(slot);
513 if (sd_bus_error_is_set(error))
514 return -sd_bus_error_get_errno(error);
518 /* Automatic handling if no callback is defined. */
520 assert(signature_is_single(v->x.property.signature, false));
521 assert(bus_type_is_basic(v->x.property.signature[0]));
523 switch (v->x.property.signature[0]) {
525 case SD_BUS_TYPE_STRING:
526 case SD_BUS_TYPE_OBJECT_PATH:
527 case SD_BUS_TYPE_SIGNATURE: {
531 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
539 free(*(char**) userdata);
540 *(char**) userdata = n;
546 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
556 static int property_get_set_callbacks_run(
559 struct vtable_member *c,
560 bool require_fallback,
562 bool *found_object) {
564 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
565 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
573 assert(found_object);
575 if (require_fallback && !c->parent->is_fallback)
578 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
580 return bus_maybe_reply_error(m, r, &error);
581 if (bus->nodes_modified)
584 slot = container_of(c->parent, sd_bus_slot, node_vtable);
586 *found_object = true;
588 r = sd_bus_message_new_method_return(m, &reply);
593 /* Note that we do not protect against reexecution
594 * here (using the last_iteration check, see below),
595 * should the node tree have changed and we got called
596 * again. We assume that property Get() calls are
597 * ultimately without side-effects or if they aren't
598 * then at least idempotent. */
600 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
604 /* Note that we do not do an access check here. Read
605 * access to properties is always unrestricted, since
606 * PropertiesChanged signals broadcast contents
609 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
611 return bus_maybe_reply_error(m, r, &error);
613 if (bus->nodes_modified)
616 r = sd_bus_message_close_container(reply);
621 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
622 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
624 /* Avoid that we call the set routine more than once
625 * if the processing of this message got restarted
626 * because the node tree changed. */
627 if (c->last_iteration == bus->iteration_counter)
630 c->last_iteration = bus->iteration_counter;
632 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
636 r = check_access(bus, m, c, &error);
638 return bus_maybe_reply_error(m, r, &error);
640 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
642 return bus_maybe_reply_error(m, r, &error);
644 if (bus->nodes_modified)
647 r = sd_bus_message_exit_container(m);
652 r = sd_bus_send(bus, reply, NULL);
659 static int vtable_append_one_property(
661 sd_bus_message *reply,
663 struct node_vtable *c,
664 const sd_bus_vtable *v,
666 sd_bus_error *error) {
677 r = sd_bus_message_open_container(reply, 'e', "sv");
681 r = sd_bus_message_append(reply, "s", v->x.property.member);
685 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
689 slot = container_of(c, sd_bus_slot, node_vtable);
691 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
694 if (bus->nodes_modified)
697 r = sd_bus_message_close_container(reply);
701 r = sd_bus_message_close_container(reply);
708 static int vtable_append_all_properties(
710 sd_bus_message *reply,
712 struct node_vtable *c,
714 sd_bus_error *error) {
716 const sd_bus_vtable *v;
724 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
727 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
728 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
731 if (v->flags & SD_BUS_VTABLE_HIDDEN)
734 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
737 if (bus->nodes_modified)
744 static int property_get_all_callbacks_run(
747 struct node_vtable *first,
748 bool require_fallback,
750 bool *found_object) {
752 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
753 struct node_vtable *c;
754 bool found_interface;
759 assert(found_object);
761 r = sd_bus_message_new_method_return(m, &reply);
765 r = sd_bus_message_open_container(reply, 'a', "{sv}");
769 found_interface = !iface ||
770 streq(iface, "org.freedesktop.DBus.Properties") ||
771 streq(iface, "org.freedesktop.DBus.Peer") ||
772 streq(iface, "org.freedesktop.DBus.Introspectable");
774 LIST_FOREACH(vtables, c, first) {
775 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
778 if (require_fallback && !c->is_fallback)
781 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
783 return bus_maybe_reply_error(m, r, &error);
784 if (bus->nodes_modified)
789 *found_object = true;
791 if (iface && !streq(c->interface, iface))
793 found_interface = true;
795 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
797 return bus_maybe_reply_error(m, r, &error);
798 if (bus->nodes_modified)
802 if (!found_interface) {
803 r = sd_bus_reply_method_errorf(
805 SD_BUS_ERROR_UNKNOWN_INTERFACE,
806 "Unknown interface '%s'.", iface);
813 r = sd_bus_message_close_container(reply);
817 r = sd_bus_send(bus, reply, NULL);
824 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
828 if (n->object_managers)
832 return bus_node_with_object_manager(bus, n->parent);
837 static bool bus_node_exists(
841 bool require_fallback) {
843 struct node_vtable *c;
844 struct node_callback *k;
850 /* Tests if there's anything attached directly to this node
851 * for the specified path */
853 LIST_FOREACH(callbacks, k, n->callbacks) {
854 if (require_fallback && !k->is_fallback)
860 LIST_FOREACH(vtables, c, n->vtables) {
861 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
863 if (require_fallback && !c->is_fallback)
866 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
868 if (bus->nodes_modified)
872 return !require_fallback && (n->enumerators || n->object_managers);
875 static int process_introspect(
879 bool require_fallback,
880 bool *found_object) {
882 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
883 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
884 _cleanup_set_free_free_ Set *s = NULL;
885 const char *previous_interface = NULL;
886 struct introspect intro;
887 struct node_vtable *c;
894 assert(found_object);
896 r = get_child_nodes(bus, m->path, n, &s, &error);
898 return bus_maybe_reply_error(m, r, &error);
899 if (bus->nodes_modified)
902 r = introspect_begin(&intro, bus->trusted);
906 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
910 empty = set_isempty(s);
912 LIST_FOREACH(vtables, c, n->vtables) {
913 if (require_fallback && !c->is_fallback)
916 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
918 r = bus_maybe_reply_error(m, r, &error);
921 if (bus->nodes_modified) {
930 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
933 if (!streq_ptr(previous_interface, c->interface)) {
935 if (previous_interface)
936 fputs(" </interface>\n", intro.f);
938 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
941 r = introspect_write_interface(&intro, c->vtable);
945 previous_interface = c->interface;
948 if (previous_interface)
949 fputs(" </interface>\n", intro.f);
952 /* Nothing?, let's see if we exist at all, and if not
953 * refuse to do anything */
954 r = bus_node_exists(bus, n, m->path, require_fallback);
957 if (bus->nodes_modified)
963 *found_object = true;
965 r = introspect_write_child_nodes(&intro, s, m->path);
969 r = introspect_finish(&intro, bus, m, &reply);
973 r = sd_bus_send(bus, reply, NULL);
980 introspect_free(&intro);
984 static int object_manager_serialize_path(
986 sd_bus_message *reply,
989 bool require_fallback,
990 sd_bus_error *error) {
992 const char *previous_interface = NULL;
993 bool found_something = false;
994 struct node_vtable *i;
1004 n = hashmap_get(bus->nodes, prefix);
1008 LIST_FOREACH(vtables, i, n->vtables) {
1011 if (require_fallback && !i->is_fallback)
1014 r = node_vtable_get_userdata(bus, path, i, &u, error);
1017 if (bus->nodes_modified)
1022 if (!found_something) {
1024 /* Open the object part */
1026 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1030 r = sd_bus_message_append(reply, "o", path);
1034 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1038 found_something = true;
1041 if (!streq_ptr(previous_interface, i->interface)) {
1043 /* Maybe close the previous interface part */
1045 if (previous_interface) {
1046 r = sd_bus_message_close_container(reply);
1050 r = sd_bus_message_close_container(reply);
1055 /* Open the new interface part */
1057 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1061 r = sd_bus_message_append(reply, "s", i->interface);
1065 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1070 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1073 if (bus->nodes_modified)
1076 previous_interface = i->interface;
1079 if (previous_interface) {
1080 r = sd_bus_message_close_container(reply);
1084 r = sd_bus_message_close_container(reply);
1089 if (found_something) {
1090 r = sd_bus_message_close_container(reply);
1094 r = sd_bus_message_close_container(reply);
1102 static int object_manager_serialize_path_and_fallbacks(
1104 sd_bus_message *reply,
1106 sd_bus_error *error) {
1116 /* First, add all vtables registered for this path */
1117 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1120 if (bus->nodes_modified)
1123 /* Second, add fallback vtables registered for any of the prefixes */
1124 prefix = alloca(strlen(path) + 1);
1125 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1126 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1129 if (bus->nodes_modified)
1136 static int process_get_managed_objects(
1140 bool require_fallback,
1141 bool *found_object) {
1143 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1144 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1145 _cleanup_set_free_free_ Set *s = NULL;
1152 assert(found_object);
1154 if (!bus_node_with_object_manager(bus, n))
1157 r = get_child_nodes(bus, m->path, n, &s, &error);
1160 if (bus->nodes_modified)
1163 r = sd_bus_message_new_method_return(m, &reply);
1167 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1171 empty = set_isempty(s);
1173 struct node_vtable *c;
1175 /* Hmm, so we have no children? Then let's check
1176 * whether we exist at all, i.e. whether at least one
1179 LIST_FOREACH(vtables, c, n->vtables) {
1181 if (require_fallback && !c->is_fallback)
1199 SET_FOREACH(path, s, i) {
1200 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1204 if (bus->nodes_modified)
1209 r = sd_bus_message_close_container(reply);
1213 r = sd_bus_send(bus, reply, NULL);
1220 static int object_find_and_run(
1224 bool require_fallback,
1225 bool *found_object) {
1228 struct vtable_member vtable_key, *v;
1234 assert(found_object);
1236 n = hashmap_get(bus->nodes, p);
1240 /* First, try object callbacks */
1241 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1244 if (bus->nodes_modified)
1247 if (!m->interface || !m->member)
1250 /* Then, look for a known method */
1251 vtable_key.path = (char*) p;
1252 vtable_key.interface = m->interface;
1253 vtable_key.member = m->member;
1255 v = hashmap_get(bus->vtable_methods, &vtable_key);
1257 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1260 if (bus->nodes_modified)
1264 /* Then, look for a known property */
1265 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1268 get = streq(m->member, "Get");
1270 if (get || streq(m->member, "Set")) {
1272 r = sd_bus_message_rewind(m, true);
1276 vtable_key.path = (char*) p;
1278 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1280 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1282 v = hashmap_get(bus->vtable_properties, &vtable_key);
1284 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1289 } else if (streq(m->member, "GetAll")) {
1292 r = sd_bus_message_rewind(m, true);
1296 r = sd_bus_message_read(m, "s", &iface);
1298 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1303 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1308 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1310 if (!isempty(sd_bus_message_get_signature(m, true)))
1311 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1313 r = process_introspect(bus, m, n, require_fallback, found_object);
1317 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1319 if (!isempty(sd_bus_message_get_signature(m, true)))
1320 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1322 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1327 if (bus->nodes_modified)
1330 if (!*found_object) {
1331 r = bus_node_exists(bus, n, m->path, require_fallback);
1335 *found_object = true;
1341 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1344 bool found_object = false;
1349 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1352 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1355 if (hashmap_isempty(bus->nodes))
1358 /* Never respond to broadcast messages */
1359 if (bus->bus_client && !m->destination)
1365 pl = strlen(m->path);
1369 bus->nodes_modified = false;
1371 r = object_find_and_run(bus, m, m->path, false, &found_object);
1375 /* Look for fallback prefixes */
1376 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1378 if (bus->nodes_modified)
1381 r = object_find_and_run(bus, m, prefix, true, &found_object);
1386 } while (bus->nodes_modified);
1391 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1392 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1393 r = sd_bus_reply_method_errorf(
1395 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1396 "Unknown property or interface.");
1398 r = sd_bus_reply_method_errorf(
1400 SD_BUS_ERROR_UNKNOWN_METHOD,
1401 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1409 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1410 struct node *n, *parent;
1412 _cleanup_free_ char *s = NULL;
1418 assert(path[0] == '/');
1420 n = hashmap_get(bus->nodes, path);
1424 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1432 if (streq(path, "/"))
1435 e = strrchr(path, '/');
1438 p = strndupa(path, MAX(1, path - e));
1440 parent = bus_node_allocate(bus, p);
1445 n = new0(struct node, 1);
1451 s = NULL; /* do not free */
1453 r = hashmap_put(bus->nodes, n->path, n);
1461 LIST_PREPEND(siblings, parent->child, n);
1466 void bus_node_gc(sd_bus *b, struct node *n) {
1479 assert(hashmap_remove(b->nodes, n->path) == n);
1482 LIST_REMOVE(siblings, n->parent->child, n);
1485 bus_node_gc(b, n->parent);
1489 static int bus_add_object(
1494 sd_bus_message_handler_t callback,
1501 assert_return(bus, -EINVAL);
1502 assert_return(object_path_is_valid(path), -EINVAL);
1503 assert_return(callback, -EINVAL);
1504 assert_return(!bus_pid_changed(bus), -ECHILD);
1506 n = bus_node_allocate(bus, path);
1510 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1516 s->node_callback.callback = callback;
1517 s->node_callback.is_fallback = fallback;
1519 s->node_callback.node = n;
1520 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1521 bus->nodes_modified = true;
1529 sd_bus_slot_unref(s);
1530 bus_node_gc(bus, n);
1535 _public_ int sd_bus_add_object(
1539 sd_bus_message_handler_t callback,
1542 return bus_add_object(bus, slot, false, path, callback, userdata);
1545 _public_ int sd_bus_add_fallback(
1549 sd_bus_message_handler_t callback,
1552 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1555 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1556 const struct vtable_member *m = a;
1557 uint8_t hash_key2[HASH_KEY_SIZE];
1562 ret = string_hash_func(m->path, hash_key);
1564 /* Use a slightly different hash key for the interface */
1565 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1567 ret ^= string_hash_func(m->interface, hash_key2);
1569 /* And an even different one for the member */
1571 ret ^= string_hash_func(m->member, hash_key2);
1576 static int vtable_member_compare_func(const void *a, const void *b) {
1577 const struct vtable_member *x = a, *y = b;
1583 r = strcmp(x->path, y->path);
1587 r = strcmp(x->interface, y->interface);
1591 return strcmp(x->member, y->member);
1594 static int add_object_vtable_internal(
1598 const char *interface,
1599 const sd_bus_vtable *vtable,
1601 sd_bus_object_find_t find,
1604 sd_bus_slot *s = NULL;
1605 struct node_vtable *i, *existing = NULL;
1606 const sd_bus_vtable *v;
1610 assert_return(bus, -EINVAL);
1611 assert_return(object_path_is_valid(path), -EINVAL);
1612 assert_return(interface_name_is_valid(interface), -EINVAL);
1613 assert_return(vtable, -EINVAL);
1614 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1615 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1616 assert_return(!bus_pid_changed(bus), -ECHILD);
1617 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1618 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1619 !streq(interface, "org.freedesktop.DBus.Peer") &&
1620 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1622 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1626 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1630 n = bus_node_allocate(bus, path);
1634 LIST_FOREACH(vtables, i, n->vtables) {
1635 if (i->is_fallback != fallback) {
1640 if (streq(i->interface, interface)) {
1642 if (i->vtable == vtable) {
1651 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1657 s->node_vtable.is_fallback = fallback;
1658 s->node_vtable.vtable = vtable;
1659 s->node_vtable.find = find;
1661 s->node_vtable.interface = strdup(interface);
1662 if (!s->node_vtable.interface) {
1667 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1671 case _SD_BUS_VTABLE_METHOD: {
1672 struct vtable_member *m;
1674 if (!member_name_is_valid(v->x.method.member) ||
1675 !signature_is_valid(strempty(v->x.method.signature), false) ||
1676 !signature_is_valid(strempty(v->x.method.result), false) ||
1677 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1678 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1683 m = new0(struct vtable_member, 1);
1689 m->parent = &s->node_vtable;
1691 m->interface = s->node_vtable.interface;
1692 m->member = v->x.method.member;
1695 r = hashmap_put(bus->vtable_methods, m, m);
1704 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1706 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1713 case _SD_BUS_VTABLE_PROPERTY: {
1714 struct vtable_member *m;
1716 if (!member_name_is_valid(v->x.property.member) ||
1717 !signature_is_single(v->x.property.signature, false) ||
1718 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1719 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1720 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1721 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1726 m = new0(struct vtable_member, 1);
1732 m->parent = &s->node_vtable;
1734 m->interface = s->node_vtable.interface;
1735 m->member = v->x.property.member;
1738 r = hashmap_put(bus->vtable_properties, m, m);
1747 case _SD_BUS_VTABLE_SIGNAL:
1749 if (!member_name_is_valid(v->x.signal.member) ||
1750 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1751 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1764 s->node_vtable.node = n;
1765 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1766 bus->nodes_modified = true;
1774 sd_bus_slot_unref(s);
1775 bus_node_gc(bus, n);
1780 _public_ int sd_bus_add_object_vtable(
1784 const char *interface,
1785 const sd_bus_vtable *vtable,
1788 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1791 _public_ int sd_bus_add_fallback_vtable(
1795 const char *interface,
1796 const sd_bus_vtable *vtable,
1797 sd_bus_object_find_t find,
1800 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1803 _public_ int sd_bus_add_node_enumerator(
1807 sd_bus_node_enumerator_t callback,
1814 assert_return(bus, -EINVAL);
1815 assert_return(object_path_is_valid(path), -EINVAL);
1816 assert_return(callback, -EINVAL);
1817 assert_return(!bus_pid_changed(bus), -ECHILD);
1819 n = bus_node_allocate(bus, path);
1823 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1829 s->node_enumerator.callback = callback;
1831 s->node_enumerator.node = n;
1832 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1833 bus->nodes_modified = true;
1841 sd_bus_slot_unref(s);
1842 bus_node_gc(bus, n);
1847 static int emit_properties_changed_on_interface(
1851 const char *interface,
1852 bool require_fallback,
1853 bool *found_interface,
1856 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1857 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1858 bool has_invalidating = false, has_changing = false;
1859 struct vtable_member key = {};
1860 struct node_vtable *c;
1870 assert(found_interface);
1872 n = hashmap_get(bus->nodes, prefix);
1876 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1880 r = sd_bus_message_append(m, "s", interface);
1884 r = sd_bus_message_open_container(m, 'a', "{sv}");
1889 key.interface = interface;
1891 LIST_FOREACH(vtables, c, n->vtables) {
1892 if (require_fallback && !c->is_fallback)
1895 if (!streq(c->interface, interface))
1898 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1901 if (bus->nodes_modified)
1906 *found_interface = true;
1909 /* If the caller specified a list of
1910 * properties we include exactly those in the
1911 * PropertiesChanged message */
1913 STRV_FOREACH(property, names) {
1914 struct vtable_member *v;
1916 assert_return(member_name_is_valid(*property), -EINVAL);
1918 key.member = *property;
1919 v = hashmap_get(bus->vtable_properties, &key);
1923 /* If there are two vtables for the same
1924 * interface, let's handle this property when
1925 * we come to that vtable. */
1929 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1930 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1932 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1934 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1935 has_invalidating = true;
1939 has_changing = true;
1941 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1944 if (bus->nodes_modified)
1948 const sd_bus_vtable *v;
1950 /* If the caller specified no properties list
1951 * we include all properties that are marked
1952 * as changing in the message. */
1954 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1955 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1958 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1961 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1962 has_invalidating = true;
1966 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1969 has_changing = true;
1971 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1974 if (bus->nodes_modified)
1980 if (!has_invalidating && !has_changing)
1983 r = sd_bus_message_close_container(m);
1987 r = sd_bus_message_open_container(m, 'a', "s");
1991 if (has_invalidating) {
1992 LIST_FOREACH(vtables, c, n->vtables) {
1993 if (require_fallback && !c->is_fallback)
1996 if (!streq(c->interface, interface))
1999 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2002 if (bus->nodes_modified)
2008 STRV_FOREACH(property, names) {
2009 struct vtable_member *v;
2011 key.member = *property;
2012 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2013 assert(c == v->parent);
2015 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2018 r = sd_bus_message_append(m, "s", *property);
2023 const sd_bus_vtable *v;
2025 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2026 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2029 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2032 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2035 r = sd_bus_message_append(m, "s", v->x.property.member);
2043 r = sd_bus_message_close_container(m);
2047 r = sd_bus_send(bus, m, NULL);
2054 _public_ int sd_bus_emit_properties_changed_strv(
2057 const char *interface,
2060 BUS_DONT_DESTROY(bus);
2061 bool found_interface = false;
2065 assert_return(bus, -EINVAL);
2066 assert_return(object_path_is_valid(path), -EINVAL);
2067 assert_return(interface_name_is_valid(interface), -EINVAL);
2068 assert_return(!bus_pid_changed(bus), -ECHILD);
2070 if (!BUS_IS_OPEN(bus->state))
2073 /* A non-NULL but empty names list means nothing needs to be
2074 generated. A NULL list OTOH indicates that all properties
2075 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2076 included in the PropertiesChanged message. */
2077 if (names && names[0] == NULL)
2081 bus->nodes_modified = false;
2083 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2086 if (bus->nodes_modified)
2089 prefix = alloca(strlen(path) + 1);
2090 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2091 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2094 if (bus->nodes_modified)
2098 } while (bus->nodes_modified);
2100 return found_interface ? 0 : -ENOENT;
2103 _public_ int sd_bus_emit_properties_changed(
2106 const char *interface,
2107 const char *name, ...) {
2111 assert_return(bus, -EINVAL);
2112 assert_return(object_path_is_valid(path), -EINVAL);
2113 assert_return(interface_name_is_valid(interface), -EINVAL);
2114 assert_return(!bus_pid_changed(bus), -ECHILD);
2116 if (!BUS_IS_OPEN(bus->state))
2122 names = strv_from_stdarg_alloca(name);
2124 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2127 static int interfaces_added_append_one_prefix(
2132 const char *interface,
2133 bool require_fallback) {
2135 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2136 bool found_interface = false;
2137 struct node_vtable *c;
2148 n = hashmap_get(bus->nodes, prefix);
2152 LIST_FOREACH(vtables, c, n->vtables) {
2153 if (require_fallback && !c->is_fallback)
2156 if (!streq(c->interface, interface))
2159 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2162 if (bus->nodes_modified)
2167 if (!found_interface) {
2168 r = sd_bus_message_append_basic(m, 's', interface);
2172 r = sd_bus_message_open_container(m, 'a', "{sv}");
2176 found_interface = true;
2179 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2182 if (bus->nodes_modified)
2186 if (found_interface) {
2187 r = sd_bus_message_close_container(m);
2192 return found_interface;
2195 static int interfaces_added_append_one(
2199 const char *interface) {
2209 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2212 if (bus->nodes_modified)
2215 prefix = alloca(strlen(path) + 1);
2216 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2217 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2220 if (bus->nodes_modified)
2227 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2228 BUS_DONT_DESTROY(bus);
2230 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2234 assert_return(bus, -EINVAL);
2235 assert_return(object_path_is_valid(path), -EINVAL);
2236 assert_return(!bus_pid_changed(bus), -ECHILD);
2238 if (!BUS_IS_OPEN(bus->state))
2241 if (strv_isempty(interfaces))
2245 bus->nodes_modified = false;
2246 m = sd_bus_message_unref(m);
2248 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2252 r = sd_bus_message_append_basic(m, 'o', path);
2256 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2260 STRV_FOREACH(i, interfaces) {
2261 assert_return(interface_name_is_valid(*i), -EINVAL);
2263 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2267 r = interfaces_added_append_one(bus, m, path, *i);
2271 if (bus->nodes_modified)
2274 r = sd_bus_message_close_container(m);
2279 if (bus->nodes_modified)
2282 r = sd_bus_message_close_container(m);
2286 } while (bus->nodes_modified);
2288 return sd_bus_send(bus, m, NULL);
2291 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2294 assert_return(bus, -EINVAL);
2295 assert_return(object_path_is_valid(path), -EINVAL);
2296 assert_return(!bus_pid_changed(bus), -ECHILD);
2298 if (!BUS_IS_OPEN(bus->state))
2301 interfaces = strv_from_stdarg_alloca(interface);
2303 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2306 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2307 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2310 assert_return(bus, -EINVAL);
2311 assert_return(object_path_is_valid(path), -EINVAL);
2312 assert_return(!bus_pid_changed(bus), -ECHILD);
2314 if (!BUS_IS_OPEN(bus->state))
2317 if (strv_isempty(interfaces))
2320 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2324 r = sd_bus_message_append_basic(m, 'o', path);
2328 r = sd_bus_message_append_strv(m, interfaces);
2332 return sd_bus_send(bus, m, NULL);
2335 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2338 assert_return(bus, -EINVAL);
2339 assert_return(object_path_is_valid(path), -EINVAL);
2340 assert_return(!bus_pid_changed(bus), -ECHILD);
2342 if (!BUS_IS_OPEN(bus->state))
2345 interfaces = strv_from_stdarg_alloca(interface);
2347 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2350 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2355 assert_return(bus, -EINVAL);
2356 assert_return(object_path_is_valid(path), -EINVAL);
2357 assert_return(!bus_pid_changed(bus), -ECHILD);
2359 n = bus_node_allocate(bus, path);
2363 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2369 s->node_object_manager.node = n;
2370 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2371 bus->nodes_modified = true;
2379 sd_bus_slot_unref(s);
2380 bus_node_gc(bus, n);