Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <fcntl.h>
4 : : #include <unistd.h>
5 : :
6 : : #include "alloc-util.h"
7 : : #include "bus-common-errors.h"
8 : : #include "bus-error.h"
9 : : #include "dbus-unit.h"
10 : : #include "strv.h"
11 : : #include "terminal-util.h"
12 : : #include "transaction.h"
13 : :
14 : : static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
15 : :
16 : 456 : static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) {
17 [ - + ]: 456 : assert(tr);
18 [ - + ]: 456 : assert(j);
19 : :
20 : : /* Deletes one job from the transaction */
21 : :
22 : 456 : transaction_unlink_job(tr, j, delete_dependencies);
23 : :
24 : 456 : job_free(j);
25 : 456 : }
26 : :
27 : 8 : static void transaction_delete_unit(Transaction *tr, Unit *u) {
28 : : Job *j;
29 : :
30 : : /* Deletes all jobs associated with a certain unit from the
31 : : * transaction */
32 : :
33 [ + + ]: 16 : while ((j = hashmap_get(tr->jobs, u)))
34 : 8 : transaction_delete_job(tr, j, true);
35 : 8 : }
36 : :
37 : 16 : void transaction_abort(Transaction *tr) {
38 : : Job *j;
39 : :
40 [ - + ]: 16 : assert(tr);
41 : :
42 [ + + ]: 108 : while ((j = hashmap_first(tr->jobs)))
43 : 92 : transaction_delete_job(tr, j, false);
44 : :
45 [ - + ]: 16 : assert(hashmap_isempty(tr->jobs));
46 : 16 : }
47 : :
48 : 504 : static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
49 : : JobDependency *l;
50 : :
51 : : /* A recursive sweep through the graph that marks all units
52 : : * that matter to the anchor job, i.e. are directly or
53 : : * indirectly a dependency of the anchor job via paths that
54 : : * are fully marked as mattering. */
55 : :
56 : 504 : j->matters_to_anchor = true;
57 : 504 : j->generation = generation;
58 : :
59 [ + + ]: 1340 : LIST_FOREACH(subject, l, j->subject_list) {
60 : :
61 : : /* This link does not matter */
62 [ + + ]: 836 : if (!l->matters)
63 : 200 : continue;
64 : :
65 : : /* This unit has already been marked */
66 [ + + ]: 636 : if (l->object->generation == generation)
67 : 204 : continue;
68 : :
69 : 432 : transaction_find_jobs_that_matter_to_anchor(l->object, generation);
70 : : }
71 : 504 : }
72 : :
73 : 0 : static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) {
74 : : JobDependency *l, *last;
75 : :
76 [ # # ]: 0 : assert(j);
77 [ # # ]: 0 : assert(other);
78 [ # # ]: 0 : assert(j->unit == other->unit);
79 [ # # ]: 0 : assert(!j->installed);
80 : :
81 : : /* Merges 'other' into 'j' and then deletes 'other'. */
82 : :
83 : 0 : j->type = t;
84 : 0 : j->state = JOB_WAITING;
85 [ # # # # ]: 0 : j->irreversible = j->irreversible || other->irreversible;
86 [ # # # # ]: 0 : j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
87 : :
88 : : /* Patch us in as new owner of the JobDependency objects */
89 : 0 : last = NULL;
90 [ # # ]: 0 : LIST_FOREACH(subject, l, other->subject_list) {
91 [ # # ]: 0 : assert(l->subject == other);
92 : 0 : l->subject = j;
93 : 0 : last = l;
94 : : }
95 : :
96 : : /* Merge both lists */
97 [ # # ]: 0 : if (last) {
98 : 0 : last->subject_next = j->subject_list;
99 [ # # ]: 0 : if (j->subject_list)
100 : 0 : j->subject_list->subject_prev = last;
101 : 0 : j->subject_list = other->subject_list;
102 : : }
103 : :
104 : : /* Patch us in as new owner of the JobDependency objects */
105 : 0 : last = NULL;
106 [ # # ]: 0 : LIST_FOREACH(object, l, other->object_list) {
107 [ # # ]: 0 : assert(l->object == other);
108 : 0 : l->object = j;
109 : 0 : last = l;
110 : : }
111 : :
112 : : /* Merge both lists */
113 [ # # ]: 0 : if (last) {
114 : 0 : last->object_next = j->object_list;
115 [ # # ]: 0 : if (j->object_list)
116 : 0 : j->object_list->object_prev = last;
117 : 0 : j->object_list = other->object_list;
118 : : }
119 : :
120 : : /* Kill the other job */
121 : 0 : other->subject_list = NULL;
122 : 0 : other->object_list = NULL;
123 : 0 : transaction_delete_job(tr, other, true);
124 : 0 : }
125 : :
126 : 0 : _pure_ static bool job_is_conflicted_by(Job *j) {
127 : : JobDependency *l;
128 : :
129 [ # # ]: 0 : assert(j);
130 : :
131 : : /* Returns true if this job is pulled in by a least one
132 : : * ConflictedBy dependency. */
133 : :
134 [ # # ]: 0 : LIST_FOREACH(object, l, j->object_list)
135 [ # # ]: 0 : if (l->conflicts)
136 : 0 : return true;
137 : :
138 : 0 : return false;
139 : : }
140 : :
141 : 0 : static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
142 : : Job *k;
143 : :
144 [ # # ]: 0 : assert(j);
145 : :
146 : : /* Tries to delete one item in the linked list
147 : : * j->transaction_next->transaction_next->... that conflicts
148 : : * with another one, in an attempt to make an inconsistent
149 : : * transaction work. */
150 : :
151 : : /* We rely here on the fact that if a merged with b does not
152 : : * merge with c, either a or b merge with c neither */
153 [ # # ]: 0 : LIST_FOREACH(transaction, j, j)
154 [ # # ]: 0 : LIST_FOREACH(transaction, k, j->transaction_next) {
155 : : Job *d;
156 : :
157 : : /* Is this one mergeable? Then skip it */
158 [ # # ]: 0 : if (job_type_is_mergeable(j->type, k->type))
159 : 0 : continue;
160 : :
161 : : /* Ok, we found two that conflict, let's see if we can
162 : : * drop one of them */
163 [ # # # # ]: 0 : if (!j->matters_to_anchor && !k->matters_to_anchor) {
164 : :
165 : : /* Both jobs don't matter, so let's
166 : : * find the one that is smarter to
167 : : * remove. Let's think positive and
168 : : * rather remove stops then starts --
169 : : * except if something is being
170 : : * stopped because it is conflicted by
171 : : * another unit in which case we
172 : : * rather remove the start. */
173 : :
174 [ # # # # : 0 : log_unit_debug(j->unit,
# # # # #
# ]
175 : : "Looking at job %s/%s conflicted_by=%s",
176 : : j->unit->id, job_type_to_string(j->type),
177 : : yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
178 [ # # # # : 0 : log_unit_debug(k->unit,
# # # # #
# ]
179 : : "Looking at job %s/%s conflicted_by=%s",
180 : : k->unit->id, job_type_to_string(k->type),
181 : : yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
182 : :
183 [ # # ]: 0 : if (j->type == JOB_STOP) {
184 : :
185 [ # # ]: 0 : if (job_is_conflicted_by(j))
186 : 0 : d = k;
187 : : else
188 : 0 : d = j;
189 : :
190 [ # # ]: 0 : } else if (k->type == JOB_STOP) {
191 : :
192 [ # # ]: 0 : if (job_is_conflicted_by(k))
193 : 0 : d = j;
194 : : else
195 : 0 : d = k;
196 : : } else
197 : 0 : d = j;
198 : :
199 [ # # ]: 0 : } else if (!j->matters_to_anchor)
200 : 0 : d = j;
201 [ # # ]: 0 : else if (!k->matters_to_anchor)
202 : 0 : d = k;
203 : : else
204 : 0 : return -ENOEXEC;
205 : :
206 : : /* Ok, we can drop one, so let's do so. */
207 [ # # ]: 0 : log_unit_debug(d->unit,
208 : : "Fixing conflicting jobs %s/%s,%s/%s by deleting job %s/%s",
209 : : j->unit->id, job_type_to_string(j->type),
210 : : k->unit->id, job_type_to_string(k->type),
211 : : d->unit->id, job_type_to_string(d->type));
212 : 0 : transaction_delete_job(tr, d, true);
213 : 0 : return 0;
214 : : }
215 : :
216 : 0 : return -EINVAL;
217 : : }
218 : :
219 : 64 : static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) {
220 : : Job *j;
221 : : Iterator i;
222 : : int r;
223 : :
224 [ - + ]: 64 : assert(tr);
225 : :
226 : : /* First step, check whether any of the jobs for one specific
227 : : * task conflict. If so, try to drop one of them. */
228 [ + + ]: 364 : HASHMAP_FOREACH(j, tr->jobs, i) {
229 : : JobType t;
230 : : Job *k;
231 : :
232 : 300 : t = j->type;
233 [ - + ]: 300 : LIST_FOREACH(transaction, k, j->transaction_next) {
234 [ # # ]: 0 : if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
235 : 0 : continue;
236 : :
237 : : /* OK, we could not merge all jobs for this
238 : : * action. Let's see if we can get rid of one
239 : : * of them */
240 : :
241 : 0 : r = delete_one_unmergeable_job(tr, j);
242 [ # # ]: 0 : if (r >= 0)
243 : : /* Ok, we managed to drop one, now
244 : : * let's ask our callers to call us
245 : : * again after garbage collecting */
246 : 0 : return -EAGAIN;
247 : :
248 : : /* We couldn't merge anything. Failure */
249 : 0 : return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING,
250 : : "Transaction contains conflicting jobs '%s' and '%s' for %s. "
251 : : "Probably contradicting requirement dependencies configured.",
252 : : job_type_to_string(t),
253 : : job_type_to_string(k->type),
254 : 0 : k->unit->id);
255 : : }
256 : : }
257 : :
258 : : /* Second step, merge the jobs. */
259 [ + + ]: 364 : HASHMAP_FOREACH(j, tr->jobs, i) {
260 : 300 : JobType t = j->type;
261 : : Job *k;
262 : :
263 : : /* Merge all transaction jobs for j->unit */
264 [ - + ]: 300 : LIST_FOREACH(transaction, k, j->transaction_next)
265 [ # # ]: 0 : assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
266 : :
267 [ - + ]: 300 : while ((k = j->transaction_next)) {
268 [ # # ]: 0 : if (tr->anchor_job == k) {
269 : 0 : transaction_merge_and_delete_job(tr, k, j, t);
270 : 0 : j = k;
271 : : } else
272 : 0 : transaction_merge_and_delete_job(tr, j, k, t);
273 : : }
274 : :
275 [ - + ]: 300 : assert(!j->transaction_next);
276 [ - + ]: 300 : assert(!j->transaction_prev);
277 : : }
278 : :
279 : 64 : return 0;
280 : : }
281 : :
282 : 136 : static void transaction_drop_redundant(Transaction *tr) {
283 : : bool again;
284 : :
285 : : /* Goes through the transaction and removes all jobs of the units whose jobs are all noops. If not
286 : : * all of a unit's jobs are redundant, they are kept. */
287 : :
288 [ - + ]: 136 : assert(tr);
289 : :
290 : : do {
291 : : Iterator i;
292 : : Job *j;
293 : :
294 : 472 : again = false;
295 : :
296 [ + + ]: 2239 : HASHMAP_FOREACH(j, tr->jobs, i) {
297 : 2103 : bool keep = false;
298 : : Job *k;
299 : :
300 [ + + ]: 2439 : LIST_FOREACH(transaction, k, j)
301 [ + + ]: 2103 : if (tr->anchor_job == k ||
302 [ + + ]: 1796 : !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
303 [ + + + + ]: 372 : (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) {
304 : 1767 : keep = true;
305 : 1767 : break;
306 : : }
307 : :
308 [ + + ]: 2103 : if (!keep) {
309 [ + + ]: 336 : log_trace("Found redundant job %s/%s, dropping from transaction.",
310 : : j->unit->id, job_type_to_string(j->type));
311 : 336 : transaction_delete_job(tr, j, false);
312 : 336 : again = true;
313 : 336 : break;
314 : : }
315 : : }
316 [ + + ]: 472 : } while (again);
317 : 136 : }
318 : :
319 : 34 : _pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) {
320 [ - + ]: 34 : assert(u);
321 [ - + ]: 34 : assert(!j->transaction_prev);
322 : :
323 : : /* Checks whether at least one of the jobs for this unit
324 : : * matters to the anchor. */
325 : :
326 [ + + ]: 42 : LIST_FOREACH(transaction, j, j)
327 [ + + ]: 34 : if (j->matters_to_anchor)
328 : 26 : return true;
329 : :
330 : 8 : return false;
331 : : }
332 : :
333 : 16 : static char* merge_unit_ids(const char* unit_log_field, char **pairs) {
334 : 16 : char **unit_id, **job_type, *ans = NULL;
335 : 16 : size_t alloc = 0, size = 0, next;
336 : :
337 [ + - + + : 60 : STRV_FOREACH_PAIR(unit_id, job_type, pairs) {
+ - ]
338 : 44 : next = strlen(unit_log_field) + strlen(*unit_id);
339 [ - + ]: 44 : if (!GREEDY_REALLOC(ans, alloc, size + next + 1)) {
340 : 0 : return mfree(ans);
341 : : }
342 : :
343 : 44 : sprintf(ans + size, "%s%s", unit_log_field, *unit_id);
344 [ + - ]: 44 : if (*(unit_id+1))
345 : 44 : ans[size + next] = '\n';
346 : 44 : size += next + 1;
347 : : }
348 : :
349 : 16 : return ans;
350 : : }
351 : :
352 : 621 : static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, sd_bus_error *e) {
353 : : Iterator i;
354 : : Unit *u;
355 : : void *v;
356 : : int r;
357 : : static const UnitDependency directions[] = {
358 : : UNIT_BEFORE,
359 : : UNIT_AFTER,
360 : : };
361 : : size_t d;
362 : :
363 [ - + ]: 621 : assert(tr);
364 [ - + ]: 621 : assert(j);
365 [ - + ]: 621 : assert(!j->transaction_prev);
366 : :
367 : : /* Does a recursive sweep through the ordering graph, looking
368 : : * for a cycle. If we find a cycle we try to break it. */
369 : :
370 : : /* Have we seen this before? */
371 [ + + ]: 621 : if (j->generation == generation) {
372 : 240 : Job *k, *delete = NULL;
373 : 240 : _cleanup_free_ char **array = NULL, *unit_ids = NULL;
374 : : char **unit_id, **job_type;
375 : :
376 : : /* If the marker is NULL we have been here already and
377 : : * decided the job was loop-free from here. Hence
378 : : * shortcut things and return right-away. */
379 [ + + ]: 240 : if (!j->marker)
380 : 224 : return 0;
381 : :
382 : : /* So, the marker is not NULL and we already have been here. We have
383 : : * a cycle. Let's try to break it. We go backwards in our path and
384 : : * try to find a suitable job to remove. We use the marker to find
385 : : * our way back, since smart how we are we stored our way back in
386 : : * there. */
387 : :
388 [ + - + - : 44 : for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
+ - ]
389 : :
390 : : /* For logging below */
391 [ - + ]: 44 : if (strv_push_pair(&array, k->unit->id, (char*) job_type_to_string(k->type)) < 0)
392 : 0 : log_oom();
393 : :
394 [ + + + - : 44 : if (!delete && hashmap_get(tr->jobs, k->unit) && !unit_matters_to_anchor(k->unit, k))
+ + ]
395 : : /* Ok, we can drop this one, so let's do so. */
396 : 8 : delete = k;
397 : :
398 : : /* Check if this in fact was the beginning of the cycle */
399 [ + + ]: 44 : if (k == j)
400 : 16 : break;
401 : : }
402 : :
403 : 16 : unit_ids = merge_unit_ids(j->manager->unit_log_field, array); /* ignore error */
404 : :
405 [ + - + + : 60 : STRV_FOREACH_PAIR(unit_id, job_type, array)
+ - ]
406 : : /* logging for j not k here to provide a consistent narrative */
407 [ + + ]: 44 : log_struct(LOG_WARNING,
408 : : "MESSAGE=%s: Found %s on %s/%s",
409 : : j->unit->id,
410 : : unit_id == array ? "ordering cycle" : "dependency",
411 : : *unit_id, *job_type,
412 : : unit_ids);
413 : :
414 [ + + ]: 16 : if (delete) {
415 : : const char *status;
416 : : /* logging for j not k here to provide a consistent narrative */
417 : 8 : log_struct(LOG_ERR,
418 : : "MESSAGE=%s: Job %s/%s deleted to break ordering cycle starting with %s/%s",
419 : : j->unit->id, delete->unit->id, job_type_to_string(delete->type),
420 : : j->unit->id, job_type_to_string(j->type),
421 : : unit_ids);
422 : :
423 [ - + ]: 8 : if (log_get_show_color())
424 : 0 : status = ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL;
425 : : else
426 : 8 : status = " SKIP ";
427 : :
428 : 8 : unit_status_printf(delete->unit, status,
429 : : "Ordering cycle found, skipping %s");
430 : 8 : transaction_delete_unit(tr, delete->unit);
431 : 8 : return -EAGAIN;
432 : : }
433 : :
434 : 8 : log_struct(LOG_ERR,
435 : : "MESSAGE=%s: Unable to break cycle starting with %s/%s",
436 : : j->unit->id, j->unit->id, job_type_to_string(j->type),
437 : : unit_ids);
438 : :
439 : 8 : return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
440 : : "Transaction order is cyclic. See system logs for details.");
441 : : }
442 : :
443 : : /* Make the marker point to where we come from, so that we can
444 : : * find our way backwards if we want to break a cycle. We use
445 : : * a special marker for the beginning: we point to
446 : : * ourselves. */
447 [ + + ]: 381 : j->marker = from ? from : j;
448 : 381 : j->generation = generation;
449 : :
450 : : /* Actual ordering of jobs depends on the unit ordering dependency and job types. We need to traverse
451 : : * the graph over 'before' edges in the actual job execution order. We traverse over both unit
452 : : * ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
453 : : * execution ordering. */
454 [ + + ]: 1017 : for (d = 0; d < ELEMENTSOF(directions); d++) {
455 [ + + ]: 2401 : HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[directions[d]], i) {
456 : : Job *o;
457 : :
458 : : /* Is there a job for this unit? */
459 : 1765 : o = hashmap_get(tr->jobs, u);
460 [ + + ]: 1765 : if (!o) {
461 : : /* Ok, there is no job for this in the
462 : : * transaction, but maybe there is already one
463 : : * running? */
464 : 1230 : o = u->job;
465 [ + + ]: 1230 : if (!o)
466 : 1186 : continue;
467 : : }
468 : :
469 : : /* Cut traversing if the job j is not really *before* o. */
470 [ + + ]: 579 : if (job_compare(j, o, directions[d]) >= 0)
471 : 279 : continue;
472 : :
473 : 300 : r = transaction_verify_order_one(tr, o, j, generation, e);
474 [ + + ]: 300 : if (r < 0)
475 : 63 : return r;
476 : : }
477 : : }
478 : :
479 : : /* Ok, let's backtrack, and remember that this entry is not on
480 : : * our path anymore. */
481 : 318 : j->marker = NULL;
482 : :
483 : 318 : return 0;
484 : : }
485 : :
486 : 80 : static int transaction_verify_order(Transaction *tr, unsigned *generation, sd_bus_error *e) {
487 : : Job *j;
488 : : int r;
489 : : Iterator i;
490 : : unsigned g;
491 : :
492 [ - + ]: 80 : assert(tr);
493 [ - + ]: 80 : assert(generation);
494 : :
495 : : /* Check if the ordering graph is cyclic. If it is, try to fix
496 : : * that up by dropping one of the jobs. */
497 : :
498 : 80 : g = (*generation)++;
499 : :
500 [ + + ]: 385 : HASHMAP_FOREACH(j, tr->jobs, i) {
501 : 321 : r = transaction_verify_order_one(tr, j, NULL, g, e);
502 [ + + ]: 321 : if (r < 0)
503 : 16 : return r;
504 : : }
505 : :
506 : 64 : return 0;
507 : : }
508 : :
509 : 80 : static void transaction_collect_garbage(Transaction *tr) {
510 : : bool again;
511 : :
512 [ - + ]: 80 : assert(tr);
513 : :
514 : : /* Drop jobs that are not required by any other job */
515 : :
516 : : do {
517 : : Iterator i;
518 : : Job *j;
519 : :
520 : 88 : again = false;
521 : :
522 [ + + ]: 530 : HASHMAP_FOREACH(j, tr->jobs, i) {
523 [ + + ]: 450 : if (tr->anchor_job == j)
524 : 82 : continue;
525 : :
526 [ + + ]: 368 : if (!j->object_list) {
527 [ + - ]: 8 : log_trace("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type));
528 : 8 : transaction_delete_job(tr, j, true);
529 : 8 : again = true;
530 : 8 : break;
531 : : }
532 : :
533 [ + + + - : 360 : log_trace("Keeping job %s/%s because of %s/%s",
+ - ]
534 : : j->unit->id, job_type_to_string(j->type),
535 : : j->object_list->subject ? j->object_list->subject->unit->id : "root",
536 : : j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root");
537 : : }
538 : :
539 [ + + ]: 88 : } while (again);
540 : 80 : }
541 : :
542 : 64 : static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_error *e) {
543 : : Iterator i;
544 : : Job *j;
545 : :
546 [ - + ]: 64 : assert(tr);
547 : :
548 : : /* Checks whether applying this transaction means that
549 : : * existing jobs would be replaced */
550 : :
551 [ + + ]: 346 : HASHMAP_FOREACH(j, tr->jobs, i) {
552 : :
553 : : /* Assume merged */
554 [ - + ]: 290 : assert(!j->transaction_prev);
555 [ - + ]: 290 : assert(!j->transaction_next);
556 : :
557 [ + + + + : 290 : if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
- + ]
558 [ + + ]: 73 : job_type_is_conflicting(j->unit->job->type, j->type))
559 : 8 : return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
560 : : "Transaction for %s/%s is destructive (%s has '%s' job queued, but '%s' is included in transaction).",
561 : 16 : tr->anchor_job->unit->id, job_type_to_string(tr->anchor_job->type),
562 : 8 : j->unit->id, job_type_to_string(j->unit->job->type), job_type_to_string(j->type));
563 : : }
564 : :
565 : 56 : return 0;
566 : : }
567 : :
568 : 24 : static void transaction_minimize_impact(Transaction *tr) {
569 : : Job *j;
570 : : Iterator i;
571 : :
572 [ - + ]: 24 : assert(tr);
573 : :
574 : : /* Drops all unnecessary jobs that reverse already active jobs
575 : : * or that stop a running service. */
576 : :
577 : 24 : rescan:
578 [ + + ]: 295 : HASHMAP_FOREACH(j, tr->jobs, i) {
579 [ + + ]: 538 : LIST_FOREACH(transaction, j, j) {
580 : : bool stops_running_service, changes_existing_job;
581 : :
582 : : /* If it matters, we shouldn't drop it */
583 [ + + ]: 271 : if (j->matters_to_anchor)
584 : 178 : continue;
585 : :
586 : : /* Would this stop a running service?
587 : : * Would this change an existing job?
588 : : * If so, let's drop this entry */
589 : :
590 : 93 : stops_running_service =
591 [ + + - + ]: 93 : j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
592 : :
593 : 93 : changes_existing_job =
594 [ + + ]: 137 : j->unit->job &&
595 [ + + ]: 44 : job_type_is_conflicting(j->type, j->unit->job->type);
596 : :
597 [ + - + + ]: 93 : if (!stops_running_service && !changes_existing_job)
598 : 89 : continue;
599 : :
600 [ - + ]: 4 : if (stops_running_service)
601 [ # # ]: 0 : log_unit_debug(j->unit,
602 : : "%s/%s would stop a running service.",
603 : : j->unit->id, job_type_to_string(j->type));
604 : :
605 [ + - ]: 4 : if (changes_existing_job)
606 [ + - ]: 4 : log_unit_debug(j->unit,
607 : : "%s/%s would change existing job.",
608 : : j->unit->id, job_type_to_string(j->type));
609 : :
610 : : /* Ok, let's get rid of this */
611 [ + - ]: 4 : log_unit_debug(j->unit,
612 : : "Deleting %s/%s to minimize impact.",
613 : : j->unit->id, job_type_to_string(j->type));
614 : :
615 : 4 : transaction_delete_job(tr, j, true);
616 : 4 : goto rescan;
617 : : }
618 : : }
619 : 24 : }
620 : :
621 : 56 : static int transaction_apply(
622 : : Transaction *tr,
623 : : Manager *m,
624 : : JobMode mode,
625 : : Set *affected_jobs) {
626 : :
627 : : Iterator i;
628 : : Job *j;
629 : : int r;
630 : :
631 : : /* Moves the transaction jobs to the set of active jobs */
632 : :
633 [ - + - + ]: 56 : if (IN_SET(mode, JOB_ISOLATE, JOB_FLUSH)) {
634 : :
635 : : /* When isolating first kill all installed jobs which
636 : : * aren't part of the new transaction */
637 [ # # ]: 0 : HASHMAP_FOREACH(j, m->jobs, i) {
638 [ # # ]: 0 : assert(j->installed);
639 : :
640 [ # # ]: 0 : if (j->unit->ignore_on_isolate)
641 : 0 : continue;
642 : :
643 [ # # ]: 0 : if (hashmap_get(tr->jobs, j->unit))
644 : 0 : continue;
645 : :
646 : : /* Not invalidating recursively. Avoids triggering
647 : : * OnFailure= actions of dependent jobs. Also avoids
648 : : * invalidating our iterator. */
649 : 0 : job_finish_and_invalidate(j, JOB_CANCELED, false, false);
650 : : }
651 : : }
652 : :
653 [ + + ]: 328 : HASHMAP_FOREACH(j, tr->jobs, i) {
654 : : /* Assume merged */
655 [ - + ]: 272 : assert(!j->transaction_prev);
656 [ - + ]: 272 : assert(!j->transaction_next);
657 : :
658 : 272 : r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
659 [ - + ]: 272 : if (r < 0)
660 : 0 : goto rollback;
661 : : }
662 : :
663 [ + + ]: 328 : while ((j = hashmap_steal_first(tr->jobs))) {
664 : : Job *installed_job;
665 : :
666 : : /* Clean the job dependencies */
667 : 272 : transaction_unlink_job(tr, j, false);
668 : :
669 : 272 : installed_job = job_install(j);
670 [ + + ]: 272 : if (installed_job != j) {
671 : : /* j has been merged into a previously installed job */
672 [ + + ]: 72 : if (tr->anchor_job == j)
673 : 8 : tr->anchor_job = installed_job;
674 : 72 : hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
675 : 72 : job_free(j);
676 : 72 : j = installed_job;
677 : : }
678 : :
679 : 272 : job_add_to_run_queue(j);
680 : 272 : job_add_to_dbus_queue(j);
681 : 272 : job_start_timer(j, false);
682 : 272 : job_shutdown_magic(j);
683 : :
684 : : /* When 'affected' is specified, let's track all in it all jobs that were touched because of
685 : : * this transaction. */
686 [ - + ]: 272 : if (affected_jobs)
687 : 0 : (void) set_put(affected_jobs, j);
688 : : }
689 : :
690 : 56 : return 0;
691 : :
692 : 0 : rollback:
693 : :
694 [ # # ]: 0 : HASHMAP_FOREACH(j, tr->jobs, i)
695 : 0 : hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
696 : :
697 : 0 : return r;
698 : : }
699 : :
700 : 72 : int transaction_activate(
701 : : Transaction *tr,
702 : : Manager *m,
703 : : JobMode mode,
704 : : Set *affected_jobs,
705 : : sd_bus_error *e) {
706 : :
707 : : Iterator i;
708 : : Job *j;
709 : : int r;
710 : 72 : unsigned generation = 1;
711 : :
712 [ - + ]: 72 : assert(tr);
713 : :
714 : : /* This applies the changes recorded in tr->jobs to
715 : : * the actual list of jobs, if possible. */
716 : :
717 : : /* Reset the generation counter of all installed jobs. The detection of cycles
718 : : * looks at installed jobs. If they had a non-zero generation from some previous
719 : : * walk of the graph, the algorithm would break. */
720 [ + + ]: 228 : HASHMAP_FOREACH(j, m->jobs, i)
721 : 156 : j->generation = 0;
722 : :
723 : : /* First step: figure out which jobs matter */
724 : 72 : transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
725 : :
726 : : /* Second step: Try not to stop any running services if
727 : : * we don't have to. Don't try to reverse running
728 : : * jobs if we don't have to. */
729 [ + + ]: 72 : if (mode == JOB_FAIL)
730 : 24 : transaction_minimize_impact(tr);
731 : :
732 : : /* Third step: Drop redundant jobs */
733 : 72 : transaction_drop_redundant(tr);
734 : :
735 : : for (;;) {
736 : : /* Fourth step: Let's remove unneeded jobs that might
737 : : * be lurking. */
738 [ + - ]: 80 : if (mode != JOB_ISOLATE)
739 : 80 : transaction_collect_garbage(tr);
740 : :
741 : : /* Fifth step: verify order makes sense and correct
742 : : * cycles if necessary and possible */
743 : 80 : r = transaction_verify_order(tr, &generation, e);
744 [ + + ]: 80 : if (r >= 0)
745 : 64 : break;
746 : :
747 [ + + ]: 16 : if (r != -EAGAIN)
748 [ + - ]: 8 : return log_warning_errno(r, "Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r));
749 : :
750 : : /* Let's see if the resulting transaction ordering
751 : : * graph is still cyclic... */
752 : : }
753 : :
754 : : for (;;) {
755 : : /* Sixth step: let's drop unmergeable entries if
756 : : * necessary and possible, merge entries we can
757 : : * merge */
758 : 64 : r = transaction_merge_jobs(tr, e);
759 [ + - ]: 64 : if (r >= 0)
760 : 64 : break;
761 : :
762 [ # # ]: 0 : if (r != -EAGAIN)
763 [ # # ]: 0 : return log_warning_errno(r, "Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r));
764 : :
765 : : /* Seventh step: an entry got dropped, let's garbage
766 : : * collect its dependencies. */
767 [ # # ]: 0 : if (mode != JOB_ISOLATE)
768 : 0 : transaction_collect_garbage(tr);
769 : :
770 : : /* Let's see if the resulting transaction still has
771 : : * unmergeable entries ... */
772 : : }
773 : :
774 : : /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
775 : 64 : transaction_drop_redundant(tr);
776 : :
777 : : /* Ninth step: check whether we can actually apply this */
778 : 64 : r = transaction_is_destructive(tr, mode, e);
779 [ + + ]: 64 : if (r < 0)
780 [ + - ]: 8 : return log_notice_errno(r, "Requested transaction contradicts existing jobs: %s", bus_error_message(e, r));
781 : :
782 : : /* Tenth step: apply changes */
783 : 56 : r = transaction_apply(tr, m, mode, affected_jobs);
784 [ - + ]: 56 : if (r < 0)
785 [ # # ]: 0 : return log_warning_errno(r, "Failed to apply transaction: %m");
786 : :
787 [ - + ]: 56 : assert(hashmap_isempty(tr->jobs));
788 : :
789 [ + - ]: 56 : if (!hashmap_isempty(m->jobs)) {
790 : : /* Are there any jobs now? Then make sure we have the
791 : : * idle pipe around. We don't really care too much
792 : : * whether this works or not, as the idle pipe is a
793 : : * feature for cosmetics, not actually useful for
794 : : * anything beyond that. */
795 : :
796 [ + + + - ]: 56 : if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 &&
797 [ + - + - ]: 36 : m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) {
798 : 36 : (void) pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
799 : 36 : (void) pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
800 : : }
801 : : }
802 : :
803 : 56 : return 0;
804 : : }
805 : :
806 : 1244 : static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) {
807 : : Job *j, *f;
808 : :
809 [ - + ]: 1244 : assert(tr);
810 [ - + ]: 1244 : assert(unit);
811 : :
812 : : /* Looks for an existing prospective job and returns that. If
813 : : * it doesn't exist it is created and added to the prospective
814 : : * jobs list. */
815 : :
816 : 1244 : f = hashmap_get(tr->jobs, unit);
817 : :
818 [ + + ]: 1244 : LIST_FOREACH(transaction, j, f) {
819 [ - + ]: 516 : assert(j->unit == unit);
820 : :
821 [ + - ]: 516 : if (j->type == type) {
822 [ + - ]: 516 : if (is_new)
823 : 516 : *is_new = false;
824 : 516 : return j;
825 : : }
826 : : }
827 : :
828 : 728 : j = job_new(unit, type);
829 [ - + ]: 728 : if (!j)
830 : 0 : return NULL;
831 : :
832 : 728 : j->generation = 0;
833 : 728 : j->marker = NULL;
834 : 728 : j->matters_to_anchor = false;
835 : 728 : j->irreversible = tr->irreversible;
836 : :
837 [ - + - + ]: 728 : LIST_PREPEND(transaction, f, j);
838 : :
839 [ - + ]: 728 : if (hashmap_replace(tr->jobs, unit, f) < 0) {
840 [ # # # # : 0 : LIST_REMOVE(transaction, f, j);
# # # # ]
841 : 0 : job_free(j);
842 : 0 : return NULL;
843 : : }
844 : :
845 [ + - ]: 728 : if (is_new)
846 : 728 : *is_new = true;
847 : :
848 [ + + ]: 728 : log_trace("Added job %s/%s to transaction.", unit->id, job_type_to_string(type));
849 : :
850 : 728 : return j;
851 : : }
852 : :
853 : 728 : static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
854 [ - + ]: 728 : assert(tr);
855 [ - + ]: 728 : assert(j);
856 : :
857 [ - + ]: 728 : if (j->transaction_prev)
858 : 0 : j->transaction_prev->transaction_next = j->transaction_next;
859 [ - + ]: 728 : else if (j->transaction_next)
860 : 0 : hashmap_replace(tr->jobs, j->unit, j->transaction_next);
861 : : else
862 : 728 : hashmap_remove_value(tr->jobs, j->unit, j);
863 : :
864 [ - + ]: 728 : if (j->transaction_next)
865 : 0 : j->transaction_next->transaction_prev = j->transaction_prev;
866 : :
867 : 728 : j->transaction_prev = j->transaction_next = NULL;
868 : :
869 [ + + ]: 974 : while (j->subject_list)
870 : 246 : job_dependency_free(j->subject_list);
871 : :
872 [ + + ]: 1654 : while (j->object_list) {
873 [ + + ]: 926 : Job *other = j->object_list->matters ? j->object_list->subject : NULL;
874 : :
875 : 926 : job_dependency_free(j->object_list);
876 : :
877 [ + + + + ]: 926 : if (other && delete_dependencies) {
878 [ + - ]: 8 : log_unit_debug(other->unit,
879 : : "Deleting job %s/%s as dependency of job %s/%s",
880 : : other->unit->id, job_type_to_string(other->type),
881 : : j->unit->id, job_type_to_string(j->type));
882 : 8 : transaction_delete_job(tr, other, delete_dependencies);
883 : : }
884 : : }
885 : 728 : }
886 : :
887 : 0 : void transaction_add_propagate_reload_jobs(Transaction *tr, Unit *unit, Job *by, bool ignore_order, sd_bus_error *e) {
888 : : Iterator i;
889 : : JobType nt;
890 : : Unit *dep;
891 : : void *v;
892 : : int r;
893 : :
894 [ # # ]: 0 : assert(tr);
895 [ # # ]: 0 : assert(unit);
896 : :
897 [ # # ]: 0 : HASHMAP_FOREACH_KEY(v, dep, unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
898 : 0 : nt = job_type_collapse(JOB_TRY_RELOAD, dep);
899 [ # # ]: 0 : if (nt == JOB_NOP)
900 : 0 : continue;
901 : :
902 : 0 : r = transaction_add_job_and_dependencies(tr, nt, dep, by, false, false, false, ignore_order, e);
903 [ # # ]: 0 : if (r < 0) {
904 [ # # ]: 0 : log_unit_warning(dep,
905 : : "Cannot add dependency reload job, ignoring: %s",
906 : : bus_error_message(e, r));
907 : 0 : sd_bus_error_free(e);
908 : : }
909 : : }
910 : 0 : }
911 : :
912 : 1524 : int transaction_add_job_and_dependencies(
913 : : Transaction *tr,
914 : : JobType type,
915 : : Unit *unit,
916 : : Job *by,
917 : : bool matters,
918 : : bool conflicts,
919 : : bool ignore_requirements,
920 : : bool ignore_order,
921 : : sd_bus_error *e) {
922 : :
923 : : bool is_new;
924 : : Iterator i;
925 : : Unit *dep;
926 : : Job *ret;
927 : : void *v;
928 : : int r;
929 : :
930 [ - + ]: 1524 : assert(tr);
931 [ - + ]: 1524 : assert(type < _JOB_TYPE_MAX);
932 [ - + ]: 1524 : assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
933 [ - + ]: 1524 : assert(unit);
934 : :
935 : : /* Before adding jobs for this unit, let's ensure that its state has been loaded
936 : : * This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()).
937 : : * This way, we "recursively" coldplug units, ensuring that we do not look at state of
938 : : * not-yet-coldplugged units. */
939 [ - + ]: 1524 : if (MANAGER_IS_RELOADING(unit->manager))
940 : 0 : unit_coldplug(unit);
941 : :
942 [ + + ]: 1524 : if (by)
943 [ + + ]: 1452 : log_trace("Pulling in %s/%s from %s/%s", unit->id, job_type_to_string(type), by->unit->id, job_type_to_string(by->type));
944 : :
945 : : /* Safety check that the unit is a valid state, i.e. not in UNIT_STUB or UNIT_MERGED which should only be set
946 : : * temporarily. */
947 [ + - - + ]: 1524 : if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_BAD_SETTING, UNIT_MASKED))
948 : 0 : return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
949 : :
950 [ + + ]: 1524 : if (type != JOB_STOP) {
951 : 984 : r = bus_unit_validate_load_state(unit, e);
952 [ + + ]: 984 : if (r < 0)
953 : 280 : return r;
954 : : }
955 : :
956 [ - + ]: 1244 : if (!unit_job_is_applicable(unit, type))
957 : 0 : return sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE,
958 : : "Job type %s is not applicable for unit %s.",
959 : : job_type_to_string(type), unit->id);
960 : :
961 : : /* First add the job. */
962 : 1244 : ret = transaction_add_one_job(tr, type, unit, &is_new);
963 [ - + ]: 1244 : if (!ret)
964 : 0 : return -ENOMEM;
965 : :
966 [ + - - + ]: 1244 : ret->ignore_order = ret->ignore_order || ignore_order;
967 : :
968 : : /* Then, add a link to the job. */
969 [ + + ]: 1244 : if (by) {
970 [ - + ]: 1172 : if (!job_dependency_new(by, ret, matters, conflicts))
971 : 0 : return -ENOMEM;
972 : : } else {
973 : : /* If the job has no parent job, it is the anchor job. */
974 [ - + ]: 72 : assert(!tr->anchor_job);
975 : 72 : tr->anchor_job = ret;
976 : : }
977 : :
978 [ + + + - : 1244 : if (is_new && !ignore_requirements && type != JOB_NOP) {
+ - ]
979 : : Set *following;
980 : :
981 : : /* If we are following some other unit, make sure we
982 : : * add all dependencies of everybody following. */
983 [ - + ]: 728 : if (unit_following_set(ret->unit, &following) > 0) {
984 [ # # ]: 0 : SET_FOREACH(dep, following, i) {
985 : 0 : r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e);
986 [ # # ]: 0 : if (r < 0) {
987 [ # # # # : 0 : log_unit_full(dep,
# # ]
988 : : r == -ERFKILL ? LOG_INFO : LOG_WARNING,
989 : : r, "Cannot add dependency job, ignoring: %s",
990 : : bus_error_message(e, r));
991 : 0 : sd_bus_error_free(e);
992 : : }
993 : : }
994 : :
995 : 0 : set_free(following);
996 : : }
997 : :
998 : : /* Finally, recursively add in all dependencies. */
999 [ + + + + ]: 728 : if (IN_SET(type, JOB_START, JOB_RESTART)) {
1000 [ + + ]: 928 : HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
1001 : 432 : r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
1002 [ - + ]: 432 : if (r < 0) {
1003 [ # # ]: 0 : if (r != -EBADR) /* job type not applicable */
1004 : 0 : goto fail;
1005 : :
1006 : 0 : sd_bus_error_free(e);
1007 : : }
1008 : : }
1009 : :
1010 [ - + ]: 496 : HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
1011 : 0 : r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
1012 [ # # ]: 0 : if (r < 0) {
1013 [ # # ]: 0 : if (r != -EBADR) /* job type not applicable */
1014 : 0 : goto fail;
1015 : :
1016 : 0 : sd_bus_error_free(e);
1017 : : }
1018 : : }
1019 : :
1020 [ + + ]: 984 : HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_WANTS], i) {
1021 : 488 : r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
1022 [ + + ]: 488 : if (r < 0) {
1023 : : /* unit masked, job type not applicable and unit not found are not considered as errors. */
1024 [ + - + - : 280 : log_unit_full(dep,
+ - # # #
# ]
1025 : : IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
1026 : : r, "Cannot add dependency job, ignoring: %s",
1027 : : bus_error_message(e, r));
1028 : 280 : sd_bus_error_free(e);
1029 : : }
1030 : : }
1031 : :
1032 [ - + ]: 496 : HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
1033 : 0 : r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e);
1034 [ # # ]: 0 : if (r < 0) {
1035 [ # # ]: 0 : if (r != -EBADR) /* job type not applicable */
1036 : 0 : goto fail;
1037 : :
1038 : 0 : sd_bus_error_free(e);
1039 : : }
1040 : : }
1041 : :
1042 [ + + ]: 1016 : HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
1043 : 520 : r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e);
1044 [ - + ]: 520 : if (r < 0) {
1045 [ # # ]: 0 : if (r != -EBADR) /* job type not applicable */
1046 : 0 : goto fail;
1047 : :
1048 : 0 : sd_bus_error_free(e);
1049 : : }
1050 : : }
1051 : :
1052 [ + + ]: 500 : HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
1053 : 4 : r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e);
1054 [ - + ]: 4 : if (r < 0) {
1055 [ # # ]: 0 : log_unit_warning(dep,
1056 : : "Cannot add dependency job, ignoring: %s",
1057 : : bus_error_message(e, r));
1058 : 0 : sd_bus_error_free(e);
1059 : : }
1060 : : }
1061 : :
1062 : : }
1063 : :
1064 [ + + + + ]: 728 : if (IN_SET(type, JOB_STOP, JOB_RESTART)) {
1065 : : static const UnitDependency propagate_deps[] = {
1066 : : UNIT_REQUIRED_BY,
1067 : : UNIT_REQUISITE_OF,
1068 : : UNIT_BOUND_BY,
1069 : : UNIT_CONSISTS_OF,
1070 : : };
1071 : :
1072 : : JobType ptype;
1073 : : unsigned j;
1074 : :
1075 : : /* We propagate STOP as STOP, but RESTART only
1076 : : * as TRY_RESTART, in order not to start
1077 : : * dependencies that are not around. */
1078 [ + + ]: 236 : ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type;
1079 : :
1080 [ + + ]: 1180 : for (j = 0; j < ELEMENTSOF(propagate_deps); j++)
1081 [ + + ]: 952 : HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[propagate_deps[j]], i) {
1082 : : JobType nt;
1083 : :
1084 : 8 : nt = job_type_collapse(ptype, dep);
1085 [ - + ]: 8 : if (nt == JOB_NOP)
1086 : 0 : continue;
1087 : :
1088 : 8 : r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
1089 [ - + ]: 8 : if (r < 0) {
1090 [ # # ]: 0 : if (r != -EBADR) /* job type not applicable */
1091 : 0 : goto fail;
1092 : :
1093 : 0 : sd_bus_error_free(e);
1094 : : }
1095 : : }
1096 : : }
1097 : :
1098 [ - + ]: 728 : if (type == JOB_RELOAD)
1099 : 0 : transaction_add_propagate_reload_jobs(tr, ret->unit, ret, ignore_order, e);
1100 : :
1101 : : /* JOB_VERIFY_ACTIVE requires no dependency handling */
1102 : : }
1103 : :
1104 : 1244 : return 0;
1105 : :
1106 : 0 : fail:
1107 : 0 : return r;
1108 : : }
1109 : :
1110 : 0 : int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
1111 : : Iterator i;
1112 : : Unit *u;
1113 : : char *k;
1114 : : int r;
1115 : :
1116 [ # # ]: 0 : assert(tr);
1117 [ # # ]: 0 : assert(m);
1118 : :
1119 [ # # ]: 0 : HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1120 : :
1121 : : /* ignore aliases */
1122 [ # # ]: 0 : if (u->id != k)
1123 : 0 : continue;
1124 : :
1125 [ # # ]: 0 : if (u->ignore_on_isolate)
1126 : 0 : continue;
1127 : :
1128 : : /* No need to stop inactive jobs */
1129 [ # # # # ]: 0 : if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
1130 : 0 : continue;
1131 : :
1132 : : /* Is there already something listed for this? */
1133 [ # # ]: 0 : if (hashmap_get(tr->jobs, u))
1134 : 0 : continue;
1135 : :
1136 : 0 : r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, NULL);
1137 [ # # ]: 0 : if (r < 0)
1138 [ # # ]: 0 : log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %m");
1139 : : }
1140 : :
1141 : 0 : return 0;
1142 : : }
1143 : :
1144 : 72 : Transaction *transaction_new(bool irreversible) {
1145 : : Transaction *tr;
1146 : :
1147 : 72 : tr = new0(Transaction, 1);
1148 [ - + ]: 72 : if (!tr)
1149 : 0 : return NULL;
1150 : :
1151 : 72 : tr->jobs = hashmap_new(NULL);
1152 [ - + ]: 72 : if (!tr->jobs)
1153 : 0 : return mfree(tr);
1154 : :
1155 : 72 : tr->irreversible = irreversible;
1156 : :
1157 : 72 : return tr;
1158 : : }
1159 : :
1160 : 72 : void transaction_free(Transaction *tr) {
1161 [ - + ]: 72 : assert(hashmap_isempty(tr->jobs));
1162 : 72 : hashmap_free(tr->jobs);
1163 : 72 : free(tr);
1164 : 72 : }
|