X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fjob.c;h=2129773edef3082f15360c1430149bf830ea6018;hp=93fa44a506a050ceae598d315c76130cddf46dde;hb=76c37ab6d205c95e8f43ffbc19553b936330195a;hpb=cb8ccb2271727fc114ca43104d3333ee4635cc79 diff --git a/src/core/job.c b/src/core/job.c index 93fa44a50..2129773ed 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -37,7 +37,7 @@ #include "special.h" #include "async.h" #include "virt.h" -#include "dbus-client-track.h" +#include "dbus.h" Job* job_new_raw(Unit *unit) { Job *j; @@ -90,16 +90,45 @@ void job_free(Job *j) { sd_event_source_unref(j->timer_event_source); - bus_client_track_free(j->subscribed); + sd_bus_track_unref(j->clients); + strv_free(j->deserialized_clients); free(j); } +static void job_set_state(Job *j, JobState state) { + assert(j); + assert(state >= 0); + assert(state < _JOB_STATE_MAX); + + if (j->state == state) + return; + + j->state = state; + + if (!j->installed) + return; + + if (j->state == JOB_RUNNING) + j->unit->manager->n_running_jobs++; + else { + assert(j->state == JOB_WAITING); + assert(j->unit->manager->n_running_jobs > 0); + + j->unit->manager->n_running_jobs--; + + if (j->unit->manager->n_running_jobs <= 0) + j->unit->manager->jobs_in_progress_event_source = sd_event_source_unref(j->unit->manager->jobs_in_progress_event_source); + } +} + void job_uninstall(Job *j) { Job **pj; assert(j->installed); + job_set_state(j, JOB_WAITING); + pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; assert(*pj == j); @@ -154,20 +183,21 @@ Job* job_install(Job *j) { assert(!j->installed); assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION); + assert(j->state == JOB_WAITING); pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; uj = *pj; if (uj) { - if (j->type != JOB_NOP && job_type_is_conflicting(uj->type, j->type)) + if (job_type_is_conflicting(uj->type, j->type)) job_finish_and_invalidate(uj, JOB_CANCELED, false); else { /* not conflicting, i.e. mergeable */ - if (j->type == JOB_NOP || uj->state == JOB_WAITING || + if (uj->state == JOB_WAITING || (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) { job_merge_into_installed(uj, j); - log_debug_unit(uj->unit->id, + log_unit_debug(uj->unit->id, "Merged into installed job %s/%s as %u", uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); return uj; @@ -177,11 +207,11 @@ Job* job_install(Job *j) { /* XXX It should be safer to queue j to run after uj finishes, but it is * not currently possible to have more than one installed job per unit. */ job_merge_into_installed(uj, j); - log_debug_unit(uj->unit->id, + log_unit_debug(uj->unit->id, "Merged into running job, re-running: %s/%s as %u", uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id); - uj->state = JOB_WAITING; - uj->manager->n_running_jobs--; + + job_set_state(uj, JOB_WAITING); return uj; } } @@ -190,8 +220,9 @@ Job* job_install(Job *j) { /* Install the job */ *pj = j; j->installed = true; + j->manager->n_installed_jobs ++; - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); return j; @@ -208,16 +239,18 @@ int job_install_deserialized(Job *j) { } pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job; - if (*pj) { - log_debug_unit(j->unit->id, - "Unit %s already has a job installed. Not installing deserialized job.", - j->unit->id); + log_unit_debug(j->unit->id, "Unit %s already has a job installed. Not installing deserialized job.", j->unit->id); return -EEXIST; } + *pj = j; j->installed = true; - log_debug_unit(j->unit->id, + + if (j->state == JOB_RUNNING) + j->unit->manager->n_running_jobs++; + + log_unit_debug(j->unit->id, "Reinstalled deserialized job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id); return 0; @@ -351,6 +384,9 @@ bool job_type_is_redundant(JobType a, UnitActiveState b) { return b == UNIT_ACTIVATING; + case JOB_NOP: + return true; + default: assert_not_reached("Invalid job type"); } @@ -453,7 +489,7 @@ static bool job_is_runnable(Job *j) { } static void job_change_type(Job *j, JobType newtype) { - log_debug_unit(j->unit->id, + log_unit_debug(j->unit->id, "Converting job %s/%s -> %s/%s", j->unit->id, job_type_to_string(j->type), j->unit->id, job_type_to_string(newtype)); @@ -480,8 +516,7 @@ int job_run_and_invalidate(Job *j) { if (!job_is_runnable(j)) return -EAGAIN; - j->state = JOB_RUNNING; - m->n_running_jobs++; + job_set_state(j, JOB_RUNNING); job_add_to_dbus_queue(j); /* While we execute this operation the job might go away (for @@ -541,10 +576,13 @@ int job_run_and_invalidate(Job *j) { r = job_finish_and_invalidate(j, JOB_SKIPPED, true); else if (r == -ENOEXEC) r = job_finish_and_invalidate(j, JOB_INVALID, true); - else if (r == -EAGAIN) { - j->state = JOB_WAITING; - m->n_running_jobs--; - } else if (r < 0) + else if (r == -EPROTO) + r = job_finish_and_invalidate(j, JOB_ASSERT, true); + else if (r == -ENOTSUP) + r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true); + else if (r == -EAGAIN) + job_set_state(j, JOB_WAITING); + else if (r < 0) r = job_finish_and_invalidate(j, JOB_FAILED, true); } @@ -585,12 +623,16 @@ _pure_ static const char *job_get_status_message_format_try_harder(Unit *u, JobT if (t == JOB_START) { if (result == JOB_DONE) return "Started %s."; + else if (result == JOB_TIMEOUT) + return "Timed out starting %s."; else if (result == JOB_FAILED) return "Failed to start %s."; else if (result == JOB_DEPENDENCY) return "Dependency failed for %s."; - else if (result == JOB_TIMEOUT) - return "Timed out starting %s."; + else if (result == JOB_ASSERT) + return "Assertion failed for %s."; + else if (result == JOB_UNSUPPORTED) + return "Starting of %s not supported."; } else if (t == JOB_STOP || t == JOB_RESTART) { if (result == JOB_DONE) return "Stopped %s."; @@ -610,8 +652,6 @@ _pure_ static const char *job_get_status_message_format_try_harder(Unit *u, JobT return NULL; } -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" static void job_print_status_message(Unit *u, JobType t, JobResult result) { const char *format; @@ -619,6 +659,8 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { assert(t >= 0); assert(t < _JOB_TYPE_MAX); + DISABLE_WARNING_FORMAT_NONLITERAL; + if (t == JOB_START) { format = job_get_status_message_format(u, t, result); if (!format) @@ -631,20 +673,37 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format); break; - case JOB_FAILED: + case JOB_TIMEOUT: + manager_flip_auto_status(u->manager, true); + unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format); + break; + + case JOB_FAILED: { + bool quotes; + + quotes = chars_intersect(u->id, SHELL_NEED_QUOTES); + manager_flip_auto_status(u->manager, true); unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format); - manager_status_printf(u->manager, false, NULL, "See 'systemctl status %s' for details.", u->id); + manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, + "See \"systemctl status %s%s%s\" for details.", + quotes ? "'" : "", u->id, quotes ? "'" : ""); break; + } case JOB_DEPENDENCY: manager_flip_auto_status(u->manager, true); unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format); break; - case JOB_TIMEOUT: + case JOB_ASSERT: manager_flip_auto_status(u->manager, true); - unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format); + unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF, format); + break; + + case JOB_UNSUPPORTED: + manager_flip_auto_status(u->manager, true); + unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "UNSUPP" ANSI_HIGHLIGHT_OFF, format); break; default: @@ -681,11 +740,10 @@ static void job_print_status_message(Unit *u, JobType t, JobResult result) { if (result == JOB_SKIPPED) unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active."); } + + REENABLE_WARNING; } -#pragma GCC diagnostic pop -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wformat-nonliteral" static void job_log_status_message(Unit *u, JobType t, JobResult result) { const char *format; char buf[LINE_MAX]; @@ -704,37 +762,38 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) { if (!format) return; + DISABLE_WARNING_FORMAT_NONLITERAL; snprintf(buf, sizeof(buf), format, unit_description(u)); char_array_0(buf); + REENABLE_WARNING; if (t == JOB_START) { sd_id128_t mid; mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED; - log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR, - u->id, - MESSAGE_ID(mid), - "RESULT=%s", job_result_to_string(result), - "MESSAGE=%s", buf, - NULL); + log_unit_struct(u->id, + result == JOB_DONE ? LOG_INFO : LOG_ERR, + LOG_MESSAGE_ID(mid), + LOG_MESSAGE("%s", buf), + "RESULT=%s", job_result_to_string(result), + NULL); } else if (t == JOB_STOP) - log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR, - u->id, - MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED), - "RESULT=%s", job_result_to_string(result), - "MESSAGE=%s", buf, - NULL); + log_unit_struct(u->id, + result == JOB_DONE ? LOG_INFO : LOG_ERR, + LOG_MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED), + LOG_MESSAGE("%s", buf), + "RESULT=%s", job_result_to_string(result), + NULL); else if (t == JOB_RELOAD) - log_struct_unit(result == JOB_DONE ? LOG_INFO : LOG_ERR, - u->id, - MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED), - "RESULT=%s", job_result_to_string(result), - "MESSAGE=%s", buf, - NULL); + log_unit_struct(u->id, + result == JOB_DONE ? LOG_INFO : LOG_ERR, + LOG_MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED), + LOG_MESSAGE("%s", buf), + "RESULT=%s", job_result_to_string(result), + NULL); } -#pragma GCC diagnostic pop int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { Unit *u; @@ -751,10 +810,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { j->result = result; - if (j->state == JOB_RUNNING) - j->manager->n_running_jobs--; - - log_debug_unit(u->id, "Job %s/%s finished, result=%s", + log_unit_debug(u->id, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result)); job_print_status_message(u, t, result); @@ -766,7 +822,7 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { if (result == JOB_DONE && t == JOB_RESTART) { job_change_type(j, JOB_START); - j->state = JOB_WAITING; + job_set_state(j, JOB_WAITING); job_add_to_run_queue(j); @@ -819,15 +875,15 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) { * this context. And JOB_FAILURE is already handled by the * unit itself. */ if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) { - log_struct_unit(LOG_NOTICE, - u->id, - "JOB_TYPE=%s", job_type_to_string(t), - "JOB_RESULT=%s", job_result_to_string(result), - "Job %s/%s failed with result '%s'.", - u->id, - job_type_to_string(t), - job_result_to_string(result), - NULL); + log_unit_struct(u->id, + LOG_NOTICE, + "JOB_TYPE=%s", job_type_to_string(t), + "JOB_RESULT=%s", job_result_to_string(result), + LOG_MESSAGE("Job %s/%s failed with result '%s'.", + u->id, + job_type_to_string(t), + job_result_to_string(result)), + NULL); unit_start_on_failure(u); } @@ -850,14 +906,18 @@ finish: static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *userdata) { Job *j = userdata; + Unit *u; assert(j); assert(s == j->timer_event_source); - log_warning_unit(j->unit->id, "Job %s/%s timed out.", - j->unit->id, job_type_to_string(j->type)); + log_unit_warning(j->unit->id, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type)); + u = j->unit; job_finish_and_invalidate(j, JOB_TIMEOUT, true); + + failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg); + return 0; } @@ -872,7 +932,12 @@ int job_start_timer(Job *j) { if (j->unit->job_timeout <= 0) return 0; - r = sd_event_add_monotonic(j->manager->event, j->begin_usec + j->unit->job_timeout, 0, job_dispatch_timer, j, &j->timer_event_source); + r = sd_event_add_time( + j->manager->event, + &j->timer_event_source, + CLOCK_MONOTONIC, + j->begin_usec + j->unit->job_timeout, 0, + job_dispatch_timer, j); if (r < 0) return r; @@ -931,7 +996,7 @@ int job_serialize(Job *j, FILE *f, FDSet *fds) { if (j->begin_usec > 0) fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec); - bus_client_track_serialize(j->manager, f, j->subscribed); + bus_track_serialize(j->clients, f); /* End marker */ fputc('\n', f); @@ -989,7 +1054,7 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) { if (s < 0) log_debug("Failed to parse job state %s", v); else - j->state = s; + job_set_state(j, s); } else if (streq(l, "job-override")) { int b; @@ -1035,13 +1100,10 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) { else j->begin_usec = ull; - } else { - char t[strlen(l) + 1 + strlen(v) + 1]; - - strcpy(stpcpy(stpcpy(t, l), "="), v); + } else if (streq(l, "subscribed")) { - if (bus_client_track_deserialize_item(j->manager, &j->subscribed, t) == 0) - log_debug("Unknown deserialization key '%s'", l); + if (strv_extend(&j->deserialized_clients, v) < 0) + return log_oom(); } } } @@ -1051,15 +1113,29 @@ int job_coldplug(Job *j) { assert(j); - if (j->begin_usec <= 0) + /* After deserialization is complete and the bus connection + * set up again, let's start watching our subscribers again */ + r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients); + if (r < 0) + return r; + + if (j->state == JOB_WAITING) + job_add_to_run_queue(j); + + if (j->begin_usec == 0 || j->unit->job_timeout == 0) return 0; if (j->timer_event_source) j->timer_event_source = sd_event_source_unref(j->timer_event_source); - r = sd_event_add_monotonic(j->manager->event, j->begin_usec + j->unit->job_timeout, 0, job_dispatch_timer, j, &j->timer_event_source); + r = sd_event_add_time( + j->manager->event, + &j->timer_event_source, + CLOCK_MONOTONIC, + j->begin_usec + j->unit->job_timeout, 0, + job_dispatch_timer, j); if (r < 0) - log_debug("Failed to restart timeout for job: %s", strerror(-r)); + log_debug_errno(r, "Failed to restart timeout for job: %m"); return r; } @@ -1118,9 +1194,6 @@ int job_get_timeout(Job *j, uint64_t *timeout) { *timeout = MIN(x, y); - log_info("job_get_timeout %s %d/%"PRIu64" %d/%"PRIu64" -> 1/%"PRIu64, - j->unit->id, r, x, q, y, *timeout); - return 1; } @@ -1149,9 +1222,9 @@ static const char* const job_mode_table[_JOB_MODE_MAX] = { [JOB_REPLACE] = "replace", [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly", [JOB_ISOLATE] = "isolate", + [JOB_FLUSH] = "flush", [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies", [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements", - [JOB_FLUSH] = "flush", }; DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode); @@ -1164,6 +1237,8 @@ static const char* const job_result_table[_JOB_RESULT_MAX] = { [JOB_DEPENDENCY] = "dependency", [JOB_SKIPPED] = "skipped", [JOB_INVALID] = "invalid", + [JOB_ASSERT] = "assert", + [JOB_UNSUPPORTED] = "unsupported", }; DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);