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