]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/job.c
dhcp-identifier: fix for unaligned write
[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
391void job_type_collapse(JobType *t, Unit *u) {
392 UnitActiveState s;
393
394 switch (*t) {
395
593fbdd2 396 case JOB_TRY_RESTART:
e0209d83
MS
397 s = unit_active_state(u);
398 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
399 *t = JOB_NOP;
400 else
401 *t = JOB_RESTART;
402 break;
403
404 case JOB_RELOAD_OR_START:
405 s = unit_active_state(u);
406 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
407 *t = JOB_START;
408 else
409 *t = JOB_RELOAD;
410 break;
593fbdd2
LP
411
412 default:
e0209d83 413 ;
593fbdd2
LP
414 }
415}
416
e0209d83
MS
417int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
418 JobType t = job_type_lookup_merge(*a, b);
419 if (t < 0)
420 return -EEXIST;
421 *a = t;
422 job_type_collapse(a, u);
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
455 * started. In this case lets wait for the
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
465 * change state after it, then lets wait. */
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
5cb5a6ff
LP
498int job_run_and_invalidate(Job *j) {
499 int r;
2cf19a7a 500 uint32_t id;
637f8b8e 501 Manager *m = j->manager;
ac1135be 502
5cb5a6ff 503 assert(j);
ac1135be 504 assert(j->installed);
e0209d83 505 assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
66aa6f7f 506 assert(j->in_run_queue);
5cb5a6ff 507
71fda00f 508 LIST_REMOVE(run_queue, j->manager->run_queue, j);
66aa6f7f 509 j->in_run_queue = false;
5cb5a6ff
LP
510
511 if (j->state != JOB_WAITING)
512 return 0;
513
034c6ed7
LP
514 if (!job_is_runnable(j))
515 return -EAGAIN;
516
9c3349e2 517 job_set_state(j, JOB_RUNNING);
c1e1601e 518 job_add_to_dbus_queue(j);
83c60c9f 519
2cf19a7a
LP
520 /* While we execute this operation the job might go away (for
521 * example: because it is replaced by a new, conflicting
522 * job.) To make sure we don't access a freed job later on we
523 * store the id here, so that we can verify the job is still
524 * valid. */
525 id = j->id;
2cf19a7a 526
5cb5a6ff
LP
527 switch (j->type) {
528
529 case JOB_START:
87f0e418 530 r = unit_start(j->unit);
57339f47 531
dd17d388 532 /* If this unit cannot be started, then simply wait */
5cb5a6ff
LP
533 if (r == -EBADR)
534 r = 0;
535 break;
536
537 case JOB_VERIFY_ACTIVE: {
87f0e418
LP
538 UnitActiveState t = unit_active_state(j->unit);
539 if (UNIT_IS_ACTIVE_OR_RELOADING(t))
5cb5a6ff 540 r = -EALREADY;
87f0e418 541 else if (t == UNIT_ACTIVATING)
5cb5a6ff
LP
542 r = -EAGAIN;
543 else
6a371e23 544 r = -EBADR;
5cb5a6ff
LP
545 break;
546 }
547
548 case JOB_STOP:
dd17d388 549 case JOB_RESTART:
87f0e418 550 r = unit_stop(j->unit);
57339f47 551
dd17d388 552 /* If this unit cannot stopped, then simply wait. */
57339f47
LP
553 if (r == -EBADR)
554 r = 0;
5cb5a6ff
LP
555 break;
556
557 case JOB_RELOAD:
87f0e418 558 r = unit_reload(j->unit);
5cb5a6ff
LP
559 break;
560
e0209d83
MS
561 case JOB_NOP:
562 r = -EALREADY;
563 break;
564
5cb5a6ff 565 default:
44d8db9e 566 assert_not_reached("Unknown job type");
5cb5a6ff
LP
567 }
568
e0209d83
MS
569 j = manager_get_job(m, id);
570 if (j) {
2cf19a7a 571 if (r == -EALREADY)
5273510e 572 r = job_finish_and_invalidate(j, JOB_DONE, true);
6a371e23 573 else if (r == -EBADR)
5273510e 574 r = job_finish_and_invalidate(j, JOB_SKIPPED, true);
6a371e23
ZJS
575 else if (r == -ENOEXEC)
576 r = job_finish_and_invalidate(j, JOB_INVALID, true);
59fccdc5
LP
577 else if (r == -EPROTO)
578 r = job_finish_and_invalidate(j, JOB_ASSERT, true);
15411c0c 579 else if (r == -EOPNOTSUPP)
0faacd47 580 r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true);
9c3349e2
LP
581 else if (r == -EAGAIN)
582 job_set_state(j, JOB_WAITING);
583 else if (r < 0)
5273510e 584 r = job_finish_and_invalidate(j, JOB_FAILED, true);
2cf19a7a 585 }
5cb5a6ff
LP
586
587 return r;
588}
589
44a6b1b6 590_pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
c6918296 591 const UnitStatusMessageFormats *format_table;
877d54e9
LP
592
593 assert(u);
594 assert(t >= 0);
595 assert(t < _JOB_TYPE_MAX);
c6918296
MS
596
597 format_table = &UNIT_VTABLE(u)->status_message_formats;
598 if (!format_table)
877d54e9
LP
599 return NULL;
600
601 if (t == JOB_START)
602 return format_table->finished_start_job[result];
603 else if (t == JOB_STOP || t == JOB_RESTART)
604 return format_table->finished_stop_job[result];
605
606 return NULL;
607}
e02cd6f7 608
44a6b1b6 609_pure_ static const char *job_get_status_message_format_try_harder(Unit *u, JobType t, JobResult result) {
877d54e9
LP
610 const char *format;
611
612 assert(u);
613 assert(t >= 0);
614 assert(t < _JOB_TYPE_MAX);
615
616 format = job_get_status_message_format(u, t, result);
617 if (format)
618 return format;
619
620 /* Return generic strings */
e02cd6f7 621 if (t == JOB_START) {
877d54e9
LP
622 if (result == JOB_DONE)
623 return "Started %s.";
0faacd47
LP
624 else if (result == JOB_TIMEOUT)
625 return "Timed out starting %s.";
877d54e9
LP
626 else if (result == JOB_FAILED)
627 return "Failed to start %s.";
628 else if (result == JOB_DEPENDENCY)
629 return "Dependency failed for %s.";
0faacd47
LP
630 else if (result == JOB_ASSERT)
631 return "Assertion failed for %s.";
632 else if (result == JOB_UNSUPPORTED)
633 return "Starting of %s not supported.";
877d54e9
LP
634 } else if (t == JOB_STOP || t == JOB_RESTART) {
635 if (result == JOB_DONE)
636 return "Stopped %s.";
637 else if (result == JOB_FAILED)
638 return "Stopped (with error) %s.";
639 else if (result == JOB_TIMEOUT)
640 return "Timed out stoppping %s.";
641 } else if (t == JOB_RELOAD) {
642 if (result == JOB_DONE)
643 return "Reloaded %s.";
644 else if (result == JOB_FAILED)
645 return "Reload failed for %s.";
646 else if (result == JOB_TIMEOUT)
647 return "Timed out reloading %s.";
648 }
649
650 return NULL;
651}
652
653static void job_print_status_message(Unit *u, JobType t, JobResult result) {
654 const char *format;
e02cd6f7 655
877d54e9
LP
656 assert(u);
657 assert(t >= 0);
658 assert(t < _JOB_TYPE_MAX);
659
bcfce235
LP
660 DISABLE_WARNING_FORMAT_NONLITERAL;
661
877d54e9
LP
662 if (t == JOB_START) {
663 format = job_get_status_message_format(u, t, result);
c6918296
MS
664 if (!format)
665 return;
666
e02cd6f7
LP
667 switch (result) {
668
669 case JOB_DONE:
69120666 670 if (u->condition_result)
076a24ad 671 unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
e02cd6f7
LP
672 break;
673
0faacd47
LP
674 case JOB_TIMEOUT:
675 manager_flip_auto_status(u->manager, true);
676 unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
677 break;
678
8e07fc41 679 case JOB_FAILED: {
019c7fba 680 _cleanup_free_ char *quoted = NULL;
8e07fc41 681
019c7fba 682 quoted = shell_maybe_quote(u->id);
8e07fc41 683
cb8ccb22 684 manager_flip_auto_status(u->manager, true);
49b1d377 685 unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON "FAILED" ANSI_HIGHLIGHT_OFF, format);
019c7fba 686 manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
e02cd6f7 687 break;
8e07fc41 688 }
e02cd6f7
LP
689
690 case JOB_DEPENDENCY:
cb8ccb22 691 manager_flip_auto_status(u->manager, true);
49b1d377 692 unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "DEPEND" ANSI_HIGHLIGHT_OFF, format);
e02cd6f7
LP
693 break;
694
0faacd47 695 case JOB_ASSERT:
cb8ccb22 696 manager_flip_auto_status(u->manager, true);
0faacd47 697 unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "ASSERT" ANSI_HIGHLIGHT_OFF, format);
e02cd6f7
LP
698 break;
699
0faacd47 700 case JOB_UNSUPPORTED:
59fccdc5 701 manager_flip_auto_status(u->manager, true);
0faacd47 702 unit_status_printf(u, ANSI_HIGHLIGHT_YELLOW_ON "UNSUPP" ANSI_HIGHLIGHT_OFF, format);
59fccdc5
LP
703 break;
704
e02cd6f7
LP
705 default:
706 ;
707 }
708
1f136e7a 709 } else if (t == JOB_STOP || t == JOB_RESTART) {
e02cd6f7 710
877d54e9 711 format = job_get_status_message_format(u, t, result);
c6918296
MS
712 if (!format)
713 return;
714
e02cd6f7
LP
715 switch (result) {
716
717 case JOB_TIMEOUT:
cb8ccb22 718 manager_flip_auto_status(u->manager, true);
49b1d377 719 unit_status_printf(u, ANSI_HIGHLIGHT_RED_ON " TIME " ANSI_HIGHLIGHT_OFF, format);
e02cd6f7
LP
720 break;
721
722 case JOB_DONE:
723 case JOB_FAILED:
076a24ad 724 unit_status_printf(u, ANSI_GREEN_ON " OK " ANSI_HIGHLIGHT_OFF, format);
e02cd6f7
LP
725 break;
726
727 default:
728 ;
729 }
7cf82e0b
MS
730
731 } else if (t == JOB_VERIFY_ACTIVE) {
732
733 /* When verify-active detects the unit is inactive, report it.
734 * Most likely a DEPEND warning from a requisiting unit will
735 * occur next and it's nice to see what was requisited. */
736 if (result == JOB_SKIPPED)
49b1d377 737 unit_status_printf(u, ANSI_HIGHLIGHT_ON " INFO " ANSI_HIGHLIGHT_OFF, "%s is not active.");
e02cd6f7 738 }
bcfce235
LP
739
740 REENABLE_WARNING;
e02cd6f7
LP
741}
742
877d54e9
LP
743static void job_log_status_message(Unit *u, JobType t, JobResult result) {
744 const char *format;
745 char buf[LINE_MAX];
746
747 assert(u);
748 assert(t >= 0);
749 assert(t < _JOB_TYPE_MAX);
750
81270860
LP
751 /* Skip this if it goes to the console. since we already print
752 * to the console anyway... */
753
754 if (log_on_console())
755 return;
756
877d54e9
LP
757 format = job_get_status_message_format_try_harder(u, t, result);
758 if (!format)
759 return;
760
bcfce235 761 DISABLE_WARNING_FORMAT_NONLITERAL;
877d54e9 762 snprintf(buf, sizeof(buf), format, unit_description(u));
bcfce235 763 REENABLE_WARNING;
877d54e9
LP
764
765 if (t == JOB_START) {
766 sd_id128_t mid;
767
768 mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
f2341e0a
LP
769 log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
770 LOG_MESSAGE_ID(mid),
771 LOG_UNIT_ID(u),
772 LOG_MESSAGE("%s", buf),
773 "RESULT=%s", job_result_to_string(result),
774 NULL);
877d54e9
LP
775
776 } else if (t == JOB_STOP)
f2341e0a
LP
777 log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
778 LOG_MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
779 LOG_UNIT_ID(u),
780 LOG_MESSAGE("%s", buf),
781 "RESULT=%s", job_result_to_string(result),
782 NULL);
877d54e9
LP
783
784 else if (t == JOB_RELOAD)
f2341e0a
LP
785 log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
786 LOG_MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
787 LOG_UNIT_ID(u),
788 LOG_MESSAGE("%s", buf),
789 "RESULT=%s", job_result_to_string(result),
790 NULL);
877d54e9 791}
877d54e9 792
be7d9ff7
LP
793static void job_fail_dependencies(Unit *u, UnitDependency d) {
794 Unit *other;
795 Iterator i;
796
797 assert(u);
798
799 SET_FOREACH(other, u->dependencies[d], i) {
800 Job *j = other->job;
801
802 if (!j)
803 continue;
804 if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE))
805 continue;
806
807 job_finish_and_invalidate(j, JOB_DEPENDENCY, true);
808 }
809}
810
5273510e 811int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
87f0e418
LP
812 Unit *u;
813 Unit *other;
b866264a 814 JobType t;
034c6ed7 815 Iterator i;
5cb5a6ff
LP
816
817 assert(j);
ac1135be 818 assert(j->installed);
e0209d83 819 assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
5cb5a6ff 820
c6918296
MS
821 u = j->unit;
822 t = j->type;
823
824 j->result = result;
825
f2341e0a 826 log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
c6918296
MS
827
828 job_print_status_message(u, t, result);
877d54e9 829 job_log_status_message(u, t, result);
c6918296 830
c1e1601e 831 job_add_to_dbus_queue(j);
f50e0a01 832
034c6ed7 833 /* Patch restart jobs so that they become normal start jobs */
c6918296 834 if (result == JOB_DONE && t == JOB_RESTART) {
f50e0a01 835
bbd1a837 836 job_change_type(j, JOB_START);
9c3349e2 837 job_set_state(j, JOB_WAITING);
cc42e081
LP
838
839 job_add_to_run_queue(j);
57981b98 840
57981b98 841 goto finish;
5cb5a6ff
LP
842 }
843
6a371e23 844 if (result == JOB_FAILED || result == JOB_INVALID)
76bf48b7
LP
845 j->manager->n_failed_jobs ++;
846
97e7d748 847 job_uninstall(j);
5cb5a6ff
LP
848 job_free(j);
849
850 /* Fail depending jobs on failure */
5273510e 851 if (result != JOB_DONE && recursive) {
be7d9ff7
LP
852 if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
853 job_fail_dependencies(u, UNIT_REQUIRED_BY);
854 job_fail_dependencies(u, UNIT_REQUISITE_OF);
855 job_fail_dependencies(u, UNIT_BOUND_BY);
856 job_fail_dependencies(u, UNIT_REQUIRED_BY_OVERRIDABLE);
857 job_fail_dependencies(u, UNIT_REQUISITE_OF_OVERRIDABLE);
858 } else if (t == JOB_STOP)
859 job_fail_dependencies(u, UNIT_CONFLICTED_BY);
5cb5a6ff
LP
860 }
861
c0daa706 862 /* Trigger OnFailure dependencies that are not generated by
66870f90 863 * the unit itself. We don't treat JOB_CANCELED as failure in
c0daa706
LP
864 * this context. And JOB_FAILURE is already handled by the
865 * unit itself. */
222ae6a8 866 if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
f2341e0a
LP
867 log_struct(LOG_NOTICE,
868 "JOB_TYPE=%s", job_type_to_string(t),
869 "JOB_RESULT=%s", job_result_to_string(result),
870 LOG_UNIT_ID(u),
871 LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.",
e2cc6eca
LP
872 u->id,
873 job_type_to_string(t),
874 job_result_to_string(result)),
f2341e0a 875 NULL);
222ae6a8 876
3ecaa09b 877 unit_start_on_failure(u);
222ae6a8 878 }
c0daa706 879
3ecaa09b
LP
880 unit_trigger_notify(u);
881
57981b98 882finish:
5cb5a6ff 883 /* Try to start the next jobs that can be started */
ac155bb8
MS
884 SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
885 if (other->job)
886 job_add_to_run_queue(other->job);
887 SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i)
888 if (other->job)
889 job_add_to_run_queue(other->job);
5cb5a6ff 890
ac155bb8 891 manager_check_finished(u->manager);
b0c918b9 892
5273510e 893 return 0;
5cb5a6ff 894}
034c6ed7 895
718db961
LP
896static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *userdata) {
897 Job *j = userdata;
f189ab18 898 Unit *u;
faf919f1 899
718db961
LP
900 assert(j);
901 assert(s == j->timer_event_source);
faf919f1 902
f2341e0a 903 log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
faf919f1 904
f189ab18 905 u = j->unit;
718db961 906 job_finish_and_invalidate(j, JOB_TIMEOUT, true);
f189ab18
LP
907
908 failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg);
909
718db961
LP
910 return 0;
911}
faf919f1 912
718db961
LP
913int job_start_timer(Job *j) {
914 int r;
faf919f1 915
8bb310c3 916 if (j->timer_event_source)
718db961 917 return 0;
faf919f1 918
718db961 919 j->begin_usec = now(CLOCK_MONOTONIC);
faf919f1 920
8bb310c3
ZJS
921 if (j->unit->job_timeout <= 0)
922 return 0;
923
6a0f1f6d
LP
924 r = sd_event_add_time(
925 j->manager->event,
926 &j->timer_event_source,
927 CLOCK_MONOTONIC,
928 j->begin_usec + j->unit->job_timeout, 0,
929 job_dispatch_timer, j);
718db961
LP
930 if (r < 0)
931 return r;
faf919f1 932
7dfbe2e3
TG
933 (void) sd_event_source_set_description(j->timer_event_source, "job-start");
934
718db961 935 return 0;
faf919f1
LP
936}
937
c1e1601e 938void job_add_to_run_queue(Job *j) {
034c6ed7 939 assert(j);
ac1135be 940 assert(j->installed);
034c6ed7
LP
941
942 if (j->in_run_queue)
943 return;
944
752b5905
LP
945 if (!j->manager->run_queue)
946 sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT);
947
71fda00f 948 LIST_PREPEND(run_queue, j->manager->run_queue, j);
034c6ed7
LP
949 j->in_run_queue = true;
950}
94f04347 951
c1e1601e
LP
952void job_add_to_dbus_queue(Job *j) {
953 assert(j);
954 assert(j->installed);
955
956 if (j->in_dbus_queue)
957 return;
958
a567261a
LP
959 /* We don't check if anybody is subscribed here, since this
960 * job might just have been created and not yet assigned to a
961 * connection/client. */
94b6dfa2 962
71fda00f 963 LIST_PREPEND(dbus_queue, j->manager->dbus_job_queue, j);
c1e1601e
LP
964 j->in_dbus_queue = true;
965}
966
ea430986
LP
967char *job_dbus_path(Job *j) {
968 char *p;
969
970 assert(j);
971
ccd06097 972 if (asprintf(&p, "/org/freedesktop/systemd1/job/%"PRIu32, j->id) < 0)
ea430986
LP
973 return NULL;
974
975 return p;
976}
977
39a18c60
MS
978int job_serialize(Job *j, FILE *f, FDSet *fds) {
979 fprintf(f, "job-id=%u\n", j->id);
980 fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
981 fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
982 fprintf(f, "job-override=%s\n", yes_no(j->override));
23ade460 983 fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
39a18c60
MS
984 fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
985 fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
718db961
LP
986
987 if (j->begin_usec > 0)
ccd06097 988 fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
718db961 989
b39a2770 990 bus_track_serialize(j->clients, f);
39a18c60
MS
991
992 /* End marker */
993 fputc('\n', f);
994 return 0;
995}
996
997int job_deserialize(Job *j, FILE *f, FDSet *fds) {
718db961
LP
998 assert(j);
999
39a18c60
MS
1000 for (;;) {
1001 char line[LINE_MAX], *l, *v;
1002 size_t k;
1003
1004 if (!fgets(line, sizeof(line), f)) {
1005 if (feof(f))
1006 return 0;
1007 return -errno;
1008 }
1009
1010 char_array_0(line);
1011 l = strstrip(line);
1012
1013 /* End marker */
1014 if (l[0] == 0)
1015 return 0;
1016
1017 k = strcspn(l, "=");
1018
1019 if (l[k] == '=') {
1020 l[k] = 0;
1021 v = l+k+1;
1022 } else
1023 v = l+k;
1024
1025 if (streq(l, "job-id")) {
718db961 1026
39a18c60
MS
1027 if (safe_atou32(v, &j->id) < 0)
1028 log_debug("Failed to parse job id value %s", v);
718db961 1029
39a18c60 1030 } else if (streq(l, "job-type")) {
718db961
LP
1031 JobType t;
1032
1033 t = job_type_from_string(v);
39a18c60
MS
1034 if (t < 0)
1035 log_debug("Failed to parse job type %s", v);
e0209d83
MS
1036 else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION)
1037 log_debug("Cannot deserialize job of type %s", v);
39a18c60
MS
1038 else
1039 j->type = t;
718db961 1040
39a18c60 1041 } else if (streq(l, "job-state")) {
718db961
LP
1042 JobState s;
1043
1044 s = job_state_from_string(v);
39a18c60
MS
1045 if (s < 0)
1046 log_debug("Failed to parse job state %s", v);
1047 else
9c3349e2 1048 job_set_state(j, s);
718db961 1049
39a18c60 1050 } else if (streq(l, "job-override")) {
718db961
LP
1051 int b;
1052
1053 b = parse_boolean(v);
39a18c60
MS
1054 if (b < 0)
1055 log_debug("Failed to parse job override flag %s", v);
1056 else
1057 j->override = j->override || b;
718db961 1058
23ade460 1059 } else if (streq(l, "job-irreversible")) {
718db961
LP
1060 int b;
1061
1062 b = parse_boolean(v);
23ade460
MS
1063 if (b < 0)
1064 log_debug("Failed to parse job irreversible flag %s", v);
1065 else
1066 j->irreversible = j->irreversible || b;
718db961 1067
39a18c60 1068 } else if (streq(l, "job-sent-dbus-new-signal")) {
718db961
LP
1069 int b;
1070
1071 b = parse_boolean(v);
39a18c60
MS
1072 if (b < 0)
1073 log_debug("Failed to parse job sent_dbus_new_signal flag %s", v);
1074 else
1075 j->sent_dbus_new_signal = j->sent_dbus_new_signal || b;
718db961 1076
39a18c60 1077 } else if (streq(l, "job-ignore-order")) {
718db961
LP
1078 int b;
1079
1080 b = parse_boolean(v);
39a18c60
MS
1081 if (b < 0)
1082 log_debug("Failed to parse job ignore_order flag %s", v);
1083 else
1084 j->ignore_order = j->ignore_order || b;
718db961
LP
1085
1086 } else if (streq(l, "job-begin")) {
1087 unsigned long long ull;
1088
1089 if (sscanf(v, "%llu", &ull) != 1)
1090 log_debug("Failed to parse job-begin value %s", v);
39a18c60 1091 else
718db961
LP
1092 j->begin_usec = ull;
1093
8f8f05a9 1094 } else if (streq(l, "subscribed")) {
718db961 1095
b39a2770 1096 if (strv_extend(&j->deserialized_clients, v) < 0)
8f8f05a9 1097 return log_oom();
39a18c60
MS
1098 }
1099 }
1100}
1101
1102int job_coldplug(Job *j) {
718db961
LP
1103 int r;
1104
1105 assert(j);
39a18c60 1106
8f8f05a9
LP
1107 /* After deserialization is complete and the bus connection
1108 * set up again, let's start watching our subscribers again */
b39a2770 1109 r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients);
8f8f05a9
LP
1110 if (r < 0)
1111 return r;
1112
1727a595
MM
1113 if (j->state == JOB_WAITING)
1114 job_add_to_run_queue(j);
1115
8bb310c3 1116 if (j->begin_usec == 0 || j->unit->job_timeout == 0)
39a18c60
MS
1117 return 0;
1118
718db961
LP
1119 if (j->timer_event_source)
1120 j->timer_event_source = sd_event_source_unref(j->timer_event_source);
39a18c60 1121
6a0f1f6d
LP
1122 r = sd_event_add_time(
1123 j->manager->event,
1124 &j->timer_event_source,
1125 CLOCK_MONOTONIC,
1126 j->begin_usec + j->unit->job_timeout, 0,
1127 job_dispatch_timer, j);
718db961 1128 if (r < 0)
da927ba9 1129 log_debug_errno(r, "Failed to restart timeout for job: %m");
718db961 1130
7dfbe2e3
TG
1131 (void) sd_event_source_set_description(j->timer_event_source, "job-timeout");
1132
718db961 1133 return r;
39a18c60
MS
1134}
1135
c65eb836
LP
1136void job_shutdown_magic(Job *j) {
1137 assert(j);
1138
1139 /* The shutdown target gets some special treatment here: we
1140 * tell the kernel to begin with flushing its disk caches, to
1141 * optimize shutdown time a bit. Ideally we wouldn't hardcode
1142 * this magic into PID 1. However all other processes aren't
1143 * options either since they'd exit much sooner than PID 1 and
1144 * asynchronous sync() would cause their exit to be
1145 * delayed. */
1146
c2756a68 1147 if (j->type != JOB_START)
c65eb836
LP
1148 return;
1149
b2c23da8 1150 if (j->unit->manager->running_as != MANAGER_SYSTEM)
c2756a68
LP
1151 return;
1152
1153 if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
c65eb836
LP
1154 return;
1155
5b1869ea
OB
1156 /* In case messages on console has been disabled on boot */
1157 j->unit->manager->no_console_output = false;
1158
c65eb836
LP
1159 if (detect_container(NULL) > 0)
1160 return;
1161
1162 asynchronous_sync();
1163}
1164
68db7a3b
ZJS
1165int job_get_timeout(Job *j, uint64_t *timeout) {
1166 Unit *u = j->unit;
1167 uint64_t x = -1, y = -1;
1168 int r = 0, q = 0;
1169
1170 assert(u);
1171
1172 if (j->timer_event_source) {
1173 r = sd_event_source_get_time(j->timer_event_source, &x);
1174 if (r < 0)
1175 return r;
1176 r = 1;
1177 }
1178
1179 if (UNIT_VTABLE(u)->get_timeout) {
1180 q = UNIT_VTABLE(u)->get_timeout(u, &y);
1181 if (q < 0)
1182 return q;
1183 }
1184
1185 if (r == 0 && q == 0)
1186 return 0;
1187
1188 *timeout = MIN(x, y);
1189
68db7a3b
ZJS
1190 return 1;
1191}
1192
94f04347
LP
1193static const char* const job_state_table[_JOB_STATE_MAX] = {
1194 [JOB_WAITING] = "waiting",
1195 [JOB_RUNNING] = "running"
1196};
1197
1198DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
1199
1200static const char* const job_type_table[_JOB_TYPE_MAX] = {
1201 [JOB_START] = "start",
1202 [JOB_VERIFY_ACTIVE] = "verify-active",
1203 [JOB_STOP] = "stop",
1204 [JOB_RELOAD] = "reload",
1205 [JOB_RELOAD_OR_START] = "reload-or-start",
1206 [JOB_RESTART] = "restart",
1207 [JOB_TRY_RESTART] = "try-restart",
e0209d83 1208 [JOB_NOP] = "nop",
94f04347
LP
1209};
1210
1211DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
b548631a
LP
1212
1213static const char* const job_mode_table[_JOB_MODE_MAX] = {
1214 [JOB_FAIL] = "fail",
c497c7a9 1215 [JOB_REPLACE] = "replace",
23ade460 1216 [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
e67c3609 1217 [JOB_ISOLATE] = "isolate",
2c5859af 1218 [JOB_FLUSH] = "flush",
cebe0d41 1219 [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
255baef6 1220 [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
b548631a
LP
1221};
1222
1223DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
5d44db4a
LP
1224
1225static const char* const job_result_table[_JOB_RESULT_MAX] = {
1226 [JOB_DONE] = "done",
1227 [JOB_CANCELED] = "canceled",
1228 [JOB_TIMEOUT] = "timeout",
1229 [JOB_FAILED] = "failed",
d68201e9 1230 [JOB_DEPENDENCY] = "dependency",
6a371e23
ZJS
1231 [JOB_SKIPPED] = "skipped",
1232 [JOB_INVALID] = "invalid",
59fccdc5 1233 [JOB_ASSERT] = "assert",
0faacd47 1234 [JOB_UNSUPPORTED] = "unsupported",
5d44db4a
LP
1235};
1236
1237DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);