job.c revision 02a3bcc6b4372ca50c0a62b193f9a75b988ffa69
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller/***
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller This file is part of systemd.
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller Copyright 2010 Lennart Poettering
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
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
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
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 Vincent***/
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
92327eba2d504cf38480c3d35550967289071f68Jason Vincent#include <assert.h>
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay#include <errno.h>
6c05341ba29056a3071388ba179ef4386f5c03eaJason Vincent#include <sys/timerfd.h>
6c05341ba29056a3071388ba179ef4386f5c03eaJason Vincent#include <sys/epoll.h>
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent#include "set.h"
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent#include "unit.h"
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent#include "macro.h"
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent#include "strv.h"
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent#include "load-fragment.h"
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent#include "load-dropin.h"
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent#include "log.h"
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay#include "dbus-job.h"
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
92327eba2d504cf38480c3d35550967289071f68Jason VincentJob* job_new(Manager *m, JobType type, Unit *unit) {
92327eba2d504cf38480c3d35550967289071f68Jason Vincent Job *j;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
92327eba2d504cf38480c3d35550967289071f68Jason Vincent assert(m);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(type < _JOB_TYPE_MAX);
92327eba2d504cf38480c3d35550967289071f68Jason Vincent assert(unit);
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent
aa3355a609d9a81d4e1e699f3dddcf128a9d1f66Brendan Mmiller if (!(j = new0(Job, 1)))
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay return NULL;
b10438ed180752c366a46cda8e16982358258148Jason Vincent
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay j->manager = m;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay j->id = m->current_job_id++;
da70d74096ee70f65c7c0bb2ff584a6f2a65306cChad Kienle j->type = type;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent j->unit = unit;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay j->timer_watch.type = WATCH_INVALID;
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller /* We don't link it here, that's what job_dependency() is for */
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay return j;
b10438ed180752c366a46cda8e16982358258148Jason Vincent}
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemayvoid job_free(Job *j) {
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent assert(j);
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay /* Detach from next 'bigger' objects */
92327eba2d504cf38480c3d35550967289071f68Jason Vincent if (j->installed) {
92327eba2d504cf38480c3d35550967289071f68Jason Vincent bus_job_send_removed_signal(j);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller if (j->unit->job == j) {
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller j->unit->job = NULL;
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller unit_add_to_gc_queue(j->unit);
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller }
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
fa36865222ac60c2f4afeb03dc190dedba751e8eJason Lemay hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller j->installed = false;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay }
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(!j->transaction_prev);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(!j->transaction_next);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(!j->subject_list);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(!j->object_list);
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay if (j->in_run_queue)
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay if (j->in_dbus_queue)
fa36865222ac60c2f4afeb03dc190dedba751e8eJason Lemay LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay if (j->timer_watch.type != WATCH_INVALID) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(j->timer_watch.type == WATCH_JOB_TIMER);
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay assert(j->timer_watch.data.job == j);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(j->timer_watch.fd >= 0);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert_se(epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_DEL, j->timer_watch.fd, NULL) >= 0);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay close_nointr_nofail(j->timer_watch.fd);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay }
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller free(j->bus_client);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay free(j);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay}
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason LemayJobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay JobDependency *l;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(object);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
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. */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay if (!(l = new0(JobDependency, 1)))
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay return NULL;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
92327eba2d504cf38480c3d35550967289071f68Jason Vincent l->subject = subject;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay l->object = object;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay l->matters = matters;
b10438ed180752c366a46cda8e16982358258148Jason Vincent l->conflicts = conflicts;
b10438ed180752c366a46cda8e16982358258148Jason Vincent
b10438ed180752c366a46cda8e16982358258148Jason Vincent if (subject)
92327eba2d504cf38480c3d35550967289071f68Jason Vincent LIST_PREPEND(JobDependency, subject, subject->subject_list, l);
6cbe55fd2b79472be64e172dd5da313f616c1882Jason Vincent else
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay LIST_PREPEND(JobDependency, subject, object->manager->transaction_anchor, l);
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay LIST_PREPEND(JobDependency, object, object->object_list, l);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay return l;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay}
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemayvoid job_dependency_free(JobDependency *l) {
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller assert(l);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
92327eba2d504cf38480c3d35550967289071f68Jason Vincent if (l->subject)
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay else
b10438ed180752c366a46cda8e16982358258148Jason Vincent LIST_REMOVE(JobDependency, subject, l->object->manager->transaction_anchor, l);
b10438ed180752c366a46cda8e16982358258148Jason Vincent
b10438ed180752c366a46cda8e16982358258148Jason Vincent LIST_REMOVE(JobDependency, object, l->object->object_list, l);
6cbe55fd2b79472be64e172dd5da313f616c1882Jason Vincent
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay free(l);
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller}
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemayvoid job_dump(Job *j, FILE*f, const char *prefix) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(j);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(f);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay if (!prefix)
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay prefix = "";
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay fprintf(f,
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->id,
b10438ed180752c366a46cda8e16982358258148Jason Vincent prefix, j->unit->id, job_type_to_string(j->type),
b10438ed180752c366a46cda8e16982358258148Jason Vincent prefix, job_state_to_string(j->state),
6cbe55fd2b79472be64e172dd5da313f616c1882Jason Vincent prefix, yes_no(j->override));
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller}
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemaybool job_is_anchor(Job *j) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay JobDependency *l;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(j);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay LIST_FOREACH(object, l, j->object_list)
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay if (!l->subject)
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay return true;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
92327eba2d504cf38480c3d35550967289071f68Jason Vincent return false;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay}
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
b10438ed180752c366a46cda8e16982358258148Jason Vincent/*
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 *
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 *
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 *
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.
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemaystatic const JobType job_merging_table[] = {
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 */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay/*JOB_VERIFY_ACTIVE */ JOB_START,
b10438ed180752c366a46cda8e16982358258148Jason Vincent/*JOB_STOP */ -1, -1,
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,
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay};
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
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);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(a >= 0 && a < _JOB_TYPE_MAX);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay assert(b >= 0 && b < _JOB_TYPE_MAX);
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay if (a == b)
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay return a;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
92327eba2d504cf38480c3d35550967289071f68Jason Vincent if (a < b) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay JobType tmp = a;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay a = b;
b10438ed180752c366a46cda8e16982358258148Jason Vincent b = tmp;
b10438ed180752c366a46cda8e16982358258148Jason Vincent }
6cbe55fd2b79472be64e172dd5da313f616c1882Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent return job_merging_table[(a - 1) * a / 2 + b];
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent}
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincentbool job_type_is_redundant(JobType a, UnitActiveState b) {
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent switch (a) {
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent case JOB_START:
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent return
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent b == UNIT_ACTIVE ||
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent b == UNIT_RELOADING;
7f6d6691a67b02e32e1536651e31820af68fdfcaJason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent case JOB_STOP:
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent return
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent b == UNIT_INACTIVE ||
7f6d6691a67b02e32e1536651e31820af68fdfcaJason Vincent b == UNIT_FAILED;
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent case JOB_VERIFY_ACTIVE:
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent return
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent b == UNIT_ACTIVE ||
6c05341ba29056a3071388ba179ef4386f5c03eaJason Vincent b == UNIT_RELOADING;
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent case JOB_RELOAD:
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent return
92327eba2d504cf38480c3d35550967289071f68Jason Vincent b == UNIT_RELOADING;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent case JOB_RELOAD_OR_START:
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent return
b10438ed180752c366a46cda8e16982358258148Jason Vincent b == UNIT_ACTIVATING ||
92327eba2d504cf38480c3d35550967289071f68Jason Vincent b == UNIT_RELOADING;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent case JOB_RESTART:
7f6d6691a67b02e32e1536651e31820af68fdfcaJason Vincent return
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent b == UNIT_ACTIVATING;
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent case JOB_TRY_RESTART:
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent return
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent b == UNIT_ACTIVATING;
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent default:
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent assert_not_reached("Invalid job type");
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent }
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent}
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincentbool job_is_runnable(Job *j) {
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent Iterator i;
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent Unit *other;
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent assert(j);
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent assert(j->installed);
6c05341ba29056a3071388ba179ef4386f5c03eaJason Vincent
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
92327eba2d504cf38480c3d35550967289071f68Jason Vincent * type. */
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent /* First check if there is an override */
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent if (j->ignore_order)
b10438ed180752c366a46cda8e16982358258148Jason Vincent return true;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
92327eba2d504cf38480c3d35550967289071f68Jason Vincent if (j->type == JOB_START ||
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent j->type == JOB_VERIFY_ACTIVE ||
154001a7695db48f5e59bd2978bf3254210feed4Jason Vincent j->type == JOB_RELOAD ||
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay j->type == JOB_RELOAD_OR_START) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
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
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i)
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay if (other->job)
92327eba2d504cf38480c3d35550967289071f68Jason Vincent return false;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay }
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
92327eba2d504cf38480c3d35550967289071f68Jason Vincent /* Also, if something else is being stopped and we should
92327eba2d504cf38480c3d35550967289071f68Jason Vincent * change state after it, then lets wait. */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i)
b10438ed180752c366a46cda8e16982358258148Jason Vincent if (other->job &&
b10438ed180752c366a46cda8e16982358258148Jason Vincent (other->job->type == JOB_STOP ||
b10438ed180752c366a46cda8e16982358258148Jason Vincent other->job->type == JOB_RESTART ||
6cbe55fd2b79472be64e172dd5da313f616c1882Jason Vincent other->job->type == JOB_TRY_RESTART))
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay return false;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay /* This means that for a service a and a service b where b
b10438ed180752c366a46cda8e16982358258148Jason Vincent * shall be started after a:
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay *
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 *
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay * This has the side effect that restarts are properly
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay * synchronized too. */
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay return true;
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay}
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemaystatic void job_change_type(Job *j, JobType newtype) {
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay log_debug("Converting job %s/%s -> %s/%s",
92327eba2d504cf38480c3d35550967289071f68Jason Vincent j->unit->id, job_type_to_string(j->type),
92327eba2d504cf38480c3d35550967289071f68Jason Vincent j->unit->id, job_type_to_string(newtype));
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay j->type = newtype;
b10438ed180752c366a46cda8e16982358258148Jason Vincent}
b10438ed180752c366a46cda8e16982358258148Jason Vincent
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemayint job_run_and_invalidate(Job *j) {
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay int r;
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay uint32_t id;
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay Manager *m;
92327eba2d504cf38480c3d35550967289071f68Jason Vincent
92327eba2d504cf38480c3d35550967289071f68Jason Vincent assert(j);
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay assert(j->installed);
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay if (j->in_run_queue) {
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay j->in_run_queue = false;
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay }
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay
92327eba2d504cf38480c3d35550967289071f68Jason Vincent if (j->state != JOB_WAITING)
92327eba2d504cf38480c3d35550967289071f68Jason Vincent return 0;
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay if (!job_is_runnable(j))
b10438ed180752c366a46cda8e16982358258148Jason Vincent return -EAGAIN;
b10438ed180752c366a46cda8e16982358258148Jason Vincent
92327eba2d504cf38480c3d35550967289071f68Jason Vincent j->state = JOB_RUNNING;
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay job_add_to_dbus_queue(j);
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay
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 * valid. */
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay id = j->id;
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay m = j->manager;
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay switch (j->type) {
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay case JOB_RELOAD_OR_START:
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay if (unit_active_state(j->unit) == UNIT_ACTIVE) {
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay job_change_type(j, JOB_RELOAD);
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay r = unit_reload(j->unit);
1c3b89d348fb391b3f8692db1592e91bf1e6f0dcJason Lemay break;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay }
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay job_change_type(j, JOB_START);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay /* fall through */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay case JOB_START:
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay r = unit_start(j->unit);
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay /* If this unit cannot be started, then simply wait */
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay if (r == -EBADR)
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay r = 0;
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay break;
fa36865222ac60c2f4afeb03dc190dedba751e8eJason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay case JOB_VERIFY_ACTIVE: {
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay UnitActiveState t = unit_active_state(j->unit);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay if (UNIT_IS_ACTIVE_OR_RELOADING(t))
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay r = -EALREADY;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay else if (t == UNIT_ACTIVATING)
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay r = -EAGAIN;
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay else
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay r = -ENOEXEC;
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay break;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay }
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay case JOB_TRY_RESTART:
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) {
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay r = -ENOEXEC;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay break;
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay }
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay job_change_type(j, JOB_RESTART);
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay /* fall through */
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay case JOB_STOP:
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay case JOB_RESTART:
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay r = unit_stop(j->unit);
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay /* If this unit cannot stopped, then simply wait. */
e9e8bd003492cef3c7b9a98eb483ad3d1a69ae43Jason Lemay if (r == -EBADR)
53887acac50b4dd113edd5c3d3f70da052a49aecJason Lemay r = 0;
aa3355a609d9a81d4e1e699f3dddcf128a9d1f66Brendan Mmiller break;
f9e793c040f681f247471dc659cdcd8238058a9dBrendan Mmiller
case JOB_RELOAD:
r = unit_reload(j->unit);
break;
default:
assert_not_reached("Unknown job type");
}
if ((j = manager_get_job(m, id))) {
if (r == -EALREADY)
r = job_finish_and_invalidate(j, JOB_DONE);
else if (r == -ENOEXEC)
r = job_finish_and_invalidate(j, JOB_SKIPPED);
else if (r == -EAGAIN)
j->state = JOB_WAITING;
else if (r < 0)
r = job_finish_and_invalidate(j, JOB_FAILED);
}
return r;
}
static void job_print_status_message(Unit *u, JobType t, JobResult result) {
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));
break;
case JOB_FAILED:
unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u));
unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
break;
case JOB_DEPENDENCY:
unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u));
break;
case JOB_TIMEOUT:
unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u));
break;
default:
;
}
} 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));
break;
case JOB_DONE:
case JOB_FAILED:
unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, "Stopped %s", unit_description(u));
break;
default:
;
}
}
}
int job_finish_and_invalidate(Job *j, JobResult result) {
Unit *u;
Unit *other;
JobType t;
Iterator i;
bool recursed = false;
assert(j);
assert(j->installed);
job_add_to_dbus_queue(j);
/* Patch restart jobs so that they become normal start jobs */
if (result == JOB_DONE && j->type == JOB_RESTART) {
job_change_type(j, JOB_START);
j->state = JOB_WAITING;
job_add_to_run_queue(j);
u = j->unit;
goto finish;
}
j->result = result;
log_debug("Job %s/%s finished, result=%s", j->unit->id, job_type_to_string(j->type), job_result_to_string(result));
if (result == JOB_FAILED)
j->manager->n_failed_jobs ++;
u = j->unit;
t = j->type;
job_free(j);
job_print_status_message(u, t, result);
/* Fail depending jobs on failure */
if (result != JOB_DONE) {
if (t == JOB_START ||
t == JOB_VERIFY_ACTIVE ||
t == JOB_RELOAD_OR_START) {
SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
if (other->job &&
(other->job->type == JOB_START ||
other->job->type == JOB_VERIFY_ACTIVE ||
other->job->type == JOB_RELOAD_OR_START)) {
job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
recursed = true;
}
SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
if (other->job &&
(other->job->type == JOB_START ||
other->job->type == JOB_VERIFY_ACTIVE ||
other->job->type == JOB_RELOAD_OR_START)) {
job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
recursed = true;
}
SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
if (other->job &&
!other->job->override &&
(other->job->type == JOB_START ||
other->job->type == JOB_VERIFY_ACTIVE ||
other->job->type == JOB_RELOAD_OR_START)) {
job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
recursed = true;
}
} else if (t == JOB_STOP) {
SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
if (other->job &&
(other->job->type == JOB_START ||
other->job->type == JOB_VERIFY_ACTIVE ||
other->job->type == JOB_RELOAD_OR_START)) {
job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
recursed = true;
}
}
}
/* Trigger OnFailure dependencies that are not generated by
* the unit itself. We don't tread JOB_CANCELED as failure in
* this context. And JOB_FAILURE is already handled by the
* unit itself. */
if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
log_notice("Job %s/%s failed with result '%s'.",
u->id,
job_type_to_string(t),
job_result_to_string(result));
unit_trigger_on_failure(u);
}
finish:
/* Try to start the next jobs that can be started */
SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
if (other->job)
job_add_to_run_queue(other->job);
SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i)
if (other->job)
job_add_to_run_queue(other->job);
manager_check_finished(u->manager);
return recursed;
}
int job_start_timer(Job *j) {
struct itimerspec its;
struct epoll_event ev;
int fd, r;
assert(j);
if (j->unit->job_timeout <= 0 ||
j->timer_watch.type == WATCH_JOB_TIMER)
return 0;
assert(j->timer_watch.type == WATCH_INVALID);
if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) {
r = -errno;
goto fail;
}
zero(its);
timespec_store(&its.it_value, j->unit->job_timeout);
if (timerfd_settime(fd, 0, &its, NULL) < 0) {
r = -errno;
goto fail;
}
zero(ev);
ev.data.ptr = &j->timer_watch;
ev.events = EPOLLIN;
if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
r = -errno;
goto fail;
}
j->timer_watch.type = WATCH_JOB_TIMER;
j->timer_watch.fd = fd;
j->timer_watch.data.job = j;
return 0;
fail:
if (fd >= 0)
close_nointr_nofail(fd);
return r;
}
void job_add_to_run_queue(Job *j) {
assert(j);
assert(j->installed);
if (j->in_run_queue)
return;
LIST_PREPEND(Job, run_queue, j->manager->run_queue, j);
j->in_run_queue = true;
}
void job_add_to_dbus_queue(Job *j) {
assert(j);
assert(j->installed);
if (j->in_dbus_queue)
return;
/* We don't check if anybody is subscribed here, since this
* job might just have been created and not yet assigned to a
* connection/client. */
LIST_PREPEND(Job, dbus_queue, j->manager->dbus_job_queue, j);
j->in_dbus_queue = true;
}
char *job_dbus_path(Job *j) {
char *p;
assert(j);
if (asprintf(&p, "/org/freedesktop/systemd1/job/%lu", (unsigned long) j->id) < 0)
return NULL;
return p;
}
void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w) {
assert(j);
assert(w == &j->timer_watch);
log_warning("Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
job_finish_and_invalidate(j, JOB_TIMEOUT);
}
static const char* const job_state_table[_JOB_STATE_MAX] = {
[JOB_WAITING] = "waiting",
[JOB_RUNNING] = "running"
};
DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
static const char* const job_type_table[_JOB_TYPE_MAX] = {
[JOB_START] = "start",
[JOB_VERIFY_ACTIVE] = "verify-active",
[JOB_STOP] = "stop",
[JOB_RELOAD] = "reload",
[JOB_RELOAD_OR_START] = "reload-or-start",
[JOB_RESTART] = "restart",
[JOB_TRY_RESTART] = "try-restart",
};
DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
static const char* const job_mode_table[_JOB_MODE_MAX] = {
[JOB_FAIL] = "fail",
[JOB_REPLACE] = "replace",
[JOB_ISOLATE] = "isolate",
[JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
[JOB_IGNORE_REQUIREMENTS] = "ignore-requirements"
};
DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
static const char* const job_result_table[_JOB_RESULT_MAX] = {
[JOB_DONE] = "done",
[JOB_CANCELED] = "canceled",
[JOB_TIMEOUT] = "timeout",
[JOB_FAILED] = "failed",
[JOB_DEPENDENCY] = "dependency",
[JOB_SKIPPED] = "skipped"
};
DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);