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_unit(j->unit->id,
190 "Looking at job %s/%s conflicted_by=%s",
191 j->unit->id, job_type_to_string(j->type),
192 yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
193 log_debug_unit(k->unit->id,
194 "Looking at job %s/%s conflicted_by=%s",
195 k->unit->id, job_type_to_string(k->type),
196 yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
198 if (j->type == JOB_STOP) {
200 if (job_is_conflicted_by(j))
205 } else if (k->type == JOB_STOP) {
207 if (job_is_conflicted_by(k))
214 } else if (!j->matters_to_anchor)
216 else if (!k->matters_to_anchor)
221 /* Ok, we can drop one, so let's do so. */
222 log_debug_unit(d->unit->id,
223 "Fixing conflicting jobs by deleting job %s/%s",
224 d->unit->id, job_type_to_string(d->type));
225 transaction_delete_job(tr, d, true);
232 static int transaction_merge_jobs(Transaction *tr, DBusError *e) {
239 /* First step, check whether any of the jobs for one specific
240 * task conflict. If so, try to drop one of them. */
241 HASHMAP_FOREACH(j, tr->jobs, i) {
246 LIST_FOREACH(transaction, k, j->transaction_next) {
247 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
250 /* OK, we could not merge all jobs for this
251 * action. Let's see if we can get rid of one
254 r = delete_one_unmergeable_job(tr, j);
256 /* Ok, we managed to drop one, now
257 * let's ask our callers to call us
258 * again after garbage collecting */
261 /* We couldn't merge anything. Failure */
262 dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
263 job_type_to_string(t), job_type_to_string(k->type), k->unit->id);
268 /* Second step, merge the jobs. */
269 HASHMAP_FOREACH(j, tr->jobs, i) {
273 /* Merge all transaction jobs for j->unit */
274 LIST_FOREACH(transaction, k, j->transaction_next)
275 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
277 while ((k = j->transaction_next)) {
278 if (tr->anchor_job == k) {
279 transaction_merge_and_delete_job(tr, k, j, t);
282 transaction_merge_and_delete_job(tr, j, k, t);
285 assert(!j->transaction_next);
286 assert(!j->transaction_prev);
292 static void transaction_drop_redundant(Transaction *tr) {
296 /* Goes through the transaction and removes all jobs of the units
297 * whose jobs are all noops. If not all of a unit's jobs are
298 * redundant, they are kept. */
303 HASHMAP_FOREACH(j, tr->jobs, i) {
306 LIST_FOREACH(transaction, k, j) {
308 if (tr->anchor_job == k ||
309 !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
310 (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type)))
314 /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
315 transaction_delete_job(tr, j, false);
321 static bool unit_matters_to_anchor(Unit *u, Job *j) {
323 assert(!j->transaction_prev);
325 /* Checks whether at least one of the jobs for this unit
326 * matters to the anchor. */
328 LIST_FOREACH(transaction, j, j)
329 if (j->matters_to_anchor)
335 static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, DBusError *e) {
342 assert(!j->transaction_prev);
344 /* Does a recursive sweep through the ordering graph, looking
345 * for a cycle. If we find cycle we try to break it. */
347 /* Have we seen this before? */
348 if (j->generation == generation) {
351 /* If the marker is NULL we have been here already and
352 * decided the job was loop-free from here. Hence
353 * shortcut things and return right-away. */
357 /* So, the marker is not NULL and we already have been
358 * here. We have a cycle. Let's try to break it. We go
359 * backwards in our path and try to find a suitable
360 * job to remove. We use the marker to find our way
361 * back, since smart how we are we stored our way back
363 log_warning_unit(j->unit->id,
364 "Found ordering cycle on %s/%s",
365 j->unit->id, job_type_to_string(j->type));
368 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
370 /* logging for j not k here here to provide consistent narrative */
371 log_info_unit(j->unit->id,
372 "Walked on cycle path to %s/%s",
373 k->unit->id, job_type_to_string(k->type));
376 !unit_matters_to_anchor(k->unit, k)) {
377 /* Ok, we can drop this one, so let's
382 /* Check if this in fact was the beginning of
390 /* logging for j not k here here to provide consistent narrative */
391 log_warning_unit(j->unit->id,
392 "Breaking ordering cycle by deleting job %s/%s",
393 delete->unit->id, job_type_to_string(delete->type));
394 log_error_unit(delete->unit->id,
395 "Job %s/%s deleted to break ordering cycle starting with %s/%s",
396 delete->unit->id, job_type_to_string(delete->type),
397 j->unit->id, job_type_to_string(j->type));
398 status_printf(ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, true,
399 "Ordering cycle found, skipping %s", unit_description(delete->unit));
400 transaction_delete_unit(tr, delete->unit);
404 log_error("Unable to break cycle");
406 dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
407 "Transaction order is cyclic. See system logs for details.");
411 /* Make the marker point to where we come from, so that we can
412 * find our way backwards if we want to break a cycle. We use
413 * a special marker for the beginning: we point to
415 j->marker = from ? from : j;
416 j->generation = generation;
418 /* We assume that the dependencies are bidirectional, and
419 * hence can ignore UNIT_AFTER */
420 SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
423 /* Is there a job for this unit? */
424 o = hashmap_get(tr->jobs, u);
426 /* Ok, there is no job for this in the
427 * transaction, but maybe there is already one
434 r = transaction_verify_order_one(tr, o, j, generation, e);
439 /* Ok, let's backtrack, and remember that this entry is not on
440 * our path anymore. */
446 static int transaction_verify_order(Transaction *tr, unsigned *generation, DBusError *e) {
455 /* Check if the ordering graph is cyclic. If it is, try to fix
456 * that up by dropping one of the jobs. */
460 HASHMAP_FOREACH(j, tr->jobs, i)
461 if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0)
467 static void transaction_collect_garbage(Transaction *tr) {
473 /* Drop jobs that are not required by any other job */
476 HASHMAP_FOREACH(j, tr->jobs, i) {
477 if (tr->anchor_job == j || j->object_list) {
478 /* log_debug("Keeping job %s/%s because of %s/%s", */
479 /* j->unit->id, job_type_to_string(j->type), */
480 /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
481 /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
485 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
486 transaction_delete_job(tr, j, true);
491 static int transaction_is_destructive(Transaction *tr, DBusError *e) {
497 /* Checks whether applying this transaction means that
498 * existing jobs would be replaced */
500 HASHMAP_FOREACH(j, tr->jobs, i) {
503 assert(!j->transaction_prev);
504 assert(!j->transaction_next);
507 !job_type_is_superset(j->type, j->unit->job->type)) {
509 dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
517 static void transaction_minimize_impact(Transaction *tr) {
523 /* Drops all unnecessary jobs that reverse already active jobs
524 * or that stop a running service. */
527 HASHMAP_FOREACH(j, tr->jobs, i) {
528 LIST_FOREACH(transaction, j, j) {
529 bool stops_running_service, changes_existing_job;
531 /* If it matters, we shouldn't drop it */
532 if (j->matters_to_anchor)
535 /* Would this stop a running service?
536 * Would this change an existing job?
537 * If so, let's drop this entry */
539 stops_running_service =
540 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
542 changes_existing_job =
544 job_type_is_conflicting(j->type, j->unit->job->type);
546 if (!stops_running_service && !changes_existing_job)
549 if (stops_running_service)
550 log_debug_unit(j->unit->id,
551 "%s/%s would stop a running service.",
552 j->unit->id, job_type_to_string(j->type));
554 if (changes_existing_job)
555 log_debug_unit(j->unit->id,
556 "%s/%s would change existing job.",
557 j->unit->id, job_type_to_string(j->type));
559 /* Ok, let's get rid of this */
560 log_debug_unit(j->unit->id,
561 "Deleting %s/%s to minimize impact.",
562 j->unit->id, job_type_to_string(j->type));
564 transaction_delete_job(tr, j, true);
570 static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
575 /* Moves the transaction jobs to the set of active jobs */
577 if (mode == JOB_ISOLATE) {
579 /* When isolating first kill all installed jobs which
580 * aren't part of the new transaction */
581 HASHMAP_FOREACH(j, m->jobs, i) {
582 assert(j->installed);
584 if (hashmap_get(tr->jobs, j->unit))
587 /* Not invalidating recursively. Avoids triggering
588 * OnFailure= actions of dependent jobs. Also avoids
589 * invalidating our iterator. */
590 job_finish_and_invalidate(j, JOB_CANCELED, false);
594 HASHMAP_FOREACH(j, tr->jobs, i) {
596 assert(!j->transaction_prev);
597 assert(!j->transaction_next);
599 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
604 while ((j = hashmap_steal_first(tr->jobs))) {
607 /* Clean the job dependencies */
608 transaction_unlink_job(tr, j, false);
610 installed_job = job_install(j);
611 if (installed_job != j) {
612 /* j has been merged into a previously installed job */
613 if (tr->anchor_job == j)
614 tr->anchor_job = installed_job;
615 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
620 job_add_to_run_queue(j);
621 job_add_to_dbus_queue(j);
623 job_shutdown_magic(j);
630 HASHMAP_FOREACH(j, tr->jobs, i)
631 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
636 int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
640 unsigned generation = 1;
644 /* This applies the changes recorded in tr->jobs to
645 * the actual list of jobs, if possible. */
647 /* Reset the generation counter of all installed jobs. The detection of cycles
648 * looks at installed jobs. If they had a non-zero generation from some previous
649 * walk of the graph, the algorithm would break. */
650 HASHMAP_FOREACH(j, m->jobs, i)
653 /* First step: figure out which jobs matter */
654 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
656 /* Second step: Try not to stop any running services if
657 * we don't have to. Don't try to reverse running
658 * jobs if we don't have to. */
659 if (mode == JOB_FAIL)
660 transaction_minimize_impact(tr);
662 /* Third step: Drop redundant jobs */
663 transaction_drop_redundant(tr);
666 /* Fourth step: Let's remove unneeded jobs that might
668 if (mode != JOB_ISOLATE)
669 transaction_collect_garbage(tr);
671 /* Fifth step: verify order makes sense and correct
672 * cycles if necessary and possible */
673 r = transaction_verify_order(tr, &generation, e);
678 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
682 /* Let's see if the resulting transaction ordering
683 * graph is still cyclic... */
687 /* Sixth step: let's drop unmergeable entries if
688 * necessary and possible, merge entries we can
690 r = transaction_merge_jobs(tr, e);
695 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
699 /* Seventh step: an entry got dropped, let's garbage
700 * collect its dependencies. */
701 if (mode != JOB_ISOLATE)
702 transaction_collect_garbage(tr);
704 /* Let's see if the resulting transaction still has
705 * unmergeable entries ... */
708 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
709 transaction_drop_redundant(tr);
711 /* Ninth step: check whether we can actually apply this */
712 if (mode == JOB_FAIL) {
713 r = transaction_is_destructive(tr, e);
715 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 pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
743 static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
749 /* Looks for an existing prospective job and returns that. If
750 * it doesn't exist it is created and added to the prospective
753 f = hashmap_get(tr->jobs, unit);
755 LIST_FOREACH(transaction, j, f) {
756 assert(j->unit == unit);
758 if (j->type == type) {
765 j = job_new(unit, type);
771 j->matters_to_anchor = false;
772 j->override = override;
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(void) {
1112 tr = new0(Transaction, 1);
1116 tr->jobs = hashmap_new(trivial_hash_func, trivial_compare_func);
1125 void transaction_free(Transaction *tr) {
1126 assert(hashmap_isempty(tr->jobs));
1127 hashmap_free(tr->jobs);