]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/job.c
tree-wide: adjust fall through comments so that gcc is happy
[thirdparty/systemd.git] / src / core / job.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22
23 #include "sd-id128.h"
24 #include "sd-messages.h"
25
26 #include "alloc-util.h"
27 #include "async.h"
28 #include "dbus-job.h"
29 #include "dbus.h"
30 #include "escape.h"
31 #include "job.h"
32 #include "log.h"
33 #include "macro.h"
34 #include "parse-util.h"
35 #include "set.h"
36 #include "special.h"
37 #include "stdio-util.h"
38 #include "string-table.h"
39 #include "string-util.h"
40 #include "strv.h"
41 #include "terminal-util.h"
42 #include "unit.h"
43 #include "virt.h"
44
45 Job* job_new_raw(Unit *unit) {
46 Job *j;
47
48 /* used for deserialization */
49
50 assert(unit);
51
52 j = new0(Job, 1);
53 if (!j)
54 return NULL;
55
56 j->manager = unit->manager;
57 j->unit = unit;
58 j->type = _JOB_TYPE_INVALID;
59
60 return j;
61 }
62
63 Job* job_new(Unit *unit, JobType type) {
64 Job *j;
65
66 assert(type < _JOB_TYPE_MAX);
67
68 j = job_new_raw(unit);
69 if (!j)
70 return NULL;
71
72 j->id = j->manager->current_job_id++;
73 j->type = type;
74
75 /* We don't link it here, that's what job_dependency() is for */
76
77 return j;
78 }
79
80 void job_free(Job *j) {
81 assert(j);
82 assert(!j->installed);
83 assert(!j->transaction_prev);
84 assert(!j->transaction_next);
85 assert(!j->subject_list);
86 assert(!j->object_list);
87
88 if (j->in_run_queue)
89 LIST_REMOVE(run_queue, j->manager->run_queue, j);
90
91 if (j->in_dbus_queue)
92 LIST_REMOVE(dbus_queue, j->manager->dbus_job_queue, j);
93
94 if (j->in_gc_queue)
95 LIST_REMOVE(gc_queue, j->manager->gc_job_queue, j);
96
97 sd_event_source_unref(j->timer_event_source);
98
99 sd_bus_track_unref(j->bus_track);
100 strv_free(j->deserialized_clients);
101
102 free(j);
103 }
104
105 static void job_set_state(Job *j, JobState state) {
106 assert(j);
107 assert(state >= 0);
108 assert(state < _JOB_STATE_MAX);
109
110 if (j->state == state)
111 return;
112
113 j->state = state;
114
115 if (!j->installed)
116 return;
117
118 if (j->state == JOB_RUNNING)
119 j->unit->manager->n_running_jobs++;
120 else {
121 assert(j->state == JOB_WAITING);
122 assert(j->unit->manager->n_running_jobs > 0);
123
124 j->unit->manager->n_running_jobs--;
125
126 if (j->unit->manager->n_running_jobs <= 0)
127 j->unit->manager->jobs_in_progress_event_source = sd_event_source_unref(j->unit->manager->jobs_in_progress_event_source);
128 }
129 }
130
131 void job_uninstall(Job *j) {
132 Job **pj;
133
134 assert(j->installed);
135
136 job_set_state(j, JOB_WAITING);
137
138 pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
139 assert(*pj == j);
140
141 /* Detach from next 'bigger' objects */
142
143 /* daemon-reload should be transparent to job observers */
144 if (!MANAGER_IS_RELOADING(j->manager))
145 bus_job_send_removed_signal(j);
146
147 *pj = NULL;
148
149 unit_add_to_gc_queue(j->unit);
150
151 hashmap_remove(j->manager->jobs, UINT32_TO_PTR(j->id));
152 j->installed = false;
153 }
154
155 static bool job_type_allows_late_merge(JobType t) {
156 /* Tells whether it is OK to merge a job of type 't' with an already
157 * running job.
158 * Reloads cannot be merged this way. Think of the sequence:
159 * 1. Reload of a daemon is in progress; the daemon has already loaded
160 * its config file, but hasn't completed the reload operation yet.
161 * 2. Edit foo's config file.
162 * 3. Trigger another reload to have the daemon use the new config.
163 * Should the second reload job be merged into the first one, the daemon
164 * would not know about the new config.
165 * JOB_RESTART jobs on the other hand can be merged, because they get
166 * patched into JOB_START after stopping the unit. So if we see a
167 * JOB_RESTART running, it means the unit hasn't stopped yet and at
168 * this time the merge is still allowed. */
169 return t != JOB_RELOAD;
170 }
171
172 static void job_merge_into_installed(Job *j, Job *other) {
173 assert(j->installed);
174 assert(j->unit == other->unit);
175
176 if (j->type != JOB_NOP)
177 job_type_merge_and_collapse(&j->type, other->type, j->unit);
178 else
179 assert(other->type == JOB_NOP);
180
181 j->irreversible = j->irreversible || other->irreversible;
182 j->ignore_order = j->ignore_order || other->ignore_order;
183 }
184
185 Job* job_install(Job *j) {
186 Job **pj;
187 Job *uj;
188
189 assert(!j->installed);
190 assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
191 assert(j->state == JOB_WAITING);
192
193 pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
194 uj = *pj;
195
196 if (uj) {
197 if (job_type_is_conflicting(uj->type, j->type))
198 job_finish_and_invalidate(uj, JOB_CANCELED, false, false);
199 else {
200 /* not conflicting, i.e. mergeable */
201
202 if (uj->state == JOB_WAITING ||
203 (job_type_allows_late_merge(j->type) && job_type_is_superset(uj->type, j->type))) {
204 job_merge_into_installed(uj, j);
205 log_unit_debug(uj->unit,
206 "Merged into installed job %s/%s as %u",
207 uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
208 return uj;
209 } else {
210 /* already running and not safe to merge into */
211 /* Patch uj to become a merged job and re-run it. */
212 /* XXX It should be safer to queue j to run after uj finishes, but it is
213 * not currently possible to have more than one installed job per unit. */
214 job_merge_into_installed(uj, j);
215 log_unit_debug(uj->unit,
216 "Merged into running job, re-running: %s/%s as %u",
217 uj->unit->id, job_type_to_string(uj->type), (unsigned) uj->id);
218
219 job_set_state(uj, JOB_WAITING);
220 return uj;
221 }
222 }
223 }
224
225 /* Install the job */
226 *pj = j;
227 j->installed = true;
228
229 j->manager->n_installed_jobs++;
230 log_unit_debug(j->unit,
231 "Installed new job %s/%s as %u",
232 j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
233
234 job_add_to_gc_queue(j);
235
236 return j;
237 }
238
239 int job_install_deserialized(Job *j) {
240 Job **pj;
241
242 assert(!j->installed);
243
244 if (j->type < 0 || j->type >= _JOB_TYPE_MAX_IN_TRANSACTION) {
245 log_debug("Invalid job type %s in deserialization.", strna(job_type_to_string(j->type)));
246 return -EINVAL;
247 }
248
249 pj = (j->type == JOB_NOP) ? &j->unit->nop_job : &j->unit->job;
250 if (*pj) {
251 log_unit_debug(j->unit, "Unit already has a job installed. Not installing deserialized job.");
252 return -EEXIST;
253 }
254
255 *pj = j;
256 j->installed = true;
257
258 if (j->state == JOB_RUNNING)
259 j->unit->manager->n_running_jobs++;
260
261 log_unit_debug(j->unit,
262 "Reinstalled deserialized job %s/%s as %u",
263 j->unit->id, job_type_to_string(j->type), (unsigned) j->id);
264 return 0;
265 }
266
267 JobDependency* job_dependency_new(Job *subject, Job *object, bool matters, bool conflicts) {
268 JobDependency *l;
269
270 assert(object);
271
272 /* Adds a new job link, which encodes that the 'subject' job
273 * needs the 'object' job in some way. If 'subject' is NULL
274 * this means the 'anchor' job (i.e. the one the user
275 * explicitly asked for) is the requester. */
276
277 l = new0(JobDependency, 1);
278 if (!l)
279 return NULL;
280
281 l->subject = subject;
282 l->object = object;
283 l->matters = matters;
284 l->conflicts = conflicts;
285
286 if (subject)
287 LIST_PREPEND(subject, subject->subject_list, l);
288
289 LIST_PREPEND(object, object->object_list, l);
290
291 return l;
292 }
293
294 void job_dependency_free(JobDependency *l) {
295 assert(l);
296
297 if (l->subject)
298 LIST_REMOVE(subject, l->subject->subject_list, l);
299
300 LIST_REMOVE(object, l->object->object_list, l);
301
302 free(l);
303 }
304
305 void job_dump(Job *j, FILE*f, const char *prefix) {
306 assert(j);
307 assert(f);
308
309 if (!prefix)
310 prefix = "";
311
312 fprintf(f,
313 "%s-> Job %u:\n"
314 "%s\tAction: %s -> %s\n"
315 "%s\tState: %s\n"
316 "%s\tIrreversible: %s\n",
317 prefix, j->id,
318 prefix, j->unit->id, job_type_to_string(j->type),
319 prefix, job_state_to_string(j->state),
320 prefix, yes_no(j->irreversible));
321 }
322
323 /*
324 * Merging is commutative, so imagine the matrix as symmetric. We store only
325 * its lower triangle to avoid duplication. We don't store the main diagonal,
326 * because A merged with A is simply A.
327 *
328 * If the resulting type is collapsed immediately afterwards (to get rid of
329 * the JOB_RELOAD_OR_START, which lies outside the lookup function's domain),
330 * the following properties hold:
331 *
332 * Merging is associative! A merged with B, and then merged with C is the same
333 * as A merged with the result of B merged with C.
334 *
335 * Mergeability is transitive! If A can be merged with B and B with C then
336 * A also with C.
337 *
338 * Also, if A merged with B cannot be merged with C, then either A or B cannot
339 * be merged with C either.
340 */
341 static const JobType job_merging_table[] = {
342 /* What \ With * JOB_START JOB_VERIFY_ACTIVE JOB_STOP JOB_RELOAD */
343 /*********************************************************************************/
344 /*JOB_START */
345 /*JOB_VERIFY_ACTIVE */ JOB_START,
346 /*JOB_STOP */ -1, -1,
347 /*JOB_RELOAD */ JOB_RELOAD_OR_START, JOB_RELOAD, -1,
348 /*JOB_RESTART */ JOB_RESTART, JOB_RESTART, -1, JOB_RESTART,
349 };
350
351 JobType job_type_lookup_merge(JobType a, JobType b) {
352 assert_cc(ELEMENTSOF(job_merging_table) == _JOB_TYPE_MAX_MERGING * (_JOB_TYPE_MAX_MERGING - 1) / 2);
353 assert(a >= 0 && a < _JOB_TYPE_MAX_MERGING);
354 assert(b >= 0 && b < _JOB_TYPE_MAX_MERGING);
355
356 if (a == b)
357 return a;
358
359 if (a < b) {
360 JobType tmp = a;
361 a = b;
362 b = tmp;
363 }
364
365 return job_merging_table[(a - 1) * a / 2 + b];
366 }
367
368 bool job_type_is_redundant(JobType a, UnitActiveState b) {
369 switch (a) {
370
371 case JOB_START:
372 return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING);
373
374 case JOB_STOP:
375 return IN_SET(b, UNIT_INACTIVE, UNIT_FAILED);
376
377 case JOB_VERIFY_ACTIVE:
378 return IN_SET(b, UNIT_ACTIVE, UNIT_RELOADING);
379
380 case JOB_RELOAD:
381 return
382 b == UNIT_RELOADING;
383
384 case JOB_RESTART:
385 return
386 b == UNIT_ACTIVATING;
387
388 case JOB_NOP:
389 return true;
390
391 default:
392 assert_not_reached("Invalid job type");
393 }
394 }
395
396 JobType job_type_collapse(JobType t, Unit *u) {
397 UnitActiveState s;
398
399 switch (t) {
400
401 case JOB_TRY_RESTART:
402 s = unit_active_state(u);
403 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
404 return JOB_NOP;
405
406 return JOB_RESTART;
407
408 case JOB_TRY_RELOAD:
409 s = unit_active_state(u);
410 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
411 return JOB_NOP;
412
413 return JOB_RELOAD;
414
415 case JOB_RELOAD_OR_START:
416 s = unit_active_state(u);
417 if (UNIT_IS_INACTIVE_OR_DEACTIVATING(s))
418 return JOB_START;
419
420 return JOB_RELOAD;
421
422 default:
423 return t;
424 }
425 }
426
427 int job_type_merge_and_collapse(JobType *a, JobType b, Unit *u) {
428 JobType t;
429
430 t = job_type_lookup_merge(*a, b);
431 if (t < 0)
432 return -EEXIST;
433
434 *a = job_type_collapse(t, u);
435 return 0;
436 }
437
438 static bool job_is_runnable(Job *j) {
439 Iterator i;
440 Unit *other;
441 void *v;
442
443 assert(j);
444 assert(j->installed);
445
446 /* Checks whether there is any job running for the units this
447 * job needs to be running after (in the case of a 'positive'
448 * job type) or before (in the case of a 'negative' job
449 * type. */
450
451 /* Note that unit types have a say in what is runnable,
452 * too. For example, if they return -EAGAIN from
453 * unit_start() they can indicate they are not
454 * runnable yet. */
455
456 /* First check if there is an override */
457 if (j->ignore_order)
458 return true;
459
460 if (j->type == JOB_NOP)
461 return true;
462
463 if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
464 /* Immediate result is that the job is or might be
465 * started. In this case let's wait for the
466 * dependencies, regardless whether they are
467 * starting or stopping something. */
468
469 HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
470 if (other->job)
471 return false;
472 }
473
474 /* Also, if something else is being stopped and we should
475 * change state after it, then let's wait. */
476
477 HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i)
478 if (other->job &&
479 IN_SET(other->job->type, JOB_STOP, JOB_RESTART))
480 return false;
481
482 /* This means that for a service a and a service b where b
483 * shall be started after a:
484 *
485 * start a + start b → 1st step start a, 2nd step start b
486 * start a + stop b → 1st step stop b, 2nd step start a
487 * stop a + start b → 1st step stop a, 2nd step start b
488 * stop a + stop b → 1st step stop b, 2nd step stop a
489 *
490 * This has the side effect that restarts are properly
491 * synchronized too. */
492
493 return true;
494 }
495
496 static void job_change_type(Job *j, JobType newtype) {
497 assert(j);
498
499 log_unit_debug(j->unit,
500 "Converting job %s/%s -> %s/%s",
501 j->unit->id, job_type_to_string(j->type),
502 j->unit->id, job_type_to_string(newtype));
503
504 j->type = newtype;
505 }
506
507 static int job_perform_on_unit(Job **j) {
508 uint32_t id;
509 Manager *m;
510 JobType t;
511 Unit *u;
512 int r;
513
514 /* While we execute this operation the job might go away (for
515 * example: because it finishes immediately or is replaced by
516 * a new, conflicting job.) To make sure we don't access a
517 * freed job later on we store the id here, so that we can
518 * verify the job is still valid. */
519
520 assert(j);
521 assert(*j);
522
523 m = (*j)->manager;
524 u = (*j)->unit;
525 t = (*j)->type;
526 id = (*j)->id;
527
528 switch (t) {
529 case JOB_START:
530 r = unit_start(u);
531 break;
532
533 case JOB_RESTART:
534 t = JOB_STOP;
535 _fallthrough_;
536 case JOB_STOP:
537 r = unit_stop(u);
538 break;
539
540 case JOB_RELOAD:
541 r = unit_reload(u);
542 break;
543
544 default:
545 assert_not_reached("Invalid job type");
546 }
547
548 /* Log if the job still exists and the start/stop/reload function
549 * actually did something. */
550 *j = manager_get_job(m, id);
551 if (*j && r > 0)
552 unit_status_emit_starting_stopping_reloading(u, t);
553
554 return r;
555 }
556
557 int job_run_and_invalidate(Job *j) {
558 int r;
559
560 assert(j);
561 assert(j->installed);
562 assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
563 assert(j->in_run_queue);
564
565 LIST_REMOVE(run_queue, j->manager->run_queue, j);
566 j->in_run_queue = false;
567
568 if (j->state != JOB_WAITING)
569 return 0;
570
571 if (!job_is_runnable(j))
572 return -EAGAIN;
573
574 job_start_timer(j, true);
575 job_set_state(j, JOB_RUNNING);
576 job_add_to_dbus_queue(j);
577
578
579 switch (j->type) {
580
581 case JOB_VERIFY_ACTIVE: {
582 UnitActiveState t = unit_active_state(j->unit);
583 if (UNIT_IS_ACTIVE_OR_RELOADING(t))
584 r = -EALREADY;
585 else if (t == UNIT_ACTIVATING)
586 r = -EAGAIN;
587 else
588 r = -EBADR;
589 break;
590 }
591
592 case JOB_START:
593 case JOB_STOP:
594 case JOB_RESTART:
595 r = job_perform_on_unit(&j);
596
597 /* If the unit type does not support starting/stopping,
598 * then simply wait. */
599 if (r == -EBADR)
600 r = 0;
601 break;
602
603 case JOB_RELOAD:
604 r = job_perform_on_unit(&j);
605 break;
606
607 case JOB_NOP:
608 r = -EALREADY;
609 break;
610
611 default:
612 assert_not_reached("Unknown job type");
613 }
614
615 if (j) {
616 if (r == -EALREADY)
617 r = job_finish_and_invalidate(j, JOB_DONE, true, true);
618 else if (r == -EBADR)
619 r = job_finish_and_invalidate(j, JOB_SKIPPED, true, false);
620 else if (r == -ENOEXEC)
621 r = job_finish_and_invalidate(j, JOB_INVALID, true, false);
622 else if (r == -EPROTO)
623 r = job_finish_and_invalidate(j, JOB_ASSERT, true, false);
624 else if (r == -EOPNOTSUPP)
625 r = job_finish_and_invalidate(j, JOB_UNSUPPORTED, true, false);
626 else if (r == -ENOLINK)
627 r = job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false);
628 else if (r == -EAGAIN)
629 job_set_state(j, JOB_WAITING);
630 else if (r < 0)
631 r = job_finish_and_invalidate(j, JOB_FAILED, true, false);
632 }
633
634 return r;
635 }
636
637 _pure_ static const char *job_get_status_message_format(Unit *u, JobType t, JobResult result) {
638
639 static const char *const generic_finished_start_job[_JOB_RESULT_MAX] = {
640 [JOB_DONE] = "Started %s.",
641 [JOB_TIMEOUT] = "Timed out starting %s.",
642 [JOB_FAILED] = "Failed to start %s.",
643 [JOB_DEPENDENCY] = "Dependency failed for %s.",
644 [JOB_ASSERT] = "Assertion failed for %s.",
645 [JOB_UNSUPPORTED] = "Starting of %s not supported.",
646 [JOB_COLLECTED] = "Unnecessary job for %s was removed.",
647 };
648 static const char *const generic_finished_stop_job[_JOB_RESULT_MAX] = {
649 [JOB_DONE] = "Stopped %s.",
650 [JOB_FAILED] = "Stopped (with error) %s.",
651 [JOB_TIMEOUT] = "Timed out stopping %s.",
652 };
653 static const char *const generic_finished_reload_job[_JOB_RESULT_MAX] = {
654 [JOB_DONE] = "Reloaded %s.",
655 [JOB_FAILED] = "Reload failed for %s.",
656 [JOB_TIMEOUT] = "Timed out reloading %s.",
657 };
658 /* When verify-active detects the unit is inactive, report it.
659 * Most likely a DEPEND warning from a requisiting unit will
660 * occur next and it's nice to see what was requisited. */
661 static const char *const generic_finished_verify_active_job[_JOB_RESULT_MAX] = {
662 [JOB_SKIPPED] = "%s is not active.",
663 };
664
665 const UnitStatusMessageFormats *format_table;
666 const char *format;
667
668 assert(u);
669 assert(t >= 0);
670 assert(t < _JOB_TYPE_MAX);
671
672 if (IN_SET(t, JOB_START, JOB_STOP, JOB_RESTART)) {
673 format_table = &UNIT_VTABLE(u)->status_message_formats;
674 if (format_table) {
675 format = t == JOB_START ? format_table->finished_start_job[result] :
676 format_table->finished_stop_job[result];
677 if (format)
678 return format;
679 }
680 }
681
682 /* Return generic strings */
683 if (t == JOB_START)
684 return generic_finished_start_job[result];
685 else if (IN_SET(t, JOB_STOP, JOB_RESTART))
686 return generic_finished_stop_job[result];
687 else if (t == JOB_RELOAD)
688 return generic_finished_reload_job[result];
689 else if (t == JOB_VERIFY_ACTIVE)
690 return generic_finished_verify_active_job[result];
691
692 return NULL;
693 }
694
695 static const struct {
696 const char *color, *word;
697 } job_print_status_messages [_JOB_RESULT_MAX] = {
698 [JOB_DONE] = { ANSI_GREEN, " OK " },
699 [JOB_TIMEOUT] = { ANSI_HIGHLIGHT_RED, " TIME " },
700 [JOB_FAILED] = { ANSI_HIGHLIGHT_RED, "FAILED" },
701 [JOB_DEPENDENCY] = { ANSI_HIGHLIGHT_YELLOW, "DEPEND" },
702 [JOB_SKIPPED] = { ANSI_HIGHLIGHT, " INFO " },
703 [JOB_ASSERT] = { ANSI_HIGHLIGHT_YELLOW, "ASSERT" },
704 [JOB_UNSUPPORTED] = { ANSI_HIGHLIGHT_YELLOW, "UNSUPP" },
705 /* JOB_COLLECTED */
706 };
707
708 static void job_print_status_message(Unit *u, JobType t, JobResult result) {
709 const char *format;
710 const char *status;
711
712 assert(u);
713 assert(t >= 0);
714 assert(t < _JOB_TYPE_MAX);
715
716 /* Reload status messages have traditionally not been printed to console. */
717 if (t == JOB_RELOAD)
718 return;
719
720 if (!job_print_status_messages[result].word)
721 return;
722
723 format = job_get_status_message_format(u, t, result);
724 if (!format)
725 return;
726
727 if (log_get_show_color())
728 status = strjoina(job_print_status_messages[result].color,
729 job_print_status_messages[result].word,
730 ANSI_NORMAL);
731 else
732 status = job_print_status_messages[result].word;
733
734 if (result != JOB_DONE)
735 manager_flip_auto_status(u->manager, true);
736
737 DISABLE_WARNING_FORMAT_NONLITERAL;
738 unit_status_printf(u, status, format);
739 REENABLE_WARNING;
740
741 if (t == JOB_START && result == JOB_FAILED) {
742 _cleanup_free_ char *quoted;
743
744 quoted = shell_maybe_quote(u->id, ESCAPE_BACKSLASH);
745 manager_status_printf(u->manager, STATUS_TYPE_NORMAL, NULL, "See 'systemctl status %s' for details.", strna(quoted));
746 }
747 }
748
749 static void job_log_status_message(Unit *u, JobType t, JobResult result) {
750 const char *format, *mid;
751 char buf[LINE_MAX];
752 static const int job_result_log_level[_JOB_RESULT_MAX] = {
753 [JOB_DONE] = LOG_INFO,
754 [JOB_CANCELED] = LOG_INFO,
755 [JOB_TIMEOUT] = LOG_ERR,
756 [JOB_FAILED] = LOG_ERR,
757 [JOB_DEPENDENCY] = LOG_WARNING,
758 [JOB_SKIPPED] = LOG_NOTICE,
759 [JOB_INVALID] = LOG_INFO,
760 [JOB_ASSERT] = LOG_WARNING,
761 [JOB_UNSUPPORTED] = LOG_WARNING,
762 [JOB_COLLECTED] = LOG_INFO,
763 };
764
765 assert(u);
766 assert(t >= 0);
767 assert(t < _JOB_TYPE_MAX);
768
769 /* Skip printing if output goes to the console, and job_print_status_message()
770 will actually print something to the console. */
771 if (log_on_console() && job_print_status_messages[result].word)
772 return;
773
774 format = job_get_status_message_format(u, t, result);
775 if (!format)
776 return;
777
778 /* The description might be longer than the buffer, but that's OK, we'll just truncate it here */
779 DISABLE_WARNING_FORMAT_NONLITERAL;
780 snprintf(buf, sizeof(buf), format, unit_description(u));
781 REENABLE_WARNING;
782
783 switch (t) {
784
785 case JOB_START:
786 if (result == JOB_DONE)
787 mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTED_STR;
788 else
789 mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_FAILED_STR;
790 break;
791
792 case JOB_RELOAD:
793 mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_RELOADED_STR;
794 break;
795
796 case JOB_STOP:
797 case JOB_RESTART:
798 mid = "MESSAGE_ID=" SD_MESSAGE_UNIT_STOPPED_STR;
799 break;
800
801 default:
802 log_struct(job_result_log_level[result],
803 LOG_MESSAGE("%s", buf),
804 "JOB_TYPE=%s", job_type_to_string(t),
805 "JOB_RESULT=%s", job_result_to_string(result),
806 LOG_UNIT_ID(u),
807 LOG_UNIT_INVOCATION_ID(u),
808 NULL);
809 return;
810 }
811
812 log_struct(job_result_log_level[result],
813 LOG_MESSAGE("%s", buf),
814 "JOB_TYPE=%s", job_type_to_string(t),
815 "JOB_RESULT=%s", job_result_to_string(result),
816 LOG_UNIT_ID(u),
817 LOG_UNIT_INVOCATION_ID(u),
818 mid,
819 NULL);
820 }
821
822 static void job_emit_status_message(Unit *u, JobType t, JobResult result) {
823 assert(u);
824
825 /* No message if the job did not actually do anything due to failed condition. */
826 if (t == JOB_START && result == JOB_DONE && !u->condition_result)
827 return;
828
829 job_log_status_message(u, t, result);
830 job_print_status_message(u, t, result);
831 }
832
833 static void job_fail_dependencies(Unit *u, UnitDependency d) {
834 Unit *other;
835 Iterator i;
836 void *v;
837
838 assert(u);
839
840 HASHMAP_FOREACH_KEY(v, other, u->dependencies[d], i) {
841 Job *j = other->job;
842
843 if (!j)
844 continue;
845 if (!IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE))
846 continue;
847
848 job_finish_and_invalidate(j, JOB_DEPENDENCY, true, false);
849 }
850 }
851
852 int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool already) {
853 Unit *u;
854 Unit *other;
855 JobType t;
856 Iterator i;
857 void *v;
858
859 assert(j);
860 assert(j->installed);
861 assert(j->type < _JOB_TYPE_MAX_IN_TRANSACTION);
862
863 u = j->unit;
864 t = j->type;
865
866 j->result = result;
867
868 log_unit_debug(u, "Job %s/%s finished, result=%s", u->id, job_type_to_string(t), job_result_to_string(result));
869
870 /* If this job did nothing to respective unit we don't log the status message */
871 if (!already)
872 job_emit_status_message(u, t, result);
873
874 /* Patch restart jobs so that they become normal start jobs */
875 if (result == JOB_DONE && t == JOB_RESTART) {
876
877 job_change_type(j, JOB_START);
878 job_set_state(j, JOB_WAITING);
879
880 job_add_to_dbus_queue(j);
881 job_add_to_run_queue(j);
882 job_add_to_gc_queue(j);
883
884 goto finish;
885 }
886
887 if (IN_SET(result, JOB_FAILED, JOB_INVALID))
888 j->manager->n_failed_jobs++;
889
890 job_uninstall(j);
891 job_free(j);
892
893 /* Fail depending jobs on failure */
894 if (result != JOB_DONE && recursive) {
895 if (IN_SET(t, JOB_START, JOB_VERIFY_ACTIVE)) {
896 job_fail_dependencies(u, UNIT_REQUIRED_BY);
897 job_fail_dependencies(u, UNIT_REQUISITE_OF);
898 job_fail_dependencies(u, UNIT_BOUND_BY);
899 } else if (t == JOB_STOP)
900 job_fail_dependencies(u, UNIT_CONFLICTED_BY);
901 }
902
903 /* Trigger OnFailure dependencies that are not generated by
904 * the unit itself. We don't treat JOB_CANCELED as failure in
905 * this context. And JOB_FAILURE is already handled by the
906 * unit itself. */
907 if (IN_SET(result, JOB_TIMEOUT, JOB_DEPENDENCY)) {
908 log_struct(LOG_NOTICE,
909 "JOB_TYPE=%s", job_type_to_string(t),
910 "JOB_RESULT=%s", job_result_to_string(result),
911 LOG_UNIT_ID(u),
912 LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.",
913 u->id,
914 job_type_to_string(t),
915 job_result_to_string(result)),
916 NULL);
917
918 unit_start_on_failure(u);
919 }
920
921 unit_trigger_notify(u);
922
923 finish:
924 /* Try to start the next jobs that can be started */
925 HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_AFTER], i)
926 if (other->job) {
927 job_add_to_run_queue(other->job);
928 job_add_to_gc_queue(other->job);
929 }
930 HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BEFORE], i)
931 if (other->job) {
932 job_add_to_run_queue(other->job);
933 job_add_to_gc_queue(other->job);
934 }
935
936 manager_check_finished(u->manager);
937
938 return 0;
939 }
940
941 static int job_dispatch_timer(sd_event_source *s, uint64_t monotonic, void *userdata) {
942 Job *j = userdata;
943 Unit *u;
944
945 assert(j);
946 assert(s == j->timer_event_source);
947
948 log_unit_warning(j->unit, "Job %s/%s timed out.", j->unit->id, job_type_to_string(j->type));
949
950 u = j->unit;
951 job_finish_and_invalidate(j, JOB_TIMEOUT, true, false);
952
953 emergency_action(u->manager, u->job_timeout_action, u->job_timeout_reboot_arg, "job timed out");
954
955 return 0;
956 }
957
958 int job_start_timer(Job *j, bool job_running) {
959 int r;
960 usec_t timeout_time, old_timeout_time;
961
962 if (job_running) {
963 j->begin_running_usec = now(CLOCK_MONOTONIC);
964
965 if (j->unit->job_running_timeout == USEC_INFINITY)
966 return 0;
967
968 timeout_time = usec_add(j->begin_running_usec, j->unit->job_running_timeout);
969
970 if (j->timer_event_source) {
971 /* Update only if JobRunningTimeoutSec= results in earlier timeout */
972 r = sd_event_source_get_time(j->timer_event_source, &old_timeout_time);
973 if (r < 0)
974 return r;
975
976 if (old_timeout_time <= timeout_time)
977 return 0;
978
979 return sd_event_source_set_time(j->timer_event_source, timeout_time);
980 }
981 } else {
982 if (j->timer_event_source)
983 return 0;
984
985 j->begin_usec = now(CLOCK_MONOTONIC);
986
987 if (j->unit->job_timeout == USEC_INFINITY)
988 return 0;
989
990 timeout_time = usec_add(j->begin_usec, j->unit->job_timeout);
991 }
992
993 r = sd_event_add_time(
994 j->manager->event,
995 &j->timer_event_source,
996 CLOCK_MONOTONIC,
997 timeout_time, 0,
998 job_dispatch_timer, j);
999 if (r < 0)
1000 return r;
1001
1002 (void) sd_event_source_set_description(j->timer_event_source, "job-start");
1003
1004 return 0;
1005 }
1006
1007 void job_add_to_run_queue(Job *j) {
1008 assert(j);
1009 assert(j->installed);
1010
1011 if (j->in_run_queue)
1012 return;
1013
1014 if (!j->manager->run_queue)
1015 sd_event_source_set_enabled(j->manager->run_queue_event_source, SD_EVENT_ONESHOT);
1016
1017 LIST_PREPEND(run_queue, j->manager->run_queue, j);
1018 j->in_run_queue = true;
1019 }
1020
1021 void job_add_to_dbus_queue(Job *j) {
1022 assert(j);
1023 assert(j->installed);
1024
1025 if (j->in_dbus_queue)
1026 return;
1027
1028 /* We don't check if anybody is subscribed here, since this
1029 * job might just have been created and not yet assigned to a
1030 * connection/client. */
1031
1032 LIST_PREPEND(dbus_queue, j->manager->dbus_job_queue, j);
1033 j->in_dbus_queue = true;
1034 }
1035
1036 char *job_dbus_path(Job *j) {
1037 char *p;
1038
1039 assert(j);
1040
1041 if (asprintf(&p, "/org/freedesktop/systemd1/job/%"PRIu32, j->id) < 0)
1042 return NULL;
1043
1044 return p;
1045 }
1046
1047 int job_serialize(Job *j, FILE *f) {
1048 assert(j);
1049 assert(f);
1050
1051 fprintf(f, "job-id=%u\n", j->id);
1052 fprintf(f, "job-type=%s\n", job_type_to_string(j->type));
1053 fprintf(f, "job-state=%s\n", job_state_to_string(j->state));
1054 fprintf(f, "job-irreversible=%s\n", yes_no(j->irreversible));
1055 fprintf(f, "job-sent-dbus-new-signal=%s\n", yes_no(j->sent_dbus_new_signal));
1056 fprintf(f, "job-ignore-order=%s\n", yes_no(j->ignore_order));
1057
1058 if (j->begin_usec > 0)
1059 fprintf(f, "job-begin="USEC_FMT"\n", j->begin_usec);
1060 if (j->begin_running_usec > 0)
1061 fprintf(f, "job-begin-running="USEC_FMT"\n", j->begin_running_usec);
1062
1063 bus_track_serialize(j->bus_track, f, "subscribed");
1064
1065 /* End marker */
1066 fputc('\n', f);
1067 return 0;
1068 }
1069
1070 int job_deserialize(Job *j, FILE *f) {
1071 assert(j);
1072 assert(f);
1073
1074 for (;;) {
1075 char line[LINE_MAX], *l, *v;
1076 size_t k;
1077
1078 if (!fgets(line, sizeof(line), f)) {
1079 if (feof(f))
1080 return 0;
1081 return -errno;
1082 }
1083
1084 char_array_0(line);
1085 l = strstrip(line);
1086
1087 /* End marker */
1088 if (l[0] == 0)
1089 return 0;
1090
1091 k = strcspn(l, "=");
1092
1093 if (l[k] == '=') {
1094 l[k] = 0;
1095 v = l+k+1;
1096 } else
1097 v = l+k;
1098
1099 if (streq(l, "job-id")) {
1100
1101 if (safe_atou32(v, &j->id) < 0)
1102 log_debug("Failed to parse job id value %s", v);
1103
1104 } else if (streq(l, "job-type")) {
1105 JobType t;
1106
1107 t = job_type_from_string(v);
1108 if (t < 0)
1109 log_debug("Failed to parse job type %s", v);
1110 else if (t >= _JOB_TYPE_MAX_IN_TRANSACTION)
1111 log_debug("Cannot deserialize job of type %s", v);
1112 else
1113 j->type = t;
1114
1115 } else if (streq(l, "job-state")) {
1116 JobState s;
1117
1118 s = job_state_from_string(v);
1119 if (s < 0)
1120 log_debug("Failed to parse job state %s", v);
1121 else
1122 job_set_state(j, s);
1123
1124 } else if (streq(l, "job-irreversible")) {
1125 int b;
1126
1127 b = parse_boolean(v);
1128 if (b < 0)
1129 log_debug("Failed to parse job irreversible flag %s", v);
1130 else
1131 j->irreversible = j->irreversible || b;
1132
1133 } else if (streq(l, "job-sent-dbus-new-signal")) {
1134 int b;
1135
1136 b = parse_boolean(v);
1137 if (b < 0)
1138 log_debug("Failed to parse job sent_dbus_new_signal flag %s", v);
1139 else
1140 j->sent_dbus_new_signal = j->sent_dbus_new_signal || b;
1141
1142 } else if (streq(l, "job-ignore-order")) {
1143 int b;
1144
1145 b = parse_boolean(v);
1146 if (b < 0)
1147 log_debug("Failed to parse job ignore_order flag %s", v);
1148 else
1149 j->ignore_order = j->ignore_order || b;
1150
1151 } else if (streq(l, "job-begin")) {
1152 unsigned long long ull;
1153
1154 if (sscanf(v, "%llu", &ull) != 1)
1155 log_debug("Failed to parse job-begin value %s", v);
1156 else
1157 j->begin_usec = ull;
1158
1159 } else if (streq(l, "job-begin-running")) {
1160 unsigned long long ull;
1161
1162 if (sscanf(v, "%llu", &ull) != 1)
1163 log_debug("Failed to parse job-begin-running value %s", v);
1164 else
1165 j->begin_running_usec = ull;
1166
1167 } else if (streq(l, "subscribed")) {
1168
1169 if (strv_extend(&j->deserialized_clients, v) < 0)
1170 log_oom();
1171 }
1172 }
1173 }
1174
1175 int job_coldplug(Job *j) {
1176 int r;
1177 usec_t timeout_time = USEC_INFINITY;
1178
1179 assert(j);
1180
1181 /* After deserialization is complete and the bus connection
1182 * set up again, let's start watching our subscribers again */
1183 (void) bus_job_coldplug_bus_track(j);
1184
1185 if (j->state == JOB_WAITING)
1186 job_add_to_run_queue(j);
1187
1188 /* Maybe due to new dependencies we don't actually need this job anymore? */
1189 job_add_to_gc_queue(j);
1190
1191 /* Create timer only when job began or began running and the respective timeout is finite.
1192 * Follow logic of job_start_timer() if both timeouts are finite */
1193 if (j->begin_usec == 0)
1194 return 0;
1195
1196 if (j->unit->job_timeout != USEC_INFINITY)
1197 timeout_time = usec_add(j->begin_usec, j->unit->job_timeout);
1198
1199 if (j->begin_running_usec > 0 && j->unit->job_running_timeout != USEC_INFINITY)
1200 timeout_time = MIN(timeout_time, usec_add(j->begin_running_usec, j->unit->job_running_timeout));
1201
1202 if (timeout_time == USEC_INFINITY)
1203 return 0;
1204
1205 j->timer_event_source = sd_event_source_unref(j->timer_event_source);
1206
1207 r = sd_event_add_time(
1208 j->manager->event,
1209 &j->timer_event_source,
1210 CLOCK_MONOTONIC,
1211 timeout_time, 0,
1212 job_dispatch_timer, j);
1213 if (r < 0)
1214 log_debug_errno(r, "Failed to restart timeout for job: %m");
1215
1216 (void) sd_event_source_set_description(j->timer_event_source, "job-timeout");
1217
1218 return r;
1219 }
1220
1221 void job_shutdown_magic(Job *j) {
1222 assert(j);
1223
1224 /* The shutdown target gets some special treatment here: we
1225 * tell the kernel to begin with flushing its disk caches, to
1226 * optimize shutdown time a bit. Ideally we wouldn't hardcode
1227 * this magic into PID 1. However all other processes aren't
1228 * options either since they'd exit much sooner than PID 1 and
1229 * asynchronous sync() would cause their exit to be
1230 * delayed. */
1231
1232 if (j->type != JOB_START)
1233 return;
1234
1235 if (!MANAGER_IS_SYSTEM(j->unit->manager))
1236 return;
1237
1238 if (!unit_has_name(j->unit, SPECIAL_SHUTDOWN_TARGET))
1239 return;
1240
1241 /* In case messages on console has been disabled on boot */
1242 j->unit->manager->no_console_output = false;
1243
1244 if (detect_container() > 0)
1245 return;
1246
1247 asynchronous_sync();
1248 }
1249
1250 int job_get_timeout(Job *j, usec_t *timeout) {
1251 usec_t x = USEC_INFINITY, y = USEC_INFINITY;
1252 Unit *u = j->unit;
1253 int r;
1254
1255 assert(u);
1256
1257 if (j->timer_event_source) {
1258 r = sd_event_source_get_time(j->timer_event_source, &x);
1259 if (r < 0)
1260 return r;
1261 }
1262
1263 if (UNIT_VTABLE(u)->get_timeout) {
1264 r = UNIT_VTABLE(u)->get_timeout(u, &y);
1265 if (r < 0)
1266 return r;
1267 }
1268
1269 if (x == USEC_INFINITY && y == USEC_INFINITY)
1270 return 0;
1271
1272 *timeout = MIN(x, y);
1273 return 1;
1274 }
1275
1276 bool job_check_gc(Job *j) {
1277 Unit *other;
1278 Iterator i;
1279 void *v;
1280
1281 assert(j);
1282
1283 /* Checks whether this job should be GC'ed away. We only do this for jobs of units that have no effect on their
1284 * own and just track external state. For now the only unit type that qualifies for this are .device units. */
1285
1286 if (!UNIT_VTABLE(j->unit)->gc_jobs)
1287 return true;
1288
1289 if (sd_bus_track_count(j->bus_track) > 0)
1290 return true;
1291
1292 /* FIXME: So this is a bit ugly: for now we don't properly track references made via private bus connections
1293 * (because it's nasty, as sd_bus_track doesn't apply to it). We simply remember that the job was once
1294 * referenced by one, and reset this whenever we notice that no private bus connections are around. This means
1295 * the GC is a bit too conservative when it comes to jobs created by private bus connections. */
1296 if (j->ref_by_private_bus) {
1297 if (set_isempty(j->unit->manager->private_buses))
1298 j->ref_by_private_bus = false;
1299 else
1300 return true;
1301 }
1302
1303 if (j->type == JOB_NOP)
1304 return true;
1305
1306 /* If a job is ordered after ours, and is to be started, then it needs to wait for us, regardless if we stop or
1307 * start, hence let's not GC in that case. */
1308 HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
1309 if (!other->job)
1310 continue;
1311
1312 if (other->job->ignore_order)
1313 continue;
1314
1315 if (IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD))
1316 return true;
1317 }
1318
1319 /* If we are going down, but something else is ordered After= us, then it needs to wait for us */
1320 if (IN_SET(j->type, JOB_STOP, JOB_RESTART))
1321 HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
1322 if (!other->job)
1323 continue;
1324
1325 if (other->job->ignore_order)
1326 continue;
1327
1328 return true;
1329 }
1330
1331 /* The logic above is kinda the inverse of the job_is_runnable() logic. Specifically, if the job "we" is
1332 * ordered before the job "other":
1333 *
1334 * we start + other start → stay
1335 * we start + other stop → gc
1336 * we stop + other start → stay
1337 * we stop + other stop → gc
1338 *
1339 * "we" are ordered after "other":
1340 *
1341 * we start + other start → gc
1342 * we start + other stop → gc
1343 * we stop + other start → stay
1344 * we stop + other stop → stay
1345 *
1346 */
1347
1348 return false;
1349 }
1350
1351 void job_add_to_gc_queue(Job *j) {
1352 assert(j);
1353
1354 if (j->in_gc_queue)
1355 return;
1356
1357 if (job_check_gc(j))
1358 return;
1359
1360 LIST_PREPEND(gc_queue, j->unit->manager->gc_job_queue, j);
1361 j->in_gc_queue = true;
1362 }
1363
1364 static int job_compare(const void *a, const void *b) {
1365 Job *x = *(Job**) a, *y = *(Job**) b;
1366
1367 if (x->id < y->id)
1368 return -1;
1369 if (x->id > y->id)
1370 return 1;
1371
1372 return 0;
1373 }
1374
1375 static size_t sort_job_list(Job **list, size_t n) {
1376 Job *previous = NULL;
1377 size_t a, b;
1378
1379 /* Order by numeric IDs */
1380 qsort_safe(list, n, sizeof(Job*), job_compare);
1381
1382 /* Filter out duplicates */
1383 for (a = 0, b = 0; a < n; a++) {
1384
1385 if (previous == list[a])
1386 continue;
1387
1388 previous = list[b++] = list[a];
1389 }
1390
1391 return b;
1392 }
1393
1394 int job_get_before(Job *j, Job*** ret) {
1395 _cleanup_free_ Job** list = NULL;
1396 size_t n = 0, n_allocated = 0;
1397 Unit *other = NULL;
1398 Iterator i;
1399 void *v;
1400
1401 /* Returns a list of all pending jobs that need to finish before this job may be started. */
1402
1403 assert(j);
1404 assert(ret);
1405
1406 if (j->ignore_order) {
1407 *ret = NULL;
1408 return 0;
1409 }
1410
1411 if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
1412
1413 HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
1414 if (!other->job)
1415 continue;
1416
1417 if (!GREEDY_REALLOC(list, n_allocated, n+1))
1418 return -ENOMEM;
1419 list[n++] = other->job;
1420 }
1421 }
1422
1423 HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
1424 if (!other->job)
1425 continue;
1426
1427 if (!IN_SET(other->job->type, JOB_STOP, JOB_RESTART))
1428 continue;
1429
1430 if (!GREEDY_REALLOC(list, n_allocated, n+1))
1431 return -ENOMEM;
1432 list[n++] = other->job;
1433 }
1434
1435 n = sort_job_list(list, n);
1436
1437 *ret = list;
1438 list = NULL;
1439
1440 return (int) n;
1441 }
1442
1443 int job_get_after(Job *j, Job*** ret) {
1444 _cleanup_free_ Job** list = NULL;
1445 size_t n = 0, n_allocated = 0;
1446 Unit *other = NULL;
1447 void *v;
1448 Iterator i;
1449
1450 assert(j);
1451 assert(ret);
1452
1453 /* Returns a list of all pending jobs that are waiting for this job to finish. */
1454
1455 HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
1456 if (!other->job)
1457 continue;
1458
1459 if (other->job->ignore_order)
1460 continue;
1461
1462 if (!IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD))
1463 continue;
1464
1465 if (!GREEDY_REALLOC(list, n_allocated, n+1))
1466 return -ENOMEM;
1467 list[n++] = other->job;
1468 }
1469
1470 if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) {
1471
1472 HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
1473 if (!other->job)
1474 continue;
1475
1476 if (other->job->ignore_order)
1477 continue;
1478
1479 if (!GREEDY_REALLOC(list, n_allocated, n+1))
1480 return -ENOMEM;
1481 list[n++] = other->job;
1482 }
1483 }
1484
1485 n = sort_job_list(list, n);
1486
1487 *ret = list;
1488 list = NULL;
1489
1490 return (int) n;
1491 }
1492
1493 static const char* const job_state_table[_JOB_STATE_MAX] = {
1494 [JOB_WAITING] = "waiting",
1495 [JOB_RUNNING] = "running",
1496 };
1497
1498 DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
1499
1500 static const char* const job_type_table[_JOB_TYPE_MAX] = {
1501 [JOB_START] = "start",
1502 [JOB_VERIFY_ACTIVE] = "verify-active",
1503 [JOB_STOP] = "stop",
1504 [JOB_RELOAD] = "reload",
1505 [JOB_RELOAD_OR_START] = "reload-or-start",
1506 [JOB_RESTART] = "restart",
1507 [JOB_TRY_RESTART] = "try-restart",
1508 [JOB_TRY_RELOAD] = "try-reload",
1509 [JOB_NOP] = "nop",
1510 };
1511
1512 DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
1513
1514 static const char* const job_mode_table[_JOB_MODE_MAX] = {
1515 [JOB_FAIL] = "fail",
1516 [JOB_REPLACE] = "replace",
1517 [JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
1518 [JOB_ISOLATE] = "isolate",
1519 [JOB_FLUSH] = "flush",
1520 [JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
1521 [JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
1522 };
1523
1524 DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
1525
1526 static const char* const job_result_table[_JOB_RESULT_MAX] = {
1527 [JOB_DONE] = "done",
1528 [JOB_CANCELED] = "canceled",
1529 [JOB_TIMEOUT] = "timeout",
1530 [JOB_FAILED] = "failed",
1531 [JOB_DEPENDENCY] = "dependency",
1532 [JOB_SKIPPED] = "skipped",
1533 [JOB_INVALID] = "invalid",
1534 [JOB_ASSERT] = "assert",
1535 [JOB_UNSUPPORTED] = "unsupported",
1536 [JOB_COLLECTED] = "collected",
1537 };
1538
1539 DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);
1540
1541 const char* job_type_to_access_method(JobType t) {
1542 assert(t >= 0);
1543 assert(t < _JOB_TYPE_MAX);
1544
1545 if (IN_SET(t, JOB_START, JOB_RESTART, JOB_TRY_RESTART))
1546 return "start";
1547 else if (t == JOB_STOP)
1548 return "stop";
1549 else
1550 return "reload";
1551 }