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;
100 j->irreversible = j->irreversible || other->irreversible;
102 j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
104 /* Patch us in as new owner of the JobDependency objects */
106 LIST_FOREACH(subject, l, other->subject_list) {
107 assert(l->subject == other);
112 /* Merge both lists */
114 last->subject_next = j->subject_list;
116 j->subject_list->subject_prev = last;
117 j->subject_list = other->subject_list;
120 /* Patch us in as new owner of the JobDependency objects */
122 LIST_FOREACH(object, l, other->object_list) {
123 assert(l->object == other);
128 /* Merge both lists */
130 last->object_next = j->object_list;
132 j->object_list->object_prev = last;
133 j->object_list = other->object_list;
136 /* Kill the other job */
137 other->subject_list = NULL;
138 other->object_list = NULL;
139 transaction_delete_job(tr, other, true);
142 static bool job_is_conflicted_by(Job *j) {
147 /* Returns true if this job is pulled in by a least one
148 * ConflictedBy dependency. */
150 LIST_FOREACH(object, l, j->object_list)
157 static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
162 /* Tries to delete one item in the linked list
163 * j->transaction_next->transaction_next->... that conflicts
164 * with another one, in an attempt to make an inconsistent
165 * transaction work. */
167 /* We rely here on the fact that if a merged with b does not
168 * merge with c, either a or b merge with c neither */
169 LIST_FOREACH(transaction, j, j)
170 LIST_FOREACH(transaction, k, j->transaction_next) {
173 /* Is this one mergeable? Then skip it */
174 if (job_type_is_mergeable(j->type, k->type))
177 /* Ok, we found two that conflict, let's see if we can
178 * drop one of them */
179 if (!j->matters_to_anchor && !k->matters_to_anchor) {
181 /* Both jobs don't matter, so let's
182 * find the one that is smarter to
183 * remove. Let's think positive and
184 * rather remove stops then starts --
185 * except if something is being
186 * stopped because it is conflicted by
187 * another unit in which case we
188 * rather remove the start. */
190 log_debug_unit(j->unit->id,
191 "Looking at job %s/%s conflicted_by=%s",
192 j->unit->id, job_type_to_string(j->type),
193 yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
194 log_debug_unit(k->unit->id,
195 "Looking at job %s/%s conflicted_by=%s",
196 k->unit->id, job_type_to_string(k->type),
197 yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
199 if (j->type == JOB_STOP) {
201 if (job_is_conflicted_by(j))
206 } else if (k->type == JOB_STOP) {
208 if (job_is_conflicted_by(k))
215 } else if (!j->matters_to_anchor)
217 else if (!k->matters_to_anchor)
222 /* Ok, we can drop one, so let's do so. */
223 log_debug_unit(d->unit->id,
224 "Fixing conflicting jobs by deleting job %s/%s",
225 d->unit->id, job_type_to_string(d->type));
226 transaction_delete_job(tr, d, true);
233 static int transaction_merge_jobs(Transaction *tr, DBusError *e) {
240 /* First step, check whether any of the jobs for one specific
241 * task conflict. If so, try to drop one of them. */
242 HASHMAP_FOREACH(j, tr->jobs, i) {
247 LIST_FOREACH(transaction, k, j->transaction_next) {
248 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
251 /* OK, we could not merge all jobs for this
252 * action. Let's see if we can get rid of one
255 r = delete_one_unmergeable_job(tr, j);
257 /* Ok, we managed to drop one, now
258 * let's ask our callers to call us
259 * again after garbage collecting */
262 /* We couldn't merge anything. Failure */
263 dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
264 job_type_to_string(t), job_type_to_string(k->type), k->unit->id);
269 /* Second step, merge the jobs. */
270 HASHMAP_FOREACH(j, tr->jobs, i) {
274 /* Merge all transaction jobs for j->unit */
275 LIST_FOREACH(transaction, k, j->transaction_next)
276 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
278 while ((k = j->transaction_next)) {
279 if (tr->anchor_job == k) {
280 transaction_merge_and_delete_job(tr, k, j, t);
283 transaction_merge_and_delete_job(tr, j, k, t);
286 assert(!j->transaction_next);
287 assert(!j->transaction_prev);
293 static void transaction_drop_redundant(Transaction *tr) {
297 /* Goes through the transaction and removes all jobs of the units
298 * whose jobs are all noops. If not all of a unit's jobs are
299 * redundant, they are kept. */
304 HASHMAP_FOREACH(j, tr->jobs, i) {
307 LIST_FOREACH(transaction, k, j) {
309 if (tr->anchor_job == k ||
310 !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
311 (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type)))
315 /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
316 transaction_delete_job(tr, j, false);
322 static bool unit_matters_to_anchor(Unit *u, Job *j) {
324 assert(!j->transaction_prev);
326 /* Checks whether at least one of the jobs for this unit
327 * matters to the anchor. */
329 LIST_FOREACH(transaction, j, j)
330 if (j->matters_to_anchor)
336 static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, DBusError *e) {
343 assert(!j->transaction_prev);
345 /* Does a recursive sweep through the ordering graph, looking
346 * for a cycle. If we find cycle we try to break it. */
348 /* Have we seen this before? */
349 if (j->generation == generation) {
352 /* If the marker is NULL we have been here already and
353 * decided the job was loop-free from here. Hence
354 * shortcut things and return right-away. */
358 /* So, the marker is not NULL and we already have been
359 * here. We have a cycle. Let's try to break it. We go
360 * backwards in our path and try to find a suitable
361 * job to remove. We use the marker to find our way
362 * back, since smart how we are we stored our way back
364 log_warning_unit(j->unit->id,
365 "Found ordering cycle on %s/%s",
366 j->unit->id, job_type_to_string(j->type));
369 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
371 /* logging for j not k here here to provide consistent narrative */
372 log_info_unit(j->unit->id,
373 "Walked on cycle path to %s/%s",
374 k->unit->id, job_type_to_string(k->type));
377 !unit_matters_to_anchor(k->unit, k)) {
378 /* Ok, we can drop this one, so let's
383 /* Check if this in fact was the beginning of
391 /* logging for j not k here here to provide consistent narrative */
392 log_warning_unit(j->unit->id,
393 "Breaking ordering cycle by deleting job %s/%s",
394 delete->unit->id, job_type_to_string(delete->type));
395 log_error_unit(delete->unit->id,
396 "Job %s/%s deleted to break ordering cycle starting with %s/%s",
397 delete->unit->id, job_type_to_string(delete->type),
398 j->unit->id, job_type_to_string(j->type));
399 unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF,
400 "Ordering cycle found, skipping %s");
401 transaction_delete_unit(tr, delete->unit);
405 log_error("Unable to break cycle");
407 dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
408 "Transaction order is cyclic. See system logs for details.");
412 /* Make the marker point to where we come from, so that we can
413 * find our way backwards if we want to break a cycle. We use
414 * a special marker for the beginning: we point to
416 j->marker = from ? from : j;
417 j->generation = generation;
419 /* We assume that the dependencies are bidirectional, and
420 * hence can ignore UNIT_AFTER */
421 SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
424 /* Is there a job for this unit? */
425 o = hashmap_get(tr->jobs, u);
427 /* Ok, there is no job for this in the
428 * transaction, but maybe there is already one
435 r = transaction_verify_order_one(tr, o, j, generation, e);
440 /* Ok, let's backtrack, and remember that this entry is not on
441 * our path anymore. */
447 static int transaction_verify_order(Transaction *tr, unsigned *generation, DBusError *e) {
456 /* Check if the ordering graph is cyclic. If it is, try to fix
457 * that up by dropping one of the jobs. */
461 HASHMAP_FOREACH(j, tr->jobs, i)
462 if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0)
468 static void transaction_collect_garbage(Transaction *tr) {
474 /* Drop jobs that are not required by any other job */
477 HASHMAP_FOREACH(j, tr->jobs, i) {
478 if (tr->anchor_job == j || j->object_list) {
479 /* log_debug("Keeping job %s/%s because of %s/%s", */
480 /* j->unit->id, job_type_to_string(j->type), */
481 /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
482 /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
486 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
487 transaction_delete_job(tr, j, true);
492 static int transaction_is_destructive(Transaction *tr, JobMode mode, DBusError *e) {
498 /* Checks whether applying this transaction means that
499 * existing jobs would be replaced */
501 HASHMAP_FOREACH(j, tr->jobs, i) {
504 assert(!j->transaction_prev);
505 assert(!j->transaction_next);
507 if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
508 !job_type_is_superset(j->type, j->unit->job->type)) {
510 dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
518 static void transaction_minimize_impact(Transaction *tr) {
524 /* Drops all unnecessary jobs that reverse already active jobs
525 * or that stop a running service. */
528 HASHMAP_FOREACH(j, tr->jobs, i) {
529 LIST_FOREACH(transaction, j, j) {
530 bool stops_running_service, changes_existing_job;
532 /* If it matters, we shouldn't drop it */
533 if (j->matters_to_anchor)
536 /* Would this stop a running service?
537 * Would this change an existing job?
538 * If so, let's drop this entry */
540 stops_running_service =
541 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
543 changes_existing_job =
545 job_type_is_conflicting(j->type, j->unit->job->type);
547 if (!stops_running_service && !changes_existing_job)
550 if (stops_running_service)
551 log_debug_unit(j->unit->id,
552 "%s/%s would stop a running service.",
553 j->unit->id, job_type_to_string(j->type));
555 if (changes_existing_job)
556 log_debug_unit(j->unit->id,
557 "%s/%s would change existing job.",
558 j->unit->id, job_type_to_string(j->type));
560 /* Ok, let's get rid of this */
561 log_debug_unit(j->unit->id,
562 "Deleting %s/%s to minimize impact.",
563 j->unit->id, job_type_to_string(j->type));
565 transaction_delete_job(tr, j, true);
571 static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
576 /* Moves the transaction jobs to the set of active jobs */
578 if (mode == JOB_ISOLATE) {
580 /* When isolating first kill all installed jobs which
581 * aren't part of the new transaction */
582 HASHMAP_FOREACH(j, m->jobs, i) {
583 assert(j->installed);
585 if (hashmap_get(tr->jobs, j->unit))
588 /* Not invalidating recursively. Avoids triggering
589 * OnFailure= actions of dependent jobs. Also avoids
590 * invalidating our iterator. */
591 job_finish_and_invalidate(j, JOB_CANCELED, false);
595 HASHMAP_FOREACH(j, tr->jobs, i) {
597 assert(!j->transaction_prev);
598 assert(!j->transaction_next);
600 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
605 while ((j = hashmap_steal_first(tr->jobs))) {
608 /* Clean the job dependencies */
609 transaction_unlink_job(tr, j, false);
611 installed_job = job_install(j);
612 if (installed_job != j) {
613 /* j has been merged into a previously installed job */
614 if (tr->anchor_job == j)
615 tr->anchor_job = installed_job;
616 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
621 job_add_to_run_queue(j);
622 job_add_to_dbus_queue(j);
624 job_shutdown_magic(j);
631 HASHMAP_FOREACH(j, tr->jobs, i)
632 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
637 int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
641 unsigned generation = 1;
645 /* This applies the changes recorded in tr->jobs to
646 * the actual list of jobs, if possible. */
648 /* Reset the generation counter of all installed jobs. The detection of cycles
649 * looks at installed jobs. If they had a non-zero generation from some previous
650 * walk of the graph, the algorithm would break. */
651 HASHMAP_FOREACH(j, m->jobs, i)
654 /* First step: figure out which jobs matter */
655 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
657 /* Second step: Try not to stop any running services if
658 * we don't have to. Don't try to reverse running
659 * jobs if we don't have to. */
660 if (mode == JOB_FAIL)
661 transaction_minimize_impact(tr);
663 /* Third step: Drop redundant jobs */
664 transaction_drop_redundant(tr);
667 /* Fourth step: Let's remove unneeded jobs that might
669 if (mode != JOB_ISOLATE)
670 transaction_collect_garbage(tr);
672 /* Fifth step: verify order makes sense and correct
673 * cycles if necessary and possible */
674 r = transaction_verify_order(tr, &generation, e);
679 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
683 /* Let's see if the resulting transaction ordering
684 * graph is still cyclic... */
688 /* Sixth step: let's drop unmergeable entries if
689 * necessary and possible, merge entries we can
691 r = transaction_merge_jobs(tr, e);
696 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
700 /* Seventh step: an entry got dropped, let's garbage
701 * collect its dependencies. */
702 if (mode != JOB_ISOLATE)
703 transaction_collect_garbage(tr);
705 /* Let's see if the resulting transaction still has
706 * unmergeable entries ... */
709 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
710 transaction_drop_redundant(tr);
712 /* Ninth step: check whether we can actually apply this */
713 r = transaction_is_destructive(tr, mode, e);
715 log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
719 /* Tenth step: apply changes */
720 r = transaction_apply(tr, m, mode);
722 log_warning("Failed to apply transaction: %s", strerror(-r));
726 assert(hashmap_isempty(tr->jobs));
728 if (!hashmap_isempty(m->jobs)) {
729 /* Are there any jobs now? Then make sure we have the
730 * idle pipe around. We don't really care too much
731 * whether this works or not, as the idle pipe is a
732 * feature for cosmetics, not actually useful for
733 * anything beyond that. */
735 if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0)
736 pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
742 static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
748 /* Looks for an existing prospective job and returns that. If
749 * it doesn't exist it is created and added to the prospective
752 f = hashmap_get(tr->jobs, unit);
754 LIST_FOREACH(transaction, j, f) {
755 assert(j->unit == unit);
757 if (j->type == type) {
764 j = job_new(unit, type);
770 j->matters_to_anchor = false;
771 j->override = override;
772 j->irreversible = tr->irreversible;
774 LIST_PREPEND(Job, transaction, f, j);
776 if (hashmap_replace(tr->jobs, unit, f) < 0) {
777 LIST_REMOVE(Job, transaction, f, j);
785 /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
790 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
794 if (j->transaction_prev)
795 j->transaction_prev->transaction_next = j->transaction_next;
796 else if (j->transaction_next)
797 hashmap_replace(tr->jobs, j->unit, j->transaction_next);
799 hashmap_remove_value(tr->jobs, j->unit, j);
801 if (j->transaction_next)
802 j->transaction_next->transaction_prev = j->transaction_prev;
804 j->transaction_prev = j->transaction_next = NULL;
806 while (j->subject_list)
807 job_dependency_free(j->subject_list);
809 while (j->object_list) {
810 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
812 job_dependency_free(j->object_list);
814 if (other && delete_dependencies) {
815 log_debug_unit(other->unit->id,
816 "Deleting job %s/%s as dependency of job %s/%s",
817 other->unit->id, job_type_to_string(other->type),
818 j->unit->id, job_type_to_string(j->type));
819 transaction_delete_job(tr, other, delete_dependencies);
824 int transaction_add_job_and_dependencies(
832 bool ignore_requirements,
842 assert(type < _JOB_TYPE_MAX);
843 assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
846 /* log_debug("Pulling in %s/%s from %s/%s", */
847 /* unit->id, job_type_to_string(type), */
848 /* by ? by->unit->id : "NA", */
849 /* by ? job_type_to_string(by->type) : "NA"); */
851 if (unit->load_state != UNIT_LOADED &&
852 unit->load_state != UNIT_ERROR &&
853 unit->load_state != UNIT_MASKED) {
854 dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
858 if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
859 dbus_set_error(e, BUS_ERROR_LOAD_FAILED,
860 "Unit %s failed to load: %s. "
861 "See system logs and 'systemctl status %s' for details.",
863 strerror(-unit->load_error),
868 if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
869 dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
870 return -EADDRNOTAVAIL;
873 if (!unit_job_is_applicable(unit, type)) {
874 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);
878 /* First add the job. */
879 ret = transaction_add_one_job(tr, type, unit, override, &is_new);
883 ret->ignore_order = ret->ignore_order || ignore_order;
885 /* Then, add a link to the job. */
887 if (!job_dependency_new(by, ret, matters, conflicts))
890 /* If the job has no parent job, it is the anchor job. */
891 assert(!tr->anchor_job);
892 tr->anchor_job = ret;
895 if (is_new && !ignore_requirements && type != JOB_NOP) {
898 /* If we are following some other unit, make sure we
899 * add all dependencies of everybody following. */
900 if (unit_following_set(ret->unit, &following) > 0) {
901 SET_FOREACH(dep, following, i) {
902 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
904 log_warning_unit(dep->id,
905 "Cannot add dependency job for unit %s, ignoring: %s",
906 dep->id, bus_error(e, r));
916 /* Finally, recursively add in all dependencies. */
917 if (type == JOB_START || type == JOB_RESTART) {
918 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
919 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
929 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
930 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
940 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) {
941 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
943 log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
944 "Cannot add dependency job for unit %s, ignoring: %s",
945 dep->id, bus_error(e, r));
952 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
953 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
955 log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
956 "Cannot add dependency job for unit %s, ignoring: %s",
957 dep->id, bus_error(e, r));
964 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
965 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
975 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) {
976 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
978 log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
979 "Cannot add dependency job for unit %s, ignoring: %s",
980 dep->id, bus_error(e, r));
987 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
988 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
998 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
999 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
1001 log_warning_unit(dep->id,
1002 "Cannot add dependency job for unit %s, ignoring: %s",
1003 dep->id, bus_error(e, r));
1012 if (type == JOB_STOP || type == JOB_RESTART) {
1014 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
1015 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1025 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) {
1026 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1036 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONSISTS_OF], i) {
1037 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1049 if (type == JOB_RELOAD) {
1051 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
1052 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
1054 log_warning_unit(dep->id,
1055 "Cannot add dependency reload job for unit %s, ignoring: %s",
1056 dep->id, bus_error(e, r));
1064 /* JOB_VERIFY_STARTED require no dependency handling */
1073 int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
1082 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1084 /* ignore aliases */
1088 if (u->ignore_on_isolate)
1091 /* No need to stop inactive jobs */
1092 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
1095 /* Is there already something listed for this? */
1096 if (hashmap_get(tr->jobs, u))
1099 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
1101 log_warning_unit(u->id,
1102 "Cannot add isolate job for unit %s, ignoring: %s",
1103 u->id, strerror(-r));
1109 Transaction *transaction_new(bool irreversible) {
1112 tr = new0(Transaction, 1);
1116 tr->jobs = hashmap_new(trivial_hash_func, trivial_compare_func);
1122 tr->irreversible = irreversible;
1127 void transaction_free(Transaction *tr) {
1128 assert(hashmap_isempty(tr->jobs));
1129 hashmap_free(tr->jobs);