]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/transaction.c
core: add transient units
[thirdparty/systemd.git] / src / core / transaction.c
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
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include "transaction.h"
26 #include "bus-errors.h"
27 #include "dbus-common.h"
28
29 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
30
31 static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) {
32 assert(tr);
33 assert(j);
34
35 /* Deletes one job from the transaction */
36
37 transaction_unlink_job(tr, j, delete_dependencies);
38
39 job_free(j);
40 }
41
42 static void transaction_delete_unit(Transaction *tr, Unit *u) {
43 Job *j;
44
45 /* Deletes all jobs associated with a certain unit from the
46 * transaction */
47
48 while ((j = hashmap_get(tr->jobs, u)))
49 transaction_delete_job(tr, j, true);
50 }
51
52 void transaction_abort(Transaction *tr) {
53 Job *j;
54
55 assert(tr);
56
57 while ((j = hashmap_first(tr->jobs)))
58 transaction_delete_job(tr, j, false);
59
60 assert(hashmap_isempty(tr->jobs));
61 }
62
63 static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
64 JobDependency *l;
65
66 /* A recursive sweep through the graph that marks all units
67 * that matter to the anchor job, i.e. are directly or
68 * indirectly a dependency of the anchor job via paths that
69 * are fully marked as mattering. */
70
71 j->matters_to_anchor = true;
72 j->generation = generation;
73
74 LIST_FOREACH(subject, l, j->subject_list) {
75
76 /* This link does not matter */
77 if (!l->matters)
78 continue;
79
80 /* This unit has already been marked */
81 if (l->object->generation == generation)
82 continue;
83
84 transaction_find_jobs_that_matter_to_anchor(l->object, generation);
85 }
86 }
87
88 static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) {
89 JobDependency *l, *last;
90
91 assert(j);
92 assert(other);
93 assert(j->unit == other->unit);
94 assert(!j->installed);
95
96 /* Merges 'other' into 'j' and then deletes 'other'. */
97
98 j->type = t;
99 j->state = JOB_WAITING;
100 j->override = j->override || other->override;
101 j->irreversible = j->irreversible || other->irreversible;
102
103 j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
104
105 /* Patch us in as new owner of the JobDependency objects */
106 last = NULL;
107 LIST_FOREACH(subject, l, other->subject_list) {
108 assert(l->subject == other);
109 l->subject = j;
110 last = l;
111 }
112
113 /* Merge both lists */
114 if (last) {
115 last->subject_next = j->subject_list;
116 if (j->subject_list)
117 j->subject_list->subject_prev = last;
118 j->subject_list = other->subject_list;
119 }
120
121 /* Patch us in as new owner of the JobDependency objects */
122 last = NULL;
123 LIST_FOREACH(object, l, other->object_list) {
124 assert(l->object == other);
125 l->object = j;
126 last = l;
127 }
128
129 /* Merge both lists */
130 if (last) {
131 last->object_next = j->object_list;
132 if (j->object_list)
133 j->object_list->object_prev = last;
134 j->object_list = other->object_list;
135 }
136
137 /* Kill the other job */
138 other->subject_list = NULL;
139 other->object_list = NULL;
140 transaction_delete_job(tr, other, true);
141 }
142
143 _pure_ static bool job_is_conflicted_by(Job *j) {
144 JobDependency *l;
145
146 assert(j);
147
148 /* Returns true if this job is pulled in by a least one
149 * ConflictedBy dependency. */
150
151 LIST_FOREACH(object, l, j->object_list)
152 if (l->conflicts)
153 return true;
154
155 return false;
156 }
157
158 static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
159 Job *k;
160
161 assert(j);
162
163 /* Tries to delete one item in the linked list
164 * j->transaction_next->transaction_next->... that conflicts
165 * with another one, in an attempt to make an inconsistent
166 * transaction work. */
167
168 /* We rely here on the fact that if a merged with b does not
169 * merge with c, either a or b merge with c neither */
170 LIST_FOREACH(transaction, j, j)
171 LIST_FOREACH(transaction, k, j->transaction_next) {
172 Job *d;
173
174 /* Is this one mergeable? Then skip it */
175 if (job_type_is_mergeable(j->type, k->type))
176 continue;
177
178 /* Ok, we found two that conflict, let's see if we can
179 * drop one of them */
180 if (!j->matters_to_anchor && !k->matters_to_anchor) {
181
182 /* Both jobs don't matter, so let's
183 * find the one that is smarter to
184 * remove. Let's think positive and
185 * rather remove stops then starts --
186 * except if something is being
187 * stopped because it is conflicted by
188 * another unit in which case we
189 * rather remove the start. */
190
191 log_debug_unit(j->unit->id,
192 "Looking at job %s/%s conflicted_by=%s",
193 j->unit->id, job_type_to_string(j->type),
194 yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
195 log_debug_unit(k->unit->id,
196 "Looking at job %s/%s conflicted_by=%s",
197 k->unit->id, job_type_to_string(k->type),
198 yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
199
200 if (j->type == JOB_STOP) {
201
202 if (job_is_conflicted_by(j))
203 d = k;
204 else
205 d = j;
206
207 } else if (k->type == JOB_STOP) {
208
209 if (job_is_conflicted_by(k))
210 d = j;
211 else
212 d = k;
213 } else
214 d = j;
215
216 } else if (!j->matters_to_anchor)
217 d = j;
218 else if (!k->matters_to_anchor)
219 d = k;
220 else
221 return -ENOEXEC;
222
223 /* Ok, we can drop one, so let's do so. */
224 log_debug_unit(d->unit->id,
225 "Fixing conflicting jobs by deleting job %s/%s",
226 d->unit->id, job_type_to_string(d->type));
227 transaction_delete_job(tr, d, true);
228 return 0;
229 }
230
231 return -EINVAL;
232 }
233
234 static int transaction_merge_jobs(Transaction *tr, DBusError *e) {
235 Job *j;
236 Iterator i;
237 int r;
238
239 assert(tr);
240
241 /* First step, check whether any of the jobs for one specific
242 * task conflict. If so, try to drop one of them. */
243 HASHMAP_FOREACH(j, tr->jobs, i) {
244 JobType t;
245 Job *k;
246
247 t = j->type;
248 LIST_FOREACH(transaction, k, j->transaction_next) {
249 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
250 continue;
251
252 /* OK, we could not merge all jobs for this
253 * action. Let's see if we can get rid of one
254 * of them */
255
256 r = delete_one_unmergeable_job(tr, j);
257 if (r >= 0)
258 /* Ok, we managed to drop one, now
259 * let's ask our callers to call us
260 * again after garbage collecting */
261 return -EAGAIN;
262
263 /* We couldn't merge anything. Failure */
264 dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
265 job_type_to_string(t), job_type_to_string(k->type), k->unit->id);
266 return r;
267 }
268 }
269
270 /* Second step, merge the jobs. */
271 HASHMAP_FOREACH(j, tr->jobs, i) {
272 JobType t = j->type;
273 Job *k;
274
275 /* Merge all transaction jobs for j->unit */
276 LIST_FOREACH(transaction, k, j->transaction_next)
277 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
278
279 while ((k = j->transaction_next)) {
280 if (tr->anchor_job == k) {
281 transaction_merge_and_delete_job(tr, k, j, t);
282 j = k;
283 } else
284 transaction_merge_and_delete_job(tr, j, k, t);
285 }
286
287 assert(!j->transaction_next);
288 assert(!j->transaction_prev);
289 }
290
291 return 0;
292 }
293
294 static void transaction_drop_redundant(Transaction *tr) {
295 Job *j;
296 Iterator i;
297
298 /* Goes through the transaction and removes all jobs of the units
299 * whose jobs are all noops. If not all of a unit's jobs are
300 * redundant, they are kept. */
301
302 assert(tr);
303
304 rescan:
305 HASHMAP_FOREACH(j, tr->jobs, i) {
306 Job *k;
307
308 LIST_FOREACH(transaction, k, j) {
309
310 if (tr->anchor_job == k ||
311 !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
312 (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type)))
313 goto next_unit;
314 }
315
316 /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
317 transaction_delete_job(tr, j, false);
318 goto rescan;
319 next_unit:;
320 }
321 }
322
323 _pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) {
324 assert(u);
325 assert(!j->transaction_prev);
326
327 /* Checks whether at least one of the jobs for this unit
328 * matters to the anchor. */
329
330 LIST_FOREACH(transaction, j, j)
331 if (j->matters_to_anchor)
332 return true;
333
334 return false;
335 }
336
337 static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, DBusError *e) {
338 Iterator i;
339 Unit *u;
340 int r;
341
342 assert(tr);
343 assert(j);
344 assert(!j->transaction_prev);
345
346 /* Does a recursive sweep through the ordering graph, looking
347 * for a cycle. If we find cycle we try to break it. */
348
349 /* Have we seen this before? */
350 if (j->generation == generation) {
351 Job *k, *delete;
352
353 /* If the marker is NULL we have been here already and
354 * decided the job was loop-free from here. Hence
355 * shortcut things and return right-away. */
356 if (!j->marker)
357 return 0;
358
359 /* So, the marker is not NULL and we already have been
360 * here. We have a cycle. Let's try to break it. We go
361 * backwards in our path and try to find a suitable
362 * job to remove. We use the marker to find our way
363 * back, since smart how we are we stored our way back
364 * in there. */
365 log_warning_unit(j->unit->id,
366 "Found ordering cycle on %s/%s",
367 j->unit->id, job_type_to_string(j->type));
368
369 delete = NULL;
370 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
371
372 /* logging for j not k here here to provide consistent narrative */
373 log_info_unit(j->unit->id,
374 "Walked on cycle path to %s/%s",
375 k->unit->id, job_type_to_string(k->type));
376
377 if (!delete &&
378 !unit_matters_to_anchor(k->unit, k)) {
379 /* Ok, we can drop this one, so let's
380 * do so. */
381 delete = k;
382 }
383
384 /* Check if this in fact was the beginning of
385 * the cycle */
386 if (k == j)
387 break;
388 }
389
390
391 if (delete) {
392 /* logging for j not k here here to provide consistent narrative */
393 log_warning_unit(j->unit->id,
394 "Breaking ordering cycle by deleting job %s/%s",
395 delete->unit->id, job_type_to_string(delete->type));
396 log_error_unit(delete->unit->id,
397 "Job %s/%s deleted to break ordering cycle starting with %s/%s",
398 delete->unit->id, job_type_to_string(delete->type),
399 j->unit->id, job_type_to_string(j->type));
400 unit_status_printf(delete->unit, ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF,
401 "Ordering cycle found, skipping %s");
402 transaction_delete_unit(tr, delete->unit);
403 return -EAGAIN;
404 }
405
406 log_error("Unable to break cycle");
407
408 dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
409 "Transaction order is cyclic. See system logs for details.");
410 return -ENOEXEC;
411 }
412
413 /* Make the marker point to where we come from, so that we can
414 * find our way backwards if we want to break a cycle. We use
415 * a special marker for the beginning: we point to
416 * ourselves. */
417 j->marker = from ? from : j;
418 j->generation = generation;
419
420 /* We assume that the dependencies are bidirectional, and
421 * hence can ignore UNIT_AFTER */
422 SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
423 Job *o;
424
425 /* Is there a job for this unit? */
426 o = hashmap_get(tr->jobs, u);
427 if (!o) {
428 /* Ok, there is no job for this in the
429 * transaction, but maybe there is already one
430 * running? */
431 o = u->job;
432 if (!o)
433 continue;
434 }
435
436 r = transaction_verify_order_one(tr, o, j, generation, e);
437 if (r < 0)
438 return r;
439 }
440
441 /* Ok, let's backtrack, and remember that this entry is not on
442 * our path anymore. */
443 j->marker = NULL;
444
445 return 0;
446 }
447
448 static int transaction_verify_order(Transaction *tr, unsigned *generation, DBusError *e) {
449 Job *j;
450 int r;
451 Iterator i;
452 unsigned g;
453
454 assert(tr);
455 assert(generation);
456
457 /* Check if the ordering graph is cyclic. If it is, try to fix
458 * that up by dropping one of the jobs. */
459
460 g = (*generation)++;
461
462 HASHMAP_FOREACH(j, tr->jobs, i)
463 if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0)
464 return r;
465
466 return 0;
467 }
468
469 static void transaction_collect_garbage(Transaction *tr) {
470 Iterator i;
471 Job *j;
472
473 assert(tr);
474
475 /* Drop jobs that are not required by any other job */
476
477 rescan:
478 HASHMAP_FOREACH(j, tr->jobs, i) {
479 if (tr->anchor_job == j || j->object_list) {
480 /* log_debug("Keeping job %s/%s because of %s/%s", */
481 /* j->unit->id, job_type_to_string(j->type), */
482 /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
483 /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
484 continue;
485 }
486
487 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
488 transaction_delete_job(tr, j, true);
489 goto rescan;
490 }
491 }
492
493 static int transaction_is_destructive(Transaction *tr, JobMode mode, DBusError *e) {
494 Iterator i;
495 Job *j;
496
497 assert(tr);
498
499 /* Checks whether applying this transaction means that
500 * existing jobs would be replaced */
501
502 HASHMAP_FOREACH(j, tr->jobs, i) {
503
504 /* Assume merged */
505 assert(!j->transaction_prev);
506 assert(!j->transaction_next);
507
508 if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
509 !job_type_is_superset(j->type, j->unit->job->type)) {
510
511 dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
512 return -EEXIST;
513 }
514 }
515
516 return 0;
517 }
518
519 static void transaction_minimize_impact(Transaction *tr) {
520 Job *j;
521 Iterator i;
522
523 assert(tr);
524
525 /* Drops all unnecessary jobs that reverse already active jobs
526 * or that stop a running service. */
527
528 rescan:
529 HASHMAP_FOREACH(j, tr->jobs, i) {
530 LIST_FOREACH(transaction, j, j) {
531 bool stops_running_service, changes_existing_job;
532
533 /* If it matters, we shouldn't drop it */
534 if (j->matters_to_anchor)
535 continue;
536
537 /* Would this stop a running service?
538 * Would this change an existing job?
539 * If so, let's drop this entry */
540
541 stops_running_service =
542 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
543
544 changes_existing_job =
545 j->unit->job &&
546 job_type_is_conflicting(j->type, j->unit->job->type);
547
548 if (!stops_running_service && !changes_existing_job)
549 continue;
550
551 if (stops_running_service)
552 log_debug_unit(j->unit->id,
553 "%s/%s would stop a running service.",
554 j->unit->id, job_type_to_string(j->type));
555
556 if (changes_existing_job)
557 log_debug_unit(j->unit->id,
558 "%s/%s would change existing job.",
559 j->unit->id, job_type_to_string(j->type));
560
561 /* Ok, let's get rid of this */
562 log_debug_unit(j->unit->id,
563 "Deleting %s/%s to minimize impact.",
564 j->unit->id, job_type_to_string(j->type));
565
566 transaction_delete_job(tr, j, true);
567 goto rescan;
568 }
569 }
570 }
571
572 static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
573 Iterator i;
574 Job *j;
575 int r;
576
577 /* Moves the transaction jobs to the set of active jobs */
578
579 if (mode == JOB_ISOLATE) {
580
581 /* When isolating first kill all installed jobs which
582 * aren't part of the new transaction */
583 HASHMAP_FOREACH(j, m->jobs, i) {
584 assert(j->installed);
585
586 if (hashmap_get(tr->jobs, j->unit))
587 continue;
588
589 /* Not invalidating recursively. Avoids triggering
590 * OnFailure= actions of dependent jobs. Also avoids
591 * invalidating our iterator. */
592 job_finish_and_invalidate(j, JOB_CANCELED, false);
593 }
594 }
595
596 HASHMAP_FOREACH(j, tr->jobs, i) {
597 /* Assume merged */
598 assert(!j->transaction_prev);
599 assert(!j->transaction_next);
600
601 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
602 if (r < 0)
603 goto rollback;
604 }
605
606 while ((j = hashmap_steal_first(tr->jobs))) {
607 Job *installed_job;
608
609 /* Clean the job dependencies */
610 transaction_unlink_job(tr, j, false);
611
612 installed_job = job_install(j);
613 if (installed_job != j) {
614 /* j has been merged into a previously installed job */
615 if (tr->anchor_job == j)
616 tr->anchor_job = installed_job;
617 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
618 job_free(j);
619 j = installed_job;
620 }
621
622 job_add_to_run_queue(j);
623 job_add_to_dbus_queue(j);
624 job_start_timer(j);
625 job_shutdown_magic(j);
626 }
627
628 return 0;
629
630 rollback:
631
632 HASHMAP_FOREACH(j, tr->jobs, i)
633 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
634
635 return r;
636 }
637
638 int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
639 Iterator i;
640 Job *j;
641 int r;
642 unsigned generation = 1;
643
644 assert(tr);
645
646 /* This applies the changes recorded in tr->jobs to
647 * the actual list of jobs, if possible. */
648
649 /* Reset the generation counter of all installed jobs. The detection of cycles
650 * looks at installed jobs. If they had a non-zero generation from some previous
651 * walk of the graph, the algorithm would break. */
652 HASHMAP_FOREACH(j, m->jobs, i)
653 j->generation = 0;
654
655 /* First step: figure out which jobs matter */
656 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
657
658 /* Second step: Try not to stop any running services if
659 * we don't have to. Don't try to reverse running
660 * jobs if we don't have to. */
661 if (mode == JOB_FAIL)
662 transaction_minimize_impact(tr);
663
664 /* Third step: Drop redundant jobs */
665 transaction_drop_redundant(tr);
666
667 for (;;) {
668 /* Fourth step: Let's remove unneeded jobs that might
669 * be lurking. */
670 if (mode != JOB_ISOLATE)
671 transaction_collect_garbage(tr);
672
673 /* Fifth step: verify order makes sense and correct
674 * cycles if necessary and possible */
675 r = transaction_verify_order(tr, &generation, e);
676 if (r >= 0)
677 break;
678
679 if (r != -EAGAIN) {
680 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
681 return r;
682 }
683
684 /* Let's see if the resulting transaction ordering
685 * graph is still cyclic... */
686 }
687
688 for (;;) {
689 /* Sixth step: let's drop unmergeable entries if
690 * necessary and possible, merge entries we can
691 * merge */
692 r = transaction_merge_jobs(tr, e);
693 if (r >= 0)
694 break;
695
696 if (r != -EAGAIN) {
697 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
698 return r;
699 }
700
701 /* Seventh step: an entry got dropped, let's garbage
702 * collect its dependencies. */
703 if (mode != JOB_ISOLATE)
704 transaction_collect_garbage(tr);
705
706 /* Let's see if the resulting transaction still has
707 * unmergeable entries ... */
708 }
709
710 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
711 transaction_drop_redundant(tr);
712
713 /* Ninth step: check whether we can actually apply this */
714 r = transaction_is_destructive(tr, mode, e);
715 if (r < 0) {
716 log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
717 return r;
718 }
719
720 /* Tenth step: apply changes */
721 r = transaction_apply(tr, m, mode);
722 if (r < 0) {
723 log_warning("Failed to apply transaction: %s", strerror(-r));
724 return r;
725 }
726
727 assert(hashmap_isempty(tr->jobs));
728
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. */
735
736 if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0)
737 pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
738 }
739
740 return 0;
741 }
742
743 static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
744 Job *j, *f;
745
746 assert(tr);
747 assert(unit);
748
749 /* Looks for an existing prospective job and returns that. If
750 * it doesn't exist it is created and added to the prospective
751 * jobs list. */
752
753 f = hashmap_get(tr->jobs, unit);
754
755 LIST_FOREACH(transaction, j, f) {
756 assert(j->unit == unit);
757
758 if (j->type == type) {
759 if (is_new)
760 *is_new = false;
761 return j;
762 }
763 }
764
765 j = job_new(unit, type);
766 if (!j)
767 return NULL;
768
769 j->generation = 0;
770 j->marker = NULL;
771 j->matters_to_anchor = false;
772 j->override = override;
773 j->irreversible = tr->irreversible;
774
775 LIST_PREPEND(Job, transaction, f, j);
776
777 if (hashmap_replace(tr->jobs, unit, f) < 0) {
778 LIST_REMOVE(Job, transaction, f, j);
779 job_free(j);
780 return NULL;
781 }
782
783 if (is_new)
784 *is_new = true;
785
786 /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
787
788 return j;
789 }
790
791 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
792 assert(tr);
793 assert(j);
794
795 if (j->transaction_prev)
796 j->transaction_prev->transaction_next = j->transaction_next;
797 else if (j->transaction_next)
798 hashmap_replace(tr->jobs, j->unit, j->transaction_next);
799 else
800 hashmap_remove_value(tr->jobs, j->unit, j);
801
802 if (j->transaction_next)
803 j->transaction_next->transaction_prev = j->transaction_prev;
804
805 j->transaction_prev = j->transaction_next = NULL;
806
807 while (j->subject_list)
808 job_dependency_free(j->subject_list);
809
810 while (j->object_list) {
811 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
812
813 job_dependency_free(j->object_list);
814
815 if (other && delete_dependencies) {
816 log_debug_unit(other->unit->id,
817 "Deleting job %s/%s as dependency of job %s/%s",
818 other->unit->id, job_type_to_string(other->type),
819 j->unit->id, job_type_to_string(j->type));
820 transaction_delete_job(tr, other, delete_dependencies);
821 }
822 }
823 }
824
825 int transaction_add_job_and_dependencies(
826 Transaction *tr,
827 JobType type,
828 Unit *unit,
829 Job *by,
830 bool matters,
831 bool override,
832 bool conflicts,
833 bool ignore_requirements,
834 bool ignore_order,
835 DBusError *e) {
836 Job *ret;
837 Iterator i;
838 Unit *dep;
839 int r;
840 bool is_new;
841
842 assert(tr);
843 assert(type < _JOB_TYPE_MAX);
844 assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
845 assert(unit);
846
847 /* log_debug("Pulling in %s/%s from %s/%s", */
848 /* unit->id, job_type_to_string(type), */
849 /* by ? by->unit->id : "NA", */
850 /* by ? job_type_to_string(by->type) : "NA"); */
851
852 if (unit->load_state != UNIT_LOADED &&
853 unit->load_state != UNIT_ERROR &&
854 unit->load_state != UNIT_NOT_FOUND &&
855 unit->load_state != UNIT_MASKED) {
856 dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
857 return -EINVAL;
858 }
859
860 if (type != JOB_STOP && (unit->load_state == UNIT_ERROR || unit->load_state == UNIT_NOT_FOUND)) {
861 dbus_set_error(e, BUS_ERROR_LOAD_FAILED,
862 "Unit %s failed to load: %s. "
863 "See system logs and 'systemctl status %s' for details.",
864 unit->id,
865 strerror(-unit->load_error),
866 unit->id);
867 return -EINVAL;
868 }
869
870 if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
871 dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
872 return -EADDRNOTAVAIL;
873 }
874
875 if (!unit_job_is_applicable(unit, type)) {
876 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);
877 return -EBADR;
878 }
879
880 /* First add the job. */
881 ret = transaction_add_one_job(tr, type, unit, override, &is_new);
882 if (!ret)
883 return -ENOMEM;
884
885 ret->ignore_order = ret->ignore_order || ignore_order;
886
887 /* Then, add a link to the job. */
888 if (by) {
889 if (!job_dependency_new(by, ret, matters, conflicts))
890 return -ENOMEM;
891 } else {
892 /* If the job has no parent job, it is the anchor job. */
893 assert(!tr->anchor_job);
894 tr->anchor_job = ret;
895 }
896
897 if (is_new && !ignore_requirements && type != JOB_NOP) {
898 Set *following;
899
900 /* If we are following some other unit, make sure we
901 * add all dependencies of everybody following. */
902 if (unit_following_set(ret->unit, &following) > 0) {
903 SET_FOREACH(dep, following, i) {
904 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
905 if (r < 0) {
906 log_warning_unit(dep->id,
907 "Cannot add dependency job for unit %s, ignoring: %s",
908 dep->id, bus_error(e, r));
909
910 if (e)
911 dbus_error_free(e);
912 }
913 }
914
915 set_free(following);
916 }
917
918 /* Finally, recursively add in all dependencies. */
919 if (type == JOB_START || type == JOB_RESTART) {
920 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
921 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
922 if (r < 0) {
923 if (r != -EBADR)
924 goto fail;
925
926 if (e)
927 dbus_error_free(e);
928 }
929 }
930
931 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
932 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
933 if (r < 0) {
934 if (r != -EBADR)
935 goto fail;
936
937 if (e)
938 dbus_error_free(e);
939 }
940 }
941
942 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) {
943 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
944 if (r < 0) {
945 log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
946 "Cannot add dependency job for unit %s, ignoring: %s",
947 dep->id, bus_error(e, r));
948
949 if (e)
950 dbus_error_free(e);
951 }
952 }
953
954 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
955 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
956 if (r < 0) {
957 log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
958 "Cannot add dependency job for unit %s, ignoring: %s",
959 dep->id, bus_error(e, r));
960
961 if (e)
962 dbus_error_free(e);
963 }
964 }
965
966 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
967 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
968 if (r < 0) {
969 if (r != -EBADR)
970 goto fail;
971
972 if (e)
973 dbus_error_free(e);
974 }
975 }
976
977 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) {
978 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
979 if (r < 0) {
980 log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
981 "Cannot add dependency job for unit %s, ignoring: %s",
982 dep->id, bus_error(e, r));
983
984 if (e)
985 dbus_error_free(e);
986 }
987 }
988
989 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
990 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
991 if (r < 0) {
992 if (r != -EBADR)
993 goto fail;
994
995 if (e)
996 dbus_error_free(e);
997 }
998 }
999
1000 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
1001 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
1002 if (r < 0) {
1003 log_warning_unit(dep->id,
1004 "Cannot add dependency job for unit %s, ignoring: %s",
1005 dep->id, bus_error(e, r));
1006
1007 if (e)
1008 dbus_error_free(e);
1009 }
1010 }
1011
1012 }
1013
1014 if (type == JOB_STOP || type == JOB_RESTART) {
1015
1016 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
1017 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1018 if (r < 0) {
1019 if (r != -EBADR)
1020 goto fail;
1021
1022 if (e)
1023 dbus_error_free(e);
1024 }
1025 }
1026
1027 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) {
1028 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1029 if (r < 0) {
1030 if (r != -EBADR)
1031 goto fail;
1032
1033 if (e)
1034 dbus_error_free(e);
1035 }
1036 }
1037
1038 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONSISTS_OF], i) {
1039 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1040 if (r < 0) {
1041 if (r != -EBADR)
1042 goto fail;
1043
1044 if (e)
1045 dbus_error_free(e);
1046 }
1047 }
1048
1049 }
1050
1051 if (type == JOB_RELOAD) {
1052
1053 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
1054 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
1055 if (r < 0) {
1056 log_warning_unit(dep->id,
1057 "Cannot add dependency reload job for unit %s, ignoring: %s",
1058 dep->id, bus_error(e, r));
1059
1060 if (e)
1061 dbus_error_free(e);
1062 }
1063 }
1064 }
1065
1066 /* JOB_VERIFY_STARTED require no dependency handling */
1067 }
1068
1069 return 0;
1070
1071 fail:
1072 return r;
1073 }
1074
1075 int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
1076 Iterator i;
1077 Unit *u;
1078 char *k;
1079 int r;
1080
1081 assert(tr);
1082 assert(m);
1083
1084 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1085
1086 /* ignore aliases */
1087 if (u->id != k)
1088 continue;
1089
1090 if (u->ignore_on_isolate)
1091 continue;
1092
1093 /* No need to stop inactive jobs */
1094 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
1095 continue;
1096
1097 /* Is there already something listed for this? */
1098 if (hashmap_get(tr->jobs, u))
1099 continue;
1100
1101 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
1102 if (r < 0)
1103 log_warning_unit(u->id,
1104 "Cannot add isolate job for unit %s, ignoring: %s",
1105 u->id, strerror(-r));
1106 }
1107
1108 return 0;
1109 }
1110
1111 Transaction *transaction_new(bool irreversible) {
1112 Transaction *tr;
1113
1114 tr = new0(Transaction, 1);
1115 if (!tr)
1116 return NULL;
1117
1118 tr->jobs = hashmap_new(trivial_hash_func, trivial_compare_func);
1119 if (!tr->jobs) {
1120 free(tr);
1121 return NULL;
1122 }
1123
1124 tr->irreversible = irreversible;
1125
1126 return tr;
1127 }
1128
1129 void transaction_free(Transaction *tr) {
1130 assert(hashmap_isempty(tr->jobs));
1131 hashmap_free(tr->jobs);
1132 free(tr);
1133 }