]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/transaction.c
Fixes for vscode/intellisense parsing (#38040)
[thirdparty/systemd.git] / src / core / transaction.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
7c0436b9 2
836e4e7e 3#include "sd-bus.h"
5bbad624 4#include "sd-messages.h"
f2b68789 5
b5efdb8a 6#include "alloc-util.h"
b7120388 7#include "ansi-color.h"
96aad8d1 8#include "bus-common-errors.h"
718db961 9#include "bus-error.h"
5cfa33e0 10#include "dbus-unit.h"
4ea4abb6 11#include "manager.h"
836e4e7e 12#include "set.h"
9ca16f6f 13#include "slice.h"
836e4e7e 14#include "string-util.h"
5cfa33e0 15#include "strv.h"
b5efdb8a 16#include "transaction.h"
75778e21
MS
17
18static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
19
20static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) {
21 assert(tr);
22 assert(j);
23
24 /* Deletes one job from the transaction */
25
26 transaction_unlink_job(tr, j, delete_dependencies);
27
d6a093d0 28 job_free(j);
75778e21
MS
29}
30
31static void transaction_delete_unit(Transaction *tr, Unit *u) {
32 Job *j;
33
34 /* Deletes all jobs associated with a certain unit from the
35 * transaction */
36
37 while ((j = hashmap_get(tr->jobs, u)))
38 transaction_delete_job(tr, j, true);
39}
40
7fb1cc85 41static void transaction_abort(Transaction *tr) {
75778e21
MS
42 Job *j;
43
44 assert(tr);
45
46 while ((j = hashmap_first(tr->jobs)))
1b9cea0c 47 transaction_delete_job(tr, j, false);
75778e21
MS
48
49 assert(hashmap_isempty(tr->jobs));
75778e21
MS
50}
51
0d9989aa 52static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
03677889 53 assert(j);
75778e21 54
75778e21
MS
55 /* A recursive sweep through the graph that marks all units
56 * that matter to the anchor job, i.e. are directly or
57 * indirectly a dependency of the anchor job via paths that
58 * are fully marked as mattering. */
59
0d9989aa
MS
60 j->matters_to_anchor = true;
61 j->generation = generation;
75778e21 62
0d9989aa 63 LIST_FOREACH(subject, l, j->subject_list) {
75778e21
MS
64
65 /* This link does not matter */
66 if (!l->matters)
67 continue;
68
69 /* This unit has already been marked */
70 if (l->object->generation == generation)
71 continue;
72
0d9989aa 73 transaction_find_jobs_that_matter_to_anchor(l->object, generation);
75778e21
MS
74 }
75}
76
77static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) {
03677889 78 JobDependency *last;
75778e21
MS
79
80 assert(j);
81 assert(other);
82 assert(j->unit == other->unit);
83 assert(!j->installed);
84
85 /* Merges 'other' into 'j' and then deletes 'other'. */
86
87 j->type = t;
88 j->state = JOB_WAITING;
23ade460 89 j->irreversible = j->irreversible || other->irreversible;
75778e21
MS
90 j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
91
92 /* Patch us in as new owner of the JobDependency objects */
93 last = NULL;
94 LIST_FOREACH(subject, l, other->subject_list) {
95 assert(l->subject == other);
96 l->subject = j;
97 last = l;
98 }
99
100 /* Merge both lists */
101 if (last) {
102 last->subject_next = j->subject_list;
103 if (j->subject_list)
104 j->subject_list->subject_prev = last;
105 j->subject_list = other->subject_list;
106 }
107
108 /* Patch us in as new owner of the JobDependency objects */
109 last = NULL;
110 LIST_FOREACH(object, l, other->object_list) {
111 assert(l->object == other);
112 l->object = j;
113 last = l;
114 }
115
116 /* Merge both lists */
117 if (last) {
118 last->object_next = j->object_list;
119 if (j->object_list)
120 j->object_list->object_prev = last;
121 j->object_list = other->object_list;
122 }
123
124 /* Kill the other job */
125 other->subject_list = NULL;
126 other->object_list = NULL;
127 transaction_delete_job(tr, other, true);
128}
129
d1e8e8b5 130static bool job_is_conflicted_by(Job *j) {
75778e21
MS
131 assert(j);
132
133 /* Returns true if this job is pulled in by a least one
134 * ConflictedBy dependency. */
135
136 LIST_FOREACH(object, l, j->object_list)
137 if (l->conflicts)
138 return true;
139
140 return false;
141}
142
03677889
YW
143static int delete_one_unmergeable_job(Transaction *tr, Job *job) {
144 assert(job);
75778e21
MS
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 */
03677889 153 LIST_FOREACH(transaction, j, job)
75778e21
MS
154 LIST_FOREACH(transaction, k, j->transaction_next) {
155 Job *d;
156
157 /* Is this one mergeable? Then skip it */
158 if (job_type_is_mergeable(j->type, k->type))
159 continue;
160
161 /* Ok, we found two that conflict, let's see if we can
162 * drop one of them */
163 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
f2341e0a 174 log_unit_debug(j->unit,
66870f90
ZJS
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)));
f2341e0a 178 log_unit_debug(k->unit,
66870f90
ZJS
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)));
75778e21
MS
182
183 if (j->type == JOB_STOP) {
184
185 if (job_is_conflicted_by(j))
186 d = k;
187 else
188 d = j;
189
190 } else if (k->type == JOB_STOP) {
191
192 if (job_is_conflicted_by(k))
193 d = j;
194 else
195 d = k;
196 } else
197 d = j;
198
199 } else if (!j->matters_to_anchor)
200 d = j;
201 else if (!k->matters_to_anchor)
202 d = k;
203 else
204 return -ENOEXEC;
205
206 /* Ok, we can drop one, so let's do so. */
f2341e0a 207 log_unit_debug(d->unit,
75cb8502
ZJS
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),
66870f90 211 d->unit->id, job_type_to_string(d->type));
75778e21
MS
212 transaction_delete_job(tr, d, true);
213 return 0;
214 }
215
216 return -EINVAL;
217}
218
718db961 219static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) {
75778e21 220 Job *j;
75778e21
MS
221 int r;
222
223 assert(tr);
224
225 /* First step, check whether any of the jobs for one specific
226 * task conflict. If so, try to drop one of them. */
90e74a66 227 HASHMAP_FOREACH(j, tr->jobs) {
75778e21 228 JobType t;
75778e21
MS
229
230 t = j->type;
231 LIST_FOREACH(transaction, k, j->transaction_next) {
e0209d83 232 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
75778e21
MS
233 continue;
234
235 /* OK, we could not merge all jobs for this
236 * action. Let's see if we can get rid of one
237 * of them */
238
239 r = delete_one_unmergeable_job(tr, j);
240 if (r >= 0)
241 /* Ok, we managed to drop one, now
242 * let's ask our callers to call us
243 * again after garbage collecting */
244 return -EAGAIN;
245
246 /* We couldn't merge anything. Failure */
7358dc02
ZJS
247 return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING,
248 "Transaction contains conflicting jobs '%s' and '%s' for %s. "
249 "Probably contradicting requirement dependencies configured.",
250 job_type_to_string(t),
251 job_type_to_string(k->type),
252 k->unit->id);
75778e21
MS
253 }
254 }
255
256 /* Second step, merge the jobs. */
90e74a66 257 HASHMAP_FOREACH(j, tr->jobs) {
75778e21 258 JobType t = j->type;
75778e21 259
e0209d83 260 /* Merge all transaction jobs for j->unit */
75778e21 261 LIST_FOREACH(transaction, k, j->transaction_next)
e0209d83 262 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
75778e21 263
03677889 264 Job *k;
75778e21 265 while ((k = j->transaction_next)) {
656bbffc 266 if (tr->anchor_job == k) {
75778e21
MS
267 transaction_merge_and_delete_job(tr, k, j, t);
268 j = k;
269 } else
270 transaction_merge_and_delete_job(tr, j, k, t);
271 }
272
75778e21
MS
273 assert(!j->transaction_next);
274 assert(!j->transaction_prev);
275 }
276
277 return 0;
278}
279
cc479760 280static void transaction_drop_redundant(Transaction *tr) {
ca006fc6 281 bool again;
75778e21 282
ca006fc6
LP
283 /* Goes through the transaction and removes all jobs of the units whose jobs are all noops. If not
284 * all of a unit's jobs are redundant, they are kept. */
75778e21 285
055163ad 286 assert(tr);
75778e21 287
ca006fc6 288 do {
ca006fc6
LP
289 Job *j;
290
291 again = false;
292
90e74a66 293 HASHMAP_FOREACH(j, tr->jobs) {
ca006fc6 294 bool keep = false;
ca006fc6
LP
295
296 LIST_FOREACH(transaction, k, j)
297 if (tr->anchor_job == k ||
cc479760 298 !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
ca006fc6
LP
299 (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) {
300 keep = true;
301 break;
302 }
303
304 if (!keep) {
305 log_trace("Found redundant job %s/%s, dropping from transaction.",
306 j->unit->id, job_type_to_string(j->type));
307 transaction_delete_job(tr, j, false);
308 again = true;
309 break;
310 }
75778e21 311 }
ca006fc6 312 } while (again);
75778e21
MS
313}
314
d1e8e8b5 315static bool job_matters_to_anchor(Job *job) {
03677889
YW
316 assert(job);
317 assert(!job->transaction_prev);
75778e21 318
735f0645 319 /* Checks whether at least one of the jobs for this transaction matters to the anchor. */
75778e21 320
03677889 321 LIST_FOREACH(transaction, j, job)
75778e21
MS
322 if (j->matters_to_anchor)
323 return true;
324
325 return false;
326}
327
718db961 328static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, sd_bus_error *e) {
15ed3c3a
LP
329
330 static const UnitDependencyAtom directions[] = {
331 UNIT_ATOM_BEFORE,
332 UNIT_ATOM_AFTER,
dfd79eca 333 };
15ed3c3a
LP
334
335 int r;
75778e21
MS
336
337 assert(tr);
338 assert(j);
339 assert(!j->transaction_prev);
340
defe63b0
LP
341 /* Does a recursive sweep through the ordering graph, looking for a cycle. If we find a cycle we try
342 * to break it. */
75778e21
MS
343
344 /* Have we seen this before? */
345 if (j->generation == generation) {
3aa5e91b 346 _cleanup_free_ char **array = NULL;
1cce8cbf 347 Job *delete = NULL;
75778e21 348
defe63b0
LP
349 /* If the marker is NULL we have been here already and decided the job was loop-free from
350 * here. Hence shortcut things and return right-away. */
75778e21
MS
351 if (!j->marker)
352 return 0;
353
defe63b0
LP
354 /* So, the marker is not NULL and we already have been here. We have a cycle. Let's try to
355 * break it. We go backwards in our path and try to find a suitable job to remove. We use the
356 * marker to find our way back, since smart how we are we stored our way back in there. */
1cce8cbf 357 for (Job *k = from; k; k = (k->generation == generation && k->marker != k) ? k->marker : NULL) {
75778e21 358
924775e8
ZJS
359 /* For logging below */
360 if (strv_push_pair(&array, k->unit->id, (char*) job_type_to_string(k->type)) < 0)
6650e213 361 (void) log_oom_warning();
75778e21 362
6112c861 363 if (!delete && hashmap_contains(tr->jobs, k->unit) && !job_matters_to_anchor(k))
924775e8 364 /* Ok, we can drop this one, so let's do so. */
75778e21 365 delete = k;
75778e21 366
924775e8 367 /* Check if this in fact was the beginning of the cycle */
75778e21
MS
368 if (k == j)
369 break;
370 }
371
3aa5e91b
MY
372 _cleanup_free_ char *unit_ids = NULL;
373 STRV_FOREACH_PAIR(unit_id, job_type, array)
374 (void) strextendf_with_separator(&unit_ids, "\n", "%s%s", unit_log_field(j->unit), *unit_id);
201647e3
LP
375
376 _cleanup_free_ char *cycle_path_text = strdup("Found ordering cycle");
6912eb31
MY
377 if (!strv_isempty(array)) {
378 (void) strextendf(&cycle_path_text, ": %s/%s", array[0], array[1]);
201647e3 379
6912eb31
MY
380 STRV_FOREACH_PAIR(unit_id, job_type, strv_skip(array, 2))
381 (void) strextendf(&cycle_path_text, " after %s/%s", *unit_id, *job_type);
201647e3 382
6912eb31
MY
383 (void) strextendf(&cycle_path_text, " - after %s", array[0]);
384 }
201647e3
LP
385
386 /* logging for j not k here to provide a consistent narrative */
387 if (cycle_path_text)
fe458ad6 388 log_struct(LOG_ERR,
201647e3 389 LOG_UNIT_MESSAGE(j->unit, "%s", cycle_path_text),
5bbad624 390 LOG_MESSAGE_ID(SD_MESSAGE_UNIT_ORDERING_CYCLE_STR),
1c8d653d 391 LOG_ITEM("%s", strempty(unit_ids)));
75778e21
MS
392
393 if (delete) {
dc9b5816 394 const char *status;
924775e8 395 /* logging for j not k here to provide a consistent narrative */
fe458ad6 396 log_struct(LOG_WARNING,
f66020ab
ZJS
397 LOG_UNIT_MESSAGE(j->unit,
398 "Job %s/%s deleted to break ordering cycle starting with %s/%s",
399 delete->unit->id, job_type_to_string(delete->type),
400 j->unit->id, job_type_to_string(j->type)),
5bbad624 401 LOG_MESSAGE_ID(SD_MESSAGE_DELETING_JOB_BECAUSE_ORDERING_CYCLE_STR),
becbd2ec
LP
402 LOG_ITEM("DELETED_UNIT=%s", delete->unit->id),
403 LOG_ITEM("DELETED_TYPE=%s", job_type_to_string(delete->type)),
1c8d653d 404 LOG_ITEM("%s", strempty(unit_ids)));
dc9b5816
ZJS
405
406 if (log_get_show_color())
407 status = ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL;
408 else
409 status = " SKIP ";
410
5bcf34eb
ZJS
411 unit_status_printf(delete->unit,
412 STATUS_TYPE_NOTICE,
413 status,
04d232d8
ZJS
414 "Ordering cycle found, skipping %s",
415 unit_status_string(delete->unit, NULL));
75778e21
MS
416 transaction_delete_unit(tr, delete->unit);
417 return -EAGAIN;
418 }
419
924775e8 420 log_struct(LOG_ERR,
f66020ab
ZJS
421 LOG_UNIT_MESSAGE(j->unit, "Unable to break cycle starting with %s/%s",
422 j->unit->id, job_type_to_string(j->type)),
5bbad624 423 LOG_MESSAGE_ID(SD_MESSAGE_CANT_BREAK_ORDERING_CYCLE_STR),
1c8d653d 424 LOG_ITEM("%s", strempty(unit_ids)));
75778e21 425
7358dc02
ZJS
426 return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
427 "Transaction order is cyclic. See system logs for details.");
75778e21
MS
428 }
429
430 /* Make the marker point to where we come from, so that we can
431 * find our way backwards if we want to break a cycle. We use
432 * a special marker for the beginning: we point to
433 * ourselves. */
1da3cb81 434 j->marker = from ?: j;
75778e21
MS
435 j->generation = generation;
436
dfd79eca
MK
437 /* Actual ordering of jobs depends on the unit ordering dependency and job types. We need to traverse
438 * the graph over 'before' edges in the actual job execution order. We traverse over both unit
439 * ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
440 * execution ordering. */
85471164 441 FOREACH_ELEMENT(d, directions) {
15ed3c3a
LP
442 Unit *u;
443
8b317c34 444 UNIT_FOREACH_DEPENDENCY(u, j->unit, *d) {
dfd79eca
MK
445 Job *o;
446
447 /* Is there a job for this unit? */
448 o = hashmap_get(tr->jobs, u);
449 if (!o) {
defe63b0
LP
450 /* Ok, there is no job for this in the transaction, but maybe there is
451 * already one running? */
dfd79eca
MK
452 o = u->job;
453 if (!o)
454 continue;
455 }
456
457 /* Cut traversing if the job j is not really *before* o. */
8b317c34 458 if (job_compare(j, o, *d) >= 0)
75778e21 459 continue;
75778e21 460
dfd79eca
MK
461 r = transaction_verify_order_one(tr, o, j, generation, e);
462 if (r < 0)
463 return r;
464 }
75778e21
MS
465 }
466
467 /* Ok, let's backtrack, and remember that this entry is not on
468 * our path anymore. */
469 j->marker = NULL;
470
471 return 0;
472}
473
718db961 474static int transaction_verify_order(Transaction *tr, unsigned *generation, sd_bus_error *e) {
75778e21
MS
475 Job *j;
476 int r;
75778e21
MS
477 unsigned g;
478
479 assert(tr);
480 assert(generation);
481
482 /* Check if the ordering graph is cyclic. If it is, try to fix
483 * that up by dropping one of the jobs. */
484
485 g = (*generation)++;
486
90e74a66 487 HASHMAP_FOREACH(j, tr->jobs) {
3cc2aff1
LP
488 r = transaction_verify_order_one(tr, j, NULL, g, e);
489 if (r < 0)
75778e21 490 return r;
3cc2aff1 491 }
75778e21
MS
492
493 return 0;
494}
495
496static void transaction_collect_garbage(Transaction *tr) {
ca006fc6 497 bool again;
75778e21
MS
498
499 assert(tr);
500
501 /* Drop jobs that are not required by any other job */
502
ca006fc6 503 do {
ca006fc6
LP
504 Job *j;
505
506 again = false;
507
90e74a66 508 HASHMAP_FOREACH(j, tr->jobs) {
ca006fc6
LP
509 if (tr->anchor_job == j)
510 continue;
511
512 if (!j->object_list) {
513 log_trace("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type));
514 transaction_delete_job(tr, j, true);
515 again = true;
516 break;
517 }
518
78218e62
JK
519 log_trace("Keeping job %s/%s because of %s/%s",
520 j->unit->id, job_type_to_string(j->type),
521 j->object_list->subject ? j->object_list->subject->unit->id : "root",
522 j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root");
75778e21
MS
523 }
524
ca006fc6 525 } while (again);
75778e21
MS
526}
527
718db961 528static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_error *e) {
75778e21
MS
529 Job *j;
530
531 assert(tr);
532
533 /* Checks whether applying this transaction means that
534 * existing jobs would be replaced */
535
90e74a66 536 HASHMAP_FOREACH(j, tr->jobs) {
75778e21
MS
537
538 /* Assume merged */
539 assert(!j->transaction_prev);
540 assert(!j->transaction_next);
541
1b9400f2 542 if (j->unit->job && (IN_SET(mode, JOB_FAIL, JOB_LENIENT) || j->unit->job->irreversible) &&
c21b92ff 543 job_type_is_conflicting(j->unit->job->type, j->type))
7358dc02 544 return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
cf99f8ea
LP
545 "Transaction for %s/%s is destructive (%s has '%s' job queued, but '%s' is included in transaction).",
546 tr->anchor_job->unit->id, job_type_to_string(tr->anchor_job->type),
547 j->unit->id, job_type_to_string(j->unit->job->type), job_type_to_string(j->type));
75778e21
MS
548 }
549
550 return 0;
551}
552
1b9400f2 553static int transaction_minimize_impact(Transaction *tr, JobMode mode, sd_bus_error *e) {
03677889 554 Job *head;
055163ad 555
75778e21
MS
556 assert(tr);
557
558 /* Drops all unnecessary jobs that reverse already active jobs
559 * or that stop a running service. */
560
1b9400f2
MY
561 if (!IN_SET(mode, JOB_FAIL, JOB_LENIENT))
562 return 0;
563
055163ad 564rescan:
03677889
YW
565 HASHMAP_FOREACH(head, tr->jobs) {
566 LIST_FOREACH(transaction, j, head) {
055163ad 567 bool stops_running_service, changes_existing_job;
75778e21 568
055163ad 569 /* If it matters, we shouldn't drop it */
1b9400f2 570 if (j->matters_to_anchor && mode != JOB_LENIENT)
055163ad 571 continue;
75778e21 572
055163ad
MS
573 /* Would this stop a running service?
574 * Would this change an existing job?
575 * If so, let's drop this entry */
75778e21 576
055163ad
MS
577 stops_running_service =
578 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
75778e21 579
055163ad
MS
580 changes_existing_job =
581 j->unit->job &&
582 job_type_is_conflicting(j->type, j->unit->job->type);
75778e21 583
055163ad
MS
584 if (!stops_running_service && !changes_existing_job)
585 continue;
75778e21 586
1b9400f2
MY
587 if (j->matters_to_anchor) {
588 assert(mode == JOB_LENIENT);
589 return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
590 "%s/%s would stop a running unit or change existing job, bailing",
591 j->unit->id, job_type_to_string(j->type));
592 }
593
055163ad 594 if (stops_running_service)
f2341e0a 595 log_unit_debug(j->unit,
66870f90
ZJS
596 "%s/%s would stop a running service.",
597 j->unit->id, job_type_to_string(j->type));
75778e21 598
055163ad 599 if (changes_existing_job)
f2341e0a 600 log_unit_debug(j->unit,
66870f90
ZJS
601 "%s/%s would change existing job.",
602 j->unit->id, job_type_to_string(j->type));
75778e21 603
055163ad 604 /* Ok, let's get rid of this */
f2341e0a 605 log_unit_debug(j->unit,
66870f90
ZJS
606 "Deleting %s/%s to minimize impact.",
607 j->unit->id, job_type_to_string(j->type));
75778e21 608
055163ad
MS
609 transaction_delete_job(tr, j, true);
610 goto rescan;
75778e21 611 }
055163ad 612 }
1b9400f2
MY
613
614 return 0;
75778e21
MS
615}
616
50cbaba4
LP
617static int transaction_apply(
618 Transaction *tr,
619 Manager *m,
620 JobMode mode,
621 Set *affected_jobs) {
622
75778e21
MS
623 Job *j;
624 int r;
625
569269d0
MY
626 assert(tr);
627 assert(m);
628
75778e21
MS
629 /* Moves the transaction jobs to the set of active jobs */
630
3742095b 631 if (IN_SET(mode, JOB_ISOLATE, JOB_FLUSH)) {
75778e21
MS
632
633 /* When isolating first kill all installed jobs which
634 * aren't part of the new transaction */
90e74a66 635 HASHMAP_FOREACH(j, m->jobs) {
75778e21
MS
636 assert(j->installed);
637
3835b9aa 638 if (j->unit->ignore_on_isolate)
2de0b9e9
MO
639 continue;
640
6112c861 641 if (hashmap_contains(tr->jobs, j->unit))
75778e21
MS
642 continue;
643
5273510e
MS
644 /* Not invalidating recursively. Avoids triggering
645 * OnFailure= actions of dependent jobs. Also avoids
646 * invalidating our iterator. */
833f92ad 647 job_finish_and_invalidate(j, JOB_CANCELED, false, false);
75778e21
MS
648 }
649 }
650
90e74a66 651 HASHMAP_FOREACH(j, tr->jobs) {
75778e21
MS
652 /* Assume merged */
653 assert(!j->transaction_prev);
654 assert(!j->transaction_next);
655
acf56b72 656 r = hashmap_ensure_put(&m->jobs, NULL, UINT32_TO_PTR(j->id), j);
75778e21
MS
657 if (r < 0)
658 goto rollback;
659 }
660
661 while ((j = hashmap_steal_first(tr->jobs))) {
656bbffc
MS
662 Job *installed_job;
663
75778e21
MS
664 /* Clean the job dependencies */
665 transaction_unlink_job(tr, j, false);
666
569269d0 667 installed_job = job_install(j);
656bbffc
MS
668 if (installed_job != j) {
669 /* j has been merged into a previously installed job */
670 if (tr->anchor_job == j)
671 tr->anchor_job = installed_job;
39939e7d 672
c49dfd89 673 hashmap_remove_value(m->jobs, UINT32_TO_PTR(j->id), j);
39939e7d 674 free_and_replace_full(j, installed_job, job_free);
656bbffc 675 }
05d576f1 676
75778e21
MS
677 job_add_to_run_queue(j);
678 job_add_to_dbus_queue(j);
a2df3ea4 679 job_start_timer(j, false);
c65eb836 680 job_shutdown_magic(j);
50cbaba4
LP
681
682 /* When 'affected' is specified, let's track all in it all jobs that were touched because of
683 * this transaction. */
684 if (affected_jobs)
685 (void) set_put(affected_jobs, j);
75778e21
MS
686 }
687
75778e21
MS
688 return 0;
689
690rollback:
691
90e74a66 692 HASHMAP_FOREACH(j, tr->jobs)
c49dfd89 693 hashmap_remove_value(m->jobs, UINT32_TO_PTR(j->id), j);
75778e21
MS
694
695 return r;
696}
697
50cbaba4
LP
698int transaction_activate(
699 Transaction *tr,
700 Manager *m,
701 JobMode mode,
702 Set *affected_jobs,
703 sd_bus_error *e) {
704
4e7bd268 705 Job *j;
75778e21
MS
706 int r;
707 unsigned generation = 1;
708
569269d0 709 /* This applies the changes recorded in tr->jobs to the actual list of jobs, if possible. */
75778e21 710
569269d0
MY
711 assert(tr);
712 assert(m);
75778e21 713
4e7bd268
MS
714 /* Reset the generation counter of all installed jobs. The detection of cycles
715 * looks at installed jobs. If they had a non-zero generation from some previous
716 * walk of the graph, the algorithm would break. */
90e74a66 717 HASHMAP_FOREACH(j, m->jobs)
4e7bd268
MS
718 j->generation = 0;
719
75778e21 720 /* First step: figure out which jobs matter */
0d9989aa 721 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
75778e21 722
1b9400f2
MY
723 /* Second step: Try not to stop any running services if we don't have to. Don't try to reverse
724 * running jobs if we don't have to. */
725 r = transaction_minimize_impact(tr, mode, e);
726 if (r < 0)
727 return r; /* Note that we don't log here, because for JOB_LENIENT conflicts are very much expected
728 and shouldn't appear to be fatal for the unit. Only inform the caller via bus error. */
75778e21
MS
729
730 /* Third step: Drop redundant jobs */
cc479760 731 transaction_drop_redundant(tr);
75778e21
MS
732
733 for (;;) {
734 /* Fourth step: Let's remove unneeded jobs that might
735 * be lurking. */
736 if (mode != JOB_ISOLATE)
737 transaction_collect_garbage(tr);
738
739 /* Fifth step: verify order makes sense and correct
740 * cycles if necessary and possible */
741 r = transaction_verify_order(tr, &generation, e);
742 if (r >= 0)
743 break;
4ae25393
YW
744 if (r != -EAGAIN)
745 return log_warning_errno(r, "Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r));
75778e21
MS
746
747 /* Let's see if the resulting transaction ordering
748 * graph is still cyclic... */
749 }
750
751 for (;;) {
752 /* Sixth step: let's drop unmergeable entries if
753 * necessary and possible, merge entries we can
754 * merge */
755 r = transaction_merge_jobs(tr, e);
756 if (r >= 0)
757 break;
4ae25393
YW
758 if (r != -EAGAIN)
759 return log_warning_errno(r, "Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r));
75778e21
MS
760
761 /* Seventh step: an entry got dropped, let's garbage
762 * collect its dependencies. */
763 if (mode != JOB_ISOLATE)
764 transaction_collect_garbage(tr);
765
766 /* Let's see if the resulting transaction still has
767 * unmergeable entries ... */
768 }
769
770 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
cc479760 771 transaction_drop_redundant(tr);
75778e21
MS
772
773 /* Ninth step: check whether we can actually apply this */
23ade460 774 r = transaction_is_destructive(tr, mode, e);
4ae25393
YW
775 if (r < 0)
776 return log_notice_errno(r, "Requested transaction contradicts existing jobs: %s", bus_error_message(e, r));
75778e21
MS
777
778 /* Tenth step: apply changes */
50cbaba4 779 r = transaction_apply(tr, m, mode, affected_jobs);
23bbb0de
MS
780 if (r < 0)
781 return log_warning_errno(r, "Failed to apply transaction: %m");
75778e21
MS
782
783 assert(hashmap_isempty(tr->jobs));
75778e21 784
06044356
LP
785 /* Are there any jobs now? Then make sure we have the idle pipe around. We don't really care too much
786 * whether this works or not, as the idle pipe is a feature for cosmetics, not actually useful for
787 * anything beyond that. */
788 if (!hashmap_isempty(m->jobs))
789 (void) manager_allocate_idle_pipe(m);
f2b68789 790
75778e21
MS
791 return 0;
792}
793
4bd29fe5 794static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) {
75778e21
MS
795 Job *j, *f;
796
797 assert(tr);
798 assert(unit);
799
800 /* Looks for an existing prospective job and returns that. If
801 * it doesn't exist it is created and added to the prospective
802 * jobs list. */
803
804 f = hashmap_get(tr->jobs, unit);
805
03677889
YW
806 LIST_FOREACH(transaction, i, f) {
807 assert(i->unit == unit);
75778e21 808
03677889 809 if (i->type == type) {
75778e21
MS
810 if (is_new)
811 *is_new = false;
03677889 812 return i;
75778e21
MS
813 }
814 }
815
3c956cfe
MS
816 j = job_new(unit, type);
817 if (!j)
818 return NULL;
75778e21 819
23ade460 820 j->irreversible = tr->irreversible;
75778e21 821
71fda00f 822 LIST_PREPEND(transaction, f, j);
75778e21
MS
823
824 if (hashmap_replace(tr->jobs, unit, f) < 0) {
71fda00f 825 LIST_REMOVE(transaction, f, j);
75778e21
MS
826 job_free(j);
827 return NULL;
828 }
829
830 if (is_new)
831 *is_new = true;
832
78218e62 833 log_trace("Added job %s/%s to transaction.", unit->id, job_type_to_string(type));
75778e21
MS
834
835 return j;
836}
837
838static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
839 assert(tr);
840 assert(j);
841
842 if (j->transaction_prev)
843 j->transaction_prev->transaction_next = j->transaction_next;
844 else if (j->transaction_next)
845 hashmap_replace(tr->jobs, j->unit, j->transaction_next);
846 else
847 hashmap_remove_value(tr->jobs, j->unit, j);
848
849 if (j->transaction_next)
850 j->transaction_next->transaction_prev = j->transaction_prev;
851
852 j->transaction_prev = j->transaction_next = NULL;
853
854 while (j->subject_list)
e6eda1f2 855 job_dependency_free(j->subject_list);
75778e21
MS
856
857 while (j->object_list) {
858 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
859
e6eda1f2 860 job_dependency_free(j->object_list);
75778e21
MS
861
862 if (other && delete_dependencies) {
f2341e0a 863 log_unit_debug(other->unit,
66870f90
ZJS
864 "Deleting job %s/%s as dependency of job %s/%s",
865 other->unit->id, job_type_to_string(other->type),
866 j->unit->id, job_type_to_string(j->type));
75778e21
MS
867 transaction_delete_job(tr, other, delete_dependencies);
868 }
869 }
870}
871
b0904249
LP
872void transaction_add_propagate_reload_jobs(
873 Transaction *tr,
874 Unit *unit,
875 Job *by,
3044510d 876 TransactionAddFlags flags) {
b0904249 877
15d167f8 878 JobType nt;
eef85c4a 879 Unit *dep;
15d167f8
JW
880 int r;
881
882 assert(tr);
883 assert(unit);
884
b7777d08 885 UNIT_FOREACH_DEPENDENCY_SAFE(dep, unit, UNIT_ATOM_PROPAGATES_RELOAD_TO) {
3044510d
LP
886 _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
887
15d167f8
JW
888 nt = job_type_collapse(JOB_TRY_RELOAD, dep);
889 if (nt == JOB_NOP)
890 continue;
891
3044510d
LP
892 r = transaction_add_job_and_dependencies(tr, nt, dep, by, flags, &e);
893 if (r < 0)
15d167f8
JW
894 log_unit_warning(dep,
895 "Cannot add dependency reload job, ignoring: %s",
3044510d 896 bus_error_message(&e, r));
15d167f8
JW
897 }
898}
899
4893902b
MY
900static JobType job_type_propagate_stop_graceful(Job *j) {
901 JobType type;
902
903 if (!j)
904 return JOB_STOP;
905
906 type = JOB_STOP;
907
908 LIST_FOREACH(transaction, i, j)
909 switch (i->type) {
910
911 case JOB_STOP:
912 case JOB_RESTART:
913 /* Nothing to worry about, an appropriate job is in-place */
914 return JOB_NOP;
915
916 case JOB_START:
917 /* This unit is pulled in by other dependency types in this transaction. We will run
918 * into job type conflict if we enqueue a stop job, so let's enqueue a restart job
919 * instead. */
920 type = JOB_RESTART;
921 break;
922
923 default: /* We don't care about others */
924 ;
925
926 }
927
928 return type;
929}
930
75778e21
MS
931int transaction_add_job_and_dependencies(
932 Transaction *tr,
933 JobType type,
934 Unit *unit,
935 Job *by,
b0904249 936 TransactionAddFlags flags,
718db961 937 sd_bus_error *e) {
eef85c4a
LP
938
939 bool is_new;
fc49b299 940 Job *job;
75778e21 941 int r;
75778e21
MS
942
943 assert(tr);
569269d0 944 assert(type >= 0);
75778e21 945 assert(type < _JOB_TYPE_MAX);
e0209d83 946 assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
75778e21
MS
947 assert(unit);
948
fe51a614
YW
949 /* Before adding jobs for this unit, let's ensure that its state has been loaded. This matters when
950 * jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()). This way, we
c25eeb65
LP
951 * "recursively" coldplug units, ensuring that we do not look at state of not-yet-coldplugged
952 * units. */
2c289ea8 953 if (MANAGER_IS_RELOADING(unit->manager))
43706330
IS
954 unit_coldplug(unit);
955
78218e62
JK
956 if (by)
957 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));
75778e21 958
c4555ad8
LP
959 /* Safety check that the unit is a valid state, i.e. not in UNIT_STUB or UNIT_MERGED which should only be set
960 * temporarily. */
0377cd29 961 if (!UNIT_IS_LOAD_COMPLETE(unit->load_state))
c6497ccb 962 return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
75778e21 963
ee87525c 964 if (type != JOB_STOP) {
93df5217 965 /* The time-based cache allows new units to be started without daemon-reload, but if they are
c25eeb65
LP
966 * already referenced (because of dependencies or ordering) then we have to force a load of
967 * the fragment. As an optimization, check first if anything in the usual paths was modified
968 * since the last time the cache was loaded. Also check if the last time an attempt to load
969 * the unit was made was before the most recent cache refresh, so that we know we need to try
970 * again — even if the cache is current, it might have been updated in a different context
971 * before we had a chance to retry loading this particular unit.
81be2388 972 *
cda66772
LB
973 * Given building up the transaction is a synchronous operation, attempt
974 * to load the unit immediately. */
7ad2e660
YW
975 if (manager_unit_cache_should_retry_load(unit)) {
976 assert(unit->load_state == UNIT_NOT_FOUND);
cda66772 977 unit->load_state = UNIT_STUB;
9b6aa9e4
YW
978 unit->load_error = 0;
979 (void) unit_load(unit);
980 assert(unit->load_state != UNIT_STUB);
cda66772 981 }
7ad2e660
YW
982
983 r = bus_unit_validate_load_state(unit, e);
ee87525c
FB
984 if (r < 0)
985 return r;
75778e21
MS
986 }
987
7358dc02
ZJS
988 if (!unit_job_is_applicable(unit, type))
989 return sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE,
990 "Job type %s is not applicable for unit %s.",
991 job_type_to_string(type), unit->id);
75778e21 992
9ca16f6f
LP
993 if (type == JOB_START) {
994 /* The hard concurrency limit for slice units we already enforce when a job is enqueued */
995 Slice *slice = SLICE(UNIT_GET_SLICE(unit));
996 if (slice && slice_concurrency_hard_max_reached(slice, unit))
997 return sd_bus_error_setf(
998 e, BUS_ERROR_CONCURRENCY_LIMIT_REACHED,
999 "Concurrency limit of the slice unit '%s' (or any of its parents) the unit '%s' is contained in has been reached, refusing start job.",
1000 UNIT(slice)->id, unit->id);
1001 }
1002
75778e21 1003 /* First add the job. */
fc49b299
YW
1004 job = transaction_add_one_job(tr, type, unit, &is_new);
1005 if (!job)
75778e21
MS
1006 return -ENOMEM;
1007
b0904249 1008 if (FLAGS_SET(flags, TRANSACTION_IGNORE_ORDER))
fc49b299 1009 job->ignore_order = true;
75778e21
MS
1010
1011 /* Then, add a link to the job. */
e6eda1f2 1012 if (by) {
fc49b299 1013 if (!job_dependency_new(by, job, FLAGS_SET(flags, TRANSACTION_MATTERS), FLAGS_SET(flags, TRANSACTION_CONFLICTS)))
e6eda1f2
MS
1014 return -ENOMEM;
1015 } else {
1016 /* If the job has no parent job, it is the anchor job. */
4483f694 1017 assert(!tr->anchor_job);
fc49b299 1018 tr->anchor_job = job;
569269d0
MY
1019
1020 if (FLAGS_SET(flags, TRANSACTION_REENQUEUE_ANCHOR))
fc49b299 1021 job->refuse_late_merge = true;
b94fbd30 1022 }
e0209d83 1023
87d17581
MY
1024 if (!is_new || FLAGS_SET(flags, TRANSACTION_IGNORE_REQUIREMENTS) || type == JOB_NOP)
1025 return 0;
1026
1027 _cleanup_set_free_ Set *following = NULL;
1028 Unit *dep;
1029
1030 /* If we are following some other unit, make sure we add all dependencies of everybody following. */
fc49b299 1031 if (unit_following_set(job->unit, &following) > 0)
87d17581 1032 SET_FOREACH(dep, following) {
fc49b299 1033 r = transaction_add_job_and_dependencies(tr, type, dep, job, flags & TRANSACTION_IGNORE_ORDER, e);
87d17581
MY
1034 if (r < 0) {
1035 log_unit_full_errno(dep, r == -ERFKILL ? LOG_INFO : LOG_WARNING, r,
1036 "Cannot add dependency job, ignoring: %s",
1037 bus_error_message(e, r));
1038 sd_bus_error_free(e);
75778e21 1039 }
87d17581 1040 }
75778e21 1041
87d17581
MY
1042 /* Finally, recursively add in all dependencies. */
1043 if (IN_SET(type, JOB_START, JOB_RESTART)) {
b7777d08 1044 UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_START) {
fc49b299 1045 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
87d17581
MY
1046 if (r < 0) {
1047 if (r != -EBADR) /* job type not applicable */
1048 goto fail;
75778e21 1049
87d17581 1050 sd_bus_error_free(e);
75778e21 1051 }
87d17581 1052 }
75778e21 1053
b7777d08 1054 UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_START_IGNORED) {
fc49b299 1055 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, job, flags & TRANSACTION_IGNORE_ORDER, e);
87d17581
MY
1056 if (r < 0) {
1057 /* unit masked, job type not applicable and unit not found are not considered
1058 * as errors. */
1059 log_unit_full_errno(dep,
1060 IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
1061 r, "Cannot add dependency job, ignoring: %s",
1062 bus_error_message(e, r));
1063 sd_bus_error_free(e);
75778e21 1064 }
87d17581 1065 }
75778e21 1066
b7777d08 1067 UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_VERIFY) {
fc49b299 1068 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
87d17581
MY
1069 if (r < 0) {
1070 if (r != -EBADR) /* job type not applicable */
1071 goto fail;
75778e21 1072
87d17581 1073 sd_bus_error_free(e);
75778e21 1074 }
87d17581 1075 }
75778e21 1076
b7777d08 1077 UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_STOP) {
fc49b299 1078 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, job, TRANSACTION_MATTERS | TRANSACTION_CONFLICTS | (flags & TRANSACTION_IGNORE_ORDER), e);
87d17581
MY
1079 if (r < 0) {
1080 if (r != -EBADR) /* job type not applicable */
1081 goto fail;
75778e21 1082
87d17581 1083 sd_bus_error_free(e);
75778e21 1084 }
87d17581 1085 }
75778e21 1086
b7777d08 1087 UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) {
fc49b299 1088 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, job, flags & TRANSACTION_IGNORE_ORDER, e);
87d17581
MY
1089 if (r < 0) {
1090 log_unit_warning(dep,
1091 "Cannot add dependency job, ignoring: %s",
1092 bus_error_message(e, r));
1093 sd_bus_error_free(e);
75778e21 1094 }
75778e21 1095 }
87d17581 1096 }
75778e21 1097
48cb073d
MY
1098 if (IN_SET(type, JOB_RESTART, JOB_STOP) || (type == JOB_START && FLAGS_SET(flags, TRANSACTION_PROPAGATE_START_AS_RESTART))) {
1099 bool is_stop = type == JOB_STOP;
c6497ccb 1100
b7777d08 1101 UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PROPAGATE_STOP) {
48cb073d
MY
1102 /* We propagate RESTART only as TRY_RESTART, in order not to start dependencies that
1103 * are not around. */
87d17581 1104 JobType nt;
017a7ba4 1105
48cb073d 1106 nt = job_type_collapse(is_stop ? JOB_STOP : JOB_TRY_RESTART, dep);
87d17581
MY
1107 if (nt == JOB_NOP)
1108 continue;
017a7ba4 1109
fc49b299 1110 r = transaction_add_job_and_dependencies(tr, nt, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
87d17581
MY
1111 if (r < 0) {
1112 if (r != -EBADR) /* job type not applicable */
1113 return r;
09d04ad3 1114
87d17581 1115 sd_bus_error_free(e);
09d04ad3
LP
1116 }
1117 }
85e9a101 1118
48cb073d
MY
1119 /* Process UNIT_ATOM_PROPAGATE_STOP_GRACEFUL (PropagatesStopTo=) units. We need to wait until
1120 * all other dependencies are processed, i.e. we're the anchor job or already in the recursion
1121 * that handles it. */
1122 if (!by || FLAGS_SET(flags, TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL))
b7777d08 1123 UNIT_FOREACH_DEPENDENCY_SAFE(dep, job->unit, UNIT_ATOM_PROPAGATE_STOP_GRACEFUL) {
4893902b 1124 JobType nt;
48cb073d
MY
1125 Job *j;
1126
1127 j = hashmap_get(tr->jobs, dep);
4893902b 1128 nt = job_type_propagate_stop_graceful(j);
48cb073d
MY
1129
1130 if (nt == JOB_NOP)
1131 continue;
48894cd0 1132
fc49b299 1133 r = transaction_add_job_and_dependencies(tr, nt, dep, job, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER) | TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL, e);
48cb073d
MY
1134 if (r < 0) {
1135 if (r != -EBADR) /* job type not applicable */
1136 return r;
85e9a101 1137
48cb073d
MY
1138 sd_bus_error_free(e);
1139 }
15ed3c3a 1140 }
87d17581 1141 }
75778e21 1142
87d17581 1143 if (type == JOB_RELOAD)
fc49b299 1144 transaction_add_propagate_reload_jobs(tr, job->unit, job, flags & TRANSACTION_IGNORE_ORDER);
75778e21 1145
87d17581 1146 /* JOB_VERIFY_ACTIVE requires no dependency handling */
75778e21 1147
75778e21 1148 return 0;
87d17581 1149
89e9df12
MS
1150fail:
1151 /* Recursive call failed to add required jobs so let's drop top level job as well. */
1152 log_unit_debug_errno(unit, r, "Cannot add dependency job to transaction, deleting job %s/%s again: %s",
1153 unit->id, job_type_to_string(type), bus_error_message(e, r));
87d17581 1154
fc49b299 1155 transaction_delete_job(tr, job, /* delete_dependencies= */ false);
89e9df12 1156 return r;
75778e21
MS
1157}
1158
32d6707d
LP
1159static bool shall_stop_on_isolate(Transaction *tr, Unit *u) {
1160 assert(tr);
1161 assert(u);
1162
1163 if (u->ignore_on_isolate)
1164 return false;
1165
1166 /* Is there already something listed for this? */
6112c861 1167 if (hashmap_contains(tr->jobs, u))
32d6707d
LP
1168 return false;
1169
1170 return true;
1171}
1172
75778e21 1173int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
75778e21
MS
1174 Unit *u;
1175 char *k;
1176 int r;
1177
1178 assert(tr);
1179 assert(m);
1180
90e74a66 1181 HASHMAP_FOREACH_KEY(u, k, m->units) {
fab7e5e8 1182 _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
32d6707d 1183 Unit *o;
75778e21 1184
32d6707d 1185 /* Ignore aliases */
75778e21
MS
1186 if (u->id != k)
1187 continue;
1188
32d6707d
LP
1189 /* No need to stop inactive units */
1190 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
75778e21
MS
1191 continue;
1192
32d6707d 1193 if (!shall_stop_on_isolate(tr, u))
75778e21
MS
1194 continue;
1195
32d6707d
LP
1196 /* Keep units that are triggered by units we want to keep around. */
1197 bool keep = false;
1198 UNIT_FOREACH_DEPENDENCY(o, u, UNIT_ATOM_TRIGGERED_BY)
1199 if (!shall_stop_on_isolate(tr, o)) {
1200 keep = true;
1201 break;
1202 }
1203 if (keep)
75778e21
MS
1204 continue;
1205
fab7e5e8 1206 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, TRANSACTION_MATTERS, &e);
75778e21 1207 if (r < 0)
fab7e5e8 1208 log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %s", bus_error_message(&e, r));
75778e21
MS
1209 }
1210
1211 return 0;
1212}
1213
1f0f9f21 1214int transaction_add_triggering_jobs(Transaction *tr, Unit *u) {
1f0f9f21
KK
1215 Unit *trigger;
1216 int r;
1217
1218 assert(tr);
132e0b53 1219 assert(u);
1f0f9f21 1220
b7777d08 1221 UNIT_FOREACH_DEPENDENCY_SAFE(trigger, u, UNIT_ATOM_TRIGGERED_BY) {
fab7e5e8 1222 _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
15ed3c3a 1223
1f0f9f21
KK
1224 /* No need to stop inactive jobs */
1225 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger)) && !trigger->job)
1226 continue;
1227
1228 /* Is there already something listed for this? */
6112c861 1229 if (hashmap_contains(tr->jobs, trigger))
1f0f9f21
KK
1230 continue;
1231
fab7e5e8 1232 r = transaction_add_job_and_dependencies(tr, JOB_STOP, trigger, tr->anchor_job, TRANSACTION_MATTERS, &e);
1f0f9f21 1233 if (r < 0)
fab7e5e8 1234 log_unit_warning_errno(u, r, "Cannot add triggered by job, ignoring: %s", bus_error_message(&e, r));
1f0f9f21
KK
1235 }
1236
1237 return 0;
1238}
1239
23ade460 1240Transaction *transaction_new(bool irreversible) {
75778e21
MS
1241 Transaction *tr;
1242
1243 tr = new0(Transaction, 1);
1244 if (!tr)
1245 return NULL;
1246
d5099efc 1247 tr->jobs = hashmap_new(NULL);
6b430fdb
ZJS
1248 if (!tr->jobs)
1249 return mfree(tr);
75778e21 1250
23ade460
MS
1251 tr->irreversible = irreversible;
1252
75778e21
MS
1253 return tr;
1254}
1255
12da8805
DT
1256Transaction *transaction_free(Transaction *tr) {
1257 if (!tr)
1258 return NULL;
1259
75778e21
MS
1260 assert(hashmap_isempty(tr->jobs));
1261 hashmap_free(tr->jobs);
12da8805
DT
1262
1263 return mfree(tr);
75778e21 1264}
542fe408
DT
1265
1266Transaction *transaction_abort_and_free(Transaction *tr) {
1267 if (!tr)
1268 return NULL;
1269
1270 transaction_abort(tr);
1271
1272 return transaction_free(tr);
75778e21 1273}