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 = s;
54 r = c->find(bus, path, c->interface, u, &u, error);
55 bus->current_slot = NULL;
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;
119 if (bus->nodes_modified)
122 bus->current_slot = container_of(c, sd_bus_slot, node_enumerator);
123 r = c->callback(bus, prefix, bus->current_slot->userdata, &children, error);
124 bus->current_slot = NULL;
128 if (sd_bus_error_is_set(error))
129 return -sd_bus_error_get_errno(error);
131 STRV_FOREACH(k, children) {
137 if (!object_path_is_valid(*k)){
143 if (!object_path_startswith(*k, prefix)) {
148 r = set_consume(s, *k);
161 static int add_subtree_to_set(
166 sd_bus_error *error) {
176 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
179 if (bus->nodes_modified)
182 LIST_FOREACH(siblings, i, n->child) {
185 if (!object_path_startswith(i->path, prefix))
192 r = set_consume(s, t);
193 if (r < 0 && r != -EEXIST)
196 r = add_subtree_to_set(bus, prefix, i, s, error);
199 if (bus->nodes_modified)
206 static int get_child_nodes(
211 sd_bus_error *error) {
221 s = set_new(string_hash_func, string_compare_func);
225 r = add_subtree_to_set(bus, prefix, n, s, error);
235 static int node_callbacks_run(
238 struct node_callback *first,
239 bool require_fallback,
240 bool *found_object) {
242 struct node_callback *c;
247 assert(found_object);
249 LIST_FOREACH(callbacks, c, first) {
250 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
252 if (bus->nodes_modified)
255 if (require_fallback && !c->is_fallback)
258 *found_object = true;
260 if (c->last_iteration == bus->iteration_counter)
263 c->last_iteration = bus->iteration_counter;
265 r = sd_bus_message_rewind(m, true);
269 bus->current_slot = container_of(c, sd_bus_slot, node_callback);
270 r = c->callback(bus, m, bus->current_slot->userdata, &error_buffer);
271 bus->current_slot = NULL;
273 r = bus_maybe_reply_error(m, r, &error_buffer);
281 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
283 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
284 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
293 /* If the entire bus is trusted let's grant access */
297 /* If the member is marked UNPRIVILEGED let's grant access */
298 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
301 /* If we are not connected to kdbus we cannot retrieve the
302 * effective capability set without race. Since we need this
303 * for a security decision we cannot use racy data, hence
304 * don't request it. */
306 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
308 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
312 /* Check have the caller has the requested capability
313 * set. Note that the flags value contains the capability
314 * number plus one, which we need to subtract here. We do this
315 * so that we have 0 as special value for "default
317 cap = CAPABILITY_SHIFT(c->vtable->flags);
319 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
325 r = sd_bus_creds_has_effective_cap(creds, cap);
329 /* Caller has same UID as us, then let's grant access */
330 r = sd_bus_creds_get_uid(creds, &uid);
336 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
339 static int method_callbacks_run(
342 struct vtable_member *c,
343 bool require_fallback,
344 bool *found_object) {
346 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
347 const char *signature;
354 assert(found_object);
356 if (require_fallback && !c->parent->is_fallback)
359 r = check_access(bus, m, c, &error);
361 return bus_maybe_reply_error(m, r, &error);
363 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
365 return bus_maybe_reply_error(m, r, &error);
366 if (bus->nodes_modified)
369 *found_object = true;
371 if (c->last_iteration == bus->iteration_counter)
374 c->last_iteration = bus->iteration_counter;
376 r = sd_bus_message_rewind(m, true);
380 signature = sd_bus_message_get_signature(m, true);
384 if (!streq(strempty(c->vtable->x.method.signature), signature))
385 return sd_bus_reply_method_errorf(
387 SD_BUS_ERROR_INVALID_ARGS,
388 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
389 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
391 /* Keep track what the signature of the reply to this message
392 * should be, so that this can be enforced when sealing the
394 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
396 if (c->vtable->x.method.handler) {
398 bus->current_slot = container_of(c->parent, sd_bus_slot, node_vtable);
399 r = c->vtable->x.method.handler(bus, m, u, &error);
400 bus->current_slot = NULL;
402 return bus_maybe_reply_error(m, r, &error);
405 /* If the method callback is NULL, make this a successful NOP */
406 r = sd_bus_reply_method_return(m, NULL);
413 static int invoke_property_get(
416 const sd_bus_vtable *v,
418 const char *interface,
419 const char *property,
420 sd_bus_message *reply,
422 sd_bus_error *error) {
435 if (v->x.property.get) {
437 bus->current_slot = slot;
438 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
439 bus->current_slot = NULL;
443 if (sd_bus_error_is_set(error))
444 return -sd_bus_error_get_errno(error);
448 /* Automatic handling if no callback is defined. */
450 if (streq(v->x.property.signature, "as"))
451 return sd_bus_message_append_strv(reply, *(char***) userdata);
453 assert(signature_is_single(v->x.property.signature, false));
454 assert(bus_type_is_basic(v->x.property.signature[0]));
456 switch (v->x.property.signature[0]) {
458 case SD_BUS_TYPE_STRING:
459 case SD_BUS_TYPE_SIGNATURE:
460 p = strempty(*(char**) userdata);
463 case SD_BUS_TYPE_OBJECT_PATH:
464 p = *(char**) userdata;
473 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
476 static int invoke_property_set(
479 const sd_bus_vtable *v,
481 const char *interface,
482 const char *property,
483 sd_bus_message *value,
485 sd_bus_error *error) {
497 if (v->x.property.set) {
499 bus->current_slot = slot;
500 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
501 bus->current_slot = NULL;
505 if (sd_bus_error_is_set(error))
506 return -sd_bus_error_get_errno(error);
510 /* Automatic handling if no callback is defined. */
512 assert(signature_is_single(v->x.property.signature, false));
513 assert(bus_type_is_basic(v->x.property.signature[0]));
515 switch (v->x.property.signature[0]) {
517 case SD_BUS_TYPE_STRING:
518 case SD_BUS_TYPE_OBJECT_PATH:
519 case SD_BUS_TYPE_SIGNATURE: {
523 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
531 free(*(char**) userdata);
532 *(char**) userdata = n;
538 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
548 static int property_get_set_callbacks_run(
551 struct vtable_member *c,
552 bool require_fallback,
554 bool *found_object) {
556 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
557 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
565 assert(found_object);
567 if (require_fallback && !c->parent->is_fallback)
570 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
572 return bus_maybe_reply_error(m, r, &error);
573 if (bus->nodes_modified)
576 slot = container_of(c->parent, sd_bus_slot, node_vtable);
578 *found_object = true;
580 r = sd_bus_message_new_method_return(m, &reply);
585 /* Note that we do not protect against reexecution
586 * here (using the last_iteration check, see below),
587 * should the node tree have changed and we got called
588 * again. We assume that property Get() calls are
589 * ultimately without side-effects or if they aren't
590 * then at least idempotent. */
592 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
596 /* Note that we do not do an access check here. Read
597 * access to properties is always unrestricted, since
598 * PropertiesChanged signals broadcast contents
601 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
603 return bus_maybe_reply_error(m, r, &error);
605 if (bus->nodes_modified)
608 r = sd_bus_message_close_container(reply);
613 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
614 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
616 /* Avoid that we call the set routine more than once
617 * if the processing of this message got restarted
618 * because the node tree changed. */
619 if (c->last_iteration == bus->iteration_counter)
622 c->last_iteration = bus->iteration_counter;
624 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
628 r = check_access(bus, m, c, &error);
630 return bus_maybe_reply_error(m, r, &error);
632 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
634 return bus_maybe_reply_error(m, r, &error);
636 if (bus->nodes_modified)
639 r = sd_bus_message_exit_container(m);
644 r = sd_bus_send(bus, reply, NULL);
651 static int vtable_append_one_property(
653 sd_bus_message *reply,
655 struct node_vtable *c,
656 const sd_bus_vtable *v,
658 sd_bus_error *error) {
669 r = sd_bus_message_open_container(reply, 'e', "sv");
673 r = sd_bus_message_append(reply, "s", v->x.property.member);
677 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
681 slot = container_of(c, sd_bus_slot, node_vtable);
683 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
686 if (bus->nodes_modified)
689 r = sd_bus_message_close_container(reply);
693 r = sd_bus_message_close_container(reply);
700 static int vtable_append_all_properties(
702 sd_bus_message *reply,
704 struct node_vtable *c,
706 sd_bus_error *error) {
708 const sd_bus_vtable *v;
716 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
719 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
720 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
723 if (v->flags & SD_BUS_VTABLE_HIDDEN)
726 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
729 if (bus->nodes_modified)
736 static int property_get_all_callbacks_run(
739 struct node_vtable *first,
740 bool require_fallback,
742 bool *found_object) {
744 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
745 struct node_vtable *c;
746 bool found_interface;
751 assert(found_object);
753 r = sd_bus_message_new_method_return(m, &reply);
757 r = sd_bus_message_open_container(reply, 'a', "{sv}");
761 found_interface = !iface ||
762 streq(iface, "org.freedesktop.DBus.Properties") ||
763 streq(iface, "org.freedesktop.DBus.Peer") ||
764 streq(iface, "org.freedesktop.DBus.Introspectable");
766 LIST_FOREACH(vtables, c, first) {
767 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
770 if (require_fallback && !c->is_fallback)
773 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
775 return bus_maybe_reply_error(m, r, &error);
776 if (bus->nodes_modified)
781 *found_object = true;
783 if (iface && !streq(c->interface, iface))
785 found_interface = true;
787 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
789 return bus_maybe_reply_error(m, r, &error);
790 if (bus->nodes_modified)
794 if (!found_interface) {
795 r = sd_bus_reply_method_errorf(
797 SD_BUS_ERROR_UNKNOWN_INTERFACE,
798 "Unknown interface '%s'.", iface);
805 r = sd_bus_message_close_container(reply);
809 r = sd_bus_send(bus, reply, NULL);
816 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
820 if (n->object_managers)
824 return bus_node_with_object_manager(bus, n->parent);
829 static bool bus_node_exists(
833 bool require_fallback) {
835 struct node_vtable *c;
836 struct node_callback *k;
842 /* Tests if there's anything attached directly to this node
843 * for the specified path */
845 LIST_FOREACH(callbacks, k, n->callbacks) {
846 if (require_fallback && !k->is_fallback)
852 LIST_FOREACH(vtables, c, n->vtables) {
853 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
855 if (require_fallback && !c->is_fallback)
858 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
860 if (bus->nodes_modified)
864 return !require_fallback && (n->enumerators || n->object_managers);
867 static int process_introspect(
871 bool require_fallback,
872 bool *found_object) {
874 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
875 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
876 _cleanup_set_free_free_ Set *s = NULL;
877 const char *previous_interface = NULL;
878 struct introspect intro;
879 struct node_vtable *c;
886 assert(found_object);
888 r = get_child_nodes(bus, m->path, n, &s, &error);
890 return bus_maybe_reply_error(m, r, &error);
891 if (bus->nodes_modified)
894 r = introspect_begin(&intro, bus->trusted);
898 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
902 empty = set_isempty(s);
904 LIST_FOREACH(vtables, c, n->vtables) {
905 if (require_fallback && !c->is_fallback)
908 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
910 r = bus_maybe_reply_error(m, r, &error);
913 if (bus->nodes_modified) {
922 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
925 if (!streq_ptr(previous_interface, c->interface)) {
927 if (previous_interface)
928 fputs(" </interface>\n", intro.f);
930 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
933 r = introspect_write_interface(&intro, c->vtable);
937 previous_interface = c->interface;
940 if (previous_interface)
941 fputs(" </interface>\n", intro.f);
944 /* Nothing?, let's see if we exist at all, and if not
945 * refuse to do anything */
946 r = bus_node_exists(bus, n, m->path, require_fallback);
949 if (bus->nodes_modified)
955 *found_object = true;
957 r = introspect_write_child_nodes(&intro, s, m->path);
961 r = introspect_finish(&intro, bus, m, &reply);
965 r = sd_bus_send(bus, reply, NULL);
972 introspect_free(&intro);
976 static int object_manager_serialize_path(
978 sd_bus_message *reply,
981 bool require_fallback,
982 sd_bus_error *error) {
984 const char *previous_interface = NULL;
985 bool found_something = false;
986 struct node_vtable *i;
996 n = hashmap_get(bus->nodes, prefix);
1000 LIST_FOREACH(vtables, i, n->vtables) {
1003 if (require_fallback && !i->is_fallback)
1006 r = node_vtable_get_userdata(bus, path, i, &u, error);
1009 if (bus->nodes_modified)
1014 if (!found_something) {
1016 /* Open the object part */
1018 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1022 r = sd_bus_message_append(reply, "o", path);
1026 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1030 found_something = true;
1033 if (!streq_ptr(previous_interface, i->interface)) {
1035 /* Maybe close the previous interface part */
1037 if (previous_interface) {
1038 r = sd_bus_message_close_container(reply);
1042 r = sd_bus_message_close_container(reply);
1047 /* Open the new interface part */
1049 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1053 r = sd_bus_message_append(reply, "s", i->interface);
1057 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1062 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1065 if (bus->nodes_modified)
1068 previous_interface = i->interface;
1071 if (previous_interface) {
1072 r = sd_bus_message_close_container(reply);
1076 r = sd_bus_message_close_container(reply);
1081 if (found_something) {
1082 r = sd_bus_message_close_container(reply);
1086 r = sd_bus_message_close_container(reply);
1094 static int object_manager_serialize_path_and_fallbacks(
1096 sd_bus_message *reply,
1098 sd_bus_error *error) {
1108 /* First, add all vtables registered for this path */
1109 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1112 if (bus->nodes_modified)
1115 /* Second, add fallback vtables registered for any of the prefixes */
1116 prefix = alloca(strlen(path) + 1);
1117 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1118 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1121 if (bus->nodes_modified)
1128 static int process_get_managed_objects(
1132 bool require_fallback,
1133 bool *found_object) {
1135 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1136 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1137 _cleanup_set_free_free_ Set *s = NULL;
1144 assert(found_object);
1146 if (!bus_node_with_object_manager(bus, n))
1149 r = get_child_nodes(bus, m->path, n, &s, &error);
1152 if (bus->nodes_modified)
1155 r = sd_bus_message_new_method_return(m, &reply);
1159 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1163 empty = set_isempty(s);
1165 struct node_vtable *c;
1167 /* Hmm, so we have no children? Then let's check
1168 * whether we exist at all, i.e. whether at least one
1171 LIST_FOREACH(vtables, c, n->vtables) {
1173 if (require_fallback && !c->is_fallback)
1191 SET_FOREACH(path, s, i) {
1192 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1196 if (bus->nodes_modified)
1201 r = sd_bus_message_close_container(reply);
1205 r = sd_bus_send(bus, reply, NULL);
1212 static int object_find_and_run(
1216 bool require_fallback,
1217 bool *found_object) {
1220 struct vtable_member vtable_key, *v;
1226 assert(found_object);
1228 n = hashmap_get(bus->nodes, p);
1232 /* First, try object callbacks */
1233 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1236 if (bus->nodes_modified)
1239 if (!m->interface || !m->member)
1242 /* Then, look for a known method */
1243 vtable_key.path = (char*) p;
1244 vtable_key.interface = m->interface;
1245 vtable_key.member = m->member;
1247 v = hashmap_get(bus->vtable_methods, &vtable_key);
1249 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1252 if (bus->nodes_modified)
1256 /* Then, look for a known property */
1257 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1260 get = streq(m->member, "Get");
1262 if (get || streq(m->member, "Set")) {
1264 r = sd_bus_message_rewind(m, true);
1268 vtable_key.path = (char*) p;
1270 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1272 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1274 v = hashmap_get(bus->vtable_properties, &vtable_key);
1276 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1281 } else if (streq(m->member, "GetAll")) {
1284 r = sd_bus_message_rewind(m, true);
1288 r = sd_bus_message_read(m, "s", &iface);
1290 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1295 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1300 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
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_introspect(bus, m, n, require_fallback, found_object);
1309 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1311 if (!isempty(sd_bus_message_get_signature(m, true)))
1312 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1314 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1319 if (bus->nodes_modified)
1322 if (!*found_object) {
1323 r = bus_node_exists(bus, n, m->path, require_fallback);
1327 *found_object = true;
1333 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1336 bool found_object = false;
1341 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1344 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1347 if (hashmap_isempty(bus->nodes))
1350 /* Never respond to broadcast messages */
1351 if (bus->bus_client && !m->destination)
1357 pl = strlen(m->path);
1361 bus->nodes_modified = false;
1363 r = object_find_and_run(bus, m, m->path, false, &found_object);
1367 /* Look for fallback prefixes */
1368 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1370 if (bus->nodes_modified)
1373 r = object_find_and_run(bus, m, prefix, true, &found_object);
1378 } while (bus->nodes_modified);
1383 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1384 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1385 r = sd_bus_reply_method_errorf(
1387 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1388 "Unknown property or interface.");
1390 r = sd_bus_reply_method_errorf(
1392 SD_BUS_ERROR_UNKNOWN_METHOD,
1393 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1401 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1402 struct node *n, *parent;
1404 _cleanup_free_ char *s = NULL;
1410 assert(path[0] == '/');
1412 n = hashmap_get(bus->nodes, path);
1416 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1424 if (streq(path, "/"))
1427 e = strrchr(path, '/');
1430 p = strndupa(path, MAX(1, path - e));
1432 parent = bus_node_allocate(bus, p);
1437 n = new0(struct node, 1);
1443 s = NULL; /* do not free */
1445 r = hashmap_put(bus->nodes, n->path, n);
1453 LIST_PREPEND(siblings, parent->child, n);
1458 void bus_node_gc(sd_bus *b, struct node *n) {
1471 assert(hashmap_remove(b->nodes, n->path) == n);
1474 LIST_REMOVE(siblings, n->parent->child, n);
1477 bus_node_gc(b, n->parent);
1481 static int bus_add_object(
1486 sd_bus_message_handler_t callback,
1493 assert_return(bus, -EINVAL);
1494 assert_return(object_path_is_valid(path), -EINVAL);
1495 assert_return(callback, -EINVAL);
1496 assert_return(!bus_pid_changed(bus), -ECHILD);
1498 n = bus_node_allocate(bus, path);
1502 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1508 s->node_callback.callback = callback;
1509 s->node_callback.is_fallback = fallback;
1511 s->node_callback.node = n;
1512 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1513 bus->nodes_modified = true;
1521 sd_bus_slot_unref(s);
1522 bus_node_gc(bus, n);
1527 _public_ int sd_bus_add_object(
1531 sd_bus_message_handler_t callback,
1534 return bus_add_object(bus, slot, false, path, callback, userdata);
1537 _public_ int sd_bus_add_fallback(
1541 sd_bus_message_handler_t callback,
1544 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1547 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1548 const struct vtable_member *m = a;
1549 uint8_t hash_key2[HASH_KEY_SIZE];
1554 ret = string_hash_func(m->path, hash_key);
1556 /* Use a slightly different hash key for the interface */
1557 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1559 ret ^= string_hash_func(m->interface, hash_key2);
1561 /* And an even different one for the member */
1563 ret ^= string_hash_func(m->member, hash_key2);
1568 static int vtable_member_compare_func(const void *a, const void *b) {
1569 const struct vtable_member *x = a, *y = b;
1575 r = strcmp(x->path, y->path);
1579 r = strcmp(x->interface, y->interface);
1583 return strcmp(x->member, y->member);
1586 static int add_object_vtable_internal(
1590 const char *interface,
1591 const sd_bus_vtable *vtable,
1593 sd_bus_object_find_t find,
1597 struct node_vtable *i, *existing = NULL;
1598 const sd_bus_vtable *v;
1602 assert_return(bus, -EINVAL);
1603 assert_return(object_path_is_valid(path), -EINVAL);
1604 assert_return(interface_name_is_valid(interface), -EINVAL);
1605 assert_return(vtable, -EINVAL);
1606 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1607 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1608 assert_return(!bus_pid_changed(bus), -ECHILD);
1609 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1610 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1611 !streq(interface, "org.freedesktop.DBus.Peer") &&
1612 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1614 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1618 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1622 n = bus_node_allocate(bus, path);
1626 LIST_FOREACH(vtables, i, n->vtables) {
1627 if (i->is_fallback != fallback) {
1632 if (streq(i->interface, interface)) {
1634 if (i->vtable == vtable) {
1643 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1649 s->node_vtable.is_fallback = fallback;
1650 s->node_vtable.vtable = vtable;
1651 s->node_vtable.find = find;
1653 s->node_vtable.interface = strdup(interface);
1654 if (!s->node_vtable.interface) {
1659 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1663 case _SD_BUS_VTABLE_METHOD: {
1664 struct vtable_member *m;
1666 if (!member_name_is_valid(v->x.method.member) ||
1667 !signature_is_valid(strempty(v->x.method.signature), false) ||
1668 !signature_is_valid(strempty(v->x.method.result), false) ||
1669 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1670 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1675 m = new0(struct vtable_member, 1);
1681 m->parent = &s->node_vtable;
1683 m->interface = s->node_vtable.interface;
1684 m->member = v->x.method.member;
1687 r = hashmap_put(bus->vtable_methods, m, m);
1696 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1698 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1705 case _SD_BUS_VTABLE_PROPERTY: {
1706 struct vtable_member *m;
1708 if (!member_name_is_valid(v->x.property.member) ||
1709 !signature_is_single(v->x.property.signature, false) ||
1710 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1711 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1712 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1713 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1718 m = new0(struct vtable_member, 1);
1724 m->parent = &s->node_vtable;
1726 m->interface = s->node_vtable.interface;
1727 m->member = v->x.property.member;
1730 r = hashmap_put(bus->vtable_properties, m, m);
1739 case _SD_BUS_VTABLE_SIGNAL:
1741 if (!member_name_is_valid(v->x.signal.member) ||
1742 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1743 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1756 s->node_vtable.node = n;
1757 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1758 bus->nodes_modified = true;
1766 sd_bus_slot_unref(s);
1767 bus_node_gc(bus, n);
1772 _public_ int sd_bus_add_object_vtable(
1776 const char *interface,
1777 const sd_bus_vtable *vtable,
1780 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1783 _public_ int sd_bus_add_fallback_vtable(
1787 const char *interface,
1788 const sd_bus_vtable *vtable,
1789 sd_bus_object_find_t find,
1792 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1795 _public_ int sd_bus_add_node_enumerator(
1799 sd_bus_node_enumerator_t callback,
1806 assert_return(bus, -EINVAL);
1807 assert_return(object_path_is_valid(path), -EINVAL);
1808 assert_return(callback, -EINVAL);
1809 assert_return(!bus_pid_changed(bus), -ECHILD);
1811 n = bus_node_allocate(bus, path);
1815 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1821 s->node_enumerator.callback = callback;
1823 s->node_enumerator.node = n;
1824 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1825 bus->nodes_modified = true;
1833 sd_bus_slot_unref(s);
1834 bus_node_gc(bus, n);
1839 static int emit_properties_changed_on_interface(
1843 const char *interface,
1844 bool require_fallback,
1845 bool *found_interface,
1848 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1849 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1850 bool has_invalidating = false, has_changing = false;
1851 struct vtable_member key = {};
1852 struct node_vtable *c;
1862 assert(found_interface);
1864 n = hashmap_get(bus->nodes, prefix);
1868 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1872 r = sd_bus_message_append(m, "s", interface);
1876 r = sd_bus_message_open_container(m, 'a', "{sv}");
1881 key.interface = interface;
1883 LIST_FOREACH(vtables, c, n->vtables) {
1884 if (require_fallback && !c->is_fallback)
1887 if (!streq(c->interface, interface))
1890 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1893 if (bus->nodes_modified)
1898 *found_interface = true;
1901 /* If the caller specified a list of
1902 * properties we include exactly those in the
1903 * PropertiesChanged message */
1905 STRV_FOREACH(property, names) {
1906 struct vtable_member *v;
1908 assert_return(member_name_is_valid(*property), -EINVAL);
1910 key.member = *property;
1911 v = hashmap_get(bus->vtable_properties, &key);
1915 /* If there are two vtables for the same
1916 * interface, let's handle this property when
1917 * we come to that vtable. */
1921 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1922 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1924 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1926 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1927 has_invalidating = true;
1931 has_changing = true;
1933 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1936 if (bus->nodes_modified)
1940 const sd_bus_vtable *v;
1942 /* If the caller specified no properties list
1943 * we include all properties that are marked
1944 * as changing in the message. */
1946 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1947 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1950 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1953 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1954 has_invalidating = true;
1958 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1961 has_changing = true;
1963 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1966 if (bus->nodes_modified)
1972 if (!has_invalidating && !has_changing)
1975 r = sd_bus_message_close_container(m);
1979 r = sd_bus_message_open_container(m, 'a', "s");
1983 if (has_invalidating) {
1984 LIST_FOREACH(vtables, c, n->vtables) {
1985 if (require_fallback && !c->is_fallback)
1988 if (!streq(c->interface, interface))
1991 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1994 if (bus->nodes_modified)
2000 STRV_FOREACH(property, names) {
2001 struct vtable_member *v;
2003 key.member = *property;
2004 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2005 assert(c == v->parent);
2007 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2010 r = sd_bus_message_append(m, "s", *property);
2015 const sd_bus_vtable *v;
2017 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2018 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2021 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2024 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2027 r = sd_bus_message_append(m, "s", v->x.property.member);
2035 r = sd_bus_message_close_container(m);
2039 r = sd_bus_send(bus, m, NULL);
2046 _public_ int sd_bus_emit_properties_changed_strv(
2049 const char *interface,
2052 BUS_DONT_DESTROY(bus);
2053 bool found_interface = false;
2057 assert_return(bus, -EINVAL);
2058 assert_return(object_path_is_valid(path), -EINVAL);
2059 assert_return(interface_name_is_valid(interface), -EINVAL);
2060 assert_return(!bus_pid_changed(bus), -ECHILD);
2062 if (!BUS_IS_OPEN(bus->state))
2065 /* A non-NULL but empty names list means nothing needs to be
2066 generated. A NULL list OTOH indicates that all properties
2067 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2068 included in the PropertiesChanged message. */
2069 if (names && names[0] == NULL)
2073 bus->nodes_modified = false;
2075 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2078 if (bus->nodes_modified)
2081 prefix = alloca(strlen(path) + 1);
2082 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2083 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2086 if (bus->nodes_modified)
2090 } while (bus->nodes_modified);
2092 return found_interface ? 0 : -ENOENT;
2095 _public_ int sd_bus_emit_properties_changed(
2098 const char *interface,
2099 const char *name, ...) {
2103 assert_return(bus, -EINVAL);
2104 assert_return(object_path_is_valid(path), -EINVAL);
2105 assert_return(interface_name_is_valid(interface), -EINVAL);
2106 assert_return(!bus_pid_changed(bus), -ECHILD);
2108 if (!BUS_IS_OPEN(bus->state))
2114 names = strv_from_stdarg_alloca(name);
2116 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2119 static int interfaces_added_append_one_prefix(
2124 const char *interface,
2125 bool require_fallback) {
2127 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2128 bool found_interface = false;
2129 struct node_vtable *c;
2140 n = hashmap_get(bus->nodes, prefix);
2144 LIST_FOREACH(vtables, c, n->vtables) {
2145 if (require_fallback && !c->is_fallback)
2148 if (!streq(c->interface, interface))
2151 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2154 if (bus->nodes_modified)
2159 if (!found_interface) {
2160 r = sd_bus_message_append_basic(m, 's', interface);
2164 r = sd_bus_message_open_container(m, 'a', "{sv}");
2168 found_interface = true;
2171 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2174 if (bus->nodes_modified)
2178 if (found_interface) {
2179 r = sd_bus_message_close_container(m);
2184 return found_interface;
2187 static int interfaces_added_append_one(
2191 const char *interface) {
2201 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2204 if (bus->nodes_modified)
2207 prefix = alloca(strlen(path) + 1);
2208 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2209 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2212 if (bus->nodes_modified)
2219 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2220 BUS_DONT_DESTROY(bus);
2222 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2226 assert_return(bus, -EINVAL);
2227 assert_return(object_path_is_valid(path), -EINVAL);
2228 assert_return(!bus_pid_changed(bus), -ECHILD);
2230 if (!BUS_IS_OPEN(bus->state))
2233 if (strv_isempty(interfaces))
2237 bus->nodes_modified = false;
2238 m = sd_bus_message_unref(m);
2240 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2244 r = sd_bus_message_append_basic(m, 'o', path);
2248 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2252 STRV_FOREACH(i, interfaces) {
2253 assert_return(interface_name_is_valid(*i), -EINVAL);
2255 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2259 r = interfaces_added_append_one(bus, m, path, *i);
2263 if (bus->nodes_modified)
2266 r = sd_bus_message_close_container(m);
2271 if (bus->nodes_modified)
2274 r = sd_bus_message_close_container(m);
2278 } while (bus->nodes_modified);
2280 return sd_bus_send(bus, m, NULL);
2283 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2286 assert_return(bus, -EINVAL);
2287 assert_return(object_path_is_valid(path), -EINVAL);
2288 assert_return(!bus_pid_changed(bus), -ECHILD);
2290 if (!BUS_IS_OPEN(bus->state))
2293 interfaces = strv_from_stdarg_alloca(interface);
2295 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2298 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2299 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2302 assert_return(bus, -EINVAL);
2303 assert_return(object_path_is_valid(path), -EINVAL);
2304 assert_return(!bus_pid_changed(bus), -ECHILD);
2306 if (!BUS_IS_OPEN(bus->state))
2309 if (strv_isempty(interfaces))
2312 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2316 r = sd_bus_message_append_basic(m, 'o', path);
2320 r = sd_bus_message_append_strv(m, interfaces);
2324 return sd_bus_send(bus, m, NULL);
2327 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2330 assert_return(bus, -EINVAL);
2331 assert_return(object_path_is_valid(path), -EINVAL);
2332 assert_return(!bus_pid_changed(bus), -ECHILD);
2334 if (!BUS_IS_OPEN(bus->state))
2337 interfaces = strv_from_stdarg_alloca(interface);
2339 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2342 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2347 assert_return(bus, -EINVAL);
2348 assert_return(object_path_is_valid(path), -EINVAL);
2349 assert_return(!bus_pid_changed(bus), -ECHILD);
2351 n = bus_node_allocate(bus, path);
2355 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2361 s->node_object_manager.node = n;
2362 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2363 bus->nodes_modified = true;
2371 sd_bus_slot_unref(s);
2372 bus_node_gc(bus, n);