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, bus->trusted);
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))
1287 /* Never respond to broadcast messages */
1288 if (bus->bus_client && !m->destination)
1294 pl = strlen(m->path);
1298 bus->nodes_modified = false;
1300 r = object_find_and_run(bus, m, m->path, false, &found_object);
1304 /* Look for fallback prefixes */
1305 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1307 if (bus->nodes_modified)
1310 r = object_find_and_run(bus, m, prefix, true, &found_object);
1315 } while (bus->nodes_modified);
1320 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1321 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1322 r = sd_bus_reply_method_errorf(
1324 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1325 "Unknown property or interface.");
1327 r = sd_bus_reply_method_errorf(
1329 SD_BUS_ERROR_UNKNOWN_METHOD,
1330 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1338 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1339 struct node *n, *parent;
1341 _cleanup_free_ char *s = NULL;
1347 assert(path[0] == '/');
1349 n = hashmap_get(bus->nodes, path);
1353 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1361 if (streq(path, "/"))
1364 e = strrchr(path, '/');
1367 p = strndupa(path, MAX(1, path - e));
1369 parent = bus_node_allocate(bus, p);
1374 n = new0(struct node, 1);
1380 s = NULL; /* do not free */
1382 r = hashmap_put(bus->nodes, n->path, n);
1390 LIST_PREPEND(siblings, parent->child, n);
1395 static void bus_node_gc(sd_bus *b, struct node *n) {
1408 assert(hashmap_remove(b->nodes, n->path) == n);
1411 LIST_REMOVE(siblings, n->parent->child, n);
1414 bus_node_gc(b, n->parent);
1418 static int bus_add_object(
1422 sd_bus_message_handler_t callback,
1425 struct node_callback *c;
1429 assert_return(bus, -EINVAL);
1430 assert_return(object_path_is_valid(path), -EINVAL);
1431 assert_return(callback, -EINVAL);
1432 assert_return(!bus_pid_changed(bus), -ECHILD);
1434 n = bus_node_allocate(bus, path);
1438 c = new0(struct node_callback, 1);
1445 c->callback = callback;
1446 c->userdata = userdata;
1447 c->is_fallback = fallback;
1449 LIST_PREPEND(callbacks, n->callbacks, c);
1450 bus->nodes_modified = true;
1456 bus_node_gc(bus, n);
1460 static int bus_remove_object(
1464 sd_bus_message_handler_t callback,
1467 struct node_callback *c;
1470 assert_return(bus, -EINVAL);
1471 assert_return(object_path_is_valid(path), -EINVAL);
1472 assert_return(callback, -EINVAL);
1473 assert_return(!bus_pid_changed(bus), -ECHILD);
1475 n = hashmap_get(bus->nodes, path);
1479 LIST_FOREACH(callbacks, c, n->callbacks)
1480 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1485 LIST_REMOVE(callbacks, n->callbacks, c);
1488 bus_node_gc(bus, n);
1489 bus->nodes_modified = true;
1494 _public_ int sd_bus_add_object(sd_bus *bus,
1496 sd_bus_message_handler_t callback,
1499 return bus_add_object(bus, false, path, callback, userdata);
1502 _public_ int sd_bus_remove_object(sd_bus *bus,
1504 sd_bus_message_handler_t callback,
1507 return bus_remove_object(bus, false, path, callback, userdata);
1510 _public_ int sd_bus_add_fallback(sd_bus *bus,
1512 sd_bus_message_handler_t callback,
1515 return bus_add_object(bus, true, prefix, callback, userdata);
1518 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1520 sd_bus_message_handler_t callback,
1523 return bus_remove_object(bus, true, prefix, callback, userdata);
1526 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1532 if (w->interface && w->node && w->vtable) {
1533 const sd_bus_vtable *v;
1535 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1536 struct vtable_member *x = NULL;
1540 case _SD_BUS_VTABLE_METHOD: {
1541 struct vtable_member key;
1543 key.path = w->node->path;
1544 key.interface = w->interface;
1545 key.member = v->x.method.member;
1547 x = hashmap_remove(bus->vtable_methods, &key);
1551 case _SD_BUS_VTABLE_PROPERTY:
1552 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1553 struct vtable_member key;
1555 key.path = w->node->path;
1556 key.interface = w->interface;
1557 key.member = v->x.property.member;
1558 x = hashmap_remove(bus->vtable_properties, &key);
1570 static unsigned vtable_member_hash_func(const void *a) {
1571 const struct vtable_member *m = a;
1576 string_hash_func(m->path) ^
1577 string_hash_func(m->interface) ^
1578 string_hash_func(m->member);
1581 static int vtable_member_compare_func(const void *a, const void *b) {
1582 const struct vtable_member *x = a, *y = b;
1588 r = strcmp(x->path, y->path);
1592 r = strcmp(x->interface, y->interface);
1596 return strcmp(x->member, y->member);
1599 static int add_object_vtable_internal(
1602 const char *interface,
1603 const sd_bus_vtable *vtable,
1605 sd_bus_object_find_t find,
1608 struct node_vtable *c = NULL, *i, *existing = NULL;
1609 const sd_bus_vtable *v;
1613 assert_return(bus, -EINVAL);
1614 assert_return(object_path_is_valid(path), -EINVAL);
1615 assert_return(interface_name_is_valid(interface), -EINVAL);
1616 assert_return(vtable, -EINVAL);
1617 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1618 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1619 assert_return(!bus_pid_changed(bus), -ECHILD);
1620 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1621 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1622 !streq(interface, "org.freedesktop.DBus.Peer") &&
1623 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1625 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1629 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1633 n = bus_node_allocate(bus, path);
1637 LIST_FOREACH(vtables, i, n->vtables) {
1638 if (i->is_fallback != fallback) {
1643 if (streq(i->interface, interface)) {
1645 if (i->vtable == vtable) {
1654 c = new0(struct node_vtable, 1);
1661 c->is_fallback = fallback;
1663 c->userdata = userdata;
1666 c->interface = strdup(interface);
1667 if (!c->interface) {
1672 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1676 case _SD_BUS_VTABLE_METHOD: {
1677 struct vtable_member *m;
1679 if (!member_name_is_valid(v->x.method.member) ||
1680 !signature_is_valid(strempty(v->x.method.signature), false) ||
1681 !signature_is_valid(strempty(v->x.method.result), false) ||
1682 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1683 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1688 m = new0(struct vtable_member, 1);
1696 m->interface = c->interface;
1697 m->member = v->x.method.member;
1700 r = hashmap_put(bus->vtable_methods, m, m);
1709 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1711 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1718 case _SD_BUS_VTABLE_PROPERTY: {
1719 struct vtable_member *m;
1721 if (!member_name_is_valid(v->x.property.member) ||
1722 !signature_is_single(v->x.property.signature, false) ||
1723 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1724 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1725 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) ||
1726 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1732 m = new0(struct vtable_member, 1);
1740 m->interface = c->interface;
1741 m->member = v->x.property.member;
1744 r = hashmap_put(bus->vtable_properties, m, m);
1753 case _SD_BUS_VTABLE_SIGNAL:
1755 if (!member_name_is_valid(v->x.signal.member) ||
1756 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1757 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1770 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1771 bus->nodes_modified = true;
1777 free_node_vtable(bus, c);
1779 bus_node_gc(bus, n);
1783 static int remove_object_vtable_internal(
1786 const char *interface,
1787 const sd_bus_vtable *vtable,
1789 sd_bus_object_find_t find,
1792 struct node_vtable *c;
1795 assert_return(bus, -EINVAL);
1796 assert_return(object_path_is_valid(path), -EINVAL);
1797 assert_return(interface_name_is_valid(interface), -EINVAL);
1798 assert_return(!bus_pid_changed(bus), -ECHILD);
1800 n = hashmap_get(bus->nodes, path);
1804 LIST_FOREACH(vtables, c, n->vtables)
1805 if (streq(c->interface, interface) &&
1806 c->is_fallback == fallback &&
1807 c->vtable == vtable &&
1809 c->userdata == userdata)
1815 LIST_REMOVE(vtables, n->vtables, c);
1817 free_node_vtable(bus, c);
1818 bus_node_gc(bus, n);
1820 bus->nodes_modified = true;
1825 _public_ int sd_bus_add_object_vtable(
1828 const char *interface,
1829 const sd_bus_vtable *vtable,
1832 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1835 _public_ int sd_bus_remove_object_vtable(
1838 const char *interface,
1839 const sd_bus_vtable *vtable,
1842 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1845 _public_ int sd_bus_add_fallback_vtable(
1848 const char *interface,
1849 const sd_bus_vtable *vtable,
1850 sd_bus_object_find_t find,
1853 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1856 _public_ int sd_bus_remove_fallback_vtable(
1859 const char *interface,
1860 const sd_bus_vtable *vtable,
1861 sd_bus_object_find_t find,
1864 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1867 _public_ int sd_bus_add_node_enumerator(
1870 sd_bus_node_enumerator_t callback,
1873 struct node_enumerator *c;
1877 assert_return(bus, -EINVAL);
1878 assert_return(object_path_is_valid(path), -EINVAL);
1879 assert_return(callback, -EINVAL);
1880 assert_return(!bus_pid_changed(bus), -ECHILD);
1882 n = bus_node_allocate(bus, path);
1886 c = new0(struct node_enumerator, 1);
1893 c->callback = callback;
1894 c->userdata = userdata;
1896 LIST_PREPEND(enumerators, n->enumerators, c);
1898 bus->nodes_modified = true;
1904 bus_node_gc(bus, n);
1908 _public_ int sd_bus_remove_node_enumerator(
1911 sd_bus_node_enumerator_t callback,
1914 struct node_enumerator *c;
1917 assert_return(bus, -EINVAL);
1918 assert_return(object_path_is_valid(path), -EINVAL);
1919 assert_return(callback, -EINVAL);
1920 assert_return(!bus_pid_changed(bus), -ECHILD);
1922 n = hashmap_get(bus->nodes, path);
1926 LIST_FOREACH(enumerators, c, n->enumerators)
1927 if (c->callback == callback && c->userdata == userdata)
1933 LIST_REMOVE(enumerators, n->enumerators, c);
1936 bus_node_gc(bus, n);
1938 bus->nodes_modified = true;
1943 static int emit_properties_changed_on_interface(
1947 const char *interface,
1948 bool require_fallback,
1951 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1952 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1953 bool has_invalidating = false, has_changing = false;
1954 struct vtable_member key = {};
1955 struct node_vtable *c;
1966 n = hashmap_get(bus->nodes, prefix);
1970 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1974 r = sd_bus_message_append(m, "s", interface);
1978 r = sd_bus_message_open_container(m, 'a', "{sv}");
1983 key.interface = interface;
1985 LIST_FOREACH(vtables, c, n->vtables) {
1986 if (require_fallback && !c->is_fallback)
1989 if (!streq(c->interface, interface))
1992 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1995 if (bus->nodes_modified)
2000 STRV_FOREACH(property, names) {
2001 struct vtable_member *v;
2003 assert_return(member_name_is_valid(*property), -EINVAL);
2005 key.member = *property;
2006 v = hashmap_get(bus->vtable_properties, &key);
2010 /* If there are two vtables for the same
2011 * interface, let's handle this property when
2012 * we come to that vtable. */
2016 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
2018 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
2019 has_invalidating = true;
2023 has_changing = true;
2025 r = sd_bus_message_open_container(m, 'e', "sv");
2029 r = sd_bus_message_append(m, "s", *property);
2033 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
2037 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error);
2040 if (bus->nodes_modified)
2043 r = sd_bus_message_close_container(m);
2047 r = sd_bus_message_close_container(m);
2053 if (!has_invalidating && !has_changing)
2056 r = sd_bus_message_close_container(m);
2060 r = sd_bus_message_open_container(m, 'a', "s");
2064 if (has_invalidating) {
2065 LIST_FOREACH(vtables, c, n->vtables) {
2066 if (require_fallback && !c->is_fallback)
2069 if (!streq(c->interface, interface))
2072 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2075 if (bus->nodes_modified)
2080 STRV_FOREACH(property, names) {
2081 struct vtable_member *v;
2083 key.member = *property;
2084 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2085 assert(c == v->parent);
2087 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
2090 r = sd_bus_message_append(m, "s", *property);
2097 r = sd_bus_message_close_container(m);
2101 r = sd_bus_send(bus, m, NULL);
2108 _public_ int sd_bus_emit_properties_changed_strv(
2111 const char *interface,
2114 BUS_DONT_DESTROY(bus);
2118 assert_return(bus, -EINVAL);
2119 assert_return(object_path_is_valid(path), -EINVAL);
2120 assert_return(interface_name_is_valid(interface), -EINVAL);
2121 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2122 assert_return(!bus_pid_changed(bus), -ECHILD);
2124 if (strv_isempty(names))
2128 bus->nodes_modified = false;
2130 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2133 if (bus->nodes_modified)
2136 prefix = alloca(strlen(path) + 1);
2137 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2138 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2141 if (bus->nodes_modified)
2145 } while (bus->nodes_modified);
2150 _public_ int sd_bus_emit_properties_changed(
2153 const char *interface,
2154 const char *name, ...) {
2158 assert_return(bus, -EINVAL);
2159 assert_return(object_path_is_valid(path), -EINVAL);
2160 assert_return(interface_name_is_valid(interface), -EINVAL);
2161 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2162 assert_return(!bus_pid_changed(bus), -ECHILD);
2167 names = strv_from_stdarg_alloca(name);
2169 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2172 static int interfaces_added_append_one_prefix(
2177 const char *interface,
2178 bool require_fallback) {
2180 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2181 bool found_interface = false;
2182 struct node_vtable *c;
2193 n = hashmap_get(bus->nodes, prefix);
2197 LIST_FOREACH(vtables, c, n->vtables) {
2198 if (require_fallback && !c->is_fallback)
2201 if (!streq(c->interface, interface))
2204 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2207 if (bus->nodes_modified)
2212 if (!found_interface) {
2213 r = sd_bus_message_append_basic(m, 's', interface);
2217 r = sd_bus_message_open_container(m, 'a', "{sv}");
2221 found_interface = true;
2224 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2227 if (bus->nodes_modified)
2231 if (found_interface) {
2232 r = sd_bus_message_close_container(m);
2237 return found_interface;
2240 static int interfaces_added_append_one(
2244 const char *interface) {
2254 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2257 if (bus->nodes_modified)
2260 prefix = alloca(strlen(path) + 1);
2261 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2262 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2265 if (bus->nodes_modified)
2272 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2273 BUS_DONT_DESTROY(bus);
2275 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2279 assert_return(bus, -EINVAL);
2280 assert_return(object_path_is_valid(path), -EINVAL);
2281 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2282 assert_return(!bus_pid_changed(bus), -ECHILD);
2284 if (strv_isempty(interfaces))
2288 bus->nodes_modified = false;
2291 m = sd_bus_message_unref(m);
2293 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2297 r = sd_bus_message_append_basic(m, 'o', path);
2301 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2305 STRV_FOREACH(i, interfaces) {
2306 assert_return(interface_name_is_valid(*i), -EINVAL);
2308 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2312 r = interfaces_added_append_one(bus, m, path, *i);
2316 if (bus->nodes_modified)
2319 r = sd_bus_message_close_container(m);
2324 if (bus->nodes_modified)
2327 r = sd_bus_message_close_container(m);
2331 } while (bus->nodes_modified);
2333 return sd_bus_send(bus, m, NULL);
2336 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2339 assert_return(bus, -EINVAL);
2340 assert_return(object_path_is_valid(path), -EINVAL);
2341 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2342 assert_return(!bus_pid_changed(bus), -ECHILD);
2344 interfaces = strv_from_stdarg_alloca(interface);
2346 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2349 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2350 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2353 assert_return(bus, -EINVAL);
2354 assert_return(object_path_is_valid(path), -EINVAL);
2355 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2356 assert_return(!bus_pid_changed(bus), -ECHILD);
2358 if (strv_isempty(interfaces))
2361 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2365 r = sd_bus_message_append_basic(m, 'o', path);
2369 r = sd_bus_message_append_strv(m, interfaces);
2373 return sd_bus_send(bus, m, NULL);
2376 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2379 assert_return(bus, -EINVAL);
2380 assert_return(object_path_is_valid(path), -EINVAL);
2381 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2382 assert_return(!bus_pid_changed(bus), -ECHILD);
2384 interfaces = strv_from_stdarg_alloca(interface);
2386 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2389 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2392 assert_return(bus, -EINVAL);
2393 assert_return(object_path_is_valid(path), -EINVAL);
2394 assert_return(!bus_pid_changed(bus), -ECHILD);
2396 n = bus_node_allocate(bus, path);
2400 n->object_manager = true;
2401 bus->nodes_modified = true;
2405 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2408 assert_return(bus, -EINVAL);
2409 assert_return(object_path_is_valid(path), -EINVAL);
2410 assert_return(!bus_pid_changed(bus), -ECHILD);
2412 n = hashmap_get(bus->nodes, path);
2416 if (!n->object_manager)
2419 n->object_manager = false;
2420 bus->nodes_modified = true;
2421 bus_node_gc(bus, n);