]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/transaction.c
Merge pull request #1983 from dmedri/master
[thirdparty/systemd.git] / src / core / transaction.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <fcntl.h>
23 #include <unistd.h>
24
25 #include "alloc-util.h"
26 #include "bus-common-errors.h"
27 #include "bus-error.h"
28 #include "terminal-util.h"
29 #include "transaction.h"
30
31 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
32
33 static 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
41 job_free(j);
42 }
43
44 static 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
54 void transaction_abort(Transaction *tr) {
55 Job *j;
56
57 assert(tr);
58
59 while ((j = hashmap_first(tr->jobs)))
60 transaction_delete_job(tr, j, false);
61
62 assert(hashmap_isempty(tr->jobs));
63 }
64
65 static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
66 JobDependency *l;
67
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
73 j->matters_to_anchor = true;
74 j->generation = generation;
75
76 LIST_FOREACH(subject, l, j->subject_list) {
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
86 transaction_find_jobs_that_matter_to_anchor(l->object, generation);
87 }
88 }
89
90 static 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;
102 j->irreversible = j->irreversible || other->irreversible;
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
143 _pure_ static bool job_is_conflicted_by(Job *j) {
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
158 static 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
191 log_unit_debug(j->unit,
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)));
195 log_unit_debug(k->unit,
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)));
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. */
224 log_unit_debug(d->unit,
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),
228 d->unit->id, job_type_to_string(d->type));
229 transaction_delete_job(tr, d, true);
230 return 0;
231 }
232
233 return -EINVAL;
234 }
235
236 static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) {
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) {
251 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
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 */
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);
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
280 /* Merge all transaction jobs for j->unit */
281 LIST_FOREACH(transaction, k, j->transaction_next)
282 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
283
284 while ((k = j->transaction_next)) {
285 if (tr->anchor_job == k) {
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
292 assert(!j->transaction_next);
293 assert(!j->transaction_prev);
294 }
295
296 return 0;
297 }
298
299 static void transaction_drop_redundant(Transaction *tr) {
300 Job *j;
301 Iterator i;
302
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. */
306
307 assert(tr);
308
309 rescan:
310 HASHMAP_FOREACH(j, tr->jobs, i) {
311 Job *k;
312
313 LIST_FOREACH(transaction, k, j) {
314
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;
319 }
320
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 }
326 }
327
328 _pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) {
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
342 static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, sd_bus_error *e) {
343 Iterator i;
344 Unit *u;
345 int r;
346
347 assert(tr);
348 assert(j);
349 assert(!j->transaction_prev);
350
351 /* Does a recursive sweep through the ordering graph, looking
352 * for a cycle. If we find a cycle we try to break it. */
353
354 /* Have we seen this before? */
355 if (j->generation == generation) {
356 Job *k, *delete;
357
358 /* If the marker is NULL we have been here already and
359 * decided the job was loop-free from here. Hence
360 * shortcut things and return right-away. */
361 if (!j->marker)
362 return 0;
363
364 /* So, the marker is not NULL and we already have been
365 * here. We have a cycle. Let's try to break it. We go
366 * backwards in our path and try to find a suitable
367 * job to remove. We use the marker to find our way
368 * back, since smart how we are we stored our way back
369 * in there. */
370 log_unit_warning(j->unit,
371 "Found ordering cycle on %s/%s",
372 j->unit->id, job_type_to_string(j->type));
373
374 delete = NULL;
375 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
376
377 /* logging for j not k here here to provide consistent narrative */
378 log_unit_warning(j->unit,
379 "Found dependency on %s/%s",
380 k->unit->id, job_type_to_string(k->type));
381
382 if (!delete && hashmap_get(tr->jobs, k->unit) && !unit_matters_to_anchor(k->unit, k))
383 /* Ok, we can drop this one, so let's
384 * do so. */
385 delete = k;
386
387 /* Check if this in fact was the beginning of
388 * the cycle */
389 if (k == j)
390 break;
391 }
392
393
394 if (delete) {
395 /* logging for j not k here here to provide consistent narrative */
396 log_unit_warning(j->unit,
397 "Breaking ordering cycle by deleting job %s/%s",
398 delete->unit->id, job_type_to_string(delete->type));
399 log_unit_error(delete->unit,
400 "Job %s/%s deleted to break ordering cycle starting with %s/%s",
401 delete->unit->id, job_type_to_string(delete->type),
402 j->unit->id, job_type_to_string(j->type));
403 unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL,
404 "Ordering cycle found, skipping %s");
405 transaction_delete_unit(tr, delete->unit);
406 return -EAGAIN;
407 }
408
409 log_error("Unable to break cycle");
410
411 return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
412 "Transaction order is cyclic. See system logs for details.");
413 }
414
415 /* Make the marker point to where we come from, so that we can
416 * find our way backwards if we want to break a cycle. We use
417 * a special marker for the beginning: we point to
418 * ourselves. */
419 j->marker = from ? from : j;
420 j->generation = generation;
421
422 /* We assume that the dependencies are bidirectional, and
423 * hence can ignore UNIT_AFTER */
424 SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
425 Job *o;
426
427 /* Is there a job for this unit? */
428 o = hashmap_get(tr->jobs, u);
429 if (!o) {
430 /* Ok, there is no job for this in the
431 * transaction, but maybe there is already one
432 * running? */
433 o = u->job;
434 if (!o)
435 continue;
436 }
437
438 r = transaction_verify_order_one(tr, o, j, generation, e);
439 if (r < 0)
440 return r;
441 }
442
443 /* Ok, let's backtrack, and remember that this entry is not on
444 * our path anymore. */
445 j->marker = NULL;
446
447 return 0;
448 }
449
450 static int transaction_verify_order(Transaction *tr, unsigned *generation, sd_bus_error *e) {
451 Job *j;
452 int r;
453 Iterator i;
454 unsigned g;
455
456 assert(tr);
457 assert(generation);
458
459 /* Check if the ordering graph is cyclic. If it is, try to fix
460 * that up by dropping one of the jobs. */
461
462 g = (*generation)++;
463
464 HASHMAP_FOREACH(j, tr->jobs, i) {
465 r = transaction_verify_order_one(tr, j, NULL, g, e);
466 if (r < 0)
467 return r;
468 }
469
470 return 0;
471 }
472
473 static void transaction_collect_garbage(Transaction *tr) {
474 Iterator i;
475 Job *j;
476
477 assert(tr);
478
479 /* Drop jobs that are not required by any other job */
480
481 rescan:
482 HASHMAP_FOREACH(j, tr->jobs, i) {
483 if (tr->anchor_job == j || j->object_list) {
484 /* log_debug("Keeping job %s/%s because of %s/%s", */
485 /* j->unit->id, job_type_to_string(j->type), */
486 /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
487 /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
488 continue;
489 }
490
491 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
492 transaction_delete_job(tr, j, true);
493 goto rescan;
494 }
495 }
496
497 static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_error *e) {
498 Iterator i;
499 Job *j;
500
501 assert(tr);
502
503 /* Checks whether applying this transaction means that
504 * existing jobs would be replaced */
505
506 HASHMAP_FOREACH(j, tr->jobs, i) {
507
508 /* Assume merged */
509 assert(!j->transaction_prev);
510 assert(!j->transaction_next);
511
512 if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
513 job_type_is_conflicting(j->unit->job->type, j->type))
514 return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
515 "Transaction is destructive.");
516 }
517
518 return 0;
519 }
520
521 static void transaction_minimize_impact(Transaction *tr) {
522 Job *j;
523 Iterator i;
524
525 assert(tr);
526
527 /* Drops all unnecessary jobs that reverse already active jobs
528 * or that stop a running service. */
529
530 rescan:
531 HASHMAP_FOREACH(j, tr->jobs, i) {
532 LIST_FOREACH(transaction, j, j) {
533 bool stops_running_service, changes_existing_job;
534
535 /* If it matters, we shouldn't drop it */
536 if (j->matters_to_anchor)
537 continue;
538
539 /* Would this stop a running service?
540 * Would this change an existing job?
541 * If so, let's drop this entry */
542
543 stops_running_service =
544 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
545
546 changes_existing_job =
547 j->unit->job &&
548 job_type_is_conflicting(j->type, j->unit->job->type);
549
550 if (!stops_running_service && !changes_existing_job)
551 continue;
552
553 if (stops_running_service)
554 log_unit_debug(j->unit,
555 "%s/%s would stop a running service.",
556 j->unit->id, job_type_to_string(j->type));
557
558 if (changes_existing_job)
559 log_unit_debug(j->unit,
560 "%s/%s would change existing job.",
561 j->unit->id, job_type_to_string(j->type));
562
563 /* Ok, let's get rid of this */
564 log_unit_debug(j->unit,
565 "Deleting %s/%s to minimize impact.",
566 j->unit->id, job_type_to_string(j->type));
567
568 transaction_delete_job(tr, j, true);
569 goto rescan;
570 }
571 }
572 }
573
574 static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
575 Iterator i;
576 Job *j;
577 int r;
578
579 /* Moves the transaction jobs to the set of active jobs */
580
581 if (mode == JOB_ISOLATE || mode == JOB_FLUSH) {
582
583 /* When isolating first kill all installed jobs which
584 * aren't part of the new transaction */
585 HASHMAP_FOREACH(j, m->jobs, i) {
586 assert(j->installed);
587
588 if (hashmap_get(tr->jobs, j->unit))
589 continue;
590
591 /* Not invalidating recursively. Avoids triggering
592 * OnFailure= actions of dependent jobs. Also avoids
593 * invalidating our iterator. */
594 job_finish_and_invalidate(j, JOB_CANCELED, false);
595 }
596 }
597
598 HASHMAP_FOREACH(j, tr->jobs, i) {
599 /* Assume merged */
600 assert(!j->transaction_prev);
601 assert(!j->transaction_next);
602
603 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
604 if (r < 0)
605 goto rollback;
606 }
607
608 while ((j = hashmap_steal_first(tr->jobs))) {
609 Job *installed_job;
610
611 /* Clean the job dependencies */
612 transaction_unlink_job(tr, j, false);
613
614 installed_job = job_install(j);
615 if (installed_job != j) {
616 /* j has been merged into a previously installed job */
617 if (tr->anchor_job == j)
618 tr->anchor_job = installed_job;
619 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
620 job_free(j);
621 j = installed_job;
622 }
623
624 job_add_to_run_queue(j);
625 job_add_to_dbus_queue(j);
626 job_start_timer(j);
627 job_shutdown_magic(j);
628 }
629
630 return 0;
631
632 rollback:
633
634 HASHMAP_FOREACH(j, tr->jobs, i)
635 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
636
637 return r;
638 }
639
640 int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error *e) {
641 Iterator i;
642 Job *j;
643 int r;
644 unsigned generation = 1;
645
646 assert(tr);
647
648 /* This applies the changes recorded in tr->jobs to
649 * the actual list of jobs, if possible. */
650
651 /* Reset the generation counter of all installed jobs. The detection of cycles
652 * looks at installed jobs. If they had a non-zero generation from some previous
653 * walk of the graph, the algorithm would break. */
654 HASHMAP_FOREACH(j, m->jobs, i)
655 j->generation = 0;
656
657 /* First step: figure out which jobs matter */
658 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
659
660 /* Second step: Try not to stop any running services if
661 * we don't have to. Don't try to reverse running
662 * jobs if we don't have to. */
663 if (mode == JOB_FAIL)
664 transaction_minimize_impact(tr);
665
666 /* Third step: Drop redundant jobs */
667 transaction_drop_redundant(tr);
668
669 for (;;) {
670 /* Fourth step: Let's remove unneeded jobs that might
671 * be lurking. */
672 if (mode != JOB_ISOLATE)
673 transaction_collect_garbage(tr);
674
675 /* Fifth step: verify order makes sense and correct
676 * cycles if necessary and possible */
677 r = transaction_verify_order(tr, &generation, e);
678 if (r >= 0)
679 break;
680
681 if (r != -EAGAIN) {
682 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r));
683 return r;
684 }
685
686 /* Let's see if the resulting transaction ordering
687 * graph is still cyclic... */
688 }
689
690 for (;;) {
691 /* Sixth step: let's drop unmergeable entries if
692 * necessary and possible, merge entries we can
693 * merge */
694 r = transaction_merge_jobs(tr, e);
695 if (r >= 0)
696 break;
697
698 if (r != -EAGAIN) {
699 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r));
700 return r;
701 }
702
703 /* Seventh step: an entry got dropped, let's garbage
704 * collect its dependencies. */
705 if (mode != JOB_ISOLATE)
706 transaction_collect_garbage(tr);
707
708 /* Let's see if the resulting transaction still has
709 * unmergeable entries ... */
710 }
711
712 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
713 transaction_drop_redundant(tr);
714
715 /* Ninth step: check whether we can actually apply this */
716 r = transaction_is_destructive(tr, mode, e);
717 if (r < 0) {
718 log_notice("Requested transaction contradicts existing jobs: %s", bus_error_message(e, r));
719 return r;
720 }
721
722 /* Tenth step: apply changes */
723 r = transaction_apply(tr, m, mode);
724 if (r < 0)
725 return log_warning_errno(r, "Failed to apply transaction: %m");
726
727 assert(hashmap_isempty(tr->jobs));
728
729 if (!hashmap_isempty(m->jobs)) {
730 /* Are there any jobs now? Then make sure we have the
731 * idle pipe around. We don't really care too much
732 * whether this works or not, as the idle pipe is a
733 * feature for cosmetics, not actually useful for
734 * anything beyond that. */
735
736 if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 &&
737 m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) {
738 (void) pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
739 (void) pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
740 }
741 }
742
743 return 0;
744 }
745
746 static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) {
747 Job *j, *f;
748
749 assert(tr);
750 assert(unit);
751
752 /* Looks for an existing prospective job and returns that. If
753 * it doesn't exist it is created and added to the prospective
754 * jobs list. */
755
756 f = hashmap_get(tr->jobs, unit);
757
758 LIST_FOREACH(transaction, j, f) {
759 assert(j->unit == unit);
760
761 if (j->type == type) {
762 if (is_new)
763 *is_new = false;
764 return j;
765 }
766 }
767
768 j = job_new(unit, type);
769 if (!j)
770 return NULL;
771
772 j->generation = 0;
773 j->marker = NULL;
774 j->matters_to_anchor = false;
775 j->irreversible = tr->irreversible;
776
777 LIST_PREPEND(transaction, f, j);
778
779 if (hashmap_replace(tr->jobs, unit, f) < 0) {
780 LIST_REMOVE(transaction, f, j);
781 job_free(j);
782 return NULL;
783 }
784
785 if (is_new)
786 *is_new = true;
787
788 /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
789
790 return j;
791 }
792
793 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
794 assert(tr);
795 assert(j);
796
797 if (j->transaction_prev)
798 j->transaction_prev->transaction_next = j->transaction_next;
799 else if (j->transaction_next)
800 hashmap_replace(tr->jobs, j->unit, j->transaction_next);
801 else
802 hashmap_remove_value(tr->jobs, j->unit, j);
803
804 if (j->transaction_next)
805 j->transaction_next->transaction_prev = j->transaction_prev;
806
807 j->transaction_prev = j->transaction_next = NULL;
808
809 while (j->subject_list)
810 job_dependency_free(j->subject_list);
811
812 while (j->object_list) {
813 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
814
815 job_dependency_free(j->object_list);
816
817 if (other && delete_dependencies) {
818 log_unit_debug(other->unit,
819 "Deleting job %s/%s as dependency of job %s/%s",
820 other->unit->id, job_type_to_string(other->type),
821 j->unit->id, job_type_to_string(j->type));
822 transaction_delete_job(tr, other, delete_dependencies);
823 }
824 }
825 }
826
827 int transaction_add_job_and_dependencies(
828 Transaction *tr,
829 JobType type,
830 Unit *unit,
831 Job *by,
832 bool matters,
833 bool conflicts,
834 bool ignore_requirements,
835 bool ignore_order,
836 sd_bus_error *e) {
837 Job *ret;
838 Iterator i;
839 Unit *dep;
840 int r;
841 bool is_new;
842
843 assert(tr);
844 assert(type < _JOB_TYPE_MAX);
845 assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
846 assert(unit);
847
848 /* Before adding jobs for this unit, let's ensure that its state has been loaded
849 * This matters when jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()).
850 * This way, we "recursively" coldplug units, ensuring that we do not look at state of
851 * not-yet-coldplugged units. */
852 if (unit->manager->n_reloading > 0)
853 unit_coldplug(unit);
854
855 /* log_debug("Pulling in %s/%s from %s/%s", */
856 /* unit->id, job_type_to_string(type), */
857 /* by ? by->unit->id : "NA", */
858 /* by ? job_type_to_string(by->type) : "NA"); */
859
860 if (!IN_SET(unit->load_state, UNIT_LOADED, UNIT_ERROR, UNIT_NOT_FOUND, UNIT_MASKED))
861 return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
862
863 if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
864 if (unit->load_error == -ENOENT || unit->manager->test_run)
865 return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
866 "Unit %s failed to load: %s.",
867 unit->id,
868 strerror(-unit->load_error));
869 else
870 return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
871 "Unit %s failed to load: %s. "
872 "See system logs and 'systemctl status %s' for details.",
873 unit->id,
874 strerror(-unit->load_error),
875 unit->id);
876 }
877
878 if (type != JOB_STOP && unit->load_state == UNIT_NOT_FOUND)
879 return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED,
880 "Unit %s failed to load: %s.",
881 unit->id, strerror(-unit->load_error));
882
883 if (type != JOB_STOP && unit->load_state == UNIT_MASKED)
884 return sd_bus_error_setf(e, BUS_ERROR_UNIT_MASKED,
885 "Unit %s is masked.", unit->id);
886
887 if (!unit_job_is_applicable(unit, type))
888 return sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE,
889 "Job type %s is not applicable for unit %s.",
890 job_type_to_string(type), unit->id);
891
892
893 /* First add the job. */
894 ret = transaction_add_one_job(tr, type, unit, &is_new);
895 if (!ret)
896 return -ENOMEM;
897
898 ret->ignore_order = ret->ignore_order || ignore_order;
899
900 /* Then, add a link to the job. */
901 if (by) {
902 if (!job_dependency_new(by, ret, matters, conflicts))
903 return -ENOMEM;
904 } else {
905 /* If the job has no parent job, it is the anchor job. */
906 assert(!tr->anchor_job);
907 tr->anchor_job = ret;
908 }
909
910 if (is_new && !ignore_requirements && type != JOB_NOP) {
911 Set *following;
912
913 /* If we are following some other unit, make sure we
914 * add all dependencies of everybody following. */
915 if (unit_following_set(ret->unit, &following) > 0) {
916 SET_FOREACH(dep, following, i) {
917 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, false, false, ignore_order, e);
918 if (r < 0) {
919 log_unit_warning(dep, "Cannot add dependency job for, ignoring: %s", bus_error_message(e, r));
920 sd_bus_error_free(e);
921 }
922 }
923
924 set_free(following);
925 }
926
927 /* Finally, recursively add in all dependencies. */
928 if (type == JOB_START || type == JOB_RESTART) {
929 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
930 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
931 if (r < 0) {
932 if (r != -EBADR)
933 goto fail;
934
935 sd_bus_error_free(e);
936 }
937 }
938
939 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
940 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
941 if (r < 0) {
942 if (r != -EBADR)
943 goto fail;
944
945 sd_bus_error_free(e);
946 }
947 }
948
949 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
950 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
951 if (r < 0) {
952 log_unit_full(dep,
953 r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, r,
954 "Cannot add dependency job, ignoring: %s",
955 bus_error_message(e, r));
956 sd_bus_error_free(e);
957 }
958 }
959
960 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
961 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, false, false, ignore_order, e);
962 if (r < 0) {
963 if (r != -EBADR)
964 goto fail;
965
966 sd_bus_error_free(e);
967 }
968 }
969
970 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
971 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e);
972 if (r < 0) {
973 if (r != -EBADR)
974 goto fail;
975
976 sd_bus_error_free(e);
977 }
978 }
979
980 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
981 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e);
982 if (r < 0) {
983 log_unit_warning(dep,
984 "Cannot add dependency job, ignoring: %s",
985 bus_error_message(e, r));
986 sd_bus_error_free(e);
987 }
988 }
989
990 }
991
992 if (type == JOB_STOP || type == JOB_RESTART) {
993 static const UnitDependency propagate_deps[] = {
994 UNIT_REQUIRED_BY,
995 UNIT_REQUISITE_OF,
996 UNIT_BOUND_BY,
997 UNIT_CONSISTS_OF,
998 };
999
1000 JobType ptype;
1001 unsigned j;
1002
1003 /* We propagate STOP as STOP, but RESTART only
1004 * as TRY_RESTART, in order not to start
1005 * dependencies that are not around. */
1006 ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type;
1007
1008 for (j = 0; j < ELEMENTSOF(propagate_deps); j++)
1009 SET_FOREACH(dep, ret->unit->dependencies[propagate_deps[j]], i) {
1010 JobType nt;
1011
1012 nt = job_type_collapse(ptype, dep);
1013 if (nt == JOB_NOP)
1014 continue;
1015
1016 r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
1017 if (r < 0) {
1018 if (r != -EBADR)
1019 goto fail;
1020
1021 sd_bus_error_free(e);
1022 }
1023 }
1024 }
1025
1026 if (type == JOB_RELOAD) {
1027
1028 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
1029 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, false, false, ignore_order, e);
1030 if (r < 0) {
1031 log_unit_warning(dep,
1032 "Cannot add dependency reload job, ignoring: %s",
1033 bus_error_message(e, r));
1034 sd_bus_error_free(e);
1035 }
1036 }
1037 }
1038
1039 /* JOB_VERIFY_STARTED require no dependency handling */
1040 }
1041
1042 return 0;
1043
1044 fail:
1045 return r;
1046 }
1047
1048 int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
1049 Iterator i;
1050 Unit *u;
1051 char *k;
1052 int r;
1053
1054 assert(tr);
1055 assert(m);
1056
1057 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1058
1059 /* ignore aliases */
1060 if (u->id != k)
1061 continue;
1062
1063 if (u->ignore_on_isolate)
1064 continue;
1065
1066 /* No need to stop inactive jobs */
1067 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
1068 continue;
1069
1070 /* Is there already something listed for this? */
1071 if (hashmap_get(tr->jobs, u))
1072 continue;
1073
1074 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, NULL);
1075 if (r < 0)
1076 log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %m");
1077 }
1078
1079 return 0;
1080 }
1081
1082 Transaction *transaction_new(bool irreversible) {
1083 Transaction *tr;
1084
1085 tr = new0(Transaction, 1);
1086 if (!tr)
1087 return NULL;
1088
1089 tr->jobs = hashmap_new(NULL);
1090 if (!tr->jobs) {
1091 free(tr);
1092 return NULL;
1093 }
1094
1095 tr->irreversible = irreversible;
1096
1097 return tr;
1098 }
1099
1100 void transaction_free(Transaction *tr) {
1101 assert(hashmap_isempty(tr->jobs));
1102 hashmap_free(tr->jobs);
1103 free(tr);
1104 }