X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flibelogind%2Fsd-bus%2Fbus-util.c;h=ee002a55f54370e83f86ed080482faa086b0222b;hb=2a7a7d0b0f645b14cf0388b40d747b0ccb0c091e;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..ee002a55f 100644 --- a/src/libelogind/sd-bus/bus-util.c +++ b/src/libelogind/sd-bus/bus-util.c @@ -30,9 +30,11 @@ #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" @@ -211,11 +213,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 +226,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 +240,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 +259,7 @@ int bus_verify_polkit( "system-bus-name", 1, "name", "s", sender, action, 0, - !!interactive, + 0, ""); if (r < 0) { @@ -963,7 +960,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 +969,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; } @@ -1733,6 +1722,68 @@ 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", "configured resource limit was exceeded" }, + { "timeout", "timeout was exceeded" }, + { "exit-code", "control process exited with error code" }, + { "signal", "fatal signal was delivered to the control process" }, + { "core-dump", "fatal signal was delivered to the control process. Core dumped" }, + { "watchdog", "service failed to send watchdog ping" }, + { "start-limit", "start of the service was attempted too often too quickly" } +}; + +static void log_job_error_with_service_result(const char* service, const char *result) { + unsigned i; + _cleanup_free_ char *service_shell_quoted = NULL; + + assert(service); + assert(result); + + service_shell_quoted = shell_maybe_quote(service); + + 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)); + else + log_error("Job for %s failed. See \"systemctl status %s\" and \"journalctl -xe\" for details.\n", + service, + strna(service_shell_quoted)); + + /* For some results maybe additional explanation is required */ + if (streq_ptr(result, "start-limit")) + log_info("To force a start please invoke \"systemctl reset-failed %s\" followed by \"systemctl start %s\" again.", + strna(service_shell_quoted), + strna(service_shell_quoted)); +} + static int check_wait_response(BusWaitForJobs *d, bool quiet) { int r = 0; @@ -1753,13 +1804,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 +1904,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; +}