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