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 const char *signature = NULL;
623 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
624 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
626 /* Avoid that we call the set routine more than once
627 * if the processing of this message got restarted
628 * because the node tree changed. */
629 if (c->last_iteration == bus->iteration_counter)
632 c->last_iteration = bus->iteration_counter;
634 r = sd_bus_message_peek_type(m, &type, &signature);
638 if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
639 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature));
641 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
645 r = check_access(bus, m, c, &error);
647 return bus_maybe_reply_error(m, r, &error);
649 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
651 return bus_maybe_reply_error(m, r, &error);
653 if (bus->nodes_modified)
656 r = sd_bus_message_exit_container(m);
661 r = sd_bus_send(bus, reply, NULL);
668 static int vtable_append_one_property(
670 sd_bus_message *reply,
672 struct node_vtable *c,
673 const sd_bus_vtable *v,
675 sd_bus_error *error) {
686 r = sd_bus_message_open_container(reply, 'e', "sv");
690 r = sd_bus_message_append(reply, "s", v->x.property.member);
694 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
698 slot = container_of(c, sd_bus_slot, node_vtable);
700 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
703 if (bus->nodes_modified)
706 r = sd_bus_message_close_container(reply);
710 r = sd_bus_message_close_container(reply);
717 static int vtable_append_all_properties(
719 sd_bus_message *reply,
721 struct node_vtable *c,
723 sd_bus_error *error) {
725 const sd_bus_vtable *v;
733 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
736 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
737 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
740 if (v->flags & SD_BUS_VTABLE_HIDDEN)
743 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
746 if (bus->nodes_modified)
753 static int property_get_all_callbacks_run(
756 struct node_vtable *first,
757 bool require_fallback,
759 bool *found_object) {
761 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
762 struct node_vtable *c;
763 bool found_interface;
768 assert(found_object);
770 r = sd_bus_message_new_method_return(m, &reply);
774 r = sd_bus_message_open_container(reply, 'a', "{sv}");
778 found_interface = !iface ||
779 streq(iface, "org.freedesktop.DBus.Properties") ||
780 streq(iface, "org.freedesktop.DBus.Peer") ||
781 streq(iface, "org.freedesktop.DBus.Introspectable");
783 LIST_FOREACH(vtables, c, first) {
784 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
787 if (require_fallback && !c->is_fallback)
790 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
792 return bus_maybe_reply_error(m, r, &error);
793 if (bus->nodes_modified)
798 *found_object = true;
800 if (iface && !streq(c->interface, iface))
802 found_interface = true;
804 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
806 return bus_maybe_reply_error(m, r, &error);
807 if (bus->nodes_modified)
811 if (!found_interface) {
812 r = sd_bus_reply_method_errorf(
814 SD_BUS_ERROR_UNKNOWN_INTERFACE,
815 "Unknown interface '%s'.", iface);
822 r = sd_bus_message_close_container(reply);
826 r = sd_bus_send(bus, reply, NULL);
833 static int bus_node_exists(
837 bool require_fallback) {
839 struct node_vtable *c;
840 struct node_callback *k;
847 /* Tests if there's anything attached directly to this node
848 * for the specified path */
850 if (!require_fallback && (n->enumerators || n->object_managers))
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 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
869 if (bus->nodes_modified)
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, !require_fallback && n->object_managers);
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;
1154 assert(found_object);
1156 /* Spec says, GetManagedObjects() is only implemented on the root of a
1157 * sub-tree. Therefore, we require a registered object-manager on
1158 * exactly the queried path, otherwise, we refuse to respond. */
1160 if (require_fallback || !n->object_managers)
1163 r = get_child_nodes(bus, m->path, n, &s, &error);
1166 if (bus->nodes_modified)
1169 r = sd_bus_message_new_method_return(m, &reply);
1173 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1177 SET_FOREACH(path, s, i) {
1178 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1182 if (bus->nodes_modified)
1186 r = sd_bus_message_close_container(reply);
1190 r = sd_bus_send(bus, reply, NULL);
1197 static int object_find_and_run(
1201 bool require_fallback,
1202 bool *found_object) {
1205 struct vtable_member vtable_key, *v;
1211 assert(found_object);
1213 n = hashmap_get(bus->nodes, p);
1217 /* First, try object callbacks */
1218 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1221 if (bus->nodes_modified)
1224 if (!m->interface || !m->member)
1227 /* Then, look for a known method */
1228 vtable_key.path = (char*) p;
1229 vtable_key.interface = m->interface;
1230 vtable_key.member = m->member;
1232 v = hashmap_get(bus->vtable_methods, &vtable_key);
1234 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1237 if (bus->nodes_modified)
1241 /* Then, look for a known property */
1242 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1245 get = streq(m->member, "Get");
1247 if (get || streq(m->member, "Set")) {
1249 r = sd_bus_message_rewind(m, true);
1253 vtable_key.path = (char*) p;
1255 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1257 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1259 v = hashmap_get(bus->vtable_properties, &vtable_key);
1261 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1266 } else if (streq(m->member, "GetAll")) {
1269 r = sd_bus_message_rewind(m, true);
1273 r = sd_bus_message_read(m, "s", &iface);
1275 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1280 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1285 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1287 if (!isempty(sd_bus_message_get_signature(m, true)))
1288 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1290 r = process_introspect(bus, m, n, require_fallback, found_object);
1294 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1296 if (!isempty(sd_bus_message_get_signature(m, true)))
1297 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1299 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1304 if (bus->nodes_modified)
1307 if (!*found_object) {
1308 r = bus_node_exists(bus, n, m->path, require_fallback);
1311 if (bus->nodes_modified)
1314 *found_object = true;
1320 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1323 bool found_object = false;
1328 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1331 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1334 if (hashmap_isempty(bus->nodes))
1337 /* Never respond to broadcast messages */
1338 if (bus->bus_client && !m->destination)
1344 pl = strlen(m->path);
1348 bus->nodes_modified = false;
1350 r = object_find_and_run(bus, m, m->path, false, &found_object);
1354 /* Look for fallback prefixes */
1355 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1357 if (bus->nodes_modified)
1360 r = object_find_and_run(bus, m, prefix, true, &found_object);
1365 } while (bus->nodes_modified);
1370 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1371 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1372 r = sd_bus_reply_method_errorf(
1374 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1375 "Unknown property or interface.");
1377 r = sd_bus_reply_method_errorf(
1379 SD_BUS_ERROR_UNKNOWN_METHOD,
1380 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1388 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1389 struct node *n, *parent;
1391 _cleanup_free_ char *s = NULL;
1397 assert(path[0] == '/');
1399 n = hashmap_get(bus->nodes, path);
1403 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1411 if (streq(path, "/"))
1414 e = strrchr(path, '/');
1417 p = strndupa(path, MAX(1, path - e));
1419 parent = bus_node_allocate(bus, p);
1424 n = new0(struct node, 1);
1430 s = NULL; /* do not free */
1432 r = hashmap_put(bus->nodes, n->path, n);
1440 LIST_PREPEND(siblings, parent->child, n);
1445 void bus_node_gc(sd_bus *b, struct node *n) {
1458 assert(hashmap_remove(b->nodes, n->path) == n);
1461 LIST_REMOVE(siblings, n->parent->child, n);
1464 bus_node_gc(b, n->parent);
1468 static int bus_add_object(
1473 sd_bus_message_handler_t callback,
1480 assert_return(bus, -EINVAL);
1481 assert_return(object_path_is_valid(path), -EINVAL);
1482 assert_return(callback, -EINVAL);
1483 assert_return(!bus_pid_changed(bus), -ECHILD);
1485 n = bus_node_allocate(bus, path);
1489 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1495 s->node_callback.callback = callback;
1496 s->node_callback.is_fallback = fallback;
1498 s->node_callback.node = n;
1499 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1500 bus->nodes_modified = true;
1508 sd_bus_slot_unref(s);
1509 bus_node_gc(bus, n);
1514 _public_ int sd_bus_add_object(
1518 sd_bus_message_handler_t callback,
1521 return bus_add_object(bus, slot, false, path, callback, userdata);
1524 _public_ int sd_bus_add_fallback(
1528 sd_bus_message_handler_t callback,
1531 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1534 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1535 const struct vtable_member *m = a;
1536 uint8_t hash_key2[HASH_KEY_SIZE];
1541 ret = string_hash_func(m->path, hash_key);
1543 /* Use a slightly different hash key for the interface */
1544 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1546 ret ^= string_hash_func(m->interface, hash_key2);
1548 /* And an even different one for the member */
1550 ret ^= string_hash_func(m->member, hash_key2);
1555 static int vtable_member_compare_func(const void *a, const void *b) {
1556 const struct vtable_member *x = a, *y = b;
1562 r = strcmp(x->path, y->path);
1566 r = strcmp(x->interface, y->interface);
1570 return strcmp(x->member, y->member);
1573 static const struct hash_ops vtable_member_hash_ops = {
1574 .hash = vtable_member_hash_func,
1575 .compare = vtable_member_compare_func
1578 static int add_object_vtable_internal(
1582 const char *interface,
1583 const sd_bus_vtable *vtable,
1585 sd_bus_object_find_t find,
1588 sd_bus_slot *s = NULL;
1589 struct node_vtable *i, *existing = NULL;
1590 const sd_bus_vtable *v;
1594 assert_return(bus, -EINVAL);
1595 assert_return(object_path_is_valid(path), -EINVAL);
1596 assert_return(interface_name_is_valid(interface), -EINVAL);
1597 assert_return(vtable, -EINVAL);
1598 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1599 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1600 assert_return(!bus_pid_changed(bus), -ECHILD);
1601 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1602 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1603 !streq(interface, "org.freedesktop.DBus.Peer") &&
1604 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1606 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1610 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1614 n = bus_node_allocate(bus, path);
1618 LIST_FOREACH(vtables, i, n->vtables) {
1619 if (i->is_fallback != fallback) {
1624 if (streq(i->interface, interface)) {
1626 if (i->vtable == vtable) {
1635 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1641 s->node_vtable.is_fallback = fallback;
1642 s->node_vtable.vtable = vtable;
1643 s->node_vtable.find = find;
1645 s->node_vtable.interface = strdup(interface);
1646 if (!s->node_vtable.interface) {
1651 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1655 case _SD_BUS_VTABLE_METHOD: {
1656 struct vtable_member *m;
1658 if (!member_name_is_valid(v->x.method.member) ||
1659 !signature_is_valid(strempty(v->x.method.signature), false) ||
1660 !signature_is_valid(strempty(v->x.method.result), false) ||
1661 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1662 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1667 m = new0(struct vtable_member, 1);
1673 m->parent = &s->node_vtable;
1675 m->interface = s->node_vtable.interface;
1676 m->member = v->x.method.member;
1679 r = hashmap_put(bus->vtable_methods, m, m);
1688 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1690 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1695 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1702 case _SD_BUS_VTABLE_PROPERTY: {
1703 struct vtable_member *m;
1705 if (!member_name_is_valid(v->x.property.member) ||
1706 !signature_is_single(v->x.property.signature, false) ||
1707 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1708 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1709 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1710 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1715 m = new0(struct vtable_member, 1);
1721 m->parent = &s->node_vtable;
1723 m->interface = s->node_vtable.interface;
1724 m->member = v->x.property.member;
1727 r = hashmap_put(bus->vtable_properties, m, m);
1736 case _SD_BUS_VTABLE_SIGNAL:
1738 if (!member_name_is_valid(v->x.signal.member) ||
1739 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1740 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1753 s->node_vtable.node = n;
1754 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1755 bus->nodes_modified = true;
1763 sd_bus_slot_unref(s);
1764 bus_node_gc(bus, n);
1769 _public_ int sd_bus_add_object_vtable(
1773 const char *interface,
1774 const sd_bus_vtable *vtable,
1777 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1780 _public_ int sd_bus_add_fallback_vtable(
1784 const char *interface,
1785 const sd_bus_vtable *vtable,
1786 sd_bus_object_find_t find,
1789 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1792 _public_ int sd_bus_add_node_enumerator(
1796 sd_bus_node_enumerator_t callback,
1803 assert_return(bus, -EINVAL);
1804 assert_return(object_path_is_valid(path), -EINVAL);
1805 assert_return(callback, -EINVAL);
1806 assert_return(!bus_pid_changed(bus), -ECHILD);
1808 n = bus_node_allocate(bus, path);
1812 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1818 s->node_enumerator.callback = callback;
1820 s->node_enumerator.node = n;
1821 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1822 bus->nodes_modified = true;
1830 sd_bus_slot_unref(s);
1831 bus_node_gc(bus, n);
1836 static int emit_properties_changed_on_interface(
1840 const char *interface,
1841 bool require_fallback,
1842 bool *found_interface,
1845 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1846 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1847 bool has_invalidating = false, has_changing = false;
1848 struct vtable_member key = {};
1849 struct node_vtable *c;
1859 assert(found_interface);
1861 n = hashmap_get(bus->nodes, prefix);
1865 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1869 r = sd_bus_message_append(m, "s", interface);
1873 r = sd_bus_message_open_container(m, 'a', "{sv}");
1878 key.interface = interface;
1880 LIST_FOREACH(vtables, c, n->vtables) {
1881 if (require_fallback && !c->is_fallback)
1884 if (!streq(c->interface, interface))
1887 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1890 if (bus->nodes_modified)
1895 *found_interface = true;
1898 /* If the caller specified a list of
1899 * properties we include exactly those in the
1900 * PropertiesChanged message */
1902 STRV_FOREACH(property, names) {
1903 struct vtable_member *v;
1905 assert_return(member_name_is_valid(*property), -EINVAL);
1907 key.member = *property;
1908 v = hashmap_get(bus->vtable_properties, &key);
1912 /* If there are two vtables for the same
1913 * interface, let's handle this property when
1914 * we come to that vtable. */
1918 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1919 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1921 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1923 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1924 has_invalidating = true;
1928 has_changing = true;
1930 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1933 if (bus->nodes_modified)
1937 const sd_bus_vtable *v;
1939 /* If the caller specified no properties list
1940 * we include all properties that are marked
1941 * as changing in the message. */
1943 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1944 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1947 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1950 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1951 has_invalidating = true;
1955 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1958 has_changing = true;
1960 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1963 if (bus->nodes_modified)
1969 if (!has_invalidating && !has_changing)
1972 r = sd_bus_message_close_container(m);
1976 r = sd_bus_message_open_container(m, 'a', "s");
1980 if (has_invalidating) {
1981 LIST_FOREACH(vtables, c, n->vtables) {
1982 if (require_fallback && !c->is_fallback)
1985 if (!streq(c->interface, interface))
1988 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1991 if (bus->nodes_modified)
1997 STRV_FOREACH(property, names) {
1998 struct vtable_member *v;
2000 key.member = *property;
2001 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2002 assert(c == v->parent);
2004 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2007 r = sd_bus_message_append(m, "s", *property);
2012 const sd_bus_vtable *v;
2014 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2015 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2018 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2021 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2024 r = sd_bus_message_append(m, "s", v->x.property.member);
2032 r = sd_bus_message_close_container(m);
2036 r = sd_bus_send(bus, m, NULL);
2043 _public_ int sd_bus_emit_properties_changed_strv(
2046 const char *interface,
2049 BUS_DONT_DESTROY(bus);
2050 bool found_interface = false;
2054 assert_return(bus, -EINVAL);
2055 assert_return(object_path_is_valid(path), -EINVAL);
2056 assert_return(interface_name_is_valid(interface), -EINVAL);
2057 assert_return(!bus_pid_changed(bus), -ECHILD);
2059 if (!BUS_IS_OPEN(bus->state))
2062 /* A non-NULL but empty names list means nothing needs to be
2063 generated. A NULL list OTOH indicates that all properties
2064 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2065 included in the PropertiesChanged message. */
2066 if (names && names[0] == NULL)
2070 bus->nodes_modified = false;
2072 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2075 if (bus->nodes_modified)
2078 prefix = alloca(strlen(path) + 1);
2079 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2080 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2083 if (bus->nodes_modified)
2087 } while (bus->nodes_modified);
2089 return found_interface ? 0 : -ENOENT;
2092 _public_ int sd_bus_emit_properties_changed(
2095 const char *interface,
2096 const char *name, ...) {
2100 assert_return(bus, -EINVAL);
2101 assert_return(object_path_is_valid(path), -EINVAL);
2102 assert_return(interface_name_is_valid(interface), -EINVAL);
2103 assert_return(!bus_pid_changed(bus), -ECHILD);
2105 if (!BUS_IS_OPEN(bus->state))
2111 names = strv_from_stdarg_alloca(name);
2113 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2116 static int interfaces_added_append_one_prefix(
2121 const char *interface,
2122 bool require_fallback) {
2124 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2125 bool found_interface = false;
2126 struct node_vtable *c;
2137 n = hashmap_get(bus->nodes, prefix);
2141 LIST_FOREACH(vtables, c, n->vtables) {
2142 if (require_fallback && !c->is_fallback)
2145 if (!streq(c->interface, interface))
2148 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2151 if (bus->nodes_modified)
2156 if (!found_interface) {
2157 r = sd_bus_message_append_basic(m, 's', interface);
2161 r = sd_bus_message_open_container(m, 'a', "{sv}");
2165 found_interface = true;
2168 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2171 if (bus->nodes_modified)
2175 if (found_interface) {
2176 r = sd_bus_message_close_container(m);
2181 return found_interface;
2184 static int interfaces_added_append_one(
2188 const char *interface) {
2198 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2201 if (bus->nodes_modified)
2204 prefix = alloca(strlen(path) + 1);
2205 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2206 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2209 if (bus->nodes_modified)
2216 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2217 BUS_DONT_DESTROY(bus);
2219 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2223 assert_return(bus, -EINVAL);
2224 assert_return(object_path_is_valid(path), -EINVAL);
2225 assert_return(!bus_pid_changed(bus), -ECHILD);
2227 if (!BUS_IS_OPEN(bus->state))
2230 if (strv_isempty(interfaces))
2234 bus->nodes_modified = false;
2235 m = sd_bus_message_unref(m);
2237 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2241 r = sd_bus_message_append_basic(m, 'o', path);
2245 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2249 STRV_FOREACH(i, interfaces) {
2250 assert_return(interface_name_is_valid(*i), -EINVAL);
2252 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2256 r = interfaces_added_append_one(bus, m, path, *i);
2260 if (bus->nodes_modified)
2263 r = sd_bus_message_close_container(m);
2268 if (bus->nodes_modified)
2271 r = sd_bus_message_close_container(m);
2275 } while (bus->nodes_modified);
2277 return sd_bus_send(bus, m, NULL);
2280 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2283 assert_return(bus, -EINVAL);
2284 assert_return(object_path_is_valid(path), -EINVAL);
2285 assert_return(!bus_pid_changed(bus), -ECHILD);
2287 if (!BUS_IS_OPEN(bus->state))
2290 interfaces = strv_from_stdarg_alloca(interface);
2292 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2295 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2296 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2299 assert_return(bus, -EINVAL);
2300 assert_return(object_path_is_valid(path), -EINVAL);
2301 assert_return(!bus_pid_changed(bus), -ECHILD);
2303 if (!BUS_IS_OPEN(bus->state))
2306 if (strv_isempty(interfaces))
2309 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2313 r = sd_bus_message_append_basic(m, 'o', path);
2317 r = sd_bus_message_append_strv(m, interfaces);
2321 return sd_bus_send(bus, m, NULL);
2324 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2327 assert_return(bus, -EINVAL);
2328 assert_return(object_path_is_valid(path), -EINVAL);
2329 assert_return(!bus_pid_changed(bus), -ECHILD);
2331 if (!BUS_IS_OPEN(bus->state))
2334 interfaces = strv_from_stdarg_alloca(interface);
2336 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2339 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2344 assert_return(bus, -EINVAL);
2345 assert_return(object_path_is_valid(path), -EINVAL);
2346 assert_return(!bus_pid_changed(bus), -ECHILD);
2348 n = bus_node_allocate(bus, path);
2352 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2358 s->node_object_manager.node = n;
2359 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2360 bus->nodes_modified = true;
2368 sd_bus_slot_unref(s);
2369 bus_node_gc(bus, n);