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