]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/transaction.c
transaction: fix detection of cycles involving installed jobs
[thirdparty/systemd.git] / src / core / transaction.c
CommitLineData
7c0436b9
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
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.
12
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.
17
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/>.
20***/
21
75778e21
MS
22#include "transaction.h"
23#include "bus-errors.h"
24
25static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
26
27static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) {
28 assert(tr);
29 assert(j);
30
31 /* Deletes one job from the transaction */
32
33 transaction_unlink_job(tr, j, delete_dependencies);
34
d6a093d0 35 job_free(j);
75778e21
MS
36}
37
38static void transaction_delete_unit(Transaction *tr, Unit *u) {
39 Job *j;
40
41 /* Deletes all jobs associated with a certain unit from the
42 * transaction */
43
44 while ((j = hashmap_get(tr->jobs, u)))
45 transaction_delete_job(tr, j, true);
46}
47
48void transaction_abort(Transaction *tr) {
49 Job *j;
50
51 assert(tr);
52
53 while ((j = hashmap_first(tr->jobs)))
54 transaction_delete_job(tr, j, true);
55
56 assert(hashmap_isempty(tr->jobs));
75778e21
MS
57}
58
0d9989aa 59static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
75778e21
MS
60 JobDependency *l;
61
75778e21
MS
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. */
66
0d9989aa
MS
67 j->matters_to_anchor = true;
68 j->generation = generation;
75778e21 69
0d9989aa 70 LIST_FOREACH(subject, l, j->subject_list) {
75778e21
MS
71
72 /* This link does not matter */
73 if (!l->matters)
74 continue;
75
76 /* This unit has already been marked */
77 if (l->object->generation == generation)
78 continue;
79
0d9989aa 80 transaction_find_jobs_that_matter_to_anchor(l->object, generation);
75778e21
MS
81 }
82}
83
84static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) {
85 JobDependency *l, *last;
86
87 assert(j);
88 assert(other);
89 assert(j->unit == other->unit);
90 assert(!j->installed);
91
92 /* Merges 'other' into 'j' and then deletes 'other'. */
93
94 j->type = t;
95 j->state = JOB_WAITING;
96 j->override = j->override || other->override;
97
98 j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
99
100 /* Patch us in as new owner of the JobDependency objects */
101 last = NULL;
102 LIST_FOREACH(subject, l, other->subject_list) {
103 assert(l->subject == other);
104 l->subject = j;
105 last = l;
106 }
107
108 /* Merge both lists */
109 if (last) {
110 last->subject_next = j->subject_list;
111 if (j->subject_list)
112 j->subject_list->subject_prev = last;
113 j->subject_list = other->subject_list;
114 }
115
116 /* Patch us in as new owner of the JobDependency objects */
117 last = NULL;
118 LIST_FOREACH(object, l, other->object_list) {
119 assert(l->object == other);
120 l->object = j;
121 last = l;
122 }
123
124 /* Merge both lists */
125 if (last) {
126 last->object_next = j->object_list;
127 if (j->object_list)
128 j->object_list->object_prev = last;
129 j->object_list = other->object_list;
130 }
131
132 /* Kill the other job */
133 other->subject_list = NULL;
134 other->object_list = NULL;
135 transaction_delete_job(tr, other, true);
136}
137
138static bool job_is_conflicted_by(Job *j) {
139 JobDependency *l;
140
141 assert(j);
142
143 /* Returns true if this job is pulled in by a least one
144 * ConflictedBy dependency. */
145
146 LIST_FOREACH(object, l, j->object_list)
147 if (l->conflicts)
148 return true;
149
150 return false;
151}
152
153static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
154 Job *k;
155
156 assert(j);
157
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. */
162
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) {
167 Job *d;
168
169 /* Is this one mergeable? Then skip it */
170 if (job_type_is_mergeable(j->type, k->type))
171 continue;
172
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) {
176
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. */
185
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)));
188
189 if (j->type == JOB_STOP) {
190
191 if (job_is_conflicted_by(j))
192 d = k;
193 else
194 d = j;
195
196 } else if (k->type == JOB_STOP) {
197
198 if (job_is_conflicted_by(k))
199 d = j;
200 else
201 d = k;
202 } else
203 d = j;
204
205 } else if (!j->matters_to_anchor)
206 d = j;
207 else if (!k->matters_to_anchor)
208 d = k;
209 else
210 return -ENOEXEC;
211
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);
215 return 0;
216 }
217
218 return -EINVAL;
219}
220
221static int transaction_merge_jobs(Transaction *tr, DBusError *e) {
222 Job *j;
223 Iterator i;
224 int r;
225
226 assert(tr);
227
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) {
231 JobType t;
232 Job *k;
233
234 t = j->type;
235 LIST_FOREACH(transaction, k, j->transaction_next) {
236 if (job_type_merge(&t, k->type) >= 0)
237 continue;
238
239 /* OK, we could not merge all jobs for this
240 * action. Let's see if we can get rid of one
241 * of them */
242
243 r = delete_one_unmergeable_job(tr, j);
244 if (r >= 0)
245 /* Ok, we managed to drop one, now
246 * let's ask our callers to call us
247 * again after garbage collecting */
248 return -EAGAIN;
249
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);
253 return r;
254 }
255 }
256
257 /* Second step, merge the jobs. */
258 HASHMAP_FOREACH(j, tr->jobs, i) {
259 JobType t = j->type;
260 Job *k;
261
262 /* Merge all transactions */
263 LIST_FOREACH(transaction, k, j->transaction_next)
264 assert_se(job_type_merge(&t, k->type) == 0);
265
75778e21 266 while ((k = j->transaction_next)) {
656bbffc 267 if (tr->anchor_job == k) {
75778e21
MS
268 transaction_merge_and_delete_job(tr, k, j, t);
269 j = k;
270 } else
271 transaction_merge_and_delete_job(tr, j, k, t);
272 }
273
75778e21
MS
274 assert(!j->transaction_next);
275 assert(!j->transaction_prev);
276 }
277
278 return 0;
279}
280
281static void transaction_drop_redundant(Transaction *tr) {
055163ad
MS
282 Job *j;
283 Iterator i;
75778e21 284
055163ad
MS
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. */
75778e21 288
055163ad 289 assert(tr);
75778e21 290
055163ad
MS
291rescan:
292 HASHMAP_FOREACH(j, tr->jobs, i) {
293 Job *k;
75778e21 294
055163ad 295 LIST_FOREACH(transaction, k, j) {
75778e21 296
055163ad
MS
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)))
300 goto next_unit;
75778e21
MS
301 }
302
055163ad
MS
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);
305 goto rescan;
306 next_unit:;
307 }
75778e21
MS
308}
309
310static bool unit_matters_to_anchor(Unit *u, Job *j) {
311 assert(u);
312 assert(!j->transaction_prev);
313
314 /* Checks whether at least one of the jobs for this unit
315 * matters to the anchor. */
316
317 LIST_FOREACH(transaction, j, j)
318 if (j->matters_to_anchor)
319 return true;
320
321 return false;
322}
323
324static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, DBusError *e) {
325 Iterator i;
326 Unit *u;
327 int r;
328
329 assert(tr);
330 assert(j);
331 assert(!j->transaction_prev);
332
333 /* Does a recursive sweep through the ordering graph, looking
334 * for a cycle. If we find cycle we try to break it. */
335
336 /* Have we seen this before? */
337 if (j->generation == generation) {
338 Job *k, *delete;
339
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. */
343 if (!j->marker)
344 return 0;
345
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
351 * in there. */
352 log_warning("Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type));
353
354 delete = NULL;
355 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
356
357 log_info("Walked on cycle path to %s/%s", k->unit->id, job_type_to_string(k->type));
358
359 if (!delete &&
75778e21
MS
360 !unit_matters_to_anchor(k->unit, k)) {
361 /* Ok, we can drop this one, so let's
362 * do so. */
363 delete = k;
364 }
365
366 /* Check if this in fact was the beginning of
367 * the cycle */
368 if (k == j)
369 break;
370 }
371
372
373 if (delete) {
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);
376 return -EAGAIN;
377 }
378
379 log_error("Unable to break cycle");
380
381 dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details.");
382 return -ENOEXEC;
383 }
384
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
388 * ourselves. */
389 j->marker = from ? from : j;
390 j->generation = generation;
391
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) {
395 Job *o;
396
397 /* Is there a job for this unit? */
398 o = hashmap_get(tr->jobs, u);
399 if (!o) {
400 /* Ok, there is no job for this in the
401 * transaction, but maybe there is already one
402 * running? */
403 o = u->job;
404 if (!o)
405 continue;
406 }
407
408 r = transaction_verify_order_one(tr, o, j, generation, e);
409 if (r < 0)
410 return r;
411 }
412
413 /* Ok, let's backtrack, and remember that this entry is not on
414 * our path anymore. */
415 j->marker = NULL;
416
417 return 0;
418}
419
420static int transaction_verify_order(Transaction *tr, unsigned *generation, DBusError *e) {
421 Job *j;
422 int r;
423 Iterator i;
424 unsigned g;
425
426 assert(tr);
427 assert(generation);
428
429 /* Check if the ordering graph is cyclic. If it is, try to fix
430 * that up by dropping one of the jobs. */
431
432 g = (*generation)++;
433
434 HASHMAP_FOREACH(j, tr->jobs, i)
435 if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0)
436 return r;
437
438 return 0;
439}
440
441static void transaction_collect_garbage(Transaction *tr) {
055163ad
MS
442 Iterator i;
443 Job *j;
75778e21
MS
444
445 assert(tr);
446
447 /* Drop jobs that are not required by any other job */
448
055163ad
MS
449rescan:
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"); */
456 continue;
75778e21
MS
457 }
458
055163ad
MS
459 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
460 transaction_delete_job(tr, j, true);
461 goto rescan;
462 }
75778e21
MS
463}
464
465static int transaction_is_destructive(Transaction *tr, DBusError *e) {
466 Iterator i;
467 Job *j;
468
469 assert(tr);
470
471 /* Checks whether applying this transaction means that
472 * existing jobs would be replaced */
473
474 HASHMAP_FOREACH(j, tr->jobs, i) {
475
476 /* Assume merged */
477 assert(!j->transaction_prev);
478 assert(!j->transaction_next);
479
480 if (j->unit->job &&
75778e21
MS
481 !job_type_is_superset(j->type, j->unit->job->type)) {
482
483 dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
484 return -EEXIST;
485 }
486 }
487
488 return 0;
489}
490
491static void transaction_minimize_impact(Transaction *tr) {
055163ad
MS
492 Job *j;
493 Iterator i;
494
75778e21
MS
495 assert(tr);
496
497 /* Drops all unnecessary jobs that reverse already active jobs
498 * or that stop a running service. */
499
055163ad
MS
500rescan:
501 HASHMAP_FOREACH(j, tr->jobs, i) {
502 LIST_FOREACH(transaction, j, j) {
503 bool stops_running_service, changes_existing_job;
75778e21 504
055163ad
MS
505 /* If it matters, we shouldn't drop it */
506 if (j->matters_to_anchor)
507 continue;
75778e21 508
055163ad
MS
509 /* Would this stop a running service?
510 * Would this change an existing job?
511 * If so, let's drop this entry */
75778e21 512
055163ad
MS
513 stops_running_service =
514 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
75778e21 515
055163ad
MS
516 changes_existing_job =
517 j->unit->job &&
518 job_type_is_conflicting(j->type, j->unit->job->type);
75778e21 519
055163ad
MS
520 if (!stops_running_service && !changes_existing_job)
521 continue;
75778e21 522
055163ad
MS
523 if (stops_running_service)
524 log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
75778e21 525
055163ad
MS
526 if (changes_existing_job)
527 log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
75778e21 528
055163ad
MS
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));
75778e21 531
055163ad
MS
532 transaction_delete_job(tr, j, true);
533 goto rescan;
75778e21 534 }
055163ad 535 }
75778e21
MS
536}
537
538static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
539 Iterator i;
540 Job *j;
541 int r;
542
543 /* Moves the transaction jobs to the set of active jobs */
544
545 if (mode == JOB_ISOLATE) {
546
547 /* When isolating first kill all installed jobs which
548 * aren't part of the new transaction */
549 rescan:
550 HASHMAP_FOREACH(j, m->jobs, i) {
551 assert(j->installed);
552
553 if (hashmap_get(tr->jobs, j->unit))
554 continue;
555
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)
560 goto rescan;
561 }
562 }
563
564 HASHMAP_FOREACH(j, tr->jobs, i) {
565 /* Assume merged */
566 assert(!j->transaction_prev);
567 assert(!j->transaction_next);
568
75778e21
MS
569 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
570 if (r < 0)
571 goto rollback;
572 }
573
574 while ((j = hashmap_steal_first(tr->jobs))) {
656bbffc
MS
575 Job *installed_job;
576
75778e21
MS
577 /* Clean the job dependencies */
578 transaction_unlink_job(tr, j, false);
579
656bbffc
MS
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));
586 job_free(j);
587 j = installed_job;
588 }
05d576f1 589
75778e21
MS
590 job_add_to_run_queue(j);
591 job_add_to_dbus_queue(j);
592 job_start_timer(j);
75778e21
MS
593 }
594
75778e21
MS
595 return 0;
596
597rollback:
598
d6a093d0 599 HASHMAP_FOREACH(j, tr->jobs, i)
75778e21 600 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
75778e21
MS
601
602 return r;
603}
604
605int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
4e7bd268
MS
606 Iterator i;
607 Job *j;
75778e21
MS
608 int r;
609 unsigned generation = 1;
610
611 assert(tr);
612
613 /* This applies the changes recorded in tr->jobs to
614 * the actual list of jobs, if possible. */
615
4e7bd268
MS
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)
620 j->generation = 0;
621
75778e21 622 /* First step: figure out which jobs matter */
0d9989aa 623 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
75778e21
MS
624
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);
630
631 /* Third step: Drop redundant jobs */
632 transaction_drop_redundant(tr);
633
634 for (;;) {
635 /* Fourth step: Let's remove unneeded jobs that might
636 * be lurking. */
637 if (mode != JOB_ISOLATE)
638 transaction_collect_garbage(tr);
639
640 /* Fifth step: verify order makes sense and correct
641 * cycles if necessary and possible */
642 r = transaction_verify_order(tr, &generation, e);
643 if (r >= 0)
644 break;
645
646 if (r != -EAGAIN) {
647 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
648 return r;
649 }
650
651 /* Let's see if the resulting transaction ordering
652 * graph is still cyclic... */
653 }
654
655 for (;;) {
656 /* Sixth step: let's drop unmergeable entries if
657 * necessary and possible, merge entries we can
658 * merge */
659 r = transaction_merge_jobs(tr, e);
660 if (r >= 0)
661 break;
662
663 if (r != -EAGAIN) {
664 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
665 return r;
666 }
667
668 /* Seventh step: an entry got dropped, let's garbage
669 * collect its dependencies. */
670 if (mode != JOB_ISOLATE)
671 transaction_collect_garbage(tr);
672
673 /* Let's see if the resulting transaction still has
674 * unmergeable entries ... */
675 }
676
677 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
678 transaction_drop_redundant(tr);
679
680 /* Ninth step: check whether we can actually apply this */
681 if (mode == JOB_FAIL) {
682 r = transaction_is_destructive(tr, e);
683 if (r < 0) {
684 log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
685 return r;
686 }
687 }
688
689 /* Tenth step: apply changes */
690 r = transaction_apply(tr, m, mode);
691 if (r < 0) {
692 log_warning("Failed to apply transaction: %s", strerror(-r));
693 return r;
694 }
695
696 assert(hashmap_isempty(tr->jobs));
75778e21
MS
697
698 return 0;
699}
700
701static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
702 Job *j, *f;
703
704 assert(tr);
705 assert(unit);
706
707 /* Looks for an existing prospective job and returns that. If
708 * it doesn't exist it is created and added to the prospective
709 * jobs list. */
710
711 f = hashmap_get(tr->jobs, unit);
712
713 LIST_FOREACH(transaction, j, f) {
714 assert(j->unit == unit);
715
716 if (j->type == type) {
717 if (is_new)
718 *is_new = false;
719 return j;
720 }
721 }
722
3c956cfe
MS
723 j = job_new(unit, type);
724 if (!j)
725 return NULL;
75778e21
MS
726
727 j->generation = 0;
728 j->marker = NULL;
729 j->matters_to_anchor = false;
730 j->override = override;
731
732 LIST_PREPEND(Job, transaction, f, j);
733
734 if (hashmap_replace(tr->jobs, unit, f) < 0) {
735 LIST_REMOVE(Job, transaction, f, j);
736 job_free(j);
737 return NULL;
738 }
739
740 if (is_new)
741 *is_new = true;
742
743 /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
744
745 return j;
746}
747
748static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
749 assert(tr);
750 assert(j);
751
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);
756 else
757 hashmap_remove_value(tr->jobs, j->unit, j);
758
759 if (j->transaction_next)
760 j->transaction_next->transaction_prev = j->transaction_prev;
761
762 j->transaction_prev = j->transaction_next = NULL;
763
764 while (j->subject_list)
e6eda1f2 765 job_dependency_free(j->subject_list);
75778e21
MS
766
767 while (j->object_list) {
768 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
769
e6eda1f2 770 job_dependency_free(j->object_list);
75778e21
MS
771
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);
777 }
778 }
779}
780
781int transaction_add_job_and_dependencies(
782 Transaction *tr,
783 JobType type,
784 Unit *unit,
785 Job *by,
786 bool matters,
787 bool override,
788 bool conflicts,
789 bool ignore_requirements,
790 bool ignore_order,
b94fbd30 791 DBusError *e) {
75778e21
MS
792 Job *ret;
793 Iterator i;
794 Unit *dep;
795 int r;
796 bool is_new;
797
798 assert(tr);
799 assert(type < _JOB_TYPE_MAX);
800 assert(unit);
801
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"); */
806
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);
811 return -EINVAL;
812 }
813
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.",
818 unit->id,
819 strerror(-unit->load_error),
820 unit->id);
821 return -EINVAL;
822 }
823
824 if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
825 dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
59e132a7 826 return -EADDRNOTAVAIL;
75778e21
MS
827 }
828
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);
831 return -EBADR;
832 }
833
834 /* First add the job. */
835 ret = transaction_add_one_job(tr, type, unit, override, &is_new);
836 if (!ret)
837 return -ENOMEM;
838
839 ret->ignore_order = ret->ignore_order || ignore_order;
840
841 /* Then, add a link to the job. */
e6eda1f2
MS
842 if (by) {
843 if (!job_dependency_new(by, ret, matters, conflicts))
844 return -ENOMEM;
845 } else {
846 /* If the job has no parent job, it is the anchor job. */
4483f694
MS
847 assert(!tr->anchor_job);
848 tr->anchor_job = ret;
b94fbd30 849 }
75778e21
MS
850 if (is_new && !ignore_requirements) {
851 Set *following;
852
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) {
b94fbd30 857 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
75778e21
MS
858 if (r < 0) {
859 log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
860
861 if (e)
862 dbus_error_free(e);
863 }
864 }
865
866 set_free(following);
867 }
868
869 /* Finally, recursively add in all dependencies. */
65304075 870 if (type == JOB_START || type == JOB_RELOAD_OR_START || type == JOB_RESTART) {
75778e21 871 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
b94fbd30 872 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
873 if (r < 0) {
874 if (r != -EBADR)
875 goto fail;
876
877 if (e)
878 dbus_error_free(e);
879 }
880 }
881
882 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BIND_TO], i) {
b94fbd30 883 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
884 if (r < 0) {
885 if (r != -EBADR)
886 goto fail;
887
888 if (e)
889 dbus_error_free(e);
890 }
891 }
892
893 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) {
b94fbd30 894 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
75778e21 895 if (r < 0) {
59e132a7
LP
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));
75778e21
MS
898
899 if (e)
900 dbus_error_free(e);
901 }
902 }
903
904 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
b94fbd30 905 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
75778e21 906 if (r < 0) {
59e132a7
LP
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));
75778e21
MS
909
910 if (e)
911 dbus_error_free(e);
912 }
913 }
914
915 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
b94fbd30 916 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
917 if (r < 0) {
918 if (r != -EBADR)
919 goto fail;
920
921 if (e)
922 dbus_error_free(e);
923 }
924 }
925
926 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) {
b94fbd30 927 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
75778e21 928 if (r < 0) {
59e132a7
LP
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));
75778e21
MS
931
932 if (e)
933 dbus_error_free(e);
934 }
935 }
936
937 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
b94fbd30 938 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
75778e21
MS
939 if (r < 0) {
940 if (r != -EBADR)
941 goto fail;
942
943 if (e)
944 dbus_error_free(e);
945 }
946 }
947
948 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
b94fbd30 949 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
75778e21
MS
950 if (r < 0) {
951 log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
952
953 if (e)
954 dbus_error_free(e);
955 }
956 }
957
958 }
959
960 if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
961
962 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
b94fbd30 963 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
964 if (r < 0) {
965 if (r != -EBADR)
966 goto fail;
967
968 if (e)
969 dbus_error_free(e);
970 }
971 }
972
973 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) {
b94fbd30 974 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
975 if (r < 0) {
976 if (r != -EBADR)
977 goto fail;
978
979 if (e)
980 dbus_error_free(e);
981 }
982 }
983 }
984
985 if (type == JOB_RELOAD || type == JOB_RELOAD_OR_START) {
986
987 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATE_RELOAD_TO], i) {
b94fbd30 988 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
75778e21
MS
989 if (r < 0) {
990 log_warning("Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
991
992 if (e)
993 dbus_error_free(e);
994 }
995 }
996 }
997
998 /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */
999 }
1000
75778e21
MS
1001 return 0;
1002
1003fail:
1004 return r;
1005}
1006
1007int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
1008 Iterator i;
1009 Unit *u;
1010 char *k;
1011 int r;
1012
1013 assert(tr);
1014 assert(m);
1015
1016 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1017
1018 /* ignore aliases */
1019 if (u->id != k)
1020 continue;
1021
1022 if (u->ignore_on_isolate)
1023 continue;
1024
1025 /* No need to stop inactive jobs */
1026 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
1027 continue;
1028
1029 /* Is there already something listed for this? */
1030 if (hashmap_get(tr->jobs, u))
1031 continue;
1032
4483f694 1033 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
75778e21
MS
1034 if (r < 0)
1035 log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r));
1036 }
1037
1038 return 0;
1039}
1040
1041Transaction *transaction_new(void) {
1042 Transaction *tr;
1043
1044 tr = new0(Transaction, 1);
1045 if (!tr)
1046 return NULL;
1047
1048 tr->jobs = hashmap_new(trivial_hash_func, trivial_compare_func);
1049 if (!tr->jobs) {
1050 free(tr);
1051 return NULL;
1052 }
1053
1054 return tr;
1055}
1056
1057void transaction_free(Transaction *tr) {
1058 assert(hashmap_isempty(tr->jobs));
1059 hashmap_free(tr->jobs);
1060 free(tr);
1061}