]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/job.c
Merge pull request #986 from karelzak/monitor
[thirdparty/systemd.git] / src / core / job.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
60918275 2
a7334b09
LP
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
5430f7f2
LP
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
a7334b09
LP
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
1ffba6fe 22#include <errno.h>
60918275 23
718db961
LP
24#include "sd-id128.h"
25#include "sd-messages.h"
94f04347
LP
26#include "set.h"
27#include "unit.h"
60918275 28#include "macro.h"
94f04347 29#include "strv.h"
f50e0a01 30#include "log.h"
4139c1b2 31#include "dbus-job.h"
c65eb836 32#include "special.h"
f485606b 33#include "async.h"
c65eb836 34#include "virt.h"
8f8f05a9 35#include "dbus.h"
288a74cc 36#include "terminal-util.h"
97e6a119 37
39a18c60 38Job* job_new_raw(Unit *unit) {
60918275
LP
39 Job *j;
40
39a18c60
MS
41 /* used for deserialization */
42
87f0e418 43 assert(unit);
60918275 44
39a18c60
MS
45 j = new0(Job, 1);
46 if (!j)
60918275
LP
47 return NULL;
48
668ad332 49 j->manager = unit->manager;
87f0e418 50 j->unit = unit;
e0209d83 51 j->type = _JOB_TYPE_INVALID;
faf919f1 52
39a18c60
MS
53 return j;
54}
55
56Job* job_new(Unit *unit, JobType type) {
57 Job *j;
58
59 assert(type < _JOB_TYPE_MAX);
60
61 j = job_new_raw(unit);
62 if (!j)
63 return NULL;
64
65 j->id = j->manager->current_job_id++;
66 j->type = type;
67
e5b5ae50 68 /* We don't link it here, that's what job_dependency() is for */
60918275
LP
69
70 return j;
71}
72
97e7d748
MS
73void job_free(Job *j) {
74 assert(j);
75 assert(!j->installed);
02a3bcc6
MS
76 assert(!j->transaction_prev);
77 assert(!j->transaction_next);
78 assert(!j->subject_list);
79 assert(!j->object_list);
60918275 80
c1e1601e 81 if (j->in_run_queue)
71fda00f 82 LIST_REMOVE(run_queue, j->manager->run_queue, j);
c1e1601e
LP
83
84 if (j->in_dbus_queue)
71fda00f 85 LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j);
c1e1601e 86
718db961 87 sd_event_source_unref(j->timer_event_source);
faf919f1 88
b39a2770
SW
89 sd_bus_track_unref(j->clients);
90 strv_free(j->deserialized_clients);
faf919f1 91
60918275
LP
92 free(j);
93}
a66d02c3 94
9c3349e2
LP
95static void job_set_state(Job *j, JobState state) {
96 assert(j);
97 assert(state >= 0);
98 assert(state < _JOB_STATE_MAX);
99
100 if (j->state == state)
101 return;
102
103 j->state = state;
104
105 if (!j->installed)
106 return;
107
108 if (j->state == JOB_RUNNING)
109 j->unit->manager->n_running_jobs++;
110 else {
111 assert(j->state == JOB_WAITING);
112 assert(j->unit->manager->n_running_jobs > 0);
113
114 j->unit->manager->n_running_jobs--;
115
116 if (j->unit->manager->n_running_jobs <= 0)
117 j->unit->manager->jobs_in_progress_event_source = sd_event_source_unref(j->unit->manager->jobs_in_progress_event_source);
118 }
119}
120
05d576f1 121void job_uninstall(Job *j) {
e0209d83
MS
122 Job **pj;
123
05d576f1 124 assert(j->installed);
e0209d83 125
9c3349e2
LP
126 job_set_state(j, JOB_WAITING);
127
e0209d83
MS
128 pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
129 assert(*pj == j);
130
05d576f1
MS
131 /* Detach from next 'bigger' objects */
132
39a18c60
MS
133 /* daemon-reload should be transparent to job observers */
134 if (j->manager->n_reloading <= 0)
135 bus_job_send_removed_signal(j);
05d576f1 136
e0209d83
MS
137 *pj = NULL;
138
d6a093d0 139 unit_add_to_gc_queue(j->unit);
05d576f1
MS
140
141 hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
142 j->installed = false;
143}
144
656bbffc
MS
145static bool job_type_allows_late_merge(JobType t) {
146 /* Tells whether it is OK to merge a job of type 't' with an already
147 * running job.
148 * Reloads cannot be merged this way. Think of the sequence:
149 * 1. Reload of a daemon is in progress; the daemon has already loaded
150 * its config file, but hasn't completed the reload operation yet.
151 * 2. Edit foo's config file.
152 * 3. Trigger another reload to have the daemon use the new config.
153 * Should the second reload job be merged into the first one, the daemon
154 * would not know about the new config.
155 * JOB_RESTART jobs on the other hand can be merged, because they get
156 * patched into JOB_START after stopping the unit. So if we see a
157 * JOB_RESTART running, it means the unit hasn't stopped yet and at
158 * this time the merge is still allowed. */
e0209d83 159 return t != JOB_RELOAD;
656bbffc
MS
160}
161
162static void job_merge_into_installed(Job *j, Job *other) {
163 assert(j->installed);
164 assert(j->unit == other->unit);
165
e0209d83
MS
166 if (j->type != JOB_NOP)
167 job_type_merge_and_collapse(&j->type, other->type, j->unit);
168 else
169 assert(other->type == JOB_NOP);
656bbffc
MS
170
171 j->override = j->override || other->override;
23ade460 172 j->irreversible = j->irreversible || other->irreversible;
e45460d6 173 j->ignore_order = j->ignore_order || other->ignore_order;
656bbffc
MS
174}
175
176Job* job_install(Job *j) {
e0209d83
MS
177 Job **pj;
178 Job *uj;
05d576f1 179
656bbffc 180 assert(!j->installed);
e0209d83 181 assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
9c3349e2 182 assert(j->state == JOB_WAITING);
e0209d83
MS
183
184 pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
185 uj = *pj;
656bbffc 186
05d576f1 187 if (uj) {
61da906a 188 if (job_type_is_conflicting(uj->type, j->type))
1abc85b8 189 job_finish_and_invalidate(uj, JOB_CANCELED, false);
656bbffc
MS
190 else {
191 /* not conflicting, i.e. mergeable */
192
61da906a 193 if (uj->state == JOB_WAITING ||
656bbffc
MS
194 (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
195 job_merge_into_installed(uj, j);
f2341e0a 196 log_unit_debug(uj->unit,
66870f90
ZJS
197 "Merged into installed job %s/%s as %u",
198 uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
656bbffc
MS
199 return uj;
200 } else {
201 /* already running and not safe to merge into */
202 /* Patch uj to become a merged job and re-run it. */
203 /* XXX It should be safer to queue j to run after uj finishes, but it is
204 * not currently possible to have more than one installed job per unit. */
205 job_merge_into_installed(uj, j);
f2341e0a 206 log_unit_debug(uj->unit,
66870f90
ZJS
207 "Merged into running job, re-running: %s/%s as %u",
208 uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
9c3349e2
LP
209
210 job_set_state(uj, JOB_WAITING);
656bbffc
MS
211 return uj;
212 }
213 }
05d576f1
MS
214 }
215
656bbffc 216 /* Install the job */
e0209d83 217 *pj = j;
05d576f1 218 j->installed = true;
9c3349e2 219
05d576f1 220 j->manager->n_installed_jobs ++;
f2341e0a 221 log_unit_debug(j->unit,
66870f90
ZJS
222 "Installed new job %s/%s as %u",
223 j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
656bbffc 224 return j;
05d576f1
MS
225}
226
e0209d83
MS
227int job_install_deserialized(Job *j) {
228 Job **pj;
229
39a18c60
MS
230 assert(!j->installed);
231
e0209d83
MS
232 if (j->type < 0 || j->type >= _JOB_TYPE_MAX_IN_TRANSACTION) {
233 log_debug("Invalid job type %s in deserialization.", strna(job_type_to_string(j->type)));
234 return -EINVAL;
235 }
236
237 pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
e0209d83 238 if (*pj) {
f2341e0a 239 log_unit_debug(j->unit, "Unit already has a job installed. Not installing deserialized job.");
e0209d83 240 return -EEXIST;
39a18c60 241 }
9c3349e2 242
e0209d83 243 *pj = j;
39a18c60 244 j->installed = true;
9c3349e2
LP
245
246 if (j->state == JOB_RUNNING)
247 j->unit->manager->n_running_jobs++;
248
f2341e0a 249 log_unit_debug(j->unit,
66870f90
ZJS
250 "Reinstalled deserialized job %s/%s as %u",
251 j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
e0209d83 252 return 0;
39a18c60
MS
253}
254
1da4264f 255JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
e5b5ae50
LP
256 JobDependency *l;
257
258 assert(object);
259
260 /* Adds a new job link, which encodes that the 'subject' job
261 * needs the 'object' job in some way. If 'subject' is NULL
262 * this means the 'anchor' job (i.e. the one the user
35b8ca3a 263 * explicitly asked for) is the requester. */
e5b5ae50 264
ceed3570 265 if (!(l = new0(JobDependency, 1)))
e5b5ae50
LP
266 return NULL;
267
268 l->subject = subject;
269 l->object = object;
270 l->matters = matters;
69dd2852 271 l->conflicts = conflicts;
e5b5ae50 272
44d8db9e 273 if (subject)
71fda00f 274 LIST_PREPEND(subject, subject->subject_list, l);
e5b5ae50 275
71fda00f 276 LIST_PREPEND(object, object->object_list, l);
e5b5ae50
LP
277
278 return l;
279}
280
1da4264f 281void job_dependency_free(JobDependency *l) {
e5b5ae50
LP
282 assert(l);
283
44d8db9e 284 if (l->subject)
71fda00f 285 LIST_REMOVE(subject, l->subject->subject_list, l);
e5b5ae50 286
71fda00f 287 LIST_REMOVE(object, l->object->object_list, l);
e5b5ae50
LP
288
289 free(l);
290}
291
1ffba6fe 292void job_dump(Job *j, FILE*f, const char *prefix) {
a66d02c3
LP
293 assert(j);
294 assert(f);
295
9eb63b3c
LP
296 if (!prefix)
297 prefix = "";
298
ceed3570 299 fprintf(f,
40d50879
LP
300 "%s-> Job %u:\n"
301 "%s\tAction: %s -> %s\n"
5cb5a6ff 302 "%s\tState: %s\n"
23ade460
MS
303 "%s\tForced: %s\n"
304 "%s\tIrreversible: %s\n",
ceed3570 305 prefix, j->id,
ac155bb8 306 prefix, j->unit->id, job_type_to_string(j->type),
94f04347 307 prefix, job_state_to_string(j->state),
23ade460
MS
308 prefix, yes_no(j->override),
309 prefix, yes_no(j->irreversible));
a66d02c3 310}
e5b5ae50 311
348e27fe
MS
312/*
313 * Merging is commutative, so imagine the matrix as symmetric. We store only
314 * its lower triangle to avoid duplication. We don't store the main diagonal,
315 * because A merged with A is simply A.
316 *
e0209d83
MS
317 * If the resulting type is collapsed immediately afterwards (to get rid of
318 * the JOB_RELOAD_OR_START, which lies outside the lookup function's domain),
319 * the following properties hold:
320 *
48b4eab4 321 * Merging is associative! A merged with B, and then merged with C is the same
103635db 322 * as A merged with the result of B merged with C.
348e27fe
MS
323 *
324 * Mergeability is transitive! If A can be merged with B and B with C then
325 * A also with C.
326 *
327 * Also, if A merged with B cannot be merged with C, then either A or B cannot
328 * be merged with C either.
329 */
330static const JobType job_merging_table[] = {
e0209d83
MS
331/* What \ With * JOB_START JOB_VERIFY_ACTIVE JOB_STOP JOB_RELOAD */
332/*********************************************************************************/
348e27fe
MS
333/*JOB_START */
334/*JOB_VERIFY_ACTIVE */ JOB_START,
335/*JOB_STOP */ -1, -1,
336/*JOB_RELOAD */ JOB_RELOAD_OR_START, JOB_RELOAD, -1,
e0209d83 337/*JOB_RESTART */ JOB_RESTART, JOB_RESTART, -1, JOB_RESTART,
348e27fe
MS
338};
339
340JobType job_type_lookup_merge(JobType a, JobType b) {
e0209d83
MS
341 assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX_MERGING * (_JOB_TYPE_MAX_MERGING - 1) / 2);
342 assert(a >= 0 && a < _JOB_TYPE_MAX_MERGING);
343 assert(b >= 0 && b < _JOB_TYPE_MAX_MERGING);
1ffba6fe
LP
344
345 if (a == b)
348e27fe 346 return a;
1ffba6fe 347
348e27fe
MS
348 if (a < b) {
349 JobType tmp = a;
350 a = b;
351 b = tmp;
1ffba6fe 352 }
e094e853 353
348e27fe 354 return job_merging_table[(a - 1) * a / 2 + b];
e094e853 355}
cd2dbd7d 356
593fbdd2
LP
357bool job_type_is_redundant(JobType a, UnitActiveState b) {
358 switch (a) {
359
360 case JOB_START:
361 return
362 b == UNIT_ACTIVE ||
032ff4af 363 b == UNIT_RELOADING;
593fbdd2
LP
364
365 case JOB_STOP:
366 return
6124958c 367 b == UNIT_INACTIVE ||
fdf20a31 368 b == UNIT_FAILED;
593fbdd2
LP
369
370 case JOB_VERIFY_ACTIVE:
371 return
372 b == UNIT_ACTIVE ||
032ff4af 373 b == UNIT_RELOADING;
593fbdd2
LP
374
375 case JOB_RELOAD:
376 return
032ff4af 377 b == UNIT_RELOADING;
593fbdd2 378
593fbdd2
LP
379 case JOB_RESTART:
380 return
381 b == UNIT_ACTIVATING;
382
7e803f5e
MS
383 case JOB_NOP:
384 return true;
385
e0209d83
MS
386 default:
387 assert_not_reached("Invalid job type");
388 }
389}
390
c6497ccb 391JobType job_type_collapse(JobType t, Unit *u) {
e0209d83
MS
392 UnitActiveState s;
393
c6497ccb 394 switch (t) {
e0209d83 395
593fbdd2 396 case JOB_TRY_RESTART:
e0209d83
MS
397 s = unit_active_state(u);
398 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
c6497ccb
LP
399 return JOB_NOP;
400
401 return JOB_RESTART;
e0209d83
MS
402
403 case JOB_RELOAD_OR_START:
404 s = unit_active_state(u);
405 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
c6497ccb
LP
406 return JOB_START;
407
408 return JOB_RELOAD;
593fbdd2
LP
409
410 default:
c6497ccb 411 return t;
593fbdd2
LP
412 }
413}
414
e0209d83 415int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
c6497ccb
LP
416 JobType t;
417
418 t = job_type_lookup_merge(*a, b);
e0209d83
MS
419 if (t < 0)
420 return -EEXIST;
c6497ccb
LP
421
422 *a = job_type_collapse(t, u);
e0209d83
MS
423 return 0;
424}
425
9588bc32 426static bool job_is_runnable(Job *j) {
034c6ed7 427 Iterator i;
87f0e418 428 Unit *other;
5cb5a6ff
LP
429
430 assert(j);
ac1135be 431 assert(j->installed);
5cb5a6ff 432
87f0e418 433 /* Checks whether there is any job running for the units this
5cb5a6ff 434 * job needs to be running after (in the case of a 'positive'
e67c3609
LP
435 * job type) or before (in the case of a 'negative' job
436 * type. */
437
66ca4ec4
LP
438 /* Note that unit types have a say in what is runnable,
439 * too. For example, if they return -EAGAIN from
440 * unit_start() they can indicate they are not
441 * runnable yet. */
442
e67c3609 443 /* First check if there is an override */
cebe0d41 444 if (j->ignore_order)
e67c3609 445 return true;
5cb5a6ff 446
e0209d83
MS
447 if (j->type == JOB_NOP)
448 return true;
449
5cb5a6ff
LP
450 if (j->type == JOB_START ||
451 j->type == JOB_VERIFY_ACTIVE ||
e0209d83 452 j->type == JOB_RELOAD) {
5cb5a6ff
LP
453
454 /* Immediate result is that the job is or might be
fc08079e 455 * started. In this case let's wait for the
5cb5a6ff
LP
456 * dependencies, regardless whether they are
457 * starting or stopping something. */
458
ac155bb8
MS
459 SET_FOREACH(other, j->unit->dependencies[UNIT_AFTER], i)
460 if (other->job)
5cb5a6ff
LP
461 return false;
462 }
463
464 /* Also, if something else is being stopped and we should
fc08079e 465 * change state after it, then let's wait. */
5cb5a6ff 466
ac155bb8
MS
467 SET_FOREACH(other, j->unit->dependencies[UNIT_BEFORE], i)
468 if (other->job &&
469 (other->job->type == JOB_STOP ||
e0209d83 470 other->job->type == JOB_RESTART))
5cb5a6ff
LP
471 return false;
472
473 /* This means that for a service a and a service b where b
474 * shall be started after a:
475 *
476 * start a + start b → 1st step start a, 2nd step start b
477 * start a + stop b → 1st step stop b, 2nd step start a
478 * stop a + start b → 1st step stop a, 2nd step start b
479 * stop a + stop b → 1st step stop b, 2nd step stop a
480 *
481 * This has the side effect that restarts are properly
482 * synchronized too. */
483
484 return true;
485}
486
bbd1a837 487static void job_change_type(Job *j, JobType newtype) {
f2341e0a
LP
488 assert(j);
489
490 log_unit_debug(j->unit,
66870f90
ZJS
491 "Converting job %s/%s -> %s/%s",
492 j->unit->id, job_type_to_string(j->type),
493 j->unit->id, job_type_to_string(newtype));
bbd1a837
MS
494
495 j->type = newtype;
496}
497
d1a34ae9
MS
498static int job_perform_on_unit(Job **j) {
499 /* While we execute this operation the job might go away (for
500 * example: because it finishes immediately or is replaced by a new,
501 * conflicting job.) To make sure we don't access a freed job later on
502 * we store the id here, so that we can verify the job is still
503 * valid. */
504 Manager *m = (*j)->manager;
505 Unit *u = (*j)->unit;
506 JobType t = (*j)->type;
507 uint32_t id = (*j)->id;
508 int r;
509
510 switch (t) {
511 case JOB_START:
512 r = unit_start(u);
513 break;
514
515 case JOB_RESTART:
516 t = JOB_STOP;
517 case JOB_STOP:
518 r = unit_stop(u);
519 break;
520
521 case JOB_RELOAD:
522 r = unit_reload(u);
523 break;
524
525 default:
526 assert_not_reached("Invalid job type");
527 }
528
529 /* Log if the job still exists and the start/stop/reload function
530 * actually did something. */
531 *j = manager_get_job(m, id);
532 if (*j && r > 0)
533 unit_status_emit_starting_stopping_reloading(u, t);
534
535 return r;
536}
537
5cb5a6ff
LP
538int job_run_and_invalidate(Job *j) {
539 int r;
ac1135be 540
5cb5a6ff 541 assert(j);
ac1135be 542 assert(j->installed);
e0209d83 543 assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
66aa6f7f 544 assert(j->in_run_queue);
5cb5a6ff 545
71fda00f 546 LIST_REMOVE(run_queue, j->manager->run_queue, j);
66aa6f7f 547 j->in_run_queue = false;
5cb5a6ff
LP
548
549 if (j->state != JOB_WAITING)
550 return 0;
551
034c6ed7
LP
552 if (!job_is_runnable(j))
553 return -EAGAIN;
554
9c3349e2 555 job_set_state(j, JOB_RUNNING);
c1e1601e 556 job_add_to_dbus_queue(j);
83c60c9f 557
2cf19a7a 558
5cb5a6ff
LP
559 switch (j->type) {
560
5cb5a6ff 561 case JOB_VERIFY_ACTIVE: {
87f0e418
LP
562 UnitActiveState t = unit_active_state(j->unit);
563 if (UNIT_IS_ACTIVE_OR_RELOADING(t))
5cb5a6ff 564 r = -EALREADY;
87f0e418 565 else if (t == UNIT_ACTIVATING)
5cb5a6ff
LP
566 r = -EAGAIN;
567 else
6a371e23 568 r = -EBADR;
5cb5a6ff
LP
569 break;
570 }
571
d1a34ae9 572 case JOB_START:
5cb5a6ff 573 case JOB_STOP:
dd17d388 574 case JOB_RESTART:
d1a34ae9 575 r = job_perform_on_unit(&j);
57339f47 576
d1a34ae9
MS
577 /* If the unit type does not support starting/stopping,
578 * then simply wait. */
57339f47
LP
579 if (r == -EBADR)
580 r = 0;
5cb5a6ff
LP
581 break;
582
583 case JOB_RELOAD:
d1a34ae9 584 r = job_perform_on_unit(&j);
5cb5a6ff
LP
585 break;
586
e0209d83
MS
587 case JOB_NOP:
588 r = -EALREADY;
589 break;
590
5cb5a6ff 591 default:
44d8db9e 592 assert_not_reached("Unknown job type");
5cb5a6ff
LP
593 }
594
e0209d83 595 if (j) {
2cf19a7a 596 if (r == -EALREADY)
5273510e 597 r = job_finish_and_invalidate(j, JOB_DONE, true);
6a371e23 598 else if (r == -EBADR)
5273510e 599 r = job_finish_and_invalidate(j, JOB_SKIPPED, true);
6a371e23
ZJS
600 else if (r == -ENOEXEC)
601 r = job_finish_and_invalidate(j, JOB_INVALID, true);
59fccdc5
LP
602 else if (r == -EPROTO)
603 r = job_finish_and_invalidate(j, JOB_ASSERT, true);
15411c0c 604 else if (r == -EOPNOTSUPP)
0faacd47 605 r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true);
9c3349e2
LP
606 else if (r == -EAGAIN)
607 job_set_state(j, JOB_WAITING);
608 else if (r < 0)
5273510e 609 r = job_finish_and_invalidate(j, JOB_FAILED, true);
2cf19a7a 610 }
5cb5a6ff
LP
611
612 return r;
613}
614
44a6b1b6 615_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
aa49ab5f 616 const char *format;
c6918296 617 const UnitStatusMessageFormats *format_table;
aa49ab5f
MS
618 static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = {
619 [JOB_DONE] = "Started %s.",
620 [JOB_TIMEOUT] = "Timed out starting %s.",
621 [JOB_FAILED] = "Failed to start %s.",
622 [JOB_DEPENDENCY] = "Dependency failed for %s.",
623 [JOB_ASSERT] = "Assertion failed for %s.",
624 [JOB_UNSUPPORTED] = "Starting of %s not supported.",
625 };
626 static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
627 [JOB_DONE] = "Stopped %s.",
628 [JOB_FAILED] = "Stopped (with error) %s.",
629 [JOB_TIMEOUT] = "Timed out stoppping %s.",
630 };
631 static const char *const generic_finished_reload_job[_JOB_RESULT_MAX] = {
632 [JOB_DONE] = "Reloaded %s.",
633 [JOB_FAILED] = "Reload failed for %s.",
634 [JOB_TIMEOUT] = "Timed out reloading %s.",
635 };
636 /* When verify-active detects the unit is inactive, report it.
637 * Most likely a DEPEND warning from a requisiting unit will
638 * occur next and it's nice to see what was requisited. */
639 static const char *const generic_finished_verify_active_job[_JOB_RESULT_MAX] = {
640 [JOB_SKIPPED] = "%s is not active.",
641 };
877d54e9
LP
642
643 assert(u);
644 assert(t >= 0);
645 assert(t < _JOB_TYPE_MAX);
c6918296 646
aa49ab5f
MS
647 if (t == JOB_START || t == JOB_STOP || t == JOB_RESTART) {
648 format_table = &UNIT_VTABLE(u)->status_message_formats;
649 if (format_table) {
650 format = t == JOB_START ? format_table->finished_start_job[result] :
651 format_table->finished_stop_job[result];
652 if (format)
653 return format;
654 }
655 }
877d54e9 656
aa49ab5f 657 /* Return generic strings */
877d54e9 658 if (t == JOB_START)
aa49ab5f 659 return generic_finished_start_job[result];
877d54e9 660 else if (t == JOB_STOP || t == JOB_RESTART)
aa49ab5f
MS
661 return generic_finished_stop_job[result];
662 else if (t == JOB_RELOAD)
663 return generic_finished_reload_job[result];
664 else if (t == JOB_VERIFY_ACTIVE)
665 return generic_finished_verify_active_job[result];
877d54e9
LP
666
667 return NULL;
668}
669
670static void job_print_status_message(Unit *u, JobType t, JobResult result) {
671 const char *format;
aa49ab5f
MS
672 static const char* const job_result_status_table[_JOB_RESULT_MAX] = {
673 [JOB_DONE] = ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF,
674 [JOB_TIMEOUT] = ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF,
675 [JOB_FAILED] = ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF,
676 [JOB_DEPENDENCY] = ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF,
677 [JOB_SKIPPED] = ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF,
678 [JOB_ASSERT] = ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF,
679 [JOB_UNSUPPORTED] = ANSI_HIGHLIGHT_YELLOW_ON "UNSUPP" ANSI_HIGHLIGHT_OFF,
680 };
e02cd6f7 681
877d54e9
LP
682 assert(u);
683 assert(t >= 0);
684 assert(t < _JOB_TYPE_MAX);
685
aa49ab5f
MS
686 format = job_get_status_message_format(u, t, result);
687 if (!format)
688 return;
e02cd6f7 689
aa49ab5f
MS
690 if (result != JOB_DONE)
691 manager_flip_auto_status(u->manager, true);
e02cd6f7 692
aa49ab5f
MS
693 DISABLE_WARNING_FORMAT_NONLITERAL;
694 unit_status_printf(u, job_result_status_table[result], format);
695 REENABLE_WARNING;
7cf82e0b 696
aa49ab5f
MS
697 if (t == JOB_START && result == JOB_FAILED) {
698 _cleanup_free_ char *quoted = shell_maybe_quote(u->id);
7cf82e0b 699
aa49ab5f
MS
700 manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL,
701 "See 'systemctl status %s' for details.", strna(quoted));
e02cd6f7
LP
702 }
703}
704
877d54e9
LP
705static void job_log_status_message(Unit *u, JobType t, JobResult result) {
706 const char *format;
707 char buf[LINE_MAX];
b81bbe53 708 sd_id128_t mid;
64f575d2
MS
709 static const int job_result_log_level[_JOB_RESULT_MAX] = {
710 [JOB_DONE] = LOG_INFO,
711 [JOB_CANCELED] = LOG_INFO,
712 [JOB_TIMEOUT] = LOG_ERR,
713 [JOB_FAILED] = LOG_ERR,
714 [JOB_DEPENDENCY] = LOG_WARNING,
715 [JOB_SKIPPED] = LOG_NOTICE,
716 [JOB_INVALID] = LOG_INFO,
717 [JOB_ASSERT] = LOG_WARNING,
718 [JOB_UNSUPPORTED] = LOG_WARNING,
719 };
877d54e9
LP
720
721 assert(u);
722 assert(t >= 0);
723 assert(t < _JOB_TYPE_MAX);
724
81270860
LP
725 /* Skip this if it goes to the console. since we already print
726 * to the console anyway... */
727
728 if (log_on_console())
729 return;
730
aa49ab5f 731 format = job_get_status_message_format(u, t, result);
877d54e9
LP
732 if (!format)
733 return;
734
bcfce235 735 DISABLE_WARNING_FORMAT_NONLITERAL;
877d54e9 736 snprintf(buf, sizeof(buf), format, unit_description(u));
bcfce235 737 REENABLE_WARNING;
877d54e9 738
b81bbe53 739 if (t == JOB_START)
877d54e9 740 mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
b81bbe53
MS
741 else if (t == JOB_STOP || t == JOB_RESTART)
742 mid = SD_MESSAGE_UNIT_STOPPED;
877d54e9 743 else if (t == JOB_RELOAD)
b81bbe53
MS
744 mid = SD_MESSAGE_UNIT_RELOADED;
745 else {
64f575d2 746 log_struct(job_result_log_level[result],
4f29c6fe
MS
747 LOG_UNIT_ID(u),
748 LOG_MESSAGE("%s", buf),
749 "RESULT=%s", job_result_to_string(result),
750 NULL);
b81bbe53
MS
751 return;
752 }
753
64f575d2 754 log_struct(job_result_log_level[result],
b81bbe53
MS
755 LOG_MESSAGE_ID(mid),
756 LOG_UNIT_ID(u),
757 LOG_MESSAGE("%s", buf),
758 "RESULT=%s", job_result_to_string(result),
759 NULL);
877d54e9 760}
877d54e9 761
30961fa3
MS
762static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
763
764 /* No message if the job did not actually do anything due to failed condition. */
765 if (t == JOB_START && result == JOB_DONE && !u->condition_result)
766 return;
767
768 job_log_status_message(u, t, result);
769
770 /* Reload status messages have traditionally not been printed to console. */
771 if (t != JOB_RELOAD)
772 job_print_status_message(u, t, result);
773}
774
be7d9ff7
LP
775static void job_fail_dependencies(Unit *u, UnitDependency d) {
776 Unit *other;
777 Iterator i;
778
779 assert(u);
780
781 SET_FOREACH(other, u->dependencies[d], i) {
782 Job *j = other->job;
783
784 if (!j)
785 continue;
786 if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE))
787 continue;
788
789 job_finish_and_invalidate(j, JOB_DEPENDENCY, true);
790 }
791}
792
5273510e 793int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
87f0e418
LP
794 Unit *u;
795 Unit *other;
b866264a 796 JobType t;
034c6ed7 797 Iterator i;
5cb5a6ff
LP
798
799 assert(j);
ac1135be 800 assert(j->installed);
e0209d83 801 assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
5cb5a6ff 802
c6918296
MS
803 u = j->unit;
804 t = j->type;
805
806 j->result = result;
807
f2341e0a 808 log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
c6918296 809
30961fa3 810 job_emit_status_message(u, t, result);
c6918296 811
c1e1601e 812 job_add_to_dbus_queue(j);
f50e0a01 813
034c6ed7 814 /* Patch restart jobs so that they become normal start jobs */
c6918296 815 if (result == JOB_DONE && t == JOB_RESTART) {
f50e0a01 816
bbd1a837 817 job_change_type(j, JOB_START);
9c3349e2 818 job_set_state(j, JOB_WAITING);
cc42e081
LP
819
820 job_add_to_run_queue(j);
57981b98 821
57981b98 822 goto finish;
5cb5a6ff
LP
823 }
824
6a371e23 825 if (result == JOB_FAILED || result == JOB_INVALID)
76bf48b7
LP
826 j->manager->n_failed_jobs ++;
827
97e7d748 828 job_uninstall(j);
5cb5a6ff
LP
829 job_free(j);
830
831 /* Fail depending jobs on failure */
5273510e 832 if (result != JOB_DONE && recursive) {
be7d9ff7
LP
833 if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
834 job_fail_dependencies(u, UNIT_REQUIRED_BY);
835 job_fail_dependencies(u, UNIT_REQUISITE_OF);
836 job_fail_dependencies(u, UNIT_BOUND_BY);
837 job_fail_dependencies(u, UNIT_REQUIRED_BY_OVERRIDABLE);
838 job_fail_dependencies(u, UNIT_REQUISITE_OF_OVERRIDABLE);
839 } else if (t == JOB_STOP)
840 job_fail_dependencies(u, UNIT_CONFLICTED_BY);
5cb5a6ff
LP
841 }
842
c0daa706 843 /* Trigger OnFailure dependencies that are not generated by
66870f90 844 * the unit itself. We don't treat JOB_CANCELED as failure in
c0daa706
LP
845 * this context. And JOB_FAILURE is already handled by the
846 * unit itself. */
222ae6a8 847 if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
f2341e0a
LP
848 log_struct(LOG_NOTICE,
849 "JOB_TYPE=%s", job_type_to_string(t),
850 "JOB_RESULT=%s", job_result_to_string(result),
851 LOG_UNIT_ID(u),
852 LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.",
e2cc6eca
LP
853 u->id,
854 job_type_to_string(t),
855 job_result_to_string(result)),
f2341e0a 856 NULL);
222ae6a8 857
3ecaa09b 858 unit_start_on_failure(u);
222ae6a8 859 }
c0daa706 860
3ecaa09b
LP
861 unit_trigger_notify(u);
862
57981b98 863finish:
5cb5a6ff 864 /* Try to start the next jobs that can be started */
ac155bb8
MS
865 SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
866 if (other->job)
867 job_add_to_run_queue(other->job);
868 SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i)
869 if (other->job)
870 job_add_to_run_queue(other->job);
5cb5a6ff 871
ac155bb8 872 manager_check_finished(u->manager);
b0c918b9 873
5273510e 874 return 0;
5cb5a6ff 875}
034c6ed7 876
718db961
LP
877static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *userdata) {
878 Job *j = userdata;
f189ab18 879 Unit *u;
faf919f1 880
718db961
LP
881 assert(j);
882 assert(s == j->timer_event_source);
faf919f1 883
f2341e0a 884 log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
faf919f1 885
f189ab18 886 u = j->unit;
718db961 887 job_finish_and_invalidate(j, JOB_TIMEOUT, true);
f189ab18
LP
888
889 failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg);
890
718db961
LP
891 return 0;
892}
faf919f1 893
718db961
LP
894int job_start_timer(Job *j) {
895 int r;
faf919f1 896
8bb310c3 897 if (j->timer_event_source)
718db961 898 return 0;
faf919f1 899
718db961 900 j->begin_usec = now(CLOCK_MONOTONIC);
faf919f1 901
8bb310c3
ZJS
902 if (j->unit->job_timeout <= 0)
903 return 0;
904
6a0f1f6d
LP
905 r = sd_event_add_time(
906 j->manager->event,
907 &j->timer_event_source,
908 CLOCK_MONOTONIC,
909 j->begin_usec + j->unit->job_timeout, 0,
910 job_dispatch_timer, j);
718db961
LP
911 if (r < 0)
912 return r;
faf919f1 913
7dfbe2e3
TG
914 (void) sd_event_source_set_description(j->timer_event_source, "job-start");
915
718db961 916 return 0;
faf919f1
LP
917}
918
c1e1601e 919void job_add_to_run_queue(Job *j) {
034c6ed7 920 assert(j);
ac1135be 921 assert(j->installed);
034c6ed7
LP
922
923 if (j->in_run_queue)
924 return;
925
752b5905
LP
926 if (!j->manager->run_queue)
927 sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT);
928
71fda00f 929 LIST_PREPEND(run_queue, j->manager->run_queue, j);
034c6ed7
LP
930 j->in_run_queue = true;
931}
94f04347 932
c1e1601e
LP
933void job_add_to_dbus_queue(Job *j) {
934 assert(j);
935 assert(j->installed);
936
937 if (j->in_dbus_queue)
938 return;
939
a567261a
LP
940 /* We don't check if anybody is subscribed here, since this
941 * job might just have been created and not yet assigned to a
942 * connection/client. */
94b6dfa2 943
71fda00f 944 LIST_PREPEND(dbus_queue, j->manager->dbus_job_queue, j);
c1e1601e
LP
945 j->in_dbus_queue = true;
946}
947
ea430986
LP
948char *job_dbus_path(Job *j) {
949 char *p;
950
951 assert(j);
952
ccd06097 953 if (asprintf(&p, "/org/freedesktop/systemd1/job/%"PRIu32, j->id) < 0)
ea430986
LP
954 return NULL;
955
956 return p;
957}
958
39a18c60
MS
959int job_serialize(Job *j, FILE *f, FDSet *fds) {
960 fprintf(f, "job-id=%u\n", j->id);
961 fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
962 fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
963 fprintf(f, "job-override=%s\n", yes_no(j->override));
23ade460 964 fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
39a18c60
MS
965 fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
966 fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
718db961
LP
967
968 if (j->begin_usec > 0)
ccd06097 969 fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
718db961 970
b39a2770 971 bus_track_serialize(j->clients, f);
39a18c60
MS
972
973 /* End marker */
974 fputc('\n', f);
975 return 0;
976}
977
978int job_deserialize(Job *j, FILE *f, FDSet *fds) {
718db961
LP
979 assert(j);
980
39a18c60
MS
981 for (;;) {
982 char line[LINE_MAX], *l, *v;
983 size_t k;
984
985 if (!fgets(line, sizeof(line), f)) {
986 if (feof(f))
987 return 0;
988 return -errno;
989 }
990
991 char_array_0(line);
992 l = strstrip(line);
993
994 /* End marker */
995 if (l[0] == 0)
996 return 0;
997
998 k = strcspn(l, "=");
999
1000 if (l[k] == '=') {
1001 l[k] = 0;
1002 v = l+k+1;
1003 } else
1004 v = l+k;
1005
1006 if (streq(l, "job-id")) {
718db961 1007
39a18c60
MS
1008 if (safe_atou32(v, &j->id) < 0)
1009 log_debug("Failed to parse job id value %s", v);
718db961 1010
39a18c60 1011 } else if (streq(l, "job-type")) {
718db961
LP
1012 JobType t;
1013
1014 t = job_type_from_string(v);
39a18c60
MS
1015 if (t < 0)
1016 log_debug("Failed to parse job type %s", v);
e0209d83
MS
1017 else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION)
1018 log_debug("Cannot deserialize job of type %s", v);
39a18c60
MS
1019 else
1020 j->type = t;
718db961 1021
39a18c60 1022 } else if (streq(l, "job-state")) {
718db961
LP
1023 JobState s;
1024
1025 s = job_state_from_string(v);
39a18c60
MS
1026 if (s < 0)
1027 log_debug("Failed to parse job state %s", v);
1028 else
9c3349e2 1029 job_set_state(j, s);
718db961 1030
39a18c60 1031 } else if (streq(l, "job-override")) {
718db961
LP
1032 int b;
1033
1034 b = parse_boolean(v);
39a18c60
MS
1035 if (b < 0)
1036 log_debug("Failed to parse job override flag %s", v);
1037 else
1038 j->override = j->override || b;
718db961 1039
23ade460 1040 } else if (streq(l, "job-irreversible")) {
718db961
LP
1041 int b;
1042
1043 b = parse_boolean(v);
23ade460
MS
1044 if (b < 0)
1045 log_debug("Failed to parse job irreversible flag %s", v);
1046 else
1047 j->irreversible = j->irreversible || b;
718db961 1048
39a18c60 1049 } else if (streq(l, "job-sent-dbus-new-signal")) {
718db961
LP
1050 int b;
1051
1052 b = parse_boolean(v);
39a18c60
MS
1053 if (b < 0)
1054 log_debug("Failed to parse job sent_dbus_new_signal flag %s", v);
1055 else
1056 j->sent_dbus_new_signal = j->sent_dbus_new_signal || b;
718db961 1057
39a18c60 1058 } else if (streq(l, "job-ignore-order")) {
718db961
LP
1059 int b;
1060
1061 b = parse_boolean(v);
39a18c60
MS
1062 if (b < 0)
1063 log_debug("Failed to parse job ignore_order flag %s", v);
1064 else
1065 j->ignore_order = j->ignore_order || b;
718db961
LP
1066
1067 } else if (streq(l, "job-begin")) {
1068 unsigned long long ull;
1069
1070 if (sscanf(v, "%llu", &ull) != 1)
1071 log_debug("Failed to parse job-begin value %s", v);
39a18c60 1072 else
718db961
LP
1073 j->begin_usec = ull;
1074
8f8f05a9 1075 } else if (streq(l, "subscribed")) {
718db961 1076
b39a2770 1077 if (strv_extend(&j->deserialized_clients, v) < 0)
8f8f05a9 1078 return log_oom();
39a18c60
MS
1079 }
1080 }
1081}
1082
1083int job_coldplug(Job *j) {
718db961
LP
1084 int r;
1085
1086 assert(j);
39a18c60 1087
8f8f05a9
LP
1088 /* After deserialization is complete and the bus connection
1089 * set up again, let's start watching our subscribers again */
b39a2770 1090 r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients);
8f8f05a9
LP
1091 if (r < 0)
1092 return r;
1093
1727a595
MM
1094 if (j->state == JOB_WAITING)
1095 job_add_to_run_queue(j);
1096
8bb310c3 1097 if (j->begin_usec == 0 || j->unit->job_timeout == 0)
39a18c60
MS
1098 return 0;
1099
718db961
LP
1100 if (j->timer_event_source)
1101 j->timer_event_source = sd_event_source_unref(j->timer_event_source);
39a18c60 1102
6a0f1f6d
LP
1103 r = sd_event_add_time(
1104 j->manager->event,
1105 &j->timer_event_source,
1106 CLOCK_MONOTONIC,
1107 j->begin_usec + j->unit->job_timeout, 0,
1108 job_dispatch_timer, j);
718db961 1109 if (r < 0)
da927ba9 1110 log_debug_errno(r, "Failed to restart timeout for job: %m");
718db961 1111
7dfbe2e3
TG
1112 (void) sd_event_source_set_description(j->timer_event_source, "job-timeout");
1113
718db961 1114 return r;
39a18c60
MS
1115}
1116
c65eb836
LP
1117void job_shutdown_magic(Job *j) {
1118 assert(j);
1119
1120 /* The shutdown target gets some special treatment here: we
1121 * tell the kernel to begin with flushing its disk caches, to
1122 * optimize shutdown time a bit. Ideally we wouldn't hardcode
1123 * this magic into PID 1. However all other processes aren't
1124 * options either since they'd exit much sooner than PID 1 and
1125 * asynchronous sync() would cause their exit to be
1126 * delayed. */
1127
c2756a68 1128 if (j->type != JOB_START)
c65eb836
LP
1129 return;
1130
b2c23da8 1131 if (j->unit->manager->running_as != MANAGER_SYSTEM)
c2756a68
LP
1132 return;
1133
1134 if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
c65eb836
LP
1135 return;
1136
5b1869ea
OB
1137 /* In case messages on console has been disabled on boot */
1138 j->unit->manager->no_console_output = false;
1139
75f86906 1140 if (detect_container() > 0)
c65eb836
LP
1141 return;
1142
1143 asynchronous_sync();
1144}
1145
68db7a3b
ZJS
1146int job_get_timeout(Job *j, uint64_t *timeout) {
1147 Unit *u = j->unit;
1148 uint64_t x = -1, y = -1;
1149 int r = 0, q = 0;
1150
1151 assert(u);
1152
1153 if (j->timer_event_source) {
1154 r = sd_event_source_get_time(j->timer_event_source, &x);
1155 if (r < 0)
1156 return r;
1157 r = 1;
1158 }
1159
1160 if (UNIT_VTABLE(u)->get_timeout) {
1161 q = UNIT_VTABLE(u)->get_timeout(u, &y);
1162 if (q < 0)
1163 return q;
1164 }
1165
1166 if (r == 0 && q == 0)
1167 return 0;
1168
1169 *timeout = MIN(x, y);
1170
68db7a3b
ZJS
1171 return 1;
1172}
1173
94f04347
LP
1174static const char* const job_state_table[_JOB_STATE_MAX] = {
1175 [JOB_WAITING] = "waiting",
1176 [JOB_RUNNING] = "running"
1177};
1178
1179DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
1180
1181static const char* const job_type_table[_JOB_TYPE_MAX] = {
1182 [JOB_START] = "start",
1183 [JOB_VERIFY_ACTIVE] = "verify-active",
1184 [JOB_STOP] = "stop",
1185 [JOB_RELOAD] = "reload",
1186 [JOB_RELOAD_OR_START] = "reload-or-start",
1187 [JOB_RESTART] = "restart",
1188 [JOB_TRY_RESTART] = "try-restart",
e0209d83 1189 [JOB_NOP] = "nop",
94f04347
LP
1190};
1191
1192DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
b548631a
LP
1193
1194static const char* const job_mode_table[_JOB_MODE_MAX] = {
1195 [JOB_FAIL] = "fail",
c497c7a9 1196 [JOB_REPLACE] = "replace",
23ade460 1197 [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
e67c3609 1198 [JOB_ISOLATE] = "isolate",
2c5859af 1199 [JOB_FLUSH] = "flush",
cebe0d41 1200 [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
255baef6 1201 [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
b548631a
LP
1202};
1203
1204DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
5d44db4a
LP
1205
1206static const char* const job_result_table[_JOB_RESULT_MAX] = {
1207 [JOB_DONE] = "done",
1208 [JOB_CANCELED] = "canceled",
1209 [JOB_TIMEOUT] = "timeout",
1210 [JOB_FAILED] = "failed",
d68201e9 1211 [JOB_DEPENDENCY] = "dependency",
6a371e23
ZJS
1212 [JOB_SKIPPED] = "skipped",
1213 [JOB_INVALID] = "invalid",
59fccdc5 1214 [JOB_ASSERT] = "assert",
0faacd47 1215 [JOB_UNSUPPORTED] = "unsupported",
5d44db4a
LP
1216};
1217
1218DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);