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_all_properties(
622 sd_bus_message *reply,
624 struct node_vtable *c,
626 sd_bus_error *error) {
628 const sd_bus_vtable *v;
636 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
639 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
640 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
643 if (v->flags & SD_BUS_VTABLE_HIDDEN)
646 r = sd_bus_message_open_container(reply, 'e', "sv");
650 r = sd_bus_message_append(reply, "s", v->x.property.member);
654 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
658 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
661 if (bus->nodes_modified)
664 r = sd_bus_message_close_container(reply);
668 r = sd_bus_message_close_container(reply);
676 static int property_get_all_callbacks_run(
679 struct node_vtable *first,
680 bool require_fallback,
682 bool *found_object) {
684 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
685 struct node_vtable *c;
686 bool found_interface;
691 assert(found_object);
693 r = sd_bus_message_new_method_return(m, &reply);
697 r = sd_bus_message_open_container(reply, 'a', "{sv}");
701 found_interface = !iface ||
702 streq(iface, "org.freedesktop.DBus.Properties") ||
703 streq(iface, "org.freedesktop.DBus.Peer") ||
704 streq(iface, "org.freedesktop.DBus.Introspectable");
706 LIST_FOREACH(vtables, c, first) {
707 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
710 if (require_fallback && !c->is_fallback)
713 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
715 return bus_maybe_reply_error(m, r, &error);
716 if (bus->nodes_modified)
721 *found_object = true;
723 if (iface && !streq(c->interface, iface))
725 found_interface = true;
727 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
729 return bus_maybe_reply_error(m, r, &error);
730 if (bus->nodes_modified)
734 if (!found_interface) {
735 r = sd_bus_reply_method_errorf(
737 SD_BUS_ERROR_UNKNOWN_INTERFACE,
738 "Unknown interface '%s'.", iface);
745 r = sd_bus_message_close_container(reply);
749 r = sd_bus_send(bus, reply, NULL);
756 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
760 if (n->object_manager)
764 return bus_node_with_object_manager(bus, n->parent);
769 static bool bus_node_exists(
773 bool require_fallback) {
775 struct node_vtable *c;
776 struct node_callback *k;
782 /* Tests if there's anything attached directly to this node
783 * for the specified path */
785 LIST_FOREACH(callbacks, k, n->callbacks) {
786 if (require_fallback && !k->is_fallback)
792 LIST_FOREACH(vtables, c, n->vtables) {
793 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
795 if (require_fallback && !c->is_fallback)
798 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
800 if (bus->nodes_modified)
804 return !require_fallback && (n->enumerators || n->object_manager);
807 static int process_introspect(
811 bool require_fallback,
812 bool *found_object) {
814 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
815 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
816 _cleanup_set_free_free_ Set *s = NULL;
817 const char *previous_interface = NULL;
818 struct introspect intro;
819 struct node_vtable *c;
826 assert(found_object);
828 r = get_child_nodes(bus, m->path, n, &s, &error);
830 return bus_maybe_reply_error(m, r, &error);
831 if (bus->nodes_modified)
834 r = introspect_begin(&intro);
838 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
842 empty = set_isempty(s);
844 LIST_FOREACH(vtables, c, n->vtables) {
845 if (require_fallback && !c->is_fallback)
848 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
850 r = bus_maybe_reply_error(m, r, &error);
853 if (bus->nodes_modified) {
862 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
865 if (!streq_ptr(previous_interface, c->interface)) {
867 if (previous_interface)
868 fputs(" </interface>\n", intro.f);
870 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
873 r = introspect_write_interface(&intro, c->vtable);
877 previous_interface = c->interface;
880 if (previous_interface)
881 fputs(" </interface>\n", intro.f);
884 /* Nothing?, let's see if we exist at all, and if not
885 * refuse to do anything */
886 r = bus_node_exists(bus, n, m->path, require_fallback);
889 if (bus->nodes_modified)
895 *found_object = true;
897 r = introspect_write_child_nodes(&intro, s, m->path);
901 r = introspect_finish(&intro, bus, m, &reply);
905 r = sd_bus_send(bus, reply, NULL);
912 introspect_free(&intro);
916 static int object_manager_serialize_path(
918 sd_bus_message *reply,
921 bool require_fallback,
922 sd_bus_error *error) {
924 const char *previous_interface = NULL;
925 bool found_something = false;
926 struct node_vtable *i;
936 n = hashmap_get(bus->nodes, prefix);
940 LIST_FOREACH(vtables, i, n->vtables) {
943 if (require_fallback && !i->is_fallback)
946 r = node_vtable_get_userdata(bus, path, i, &u, error);
949 if (bus->nodes_modified)
954 if (!found_something) {
956 /* Open the object part */
958 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
962 r = sd_bus_message_append(reply, "o", path);
966 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
970 found_something = true;
973 if (!streq_ptr(previous_interface, i->interface)) {
975 /* Maybe close the previous interface part */
977 if (previous_interface) {
978 r = sd_bus_message_close_container(reply);
982 r = sd_bus_message_close_container(reply);
987 /* Open the new interface part */
989 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
993 r = sd_bus_message_append(reply, "s", i->interface);
997 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1002 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1005 if (bus->nodes_modified)
1008 previous_interface = i->interface;
1011 if (previous_interface) {
1012 r = sd_bus_message_close_container(reply);
1016 r = sd_bus_message_close_container(reply);
1021 if (found_something) {
1022 r = sd_bus_message_close_container(reply);
1026 r = sd_bus_message_close_container(reply);
1034 static int object_manager_serialize_path_and_fallbacks(
1036 sd_bus_message *reply,
1038 sd_bus_error *error) {
1048 /* First, add all vtables registered for this path */
1049 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1052 if (bus->nodes_modified)
1055 /* Second, add fallback vtables registered for any of the prefixes */
1056 prefix = alloca(strlen(path) + 1);
1057 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1058 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1061 if (bus->nodes_modified)
1068 static int process_get_managed_objects(
1072 bool require_fallback,
1073 bool *found_object) {
1075 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1076 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1077 _cleanup_set_free_free_ Set *s = NULL;
1084 assert(found_object);
1086 if (!bus_node_with_object_manager(bus, n))
1089 r = get_child_nodes(bus, m->path, n, &s, &error);
1092 if (bus->nodes_modified)
1095 r = sd_bus_message_new_method_return(m, &reply);
1099 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1103 empty = set_isempty(s);
1105 struct node_vtable *c;
1107 /* Hmm, so we have no children? Then let's check
1108 * whether we exist at all, i.e. whether at least one
1111 LIST_FOREACH(vtables, c, n->vtables) {
1113 if (require_fallback && !c->is_fallback)
1131 SET_FOREACH(path, s, i) {
1132 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1136 if (bus->nodes_modified)
1141 r = sd_bus_message_close_container(reply);
1145 r = sd_bus_send(bus, reply, NULL);
1152 static int object_find_and_run(
1156 bool require_fallback,
1157 bool *found_object) {
1160 struct vtable_member vtable_key, *v;
1166 assert(found_object);
1168 n = hashmap_get(bus->nodes, p);
1172 /* First, try object callbacks */
1173 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1176 if (bus->nodes_modified)
1179 if (!m->interface || !m->member)
1182 /* Then, look for a known method */
1183 vtable_key.path = (char*) p;
1184 vtable_key.interface = m->interface;
1185 vtable_key.member = m->member;
1187 v = hashmap_get(bus->vtable_methods, &vtable_key);
1189 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1192 if (bus->nodes_modified)
1196 /* Then, look for a known property */
1197 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1200 get = streq(m->member, "Get");
1202 if (get || streq(m->member, "Set")) {
1204 r = sd_bus_message_rewind(m, true);
1208 vtable_key.path = (char*) p;
1210 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1212 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1214 v = hashmap_get(bus->vtable_properties, &vtable_key);
1216 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1221 } else if (streq(m->member, "GetAll")) {
1224 r = sd_bus_message_rewind(m, true);
1228 r = sd_bus_message_read(m, "s", &iface);
1230 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1235 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1240 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1242 if (!isempty(sd_bus_message_get_signature(m, true)))
1243 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1245 r = process_introspect(bus, m, n, require_fallback, found_object);
1249 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1251 if (!isempty(sd_bus_message_get_signature(m, true)))
1252 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1254 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1259 if (bus->nodes_modified)
1262 if (!*found_object) {
1263 r = bus_node_exists(bus, n, m->path, require_fallback);
1267 *found_object = true;
1273 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1276 bool found_object = false;
1281 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1284 if (hashmap_isempty(bus->nodes))
1290 pl = strlen(m->path);
1294 bus->nodes_modified = false;
1296 r = object_find_and_run(bus, m, m->path, false, &found_object);
1300 /* Look for fallback prefixes */
1301 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1303 if (bus->nodes_modified)
1306 r = object_find_and_run(bus, m, prefix, true, &found_object);
1311 } while (bus->nodes_modified);
1316 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1317 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1318 r = sd_bus_reply_method_errorf(
1320 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1321 "Unknown property or interface.");
1323 r = sd_bus_reply_method_errorf(
1325 SD_BUS_ERROR_UNKNOWN_METHOD,
1326 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1334 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1335 struct node *n, *parent;
1342 assert(path[0] == '/');
1344 n = hashmap_get(bus->nodes, path);
1348 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1356 if (streq(path, "/"))
1359 e = strrchr(path, '/');
1362 p = strndupa(path, MAX(1, path - e));
1364 parent = bus_node_allocate(bus, p);
1371 n = new0(struct node, 1);
1378 r = hashmap_put(bus->nodes, s, n);
1386 LIST_PREPEND(siblings, parent->child, n);
1391 static void bus_node_gc(sd_bus *b, struct node *n) {
1404 assert(hashmap_remove(b->nodes, n->path) == n);
1407 LIST_REMOVE(siblings, n->parent->child, n);
1410 bus_node_gc(b, n->parent);
1414 static int bus_add_object(
1418 sd_bus_message_handler_t callback,
1421 struct node_callback *c;
1425 assert_return(bus, -EINVAL);
1426 assert_return(object_path_is_valid(path), -EINVAL);
1427 assert_return(callback, -EINVAL);
1428 assert_return(!bus_pid_changed(bus), -ECHILD);
1430 n = bus_node_allocate(bus, path);
1434 c = new0(struct node_callback, 1);
1441 c->callback = callback;
1442 c->userdata = userdata;
1443 c->is_fallback = fallback;
1445 LIST_PREPEND(callbacks, n->callbacks, c);
1446 bus->nodes_modified = true;
1452 bus_node_gc(bus, n);
1456 static int bus_remove_object(
1460 sd_bus_message_handler_t callback,
1463 struct node_callback *c;
1466 assert_return(bus, -EINVAL);
1467 assert_return(object_path_is_valid(path), -EINVAL);
1468 assert_return(callback, -EINVAL);
1469 assert_return(!bus_pid_changed(bus), -ECHILD);
1471 n = hashmap_get(bus->nodes, path);
1475 LIST_FOREACH(callbacks, c, n->callbacks)
1476 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1481 LIST_REMOVE(callbacks, n->callbacks, c);
1484 bus_node_gc(bus, n);
1485 bus->nodes_modified = true;
1490 _public_ int sd_bus_add_object(sd_bus *bus,
1492 sd_bus_message_handler_t callback,
1495 return bus_add_object(bus, false, path, callback, userdata);
1498 _public_ int sd_bus_remove_object(sd_bus *bus,
1500 sd_bus_message_handler_t callback,
1503 return bus_remove_object(bus, false, path, callback, userdata);
1506 _public_ int sd_bus_add_fallback(sd_bus *bus,
1508 sd_bus_message_handler_t callback,
1511 return bus_add_object(bus, true, prefix, callback, userdata);
1514 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1516 sd_bus_message_handler_t callback,
1519 return bus_remove_object(bus, true, prefix, callback, userdata);
1522 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1528 if (w->interface && w->node && w->vtable) {
1529 const sd_bus_vtable *v;
1531 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1532 struct vtable_member *x = NULL;
1536 case _SD_BUS_VTABLE_METHOD: {
1537 struct vtable_member key;
1539 key.path = w->node->path;
1540 key.interface = w->interface;
1541 key.member = v->x.method.member;
1543 x = hashmap_remove(bus->vtable_methods, &key);
1547 case _SD_BUS_VTABLE_PROPERTY:
1548 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1549 struct vtable_member key;
1551 key.path = w->node->path;
1552 key.interface = w->interface;
1553 key.member = v->x.property.member;
1554 x = hashmap_remove(bus->vtable_properties, &key);
1566 static unsigned vtable_member_hash_func(const void *a) {
1567 const struct vtable_member *m = a;
1572 string_hash_func(m->path) ^
1573 string_hash_func(m->interface) ^
1574 string_hash_func(m->member);
1577 static int vtable_member_compare_func(const void *a, const void *b) {
1578 const struct vtable_member *x = a, *y = b;
1584 r = strcmp(x->path, y->path);
1588 r = strcmp(x->interface, y->interface);
1592 return strcmp(x->member, y->member);
1595 static int add_object_vtable_internal(
1598 const char *interface,
1599 const sd_bus_vtable *vtable,
1601 sd_bus_object_find_t find,
1604 struct node_vtable *c = NULL, *i, *existing = NULL;
1605 const sd_bus_vtable *v;
1609 assert_return(bus, -EINVAL);
1610 assert_return(object_path_is_valid(path), -EINVAL);
1611 assert_return(interface_name_is_valid(interface), -EINVAL);
1612 assert_return(vtable, -EINVAL);
1613 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1614 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1615 assert_return(!bus_pid_changed(bus), -ECHILD);
1616 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1617 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1618 !streq(interface, "org.freedesktop.DBus.Peer") &&
1619 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1621 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1625 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1629 n = bus_node_allocate(bus, path);
1633 LIST_FOREACH(vtables, i, n->vtables) {
1634 if (i->is_fallback != fallback) {
1639 if (streq(i->interface, interface)) {
1641 if (i->vtable == vtable) {
1650 c = new0(struct node_vtable, 1);
1657 c->is_fallback = fallback;
1659 c->userdata = userdata;
1662 c->interface = strdup(interface);
1663 if (!c->interface) {
1668 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1672 case _SD_BUS_VTABLE_METHOD: {
1673 struct vtable_member *m;
1675 if (!member_name_is_valid(v->x.method.member) ||
1676 !signature_is_valid(strempty(v->x.method.signature), false) ||
1677 !signature_is_valid(strempty(v->x.method.result), false) ||
1678 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1679 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1684 m = new0(struct vtable_member, 1);
1692 m->interface = c->interface;
1693 m->member = v->x.method.member;
1696 r = hashmap_put(bus->vtable_methods, m, m);
1705 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1707 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1714 case _SD_BUS_VTABLE_PROPERTY: {
1715 struct vtable_member *m;
1717 if (!member_name_is_valid(v->x.property.member) ||
1718 !signature_is_single(v->x.property.signature, false) ||
1719 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1720 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1721 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) ||
1722 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1728 m = new0(struct vtable_member, 1);
1736 m->interface = c->interface;
1737 m->member = v->x.property.member;
1740 r = hashmap_put(bus->vtable_properties, m, m);
1749 case _SD_BUS_VTABLE_SIGNAL:
1751 if (!member_name_is_valid(v->x.signal.member) ||
1752 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1753 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1766 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1767 bus->nodes_modified = true;
1773 free_node_vtable(bus, c);
1775 bus_node_gc(bus, n);
1779 static int remove_object_vtable_internal(
1782 const char *interface,
1783 const sd_bus_vtable *vtable,
1785 sd_bus_object_find_t find,
1788 struct node_vtable *c;
1791 assert_return(bus, -EINVAL);
1792 assert_return(object_path_is_valid(path), -EINVAL);
1793 assert_return(interface_name_is_valid(interface), -EINVAL);
1794 assert_return(!bus_pid_changed(bus), -ECHILD);
1796 n = hashmap_get(bus->nodes, path);
1800 LIST_FOREACH(vtables, c, n->vtables)
1801 if (streq(c->interface, interface) &&
1802 c->is_fallback == fallback &&
1803 c->vtable == vtable &&
1805 c->userdata == userdata)
1811 LIST_REMOVE(vtables, n->vtables, c);
1813 free_node_vtable(bus, c);
1814 bus_node_gc(bus, n);
1816 bus->nodes_modified = true;
1821 _public_ int sd_bus_add_object_vtable(
1824 const char *interface,
1825 const sd_bus_vtable *vtable,
1828 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1831 _public_ int sd_bus_remove_object_vtable(
1834 const char *interface,
1835 const sd_bus_vtable *vtable,
1838 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1841 _public_ int sd_bus_add_fallback_vtable(
1844 const char *interface,
1845 const sd_bus_vtable *vtable,
1846 sd_bus_object_find_t find,
1849 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1852 _public_ int sd_bus_remove_fallback_vtable(
1855 const char *interface,
1856 const sd_bus_vtable *vtable,
1857 sd_bus_object_find_t find,
1860 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1863 _public_ int sd_bus_add_node_enumerator(
1866 sd_bus_node_enumerator_t callback,
1869 struct node_enumerator *c;
1873 assert_return(bus, -EINVAL);
1874 assert_return(object_path_is_valid(path), -EINVAL);
1875 assert_return(callback, -EINVAL);
1876 assert_return(!bus_pid_changed(bus), -ECHILD);
1878 n = bus_node_allocate(bus, path);
1882 c = new0(struct node_enumerator, 1);
1889 c->callback = callback;
1890 c->userdata = userdata;
1892 LIST_PREPEND(enumerators, n->enumerators, c);
1894 bus->nodes_modified = true;
1900 bus_node_gc(bus, n);
1904 _public_ int sd_bus_remove_node_enumerator(
1907 sd_bus_node_enumerator_t callback,
1910 struct node_enumerator *c;
1913 assert_return(bus, -EINVAL);
1914 assert_return(object_path_is_valid(path), -EINVAL);
1915 assert_return(callback, -EINVAL);
1916 assert_return(!bus_pid_changed(bus), -ECHILD);
1918 n = hashmap_get(bus->nodes, path);
1922 LIST_FOREACH(enumerators, c, n->enumerators)
1923 if (c->callback == callback && c->userdata == userdata)
1929 LIST_REMOVE(enumerators, n->enumerators, c);
1932 bus_node_gc(bus, n);
1934 bus->nodes_modified = true;
1939 static int emit_properties_changed_on_interface(
1943 const char *interface,
1944 bool require_fallback,
1947 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1948 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1949 bool has_invalidating = false, has_changing = false;
1950 struct vtable_member key = {};
1951 struct node_vtable *c;
1962 n = hashmap_get(bus->nodes, prefix);
1966 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1970 r = sd_bus_message_append(m, "s", interface);
1974 r = sd_bus_message_open_container(m, 'a', "{sv}");
1979 key.interface = interface;
1981 LIST_FOREACH(vtables, c, n->vtables) {
1982 if (require_fallback && !c->is_fallback)
1985 if (!streq(c->interface, interface))
1988 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1991 if (bus->nodes_modified)
1996 STRV_FOREACH(property, names) {
1997 struct vtable_member *v;
1999 assert_return(member_name_is_valid(*property), -EINVAL);
2001 key.member = *property;
2002 v = hashmap_get(bus->vtable_properties, &key);
2006 /* If there are two vtables for the same
2007 * interface, let's handle this property when
2008 * we come to that vtable. */
2012 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
2014 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
2015 has_invalidating = true;
2019 has_changing = true;
2021 r = sd_bus_message_open_container(m, 'e', "sv");
2025 r = sd_bus_message_append(m, "s", *property);
2029 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
2033 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error);
2036 if (bus->nodes_modified)
2039 r = sd_bus_message_close_container(m);
2043 r = sd_bus_message_close_container(m);
2049 if (!has_invalidating && !has_changing)
2052 r = sd_bus_message_close_container(m);
2056 r = sd_bus_message_open_container(m, 'a', "s");
2060 if (has_invalidating) {
2061 LIST_FOREACH(vtables, c, n->vtables) {
2062 if (require_fallback && !c->is_fallback)
2065 if (!streq(c->interface, interface))
2068 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2071 if (bus->nodes_modified)
2076 STRV_FOREACH(property, names) {
2077 struct vtable_member *v;
2079 key.member = *property;
2080 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2081 assert(c == v->parent);
2083 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
2086 r = sd_bus_message_append(m, "s", *property);
2093 r = sd_bus_message_close_container(m);
2097 r = sd_bus_send(bus, m, NULL);
2104 _public_ int sd_bus_emit_properties_changed_strv(
2107 const char *interface,
2110 BUS_DONT_DESTROY(bus);
2114 assert_return(bus, -EINVAL);
2115 assert_return(object_path_is_valid(path), -EINVAL);
2116 assert_return(interface_name_is_valid(interface), -EINVAL);
2117 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2118 assert_return(!bus_pid_changed(bus), -ECHILD);
2120 if (strv_isempty(names))
2124 bus->nodes_modified = false;
2126 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2129 if (bus->nodes_modified)
2132 prefix = alloca(strlen(path) + 1);
2133 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2134 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2137 if (bus->nodes_modified)
2141 } while (bus->nodes_modified);
2146 _public_ int sd_bus_emit_properties_changed(
2149 const char *interface,
2150 const char *name, ...) {
2154 assert_return(bus, -EINVAL);
2155 assert_return(object_path_is_valid(path), -EINVAL);
2156 assert_return(interface_name_is_valid(interface), -EINVAL);
2157 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2158 assert_return(!bus_pid_changed(bus), -ECHILD);
2163 names = strv_from_stdarg_alloca(name);
2165 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2168 static int interfaces_added_append_one_prefix(
2173 const char *interface,
2174 bool require_fallback) {
2176 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2177 bool found_interface = false;
2178 struct node_vtable *c;
2189 n = hashmap_get(bus->nodes, prefix);
2193 LIST_FOREACH(vtables, c, n->vtables) {
2194 if (require_fallback && !c->is_fallback)
2197 if (!streq(c->interface, interface))
2200 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2203 if (bus->nodes_modified)
2208 if (!found_interface) {
2209 r = sd_bus_message_append_basic(m, 's', interface);
2213 r = sd_bus_message_open_container(m, 'a', "{sv}");
2217 found_interface = true;
2220 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2223 if (bus->nodes_modified)
2227 if (found_interface) {
2228 r = sd_bus_message_close_container(m);
2233 return found_interface;
2236 static int interfaces_added_append_one(
2240 const char *interface) {
2250 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2253 if (bus->nodes_modified)
2256 prefix = alloca(strlen(path) + 1);
2257 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2258 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2261 if (bus->nodes_modified)
2268 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2269 BUS_DONT_DESTROY(bus);
2271 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2275 assert_return(bus, -EINVAL);
2276 assert_return(object_path_is_valid(path), -EINVAL);
2277 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2278 assert_return(!bus_pid_changed(bus), -ECHILD);
2280 if (strv_isempty(interfaces))
2284 bus->nodes_modified = false;
2287 m = sd_bus_message_unref(m);
2289 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2293 r = sd_bus_message_append_basic(m, 'o', path);
2297 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2301 STRV_FOREACH(i, interfaces) {
2302 assert_return(interface_name_is_valid(*i), -EINVAL);
2304 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2308 r = interfaces_added_append_one(bus, m, path, *i);
2312 if (bus->nodes_modified)
2315 r = sd_bus_message_close_container(m);
2320 if (bus->nodes_modified)
2323 r = sd_bus_message_close_container(m);
2327 } while (bus->nodes_modified);
2329 return sd_bus_send(bus, m, NULL);
2332 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2335 assert_return(bus, -EINVAL);
2336 assert_return(object_path_is_valid(path), -EINVAL);
2337 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2338 assert_return(!bus_pid_changed(bus), -ECHILD);
2340 interfaces = strv_from_stdarg_alloca(interface);
2342 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2345 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2346 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2349 assert_return(bus, -EINVAL);
2350 assert_return(object_path_is_valid(path), -EINVAL);
2351 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2352 assert_return(!bus_pid_changed(bus), -ECHILD);
2354 if (strv_isempty(interfaces))
2357 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2361 r = sd_bus_message_append_basic(m, 'o', path);
2365 r = sd_bus_message_append_strv(m, interfaces);
2369 return sd_bus_send(bus, m, NULL);
2372 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2375 assert_return(bus, -EINVAL);
2376 assert_return(object_path_is_valid(path), -EINVAL);
2377 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2378 assert_return(!bus_pid_changed(bus), -ECHILD);
2380 interfaces = strv_from_stdarg_alloca(interface);
2382 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2385 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2388 assert_return(bus, -EINVAL);
2389 assert_return(object_path_is_valid(path), -EINVAL);
2390 assert_return(!bus_pid_changed(bus), -ECHILD);
2392 n = bus_node_allocate(bus, path);
2396 n->object_manager = true;
2397 bus->nodes_modified = true;
2401 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2404 assert_return(bus, -EINVAL);
2405 assert_return(object_path_is_valid(path), -EINVAL);
2406 assert_return(!bus_pid_changed(bus), -ECHILD);
2408 n = hashmap_get(bus->nodes, path);
2412 if (!n->object_manager)
2415 n->object_manager = false;
2416 bus->nodes_modified = true;
2417 bus_node_gc(bus, n);