]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/transaction.c
core: redefine unit_status_printf()
[thirdparty/systemd.git] / src / core / transaction.c
CommitLineData
7c0436b9
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
f2b68789
LP
22#include <unistd.h>
23#include <fcntl.h>
24
75778e21
MS
25#include "transaction.h"
26#include "bus-errors.h"
27
28static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
29
30static 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
d6a093d0 38 job_free(j);
75778e21
MS
39}
40
41static 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
51void transaction_abort(Transaction *tr) {
52 Job *j;
53
54 assert(tr);
55
56 while ((j = hashmap_first(tr->jobs)))
1b9cea0c 57 transaction_delete_job(tr, j, false);
75778e21
MS
58
59 assert(hashmap_isempty(tr->jobs));
75778e21
MS
60}
61
0d9989aa 62static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
75778e21
MS
63 JobDependency *l;
64
75778e21
MS
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
0d9989aa
MS
70 j->matters_to_anchor = true;
71 j->generation = generation;
75778e21 72
0d9989aa 73 LIST_FOREACH(subject, l, j->subject_list) {
75778e21
MS
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
0d9989aa 83 transaction_find_jobs_that_matter_to_anchor(l->object, generation);
75778e21
MS
84 }
85}
86
87static 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;
23ade460 100 j->irreversible = j->irreversible || other->irreversible;
75778e21
MS
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
142static 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
157static 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
66870f90
ZJS
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)));
75778e21
MS
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. */
66870f90
ZJS
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));
75778e21
MS
226 transaction_delete_job(tr, d, true);
227 return 0;
228 }
229
230 return -EINVAL;
231}
232
233static 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) {
e0209d83 248 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
75778e21
MS
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
e0209d83 274 /* Merge all transaction jobs for j->unit */
75778e21 275 LIST_FOREACH(transaction, k, j->transaction_next)
e0209d83 276 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
75778e21 277
75778e21 278 while ((k = j->transaction_next)) {
656bbffc 279 if (tr->anchor_job == k) {
75778e21
MS
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
75778e21
MS
286 assert(!j->transaction_next);
287 assert(!j->transaction_prev);
288 }
289
290 return 0;
291}
292
293static void transaction_drop_redundant(Transaction *tr) {
055163ad
MS
294 Job *j;
295 Iterator i;
75778e21 296
055163ad
MS
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. */
75778e21 300
055163ad 301 assert(tr);
75778e21 302
055163ad
MS
303rescan:
304 HASHMAP_FOREACH(j, tr->jobs, i) {
305 Job *k;
75778e21 306
055163ad 307 LIST_FOREACH(transaction, k, j) {
75778e21 308
055163ad
MS
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;
75778e21
MS
313 }
314
055163ad
MS
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 }
75778e21
MS
320}
321
322static 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
336static 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. */
66870f90
ZJS
364 log_warning_unit(j->unit->id,
365 "Found ordering cycle on %s/%s",
366 j->unit->id, job_type_to_string(j->type));
75778e21
MS
367
368 delete = NULL;
369 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
370
66870f90
ZJS
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));
75778e21
MS
375
376 if (!delete &&
75778e21
MS
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) {
66870f90
ZJS
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 status_printf(ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, true,
400 "Ordering cycle found, skipping %s", unit_description(delete->unit));
75778e21
MS
401 transaction_delete_unit(tr, delete->unit);
402 return -EAGAIN;
403 }
404
405 log_error("Unable to break cycle");
406
66870f90
ZJS
407 dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
408 "Transaction order is cyclic. See system logs for details.");
75778e21
MS
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
c5315881 419 /* We assume that the dependencies are bidirectional, and
75778e21
MS
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
447static 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
468static void transaction_collect_garbage(Transaction *tr) {
055163ad
MS
469 Iterator i;
470 Job *j;
75778e21
MS
471
472 assert(tr);
473
474 /* Drop jobs that are not required by any other job */
475
055163ad
MS
476rescan:
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;
75778e21
MS
484 }
485
055163ad
MS
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 }
75778e21
MS
490}
491
23ade460 492static int transaction_is_destructive(Transaction *tr, JobMode mode, DBusError *e) {
75778e21
MS
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
23ade460 507 if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
75778e21
MS
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
518static void transaction_minimize_impact(Transaction *tr) {
055163ad
MS
519 Job *j;
520 Iterator i;
521
75778e21
MS
522 assert(tr);
523
524 /* Drops all unnecessary jobs that reverse already active jobs
525 * or that stop a running service. */
526
055163ad
MS
527rescan:
528 HASHMAP_FOREACH(j, tr->jobs, i) {
529 LIST_FOREACH(transaction, j, j) {
530 bool stops_running_service, changes_existing_job;
75778e21 531
055163ad
MS
532 /* If it matters, we shouldn't drop it */
533 if (j->matters_to_anchor)
534 continue;
75778e21 535
055163ad
MS
536 /* Would this stop a running service?
537 * Would this change an existing job?
538 * If so, let's drop this entry */
75778e21 539
055163ad
MS
540 stops_running_service =
541 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
75778e21 542
055163ad
MS
543 changes_existing_job =
544 j->unit->job &&
545 job_type_is_conflicting(j->type, j->unit->job->type);
75778e21 546
055163ad
MS
547 if (!stops_running_service && !changes_existing_job)
548 continue;
75778e21 549
055163ad 550 if (stops_running_service)
66870f90
ZJS
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));
75778e21 554
055163ad 555 if (changes_existing_job)
66870f90
ZJS
556 log_debug_unit(j->unit->id,
557 "%s/%s would change existing job.",
558 j->unit->id, job_type_to_string(j->type));
75778e21 559
055163ad 560 /* Ok, let's get rid of this */
66870f90
ZJS
561 log_debug_unit(j->unit->id,
562 "Deleting %s/%s to minimize impact.",
563 j->unit->id, job_type_to_string(j->type));
75778e21 564
055163ad
MS
565 transaction_delete_job(tr, j, true);
566 goto rescan;
75778e21 567 }
055163ad 568 }
75778e21
MS
569}
570
571static 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 */
75778e21
MS
582 HASHMAP_FOREACH(j, m->jobs, i) {
583 assert(j->installed);
584
585 if (hashmap_get(tr->jobs, j->unit))
586 continue;
587
5273510e
MS
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);
75778e21
MS
592 }
593 }
594
595 HASHMAP_FOREACH(j, tr->jobs, i) {
596 /* Assume merged */
597 assert(!j->transaction_prev);
598 assert(!j->transaction_next);
599
75778e21
MS
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))) {
656bbffc
MS
606 Job *installed_job;
607
75778e21
MS
608 /* Clean the job dependencies */
609 transaction_unlink_job(tr, j, false);
610
656bbffc
MS
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 }
05d576f1 620
75778e21
MS
621 job_add_to_run_queue(j);
622 job_add_to_dbus_queue(j);
623 job_start_timer(j);
c65eb836 624 job_shutdown_magic(j);
75778e21
MS
625 }
626
75778e21
MS
627 return 0;
628
629rollback:
630
d6a093d0 631 HASHMAP_FOREACH(j, tr->jobs, i)
75778e21 632 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
75778e21
MS
633
634 return r;
635}
636
637int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
4e7bd268
MS
638 Iterator i;
639 Job *j;
75778e21
MS
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
4e7bd268
MS
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
75778e21 654 /* First step: figure out which jobs matter */
0d9989aa 655 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
75778e21
MS
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 */
23ade460
MS
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;
75778e21
MS
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));
75778e21 727
f2b68789
LP
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
75778e21
MS
739 return 0;
740}
741
742static 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
3c956cfe
MS
764 j = job_new(unit, type);
765 if (!j)
766 return NULL;
75778e21
MS
767
768 j->generation = 0;
769 j->marker = NULL;
770 j->matters_to_anchor = false;
771 j->override = override;
23ade460 772 j->irreversible = tr->irreversible;
75778e21
MS
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
790static 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)
e6eda1f2 807 job_dependency_free(j->subject_list);
75778e21
MS
808
809 while (j->object_list) {
810 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
811
e6eda1f2 812 job_dependency_free(j->object_list);
75778e21
MS
813
814 if (other && delete_dependencies) {
66870f90
ZJS
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));
75778e21
MS
819 transaction_delete_job(tr, other, delete_dependencies);
820 }
821 }
822}
823
824int 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,
b94fbd30 834 DBusError *e) {
75778e21
MS
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);
e0209d83 843 assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
75778e21
MS
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);
59e132a7 870 return -EADDRNOTAVAIL;
75778e21
MS
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. */
e6eda1f2
MS
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. */
4483f694
MS
891 assert(!tr->anchor_job);
892 tr->anchor_job = ret;
b94fbd30 893 }
e0209d83
MS
894
895 if (is_new && !ignore_requirements && type != JOB_NOP) {
75778e21
MS
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) {
b94fbd30 902 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
75778e21 903 if (r < 0) {
66870f90
ZJS
904 log_warning_unit(dep->id,
905 "Cannot add dependency job for unit %s, ignoring: %s",
906 dep->id, bus_error(e, r));
75778e21
MS
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. */
e0209d83 917 if (type == JOB_START || type == JOB_RESTART) {
75778e21 918 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
b94fbd30 919 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
920 if (r < 0) {
921 if (r != -EBADR)
922 goto fail;
923
924 if (e)
925 dbus_error_free(e);
926 }
927 }
928
7f2cddae 929 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
b94fbd30 930 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
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) {
b94fbd30 941 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
75778e21 942 if (r < 0) {
66870f90
ZJS
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));
75778e21
MS
946
947 if (e)
948 dbus_error_free(e);
949 }
950 }
951
952 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
b94fbd30 953 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
75778e21 954 if (r < 0) {
66870f90
ZJS
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));
75778e21
MS
958
959 if (e)
960 dbus_error_free(e);
961 }
962 }
963
964 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
b94fbd30 965 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
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) {
b94fbd30 976 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
75778e21 977 if (r < 0) {
66870f90
ZJS
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));
75778e21
MS
981
982 if (e)
983 dbus_error_free(e);
984 }
985 }
986
987 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
b94fbd30 988 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
75778e21
MS
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) {
b94fbd30 999 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
75778e21 1000 if (r < 0) {
66870f90
ZJS
1001 log_warning_unit(dep->id,
1002 "Cannot add dependency job for unit %s, ignoring: %s",
1003 dep->id, bus_error(e, r));
75778e21
MS
1004
1005 if (e)
1006 dbus_error_free(e);
1007 }
1008 }
1009
1010 }
1011
e0209d83 1012 if (type == JOB_STOP || type == JOB_RESTART) {
75778e21
MS
1013
1014 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
b94fbd30 1015 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
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) {
b94fbd30 1026 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
1027 if (r < 0) {
1028 if (r != -EBADR)
1029 goto fail;
1030
1031 if (e)
1032 dbus_error_free(e);
1033 }
1034 }
85e9a101
MS
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
75778e21
MS
1047 }
1048
e0209d83 1049 if (type == JOB_RELOAD) {
75778e21 1050
7f2cddae 1051 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
b94fbd30 1052 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
75778e21 1053 if (r < 0) {
66870f90
ZJS
1054 log_warning_unit(dep->id,
1055 "Cannot add dependency reload job for unit %s, ignoring: %s",
1056 dep->id, bus_error(e, r));
75778e21
MS
1057
1058 if (e)
1059 dbus_error_free(e);
1060 }
1061 }
1062 }
1063
e0209d83 1064 /* JOB_VERIFY_STARTED require no dependency handling */
75778e21
MS
1065 }
1066
75778e21
MS
1067 return 0;
1068
1069fail:
1070 return r;
1071}
1072
1073int 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
4483f694 1099 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
75778e21 1100 if (r < 0)
66870f90
ZJS
1101 log_warning_unit(u->id,
1102 "Cannot add isolate job for unit %s, ignoring: %s",
1103 u->id, strerror(-r));
75778e21
MS
1104 }
1105
1106 return 0;
1107}
1108
23ade460 1109Transaction *transaction_new(bool irreversible) {
75778e21
MS
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
23ade460
MS
1122 tr->irreversible = irreversible;
1123
75778e21
MS
1124 return tr;
1125}
1126
1127void transaction_free(Transaction *tr) {
1128 assert(hashmap_isempty(tr->jobs));
1129 hashmap_free(tr->jobs);
1130 free(tr);
1131}