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