1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
25 #include "transaction.h"
26 #include "bus-errors.h"
27 #include "dbus-common.h"
29 static void transaction_unlink_job(Transaction
*tr
, Job
*j
, bool delete_dependencies
);
31 static void transaction_delete_job(Transaction
*tr
, Job
*j
, bool delete_dependencies
) {
35 /* Deletes one job from the transaction */
37 transaction_unlink_job(tr
, j
, delete_dependencies
);
42 static void transaction_delete_unit(Transaction
*tr
, Unit
*u
) {
45 /* Deletes all jobs associated with a certain unit from the
48 while ((j
= hashmap_get(tr
->jobs
, u
)))
49 transaction_delete_job(tr
, j
, true);
52 void transaction_abort(Transaction
*tr
) {
57 while ((j
= hashmap_first(tr
->jobs
)))
58 transaction_delete_job(tr
, j
, false);
60 assert(hashmap_isempty(tr
->jobs
));
63 static void transaction_find_jobs_that_matter_to_anchor(Job
*j
, unsigned generation
) {
66 /* A recursive sweep through the graph that marks all units
67 * that matter to the anchor job, i.e. are directly or
68 * indirectly a dependency of the anchor job via paths that
69 * are fully marked as mattering. */
71 j
->matters_to_anchor
= true;
72 j
->generation
= generation
;
74 LIST_FOREACH(subject
, l
, j
->subject_list
) {
76 /* This link does not matter */
80 /* This unit has already been marked */
81 if (l
->object
->generation
== generation
)
84 transaction_find_jobs_that_matter_to_anchor(l
->object
, generation
);
88 static void transaction_merge_and_delete_job(Transaction
*tr
, Job
*j
, Job
*other
, JobType t
) {
89 JobDependency
*l
, *last
;
93 assert(j
->unit
== other
->unit
);
94 assert(!j
->installed
);
96 /* Merges 'other' into 'j' and then deletes 'other'. */
99 j
->state
= JOB_WAITING
;
100 j
->override
= j
->override
|| other
->override
;
101 j
->irreversible
= j
->irreversible
|| other
->irreversible
;
103 j
->matters_to_anchor
= j
->matters_to_anchor
|| other
->matters_to_anchor
;
105 /* Patch us in as new owner of the JobDependency objects */
107 LIST_FOREACH(subject
, l
, other
->subject_list
) {
108 assert(l
->subject
== other
);
113 /* Merge both lists */
115 last
->subject_next
= j
->subject_list
;
117 j
->subject_list
->subject_prev
= last
;
118 j
->subject_list
= other
->subject_list
;
121 /* Patch us in as new owner of the JobDependency objects */
123 LIST_FOREACH(object
, l
, other
->object_list
) {
124 assert(l
->object
== other
);
129 /* Merge both lists */
131 last
->object_next
= j
->object_list
;
133 j
->object_list
->object_prev
= last
;
134 j
->object_list
= other
->object_list
;
137 /* Kill the other job */
138 other
->subject_list
= NULL
;
139 other
->object_list
= NULL
;
140 transaction_delete_job(tr
, other
, true);
143 _pure_
static bool job_is_conflicted_by(Job
*j
) {
148 /* Returns true if this job is pulled in by a least one
149 * ConflictedBy dependency. */
151 LIST_FOREACH(object
, l
, j
->object_list
)
158 static int delete_one_unmergeable_job(Transaction
*tr
, Job
*j
) {
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. */
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
) {
174 /* Is this one mergeable? Then skip it */
175 if (job_type_is_mergeable(j
->type
, k
->type
))
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
) {
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. */
191 log_debug_unit(j
->unit
->id
,
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_debug_unit(k
->unit
->id
,
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
)));
200 if (j
->type
== JOB_STOP
) {
202 if (job_is_conflicted_by(j
))
207 } else if (k
->type
== JOB_STOP
) {
209 if (job_is_conflicted_by(k
))
216 } else if (!j
->matters_to_anchor
)
218 else if (!k
->matters_to_anchor
)
223 /* Ok, we can drop one, so let's do so. */
224 log_debug_unit(d
->unit
->id
,
225 "Fixing conflicting jobs by deleting job %s/%s",
226 d
->unit
->id
, job_type_to_string(d
->type
));
227 transaction_delete_job(tr
, d
, true);
234 static int transaction_merge_jobs(Transaction
*tr
, DBusError
*e
) {
241 /* First step, check whether any of the jobs for one specific
242 * task conflict. If so, try to drop one of them. */
243 HASHMAP_FOREACH(j
, tr
->jobs
, i
) {
248 LIST_FOREACH(transaction
, k
, j
->transaction_next
) {
249 if (job_type_merge_and_collapse(&t
, k
->type
, j
->unit
) >= 0)
252 /* OK, we could not merge all jobs for this
253 * action. Let's see if we can get rid of one
256 r
= delete_one_unmergeable_job(tr
, j
);
258 /* Ok, we managed to drop one, now
259 * let's ask our callers to call us
260 * again after garbage collecting */
263 /* We couldn't merge anything. Failure */
264 dbus_set_error(e
, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING
, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
265 job_type_to_string(t
), job_type_to_string(k
->type
), k
->unit
->id
);
270 /* Second step, merge the jobs. */
271 HASHMAP_FOREACH(j
, tr
->jobs
, i
) {
275 /* Merge all transaction jobs for j->unit */
276 LIST_FOREACH(transaction
, k
, j
->transaction_next
)
277 assert_se(job_type_merge_and_collapse(&t
, k
->type
, j
->unit
) == 0);
279 while ((k
= j
->transaction_next
)) {
280 if (tr
->anchor_job
== k
) {
281 transaction_merge_and_delete_job(tr
, k
, j
, t
);
284 transaction_merge_and_delete_job(tr
, j
, k
, t
);
287 assert(!j
->transaction_next
);
288 assert(!j
->transaction_prev
);
294 static void transaction_drop_redundant(Transaction
*tr
) {
298 /* Goes through the transaction and removes all jobs of the units
299 * whose jobs are all noops. If not all of a unit's jobs are
300 * redundant, they are kept. */
305 HASHMAP_FOREACH(j
, tr
->jobs
, i
) {
308 LIST_FOREACH(transaction
, k
, j
) {
310 if (tr
->anchor_job
== k
||
311 !job_type_is_redundant(k
->type
, unit_active_state(k
->unit
)) ||
312 (k
->unit
->job
&& job_type_is_conflicting(k
->type
, k
->unit
->job
->type
)))
316 /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
317 transaction_delete_job(tr
, j
, false);
323 _pure_
static bool unit_matters_to_anchor(Unit
*u
, Job
*j
) {
325 assert(!j
->transaction_prev
);
327 /* Checks whether at least one of the jobs for this unit
328 * matters to the anchor. */
330 LIST_FOREACH(transaction
, j
, j
)
331 if (j
->matters_to_anchor
)
337 static int transaction_verify_order_one(Transaction
*tr
, Job
*j
, Job
*from
, unsigned generation
, DBusError
*e
) {
344 assert(!j
->transaction_prev
);
346 /* Does a recursive sweep through the ordering graph, looking
347 * for a cycle. If we find cycle we try to break it. */
349 /* Have we seen this before? */
350 if (j
->generation
== generation
) {
353 /* If the marker is NULL we have been here already and
354 * decided the job was loop-free from here. Hence
355 * shortcut things and return right-away. */
359 /* So, the marker is not NULL and we already have been
360 * here. We have a cycle. Let's try to break it. We go
361 * backwards in our path and try to find a suitable
362 * job to remove. We use the marker to find our way
363 * back, since smart how we are we stored our way back
365 log_warning_unit(j
->unit
->id
,
366 "Found ordering cycle on %s/%s",
367 j
->unit
->id
, job_type_to_string(j
->type
));
370 for (k
= from
; k
; k
= ((k
->generation
== generation
&& k
->marker
!= k
) ? k
->marker
: NULL
)) {
372 /* logging for j not k here here to provide consistent narrative */
373 log_info_unit(j
->unit
->id
,
374 "Walked on cycle path to %s/%s",
375 k
->unit
->id
, job_type_to_string(k
->type
));
378 !unit_matters_to_anchor(k
->unit
, k
)) {
379 /* Ok, we can drop this one, so let's
384 /* Check if this in fact was the beginning of
392 /* logging for j not k here here to provide consistent narrative */
393 log_warning_unit(j
->unit
->id
,
394 "Breaking ordering cycle by deleting job %s/%s",
395 delete->unit
->id
, job_type_to_string(delete->type
));
396 log_error_unit(delete->unit
->id
,
397 "Job %s/%s deleted to break ordering cycle starting with %s/%s",
398 delete->unit
->id
, job_type_to_string(delete->type
),
399 j
->unit
->id
, job_type_to_string(j
->type
));
400 unit_status_printf(delete->unit
, ANSI_HIGHLIGHT_RED_ON
" SKIP " ANSI_HIGHLIGHT_OFF
,
401 "Ordering cycle found, skipping %s");
402 transaction_delete_unit(tr
, delete->unit
);
406 log_error("Unable to break cycle");
408 dbus_set_error(e
, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC
,
409 "Transaction order is cyclic. See system logs for details.");
413 /* Make the marker point to where we come from, so that we can
414 * find our way backwards if we want to break a cycle. We use
415 * a special marker for the beginning: we point to
417 j
->marker
= from
? from
: j
;
418 j
->generation
= generation
;
420 /* We assume that the dependencies are bidirectional, and
421 * hence can ignore UNIT_AFTER */
422 SET_FOREACH(u
, j
->unit
->dependencies
[UNIT_BEFORE
], i
) {
425 /* Is there a job for this unit? */
426 o
= hashmap_get(tr
->jobs
, u
);
428 /* Ok, there is no job for this in the
429 * transaction, but maybe there is already one
436 r
= transaction_verify_order_one(tr
, o
, j
, generation
, e
);
441 /* Ok, let's backtrack, and remember that this entry is not on
442 * our path anymore. */
448 static int transaction_verify_order(Transaction
*tr
, unsigned *generation
, DBusError
*e
) {
457 /* Check if the ordering graph is cyclic. If it is, try to fix
458 * that up by dropping one of the jobs. */
462 HASHMAP_FOREACH(j
, tr
->jobs
, i
)
463 if ((r
= transaction_verify_order_one(tr
, j
, NULL
, g
, e
)) < 0)
469 static void transaction_collect_garbage(Transaction
*tr
) {
475 /* Drop jobs that are not required by any other job */
478 HASHMAP_FOREACH(j
, tr
->jobs
, i
) {
479 if (tr
->anchor_job
== j
|| j
->object_list
) {
480 /* log_debug("Keeping job %s/%s because of %s/%s", */
481 /* j->unit->id, job_type_to_string(j->type), */
482 /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
483 /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
487 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
488 transaction_delete_job(tr
, j
, true);
493 static int transaction_is_destructive(Transaction
*tr
, JobMode mode
, DBusError
*e
) {
499 /* Checks whether applying this transaction means that
500 * existing jobs would be replaced */
502 HASHMAP_FOREACH(j
, tr
->jobs
, i
) {
505 assert(!j
->transaction_prev
);
506 assert(!j
->transaction_next
);
508 if (j
->unit
->job
&& (mode
== JOB_FAIL
|| j
->unit
->job
->irreversible
) &&
509 !job_type_is_superset(j
->type
, j
->unit
->job
->type
)) {
511 dbus_set_error(e
, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE
, "Transaction is destructive.");
519 static void transaction_minimize_impact(Transaction
*tr
) {
525 /* Drops all unnecessary jobs that reverse already active jobs
526 * or that stop a running service. */
529 HASHMAP_FOREACH(j
, tr
->jobs
, i
) {
530 LIST_FOREACH(transaction
, j
, j
) {
531 bool stops_running_service
, changes_existing_job
;
533 /* If it matters, we shouldn't drop it */
534 if (j
->matters_to_anchor
)
537 /* Would this stop a running service?
538 * Would this change an existing job?
539 * If so, let's drop this entry */
541 stops_running_service
=
542 j
->type
== JOB_STOP
&& UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j
->unit
));
544 changes_existing_job
=
546 job_type_is_conflicting(j
->type
, j
->unit
->job
->type
);
548 if (!stops_running_service
&& !changes_existing_job
)
551 if (stops_running_service
)
552 log_debug_unit(j
->unit
->id
,
553 "%s/%s would stop a running service.",
554 j
->unit
->id
, job_type_to_string(j
->type
));
556 if (changes_existing_job
)
557 log_debug_unit(j
->unit
->id
,
558 "%s/%s would change existing job.",
559 j
->unit
->id
, job_type_to_string(j
->type
));
561 /* Ok, let's get rid of this */
562 log_debug_unit(j
->unit
->id
,
563 "Deleting %s/%s to minimize impact.",
564 j
->unit
->id
, job_type_to_string(j
->type
));
566 transaction_delete_job(tr
, j
, true);
572 static int transaction_apply(Transaction
*tr
, Manager
*m
, JobMode mode
) {
577 /* Moves the transaction jobs to the set of active jobs */
579 if (mode
== JOB_ISOLATE
) {
581 /* When isolating first kill all installed jobs which
582 * aren't part of the new transaction */
583 HASHMAP_FOREACH(j
, m
->jobs
, i
) {
584 assert(j
->installed
);
586 if (hashmap_get(tr
->jobs
, j
->unit
))
589 /* Not invalidating recursively. Avoids triggering
590 * OnFailure= actions of dependent jobs. Also avoids
591 * invalidating our iterator. */
592 job_finish_and_invalidate(j
, JOB_CANCELED
, false);
596 HASHMAP_FOREACH(j
, tr
->jobs
, i
) {
598 assert(!j
->transaction_prev
);
599 assert(!j
->transaction_next
);
601 r
= hashmap_put(m
->jobs
, UINT32_TO_PTR(j
->id
), j
);
606 while ((j
= hashmap_steal_first(tr
->jobs
))) {
609 /* Clean the job dependencies */
610 transaction_unlink_job(tr
, j
, false);
612 installed_job
= job_install(j
);
613 if (installed_job
!= j
) {
614 /* j has been merged into a previously installed job */
615 if (tr
->anchor_job
== j
)
616 tr
->anchor_job
= installed_job
;
617 hashmap_remove(m
->jobs
, UINT32_TO_PTR(j
->id
));
622 job_add_to_run_queue(j
);
623 job_add_to_dbus_queue(j
);
625 job_shutdown_magic(j
);
632 HASHMAP_FOREACH(j
, tr
->jobs
, i
)
633 hashmap_remove(m
->jobs
, UINT32_TO_PTR(j
->id
));
638 int transaction_activate(Transaction
*tr
, Manager
*m
, JobMode mode
, DBusError
*e
) {
642 unsigned generation
= 1;
646 /* This applies the changes recorded in tr->jobs to
647 * the actual list of jobs, if possible. */
649 /* Reset the generation counter of all installed jobs. The detection of cycles
650 * looks at installed jobs. If they had a non-zero generation from some previous
651 * walk of the graph, the algorithm would break. */
652 HASHMAP_FOREACH(j
, m
->jobs
, i
)
655 /* First step: figure out which jobs matter */
656 transaction_find_jobs_that_matter_to_anchor(tr
->anchor_job
, generation
++);
658 /* Second step: Try not to stop any running services if
659 * we don't have to. Don't try to reverse running
660 * jobs if we don't have to. */
661 if (mode
== JOB_FAIL
)
662 transaction_minimize_impact(tr
);
664 /* Third step: Drop redundant jobs */
665 transaction_drop_redundant(tr
);
668 /* Fourth step: Let's remove unneeded jobs that might
670 if (mode
!= JOB_ISOLATE
)
671 transaction_collect_garbage(tr
);
673 /* Fifth step: verify order makes sense and correct
674 * cycles if necessary and possible */
675 r
= transaction_verify_order(tr
, &generation
, e
);
680 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e
, r
));
684 /* Let's see if the resulting transaction ordering
685 * graph is still cyclic... */
689 /* Sixth step: let's drop unmergeable entries if
690 * necessary and possible, merge entries we can
692 r
= transaction_merge_jobs(tr
, e
);
697 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e
, r
));
701 /* Seventh step: an entry got dropped, let's garbage
702 * collect its dependencies. */
703 if (mode
!= JOB_ISOLATE
)
704 transaction_collect_garbage(tr
);
706 /* Let's see if the resulting transaction still has
707 * unmergeable entries ... */
710 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
711 transaction_drop_redundant(tr
);
713 /* Ninth step: check whether we can actually apply this */
714 r
= transaction_is_destructive(tr
, mode
, e
);
716 log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e
, r
));
720 /* Tenth step: apply changes */
721 r
= transaction_apply(tr
, m
, mode
);
723 log_warning("Failed to apply transaction: %s", strerror(-r
));
727 assert(hashmap_isempty(tr
->jobs
));
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. */
736 if (m
->idle_pipe
[0] < 0 && m
->idle_pipe
[1] < 0 &&
737 m
->idle_pipe
[2] < 0 && m
->idle_pipe
[3] < 0) {
738 pipe2(m
->idle_pipe
, O_NONBLOCK
|O_CLOEXEC
);
739 pipe2(m
->idle_pipe
+ 2, O_NONBLOCK
|O_CLOEXEC
);
746 static Job
* transaction_add_one_job(Transaction
*tr
, JobType type
, Unit
*unit
, bool override
, bool *is_new
) {
752 /* Looks for an existing prospective job and returns that. If
753 * it doesn't exist it is created and added to the prospective
756 f
= hashmap_get(tr
->jobs
, unit
);
758 LIST_FOREACH(transaction
, j
, f
) {
759 assert(j
->unit
== unit
);
761 if (j
->type
== type
) {
768 j
= job_new(unit
, type
);
774 j
->matters_to_anchor
= false;
775 j
->override
= override
;
776 j
->irreversible
= tr
->irreversible
;
778 LIST_PREPEND(Job
, transaction
, f
, j
);
780 if (hashmap_replace(tr
->jobs
, unit
, f
) < 0) {
781 LIST_REMOVE(Job
, transaction
, f
, j
);
789 /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
794 static void transaction_unlink_job(Transaction
*tr
, Job
*j
, bool delete_dependencies
) {
798 if (j
->transaction_prev
)
799 j
->transaction_prev
->transaction_next
= j
->transaction_next
;
800 else if (j
->transaction_next
)
801 hashmap_replace(tr
->jobs
, j
->unit
, j
->transaction_next
);
803 hashmap_remove_value(tr
->jobs
, j
->unit
, j
);
805 if (j
->transaction_next
)
806 j
->transaction_next
->transaction_prev
= j
->transaction_prev
;
808 j
->transaction_prev
= j
->transaction_next
= NULL
;
810 while (j
->subject_list
)
811 job_dependency_free(j
->subject_list
);
813 while (j
->object_list
) {
814 Job
*other
= j
->object_list
->matters
? j
->object_list
->subject
: NULL
;
816 job_dependency_free(j
->object_list
);
818 if (other
&& delete_dependencies
) {
819 log_debug_unit(other
->unit
->id
,
820 "Deleting job %s/%s as dependency of job %s/%s",
821 other
->unit
->id
, job_type_to_string(other
->type
),
822 j
->unit
->id
, job_type_to_string(j
->type
));
823 transaction_delete_job(tr
, other
, delete_dependencies
);
828 int transaction_add_job_and_dependencies(
836 bool ignore_requirements
,
846 assert(type
< _JOB_TYPE_MAX
);
847 assert(type
< _JOB_TYPE_MAX_IN_TRANSACTION
);
850 /* log_debug("Pulling in %s/%s from %s/%s", */
851 /* unit->id, job_type_to_string(type), */
852 /* by ? by->unit->id : "NA", */
853 /* by ? job_type_to_string(by->type) : "NA"); */
855 if (unit
->load_state
!= UNIT_LOADED
&&
856 unit
->load_state
!= UNIT_ERROR
&&
857 unit
->load_state
!= UNIT_NOT_FOUND
&&
858 unit
->load_state
!= UNIT_MASKED
) {
859 dbus_set_error(e
, BUS_ERROR_LOAD_FAILED
, "Unit %s is not loaded properly.", unit
->id
);
863 if (type
!= JOB_STOP
&& (unit
->load_state
== UNIT_ERROR
|| unit
->load_state
== UNIT_NOT_FOUND
)) {
864 dbus_set_error(e
, BUS_ERROR_LOAD_FAILED
,
865 "Unit %s failed to load: %s. "
866 "See system logs and 'systemctl status %s' for details.",
868 strerror(-unit
->load_error
),
873 if (type
!= JOB_STOP
&& unit
->load_state
== UNIT_MASKED
) {
874 dbus_set_error(e
, BUS_ERROR_MASKED
, "Unit %s is masked.", unit
->id
);
875 return -EADDRNOTAVAIL
;
878 if (!unit_job_is_applicable(unit
, type
)) {
879 dbus_set_error(e
, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE
, "Job type %s is not applicable for unit %s.", job_type_to_string(type
), unit
->id
);
883 /* First add the job. */
884 ret
= transaction_add_one_job(tr
, type
, unit
, override
, &is_new
);
888 ret
->ignore_order
= ret
->ignore_order
|| ignore_order
;
890 /* Then, add a link to the job. */
892 if (!job_dependency_new(by
, ret
, matters
, conflicts
))
895 /* If the job has no parent job, it is the anchor job. */
896 assert(!tr
->anchor_job
);
897 tr
->anchor_job
= ret
;
900 if (is_new
&& !ignore_requirements
&& type
!= JOB_NOP
) {
903 /* If we are following some other unit, make sure we
904 * add all dependencies of everybody following. */
905 if (unit_following_set(ret
->unit
, &following
) > 0) {
906 SET_FOREACH(dep
, following
, i
) {
907 r
= transaction_add_job_and_dependencies(tr
, type
, dep
, ret
, false, override
, false, false, ignore_order
, e
);
909 log_warning_unit(dep
->id
,
910 "Cannot add dependency job for unit %s, ignoring: %s",
911 dep
->id
, bus_error(e
, r
));
921 /* Finally, recursively add in all dependencies. */
922 if (type
== JOB_START
|| type
== JOB_RESTART
) {
923 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_REQUIRES
], i
) {
924 r
= transaction_add_job_and_dependencies(tr
, JOB_START
, dep
, ret
, true, override
, false, false, ignore_order
, e
);
934 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_BINDS_TO
], i
) {
935 r
= transaction_add_job_and_dependencies(tr
, JOB_START
, dep
, ret
, true, override
, false, false, ignore_order
, e
);
945 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_REQUIRES_OVERRIDABLE
], i
) {
946 r
= transaction_add_job_and_dependencies(tr
, JOB_START
, dep
, ret
, !override
, override
, false, false, ignore_order
, e
);
948 log_full_unit(r
== -EADDRNOTAVAIL
? LOG_DEBUG
: LOG_WARNING
, dep
->id
,
949 "Cannot add dependency job for unit %s, ignoring: %s",
950 dep
->id
, bus_error(e
, r
));
957 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_WANTS
], i
) {
958 r
= transaction_add_job_and_dependencies(tr
, JOB_START
, dep
, ret
, false, false, false, false, ignore_order
, e
);
960 log_full_unit(r
== -EADDRNOTAVAIL
? LOG_DEBUG
: LOG_WARNING
, dep
->id
,
961 "Cannot add dependency job for unit %s, ignoring: %s",
962 dep
->id
, bus_error(e
, r
));
969 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_REQUISITE
], i
) {
970 r
= transaction_add_job_and_dependencies(tr
, JOB_VERIFY_ACTIVE
, dep
, ret
, true, override
, false, false, ignore_order
, e
);
980 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_REQUISITE_OVERRIDABLE
], i
) {
981 r
= transaction_add_job_and_dependencies(tr
, JOB_VERIFY_ACTIVE
, dep
, ret
, !override
, override
, false, false, ignore_order
, e
);
983 log_full_unit(r
== -EADDRNOTAVAIL
? LOG_DEBUG
: LOG_WARNING
, dep
->id
,
984 "Cannot add dependency job for unit %s, ignoring: %s",
985 dep
->id
, bus_error(e
, r
));
992 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_CONFLICTS
], i
) {
993 r
= transaction_add_job_and_dependencies(tr
, JOB_STOP
, dep
, ret
, true, override
, true, false, ignore_order
, e
);
1003 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_CONFLICTED_BY
], i
) {
1004 r
= transaction_add_job_and_dependencies(tr
, JOB_STOP
, dep
, ret
, false, override
, false, false, ignore_order
, e
);
1006 log_warning_unit(dep
->id
,
1007 "Cannot add dependency job for unit %s, ignoring: %s",
1008 dep
->id
, bus_error(e
, r
));
1017 if (type
== JOB_STOP
|| type
== JOB_RESTART
) {
1019 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_REQUIRED_BY
], i
) {
1020 r
= transaction_add_job_and_dependencies(tr
, type
, dep
, ret
, true, override
, false, false, ignore_order
, e
);
1030 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_BOUND_BY
], i
) {
1031 r
= transaction_add_job_and_dependencies(tr
, type
, dep
, ret
, true, override
, false, false, ignore_order
, e
);
1041 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_CONSISTS_OF
], i
) {
1042 r
= transaction_add_job_and_dependencies(tr
, type
, dep
, ret
, true, override
, false, false, ignore_order
, e
);
1054 if (type
== JOB_RELOAD
) {
1056 SET_FOREACH(dep
, ret
->unit
->dependencies
[UNIT_PROPAGATES_RELOAD_TO
], i
) {
1057 r
= transaction_add_job_and_dependencies(tr
, JOB_RELOAD
, dep
, ret
, false, override
, false, false, ignore_order
, e
);
1059 log_warning_unit(dep
->id
,
1060 "Cannot add dependency reload job for unit %s, ignoring: %s",
1061 dep
->id
, bus_error(e
, r
));
1069 /* JOB_VERIFY_STARTED require no dependency handling */
1078 int transaction_add_isolate_jobs(Transaction
*tr
, Manager
*m
) {
1087 HASHMAP_FOREACH_KEY(u
, k
, m
->units
, i
) {
1089 /* ignore aliases */
1093 if (u
->ignore_on_isolate
)
1096 /* No need to stop inactive jobs */
1097 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u
)) && !u
->job
)
1100 /* Is there already something listed for this? */
1101 if (hashmap_get(tr
->jobs
, u
))
1104 r
= transaction_add_job_and_dependencies(tr
, JOB_STOP
, u
, tr
->anchor_job
, true, false, false, false, false, NULL
);
1106 log_warning_unit(u
->id
,
1107 "Cannot add isolate job for unit %s, ignoring: %s",
1108 u
->id
, strerror(-r
));
1114 Transaction
*transaction_new(bool irreversible
) {
1117 tr
= new0(Transaction
, 1);
1121 tr
->jobs
= hashmap_new(trivial_hash_func
, trivial_compare_func
);
1127 tr
->irreversible
= irreversible
;
1132 void transaction_free(Transaction
*tr
) {
1133 assert(hashmap_isempty(tr
->jobs
));
1134 hashmap_free(tr
->jobs
);