job.c revision 02a3bcc6b4372ca50c0a62b193f9a75b988ffa69
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller This file is part of systemd.
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller Copyright 2010 Lennart Poettering
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller systemd is free software; you can redistribute it and/or modify it
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller under the terms of the GNU Lesser General Public License as published by
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller the Free Software Foundation; either version 2.1 of the License, or
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller (at your option) any later version.
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller systemd is distributed in the hope that it will be useful, but
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay WITHOUT ANY WARRANTY; without even the implied warranty of
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller Lesser General Public License for more details.
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller You should have received a copy of the GNU Lesser General Public License
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent along with systemd; If not, see <http://www.gnu.org/licenses/>.
92327eba2d504cf38480c3d35550967289071f68Jason VincentJob* job_new(Manager *m, JobType type, Unit *unit) {
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller /* We don't link it here, that's what job_dependency() is for */
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay /* Detach from next 'bigger' objects */
fa36865222ac60c2f4afeb03dc190dedba751e8eJason Lemay hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
fa36865222ac60c2f4afeb03dc190dedba751e8eJason Lemay LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert_se(epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_DEL, j->timer_watch.fd, NULL) >= 0);
53887acac50b4dd113edd5c3d3f70da052a49aecJason LemayJobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay /* Adds a new job link, which encodes that the 'subject' job
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * needs the 'object' job in some way. If 'subject' is NULL
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * this means the 'anchor' job (i.e. the one the user
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * explicitly asked for) is the requester. */
92327eba2d504cf38480c3d35550967289071f68Jason Vincent LIST_PREPEND(JobDependency, subject, subject->subject_list, l);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay LIST_PREPEND(JobDependency, subject, object->manager->transaction_anchor, l);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay LIST_PREPEND(JobDependency, object, object->object_list, l);
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l);
b10438ed180752c366a46cda8e16982358258148Jason Vincent LIST_REMOVE(JobDependency, subject, l->object->manager->transaction_anchor, l);
b10438ed180752c366a46cda8e16982358258148Jason Vincent LIST_REMOVE(JobDependency, object, l->object->object_list, l);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemayvoid job_dump(Job *j, FILE*f, const char *prefix) {
92327eba2d504cf38480c3d35550967289071f68Jason Vincent "%s-> Job %u:\n"
92327eba2d504cf38480c3d35550967289071f68Jason Vincent "%s\tAction: %s -> %s\n"
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay "%s\tState: %s\n"
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay "%s\tForced: %s\n",
b10438ed180752c366a46cda8e16982358258148Jason Vincent prefix, j->unit->id, job_type_to_string(j->type),
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay return true;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent return false;
b10438ed180752c366a46cda8e16982358258148Jason Vincent * Merging is commutative, so imagine the matrix as symmetric. We store only
b10438ed180752c366a46cda8e16982358258148Jason Vincent * its lower triangle to avoid duplication. We don't store the main diagonal,
6cbe55fd2b79472be64e172dd5da313f616c1882Jason Vincent * because A merged with A is simply A.
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller * Merging is associative! A merged with B merged with C is the same as
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller * A merged with C merged with B.
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * Mergeability is transitive! If A can be merged with B and B with C then
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * A also with C.
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * Also, if A merged with B cannot be merged with C, then either A or B cannot
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * be merged with C either.
92327eba2d504cf38480c3d35550967289071f68Jason Vincent/* What \ With * JOB_START JOB_VERIFY_ACTIVE JOB_STOP JOB_RELOAD JOB_RELOAD_OR_START JOB_RESTART JOB_TRY_RESTART */
92327eba2d504cf38480c3d35550967289071f68Jason Vincent/************************************************************************************************************************************/
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay/*JOB_START */
b10438ed180752c366a46cda8e16982358258148Jason Vincent/*JOB_RELOAD */ JOB_RELOAD_OR_START, JOB_RELOAD, -1,
b10438ed180752c366a46cda8e16982358258148Jason Vincent/*JOB_RELOAD_OR_START*/ JOB_RELOAD_OR_START, JOB_RELOAD_OR_START, -1, JOB_RELOAD_OR_START,
6cbe55fd2b79472be64e172dd5da313f616c1882Jason Vincent/*JOB_RESTART */ JOB_RESTART, JOB_RESTART, -1, JOB_RESTART, JOB_RESTART,
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay/*JOB_TRY_RESTART */ JOB_RESTART, JOB_TRY_RESTART, -1, JOB_TRY_RESTART, JOB_RESTART, JOB_RESTART,
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason LemayJobType job_type_lookup_merge(JobType a, JobType b) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX * (_JOB_TYPE_MAX - 1) / 2);
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent return job_merging_table[(a - 1) * a / 2 + b];
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincentbool job_type_is_redundant(JobType a, UnitActiveState b) {
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent /* Checks whether there is any job running for the units this
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent * job needs to be running after (in the case of a 'positive'
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent * job type) or before (in the case of a 'negative' job
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent /* First check if there is an override */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay /* Immediate result is that the job is or might be
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * started. In this case lets wait for the
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * dependencies, regardless whether they are
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay * starting or stopping something. */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i)
92327eba2d504cf38480c3d35550967289071f68Jason Vincent return false;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent /* Also, if something else is being stopped and we should
92327eba2d504cf38480c3d35550967289071f68Jason Vincent * change state after it, then lets wait. */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i)
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay return false;
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay /* This means that for a service a and a service b where b
b10438ed180752c366a46cda8e16982358258148Jason Vincent * shall be started after a:
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay * start a + start b → 1st step start a, 2nd step start b
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay * start a + stop b → 1st step stop b, 2nd step start a
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay * stop a + start b → 1st step stop a, 2nd step start b
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay * stop a + stop b → 1st step stop b, 2nd step stop a
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay * This has the side effect that restarts are properly
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay * synchronized too. */
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay return true;
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemaystatic void job_change_type(Job *j, JobType newtype) {
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
b10438ed180752c366a46cda8e16982358258148Jason Vincent /* While we execute this operation the job might go away (for
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay * example: because it is replaced by a new, conflicting
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay * job.) To make sure we don't access a freed job later on we
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay * store the id here, so that we can verify the job is still
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay switch (j->type) {
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay if (unit_active_state(j->unit) == UNIT_ACTIVE) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay /* fall through */
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay /* If this unit cannot be started, then simply wait */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay /* fall through */
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay /* If this unit cannot stopped, then simply wait. */
case JOB_RELOAD:
if (r == -EALREADY)
else if (r == -ENOEXEC)
else if (r == -EAGAIN)
assert(u);
if (t == JOB_START) {
switch (result) {
case JOB_DONE:
if (u->condition_result)
unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Started %s", unit_description(u));
case JOB_FAILED:
unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u));
case JOB_DEPENDENCY:
unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u));
case JOB_TIMEOUT:
unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u));
} else if (t == JOB_STOP) {
switch (result) {
case JOB_TIMEOUT:
unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u));
case JOB_DONE:
case JOB_FAILED:
unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Stopped %s", unit_description(u));
Unit *u;
JobType t;
Iterator i;
bool recursed = false;
assert(j);
u = j->unit;
goto finish;
log_debug("Job %s/%s finished, result=%s", j->unit->id, job_type_to_string(j->type), job_result_to_string(result));
u = j->unit;
t = j->type;
job_free(j);
if (t == JOB_START ||
t == JOB_VERIFY_ACTIVE ||
t == JOB_RELOAD_OR_START) {
recursed = true;
recursed = true;
recursed = true;
} else if (t == JOB_STOP) {
recursed = true;
u->id,
return recursed;
int fd, r;
assert(j);
r = -errno;
goto fail;
r = -errno;
goto fail;
r = -errno;
goto fail;
fail:
if (fd >= 0)
assert(j);
if (j->in_run_queue)
j->in_run_queue = true;
assert(j);
if (j->in_dbus_queue)
* connection/client. */
j->in_dbus_queue = true;
assert(j);
return NULL;
assert(j);