]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/job.c
core: log completion of remaining job types
[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];
708
709 assert(u);
710 assert(t >= 0);
711 assert(t < _JOB_TYPE_MAX);
712
81270860
LP
713 /* Skip this if it goes to the console. since we already print
714 * to the console anyway... */
715
716 if (log_on_console())
717 return;
718
aa49ab5f 719 format = job_get_status_message_format(u, t, result);
877d54e9
LP
720 if (!format)
721 return;
722
bcfce235 723 DISABLE_WARNING_FORMAT_NONLITERAL;
877d54e9 724 snprintf(buf, sizeof(buf), format, unit_description(u));
bcfce235 725 REENABLE_WARNING;
877d54e9
LP
726
727 if (t == JOB_START) {
728 sd_id128_t mid;
729
730 mid = result == JOB_DONE ? SD_MESSAGE_UNIT_STARTED : SD_MESSAGE_UNIT_FAILED;
f2341e0a
LP
731 log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
732 LOG_MESSAGE_ID(mid),
733 LOG_UNIT_ID(u),
734 LOG_MESSAGE("%s", buf),
735 "RESULT=%s", job_result_to_string(result),
736 NULL);
877d54e9 737
4f29c6fe 738 } else if (t == JOB_STOP || t == JOB_RESTART)
f2341e0a
LP
739 log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
740 LOG_MESSAGE_ID(SD_MESSAGE_UNIT_STOPPED),
741 LOG_UNIT_ID(u),
742 LOG_MESSAGE("%s", buf),
743 "RESULT=%s", job_result_to_string(result),
744 NULL);
877d54e9
LP
745
746 else if (t == JOB_RELOAD)
f2341e0a
LP
747 log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
748 LOG_MESSAGE_ID(SD_MESSAGE_UNIT_RELOADED),
749 LOG_UNIT_ID(u),
750 LOG_MESSAGE("%s", buf),
751 "RESULT=%s", job_result_to_string(result),
752 NULL);
4f29c6fe
MS
753 else
754 log_struct(result == JOB_DONE ? LOG_INFO : LOG_ERR,
755 LOG_UNIT_ID(u),
756 LOG_MESSAGE("%s", buf),
757 "RESULT=%s", job_result_to_string(result),
758 NULL);
877d54e9 759}
877d54e9 760
30961fa3
MS
761static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
762
763 /* No message if the job did not actually do anything due to failed condition. */
764 if (t == JOB_START && result == JOB_DONE && !u->condition_result)
765 return;
766
767 job_log_status_message(u, t, result);
768
769 /* Reload status messages have traditionally not been printed to console. */
770 if (t != JOB_RELOAD)
771 job_print_status_message(u, t, result);
772}
773
be7d9ff7
LP
774static void job_fail_dependencies(Unit *u, UnitDependency d) {
775 Unit *other;
776 Iterator i;
777
778 assert(u);
779
780 SET_FOREACH(other, u->dependencies[d], i) {
781 Job *j = other->job;
782
783 if (!j)
784 continue;
785 if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE))
786 continue;
787
788 job_finish_and_invalidate(j, JOB_DEPENDENCY, true);
789 }
790}
791
5273510e 792int job_finish_and_invalidate(Job *j, JobResult result, bool recursive) {
87f0e418
LP
793 Unit *u;
794 Unit *other;
b866264a 795 JobType t;
034c6ed7 796 Iterator i;
5cb5a6ff
LP
797
798 assert(j);
ac1135be 799 assert(j->installed);
e0209d83 800 assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
5cb5a6ff 801
c6918296
MS
802 u = j->unit;
803 t = j->type;
804
805 j->result = result;
806
f2341e0a 807 log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
c6918296 808
30961fa3 809 job_emit_status_message(u, t, result);
c6918296 810
c1e1601e 811 job_add_to_dbus_queue(j);
f50e0a01 812
034c6ed7 813 /* Patch restart jobs so that they become normal start jobs */
c6918296 814 if (result == JOB_DONE && t == JOB_RESTART) {
f50e0a01 815
bbd1a837 816 job_change_type(j, JOB_START);
9c3349e2 817 job_set_state(j, JOB_WAITING);
cc42e081
LP
818
819 job_add_to_run_queue(j);
57981b98 820
57981b98 821 goto finish;
5cb5a6ff
LP
822 }
823
6a371e23 824 if (result == JOB_FAILED || result == JOB_INVALID)
76bf48b7
LP
825 j->manager->n_failed_jobs ++;
826
97e7d748 827 job_uninstall(j);
5cb5a6ff
LP
828 job_free(j);
829
830 /* Fail depending jobs on failure */
5273510e 831 if (result != JOB_DONE && recursive) {
be7d9ff7
LP
832 if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
833 job_fail_dependencies(u, UNIT_REQUIRED_BY);
834 job_fail_dependencies(u, UNIT_REQUISITE_OF);
835 job_fail_dependencies(u, UNIT_BOUND_BY);
836 job_fail_dependencies(u, UNIT_REQUIRED_BY_OVERRIDABLE);
837 job_fail_dependencies(u, UNIT_REQUISITE_OF_OVERRIDABLE);
838 } else if (t == JOB_STOP)
839 job_fail_dependencies(u, UNIT_CONFLICTED_BY);
5cb5a6ff
LP
840 }
841
c0daa706 842 /* Trigger OnFailure dependencies that are not generated by
66870f90 843 * the unit itself. We don't treat JOB_CANCELED as failure in
c0daa706
LP
844 * this context. And JOB_FAILURE is already handled by the
845 * unit itself. */
222ae6a8 846 if (result == JOB_TIMEOUT || result == JOB_DEPENDENCY) {
f2341e0a
LP
847 log_struct(LOG_NOTICE,
848 "JOB_TYPE=%s", job_type_to_string(t),
849 "JOB_RESULT=%s", job_result_to_string(result),
850 LOG_UNIT_ID(u),
851 LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.",
e2cc6eca
LP
852 u->id,
853 job_type_to_string(t),
854 job_result_to_string(result)),
f2341e0a 855 NULL);
222ae6a8 856
3ecaa09b 857 unit_start_on_failure(u);
222ae6a8 858 }
c0daa706 859
3ecaa09b
LP
860 unit_trigger_notify(u);
861
57981b98 862finish:
5cb5a6ff 863 /* Try to start the next jobs that can be started */
ac155bb8
MS
864 SET_FOREACH(other, u->dependencies[UNIT_AFTER], i)
865 if (other->job)
866 job_add_to_run_queue(other->job);
867 SET_FOREACH(other, u->dependencies[UNIT_BEFORE], i)
868 if (other->job)
869 job_add_to_run_queue(other->job);
5cb5a6ff 870
ac155bb8 871 manager_check_finished(u->manager);
b0c918b9 872
5273510e 873 return 0;
5cb5a6ff 874}
034c6ed7 875
718db961
LP
876static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *userdata) {
877 Job *j = userdata;
f189ab18 878 Unit *u;
faf919f1 879
718db961
LP
880 assert(j);
881 assert(s == j->timer_event_source);
faf919f1 882
f2341e0a 883 log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
faf919f1 884
f189ab18 885 u = j->unit;
718db961 886 job_finish_and_invalidate(j, JOB_TIMEOUT, true);
f189ab18
LP
887
888 failure_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg);
889
718db961
LP
890 return 0;
891}
faf919f1 892
718db961
LP
893int job_start_timer(Job *j) {
894 int r;
faf919f1 895
8bb310c3 896 if (j->timer_event_source)
718db961 897 return 0;
faf919f1 898
718db961 899 j->begin_usec = now(CLOCK_MONOTONIC);
faf919f1 900
8bb310c3
ZJS
901 if (j->unit->job_timeout <= 0)
902 return 0;
903
6a0f1f6d
LP
904 r = sd_event_add_time(
905 j->manager->event,
906 &j->timer_event_source,
907 CLOCK_MONOTONIC,
908 j->begin_usec + j->unit->job_timeout, 0,
909 job_dispatch_timer, j);
718db961
LP
910 if (r < 0)
911 return r;
faf919f1 912
7dfbe2e3
TG
913 (void) sd_event_source_set_description(j->timer_event_source, "job-start");
914
718db961 915 return 0;
faf919f1
LP
916}
917
c1e1601e 918void job_add_to_run_queue(Job *j) {
034c6ed7 919 assert(j);
ac1135be 920 assert(j->installed);
034c6ed7
LP
921
922 if (j->in_run_queue)
923 return;
924
752b5905
LP
925 if (!j->manager->run_queue)
926 sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT);
927
71fda00f 928 LIST_PREPEND(run_queue, j->manager->run_queue, j);
034c6ed7
LP
929 j->in_run_queue = true;
930}
94f04347 931
c1e1601e
LP
932void job_add_to_dbus_queue(Job *j) {
933 assert(j);
934 assert(j->installed);
935
936 if (j->in_dbus_queue)
937 return;
938
a567261a
LP
939 /* We don't check if anybody is subscribed here, since this
940 * job might just have been created and not yet assigned to a
941 * connection/client. */
94b6dfa2 942
71fda00f 943 LIST_PREPEND(dbus_queue, j->manager->dbus_job_queue, j);
c1e1601e
LP
944 j->in_dbus_queue = true;
945}
946
ea430986
LP
947char *job_dbus_path(Job *j) {
948 char *p;
949
950 assert(j);
951
ccd06097 952 if (asprintf(&p, "/org/freedesktop/systemd1/job/%"PRIu32, j->id) < 0)
ea430986
LP
953 return NULL;
954
955 return p;
956}
957
39a18c60
MS
958int job_serialize(Job *j, FILE *f, FDSet *fds) {
959 fprintf(f, "job-id=%u\n", j->id);
960 fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
961 fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
962 fprintf(f, "job-override=%s\n", yes_no(j->override));
23ade460 963 fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
39a18c60
MS
964 fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
965 fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
718db961
LP
966
967 if (j->begin_usec > 0)
ccd06097 968 fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
718db961 969
b39a2770 970 bus_track_serialize(j->clients, f);
39a18c60
MS
971
972 /* End marker */
973 fputc('\n', f);
974 return 0;
975}
976
977int job_deserialize(Job *j, FILE *f, FDSet *fds) {
718db961
LP
978 assert(j);
979
39a18c60
MS
980 for (;;) {
981 char line[LINE_MAX], *l, *v;
982 size_t k;
983
984 if (!fgets(line, sizeof(line), f)) {
985 if (feof(f))
986 return 0;
987 return -errno;
988 }
989
990 char_array_0(line);
991 l = strstrip(line);
992
993 /* End marker */
994 if (l[0] == 0)
995 return 0;
996
997 k = strcspn(l, "=");
998
999 if (l[k] == '=') {
1000 l[k] = 0;
1001 v = l+k+1;
1002 } else
1003 v = l+k;
1004
1005 if (streq(l, "job-id")) {
718db961 1006
39a18c60
MS
1007 if (safe_atou32(v, &j->id) < 0)
1008 log_debug("Failed to parse job id value %s", v);
718db961 1009
39a18c60 1010 } else if (streq(l, "job-type")) {
718db961
LP
1011 JobType t;
1012
1013 t = job_type_from_string(v);
39a18c60
MS
1014 if (t < 0)
1015 log_debug("Failed to parse job type %s", v);
e0209d83
MS
1016 else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION)
1017 log_debug("Cannot deserialize job of type %s", v);
39a18c60
MS
1018 else
1019 j->type = t;
718db961 1020
39a18c60 1021 } else if (streq(l, "job-state")) {
718db961
LP
1022 JobState s;
1023
1024 s = job_state_from_string(v);
39a18c60
MS
1025 if (s < 0)
1026 log_debug("Failed to parse job state %s", v);
1027 else
9c3349e2 1028 job_set_state(j, s);
718db961 1029
39a18c60 1030 } else if (streq(l, "job-override")) {
718db961
LP
1031 int b;
1032
1033 b = parse_boolean(v);
39a18c60
MS
1034 if (b < 0)
1035 log_debug("Failed to parse job override flag %s", v);
1036 else
1037 j->override = j->override || b;
718db961 1038
23ade460 1039 } else if (streq(l, "job-irreversible")) {
718db961
LP
1040 int b;
1041
1042 b = parse_boolean(v);
23ade460
MS
1043 if (b < 0)
1044 log_debug("Failed to parse job irreversible flag %s", v);
1045 else
1046 j->irreversible = j->irreversible || b;
718db961 1047
39a18c60 1048 } else if (streq(l, "job-sent-dbus-new-signal")) {
718db961
LP
1049 int b;
1050
1051 b = parse_boolean(v);
39a18c60
MS
1052 if (b < 0)
1053 log_debug("Failed to parse job sent_dbus_new_signal flag %s", v);
1054 else
1055 j->sent_dbus_new_signal = j->sent_dbus_new_signal || b;
718db961 1056
39a18c60 1057 } else if (streq(l, "job-ignore-order")) {
718db961
LP
1058 int b;
1059
1060 b = parse_boolean(v);
39a18c60
MS
1061 if (b < 0)
1062 log_debug("Failed to parse job ignore_order flag %s", v);
1063 else
1064 j->ignore_order = j->ignore_order || b;
718db961
LP
1065
1066 } else if (streq(l, "job-begin")) {
1067 unsigned long long ull;
1068
1069 if (sscanf(v, "%llu", &ull) != 1)
1070 log_debug("Failed to parse job-begin value %s", v);
39a18c60 1071 else
718db961
LP
1072 j->begin_usec = ull;
1073
8f8f05a9 1074 } else if (streq(l, "subscribed")) {
718db961 1075
b39a2770 1076 if (strv_extend(&j->deserialized_clients, v) < 0)
8f8f05a9 1077 return log_oom();
39a18c60
MS
1078 }
1079 }
1080}
1081
1082int job_coldplug(Job *j) {
718db961
LP
1083 int r;
1084
1085 assert(j);
39a18c60 1086
8f8f05a9
LP
1087 /* After deserialization is complete and the bus connection
1088 * set up again, let's start watching our subscribers again */
b39a2770 1089 r = bus_track_coldplug(j->manager, &j->clients, &j->deserialized_clients);
8f8f05a9
LP
1090 if (r < 0)
1091 return r;
1092
1727a595
MM
1093 if (j->state == JOB_WAITING)
1094 job_add_to_run_queue(j);
1095
8bb310c3 1096 if (j->begin_usec == 0 || j->unit->job_timeout == 0)
39a18c60
MS
1097 return 0;
1098
718db961
LP
1099 if (j->timer_event_source)
1100 j->timer_event_source = sd_event_source_unref(j->timer_event_source);
39a18c60 1101
6a0f1f6d
LP
1102 r = sd_event_add_time(
1103 j->manager->event,
1104 &j->timer_event_source,
1105 CLOCK_MONOTONIC,
1106 j->begin_usec + j->unit->job_timeout, 0,
1107 job_dispatch_timer, j);
718db961 1108 if (r < 0)
da927ba9 1109 log_debug_errno(r, "Failed to restart timeout for job: %m");
718db961 1110
7dfbe2e3
TG
1111 (void) sd_event_source_set_description(j->timer_event_source, "job-timeout");
1112
718db961 1113 return r;
39a18c60
MS
1114}
1115
c65eb836
LP
1116void job_shutdown_magic(Job *j) {
1117 assert(j);
1118
1119 /* The shutdown target gets some special treatment here: we
1120 * tell the kernel to begin with flushing its disk caches, to
1121 * optimize shutdown time a bit. Ideally we wouldn't hardcode
1122 * this magic into PID 1. However all other processes aren't
1123 * options either since they'd exit much sooner than PID 1 and
1124 * asynchronous sync() would cause their exit to be
1125 * delayed. */
1126
c2756a68 1127 if (j->type != JOB_START)
c65eb836
LP
1128 return;
1129
b2c23da8 1130 if (j->unit->manager->running_as != MANAGER_SYSTEM)
c2756a68
LP
1131 return;
1132
1133 if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
c65eb836
LP
1134 return;
1135
5b1869ea
OB
1136 /* In case messages on console has been disabled on boot */
1137 j->unit->manager->no_console_output = false;
1138
c65eb836
LP
1139 if (detect_container(NULL) > 0)
1140 return;
1141
1142 asynchronous_sync();
1143}
1144
68db7a3b
ZJS
1145int job_get_timeout(Job *j, uint64_t *timeout) {
1146 Unit *u = j->unit;
1147 uint64_t x = -1, y = -1;
1148 int r = 0, q = 0;
1149
1150 assert(u);
1151
1152 if (j->timer_event_source) {
1153 r = sd_event_source_get_time(j->timer_event_source, &x);
1154 if (r < 0)
1155 return r;
1156 r = 1;
1157 }
1158
1159 if (UNIT_VTABLE(u)->get_timeout) {
1160 q = UNIT_VTABLE(u)->get_timeout(u, &y);
1161 if (q < 0)
1162 return q;
1163 }
1164
1165 if (r == 0 && q == 0)
1166 return 0;
1167
1168 *timeout = MIN(x, y);
1169
68db7a3b
ZJS
1170 return 1;
1171}
1172
94f04347
LP
1173static const char* const job_state_table[_JOB_STATE_MAX] = {
1174 [JOB_WAITING] = "waiting",
1175 [JOB_RUNNING] = "running"
1176};
1177
1178DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
1179
1180static const char* const job_type_table[_JOB_TYPE_MAX] = {
1181 [JOB_START] = "start",
1182 [JOB_VERIFY_ACTIVE] = "verify-active",
1183 [JOB_STOP] = "stop",
1184 [JOB_RELOAD] = "reload",
1185 [JOB_RELOAD_OR_START] = "reload-or-start",
1186 [JOB_RESTART] = "restart",
1187 [JOB_TRY_RESTART] = "try-restart",
e0209d83 1188 [JOB_NOP] = "nop",
94f04347
LP
1189};
1190
1191DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
b548631a
LP
1192
1193static const char* const job_mode_table[_JOB_MODE_MAX] = {
1194 [JOB_FAIL] = "fail",
c497c7a9 1195 [JOB_REPLACE] = "replace",
23ade460 1196 [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
e67c3609 1197 [JOB_ISOLATE] = "isolate",
2c5859af 1198 [JOB_FLUSH] = "flush",
cebe0d41 1199 [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
255baef6 1200 [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
b548631a
LP
1201};
1202
1203DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
5d44db4a
LP
1204
1205static const char* const job_result_table[_JOB_RESULT_MAX] = {
1206 [JOB_DONE] = "done",
1207 [JOB_CANCELED] = "canceled",
1208 [JOB_TIMEOUT] = "timeout",
1209 [JOB_FAILED] = "failed",
d68201e9 1210 [JOB_DEPENDENCY] = "dependency",
6a371e23
ZJS
1211 [JOB_SKIPPED] = "skipped",
1212 [JOB_INVALID] = "invalid",
59fccdc5 1213 [JOB_ASSERT] = "assert",
0faacd47 1214 [JOB_UNSUPPORTED] = "unsupported",
5d44db4a
LP
1215};
1216
1217DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);