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/>.
22 #include "transaction.h"
23 #include "bus-errors.h"
25 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
27 static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) {
31 /* Deletes one job from the transaction */
33 transaction_unlink_job(tr, j, delete_dependencies);
38 static void transaction_delete_unit(Transaction *tr, Unit *u) {
41 /* Deletes all jobs associated with a certain unit from the
44 while ((j = hashmap_get(tr->jobs, u)))
45 transaction_delete_job(tr, j, true);
48 void transaction_abort(Transaction *tr) {
53 while ((j = hashmap_first(tr->jobs)))
54 transaction_delete_job(tr, j, true);
56 assert(hashmap_isempty(tr->jobs));
59 static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
62 /* A recursive sweep through the graph that marks all units
63 * that matter to the anchor job, i.e. are directly or
64 * indirectly a dependency of the anchor job via paths that
65 * are fully marked as mattering. */
67 j->matters_to_anchor = true;
68 j->generation = generation;
70 LIST_FOREACH(subject, l, j->subject_list) {
72 /* This link does not matter */
76 /* This unit has already been marked */
77 if (l->object->generation == generation)
80 transaction_find_jobs_that_matter_to_anchor(l->object, generation);
84 static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) {
85 JobDependency *l, *last;
89 assert(j->unit == other->unit);
90 assert(!j->installed);
92 /* Merges 'other' into 'j' and then deletes 'other'. */
95 j->state = JOB_WAITING;
96 j->override = j->override || other->override;
98 j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
100 /* Patch us in as new owner of the JobDependency objects */
102 LIST_FOREACH(subject, l, other->subject_list) {
103 assert(l->subject == other);
108 /* Merge both lists */
110 last->subject_next = j->subject_list;
112 j->subject_list->subject_prev = last;
113 j->subject_list = other->subject_list;
116 /* Patch us in as new owner of the JobDependency objects */
118 LIST_FOREACH(object, l, other->object_list) {
119 assert(l->object == other);
124 /* Merge both lists */
126 last->object_next = j->object_list;
128 j->object_list->object_prev = last;
129 j->object_list = other->object_list;
132 /* Kill the other job */
133 other->subject_list = NULL;
134 other->object_list = NULL;
135 transaction_delete_job(tr, other, true);
138 static bool job_is_conflicted_by(Job *j) {
143 /* Returns true if this job is pulled in by a least one
144 * ConflictedBy dependency. */
146 LIST_FOREACH(object, l, j->object_list)
153 static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
158 /* Tries to delete one item in the linked list
159 * j->transaction_next->transaction_next->... that conflicts
160 * with another one, in an attempt to make an inconsistent
161 * transaction work. */
163 /* We rely here on the fact that if a merged with b does not
164 * merge with c, either a or b merge with c neither */
165 LIST_FOREACH(transaction, j, j)
166 LIST_FOREACH(transaction, k, j->transaction_next) {
169 /* Is this one mergeable? Then skip it */
170 if (job_type_is_mergeable(j->type, k->type))
173 /* Ok, we found two that conflict, let's see if we can
174 * drop one of them */
175 if (!j->matters_to_anchor && !k->matters_to_anchor) {
177 /* Both jobs don't matter, so let's
178 * find the one that is smarter to
179 * remove. Let's think positive and
180 * rather remove stops then starts --
181 * except if something is being
182 * stopped because it is conflicted by
183 * another unit in which case we
184 * rather remove the start. */
186 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)));
187 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)));
189 if (j->type == JOB_STOP) {
191 if (job_is_conflicted_by(j))
196 } else if (k->type == JOB_STOP) {
198 if (job_is_conflicted_by(k))
205 } else if (!j->matters_to_anchor)
207 else if (!k->matters_to_anchor)
212 /* Ok, we can drop one, so let's do so. */
213 log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->id, job_type_to_string(d->type));
214 transaction_delete_job(tr, d, true);
221 static int transaction_merge_jobs(Transaction *tr, DBusError *e) {
228 /* First step, check whether any of the jobs for one specific
229 * task conflict. If so, try to drop one of them. */
230 HASHMAP_FOREACH(j, tr->jobs, i) {
235 LIST_FOREACH(transaction, k, j->transaction_next) {
236 if (job_type_merge(&t, k->type) >= 0)
239 /* OK, we could not merge all jobs for this
240 * action. Let's see if we can get rid of one
243 r = delete_one_unmergeable_job(tr, j);
245 /* Ok, we managed to drop one, now
246 * let's ask our callers to call us
247 * again after garbage collecting */
250 /* We couldn't merge anything. Failure */
251 dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
252 job_type_to_string(t), job_type_to_string(k->type), k->unit->id);
257 /* Second step, merge the jobs. */
258 HASHMAP_FOREACH(j, tr->jobs, i) {
262 /* Merge all transactions */
263 LIST_FOREACH(transaction, k, j->transaction_next)
264 assert_se(job_type_merge(&t, k->type) == 0);
266 while ((k = j->transaction_next)) {
267 if (tr->anchor_job == k) {
268 transaction_merge_and_delete_job(tr, k, j, t);
271 transaction_merge_and_delete_job(tr, j, k, t);
274 assert(!j->transaction_next);
275 assert(!j->transaction_prev);
281 static void transaction_drop_redundant(Transaction *tr) {
285 /* Goes through the transaction and removes all jobs of the units
286 * whose jobs are all noops. If not all of a unit's jobs are
287 * redundant, they are kept. */
292 HASHMAP_FOREACH(j, tr->jobs, i) {
295 LIST_FOREACH(transaction, k, j) {
297 if (tr->anchor_job == k ||
298 !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
299 (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type)))
303 /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
304 transaction_delete_job(tr, j, false);
310 static bool unit_matters_to_anchor(Unit *u, Job *j) {
312 assert(!j->transaction_prev);
314 /* Checks whether at least one of the jobs for this unit
315 * matters to the anchor. */
317 LIST_FOREACH(transaction, j, j)
318 if (j->matters_to_anchor)
324 static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, DBusError *e) {
331 assert(!j->transaction_prev);
333 /* Does a recursive sweep through the ordering graph, looking
334 * for a cycle. If we find cycle we try to break it. */
336 /* Have we seen this before? */
337 if (j->generation == generation) {
340 /* If the marker is NULL we have been here already and
341 * decided the job was loop-free from here. Hence
342 * shortcut things and return right-away. */
346 /* So, the marker is not NULL and we already have been
347 * here. We have a cycle. Let's try to break it. We go
348 * backwards in our path and try to find a suitable
349 * job to remove. We use the marker to find our way
350 * back, since smart how we are we stored our way back
352 log_warning("Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type));
355 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
357 log_info("Walked on cycle path to %s/%s", k->unit->id, job_type_to_string(k->type));
360 !unit_matters_to_anchor(k->unit, k)) {
361 /* Ok, we can drop this one, so let's
366 /* Check if this in fact was the beginning of
374 log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type));
375 transaction_delete_unit(tr, delete->unit);
379 log_error("Unable to break cycle");
381 dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details.");
385 /* Make the marker point to where we come from, so that we can
386 * find our way backwards if we want to break a cycle. We use
387 * a special marker for the beginning: we point to
389 j->marker = from ? from : j;
390 j->generation = generation;
392 /* We assume that the the dependencies are bidirectional, and
393 * hence can ignore UNIT_AFTER */
394 SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
397 /* Is there a job for this unit? */
398 o = hashmap_get(tr->jobs, u);
400 /* Ok, there is no job for this in the
401 * transaction, but maybe there is already one
408 r = transaction_verify_order_one(tr, o, j, generation, e);
413 /* Ok, let's backtrack, and remember that this entry is not on
414 * our path anymore. */
420 static int transaction_verify_order(Transaction *tr, unsigned *generation, DBusError *e) {
429 /* Check if the ordering graph is cyclic. If it is, try to fix
430 * that up by dropping one of the jobs. */
434 HASHMAP_FOREACH(j, tr->jobs, i)
435 if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0)
441 static void transaction_collect_garbage(Transaction *tr) {
447 /* Drop jobs that are not required by any other job */
450 HASHMAP_FOREACH(j, tr->jobs, i) {
451 if (tr->anchor_job == j || j->object_list) {
452 /* log_debug("Keeping job %s/%s because of %s/%s", */
453 /* j->unit->id, job_type_to_string(j->type), */
454 /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
455 /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
459 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
460 transaction_delete_job(tr, j, true);
465 static int transaction_is_destructive(Transaction *tr, DBusError *e) {
471 /* Checks whether applying this transaction means that
472 * existing jobs would be replaced */
474 HASHMAP_FOREACH(j, tr->jobs, i) {
477 assert(!j->transaction_prev);
478 assert(!j->transaction_next);
481 !job_type_is_superset(j->type, j->unit->job->type)) {
483 dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
491 static void transaction_minimize_impact(Transaction *tr) {
497 /* Drops all unnecessary jobs that reverse already active jobs
498 * or that stop a running service. */
501 HASHMAP_FOREACH(j, tr->jobs, i) {
502 LIST_FOREACH(transaction, j, j) {
503 bool stops_running_service, changes_existing_job;
505 /* If it matters, we shouldn't drop it */
506 if (j->matters_to_anchor)
509 /* Would this stop a running service?
510 * Would this change an existing job?
511 * If so, let's drop this entry */
513 stops_running_service =
514 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
516 changes_existing_job =
518 job_type_is_conflicting(j->type, j->unit->job->type);
520 if (!stops_running_service && !changes_existing_job)
523 if (stops_running_service)
524 log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
526 if (changes_existing_job)
527 log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
529 /* Ok, let's get rid of this */
530 log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type));
532 transaction_delete_job(tr, j, true);
538 static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
543 /* Moves the transaction jobs to the set of active jobs */
545 if (mode == JOB_ISOLATE) {
547 /* When isolating first kill all installed jobs which
548 * aren't part of the new transaction */
550 HASHMAP_FOREACH(j, m->jobs, i) {
551 assert(j->installed);
553 if (hashmap_get(tr->jobs, j->unit))
556 /* 'j' itself is safe to remove, but if other jobs
557 are invalidated recursively, our iterator may become
558 invalid and we need to start over. */
559 if (job_finish_and_invalidate(j, JOB_CANCELED) > 0)
564 HASHMAP_FOREACH(j, tr->jobs, i) {
566 assert(!j->transaction_prev);
567 assert(!j->transaction_next);
569 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
574 while ((j = hashmap_steal_first(tr->jobs))) {
577 /* Clean the job dependencies */
578 transaction_unlink_job(tr, j, false);
580 installed_job = job_install(j);
581 if (installed_job != j) {
582 /* j has been merged into a previously installed job */
583 if (tr->anchor_job == j)
584 tr->anchor_job = installed_job;
585 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
590 job_add_to_run_queue(j);
591 job_add_to_dbus_queue(j);
599 HASHMAP_FOREACH(j, tr->jobs, i)
600 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
605 int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
609 unsigned generation = 1;
613 /* This applies the changes recorded in tr->jobs to
614 * the actual list of jobs, if possible. */
616 /* Reset the generation counter of all installed jobs. The detection of cycles
617 * looks at installed jobs. If they had a non-zero generation from some previous
618 * walk of the graph, the algorithm would break. */
619 HASHMAP_FOREACH(j, m->jobs, i)
622 /* First step: figure out which jobs matter */
623 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
625 /* Second step: Try not to stop any running services if
626 * we don't have to. Don't try to reverse running
627 * jobs if we don't have to. */
628 if (mode == JOB_FAIL)
629 transaction_minimize_impact(tr);
631 /* Third step: Drop redundant jobs */
632 transaction_drop_redundant(tr);
635 /* Fourth step: Let's remove unneeded jobs that might
637 if (mode != JOB_ISOLATE)
638 transaction_collect_garbage(tr);
640 /* Fifth step: verify order makes sense and correct
641 * cycles if necessary and possible */
642 r = transaction_verify_order(tr, &generation, e);
647 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
651 /* Let's see if the resulting transaction ordering
652 * graph is still cyclic... */
656 /* Sixth step: let's drop unmergeable entries if
657 * necessary and possible, merge entries we can
659 r = transaction_merge_jobs(tr, e);
664 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
668 /* Seventh step: an entry got dropped, let's garbage
669 * collect its dependencies. */
670 if (mode != JOB_ISOLATE)
671 transaction_collect_garbage(tr);
673 /* Let's see if the resulting transaction still has
674 * unmergeable entries ... */
677 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
678 transaction_drop_redundant(tr);
680 /* Ninth step: check whether we can actually apply this */
681 if (mode == JOB_FAIL) {
682 r = transaction_is_destructive(tr, e);
684 log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
689 /* Tenth step: apply changes */
690 r = transaction_apply(tr, m, mode);
692 log_warning("Failed to apply transaction: %s", strerror(-r));
696 assert(hashmap_isempty(tr->jobs));
701 static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
707 /* Looks for an existing prospective job and returns that. If
708 * it doesn't exist it is created and added to the prospective
711 f = hashmap_get(tr->jobs, unit);
713 LIST_FOREACH(transaction, j, f) {
714 assert(j->unit == unit);
716 if (j->type == type) {
723 j = job_new(unit, type);
729 j->matters_to_anchor = false;
730 j->override = override;
732 LIST_PREPEND(Job, transaction, f, j);
734 if (hashmap_replace(tr->jobs, unit, f) < 0) {
735 LIST_REMOVE(Job, transaction, f, j);
743 /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
748 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
752 if (j->transaction_prev)
753 j->transaction_prev->transaction_next = j->transaction_next;
754 else if (j->transaction_next)
755 hashmap_replace(tr->jobs, j->unit, j->transaction_next);
757 hashmap_remove_value(tr->jobs, j->unit, j);
759 if (j->transaction_next)
760 j->transaction_next->transaction_prev = j->transaction_prev;
762 j->transaction_prev = j->transaction_next = NULL;
764 while (j->subject_list)
765 job_dependency_free(j->subject_list);
767 while (j->object_list) {
768 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
770 job_dependency_free(j->object_list);
772 if (other && delete_dependencies) {
773 log_debug("Deleting job %s/%s as dependency of job %s/%s",
774 other->unit->id, job_type_to_string(other->type),
775 j->unit->id, job_type_to_string(j->type));
776 transaction_delete_job(tr, other, delete_dependencies);
781 int transaction_add_job_and_dependencies(
789 bool ignore_requirements,
799 assert(type < _JOB_TYPE_MAX);
802 /* log_debug("Pulling in %s/%s from %s/%s", */
803 /* unit->id, job_type_to_string(type), */
804 /* by ? by->unit->id : "NA", */
805 /* by ? job_type_to_string(by->type) : "NA"); */
807 if (unit->load_state != UNIT_LOADED &&
808 unit->load_state != UNIT_ERROR &&
809 unit->load_state != UNIT_MASKED) {
810 dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
814 if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
815 dbus_set_error(e, BUS_ERROR_LOAD_FAILED,
816 "Unit %s failed to load: %s. "
817 "See system logs and 'systemctl status %s' for details.",
819 strerror(-unit->load_error),
824 if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
825 dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
826 return -EADDRNOTAVAIL;
829 if (!unit_job_is_applicable(unit, type)) {
830 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);
834 /* First add the job. */
835 ret = transaction_add_one_job(tr, type, unit, override, &is_new);
839 ret->ignore_order = ret->ignore_order || ignore_order;
841 /* Then, add a link to the job. */
843 if (!job_dependency_new(by, ret, matters, conflicts))
846 /* If the job has no parent job, it is the anchor job. */
847 assert(!tr->anchor_job);
848 tr->anchor_job = ret;
850 if (is_new && !ignore_requirements) {
853 /* If we are following some other unit, make sure we
854 * add all dependencies of everybody following. */
855 if (unit_following_set(ret->unit, &following) > 0) {
856 SET_FOREACH(dep, following, i) {
857 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
859 log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
869 /* Finally, recursively add in all dependencies. */
870 if (type == JOB_START || type == JOB_RELOAD_OR_START || type == JOB_RESTART) {
871 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
872 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
882 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BIND_TO], i) {
883 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
893 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) {
894 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
896 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
897 "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
904 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
905 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
907 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
908 "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
915 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
916 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
926 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) {
927 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
929 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
930 "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
937 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
938 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
948 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
949 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
951 log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
960 if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
962 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
963 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
973 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) {
974 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
985 if (type == JOB_RELOAD || type == JOB_RELOAD_OR_START) {
987 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATE_RELOAD_TO], i) {
988 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
990 log_warning("Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
998 /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */
1007 int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
1016 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1018 /* ignore aliases */
1022 if (u->ignore_on_isolate)
1025 /* No need to stop inactive jobs */
1026 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
1029 /* Is there already something listed for this? */
1030 if (hashmap_get(tr->jobs, u))
1033 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
1035 log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r));
1041 Transaction *transaction_new(void) {
1044 tr = new0(Transaction, 1);
1048 tr->jobs = hashmap_new(trivial_hash_func, trivial_compare_func);
1057 void transaction_free(Transaction *tr) {
1058 assert(hashmap_isempty(tr->jobs));
1059 hashmap_free(tr->jobs);