diff options
Diffstat (limited to '')
-rw-r--r-- | src/shared/bus-wait-for-jobs.c | 216 |
1 files changed, 107 insertions, 109 deletions
diff --git a/src/shared/bus-wait-for-jobs.c b/src/shared/bus-wait-for-jobs.c index 969c629..e12189f 100644 --- a/src/shared/bus-wait-for-jobs.c +++ b/src/shared/bus-wait-for-jobs.c @@ -1,13 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" -#include "bus-wait-for-jobs.h" -#include "set.h" -#include "bus-util.h" #include "bus-internal.h" -#include "unit-def.h" +#include "bus-util.h" +#include "bus-wait-for-jobs.h" #include "escape.h" +#include "set.h" #include "strv.h" +#include "unit-def.h" typedef struct BusWaitForJobs { sd_bus *bus; @@ -23,60 +23,56 @@ typedef struct BusWaitForJobs { sd_bus_slot *slot_disconnected; } BusWaitForJobs; +BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d) { + if (!d) + return NULL; + + set_free(d->jobs); + + sd_bus_slot_unref(d->slot_disconnected); + sd_bus_slot_unref(d->slot_job_removed); + + sd_bus_unref(d->bus); + + free(d->name); + free(d->result); + + return mfree(d); +} + static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { assert(m); - log_error("Warning! D-Bus connection terminated."); + log_warning("D-Bus connection terminated while waiting for jobs."); sd_bus_close(sd_bus_message_get_bus(m)); return 0; } static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - const char *path, *unit, *result; BusWaitForJobs *d = ASSERT_PTR(userdata); - uint32_t id; - char *found; + _cleanup_free_ char *job_found = NULL; + const char *path, *unit, *result; int r; assert(m); - r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); + r = sd_bus_message_read(m, "uoss", /* id = */ NULL, &path, &unit, &result); if (r < 0) { bus_log_parse_error(r); return 0; } - found = set_remove(d->jobs, (char*) path); - if (!found) + job_found = set_remove(d->jobs, (char*) path); + if (!job_found) return 0; - free(found); - - (void) free_and_strdup(&d->result, empty_to_null(result)); - (void) free_and_strdup(&d->name, empty_to_null(unit)); + (void) free_and_strdup(&d->result, empty_to_null(result)); return 0; } -BusWaitForJobs* bus_wait_for_jobs_free(BusWaitForJobs *d) { - if (!d) - return NULL; - - set_free(d->jobs); - - sd_bus_slot_unref(d->slot_disconnected); - sd_bus_slot_unref(d->slot_job_removed); - - sd_bus_unref(d->bus); - - free(d->name); - free(d->result); - - return mfree(d); -} - int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; int r; @@ -92,9 +88,8 @@ int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { .bus = sd_bus_ref(bus), }; - /* When we are a bus client we match by sender. Direct - * connections OTOH have no initialized sender field, and - * hence we ignore the sender then */ + /* When we are a bus client we match by sender. Direct connections OTOH have no initialized sender + * field, and hence we ignore the sender then */ r = sd_bus_match_signal_async( bus, &d->slot_job_removed, @@ -138,12 +133,12 @@ static int bus_process_wait(sd_bus *bus) { } } -static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { +static int bus_job_get_service_result(BusWaitForJobs *d, char **ret) { _cleanup_free_ char *dbus_path = NULL; assert(d); assert(d->name); - assert(result); + assert(ret); if (!endswith(d->name, ".service")) return -EINVAL; @@ -158,67 +153,57 @@ static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { "org.freedesktop.systemd1.Service", "Result", NULL, - result); + ret); } static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { - _cleanup_free_ char *service_shell_quoted = NULL; - const char *systemctl = "systemctl", *journalctl = "journalctl"; static const struct { const char *result, *explanation; } explanations[] = { - { "resources", "of unavailable resources or another system error" }, + { "resources", "of unavailable resources or another system error" }, { "protocol", "the service did not take the steps required by its unit configuration" }, - { "timeout", "a timeout was exceeded" }, - { "exit-code", "the control process exited with error code" }, - { "signal", "a fatal signal was delivered to the control process" }, + { "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" } + { "watchdog", "the service failed to send watchdog ping" }, + { "start-limit", "start of the service was attempted too often" }, }; + _cleanup_free_ char *service_shell_quoted = NULL; + const char *systemctl = "systemctl", *journalctl = "journalctl"; + assert(service); service_shell_quoted = shell_maybe_quote(service, 0); - if (!strv_isempty((char**) extra_args)) { + if (!strv_isempty((char* const*) extra_args)) { _cleanup_free_ char *t = NULL; - t = strv_join((char**) extra_args, " "); + t = strv_join((char* const*) extra_args, " "); systemctl = strjoina("systemctl ", t ?: "<args>"); journalctl = strjoina("journalctl ", t ?: "<args>"); } - if (!isempty(result)) { - size_t 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.\n" - "See \"%s status %s\" and \"%s -xeu %s\" for details.\n", - service, - explanations[i].explanation, - systemctl, - service_shell_quoted ?: "<service>", - journalctl, - service_shell_quoted ?: "<service>"); - goto finish; - } - } + if (!isempty(result)) + FOREACH_ELEMENT(i, explanations) + if (streq(result, i->result)) { + log_error("Job for %s failed because %s.\n" + "See \"%s status %s\" and \"%s -xeu %s\" for details.\n", + service, i->explanation, + systemctl, service_shell_quoted ?: "<service>", + journalctl, service_shell_quoted ?: "<service>"); + goto extra; + } log_error("Job for %s failed.\n" "See \"%s status %s\" and \"%s -xeu %s\" for details.\n", service, - systemctl, - service_shell_quoted ?: "<service>", - journalctl, - service_shell_quoted ?: "<service>"); + systemctl, service_shell_quoted ?: "<service>", + journalctl, service_shell_quoted ?: "<service>"); -finish: +extra: /* For some results maybe additional explanation is required */ if (streq_ptr(result, "start-limit")) log_info("To force a start use \"%1$s reset-failed %2$s\"\n" @@ -227,42 +212,56 @@ finish: service_shell_quoted ?: "<service>"); } -static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { +static int check_wait_response(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) { + int r; + assert(d); assert(d->name); assert(d->result); - if (!quiet) { + if (streq(d->result, "done")) { + if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS)) + log_info("Job for %s finished.", d->name); + + return 0; + } else if (streq(d->result, "skipped")) { + if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_SUCCESS)) + log_info("Job for %s was skipped.", d->name); + + return 0; + } + + if (FLAGS_SET(flags, BUS_WAIT_JOBS_LOG_ERROR)) { if (streq(d->result, "canceled")) - log_error("Job for %s canceled.", strna(d->name)); + log_error("Job for %s canceled.", d->name); else if (streq(d->result, "timeout")) - log_error("Job for %s timed out.", strna(d->name)); + log_error("Job for %s timed out.", d->name); else if (streq(d->result, "dependency")) - log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); + log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", d->name); else if (streq(d->result, "invalid")) - log_error("%s is not active, cannot reload.", strna(d->name)); + log_error("%s is not active, cannot reload.", d->name); else if (streq(d->result, "assert")) - log_error("Assertion failed on job for %s.", strna(d->name)); + log_error("Assertion failed on job for %s.", d->name); else if (streq(d->result, "unsupported")) - log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); + log_error("Operation on or unit type of %s not supported on this system.", d->name); else if (streq(d->result, "collected")) - log_error("Queued job for %s was garbage collected.", strna(d->name)); + log_error("Queued job for %s was garbage collected.", d->name); else if (streq(d->result, "once")) - log_error("Unit %s was started already once and can't be started again.", strna(d->name)); - else if (!STR_IN_SET(d->result, "done", "skipped")) { - - if (d->name && endswith(d->name, ".service")) { - _cleanup_free_ char *result = NULL; - int q; - - q = bus_job_get_service_result(d, &result); - if (q < 0) - log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name); - - log_job_error_with_service_result(d->name, result, extra_args); - } else - log_error("Job failed. See \"journalctl -xe\" for details."); - } + log_error("Unit %s was started already once and can't be started again.", d->name); + else if (streq(d->result, "frozen")) + log_error("Cannot perform operation on frozen unit %s.", d->name); + else if (endswith(d->name, ".service")) { + /* Job result is unknown. For services, let's also try Result property. */ + _cleanup_free_ char *result = NULL; + + r = bus_job_get_service_result(d, &result); + if (r < 0) + log_debug_errno(r, "Failed to get Result property of unit %s, ignoring: %m", + d->name); + + log_job_error_with_service_result(d->name, result, extra_args); + } else /* Otherwise we just show a generic message. */ + log_error("Job failed. See \"journalctl -xe\" for details."); } if (STR_IN_SET(d->result, "canceled", "collected")) @@ -279,14 +278,15 @@ static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* return -EOPNOTSUPP; else if (streq(d->result, "once")) return -ESTALE; - else if (STR_IN_SET(d->result, "done", "skipped")) - return 0; + else if (streq(d->result, "frozen")) + return -EDEADLK; - return log_debug_errno(SYNTHETIC_ERRNO(EIO), - "Unexpected job result, assuming server side newer than us: %s", d->result); + return log_debug_errno(SYNTHETIC_ERRNO(ENOMEDIUM), + "Unexpected job result '%s' for unit '%s', assuming server side newer than us.", + d->result, d->name); } -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { +int bus_wait_for_jobs(BusWaitForJobs *d, WaitJobsFlags flags, const char* const* extra_args) { int r = 0; assert(d); @@ -299,14 +299,12 @@ int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_ar return log_error_errno(q, "Failed to wait for response: %m"); if (d->name && d->result) { - q = check_wait_response(d, quiet, extra_args); - /* Return the first error as it is most likely to be - * meaningful. */ - if (q < 0 && r == 0) - r = q; + q = check_wait_response(d, flags, extra_args); + /* Return the first error as it is most likely to be meaningful. */ + RET_GATHER(r, q); log_full_errno_zerook(LOG_DEBUG, q, - "Got result %s/%m for job %s", d->result, d->name); + "Got result %s/%m for job %s.", d->result, d->name); } d->name = mfree(d->name); @@ -322,12 +320,12 @@ int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { return set_put_strdup(&d->jobs, path); } -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet, const char* const* extra_args) { +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, WaitJobsFlags flags, const char* const* extra_args) { int r; r = bus_wait_for_jobs_add(d, path); if (r < 0) return log_oom(); - return bus_wait_for_jobs(d, quiet, extra_args); + return bus_wait_for_jobs(d, flags, extra_args); } |