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) {
297 /* If the entire bus is trusted let's grant access */
301 /* If the member is marked UNPRIVILEGED let's grant access */
302 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
305 /* Check have the caller has the requested capability
306 * set. Note that the flags value contains the capability
307 * number plus one, which we need to subtract here. We do this
308 * so that we have 0 as special value for "default
310 cap = CAPABILITY_SHIFT(c->vtable->flags);
312 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
318 r = sd_bus_query_sender_privilege(m, cap);
324 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
327 static int method_callbacks_run(
330 struct vtable_member *c,
331 bool require_fallback,
332 bool *found_object) {
334 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
335 const char *signature;
342 assert(found_object);
344 if (require_fallback && !c->parent->is_fallback)
347 r = check_access(bus, m, c, &error);
349 return bus_maybe_reply_error(m, r, &error);
351 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
353 return bus_maybe_reply_error(m, r, &error);
354 if (bus->nodes_modified)
357 *found_object = true;
359 if (c->last_iteration == bus->iteration_counter)
362 c->last_iteration = bus->iteration_counter;
364 r = sd_bus_message_rewind(m, true);
368 signature = sd_bus_message_get_signature(m, true);
372 if (!streq(strempty(c->vtable->x.method.signature), signature))
373 return sd_bus_reply_method_errorf(
375 SD_BUS_ERROR_INVALID_ARGS,
376 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
377 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
379 /* Keep track what the signature of the reply to this message
380 * should be, so that this can be enforced when sealing the
382 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
384 if (c->vtable->x.method.handler) {
387 slot = container_of(c->parent, sd_bus_slot, node_vtable);
389 bus->current_slot = sd_bus_slot_ref(slot);
390 r = c->vtable->x.method.handler(bus, m, u, &error);
391 bus->current_slot = sd_bus_slot_unref(slot);
393 return bus_maybe_reply_error(m, r, &error);
396 /* If the method callback is NULL, make this a successful NOP */
397 r = sd_bus_reply_method_return(m, NULL);
404 static int invoke_property_get(
407 const sd_bus_vtable *v,
409 const char *interface,
410 const char *property,
411 sd_bus_message *reply,
413 sd_bus_error *error) {
426 if (v->x.property.get) {
428 bus->current_slot = sd_bus_slot_ref(slot);
429 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
430 bus->current_slot = sd_bus_slot_unref(slot);
434 if (sd_bus_error_is_set(error))
435 return -sd_bus_error_get_errno(error);
439 /* Automatic handling if no callback is defined. */
441 if (streq(v->x.property.signature, "as"))
442 return sd_bus_message_append_strv(reply, *(char***) userdata);
444 assert(signature_is_single(v->x.property.signature, false));
445 assert(bus_type_is_basic(v->x.property.signature[0]));
447 switch (v->x.property.signature[0]) {
449 case SD_BUS_TYPE_STRING:
450 case SD_BUS_TYPE_SIGNATURE:
451 p = strempty(*(char**) userdata);
454 case SD_BUS_TYPE_OBJECT_PATH:
455 p = *(char**) userdata;
464 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
467 static int invoke_property_set(
470 const sd_bus_vtable *v,
472 const char *interface,
473 const char *property,
474 sd_bus_message *value,
476 sd_bus_error *error) {
488 if (v->x.property.set) {
490 bus->current_slot = sd_bus_slot_ref(slot);
491 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
492 bus->current_slot = sd_bus_slot_unref(slot);
496 if (sd_bus_error_is_set(error))
497 return -sd_bus_error_get_errno(error);
501 /* Automatic handling if no callback is defined. */
503 assert(signature_is_single(v->x.property.signature, false));
504 assert(bus_type_is_basic(v->x.property.signature[0]));
506 switch (v->x.property.signature[0]) {
508 case SD_BUS_TYPE_STRING:
509 case SD_BUS_TYPE_OBJECT_PATH:
510 case SD_BUS_TYPE_SIGNATURE: {
514 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
522 free(*(char**) userdata);
523 *(char**) userdata = n;
529 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
539 static int property_get_set_callbacks_run(
542 struct vtable_member *c,
543 bool require_fallback,
545 bool *found_object) {
547 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
548 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
556 assert(found_object);
558 if (require_fallback && !c->parent->is_fallback)
561 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
563 return bus_maybe_reply_error(m, r, &error);
564 if (bus->nodes_modified)
567 slot = container_of(c->parent, sd_bus_slot, node_vtable);
569 *found_object = true;
571 r = sd_bus_message_new_method_return(m, &reply);
576 /* Note that we do not protect against reexecution
577 * here (using the last_iteration check, see below),
578 * should the node tree have changed and we got called
579 * again. We assume that property Get() calls are
580 * ultimately without side-effects or if they aren't
581 * then at least idempotent. */
583 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
587 /* Note that we do not do an access check here. Read
588 * access to properties is always unrestricted, since
589 * PropertiesChanged signals broadcast contents
592 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
594 return bus_maybe_reply_error(m, r, &error);
596 if (bus->nodes_modified)
599 r = sd_bus_message_close_container(reply);
604 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
605 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
607 /* Avoid that we call the set routine more than once
608 * if the processing of this message got restarted
609 * because the node tree changed. */
610 if (c->last_iteration == bus->iteration_counter)
613 c->last_iteration = bus->iteration_counter;
615 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
619 r = check_access(bus, m, c, &error);
621 return bus_maybe_reply_error(m, r, &error);
623 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
625 return bus_maybe_reply_error(m, r, &error);
627 if (bus->nodes_modified)
630 r = sd_bus_message_exit_container(m);
635 r = sd_bus_send(bus, reply, NULL);
642 static int vtable_append_one_property(
644 sd_bus_message *reply,
646 struct node_vtable *c,
647 const sd_bus_vtable *v,
649 sd_bus_error *error) {
660 r = sd_bus_message_open_container(reply, 'e', "sv");
664 r = sd_bus_message_append(reply, "s", v->x.property.member);
668 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
672 slot = container_of(c, sd_bus_slot, node_vtable);
674 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
677 if (bus->nodes_modified)
680 r = sd_bus_message_close_container(reply);
684 r = sd_bus_message_close_container(reply);
691 static int vtable_append_all_properties(
693 sd_bus_message *reply,
695 struct node_vtable *c,
697 sd_bus_error *error) {
699 const sd_bus_vtable *v;
707 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
710 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
711 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
714 if (v->flags & SD_BUS_VTABLE_HIDDEN)
717 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
720 if (bus->nodes_modified)
727 static int property_get_all_callbacks_run(
730 struct node_vtable *first,
731 bool require_fallback,
733 bool *found_object) {
735 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
736 struct node_vtable *c;
737 bool found_interface;
742 assert(found_object);
744 r = sd_bus_message_new_method_return(m, &reply);
748 r = sd_bus_message_open_container(reply, 'a', "{sv}");
752 found_interface = !iface ||
753 streq(iface, "org.freedesktop.DBus.Properties") ||
754 streq(iface, "org.freedesktop.DBus.Peer") ||
755 streq(iface, "org.freedesktop.DBus.Introspectable");
757 LIST_FOREACH(vtables, c, first) {
758 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
761 if (require_fallback && !c->is_fallback)
764 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
766 return bus_maybe_reply_error(m, r, &error);
767 if (bus->nodes_modified)
772 *found_object = true;
774 if (iface && !streq(c->interface, iface))
776 found_interface = true;
778 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
780 return bus_maybe_reply_error(m, r, &error);
781 if (bus->nodes_modified)
785 if (!found_interface) {
786 r = sd_bus_reply_method_errorf(
788 SD_BUS_ERROR_UNKNOWN_INTERFACE,
789 "Unknown interface '%s'.", iface);
796 r = sd_bus_message_close_container(reply);
800 r = sd_bus_send(bus, reply, NULL);
807 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
811 if (n->object_managers)
815 return bus_node_with_object_manager(bus, n->parent);
820 static bool bus_node_exists(
824 bool require_fallback) {
826 struct node_vtable *c;
827 struct node_callback *k;
833 /* Tests if there's anything attached directly to this node
834 * for the specified path */
836 LIST_FOREACH(callbacks, k, n->callbacks) {
837 if (require_fallback && !k->is_fallback)
843 LIST_FOREACH(vtables, c, n->vtables) {
844 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
846 if (require_fallback && !c->is_fallback)
849 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
851 if (bus->nodes_modified)
855 return !require_fallback && (n->enumerators || n->object_managers);
858 static int process_introspect(
862 bool require_fallback,
863 bool *found_object) {
865 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
866 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
867 _cleanup_set_free_free_ Set *s = NULL;
868 const char *previous_interface = NULL;
869 struct introspect intro;
870 struct node_vtable *c;
877 assert(found_object);
879 r = get_child_nodes(bus, m->path, n, &s, &error);
881 return bus_maybe_reply_error(m, r, &error);
882 if (bus->nodes_modified)
885 r = introspect_begin(&intro, bus->trusted);
889 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
893 empty = set_isempty(s);
895 LIST_FOREACH(vtables, c, n->vtables) {
896 if (require_fallback && !c->is_fallback)
899 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
901 r = bus_maybe_reply_error(m, r, &error);
904 if (bus->nodes_modified) {
913 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
916 if (!streq_ptr(previous_interface, c->interface)) {
918 if (previous_interface)
919 fputs(" </interface>\n", intro.f);
921 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
924 r = introspect_write_interface(&intro, c->vtable);
928 previous_interface = c->interface;
931 if (previous_interface)
932 fputs(" </interface>\n", intro.f);
935 /* Nothing?, let's see if we exist at all, and if not
936 * refuse to do anything */
937 r = bus_node_exists(bus, n, m->path, require_fallback);
940 if (bus->nodes_modified)
946 *found_object = true;
948 r = introspect_write_child_nodes(&intro, s, m->path);
952 r = introspect_finish(&intro, bus, m, &reply);
956 r = sd_bus_send(bus, reply, NULL);
963 introspect_free(&intro);
967 static int object_manager_serialize_path(
969 sd_bus_message *reply,
972 bool require_fallback,
973 sd_bus_error *error) {
975 const char *previous_interface = NULL;
976 bool found_something = false;
977 struct node_vtable *i;
987 n = hashmap_get(bus->nodes, prefix);
991 LIST_FOREACH(vtables, i, n->vtables) {
994 if (require_fallback && !i->is_fallback)
997 r = node_vtable_get_userdata(bus, path, i, &u, error);
1000 if (bus->nodes_modified)
1005 if (!found_something) {
1007 /* Open the object part */
1009 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1013 r = sd_bus_message_append(reply, "o", path);
1017 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1021 found_something = true;
1024 if (!streq_ptr(previous_interface, i->interface)) {
1026 /* Maybe close the previous interface part */
1028 if (previous_interface) {
1029 r = sd_bus_message_close_container(reply);
1033 r = sd_bus_message_close_container(reply);
1038 /* Open the new interface part */
1040 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1044 r = sd_bus_message_append(reply, "s", i->interface);
1048 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1053 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1056 if (bus->nodes_modified)
1059 previous_interface = i->interface;
1062 if (previous_interface) {
1063 r = sd_bus_message_close_container(reply);
1067 r = sd_bus_message_close_container(reply);
1072 if (found_something) {
1073 r = sd_bus_message_close_container(reply);
1077 r = sd_bus_message_close_container(reply);
1085 static int object_manager_serialize_path_and_fallbacks(
1087 sd_bus_message *reply,
1089 sd_bus_error *error) {
1099 /* First, add all vtables registered for this path */
1100 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1103 if (bus->nodes_modified)
1106 /* Second, add fallback vtables registered for any of the prefixes */
1107 prefix = alloca(strlen(path) + 1);
1108 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1109 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1112 if (bus->nodes_modified)
1119 static int process_get_managed_objects(
1123 bool require_fallback,
1124 bool *found_object) {
1126 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1127 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1128 _cleanup_set_free_free_ Set *s = NULL;
1135 assert(found_object);
1137 if (!bus_node_with_object_manager(bus, n))
1140 r = get_child_nodes(bus, m->path, n, &s, &error);
1143 if (bus->nodes_modified)
1146 r = sd_bus_message_new_method_return(m, &reply);
1150 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1154 empty = set_isempty(s);
1156 struct node_vtable *c;
1158 /* Hmm, so we have no children? Then let's check
1159 * whether we exist at all, i.e. whether at least one
1162 LIST_FOREACH(vtables, c, n->vtables) {
1164 if (require_fallback && !c->is_fallback)
1182 SET_FOREACH(path, s, i) {
1183 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1187 if (bus->nodes_modified)
1192 r = sd_bus_message_close_container(reply);
1196 r = sd_bus_send(bus, reply, NULL);
1203 static int object_find_and_run(
1207 bool require_fallback,
1208 bool *found_object) {
1211 struct vtable_member vtable_key, *v;
1217 assert(found_object);
1219 n = hashmap_get(bus->nodes, p);
1223 /* First, try object callbacks */
1224 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1227 if (bus->nodes_modified)
1230 if (!m->interface || !m->member)
1233 /* Then, look for a known method */
1234 vtable_key.path = (char*) p;
1235 vtable_key.interface = m->interface;
1236 vtable_key.member = m->member;
1238 v = hashmap_get(bus->vtable_methods, &vtable_key);
1240 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1243 if (bus->nodes_modified)
1247 /* Then, look for a known property */
1248 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1251 get = streq(m->member, "Get");
1253 if (get || streq(m->member, "Set")) {
1255 r = sd_bus_message_rewind(m, true);
1259 vtable_key.path = (char*) p;
1261 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1263 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1265 v = hashmap_get(bus->vtable_properties, &vtable_key);
1267 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1272 } else if (streq(m->member, "GetAll")) {
1275 r = sd_bus_message_rewind(m, true);
1279 r = sd_bus_message_read(m, "s", &iface);
1281 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1286 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1291 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1293 if (!isempty(sd_bus_message_get_signature(m, true)))
1294 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1296 r = process_introspect(bus, m, n, require_fallback, found_object);
1300 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1302 if (!isempty(sd_bus_message_get_signature(m, true)))
1303 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1305 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1310 if (bus->nodes_modified)
1313 if (!*found_object) {
1314 r = bus_node_exists(bus, n, m->path, require_fallback);
1318 *found_object = true;
1324 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1327 bool found_object = false;
1332 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1335 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1338 if (hashmap_isempty(bus->nodes))
1341 /* Never respond to broadcast messages */
1342 if (bus->bus_client && !m->destination)
1348 pl = strlen(m->path);
1352 bus->nodes_modified = false;
1354 r = object_find_and_run(bus, m, m->path, false, &found_object);
1358 /* Look for fallback prefixes */
1359 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1361 if (bus->nodes_modified)
1364 r = object_find_and_run(bus, m, prefix, true, &found_object);
1369 } while (bus->nodes_modified);
1374 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1375 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1376 r = sd_bus_reply_method_errorf(
1378 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1379 "Unknown property or interface.");
1381 r = sd_bus_reply_method_errorf(
1383 SD_BUS_ERROR_UNKNOWN_METHOD,
1384 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1392 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1393 struct node *n, *parent;
1395 _cleanup_free_ char *s = NULL;
1401 assert(path[0] == '/');
1403 n = hashmap_get(bus->nodes, path);
1407 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1415 if (streq(path, "/"))
1418 e = strrchr(path, '/');
1421 p = strndupa(path, MAX(1, path - e));
1423 parent = bus_node_allocate(bus, p);
1428 n = new0(struct node, 1);
1434 s = NULL; /* do not free */
1436 r = hashmap_put(bus->nodes, n->path, n);
1444 LIST_PREPEND(siblings, parent->child, n);
1449 void bus_node_gc(sd_bus *b, struct node *n) {
1462 assert(hashmap_remove(b->nodes, n->path) == n);
1465 LIST_REMOVE(siblings, n->parent->child, n);
1468 bus_node_gc(b, n->parent);
1472 static int bus_add_object(
1477 sd_bus_message_handler_t callback,
1484 assert_return(bus, -EINVAL);
1485 assert_return(object_path_is_valid(path), -EINVAL);
1486 assert_return(callback, -EINVAL);
1487 assert_return(!bus_pid_changed(bus), -ECHILD);
1489 n = bus_node_allocate(bus, path);
1493 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1499 s->node_callback.callback = callback;
1500 s->node_callback.is_fallback = fallback;
1502 s->node_callback.node = n;
1503 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1504 bus->nodes_modified = true;
1512 sd_bus_slot_unref(s);
1513 bus_node_gc(bus, n);
1518 _public_ int sd_bus_add_object(
1522 sd_bus_message_handler_t callback,
1525 return bus_add_object(bus, slot, false, path, callback, userdata);
1528 _public_ int sd_bus_add_fallback(
1532 sd_bus_message_handler_t callback,
1535 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1538 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1539 const struct vtable_member *m = a;
1540 uint8_t hash_key2[HASH_KEY_SIZE];
1545 ret = string_hash_func(m->path, hash_key);
1547 /* Use a slightly different hash key for the interface */
1548 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1550 ret ^= string_hash_func(m->interface, hash_key2);
1552 /* And an even different one for the member */
1554 ret ^= string_hash_func(m->member, hash_key2);
1559 static int vtable_member_compare_func(const void *a, const void *b) {
1560 const struct vtable_member *x = a, *y = b;
1566 r = strcmp(x->path, y->path);
1570 r = strcmp(x->interface, y->interface);
1574 return strcmp(x->member, y->member);
1577 static int add_object_vtable_internal(
1581 const char *interface,
1582 const sd_bus_vtable *vtable,
1584 sd_bus_object_find_t find,
1587 sd_bus_slot *s = NULL;
1588 struct node_vtable *i, *existing = NULL;
1589 const sd_bus_vtable *v;
1593 assert_return(bus, -EINVAL);
1594 assert_return(object_path_is_valid(path), -EINVAL);
1595 assert_return(interface_name_is_valid(interface), -EINVAL);
1596 assert_return(vtable, -EINVAL);
1597 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1598 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1599 assert_return(!bus_pid_changed(bus), -ECHILD);
1600 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1601 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1602 !streq(interface, "org.freedesktop.DBus.Peer") &&
1603 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1605 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1609 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1613 n = bus_node_allocate(bus, path);
1617 LIST_FOREACH(vtables, i, n->vtables) {
1618 if (i->is_fallback != fallback) {
1623 if (streq(i->interface, interface)) {
1625 if (i->vtable == vtable) {
1634 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1640 s->node_vtable.is_fallback = fallback;
1641 s->node_vtable.vtable = vtable;
1642 s->node_vtable.find = find;
1644 s->node_vtable.interface = strdup(interface);
1645 if (!s->node_vtable.interface) {
1650 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1654 case _SD_BUS_VTABLE_METHOD: {
1655 struct vtable_member *m;
1657 if (!member_name_is_valid(v->x.method.member) ||
1658 !signature_is_valid(strempty(v->x.method.signature), false) ||
1659 !signature_is_valid(strempty(v->x.method.result), false) ||
1660 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1661 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1666 m = new0(struct vtable_member, 1);
1672 m->parent = &s->node_vtable;
1674 m->interface = s->node_vtable.interface;
1675 m->member = v->x.method.member;
1678 r = hashmap_put(bus->vtable_methods, m, m);
1687 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1689 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1696 case _SD_BUS_VTABLE_PROPERTY: {
1697 struct vtable_member *m;
1699 if (!member_name_is_valid(v->x.property.member) ||
1700 !signature_is_single(v->x.property.signature, false) ||
1701 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1702 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1703 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1704 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1709 m = new0(struct vtable_member, 1);
1715 m->parent = &s->node_vtable;
1717 m->interface = s->node_vtable.interface;
1718 m->member = v->x.property.member;
1721 r = hashmap_put(bus->vtable_properties, m, m);
1730 case _SD_BUS_VTABLE_SIGNAL:
1732 if (!member_name_is_valid(v->x.signal.member) ||
1733 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1734 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1747 s->node_vtable.node = n;
1748 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1749 bus->nodes_modified = true;
1757 sd_bus_slot_unref(s);
1758 bus_node_gc(bus, n);
1763 _public_ int sd_bus_add_object_vtable(
1767 const char *interface,
1768 const sd_bus_vtable *vtable,
1771 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1774 _public_ int sd_bus_add_fallback_vtable(
1778 const char *interface,
1779 const sd_bus_vtable *vtable,
1780 sd_bus_object_find_t find,
1783 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1786 _public_ int sd_bus_add_node_enumerator(
1790 sd_bus_node_enumerator_t callback,
1797 assert_return(bus, -EINVAL);
1798 assert_return(object_path_is_valid(path), -EINVAL);
1799 assert_return(callback, -EINVAL);
1800 assert_return(!bus_pid_changed(bus), -ECHILD);
1802 n = bus_node_allocate(bus, path);
1806 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1812 s->node_enumerator.callback = callback;
1814 s->node_enumerator.node = n;
1815 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1816 bus->nodes_modified = true;
1824 sd_bus_slot_unref(s);
1825 bus_node_gc(bus, n);
1830 static int emit_properties_changed_on_interface(
1834 const char *interface,
1835 bool require_fallback,
1836 bool *found_interface,
1839 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1840 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1841 bool has_invalidating = false, has_changing = false;
1842 struct vtable_member key = {};
1843 struct node_vtable *c;
1853 assert(found_interface);
1855 n = hashmap_get(bus->nodes, prefix);
1859 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1863 r = sd_bus_message_append(m, "s", interface);
1867 r = sd_bus_message_open_container(m, 'a', "{sv}");
1872 key.interface = interface;
1874 LIST_FOREACH(vtables, c, n->vtables) {
1875 if (require_fallback && !c->is_fallback)
1878 if (!streq(c->interface, interface))
1881 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1884 if (bus->nodes_modified)
1889 *found_interface = true;
1892 /* If the caller specified a list of
1893 * properties we include exactly those in the
1894 * PropertiesChanged message */
1896 STRV_FOREACH(property, names) {
1897 struct vtable_member *v;
1899 assert_return(member_name_is_valid(*property), -EINVAL);
1901 key.member = *property;
1902 v = hashmap_get(bus->vtable_properties, &key);
1906 /* If there are two vtables for the same
1907 * interface, let's handle this property when
1908 * we come to that vtable. */
1912 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1913 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1915 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1917 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1918 has_invalidating = true;
1922 has_changing = true;
1924 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1927 if (bus->nodes_modified)
1931 const sd_bus_vtable *v;
1933 /* If the caller specified no properties list
1934 * we include all properties that are marked
1935 * as changing in the message. */
1937 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1938 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1941 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1944 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1945 has_invalidating = true;
1949 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1952 has_changing = true;
1954 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1957 if (bus->nodes_modified)
1963 if (!has_invalidating && !has_changing)
1966 r = sd_bus_message_close_container(m);
1970 r = sd_bus_message_open_container(m, 'a', "s");
1974 if (has_invalidating) {
1975 LIST_FOREACH(vtables, c, n->vtables) {
1976 if (require_fallback && !c->is_fallback)
1979 if (!streq(c->interface, interface))
1982 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1985 if (bus->nodes_modified)
1991 STRV_FOREACH(property, names) {
1992 struct vtable_member *v;
1994 key.member = *property;
1995 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1996 assert(c == v->parent);
1998 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2001 r = sd_bus_message_append(m, "s", *property);
2006 const sd_bus_vtable *v;
2008 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2009 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2012 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2015 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2018 r = sd_bus_message_append(m, "s", v->x.property.member);
2026 r = sd_bus_message_close_container(m);
2030 r = sd_bus_send(bus, m, NULL);
2037 _public_ int sd_bus_emit_properties_changed_strv(
2040 const char *interface,
2043 BUS_DONT_DESTROY(bus);
2044 bool found_interface = false;
2048 assert_return(bus, -EINVAL);
2049 assert_return(object_path_is_valid(path), -EINVAL);
2050 assert_return(interface_name_is_valid(interface), -EINVAL);
2051 assert_return(!bus_pid_changed(bus), -ECHILD);
2053 if (!BUS_IS_OPEN(bus->state))
2056 /* A non-NULL but empty names list means nothing needs to be
2057 generated. A NULL list OTOH indicates that all properties
2058 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2059 included in the PropertiesChanged message. */
2060 if (names && names[0] == NULL)
2064 bus->nodes_modified = false;
2066 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2069 if (bus->nodes_modified)
2072 prefix = alloca(strlen(path) + 1);
2073 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2074 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2077 if (bus->nodes_modified)
2081 } while (bus->nodes_modified);
2083 return found_interface ? 0 : -ENOENT;
2086 _public_ int sd_bus_emit_properties_changed(
2089 const char *interface,
2090 const char *name, ...) {
2094 assert_return(bus, -EINVAL);
2095 assert_return(object_path_is_valid(path), -EINVAL);
2096 assert_return(interface_name_is_valid(interface), -EINVAL);
2097 assert_return(!bus_pid_changed(bus), -ECHILD);
2099 if (!BUS_IS_OPEN(bus->state))
2105 names = strv_from_stdarg_alloca(name);
2107 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2110 static int interfaces_added_append_one_prefix(
2115 const char *interface,
2116 bool require_fallback) {
2118 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2119 bool found_interface = false;
2120 struct node_vtable *c;
2131 n = hashmap_get(bus->nodes, prefix);
2135 LIST_FOREACH(vtables, c, n->vtables) {
2136 if (require_fallback && !c->is_fallback)
2139 if (!streq(c->interface, interface))
2142 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2145 if (bus->nodes_modified)
2150 if (!found_interface) {
2151 r = sd_bus_message_append_basic(m, 's', interface);
2155 r = sd_bus_message_open_container(m, 'a', "{sv}");
2159 found_interface = true;
2162 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2165 if (bus->nodes_modified)
2169 if (found_interface) {
2170 r = sd_bus_message_close_container(m);
2175 return found_interface;
2178 static int interfaces_added_append_one(
2182 const char *interface) {
2192 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2195 if (bus->nodes_modified)
2198 prefix = alloca(strlen(path) + 1);
2199 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2200 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2203 if (bus->nodes_modified)
2210 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2211 BUS_DONT_DESTROY(bus);
2213 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2217 assert_return(bus, -EINVAL);
2218 assert_return(object_path_is_valid(path), -EINVAL);
2219 assert_return(!bus_pid_changed(bus), -ECHILD);
2221 if (!BUS_IS_OPEN(bus->state))
2224 if (strv_isempty(interfaces))
2228 bus->nodes_modified = false;
2229 m = sd_bus_message_unref(m);
2231 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2235 r = sd_bus_message_append_basic(m, 'o', path);
2239 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2243 STRV_FOREACH(i, interfaces) {
2244 assert_return(interface_name_is_valid(*i), -EINVAL);
2246 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2250 r = interfaces_added_append_one(bus, m, path, *i);
2254 if (bus->nodes_modified)
2257 r = sd_bus_message_close_container(m);
2262 if (bus->nodes_modified)
2265 r = sd_bus_message_close_container(m);
2269 } while (bus->nodes_modified);
2271 return sd_bus_send(bus, m, NULL);
2274 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2277 assert_return(bus, -EINVAL);
2278 assert_return(object_path_is_valid(path), -EINVAL);
2279 assert_return(!bus_pid_changed(bus), -ECHILD);
2281 if (!BUS_IS_OPEN(bus->state))
2284 interfaces = strv_from_stdarg_alloca(interface);
2286 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2289 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2290 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2293 assert_return(bus, -EINVAL);
2294 assert_return(object_path_is_valid(path), -EINVAL);
2295 assert_return(!bus_pid_changed(bus), -ECHILD);
2297 if (!BUS_IS_OPEN(bus->state))
2300 if (strv_isempty(interfaces))
2303 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2307 r = sd_bus_message_append_basic(m, 'o', path);
2311 r = sd_bus_message_append_strv(m, interfaces);
2315 return sd_bus_send(bus, m, NULL);
2318 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2321 assert_return(bus, -EINVAL);
2322 assert_return(object_path_is_valid(path), -EINVAL);
2323 assert_return(!bus_pid_changed(bus), -ECHILD);
2325 if (!BUS_IS_OPEN(bus->state))
2328 interfaces = strv_from_stdarg_alloca(interface);
2330 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2333 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2338 assert_return(bus, -EINVAL);
2339 assert_return(object_path_is_valid(path), -EINVAL);
2340 assert_return(!bus_pid_changed(bus), -ECHILD);
2342 n = bus_node_allocate(bus, path);
2346 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2352 s->node_object_manager.node = n;
2353 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2354 bus->nodes_modified = true;
2362 sd_bus_slot_unref(s);
2363 bus_node_gc(bus, n);