]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/transaction.c
systemd: do not output status messages once gettys are running
[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 m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) {
738 pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
739 pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
740 }
741 }
742
743 return 0;
744 }
745
746 static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
747 Job *j, *f;
748
749 assert(tr);
750 assert(unit);
751
752 /* Looks for an existing prospective job and returns that. If
753 * it doesn't exist it is created and added to the prospective
754 * jobs list. */
755
756 f = hashmap_get(tr->jobs, unit);
757
758 LIST_FOREACH(transaction, j, f) {
759 assert(j->unit == unit);
760
761 if (j->type == type) {
762 if (is_new)
763 *is_new = false;
764 return j;
765 }
766 }
767
768 j = job_new(unit, type);
769 if (!j)
770 return NULL;
771
772 j->generation = 0;
773 j->marker = NULL;
774 j->matters_to_anchor = false;
775 j->override = override;
776 j->irreversible = tr->irreversible;
777
778 LIST_PREPEND(Job, transaction, f, j);
779
780 if (hashmap_replace(tr->jobs, unit, f) < 0) {
781 LIST_REMOVE(Job, transaction, f, j);
782 job_free(j);
783 return NULL;
784 }
785
786 if (is_new)
787 *is_new = true;
788
789 /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
790
791 return j;
792 }
793
794 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
795 assert(tr);
796 assert(j);
797
798 if (j->transaction_prev)
799 j->transaction_prev->transaction_next = j->transaction_next;
800 else if (j->transaction_next)
801 hashmap_replace(tr->jobs, j->unit, j->transaction_next);
802 else
803 hashmap_remove_value(tr->jobs, j->unit, j);
804
805 if (j->transaction_next)
806 j->transaction_next->transaction_prev = j->transaction_prev;
807
808 j->transaction_prev = j->transaction_next = NULL;
809
810 while (j->subject_list)
811 job_dependency_free(j->subject_list);
812
813 while (j->object_list) {
814 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
815
816 job_dependency_free(j->object_list);
817
818 if (other && delete_dependencies) {
819 log_debug_unit(other->unit->id,
820 "Deleting job %s/%s as dependency of job %s/%s",
821 other->unit->id, job_type_to_string(other->type),
822 j->unit->id, job_type_to_string(j->type));
823 transaction_delete_job(tr, other, delete_dependencies);
824 }
825 }
826 }
827
828 int transaction_add_job_and_dependencies(
829 Transaction *tr,
830 JobType type,
831 Unit *unit,
832 Job *by,
833 bool matters,
834 bool override,
835 bool conflicts,
836 bool ignore_requirements,
837 bool ignore_order,
838 DBusError *e) {
839 Job *ret;
840 Iterator i;
841 Unit *dep;
842 int r;
843 bool is_new;
844
845 assert(tr);
846 assert(type < _JOB_TYPE_MAX);
847 assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
848 assert(unit);
849
850 /* log_debug("Pulling in %s/%s from %s/%s", */
851 /* unit->id, job_type_to_string(type), */
852 /* by ? by->unit->id : "NA", */
853 /* by ? job_type_to_string(by->type) : "NA"); */
854
855 if (unit->load_state != UNIT_LOADED &&
856 unit->load_state != UNIT_ERROR &&
857 unit->load_state != UNIT_NOT_FOUND &&
858 unit->load_state != UNIT_MASKED) {
859 dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
860 return -EINVAL;
861 }
862
863 if (type != JOB_STOP && (unit->load_state == UNIT_ERROR || unit->load_state == UNIT_NOT_FOUND)) {
864 dbus_set_error(e, BUS_ERROR_LOAD_FAILED,
865 "Unit %s failed to load: %s. "
866 "See system logs and 'systemctl status %s' for details.",
867 unit->id,
868 strerror(-unit->load_error),
869 unit->id);
870 return -EINVAL;
871 }
872
873 if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
874 dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
875 return -EADDRNOTAVAIL;
876 }
877
878 if (!unit_job_is_applicable(unit, type)) {
879 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);
880 return -EBADR;
881 }
882
883 /* First add the job. */
884 ret = transaction_add_one_job(tr, type, unit, override, &is_new);
885 if (!ret)
886 return -ENOMEM;
887
888 ret->ignore_order = ret->ignore_order || ignore_order;
889
890 /* Then, add a link to the job. */
891 if (by) {
892 if (!job_dependency_new(by, ret, matters, conflicts))
893 return -ENOMEM;
894 } else {
895 /* If the job has no parent job, it is the anchor job. */
896 assert(!tr->anchor_job);
897 tr->anchor_job = ret;
898 }
899
900 if (is_new && !ignore_requirements && type != JOB_NOP) {
901 Set *following;
902
903 /* If we are following some other unit, make sure we
904 * add all dependencies of everybody following. */
905 if (unit_following_set(ret->unit, &following) > 0) {
906 SET_FOREACH(dep, following, i) {
907 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
908 if (r < 0) {
909 log_warning_unit(dep->id,
910 "Cannot add dependency job for unit %s, ignoring: %s",
911 dep->id, bus_error(e, r));
912
913 if (e)
914 dbus_error_free(e);
915 }
916 }
917
918 set_free(following);
919 }
920
921 /* Finally, recursively add in all dependencies. */
922 if (type == JOB_START || type == JOB_RESTART) {
923 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
924 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
925 if (r < 0) {
926 if (r != -EBADR)
927 goto fail;
928
929 if (e)
930 dbus_error_free(e);
931 }
932 }
933
934 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
935 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
936 if (r < 0) {
937 if (r != -EBADR)
938 goto fail;
939
940 if (e)
941 dbus_error_free(e);
942 }
943 }
944
945 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) {
946 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
947 if (r < 0) {
948 log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
949 "Cannot add dependency job for unit %s, ignoring: %s",
950 dep->id, bus_error(e, r));
951
952 if (e)
953 dbus_error_free(e);
954 }
955 }
956
957 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
958 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
959 if (r < 0) {
960 log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
961 "Cannot add dependency job for unit %s, ignoring: %s",
962 dep->id, bus_error(e, r));
963
964 if (e)
965 dbus_error_free(e);
966 }
967 }
968
969 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
970 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
971 if (r < 0) {
972 if (r != -EBADR)
973 goto fail;
974
975 if (e)
976 dbus_error_free(e);
977 }
978 }
979
980 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) {
981 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
982 if (r < 0) {
983 log_full_unit(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING, dep->id,
984 "Cannot add dependency job for unit %s, ignoring: %s",
985 dep->id, bus_error(e, r));
986
987 if (e)
988 dbus_error_free(e);
989 }
990 }
991
992 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
993 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
994 if (r < 0) {
995 if (r != -EBADR)
996 goto fail;
997
998 if (e)
999 dbus_error_free(e);
1000 }
1001 }
1002
1003 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
1004 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
1005 if (r < 0) {
1006 log_warning_unit(dep->id,
1007 "Cannot add dependency job for unit %s, ignoring: %s",
1008 dep->id, bus_error(e, r));
1009
1010 if (e)
1011 dbus_error_free(e);
1012 }
1013 }
1014
1015 }
1016
1017 if (type == JOB_STOP || type == JOB_RESTART) {
1018
1019 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
1020 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1021 if (r < 0) {
1022 if (r != -EBADR)
1023 goto fail;
1024
1025 if (e)
1026 dbus_error_free(e);
1027 }
1028 }
1029
1030 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) {
1031 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1032 if (r < 0) {
1033 if (r != -EBADR)
1034 goto fail;
1035
1036 if (e)
1037 dbus_error_free(e);
1038 }
1039 }
1040
1041 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONSISTS_OF], i) {
1042 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
1043 if (r < 0) {
1044 if (r != -EBADR)
1045 goto fail;
1046
1047 if (e)
1048 dbus_error_free(e);
1049 }
1050 }
1051
1052 }
1053
1054 if (type == JOB_RELOAD) {
1055
1056 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
1057 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
1058 if (r < 0) {
1059 log_warning_unit(dep->id,
1060 "Cannot add dependency reload job for unit %s, ignoring: %s",
1061 dep->id, bus_error(e, r));
1062
1063 if (e)
1064 dbus_error_free(e);
1065 }
1066 }
1067 }
1068
1069 /* JOB_VERIFY_STARTED require no dependency handling */
1070 }
1071
1072 return 0;
1073
1074 fail:
1075 return r;
1076 }
1077
1078 int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
1079 Iterator i;
1080 Unit *u;
1081 char *k;
1082 int r;
1083
1084 assert(tr);
1085 assert(m);
1086
1087 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1088
1089 /* ignore aliases */
1090 if (u->id != k)
1091 continue;
1092
1093 if (u->ignore_on_isolate)
1094 continue;
1095
1096 /* No need to stop inactive jobs */
1097 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
1098 continue;
1099
1100 /* Is there already something listed for this? */
1101 if (hashmap_get(tr->jobs, u))
1102 continue;
1103
1104 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
1105 if (r < 0)
1106 log_warning_unit(u->id,
1107 "Cannot add isolate job for unit %s, ignoring: %s",
1108 u->id, strerror(-r));
1109 }
1110
1111 return 0;
1112 }
1113
1114 Transaction *transaction_new(bool irreversible) {
1115 Transaction *tr;
1116
1117 tr = new0(Transaction, 1);
1118 if (!tr)
1119 return NULL;
1120
1121 tr->jobs = hashmap_new(trivial_hash_func, trivial_compare_func);
1122 if (!tr->jobs) {
1123 free(tr);
1124 return NULL;
1125 }
1126
1127 tr->irreversible = irreversible;
1128
1129 return tr;
1130 }
1131
1132 void transaction_free(Transaction *tr) {
1133 assert(hashmap_isempty(tr->jobs));
1134 hashmap_free(tr->jobs);
1135 free(tr);
1136 }