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