This file is part of systemd.
Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
j->manager = unit->manager;
j->unit = unit;
j->type = _JOB_TYPE_INVALID;
+ j->reloaded = false;
return j;
}
return j;
}
-void job_free(Job *j) {
+void job_unlink(Job *j) {
assert(j);
assert(!j->installed);
assert(!j->transaction_prev);
assert(!j->subject_list);
assert(!j->object_list);
- if (j->in_run_queue)
+ if (j->in_run_queue) {
LIST_REMOVE(run_queue, j->manager->run_queue, j);
+ j->in_run_queue = false;
+ }
- if (j->in_dbus_queue)
+ if (j->in_dbus_queue) {
LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j);
+ j->in_dbus_queue = false;
+ }
- if (j->in_gc_queue)
+ if (j->in_gc_queue) {
LIST_REMOVE(gc_queue, j->manager->gc_job_queue, j);
+ j->in_gc_queue = false;
+ }
+
+ j->timer_event_source = sd_event_source_unref(j->timer_event_source);
+}
+
+void job_free(Job *j) {
+ assert(j);
+ assert(!j->installed);
+ assert(!j->transaction_prev);
+ assert(!j->transaction_next);
+ assert(!j->subject_list);
+ assert(!j->object_list);
- sd_event_source_unref(j->timer_event_source);
+ job_unlink(j);
sd_bus_track_unref(j->bus_track);
strv_free(j->deserialized_clients);
*pj = j;
j->installed = true;
+ j->reloaded = true;
if (j->state == JOB_RUNNING)
j->unit->manager->n_running_jobs++;
assert(j);
assert(f);
- if (!prefix)
- prefix = "";
+ prefix = strempty(prefix);
fprintf(f,
"%s-> Job %u:\n"
"%s\tAction: %s -> %s\n"
"%s\tState: %s\n"
- "%s\tIrreversible: %s\n",
+ "%s\tIrreversible: %s\n"
+ "%s\tMay GC: %s\n",
prefix, j->id,
prefix, j->unit->id, job_type_to_string(j->type),
prefix, job_state_to_string(j->state),
- prefix, yes_no(j->irreversible));
+ prefix, yes_no(j->irreversible),
+ prefix, yes_no(job_may_gc(j)));
}
/*
job_set_state(j, JOB_RUNNING);
job_add_to_dbus_queue(j);
-
switch (j->type) {
case JOB_VERIFY_ACTIVE: {
r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false);
else if (r == -ENOLINK)
r = job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false);
+ else if (r == -ESTALE)
+ r = job_finish_and_invalidate(j, JOB_ONCE, true, false);
else if (r == -EAGAIN)
job_set_state(j, JOB_WAITING);
else if (r < 0)
[JOB_ASSERT] = "Assertion failed for %s.",
[JOB_UNSUPPORTED] = "Starting of %s not supported.",
[JOB_COLLECTED] = "Unnecessary job for %s was removed.",
+ [JOB_ONCE] = "Unit %s has been started before and cannot be started again."
};
static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
[JOB_DONE] = "Stopped %s.",
static const struct {
const char *color, *word;
} job_print_status_messages [_JOB_RESULT_MAX] = {
- [JOB_DONE] = { ANSI_GREEN, " OK " },
+ [JOB_DONE] = { ANSI_OK_COLOR, " OK " },
[JOB_TIMEOUT] = { ANSI_HIGHLIGHT_RED, " TIME " },
[JOB_FAILED] = { ANSI_HIGHLIGHT_RED, "FAILED" },
[JOB_DEPENDENCY] = { ANSI_HIGHLIGHT_YELLOW, "DEPEND" },
[JOB_ASSERT] = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" },
[JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" },
/* JOB_COLLECTED */
+ [JOB_ONCE] = { ANSI_HIGHLIGHT_RED, " ONCE " },
};
static void job_print_status_message(Unit *u, JobType t, JobResult result) {
[JOB_ASSERT] = LOG_WARNING,
[JOB_UNSUPPORTED] = LOG_WARNING,
[JOB_COLLECTED] = LOG_INFO,
+ [JOB_ONCE] = LOG_ERR,
};
assert(u);
if (!format)
return;
- /* The description might be longer than the buffer, but that's OK, we'll just truncate it here */
+ /* The description might be longer than the buffer, but that's OK,
+ * we'll just truncate it here. Note that we use snprintf() rather than
+ * xsprintf() on purpose here: we are fine with truncation and don't
+ * consider that an error. */
DISABLE_WARNING_FORMAT_NONLITERAL;
- snprintf(buf, sizeof(buf), format, unit_description(u));
+ (void) snprintf(buf, sizeof(buf), format, unit_description(u));
REENABLE_WARNING;
switch (t) {
"JOB_TYPE=%s", job_type_to_string(t),
"JOB_RESULT=%s", job_result_to_string(result),
LOG_UNIT_ID(u),
- LOG_UNIT_INVOCATION_ID(u),
- NULL);
+ LOG_UNIT_INVOCATION_ID(u));
return;
}
"JOB_RESULT=%s", job_result_to_string(result),
LOG_UNIT_ID(u),
LOG_UNIT_INVOCATION_ID(u),
- mid,
- NULL);
+ mid);
}
static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
}
}
+static int job_save_pending_finished_job(Job *j) {
+ int r;
+
+ assert(j);
+
+ r = set_ensure_allocated(&j->manager->pending_finished_jobs, NULL);
+ if (r < 0)
+ return r;
+
+ job_unlink(j);
+ return set_put(j->manager->pending_finished_jobs, j);
+}
+
int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) {
Unit *u;
Unit *other;
j->manager->n_failed_jobs++;
job_uninstall(j);
- job_free(j);
+ /* Keep jobs started before the reload to send singal later, free all others */
+ if (!MANAGER_IS_RELOADING(j->manager) ||
+ !j->reloaded ||
+ job_save_pending_finished_job(j) < 0)
+ job_free(j);
/* Fail depending jobs on failure */
if (result != JOB_DONE && recursive) {
LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.",
u->id,
job_type_to_string(t),
- job_result_to_string(result)),
- NULL);
+ job_result_to_string(result)));
unit_start_on_failure(u);
}
if (detect_container() > 0)
return;
- asynchronous_sync();
+ (void) asynchronous_sync(NULL);
}
int job_get_timeout(Job *j, usec_t *timeout) {
return 1;
}
-bool job_check_gc(Job *j) {
+bool job_may_gc(Job *j) {
Unit *other;
Iterator i;
void *v;
assert(j);
/* Checks whether this job should be GC'ed away. We only do this for jobs of units that have no effect on their
- * own and just track external state. For now the only unit type that qualifies for this are .device units. */
+ * own and just track external state. For now the only unit type that qualifies for this are .device units.
+ * Returns true if the job can be collected. */
if (!UNIT_VTABLE(j->unit)->gc_jobs)
- return true;
+ return false;
if (sd_bus_track_count(j->bus_track) > 0)
- return true;
+ return false;
/* FIXME: So this is a bit ugly: for now we don't properly track references made via private bus connections
* (because it's nasty, as sd_bus_track doesn't apply to it). We simply remember that the job was once
if (set_isempty(j->unit->manager->private_buses))
j->ref_by_private_bus = false;
else
- return true;
+ return false;
}
if (j->type == JOB_NOP)
- return true;
+ return false;
/* If a job is ordered after ours, and is to be started, then it needs to wait for us, regardless if we stop or
* start, hence let's not GC in that case. */
continue;
if (IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD))
- return true;
+ return false;
}
/* If we are going down, but something else is ordered After= us, then it needs to wait for us */
if (other->job->ignore_order)
continue;
- return true;
+ return false;
}
/* The logic above is kinda the inverse of the job_is_runnable() logic. Specifically, if the job "we" is
*
*/
- return false;
+ return true;
}
void job_add_to_gc_queue(Job *j) {
if (j->in_gc_queue)
return;
- if (job_check_gc(j))
+ if (!job_may_gc(j))
return;
LIST_PREPEND(gc_queue, j->unit->manager->gc_job_queue, j);
n = sort_job_list(list, n);
- *ret = list;
- list = NULL;
+ *ret = TAKE_PTR(list);
return (int) n;
}
n = sort_job_list(list, n);
- *ret = list;
- list = NULL;
+ *ret = TAKE_PTR(list);
return (int) n;
}
[JOB_ASSERT] = "assert",
[JOB_UNSUPPORTED] = "unsupported",
[JOB_COLLECTED] = "collected",
+ [JOB_ONCE] = "once",
};
DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);