X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fjob.c;h=db7ebae6b174dc75660a38167639d9873170602f;hp=8e98bd93ab1163c02fe8266167a6e8d606b0966c;hb=c43b2132f37264600cc26e07c8d85dfdd6c969f0;hpb=2c5859afecee81e345fc9526b1083bf79990ffb8
diff --git a/src/core/job.c b/src/core/job.c
index 8e98bd93a..db7ebae6b 100644
--- a/src/core/job.c
+++ b/src/core/job.c
@@ -19,10 +19,7 @@
along with systemd; If not, see .
***/
-#include
#include
-#include
-#include
#include "sd-id128.h"
#include "sd-messages.h"
@@ -30,8 +27,6 @@
#include "unit.h"
#include "macro.h"
#include "strv.h"
-#include "load-fragment.h"
-#include "load-dropin.h"
#include "log.h"
#include "dbus-job.h"
#include "special.h"
@@ -90,17 +85,45 @@ void job_free(Job *j) {
sd_event_source_unref(j->timer_event_source);
- sd_bus_track_unref(j->subscribed);
- strv_free(j->deserialized_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);
@@ -155,20 +178,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;
@@ -178,11 +202,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;
}
}
@@ -191,8 +215,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;
@@ -209,16 +234,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;
@@ -352,6 +379,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");
}
@@ -454,7 +484,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));
@@ -481,8 +511,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
@@ -542,10 +571,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);
}
@@ -586,12 +618,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.";
@@ -632,20 +668,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:
@@ -706,35 +759,34 @@ static void job_log_status_message(Unit *u, JobType t, JobResult result) {
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);
}
int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
@@ -752,10 +804,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);
@@ -767,7 +816,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);
@@ -820,15 +869,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);
}
@@ -851,14 +900,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;
}
@@ -873,7 +926,12 @@ int job_start_timer(Job *j) {
if (j->unit->job_timeout <= 0)
return 0;
- r = sd_event_add_monotonic(j->manager->event, &j->timer_event_source, j->begin_usec + j->unit->job_timeout, 0, job_dispatch_timer, j);
+ 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;
@@ -932,7 +990,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_track_serialize(j->subscribed, f);
+ bus_track_serialize(j->clients, f);
/* End marker */
fputc('\n', f);
@@ -990,7 +1048,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;
@@ -1038,7 +1096,7 @@ int job_deserialize(Job *j, FILE *f, FDSet *fds) {
} else if (streq(l, "subscribed")) {
- if (strv_extend(&j->deserialized_subscribed, v) < 0)
+ if (strv_extend(&j->deserialized_clients, v) < 0)
return log_oom();
}
}
@@ -1051,19 +1109,27 @@ int job_coldplug(Job *j) {
/* 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->subscribed, &j->deserialized_subscribed);
+ 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->timer_event_source, j->begin_usec + j->unit->job_timeout, 0, job_dispatch_timer, j);
+ 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;
}
@@ -1165,6 +1231,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);