chiark / gitweb /
3454ffd1db861219381030f08c51b9710348910c
[elogind.git] / src / core / job.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <sys/timerfd.h>
25 #include <sys/epoll.h>
26
27 #include "set.h"
28 #include "unit.h"
29 #include "macro.h"
30 #include "strv.h"
31 #include "load-fragment.h"
32 #include "load-dropin.h"
33 #include "log.h"
34 #include "dbus-job.h"
35
36 Job* job_new(Unit *unit, JobType type) {
37         Job *j;
38
39         assert(type < _JOB_TYPE_MAX);
40         assert(unit);
41
42         if (!(j = new0(Job, 1)))
43                 return NULL;
44
45         j->manager = unit->manager;
46         j->id = j->manager->current_job_id++;
47         j->type = type;
48         j->unit = unit;
49
50         j->timer_watch.type = WATCH_INVALID;
51
52         /* We don't link it here, that's what job_dependency() is for */
53
54         return j;
55 }
56
57 void job_free(Job *j) {
58         assert(j);
59         assert(!j->installed);
60         assert(!j->transaction_prev);
61         assert(!j->transaction_next);
62         assert(!j->subject_list);
63         assert(!j->object_list);
64
65         if (j->in_run_queue)
66                 LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
67
68         if (j->in_dbus_queue)
69                 LIST_REMOVE(Job, dbus_queue, j->manager->dbus_job_queue, j);
70
71         if (j->timer_watch.type != WATCH_INVALID) {
72                 assert(j->timer_watch.type == WATCH_JOB_TIMER);
73                 assert(j->timer_watch.data.job == j);
74                 assert(j->timer_watch.fd >= 0);
75
76                 assert_se(epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_DEL, j->timer_watch.fd, NULL) >= 0);
77                 close_nointr_nofail(j->timer_watch.fd);
78         }
79
80         free(j->bus_client);
81         free(j);
82 }
83
84 void job_uninstall(Job *j) {
85         assert(j->installed);
86         /* Detach from next 'bigger' objects */
87
88         bus_job_send_removed_signal(j);
89
90         if (j->unit->job == j) {
91                 j->unit->job = NULL;
92                 unit_add_to_gc_queue(j->unit);
93         }
94
95         hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
96         j->installed = false;
97 }
98
99 static bool job_type_allows_late_merge(JobType t) {
100         /* Tells whether it is OK to merge a job of type 't' with an already
101          * running job.
102          * Reloads cannot be merged this way. Think of the sequence:
103          * 1. Reload of a daemon is in progress; the daemon has already loaded
104          *    its config file, but hasn't completed the reload operation yet.
105          * 2. Edit foo's config file.
106          * 3. Trigger another reload to have the daemon use the new config.
107          * Should the second reload job be merged into the first one, the daemon
108          * would not know about the new config.
109          * JOB_RESTART jobs on the other hand can be merged, because they get
110          * patched into JOB_START after stopping the unit. So if we see a
111          * JOB_RESTART running, it means the unit hasn't stopped yet and at
112          * this time the merge is still allowed. */
113         return !(t == JOB_RELOAD || t == JOB_RELOAD_OR_START);
114 }
115
116 static void job_merge_into_installed(Job *j, Job *other) {
117         assert(j->installed);
118         assert(j->unit == other->unit);
119
120         j->type = job_type_lookup_merge(j->type, other->type);
121         assert(j->type >= 0);
122
123         j->override = j->override || other->override;
124 }
125
126 Job* job_install(Job *j) {
127         Job *uj = j->unit->job;
128
129         assert(!j->installed);
130
131         if (uj) {
132                 if (job_type_is_conflicting(uj->type, j->type))
133                         job_finish_and_invalidate(uj, JOB_CANCELED);
134                 else {
135                         /* not conflicting, i.e. mergeable */
136
137                         if (uj->state == JOB_WAITING ||
138                             (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
139                                 job_merge_into_installed(uj, j);
140                                 log_debug("Merged into installed job %s/%s as %u",
141                                           uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
142                                 return uj;
143                         } else {
144                                 /* already running and not safe to merge into */
145                                 /* Patch uj to become a merged job and re-run it. */
146                                 /* XXX It should be safer to queue j to run after uj finishes, but it is
147                                  * not currently possible to have more than one installed job per unit. */
148                                 job_merge_into_installed(uj, j);
149                                 log_debug("Merged into running job, re-running: %s/%s as %u",
150                                           uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
151                                 uj->state = JOB_WAITING;
152                                 return uj;
153                         }
154                 }
155         }
156
157         /* Install the job */
158         j->unit->job = j;
159         j->installed = true;
160         j->manager->n_installed_jobs ++;
161         log_debug("Installed new job %s/%s as %u", j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
162         return j;
163 }
164
165 JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
166         JobDependency *l;
167
168         assert(object);
169
170         /* Adds a new job link, which encodes that the 'subject' job
171          * needs the 'object' job in some way. If 'subject' is NULL
172          * this means the 'anchor' job (i.e. the one the user
173          * explicitly asked for) is the requester. */
174
175         if (!(l = new0(JobDependency, 1)))
176                 return NULL;
177
178         l->subject = subject;
179         l->object = object;
180         l->matters = matters;
181         l->conflicts = conflicts;
182
183         if (subject)
184                 LIST_PREPEND(JobDependency, subject, subject->subject_list, l);
185
186         LIST_PREPEND(JobDependency, object, object->object_list, l);
187
188         return l;
189 }
190
191 void job_dependency_free(JobDependency *l) {
192         assert(l);
193
194         if (l->subject)
195                 LIST_REMOVE(JobDependency, subject, l->subject->subject_list, l);
196
197         LIST_REMOVE(JobDependency, object, l->object->object_list, l);
198
199         free(l);
200 }
201
202 void job_dump(Job *j, FILE*f, const char *prefix) {
203         assert(j);
204         assert(f);
205
206         if (!prefix)
207                 prefix = "";
208
209         fprintf(f,
210                 "%s-> Job %u:\n"
211                 "%s\tAction: %s -> %s\n"
212                 "%s\tState: %s\n"
213                 "%s\tForced: %s\n",
214                 prefix, j->id,
215                 prefix, j->unit->id, job_type_to_string(j->type),
216                 prefix, job_state_to_string(j->state),
217                 prefix, yes_no(j->override));
218 }
219
220 /*
221  * Merging is commutative, so imagine the matrix as symmetric. We store only
222  * its lower triangle to avoid duplication. We don't store the main diagonal,
223  * because A merged with A is simply A.
224  *
225  * Merging is associative! A merged with B merged with C is the same as
226  * A merged with C merged with B.
227  *
228  * Mergeability is transitive! If A can be merged with B and B with C then
229  * A also with C.
230  *
231  * Also, if A merged with B cannot be merged with C, then either A or B cannot
232  * be merged with C either.
233  */
234 static const JobType job_merging_table[] = {
235 /* What \ With       *  JOB_START         JOB_VERIFY_ACTIVE  JOB_STOP JOB_RELOAD   JOB_RELOAD_OR_START  JOB_RESTART JOB_TRY_RESTART */
236 /************************************************************************************************************************************/
237 /*JOB_START          */
238 /*JOB_VERIFY_ACTIVE  */ JOB_START,
239 /*JOB_STOP           */ -1,                  -1,
240 /*JOB_RELOAD         */ JOB_RELOAD_OR_START, JOB_RELOAD,          -1,
241 /*JOB_RELOAD_OR_START*/ JOB_RELOAD_OR_START, JOB_RELOAD_OR_START, -1, JOB_RELOAD_OR_START,
242 /*JOB_RESTART        */ JOB_RESTART,         JOB_RESTART,         -1, JOB_RESTART,         JOB_RESTART,
243 /*JOB_TRY_RESTART    */ JOB_RESTART,         JOB_TRY_RESTART,     -1, JOB_TRY_RESTART,     JOB_RESTART, JOB_RESTART,
244 };
245
246 JobType job_type_lookup_merge(JobType a, JobType b) {
247         assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX * (_JOB_TYPE_MAX - 1) / 2);
248         assert(a >= 0 && a < _JOB_TYPE_MAX);
249         assert(b >= 0 && b < _JOB_TYPE_MAX);
250
251         if (a == b)
252                 return a;
253
254         if (a < b) {
255                 JobType tmp = a;
256                 a = b;
257                 b = tmp;
258         }
259
260         return job_merging_table[(a - 1) * a / 2 + b];
261 }
262
263 bool job_type_is_redundant(JobType a, UnitActiveState b) {
264         switch (a) {
265
266         case JOB_START:
267                 return
268                         b == UNIT_ACTIVE ||
269                         b == UNIT_RELOADING;
270
271         case JOB_STOP:
272                 return
273                         b == UNIT_INACTIVE ||
274                         b == UNIT_FAILED;
275
276         case JOB_VERIFY_ACTIVE:
277                 return
278                         b == UNIT_ACTIVE ||
279                         b == UNIT_RELOADING;
280
281         case JOB_RELOAD:
282                 return
283                         b == UNIT_RELOADING;
284
285         case JOB_RELOAD_OR_START:
286                 return
287                         b == UNIT_ACTIVATING ||
288                         b == UNIT_RELOADING;
289
290         case JOB_RESTART:
291                 return
292                         b == UNIT_ACTIVATING;
293
294         case JOB_TRY_RESTART:
295                 return
296                         b == UNIT_ACTIVATING;
297
298         default:
299                 assert_not_reached("Invalid job type");
300         }
301 }
302
303 bool job_is_runnable(Job *j) {
304         Iterator i;
305         Unit *other;
306
307         assert(j);
308         assert(j->installed);
309
310         /* Checks whether there is any job running for the units this
311          * job needs to be running after (in the case of a 'positive'
312          * job type) or before (in the case of a 'negative' job
313          * type. */
314
315         /* First check if there is an override */
316         if (j->ignore_order)
317                 return true;
318
319         if (j->type == JOB_START ||
320             j->type == JOB_VERIFY_ACTIVE ||
321             j->type == JOB_RELOAD ||
322             j->type == JOB_RELOAD_OR_START) {
323
324                 /* Immediate result is that the job is or might be
325                  * started. In this case lets wait for the
326                  * dependencies, regardless whether they are
327                  * starting or stopping something. */
328
329                 SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i)
330                         if (other->job)
331                                 return false;
332         }
333
334         /* Also, if something else is being stopped and we should
335          * change state after it, then lets wait. */
336
337         SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i)
338                 if (other->job &&
339                     (other->job->type == JOB_STOP ||
340                      other->job->type == JOB_RESTART ||
341                      other->job->type == JOB_TRY_RESTART))
342                         return false;
343
344         /* This means that for a service a and a service b where b
345          * shall be started after a:
346          *
347          *  start a + start b â†’ 1st step start a, 2nd step start b
348          *  start a + stop b  â†’ 1st step stop b,  2nd step start a
349          *  stop a  + start b â†’ 1st step stop a,  2nd step start b
350          *  stop a  + stop b  â†’ 1st step stop b,  2nd step stop a
351          *
352          *  This has the side effect that restarts are properly
353          *  synchronized too. */
354
355         return true;
356 }
357
358 static void job_change_type(Job *j, JobType newtype) {
359         log_debug("Converting job %s/%s -> %s/%s",
360                   j->unit->id, job_type_to_string(j->type),
361                   j->unit->id, job_type_to_string(newtype));
362
363         j->type = newtype;
364 }
365
366 int job_run_and_invalidate(Job *j) {
367         int r;
368         uint32_t id;
369         Manager *m;
370
371         assert(j);
372         assert(j->installed);
373
374         if (j->in_run_queue) {
375                 LIST_REMOVE(Job, run_queue, j->manager->run_queue, j);
376                 j->in_run_queue = false;
377         }
378
379         if (j->state != JOB_WAITING)
380                 return 0;
381
382         if (!job_is_runnable(j))
383                 return -EAGAIN;
384
385         j->state = JOB_RUNNING;
386         job_add_to_dbus_queue(j);
387
388         /* While we execute this operation the job might go away (for
389          * example: because it is replaced by a new, conflicting
390          * job.) To make sure we don't access a freed job later on we
391          * store the id here, so that we can verify the job is still
392          * valid. */
393         id = j->id;
394         m = j->manager;
395
396         switch (j->type) {
397
398                 case JOB_RELOAD_OR_START:
399                         if (unit_active_state(j->unit) == UNIT_ACTIVE) {
400                                 job_change_type(j, JOB_RELOAD);
401                                 r = unit_reload(j->unit);
402                                 break;
403                         }
404                         job_change_type(j, JOB_START);
405                         /* fall through */
406
407                 case JOB_START:
408                         r = unit_start(j->unit);
409
410                         /* If this unit cannot be started, then simply wait */
411                         if (r == -EBADR)
412                                 r = 0;
413                         break;
414
415                 case JOB_VERIFY_ACTIVE: {
416                         UnitActiveState t = unit_active_state(j->unit);
417                         if (UNIT_IS_ACTIVE_OR_RELOADING(t))
418                                 r = -EALREADY;
419                         else if (t == UNIT_ACTIVATING)
420                                 r = -EAGAIN;
421                         else
422                                 r = -ENOEXEC;
423                         break;
424                 }
425
426                 case JOB_TRY_RESTART:
427                         if (UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) {
428                                 r = -ENOEXEC;
429                                 break;
430                         }
431                         job_change_type(j, JOB_RESTART);
432                         /* fall through */
433
434                 case JOB_STOP:
435                 case JOB_RESTART:
436                         r = unit_stop(j->unit);
437
438                         /* If this unit cannot stopped, then simply wait. */
439                         if (r == -EBADR)
440                                 r = 0;
441                         break;
442
443                 case JOB_RELOAD:
444                         r = unit_reload(j->unit);
445                         break;
446
447                 default:
448                         assert_not_reached("Unknown job type");
449         }
450
451         if ((j = manager_get_job(m, id))) {
452                 if (r == -EALREADY)
453                         r = job_finish_and_invalidate(j, JOB_DONE);
454                 else if (r == -ENOEXEC)
455                         r = job_finish_and_invalidate(j, JOB_SKIPPED);
456                 else if (r == -EAGAIN)
457                         j->state = JOB_WAITING;
458                 else if (r < 0)
459                         r = job_finish_and_invalidate(j, JOB_FAILED);
460         }
461
462         return r;
463 }
464
465 static void job_print_status_message(Unit *u, JobType t, JobResult result) {
466         assert(u);
467
468         if (t == JOB_START) {
469
470                 switch (result) {
471
472                 case JOB_DONE:
473                         if (u->condition_result)
474                                 unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, "Started %s", unit_description(u));
475                         break;
476
477                 case JOB_FAILED:
478                         unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, "Failed to start %s", unit_description(u));
479                         unit_status_printf(u, NULL, "See 'systemctl status %s' for details.", u->id);
480                         break;
481
482                 case JOB_DEPENDENCY:
483                         unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " ABORT" ANSI_HIGHLIGHT_OFF, "Dependency failed. Aborted start of %s", unit_description(u));
484                         break;
485
486                 case JOB_TIMEOUT:
487                         unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out starting %s", unit_description(u));
488                         break;
489
490                 default:
491                         ;
492                 }
493
494         } else if (t == JOB_STOP) {
495
496                 switch (result) {
497
498                 case JOB_TIMEOUT:
499                         unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, "Timed out stopping %s", unit_description(u));
500                         break;
501
502                 case JOB_DONE:
503                 case JOB_FAILED:
504                         unit_status_printf(u, ANSI_HIGHLIGHT_GREEN_ON "  OK  " ANSI_HIGHLIGHT_OFF, "Stopped %s", unit_description(u));
505                         break;
506
507                 default:
508                         ;
509                 }
510         }
511 }
512
513 int job_finish_and_invalidate(Job *j, JobResult result) {
514         Unit *u;
515         Unit *other;
516         JobType t;
517         Iterator i;
518         bool recursed = false;
519
520         assert(j);
521         assert(j->installed);
522
523         job_add_to_dbus_queue(j);
524
525         /* Patch restart jobs so that they become normal start jobs */
526         if (result == JOB_DONE && j->type == JOB_RESTART) {
527
528                 job_change_type(j, JOB_START);
529                 j->state = JOB_WAITING;
530
531                 job_add_to_run_queue(j);
532
533                 u = j->unit;
534                 goto finish;
535         }
536
537         j->result = result;
538
539         log_debug("Job %s/%s finished, result=%s", j->unit->id, job_type_to_string(j->type), job_result_to_string(result));
540
541         if (result == JOB_FAILED)
542                 j->manager->n_failed_jobs ++;
543
544         u = j->unit;
545         t = j->type;
546         job_uninstall(j);
547         job_free(j);
548
549         job_print_status_message(u, t, result);
550
551         /* Fail depending jobs on failure */
552         if (result != JOB_DONE) {
553
554                 if (t == JOB_START ||
555                     t == JOB_VERIFY_ACTIVE ||
556                     t == JOB_RELOAD_OR_START) {
557
558                         SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY], i)
559                                 if (other->job &&
560                                     (other->job->type == JOB_START ||
561                                      other->job->type == JOB_VERIFY_ACTIVE ||
562                                      other->job->type == JOB_RELOAD_OR_START)) {
563                                         job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
564                                         recursed = true;
565                                 }
566
567                         SET_FOREACH(other, u->dependencies[UNIT_BOUND_BY], i)
568                                 if (other->job &&
569                                     (other->job->type == JOB_START ||
570                                      other->job->type == JOB_VERIFY_ACTIVE ||
571                                      other->job->type == JOB_RELOAD_OR_START)) {
572                                         job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
573                                         recursed = true;
574                                 }
575
576                         SET_FOREACH(other, u->dependencies[UNIT_REQUIRED_BY_OVERRIDABLE], i)
577                                 if (other->job &&
578                                     !other->job->override &&
579                                     (other->job->type == JOB_START ||
580                                      other->job->type == JOB_VERIFY_ACTIVE ||
581                                      other->job->type == JOB_RELOAD_OR_START)) {
582                                         job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
583                                         recursed = true;
584                                 }
585
586                 } else if (t == JOB_STOP) {
587
588                         SET_FOREACH(other, u->dependencies[UNIT_CONFLICTED_BY], i)
589                                 if (other->job &&
590                                     (other->job->type == JOB_START ||
591                                      other->job->type == JOB_VERIFY_ACTIVE ||
592                                      other->job->type == JOB_RELOAD_OR_START)) {
593                                         job_finish_and_invalidate(other->job, JOB_DEPENDENCY);
594                                         recursed = true;
595                                 }
596                 }
597         }
598
599         /* Trigger OnFailure dependencies that are not generated by
600          * the unit itself. We don't tread JOB_CANCELED as failure in
601          * this context. And JOB_FAILURE is already handled by the
602          * unit itself. */
603         if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
604                 log_notice("Job %s/%s failed with result '%s'.",
605                            u->id,
606                            job_type_to_string(t),
607                            job_result_to_string(result));
608
609                 unit_trigger_on_failure(u);
610         }
611
612 finish:
613         /* Try to start the next jobs that can be started */
614         SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
615                 if (other->job)
616                         job_add_to_run_queue(other->job);
617         SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i)
618                 if (other->job)
619                         job_add_to_run_queue(other->job);
620
621         manager_check_finished(u->manager);
622
623         return recursed;
624 }
625
626 int job_start_timer(Job *j) {
627         struct itimerspec its;
628         struct epoll_event ev;
629         int fd, r;
630         assert(j);
631
632         if (j->unit->job_timeout <= 0 ||
633             j->timer_watch.type == WATCH_JOB_TIMER)
634                 return 0;
635
636         assert(j->timer_watch.type == WATCH_INVALID);
637
638         if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0) {
639                 r = -errno;
640                 goto fail;
641         }
642
643         zero(its);
644         timespec_store(&its.it_value, j->unit->job_timeout);
645
646         if (timerfd_settime(fd, 0, &its, NULL) < 0) {
647                 r = -errno;
648                 goto fail;
649         }
650
651         zero(ev);
652         ev.data.ptr = &j->timer_watch;
653         ev.events = EPOLLIN;
654
655         if (epoll_ctl(j->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
656                 r = -errno;
657                 goto fail;
658         }
659
660         j->timer_watch.type = WATCH_JOB_TIMER;
661         j->timer_watch.fd = fd;
662         j->timer_watch.data.job = j;
663
664         return 0;
665
666 fail:
667         if (fd >= 0)
668                 close_nointr_nofail(fd);
669
670         return r;
671 }
672
673 void job_add_to_run_queue(Job *j) {
674         assert(j);
675         assert(j->installed);
676
677         if (j->in_run_queue)
678                 return;
679
680         LIST_PREPEND(Job, run_queue, j->manager->run_queue, j);
681         j->in_run_queue = true;
682 }
683
684 void job_add_to_dbus_queue(Job *j) {
685         assert(j);
686         assert(j->installed);
687
688         if (j->in_dbus_queue)
689                 return;
690
691         /* We don't check if anybody is subscribed here, since this
692          * job might just have been created and not yet assigned to a
693          * connection/client. */
694
695         LIST_PREPEND(Job, dbus_queue, j->manager->dbus_job_queue, j);
696         j->in_dbus_queue = true;
697 }
698
699 char *job_dbus_path(Job *j) {
700         char *p;
701
702         assert(j);
703
704         if (asprintf(&p, "/org/freedesktop/systemd1/job/%lu", (unsigned long) j->id) < 0)
705                 return NULL;
706
707         return p;
708 }
709
710 void job_timer_event(Job *j, uint64_t n_elapsed, Watch *w) {
711         assert(j);
712         assert(w == &j->timer_watch);
713
714         log_warning("Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
715         job_finish_and_invalidate(j, JOB_TIMEOUT);
716 }
717
718 static const char* const job_state_table[_JOB_STATE_MAX] = {
719         [JOB_WAITING] = "waiting",
720         [JOB_RUNNING] = "running"
721 };
722
723 DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
724
725 static const char* const job_type_table[_JOB_TYPE_MAX] = {
726         [JOB_START] = "start",
727         [JOB_VERIFY_ACTIVE] = "verify-active",
728         [JOB_STOP] = "stop",
729         [JOB_RELOAD] = "reload",
730         [JOB_RELOAD_OR_START] = "reload-or-start",
731         [JOB_RESTART] = "restart",
732         [JOB_TRY_RESTART] = "try-restart",
733 };
734
735 DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
736
737 static const char* const job_mode_table[_JOB_MODE_MAX] = {
738         [JOB_FAIL] = "fail",
739         [JOB_REPLACE] = "replace",
740         [JOB_ISOLATE] = "isolate",
741         [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
742         [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements"
743 };
744
745 DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
746
747 static const char* const job_result_table[_JOB_RESULT_MAX] = {
748         [JOB_DONE] = "done",
749         [JOB_CANCELED] = "canceled",
750         [JOB_TIMEOUT] = "timeout",
751         [JOB_FAILED] = "failed",
752         [JOB_DEPENDENCY] = "dependency",
753         [JOB_SKIPPED] = "skipped"
754 };
755
756 DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);