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