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