X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibelogind%2Fsd-bus%2Fbus-util.c;h=32a17c6ea4af31812c0a5fd137f86415048e978b;hb=81295bb5aced5d5350db11b0e9bb284e22d2ac37;hp=164fbf8f8d7b512ee0c9b266060fc2ce5b6719f8;hpb=7c94b3e78c875c77893421e9e337eeafc4d47b2b;p=elogind.git diff --git a/src/libelogind/sd-bus/bus-util.c b/src/libelogind/sd-bus/bus-util.c index 164fbf8f8..32a17c6ea 100644 --- a/src/libelogind/sd-bus/bus-util.c +++ b/src/libelogind/sd-bus/bus-util.c @@ -30,21 +30,22 @@ #include "path-util.h" #include "missing.h" #include "set.h" +#include "unit-name.h" #include "sd-bus.h" #include "bus-error.h" +#include "bus-label.h" #include "bus-message.h" #include "bus-util.h" #include "bus-internal.h" -static int name_owner_change_callback(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { +static int name_owner_change_callback(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { sd_event *e = userdata; - assert(bus); assert(m); assert(e); - sd_bus_close(bus); + sd_bus_close(sd_bus_message_get_bus(m)); sd_event_exit(e, 0); return 1; @@ -204,6 +205,9 @@ static int check_good_user(sd_bus_message *m, uid_t good_user) { if (r < 0) return r; + /* Don't trust augmented credentials for authorization */ + assert_return((sd_bus_creds_get_augmented_mask(creds) & SD_BUS_CREDS_EUID) == 0, -EPERM); + r = sd_bus_creds_get_euid(creds, &sender_uid); if (r < 0) return r; @@ -211,11 +215,10 @@ static int check_good_user(sd_bus_message *m, uid_t good_user) { return sender_uid == good_user; } -int bus_verify_polkit( +int bus_test_polkit( sd_bus_message *call, int capability, const char *action, - bool interactive, uid_t good_user, bool *_challenge, sd_bus_error *e) { @@ -225,6 +228,8 @@ int bus_verify_polkit( assert(call); assert(action); + /* Tests non-interactively! */ + r = check_good_user(call, good_user); if (r != 0) return r; @@ -237,19 +242,13 @@ int bus_verify_polkit( #ifdef ENABLE_POLKIT else { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - int authorized = false, challenge = false, c; + int authorized = false, challenge = false; const char *sender; sender = sd_bus_message_get_sender(call); if (!sender) return -EBADMSG; - c = sd_bus_message_get_allow_interactive_authorization(call); - if (c < 0) - return c; - if (c > 0) - interactive = true; - r = sd_bus_call_method( call->bus, "org.freedesktop.PolicyKit1", @@ -262,7 +261,7 @@ int bus_verify_polkit( "system-bus-name", 1, "name", "s", sender, action, 0, - !!interactive, + 0, ""); if (r < 0) { @@ -322,12 +321,11 @@ static void async_polkit_query_free(AsyncPolkitQuery *q) { free(q); } -static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userdata, sd_bus_error *error) { +static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_error *error) { _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL; AsyncPolkitQuery *q = userdata; int r; - assert(bus); assert(reply); assert(q); @@ -340,7 +338,7 @@ static int async_polkit_callback(sd_bus *bus, sd_bus_message *reply, void *userd goto finish; } - r = q->callback(bus, q->request, q->userdata, &error_buffer); + r = q->callback(q->request, q->userdata, &error_buffer); r = bus_maybe_reply_error(q->request, r, &error_buffer); finish: @@ -963,7 +961,6 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_ switch (type) { case SD_BUS_TYPE_STRING: { const char *s; - char *str; char **p = userdata; r = sd_bus_message_read_basic(m, type, &s); @@ -973,14 +970,7 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_ if (isempty(s)) break; - str = strdup(s); - if (!str) { - r = -ENOMEM; - break; - } - free(*p); - *p = str; - + r = free_and_strdup(p, s); break; } @@ -1045,14 +1035,14 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_ return r; } -int bus_message_map_all_properties(sd_bus *bus, - sd_bus_message *m, - const struct bus_properties_map *map, - void *userdata) { +int bus_message_map_all_properties( + sd_bus_message *m, + const struct bus_properties_map *map, + void *userdata) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int r; - assert(bus); assert(m); assert(map); @@ -1088,9 +1078,9 @@ int bus_message_map_all_properties(sd_bus *bus, v = (uint8_t *)userdata + prop->offset; if (map[i].set) - r = prop->set(bus, member, m, &error, v); + r = prop->set(sd_bus_message_get_bus(m), member, m, &error, v); else - r = map_basic(bus, member, m, &error, v); + r = map_basic(sd_bus_message_get_bus(m), member, m, &error, v); if (r < 0) return r; @@ -1107,22 +1097,24 @@ int bus_message_map_all_properties(sd_bus *bus, if (r < 0) return r; } + if (r < 0) + return r; return sd_bus_message_exit_container(m); } -int bus_message_map_properties_changed(sd_bus *bus, - sd_bus_message *m, - const struct bus_properties_map *map, - void *userdata) { +int bus_message_map_properties_changed( + sd_bus_message *m, + const struct bus_properties_map *map, + void *userdata) { + const char *member; int r, invalidated, i; - assert(bus); assert(m); assert(map); - r = bus_message_map_all_properties(bus, m, map, userdata); + r = bus_message_map_all_properties(m, map, userdata); if (r < 0) return r; @@ -1137,6 +1129,8 @@ int bus_message_map_properties_changed(sd_bus *bus, ++invalidated; break; } + if (r < 0) + return r; r = sd_bus_message_exit_container(m); if (r < 0) @@ -1145,11 +1139,13 @@ int bus_message_map_properties_changed(sd_bus *bus, return invalidated; } -int bus_map_all_properties(sd_bus *bus, - const char *destination, - const char *path, - const struct bus_properties_map *map, - void *userdata) { +int bus_map_all_properties( + sd_bus *bus, + const char *destination, + const char *path, + const struct bus_properties_map *map, + void *userdata) { + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int r; @@ -1171,7 +1167,7 @@ int bus_map_all_properties(sd_bus *bus, if (r < 0) return r; - return bus_message_map_all_properties(bus, m, map, userdata); + return bus_message_map_all_properties(m, map, userdata); } int bus_open_transport(BusTransport transport, const char *host, bool user, sd_bus **bus) { @@ -1608,24 +1604,22 @@ typedef struct BusWaitForJobs { sd_bus_slot *slot_disconnected; } BusWaitForJobs; -static int match_disconnected(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { - assert(bus); +static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { assert(m); log_error("Warning! D-Bus connection terminated."); - sd_bus_close(bus); + sd_bus_close(sd_bus_message_get_bus(m)); return 0; } -static int match_job_removed(sd_bus *bus, sd_bus_message *m, void *userdata, sd_bus_error *error) { +static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { const char *path, *unit, *result; BusWaitForJobs *d = userdata; uint32_t id; char *found; int r; - assert(bus); assert(m); assert(d); @@ -1733,6 +1727,73 @@ static int bus_process_wait(sd_bus *bus) { } } +static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { + _cleanup_free_ char *dbus_path = NULL; + + assert(d); + assert(d->name); + assert(result); + + dbus_path = unit_dbus_path_from_name(d->name); + if (!dbus_path) + return -ENOMEM; + + return sd_bus_get_property_string(d->bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Service", + "Result", + NULL, + result); +} + +static const struct { + const char *result, *explanation; +} explanations [] = { + { "resources", "a configured resource limit was exceeded" }, + { "timeout", "a timeout was exceeded" }, + { "exit-code", "the control process exited with error code" }, + { "signal", "a fatal signal was delivered to the control process" }, + { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, + { "watchdog", "the service failed to send watchdog ping" }, + { "start-limit", "start of the service was attempted too often" } +}; + +static void log_job_error_with_service_result(const char* service, const char *result) { + _cleanup_free_ char *service_shell_quoted = NULL; + + assert(service); + + service_shell_quoted = shell_maybe_quote(service); + + if (!isempty(result)) { + unsigned i; + + for (i = 0; i < ELEMENTSOF(explanations); ++i) + if (streq(result, explanations[i].result)) + break; + + if (i < ELEMENTSOF(explanations)) { + log_error("Job for %s failed because %s. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n", + service, + explanations[i].explanation, + strna(service_shell_quoted)); + + goto finish; + } + } + + log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n", + service, + strna(service_shell_quoted)); + +finish: + /* For some results maybe additional explanation is required */ + if (streq_ptr(result, "start-limit")) + log_info("To force a start use \"systemctl reset-failed %1$s\" followed by \"systemctl start %1$s\" again.", + strna(service_shell_quoted)); +} + static int check_wait_response(BusWaitForJobs *d, bool quiet) { int r = 0; @@ -1753,13 +1814,14 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet) { log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); else if (!streq(d->result, "done") && !streq(d->result, "skipped")) { if (d->name) { - bool quotes; + int q; + _cleanup_free_ char *result = NULL; - quotes = chars_intersect(d->name, SHELL_NEED_QUOTES); + q = bus_job_get_service_result(d, &result); + if (q < 0) + log_debug_errno(q, "Failed to get Result property of service %s: %m", d->name); - log_error("Job for %s failed. See \"systemctl status %s%s%s\" and \"journalctl -xe\" for details.", - d->name, - quotes ? "'" : "", d->name, quotes ? "'" : ""); + log_job_error_with_service_result(d->name, result); } else log_error("Job failed. See \"journalctl -xe\" for details."); } @@ -1852,3 +1914,130 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet) { return 0; } + +/** + * bus_path_encode_unique() - encode unique object path + * @b: bus connection or NULL + * @prefix: object path prefix + * @sender_id: unique-name of client, or NULL + * @external_id: external ID to be chosen by client, or NULL + * @ret_path: storage for encoded object path pointer + * + * Whenever we provide a bus API that allows clients to create and manage + * server-side objects, we need to provide a unique name for these objects. If + * we let the server choose the name, we suffer from a race condition: If a + * client creates an object asynchronously, it cannot destroy that object until + * it received the method reply. It cannot know the name of the new object, + * thus, it cannot destroy it. Furthermore, it enforces a round-trip. + * + * Therefore, many APIs allow the client to choose the unique name for newly + * created objects. There're two problems to solve, though: + * 1) Object names are usually defined via dbus object paths, which are + * usually globally namespaced. Therefore, multiple clients must be able + * to choose unique object names without interference. + * 2) If multiple libraries share the same bus connection, they must be + * able to choose unique object names without interference. + * The first problem is solved easily by prefixing a name with the + * unique-bus-name of a connection. The server side must enforce this and + * reject any other name. The second problem is solved by providing unique + * suffixes from within sd-bus. + * + * This helper allows clients to create unique object-paths. It uses the + * template '/prefix/sender_id/external_id' and returns the new path in + * @ret_path (must be freed by the caller). + * If @sender_id is NULL, the unique-name of @b is used. If @external_id is + * NULL, this function allocates a unique suffix via @b (by requesting a new + * cookie). If both @sender_id and @external_id are given, @b can be passed as + * NULL. + * + * Returns: 0 on success, negative error code on failure. + */ +int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, const char *external_id, char **ret_path) { + _cleanup_free_ char *sender_label = NULL, *external_label = NULL; + char external_buf[DECIMAL_STR_MAX(uint64_t)], *p; + int r; + + assert_return(b || (sender_id && external_id), -EINVAL); + assert_return(object_path_is_valid(prefix), -EINVAL); + assert_return(ret_path, -EINVAL); + + if (!sender_id) { + r = sd_bus_get_unique_name(b, &sender_id); + if (r < 0) + return r; + } + + if (!external_id) { + xsprintf(external_buf, "%"PRIu64, ++b->cookie); + external_id = external_buf; + } + + sender_label = bus_label_escape(sender_id); + if (!sender_label) + return -ENOMEM; + + external_label = bus_label_escape(external_id); + if (!external_label) + return -ENOMEM; + + p = strjoin(prefix, "/", sender_label, "/", external_label, NULL); + if (!p) + return -ENOMEM; + + *ret_path = p; + return 0; +} + +/** + * bus_path_decode_unique() - decode unique object path + * @path: object path to decode + * @prefix: object path prefix + * @ret_sender: output parameter for sender-id label + * @ret_external: output parameter for external-id label + * + * This does the reverse of bus_path_encode_unique() (see its description for + * details). Both trailing labels, sender-id and external-id, are unescaped and + * returned in the given output parameters (the caller must free them). + * + * Note that this function returns 0 if the path does not match the template + * (see bus_path_encode_unique()), 1 if it matched. + * + * Returns: Negative error code on failure, 0 if the given object path does not + * match the template (return parameters are set to NULL), 1 if it was + * parsed successfully (return parameters contain allocated labels). + */ +int bus_path_decode_unique(const char *path, const char *prefix, char **ret_sender, char **ret_external) { + const char *p, *q; + char *sender, *external; + + assert(object_path_is_valid(path)); + assert(object_path_is_valid(prefix)); + assert(ret_sender); + assert(ret_external); + + p = object_path_startswith(path, prefix); + if (!p) { + *ret_sender = NULL; + *ret_external = NULL; + return 0; + } + + q = strchr(p, '/'); + if (!q) { + *ret_sender = NULL; + *ret_external = NULL; + return 0; + } + + sender = bus_label_unescape_n(p, q - p); + external = bus_label_unescape(q + 1); + if (!sender || !external) { + free(sender); + free(external); + return -ENOMEM; + } + + *ret_sender = sender; + *ret_external = external; + return 1; +}