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