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"
31 #include "bus-objects.h"
34 static int node_vtable_get_userdata(
37 struct node_vtable *c,
39 sd_bus_error *error) {
50 r = c->find(bus, path, c->interface, u, &u, error);
53 if (sd_bus_error_is_set(error))
54 return -sd_bus_error_get_errno(error);
65 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
68 return (uint8_t*) u + p->x.property.offset;
71 static int vtable_property_get_userdata(
74 struct vtable_member *p,
76 sd_bus_error *error) {
86 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
89 if (bus->nodes_modified)
92 *userdata = vtable_property_convert_userdata(p->vtable, u);
96 static int add_enumerated_to_set(
99 struct node_enumerator *first,
101 sd_bus_error *error) {
103 struct node_enumerator *c;
110 LIST_FOREACH(enumerators, c, first) {
111 char **children = NULL, **k;
113 if (bus->nodes_modified)
116 r = c->callback(bus, prefix, c->userdata, &children, error);
119 if (sd_bus_error_is_set(error))
120 return -sd_bus_error_get_errno(error);
122 STRV_FOREACH(k, children) {
128 if (!object_path_is_valid(*k)){
134 if (!object_path_startswith(*k, prefix)) {
139 r = set_consume(s, *k);
152 static int add_subtree_to_set(
157 sd_bus_error *error) {
167 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
170 if (bus->nodes_modified)
173 LIST_FOREACH(siblings, i, n->child) {
176 if (!object_path_startswith(i->path, prefix))
183 r = set_consume(s, t);
184 if (r < 0 && r != -EEXIST)
187 r = add_subtree_to_set(bus, prefix, i, s, error);
190 if (bus->nodes_modified)
197 static int get_child_nodes(
202 sd_bus_error *error) {
212 s = set_new(string_hash_func, string_compare_func);
216 r = add_subtree_to_set(bus, prefix, n, s, error);
226 static int node_callbacks_run(
229 struct node_callback *first,
230 bool require_fallback,
231 bool *found_object) {
233 struct node_callback *c;
238 assert(found_object);
240 LIST_FOREACH(callbacks, c, first) {
241 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
243 if (bus->nodes_modified)
246 if (require_fallback && !c->is_fallback)
249 *found_object = true;
251 if (c->last_iteration == bus->iteration_counter)
254 c->last_iteration = bus->iteration_counter;
256 r = sd_bus_message_rewind(m, true);
260 r = c->callback(bus, m, c->userdata, &error_buffer);
261 r = bus_maybe_reply_error(m, r, &error_buffer);
269 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
271 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
272 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
281 /* If the entire bus is trusted let's grant access */
285 /* If the member is marked UNPRIVILEGED let's grant access */
286 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
289 /* If we are not connected to kdbus we cannot retrieve the
290 * effective capability set without race. Since we need this
291 * for a security decision we cannot use racy data, hence
292 * don't request it. */
294 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
296 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
300 /* Check have the caller has the requested capability
301 * set. Note that the flags value contains the capability
302 * number plus one, which we need to subtract here. We do this
303 * so that we have 0 as special value for "default
305 cap = CAPABILITY_SHIFT(c->vtable->flags);
307 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
313 r = sd_bus_creds_has_effective_cap(creds, cap);
317 /* Caller has same UID as us, then let's grant access */
318 r = sd_bus_creds_get_uid(creds, &uid);
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) {
385 r = c->vtable->x.method.handler(bus, m, u, &error);
386 return bus_maybe_reply_error(m, r, &error);
389 /* If the method callback is NULL, make this a successful NOP */
390 r = sd_bus_reply_method_return(m, NULL);
397 static int invoke_property_get(
399 const sd_bus_vtable *v,
401 const char *interface,
402 const char *property,
403 sd_bus_message *reply,
405 sd_bus_error *error) {
417 if (v->x.property.get) {
418 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
421 if (sd_bus_error_is_set(error))
422 return -sd_bus_error_get_errno(error);
426 /* Automatic handling if no callback is defined. */
428 if (streq(v->x.property.signature, "as"))
429 return sd_bus_message_append_strv(reply, *(char***) userdata);
431 assert(signature_is_single(v->x.property.signature, false));
432 assert(bus_type_is_basic(v->x.property.signature[0]));
434 switch (v->x.property.signature[0]) {
436 case SD_BUS_TYPE_STRING:
437 case SD_BUS_TYPE_SIGNATURE:
438 p = strempty(*(char**) userdata);
441 case SD_BUS_TYPE_OBJECT_PATH:
442 p = *(char**) userdata;
451 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
454 static int invoke_property_set(
456 const sd_bus_vtable *v,
458 const char *interface,
459 const char *property,
460 sd_bus_message *value,
462 sd_bus_error *error) {
473 if (v->x.property.set) {
474 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
477 if (sd_bus_error_is_set(error))
478 return -sd_bus_error_get_errno(error);
482 /* Automatic handling if no callback is defined. */
484 assert(signature_is_single(v->x.property.signature, false));
485 assert(bus_type_is_basic(v->x.property.signature[0]));
487 switch (v->x.property.signature[0]) {
489 case SD_BUS_TYPE_STRING:
490 case SD_BUS_TYPE_OBJECT_PATH:
491 case SD_BUS_TYPE_SIGNATURE: {
495 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
503 free(*(char**) userdata);
504 *(char**) userdata = n;
510 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
520 static int property_get_set_callbacks_run(
523 struct vtable_member *c,
524 bool require_fallback,
526 bool *found_object) {
528 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
529 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
536 assert(found_object);
538 if (require_fallback && !c->parent->is_fallback)
541 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
543 return bus_maybe_reply_error(m, r, &error);
544 if (bus->nodes_modified)
547 *found_object = true;
549 r = sd_bus_message_new_method_return(m, &reply);
554 /* Note that we do not protect against reexecution
555 * here (using the last_iteration check, see below),
556 * should the node tree have changed and we got called
557 * again. We assume that property Get() calls are
558 * ultimately without side-effects or if they aren't
559 * then at least idempotent. */
561 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
565 /* Note that we do not do an access check here. Read
566 * access to properties is always unrestricted, since
567 * PropertiesChanged signals broadcast contents
570 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
572 return bus_maybe_reply_error(m, r, &error);
574 if (bus->nodes_modified)
577 r = sd_bus_message_close_container(reply);
582 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
583 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
585 /* Avoid that we call the set routine more than once
586 * if the processing of this message got restarted
587 * because the node tree changed. */
588 if (c->last_iteration == bus->iteration_counter)
591 c->last_iteration = bus->iteration_counter;
593 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
597 r = check_access(bus, m, c, &error);
599 return bus_maybe_reply_error(m, r, &error);
601 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
603 return bus_maybe_reply_error(m, r, &error);
605 if (bus->nodes_modified)
608 r = sd_bus_message_exit_container(m);
613 r = sd_bus_send(bus, reply, NULL);
620 static int vtable_append_one_property(
622 sd_bus_message *reply,
624 struct node_vtable *c,
625 const sd_bus_vtable *v,
627 sd_bus_error *error) {
637 r = sd_bus_message_open_container(reply, 'e', "sv");
641 r = sd_bus_message_append(reply, "s", v->x.property.member);
645 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
649 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
652 if (bus->nodes_modified)
655 r = sd_bus_message_close_container(reply);
659 r = sd_bus_message_close_container(reply);
666 static int vtable_append_all_properties(
668 sd_bus_message *reply,
670 struct node_vtable *c,
672 sd_bus_error *error) {
674 const sd_bus_vtable *v;
682 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
685 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
686 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
689 if (v->flags & SD_BUS_VTABLE_HIDDEN)
692 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
695 if (bus->nodes_modified)
702 static int property_get_all_callbacks_run(
705 struct node_vtable *first,
706 bool require_fallback,
708 bool *found_object) {
710 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
711 struct node_vtable *c;
712 bool found_interface;
717 assert(found_object);
719 r = sd_bus_message_new_method_return(m, &reply);
723 r = sd_bus_message_open_container(reply, 'a', "{sv}");
727 found_interface = !iface ||
728 streq(iface, "org.freedesktop.DBus.Properties") ||
729 streq(iface, "org.freedesktop.DBus.Peer") ||
730 streq(iface, "org.freedesktop.DBus.Introspectable");
732 LIST_FOREACH(vtables, c, first) {
733 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
736 if (require_fallback && !c->is_fallback)
739 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
741 return bus_maybe_reply_error(m, r, &error);
742 if (bus->nodes_modified)
747 *found_object = true;
749 if (iface && !streq(c->interface, iface))
751 found_interface = true;
753 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
755 return bus_maybe_reply_error(m, r, &error);
756 if (bus->nodes_modified)
760 if (!found_interface) {
761 r = sd_bus_reply_method_errorf(
763 SD_BUS_ERROR_UNKNOWN_INTERFACE,
764 "Unknown interface '%s'.", iface);
771 r = sd_bus_message_close_container(reply);
775 r = sd_bus_send(bus, reply, NULL);
782 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
786 if (n->object_manager)
790 return bus_node_with_object_manager(bus, n->parent);
795 static bool bus_node_exists(
799 bool require_fallback) {
801 struct node_vtable *c;
802 struct node_callback *k;
808 /* Tests if there's anything attached directly to this node
809 * for the specified path */
811 LIST_FOREACH(callbacks, k, n->callbacks) {
812 if (require_fallback && !k->is_fallback)
818 LIST_FOREACH(vtables, c, n->vtables) {
819 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
821 if (require_fallback && !c->is_fallback)
824 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
826 if (bus->nodes_modified)
830 return !require_fallback && (n->enumerators || n->object_manager);
833 static int process_introspect(
837 bool require_fallback,
838 bool *found_object) {
840 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
841 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
842 _cleanup_set_free_free_ Set *s = NULL;
843 const char *previous_interface = NULL;
844 struct introspect intro;
845 struct node_vtable *c;
852 assert(found_object);
854 r = get_child_nodes(bus, m->path, n, &s, &error);
856 return bus_maybe_reply_error(m, r, &error);
857 if (bus->nodes_modified)
860 r = introspect_begin(&intro, bus->trusted);
864 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
868 empty = set_isempty(s);
870 LIST_FOREACH(vtables, c, n->vtables) {
871 if (require_fallback && !c->is_fallback)
874 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
876 r = bus_maybe_reply_error(m, r, &error);
879 if (bus->nodes_modified) {
888 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
891 if (!streq_ptr(previous_interface, c->interface)) {
893 if (previous_interface)
894 fputs(" </interface>\n", intro.f);
896 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
899 r = introspect_write_interface(&intro, c->vtable);
903 previous_interface = c->interface;
906 if (previous_interface)
907 fputs(" </interface>\n", intro.f);
910 /* Nothing?, let's see if we exist at all, and if not
911 * refuse to do anything */
912 r = bus_node_exists(bus, n, m->path, require_fallback);
915 if (bus->nodes_modified)
921 *found_object = true;
923 r = introspect_write_child_nodes(&intro, s, m->path);
927 r = introspect_finish(&intro, bus, m, &reply);
931 r = sd_bus_send(bus, reply, NULL);
938 introspect_free(&intro);
942 static int object_manager_serialize_path(
944 sd_bus_message *reply,
947 bool require_fallback,
948 sd_bus_error *error) {
950 const char *previous_interface = NULL;
951 bool found_something = false;
952 struct node_vtable *i;
962 n = hashmap_get(bus->nodes, prefix);
966 LIST_FOREACH(vtables, i, n->vtables) {
969 if (require_fallback && !i->is_fallback)
972 r = node_vtable_get_userdata(bus, path, i, &u, error);
975 if (bus->nodes_modified)
980 if (!found_something) {
982 /* Open the object part */
984 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
988 r = sd_bus_message_append(reply, "o", path);
992 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
996 found_something = true;
999 if (!streq_ptr(previous_interface, i->interface)) {
1001 /* Maybe close the previous interface part */
1003 if (previous_interface) {
1004 r = sd_bus_message_close_container(reply);
1008 r = sd_bus_message_close_container(reply);
1013 /* Open the new interface part */
1015 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1019 r = sd_bus_message_append(reply, "s", i->interface);
1023 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1028 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1031 if (bus->nodes_modified)
1034 previous_interface = i->interface;
1037 if (previous_interface) {
1038 r = sd_bus_message_close_container(reply);
1042 r = sd_bus_message_close_container(reply);
1047 if (found_something) {
1048 r = sd_bus_message_close_container(reply);
1052 r = sd_bus_message_close_container(reply);
1060 static int object_manager_serialize_path_and_fallbacks(
1062 sd_bus_message *reply,
1064 sd_bus_error *error) {
1074 /* First, add all vtables registered for this path */
1075 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1078 if (bus->nodes_modified)
1081 /* Second, add fallback vtables registered for any of the prefixes */
1082 prefix = alloca(strlen(path) + 1);
1083 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1084 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1087 if (bus->nodes_modified)
1094 static int process_get_managed_objects(
1098 bool require_fallback,
1099 bool *found_object) {
1101 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1102 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1103 _cleanup_set_free_free_ Set *s = NULL;
1110 assert(found_object);
1112 if (!bus_node_with_object_manager(bus, n))
1115 r = get_child_nodes(bus, m->path, n, &s, &error);
1118 if (bus->nodes_modified)
1121 r = sd_bus_message_new_method_return(m, &reply);
1125 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1129 empty = set_isempty(s);
1131 struct node_vtable *c;
1133 /* Hmm, so we have no children? Then let's check
1134 * whether we exist at all, i.e. whether at least one
1137 LIST_FOREACH(vtables, c, n->vtables) {
1139 if (require_fallback && !c->is_fallback)
1157 SET_FOREACH(path, s, i) {
1158 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1162 if (bus->nodes_modified)
1167 r = sd_bus_message_close_container(reply);
1171 r = sd_bus_send(bus, reply, NULL);
1178 static int object_find_and_run(
1182 bool require_fallback,
1183 bool *found_object) {
1186 struct vtable_member vtable_key, *v;
1192 assert(found_object);
1194 n = hashmap_get(bus->nodes, p);
1198 /* First, try object callbacks */
1199 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1202 if (bus->nodes_modified)
1205 if (!m->interface || !m->member)
1208 /* Then, look for a known method */
1209 vtable_key.path = (char*) p;
1210 vtable_key.interface = m->interface;
1211 vtable_key.member = m->member;
1213 v = hashmap_get(bus->vtable_methods, &vtable_key);
1215 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1218 if (bus->nodes_modified)
1222 /* Then, look for a known property */
1223 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1226 get = streq(m->member, "Get");
1228 if (get || streq(m->member, "Set")) {
1230 r = sd_bus_message_rewind(m, true);
1234 vtable_key.path = (char*) p;
1236 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1238 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1240 v = hashmap_get(bus->vtable_properties, &vtable_key);
1242 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1247 } else if (streq(m->member, "GetAll")) {
1250 r = sd_bus_message_rewind(m, true);
1254 r = sd_bus_message_read(m, "s", &iface);
1256 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1261 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1266 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1268 if (!isempty(sd_bus_message_get_signature(m, true)))
1269 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1271 r = process_introspect(bus, m, n, require_fallback, found_object);
1275 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1277 if (!isempty(sd_bus_message_get_signature(m, true)))
1278 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1280 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1285 if (bus->nodes_modified)
1288 if (!*found_object) {
1289 r = bus_node_exists(bus, n, m->path, require_fallback);
1293 *found_object = true;
1299 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1302 bool found_object = false;
1307 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1310 if (hashmap_isempty(bus->nodes))
1313 /* Never respond to broadcast messages */
1314 if (bus->bus_client && !m->destination)
1320 pl = strlen(m->path);
1324 bus->nodes_modified = false;
1326 r = object_find_and_run(bus, m, m->path, false, &found_object);
1330 /* Look for fallback prefixes */
1331 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1333 if (bus->nodes_modified)
1336 r = object_find_and_run(bus, m, prefix, true, &found_object);
1341 } while (bus->nodes_modified);
1346 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1347 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1348 r = sd_bus_reply_method_errorf(
1350 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1351 "Unknown property or interface.");
1353 r = sd_bus_reply_method_errorf(
1355 SD_BUS_ERROR_UNKNOWN_METHOD,
1356 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1364 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1365 struct node *n, *parent;
1367 _cleanup_free_ char *s = NULL;
1373 assert(path[0] == '/');
1375 n = hashmap_get(bus->nodes, path);
1379 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1387 if (streq(path, "/"))
1390 e = strrchr(path, '/');
1393 p = strndupa(path, MAX(1, path - e));
1395 parent = bus_node_allocate(bus, p);
1400 n = new0(struct node, 1);
1406 s = NULL; /* do not free */
1408 r = hashmap_put(bus->nodes, n->path, n);
1416 LIST_PREPEND(siblings, parent->child, n);
1421 static void bus_node_gc(sd_bus *b, struct node *n) {
1434 assert(hashmap_remove(b->nodes, n->path) == n);
1437 LIST_REMOVE(siblings, n->parent->child, n);
1440 bus_node_gc(b, n->parent);
1444 static int bus_add_object(
1448 sd_bus_message_handler_t callback,
1451 struct node_callback *c;
1455 assert_return(bus, -EINVAL);
1456 assert_return(object_path_is_valid(path), -EINVAL);
1457 assert_return(callback, -EINVAL);
1458 assert_return(!bus_pid_changed(bus), -ECHILD);
1460 n = bus_node_allocate(bus, path);
1464 c = new0(struct node_callback, 1);
1471 c->callback = callback;
1472 c->userdata = userdata;
1473 c->is_fallback = fallback;
1475 LIST_PREPEND(callbacks, n->callbacks, c);
1476 bus->nodes_modified = true;
1482 bus_node_gc(bus, n);
1486 static int bus_remove_object(
1490 sd_bus_message_handler_t callback,
1493 struct node_callback *c;
1496 assert_return(bus, -EINVAL);
1497 assert_return(object_path_is_valid(path), -EINVAL);
1498 assert_return(callback, -EINVAL);
1499 assert_return(!bus_pid_changed(bus), -ECHILD);
1501 n = hashmap_get(bus->nodes, path);
1505 LIST_FOREACH(callbacks, c, n->callbacks)
1506 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1511 LIST_REMOVE(callbacks, n->callbacks, c);
1514 bus_node_gc(bus, n);
1515 bus->nodes_modified = true;
1520 _public_ int sd_bus_add_object(sd_bus *bus,
1522 sd_bus_message_handler_t callback,
1525 return bus_add_object(bus, false, path, callback, userdata);
1528 _public_ int sd_bus_remove_object(sd_bus *bus,
1530 sd_bus_message_handler_t callback,
1533 return bus_remove_object(bus, false, path, callback, userdata);
1536 _public_ int sd_bus_add_fallback(sd_bus *bus,
1538 sd_bus_message_handler_t callback,
1541 return bus_add_object(bus, true, prefix, callback, userdata);
1544 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1546 sd_bus_message_handler_t callback,
1549 return bus_remove_object(bus, true, prefix, callback, userdata);
1552 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1558 if (w->interface && w->node && w->vtable) {
1559 const sd_bus_vtable *v;
1561 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1562 struct vtable_member *x = NULL;
1566 case _SD_BUS_VTABLE_METHOD: {
1567 struct vtable_member key;
1569 key.path = w->node->path;
1570 key.interface = w->interface;
1571 key.member = v->x.method.member;
1573 x = hashmap_remove(bus->vtable_methods, &key);
1577 case _SD_BUS_VTABLE_PROPERTY:
1578 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1579 struct vtable_member key;
1581 key.path = w->node->path;
1582 key.interface = w->interface;
1583 key.member = v->x.property.member;
1584 x = hashmap_remove(bus->vtable_properties, &key);
1596 static unsigned vtable_member_hash_func(const void *a) {
1597 const struct vtable_member *m = a;
1602 string_hash_func(m->path) ^
1603 string_hash_func(m->interface) ^
1604 string_hash_func(m->member);
1607 static int vtable_member_compare_func(const void *a, const void *b) {
1608 const struct vtable_member *x = a, *y = b;
1614 r = strcmp(x->path, y->path);
1618 r = strcmp(x->interface, y->interface);
1622 return strcmp(x->member, y->member);
1625 static int add_object_vtable_internal(
1628 const char *interface,
1629 const sd_bus_vtable *vtable,
1631 sd_bus_object_find_t find,
1634 struct node_vtable *c = NULL, *i, *existing = NULL;
1635 const sd_bus_vtable *v;
1639 assert_return(bus, -EINVAL);
1640 assert_return(object_path_is_valid(path), -EINVAL);
1641 assert_return(interface_name_is_valid(interface), -EINVAL);
1642 assert_return(vtable, -EINVAL);
1643 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1644 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1645 assert_return(!bus_pid_changed(bus), -ECHILD);
1646 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1647 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1648 !streq(interface, "org.freedesktop.DBus.Peer") &&
1649 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1651 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1655 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1659 n = bus_node_allocate(bus, path);
1663 LIST_FOREACH(vtables, i, n->vtables) {
1664 if (i->is_fallback != fallback) {
1669 if (streq(i->interface, interface)) {
1671 if (i->vtable == vtable) {
1680 c = new0(struct node_vtable, 1);
1687 c->is_fallback = fallback;
1689 c->userdata = userdata;
1692 c->interface = strdup(interface);
1693 if (!c->interface) {
1698 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1702 case _SD_BUS_VTABLE_METHOD: {
1703 struct vtable_member *m;
1705 if (!member_name_is_valid(v->x.method.member) ||
1706 !signature_is_valid(strempty(v->x.method.signature), false) ||
1707 !signature_is_valid(strempty(v->x.method.result), false) ||
1708 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1709 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1714 m = new0(struct vtable_member, 1);
1722 m->interface = c->interface;
1723 m->member = v->x.method.member;
1726 r = hashmap_put(bus->vtable_methods, m, m);
1735 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1737 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1744 case _SD_BUS_VTABLE_PROPERTY: {
1745 struct vtable_member *m;
1747 if (!member_name_is_valid(v->x.property.member) ||
1748 !signature_is_single(v->x.property.signature, false) ||
1749 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1750 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1751 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1752 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1757 m = new0(struct vtable_member, 1);
1765 m->interface = c->interface;
1766 m->member = v->x.property.member;
1769 r = hashmap_put(bus->vtable_properties, m, m);
1778 case _SD_BUS_VTABLE_SIGNAL:
1780 if (!member_name_is_valid(v->x.signal.member) ||
1781 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1782 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1795 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1796 bus->nodes_modified = true;
1802 free_node_vtable(bus, c);
1804 bus_node_gc(bus, n);
1808 static int remove_object_vtable_internal(
1811 const char *interface,
1812 const sd_bus_vtable *vtable,
1814 sd_bus_object_find_t find,
1817 struct node_vtable *c;
1820 assert_return(bus, -EINVAL);
1821 assert_return(object_path_is_valid(path), -EINVAL);
1822 assert_return(interface_name_is_valid(interface), -EINVAL);
1823 assert_return(!bus_pid_changed(bus), -ECHILD);
1825 n = hashmap_get(bus->nodes, path);
1829 LIST_FOREACH(vtables, c, n->vtables)
1830 if (streq(c->interface, interface) &&
1831 c->is_fallback == fallback &&
1832 c->vtable == vtable &&
1834 c->userdata == userdata)
1840 LIST_REMOVE(vtables, n->vtables, c);
1842 free_node_vtable(bus, c);
1843 bus_node_gc(bus, n);
1845 bus->nodes_modified = true;
1850 _public_ int sd_bus_add_object_vtable(
1853 const char *interface,
1854 const sd_bus_vtable *vtable,
1857 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1860 _public_ int sd_bus_remove_object_vtable(
1863 const char *interface,
1864 const sd_bus_vtable *vtable,
1867 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1870 _public_ int sd_bus_add_fallback_vtable(
1873 const char *interface,
1874 const sd_bus_vtable *vtable,
1875 sd_bus_object_find_t find,
1878 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1881 _public_ int sd_bus_remove_fallback_vtable(
1884 const char *interface,
1885 const sd_bus_vtable *vtable,
1886 sd_bus_object_find_t find,
1889 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1892 _public_ int sd_bus_add_node_enumerator(
1895 sd_bus_node_enumerator_t callback,
1898 struct node_enumerator *c;
1902 assert_return(bus, -EINVAL);
1903 assert_return(object_path_is_valid(path), -EINVAL);
1904 assert_return(callback, -EINVAL);
1905 assert_return(!bus_pid_changed(bus), -ECHILD);
1907 n = bus_node_allocate(bus, path);
1911 c = new0(struct node_enumerator, 1);
1918 c->callback = callback;
1919 c->userdata = userdata;
1921 LIST_PREPEND(enumerators, n->enumerators, c);
1923 bus->nodes_modified = true;
1929 bus_node_gc(bus, n);
1933 _public_ int sd_bus_remove_node_enumerator(
1936 sd_bus_node_enumerator_t callback,
1939 struct node_enumerator *c;
1942 assert_return(bus, -EINVAL);
1943 assert_return(object_path_is_valid(path), -EINVAL);
1944 assert_return(callback, -EINVAL);
1945 assert_return(!bus_pid_changed(bus), -ECHILD);
1947 n = hashmap_get(bus->nodes, path);
1951 LIST_FOREACH(enumerators, c, n->enumerators)
1952 if (c->callback == callback && c->userdata == userdata)
1958 LIST_REMOVE(enumerators, n->enumerators, c);
1961 bus_node_gc(bus, n);
1963 bus->nodes_modified = true;
1968 static int emit_properties_changed_on_interface(
1972 const char *interface,
1973 bool require_fallback,
1976 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1977 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1978 bool has_invalidating = false, has_changing = false;
1979 struct vtable_member key = {};
1980 struct node_vtable *c;
1991 n = hashmap_get(bus->nodes, prefix);
1995 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1999 r = sd_bus_message_append(m, "s", interface);
2003 r = sd_bus_message_open_container(m, 'a', "{sv}");
2008 key.interface = interface;
2010 LIST_FOREACH(vtables, c, n->vtables) {
2011 if (require_fallback && !c->is_fallback)
2014 if (!streq(c->interface, interface))
2017 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2020 if (bus->nodes_modified)
2026 /* If the caller specified a list of
2027 * properties we include exactly those in the
2028 * PropertiesChanged message */
2030 STRV_FOREACH(property, names) {
2031 struct vtable_member *v;
2033 assert_return(member_name_is_valid(*property), -EINVAL);
2035 key.member = *property;
2036 v = hashmap_get(bus->vtable_properties, &key);
2040 /* If there are two vtables for the same
2041 * interface, let's handle this property when
2042 * we come to that vtable. */
2046 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
2047 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
2049 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
2051 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2052 has_invalidating = true;
2056 has_changing = true;
2058 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
2061 if (bus->nodes_modified)
2065 const sd_bus_vtable *v;
2067 /* If the caller specified no properties list
2068 * we include all properties that are marked
2069 * as changing in the message. */
2071 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2072 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2075 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2078 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2079 has_invalidating = true;
2083 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2086 has_changing = true;
2088 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2091 if (bus->nodes_modified)
2097 if (!has_invalidating && !has_changing)
2100 r = sd_bus_message_close_container(m);
2104 r = sd_bus_message_open_container(m, 'a', "s");
2108 if (has_invalidating) {
2109 LIST_FOREACH(vtables, c, n->vtables) {
2110 if (require_fallback && !c->is_fallback)
2113 if (!streq(c->interface, interface))
2116 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2119 if (bus->nodes_modified)
2125 STRV_FOREACH(property, names) {
2126 struct vtable_member *v;
2128 key.member = *property;
2129 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2130 assert(c == v->parent);
2132 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2135 r = sd_bus_message_append(m, "s", *property);
2140 const sd_bus_vtable *v;
2142 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2143 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2146 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2149 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2152 r = sd_bus_message_append(m, "s", v->x.property.member);
2160 r = sd_bus_message_close_container(m);
2164 r = sd_bus_send(bus, m, NULL);
2171 _public_ int sd_bus_emit_properties_changed_strv(
2174 const char *interface,
2177 BUS_DONT_DESTROY(bus);
2181 assert_return(bus, -EINVAL);
2182 assert_return(object_path_is_valid(path), -EINVAL);
2183 assert_return(interface_name_is_valid(interface), -EINVAL);
2184 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2185 assert_return(!bus_pid_changed(bus), -ECHILD);
2188 /* A non-NULL but empty names list means nothing needs to be
2189 generated. A NULL list OTOH indicates that all properties
2190 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2191 included in the PropertiesChanged message. */
2192 if (names && names[0] == NULL)
2196 bus->nodes_modified = false;
2198 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2201 if (bus->nodes_modified)
2204 prefix = alloca(strlen(path) + 1);
2205 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2206 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2209 if (bus->nodes_modified)
2213 } while (bus->nodes_modified);
2218 _public_ int sd_bus_emit_properties_changed(
2221 const char *interface,
2222 const char *name, ...) {
2226 assert_return(bus, -EINVAL);
2227 assert_return(object_path_is_valid(path), -EINVAL);
2228 assert_return(interface_name_is_valid(interface), -EINVAL);
2229 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2230 assert_return(!bus_pid_changed(bus), -ECHILD);
2235 names = strv_from_stdarg_alloca(name);
2237 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2240 static int interfaces_added_append_one_prefix(
2245 const char *interface,
2246 bool require_fallback) {
2248 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2249 bool found_interface = false;
2250 struct node_vtable *c;
2261 n = hashmap_get(bus->nodes, prefix);
2265 LIST_FOREACH(vtables, c, n->vtables) {
2266 if (require_fallback && !c->is_fallback)
2269 if (!streq(c->interface, interface))
2272 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2275 if (bus->nodes_modified)
2280 if (!found_interface) {
2281 r = sd_bus_message_append_basic(m, 's', interface);
2285 r = sd_bus_message_open_container(m, 'a', "{sv}");
2289 found_interface = true;
2292 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2295 if (bus->nodes_modified)
2299 if (found_interface) {
2300 r = sd_bus_message_close_container(m);
2305 return found_interface;
2308 static int interfaces_added_append_one(
2312 const char *interface) {
2322 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2325 if (bus->nodes_modified)
2328 prefix = alloca(strlen(path) + 1);
2329 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2330 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2333 if (bus->nodes_modified)
2340 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2341 BUS_DONT_DESTROY(bus);
2343 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2347 assert_return(bus, -EINVAL);
2348 assert_return(object_path_is_valid(path), -EINVAL);
2349 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2350 assert_return(!bus_pid_changed(bus), -ECHILD);
2352 if (strv_isempty(interfaces))
2356 bus->nodes_modified = false;
2359 m = sd_bus_message_unref(m);
2361 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2365 r = sd_bus_message_append_basic(m, 'o', path);
2369 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2373 STRV_FOREACH(i, interfaces) {
2374 assert_return(interface_name_is_valid(*i), -EINVAL);
2376 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2380 r = interfaces_added_append_one(bus, m, path, *i);
2384 if (bus->nodes_modified)
2387 r = sd_bus_message_close_container(m);
2392 if (bus->nodes_modified)
2395 r = sd_bus_message_close_container(m);
2399 } while (bus->nodes_modified);
2401 return sd_bus_send(bus, m, NULL);
2404 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2407 assert_return(bus, -EINVAL);
2408 assert_return(object_path_is_valid(path), -EINVAL);
2409 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2410 assert_return(!bus_pid_changed(bus), -ECHILD);
2412 interfaces = strv_from_stdarg_alloca(interface);
2414 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2417 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2418 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2421 assert_return(bus, -EINVAL);
2422 assert_return(object_path_is_valid(path), -EINVAL);
2423 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2424 assert_return(!bus_pid_changed(bus), -ECHILD);
2426 if (strv_isempty(interfaces))
2429 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2433 r = sd_bus_message_append_basic(m, 'o', path);
2437 r = sd_bus_message_append_strv(m, interfaces);
2441 return sd_bus_send(bus, m, NULL);
2444 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2447 assert_return(bus, -EINVAL);
2448 assert_return(object_path_is_valid(path), -EINVAL);
2449 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2450 assert_return(!bus_pid_changed(bus), -ECHILD);
2452 interfaces = strv_from_stdarg_alloca(interface);
2454 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2457 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2460 assert_return(bus, -EINVAL);
2461 assert_return(object_path_is_valid(path), -EINVAL);
2462 assert_return(!bus_pid_changed(bus), -ECHILD);
2464 n = bus_node_allocate(bus, path);
2468 n->object_manager = true;
2469 bus->nodes_modified = true;
2473 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2476 assert_return(bus, -EINVAL);
2477 assert_return(object_path_is_valid(path), -EINVAL);
2478 assert_return(!bus_pid_changed(bus), -ECHILD);
2480 n = hashmap_get(bus->nodes, path);
2484 if (!n->object_manager)
2487 n->object_manager = false;
2488 bus->nodes_modified = true;
2489 bus_node_gc(bus, n);