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