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"
28 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
30 static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) {
34 /* Deletes one job from the transaction */
36 transaction_unlink_job(tr, j, delete_dependencies);
41 static void transaction_delete_unit(Transaction *tr, Unit *u) {
44 /* Deletes all jobs associated with a certain unit from the
47 while ((j = hashmap_get(tr->jobs, u)))
48 transaction_delete_job(tr, j, true);
51 void transaction_abort(Transaction *tr) {
56 while ((j = hashmap_first(tr->jobs)))
57 transaction_delete_job(tr, j, false);
59 assert(hashmap_isempty(tr->jobs));
62 static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
65 /* A recursive sweep through the graph that marks all units
66 * that matter to the anchor job, i.e. are directly or
67 * indirectly a dependency of the anchor job via paths that
68 * are fully marked as mattering. */
70 j->matters_to_anchor = true;
71 j->generation = generation;
73 LIST_FOREACH(subject, l, j->subject_list) {
75 /* This link does not matter */
79 /* This unit has already been marked */
80 if (l->object->generation == generation)
83 transaction_find_jobs_that_matter_to_anchor(l->object, generation);
87 static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) {
88 JobDependency *l, *last;
92 assert(j->unit == other->unit);
93 assert(!j->installed);
95 /* Merges 'other' into 'j' and then deletes 'other'. */
98 j->state = JOB_WAITING;
99 j->override = j->override || other->override;
101 j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
103 /* Patch us in as new owner of the JobDependency objects */
105 LIST_FOREACH(subject, l, other->subject_list) {
106 assert(l->subject == other);
111 /* Merge both lists */
113 last->subject_next = j->subject_list;
115 j->subject_list->subject_prev = last;
116 j->subject_list = other->subject_list;
119 /* Patch us in as new owner of the JobDependency objects */
121 LIST_FOREACH(object, l, other->object_list) {
122 assert(l->object == other);
127 /* Merge both lists */
129 last->object_next = j->object_list;
131 j->object_list->object_prev = last;
132 j->object_list = other->object_list;
135 /* Kill the other job */
136 other->subject_list = NULL;
137 other->object_list = NULL;
138 transaction_delete_job(tr, other, true);
141 static bool job_is_conflicted_by(Job *j) {
146 /* Returns true if this job is pulled in by a least one
147 * ConflictedBy dependency. */
149 LIST_FOREACH(object, l, j->object_list)
156 static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
161 /* Tries to delete one item in the linked list
162 * j->transaction_next->transaction_next->... that conflicts
163 * with another one, in an attempt to make an inconsistent
164 * transaction work. */
166 /* We rely here on the fact that if a merged with b does not
167 * merge with c, either a or b merge with c neither */
168 LIST_FOREACH(transaction, j, j)
169 LIST_FOREACH(transaction, k, j->transaction_next) {
172 /* Is this one mergeable? Then skip it */
173 if (job_type_is_mergeable(j->type, k->type))
176 /* Ok, we found two that conflict, let's see if we can
177 * drop one of them */
178 if (!j->matters_to_anchor && !k->matters_to_anchor) {
180 /* Both jobs don't matter, so let's
181 * find the one that is smarter to
182 * remove. Let's think positive and
183 * rather remove stops then starts --
184 * except if something is being
185 * stopped because it is conflicted by
186 * another unit in which case we
187 * rather remove the start. */
189 log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
190 log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
192 if (j->type == JOB_STOP) {
194 if (job_is_conflicted_by(j))
199 } else if (k->type == JOB_STOP) {
201 if (job_is_conflicted_by(k))
208 } else if (!j->matters_to_anchor)
210 else if (!k->matters_to_anchor)
215 /* Ok, we can drop one, so let's do so. */
216 log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->id, job_type_to_string(d->type));
217 transaction_delete_job(tr, d, true);
224 static int transaction_merge_jobs(Transaction *tr, DBusError *e) {
231 /* First step, check whether any of the jobs for one specific
232 * task conflict. If so, try to drop one of them. */
233 HASHMAP_FOREACH(j, tr->jobs, i) {
238 LIST_FOREACH(transaction, k, j->transaction_next) {
239 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
242 /* OK, we could not merge all jobs for this
243 * action. Let's see if we can get rid of one
246 r = delete_one_unmergeable_job(tr, j);
248 /* Ok, we managed to drop one, now
249 * let's ask our callers to call us
250 * again after garbage collecting */
253 /* We couldn't merge anything. Failure */
254 dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
255 job_type_to_string(t), job_type_to_string(k->type), k->unit->id);
260 /* Second step, merge the jobs. */
261 HASHMAP_FOREACH(j, tr->jobs, i) {
265 /* Merge all transaction jobs for j->unit */
266 LIST_FOREACH(transaction, k, j->transaction_next)
267 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
269 while ((k = j->transaction_next)) {
270 if (tr->anchor_job == k) {
271 transaction_merge_and_delete_job(tr, k, j, t);
274 transaction_merge_and_delete_job(tr, j, k, t);
277 assert(!j->transaction_next);
278 assert(!j->transaction_prev);
284 static void transaction_drop_redundant(Transaction *tr) {
288 /* Goes through the transaction and removes all jobs of the units
289 * whose jobs are all noops. If not all of a unit's jobs are
290 * redundant, they are kept. */
295 HASHMAP_FOREACH(j, tr->jobs, i) {
298 LIST_FOREACH(transaction, k, j) {
300 if (tr->anchor_job == k ||
301 !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
302 (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type)))
306 /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
307 transaction_delete_job(tr, j, false);
313 static bool unit_matters_to_anchor(Unit *u, Job *j) {
315 assert(!j->transaction_prev);
317 /* Checks whether at least one of the jobs for this unit
318 * matters to the anchor. */
320 LIST_FOREACH(transaction, j, j)
321 if (j->matters_to_anchor)
327 static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, DBusError *e) {
334 assert(!j->transaction_prev);
336 /* Does a recursive sweep through the ordering graph, looking
337 * for a cycle. If we find cycle we try to break it. */
339 /* Have we seen this before? */
340 if (j->generation == generation) {
343 /* If the marker is NULL we have been here already and
344 * decided the job was loop-free from here. Hence
345 * shortcut things and return right-away. */
349 /* So, the marker is not NULL and we already have been
350 * here. We have a cycle. Let's try to break it. We go
351 * backwards in our path and try to find a suitable
352 * job to remove. We use the marker to find our way
353 * back, since smart how we are we stored our way back
355 log_warning("Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type));
358 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
360 log_info("Walked on cycle path to %s/%s", k->unit->id, job_type_to_string(k->type));
363 !unit_matters_to_anchor(k->unit, k)) {
364 /* Ok, we can drop this one, so let's
369 /* Check if this in fact was the beginning of
377 log_error("Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type));
378 status_printf(ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, true, "Ordering cycle found, skip %s", unit_description(delete->unit));
379 transaction_delete_unit(tr, delete->unit);
383 log_error("Unable to break cycle");
385 dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details.");
389 /* Make the marker point to where we come from, so that we can
390 * find our way backwards if we want to break a cycle. We use
391 * a special marker for the beginning: we point to
393 j->marker = from ? from : j;
394 j->generation = generation;
396 /* We assume that the dependencies are bidirectional, and
397 * hence can ignore UNIT_AFTER */
398 SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
401 /* Is there a job for this unit? */
402 o = hashmap_get(tr->jobs, u);
404 /* Ok, there is no job for this in the
405 * transaction, but maybe there is already one
412 r = transaction_verify_order_one(tr, o, j, generation, e);
417 /* Ok, let's backtrack, and remember that this entry is not on
418 * our path anymore. */
424 static int transaction_verify_order(Transaction *tr, unsigned *generation, DBusError *e) {
433 /* Check if the ordering graph is cyclic. If it is, try to fix
434 * that up by dropping one of the jobs. */
438 HASHMAP_FOREACH(j, tr->jobs, i)
439 if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0)
445 static void transaction_collect_garbage(Transaction *tr) {
451 /* Drop jobs that are not required by any other job */
454 HASHMAP_FOREACH(j, tr->jobs, i) {
455 if (tr->anchor_job == j || j->object_list) {
456 /* log_debug("Keeping job %s/%s because of %s/%s", */
457 /* j->unit->id, job_type_to_string(j->type), */
458 /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
459 /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
463 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
464 transaction_delete_job(tr, j, true);
469 static int transaction_is_destructive(Transaction *tr, DBusError *e) {
475 /* Checks whether applying this transaction means that
476 * existing jobs would be replaced */
478 HASHMAP_FOREACH(j, tr->jobs, i) {
481 assert(!j->transaction_prev);
482 assert(!j->transaction_next);
485 !job_type_is_superset(j->type, j->unit->job->type)) {
487 dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
495 static void transaction_minimize_impact(Transaction *tr) {
501 /* Drops all unnecessary jobs that reverse already active jobs
502 * or that stop a running service. */
505 HASHMAP_FOREACH(j, tr->jobs, i) {
506 LIST_FOREACH(transaction, j, j) {
507 bool stops_running_service, changes_existing_job;
509 /* If it matters, we shouldn't drop it */
510 if (j->matters_to_anchor)
513 /* Would this stop a running service?
514 * Would this change an existing job?
515 * If so, let's drop this entry */
517 stops_running_service =
518 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
520 changes_existing_job =
522 job_type_is_conflicting(j->type, j->unit->job->type);
524 if (!stops_running_service && !changes_existing_job)
527 if (stops_running_service)
528 log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
530 if (changes_existing_job)
531 log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
533 /* Ok, let's get rid of this */
534 log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type));
536 transaction_delete_job(tr, j, true);
542 static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
547 /* Moves the transaction jobs to the set of active jobs */
549 if (mode == JOB_ISOLATE) {
551 /* When isolating first kill all installed jobs which
552 * aren't part of the new transaction */
553 HASHMAP_FOREACH(j, m->jobs, i) {
554 assert(j->installed);
556 if (hashmap_get(tr->jobs, j->unit))
559 /* Not invalidating recursively. Avoids triggering
560 * OnFailure= actions of dependent jobs. Also avoids
561 * invalidating our iterator. */
562 job_finish_and_invalidate(j, JOB_CANCELED, false);
566 HASHMAP_FOREACH(j, tr->jobs, i) {
568 assert(!j->transaction_prev);
569 assert(!j->transaction_next);
571 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
576 while ((j = hashmap_steal_first(tr->jobs))) {
579 /* Clean the job dependencies */
580 transaction_unlink_job(tr, j, false);
582 installed_job = job_install(j);
583 if (installed_job != j) {
584 /* j has been merged into a previously installed job */
585 if (tr->anchor_job == j)
586 tr->anchor_job = installed_job;
587 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
592 job_add_to_run_queue(j);
593 job_add_to_dbus_queue(j);
601 HASHMAP_FOREACH(j, tr->jobs, i)
602 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
607 int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
611 unsigned generation = 1;
615 /* This applies the changes recorded in tr->jobs to
616 * the actual list of jobs, if possible. */
618 /* Reset the generation counter of all installed jobs. The detection of cycles
619 * looks at installed jobs. If they had a non-zero generation from some previous
620 * walk of the graph, the algorithm would break. */
621 HASHMAP_FOREACH(j, m->jobs, i)
624 /* First step: figure out which jobs matter */
625 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
627 /* Second step: Try not to stop any running services if
628 * we don't have to. Don't try to reverse running
629 * jobs if we don't have to. */
630 if (mode == JOB_FAIL)
631 transaction_minimize_impact(tr);
633 /* Third step: Drop redundant jobs */
634 transaction_drop_redundant(tr);
637 /* Fourth step: Let's remove unneeded jobs that might
639 if (mode != JOB_ISOLATE)
640 transaction_collect_garbage(tr);
642 /* Fifth step: verify order makes sense and correct
643 * cycles if necessary and possible */
644 r = transaction_verify_order(tr, &generation, e);
649 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
653 /* Let's see if the resulting transaction ordering
654 * graph is still cyclic... */
658 /* Sixth step: let's drop unmergeable entries if
659 * necessary and possible, merge entries we can
661 r = transaction_merge_jobs(tr, e);
666 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
670 /* Seventh step: an entry got dropped, let's garbage
671 * collect its dependencies. */
672 if (mode != JOB_ISOLATE)
673 transaction_collect_garbage(tr);
675 /* Let's see if the resulting transaction still has
676 * unmergeable entries ... */
679 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
680 transaction_drop_redundant(tr);
682 /* Ninth step: check whether we can actually apply this */
683 if (mode == JOB_FAIL) {
684 r = transaction_is_destructive(tr, e);
686 log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
691 /* Tenth step: apply changes */
692 r = transaction_apply(tr, m, mode);
694 log_warning("Failed to apply transaction: %s", strerror(-r));
698 assert(hashmap_isempty(tr->jobs));
700 if (!hashmap_isempty(m->jobs)) {
701 /* Are there any jobs now? Then make sure we have the
702 * idle pipe around. We don't really care too much
703 * whether this works or not, as the idle pipe is a
704 * feature for cosmetics, not actually useful for
705 * anything beyond that. */
707 if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0)
708 pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
714 static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
720 /* Looks for an existing prospective job and returns that. If
721 * it doesn't exist it is created and added to the prospective
724 f = hashmap_get(tr->jobs, unit);
726 LIST_FOREACH(transaction, j, f) {
727 assert(j->unit == unit);
729 if (j->type == type) {
736 j = job_new(unit, type);
742 j->matters_to_anchor = false;
743 j->override = override;
745 LIST_PREPEND(Job, transaction, f, j);
747 if (hashmap_replace(tr->jobs, unit, f) < 0) {
748 LIST_REMOVE(Job, transaction, f, j);
756 /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
761 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
765 if (j->transaction_prev)
766 j->transaction_prev->transaction_next = j->transaction_next;
767 else if (j->transaction_next)
768 hashmap_replace(tr->jobs, j->unit, j->transaction_next);
770 hashmap_remove_value(tr->jobs, j->unit, j);
772 if (j->transaction_next)
773 j->transaction_next->transaction_prev = j->transaction_prev;
775 j->transaction_prev = j->transaction_next = NULL;
777 while (j->subject_list)
778 job_dependency_free(j->subject_list);
780 while (j->object_list) {
781 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
783 job_dependency_free(j->object_list);
785 if (other && delete_dependencies) {
786 log_debug("Deleting job %s/%s as dependency of job %s/%s",
787 other->unit->id, job_type_to_string(other->type),
788 j->unit->id, job_type_to_string(j->type));
789 transaction_delete_job(tr, other, delete_dependencies);
794 int transaction_add_job_and_dependencies(
802 bool ignore_requirements,
812 assert(type < _JOB_TYPE_MAX);
813 assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
816 /* log_debug("Pulling in %s/%s from %s/%s", */
817 /* unit->id, job_type_to_string(type), */
818 /* by ? by->unit->id : "NA", */
819 /* by ? job_type_to_string(by->type) : "NA"); */
821 if (unit->load_state != UNIT_LOADED &&
822 unit->load_state != UNIT_ERROR &&
823 unit->load_state != UNIT_MASKED) {
824 dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
828 if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
829 dbus_set_error(e, BUS_ERROR_LOAD_FAILED,
830 "Unit %s failed to load: %s. "
831 "See system logs and 'systemctl status %s' for details.",
833 strerror(-unit->load_error),
838 if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
839 dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
840 return -EADDRNOTAVAIL;
843 if (!unit_job_is_applicable(unit, type)) {
844 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);
848 /* First add the job. */
849 ret = transaction_add_one_job(tr, type, unit, override, &is_new);
853 ret->ignore_order = ret->ignore_order || ignore_order;
855 /* Then, add a link to the job. */
857 if (!job_dependency_new(by, ret, matters, conflicts))
860 /* If the job has no parent job, it is the anchor job. */
861 assert(!tr->anchor_job);
862 tr->anchor_job = ret;
865 if (is_new && !ignore_requirements && type != JOB_NOP) {
868 /* If we are following some other unit, make sure we
869 * add all dependencies of everybody following. */
870 if (unit_following_set(ret->unit, &following) > 0) {
871 SET_FOREACH(dep, following, i) {
872 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
874 log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
884 /* Finally, recursively add in all dependencies. */
885 if (type == JOB_START || type == JOB_RESTART) {
886 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
887 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
897 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
898 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
908 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) {
909 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
911 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
912 "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
919 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
920 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
922 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
923 "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
930 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
931 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
941 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) {
942 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
944 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
945 "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
952 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
953 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
963 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
964 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
966 log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
975 if (type == JOB_STOP || type == JOB_RESTART) {
977 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
978 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
988 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) {
989 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
999 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONSISTS_OF], i) {
1000 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1012 if (type == JOB_RELOAD) {
1014 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
1015 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
1017 log_warning("Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
1025 /* JOB_VERIFY_STARTED require no dependency handling */
1034 int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
1043 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1045 /* ignore aliases */
1049 if (u->ignore_on_isolate)
1052 /* No need to stop inactive jobs */
1053 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
1056 /* Is there already something listed for this? */
1057 if (hashmap_get(tr->jobs, u))
1060 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
1062 log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r));
1068 Transaction *transaction_new(void) {
1071 tr = new0(Transaction, 1);
1075 tr->jobs = hashmap_new(trivial_hash_func, trivial_compare_func);
1084 void transaction_free(Transaction *tr) {
1085 assert(hashmap_isempty(tr->jobs));
1086 hashmap_free(tr->jobs);