]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/transaction.c
systemctl: make "systemctl default" use "isolate" job mode
[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;
100
101 j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
102
103 /* Patch us in as new owner of the JobDependency objects */
104 last = NULL;
105 LIST_FOREACH(subject, l, other->subject_list) {
106 assert(l->subject == other);
107 l->subject = j;
108 last = l;
109 }
110
111 /* Merge both lists */
112 if (last) {
113 last->subject_next = j->subject_list;
114 if (j->subject_list)
115 j->subject_list->subject_prev = last;
116 j->subject_list = other->subject_list;
117 }
118
119 /* Patch us in as new owner of the JobDependency objects */
120 last = NULL;
121 LIST_FOREACH(object, l, other->object_list) {
122 assert(l->object == other);
123 l->object = j;
124 last = l;
125 }
126
127 /* Merge both lists */
128 if (last) {
129 last->object_next = j->object_list;
130 if (j->object_list)
131 j->object_list->object_prev = last;
132 j->object_list = other->object_list;
133 }
134
135 /* Kill the other job */
136 other->subject_list = NULL;
137 other->object_list = NULL;
138 transaction_delete_job(tr, other, true);
139}
140
141static bool job_is_conflicted_by(Job *j) {
142 JobDependency *l;
143
144 assert(j);
145
146 /* Returns true if this job is pulled in by a least one
147 * ConflictedBy dependency. */
148
149 LIST_FOREACH(object, l, j->object_list)
150 if (l->conflicts)
151 return true;
152
153 return false;
154}
155
156static int delete_one_unmergeable_job(Transaction *tr, Job *j) {
157 Job *k;
158
159 assert(j);
160
161 /* Tries to delete one item in the linked list
162 * j->transaction_next->transaction_next->... that conflicts
163 * with another one, in an attempt to make an inconsistent
164 * transaction work. */
165
166 /* We rely here on the fact that if a merged with b does not
167 * merge with c, either a or b merge with c neither */
168 LIST_FOREACH(transaction, j, j)
169 LIST_FOREACH(transaction, k, j->transaction_next) {
170 Job *d;
171
172 /* Is this one mergeable? Then skip it */
173 if (job_type_is_mergeable(j->type, k->type))
174 continue;
175
176 /* Ok, we found two that conflict, let's see if we can
177 * drop one of them */
178 if (!j->matters_to_anchor && !k->matters_to_anchor) {
179
180 /* Both jobs don't matter, so let's
181 * find the one that is smarter to
182 * remove. Let's think positive and
183 * rather remove stops then starts --
184 * except if something is being
185 * stopped because it is conflicted by
186 * another unit in which case we
187 * rather remove the start. */
188
66870f90
ZJS
189 log_debug_unit(j->unit->id,
190 "Looking at job %s/%s conflicted_by=%s",
191 j->unit->id, job_type_to_string(j->type),
192 yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
193 log_debug_unit(k->unit->id,
194 "Looking at job %s/%s conflicted_by=%s",
195 k->unit->id, job_type_to_string(k->type),
196 yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
75778e21
MS
197
198 if (j->type == JOB_STOP) {
199
200 if (job_is_conflicted_by(j))
201 d = k;
202 else
203 d = j;
204
205 } else if (k->type == JOB_STOP) {
206
207 if (job_is_conflicted_by(k))
208 d = j;
209 else
210 d = k;
211 } else
212 d = j;
213
214 } else if (!j->matters_to_anchor)
215 d = j;
216 else if (!k->matters_to_anchor)
217 d = k;
218 else
219 return -ENOEXEC;
220
221 /* Ok, we can drop one, so let's do so. */
66870f90
ZJS
222 log_debug_unit(d->unit->id,
223 "Fixing conflicting jobs by deleting job %s/%s",
224 d->unit->id, job_type_to_string(d->type));
75778e21
MS
225 transaction_delete_job(tr, d, true);
226 return 0;
227 }
228
229 return -EINVAL;
230}
231
232static int transaction_merge_jobs(Transaction *tr, DBusError *e) {
233 Job *j;
234 Iterator i;
235 int r;
236
237 assert(tr);
238
239 /* First step, check whether any of the jobs for one specific
240 * task conflict. If so, try to drop one of them. */
241 HASHMAP_FOREACH(j, tr->jobs, i) {
242 JobType t;
243 Job *k;
244
245 t = j->type;
246 LIST_FOREACH(transaction, k, j->transaction_next) {
e0209d83 247 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
75778e21
MS
248 continue;
249
250 /* OK, we could not merge all jobs for this
251 * action. Let's see if we can get rid of one
252 * of them */
253
254 r = delete_one_unmergeable_job(tr, j);
255 if (r >= 0)
256 /* Ok, we managed to drop one, now
257 * let's ask our callers to call us
258 * again after garbage collecting */
259 return -EAGAIN;
260
261 /* We couldn't merge anything. Failure */
262 dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
263 job_type_to_string(t), job_type_to_string(k->type), k->unit->id);
264 return r;
265 }
266 }
267
268 /* Second step, merge the jobs. */
269 HASHMAP_FOREACH(j, tr->jobs, i) {
270 JobType t = j->type;
271 Job *k;
272
e0209d83 273 /* Merge all transaction jobs for j->unit */
75778e21 274 LIST_FOREACH(transaction, k, j->transaction_next)
e0209d83 275 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
75778e21 276
75778e21 277 while ((k = j->transaction_next)) {
656bbffc 278 if (tr->anchor_job == k) {
75778e21
MS
279 transaction_merge_and_delete_job(tr, k, j, t);
280 j = k;
281 } else
282 transaction_merge_and_delete_job(tr, j, k, t);
283 }
284
75778e21
MS
285 assert(!j->transaction_next);
286 assert(!j->transaction_prev);
287 }
288
289 return 0;
290}
291
292static void transaction_drop_redundant(Transaction *tr) {
055163ad
MS
293 Job *j;
294 Iterator i;
75778e21 295
055163ad
MS
296 /* Goes through the transaction and removes all jobs of the units
297 * whose jobs are all noops. If not all of a unit's jobs are
298 * redundant, they are kept. */
75778e21 299
055163ad 300 assert(tr);
75778e21 301
055163ad
MS
302rescan:
303 HASHMAP_FOREACH(j, tr->jobs, i) {
304 Job *k;
75778e21 305
055163ad 306 LIST_FOREACH(transaction, k, j) {
75778e21 307
055163ad
MS
308 if (tr->anchor_job == k ||
309 !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
310 (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type)))
311 goto next_unit;
75778e21
MS
312 }
313
055163ad
MS
314 /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
315 transaction_delete_job(tr, j, false);
316 goto rescan;
317 next_unit:;
318 }
75778e21
MS
319}
320
321static bool unit_matters_to_anchor(Unit *u, Job *j) {
322 assert(u);
323 assert(!j->transaction_prev);
324
325 /* Checks whether at least one of the jobs for this unit
326 * matters to the anchor. */
327
328 LIST_FOREACH(transaction, j, j)
329 if (j->matters_to_anchor)
330 return true;
331
332 return false;
333}
334
335static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, DBusError *e) {
336 Iterator i;
337 Unit *u;
338 int r;
339
340 assert(tr);
341 assert(j);
342 assert(!j->transaction_prev);
343
344 /* Does a recursive sweep through the ordering graph, looking
345 * for a cycle. If we find cycle we try to break it. */
346
347 /* Have we seen this before? */
348 if (j->generation == generation) {
349 Job *k, *delete;
350
351 /* If the marker is NULL we have been here already and
352 * decided the job was loop-free from here. Hence
353 * shortcut things and return right-away. */
354 if (!j->marker)
355 return 0;
356
357 /* So, the marker is not NULL and we already have been
358 * here. We have a cycle. Let's try to break it. We go
359 * backwards in our path and try to find a suitable
360 * job to remove. We use the marker to find our way
361 * back, since smart how we are we stored our way back
362 * in there. */
66870f90
ZJS
363 log_warning_unit(j->unit->id,
364 "Found ordering cycle on %s/%s",
365 j->unit->id, job_type_to_string(j->type));
75778e21
MS
366
367 delete = NULL;
368 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
369
66870f90
ZJS
370 /* logging for j not k here here to provide consistent narrative */
371 log_info_unit(j->unit->id,
372 "Walked on cycle path to %s/%s",
373 k->unit->id, job_type_to_string(k->type));
75778e21
MS
374
375 if (!delete &&
75778e21
MS
376 !unit_matters_to_anchor(k->unit, k)) {
377 /* Ok, we can drop this one, so let's
378 * do so. */
379 delete = k;
380 }
381
382 /* Check if this in fact was the beginning of
383 * the cycle */
384 if (k == j)
385 break;
386 }
387
388
389 if (delete) {
66870f90
ZJS
390 /* logging for j not k here here to provide consistent narrative */
391 log_warning_unit(j->unit->id,
392 "Breaking ordering cycle by deleting job %s/%s",
393 delete->unit->id, job_type_to_string(delete->type));
394 log_error_unit(delete->unit->id,
395 "Job %s/%s deleted to break ordering cycle starting with %s/%s",
396 delete->unit->id, job_type_to_string(delete->type),
397 j->unit->id, job_type_to_string(j->type));
398 status_printf(ANSI_HIGHLIGHT_RED_ON " SKIP " ANSI_HIGHLIGHT_OFF, true,
399 "Ordering cycle found, skipping %s", unit_description(delete->unit));
75778e21
MS
400 transaction_delete_unit(tr, delete->unit);
401 return -EAGAIN;
402 }
403
404 log_error("Unable to break cycle");
405
66870f90
ZJS
406 dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
407 "Transaction order is cyclic. See system logs for details.");
75778e21
MS
408 return -ENOEXEC;
409 }
410
411 /* Make the marker point to where we come from, so that we can
412 * find our way backwards if we want to break a cycle. We use
413 * a special marker for the beginning: we point to
414 * ourselves. */
415 j->marker = from ? from : j;
416 j->generation = generation;
417
c5315881 418 /* We assume that the dependencies are bidirectional, and
75778e21
MS
419 * hence can ignore UNIT_AFTER */
420 SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
421 Job *o;
422
423 /* Is there a job for this unit? */
424 o = hashmap_get(tr->jobs, u);
425 if (!o) {
426 /* Ok, there is no job for this in the
427 * transaction, but maybe there is already one
428 * running? */
429 o = u->job;
430 if (!o)
431 continue;
432 }
433
434 r = transaction_verify_order_one(tr, o, j, generation, e);
435 if (r < 0)
436 return r;
437 }
438
439 /* Ok, let's backtrack, and remember that this entry is not on
440 * our path anymore. */
441 j->marker = NULL;
442
443 return 0;
444}
445
446static int transaction_verify_order(Transaction *tr, unsigned *generation, DBusError *e) {
447 Job *j;
448 int r;
449 Iterator i;
450 unsigned g;
451
452 assert(tr);
453 assert(generation);
454
455 /* Check if the ordering graph is cyclic. If it is, try to fix
456 * that up by dropping one of the jobs. */
457
458 g = (*generation)++;
459
460 HASHMAP_FOREACH(j, tr->jobs, i)
461 if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0)
462 return r;
463
464 return 0;
465}
466
467static void transaction_collect_garbage(Transaction *tr) {
055163ad
MS
468 Iterator i;
469 Job *j;
75778e21
MS
470
471 assert(tr);
472
473 /* Drop jobs that are not required by any other job */
474
055163ad
MS
475rescan:
476 HASHMAP_FOREACH(j, tr->jobs, i) {
477 if (tr->anchor_job == j || j->object_list) {
478 /* log_debug("Keeping job %s/%s because of %s/%s", */
479 /* j->unit->id, job_type_to_string(j->type), */
480 /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
481 /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
482 continue;
75778e21
MS
483 }
484
055163ad
MS
485 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
486 transaction_delete_job(tr, j, true);
487 goto rescan;
488 }
75778e21
MS
489}
490
491static int transaction_is_destructive(Transaction *tr, DBusError *e) {
492 Iterator i;
493 Job *j;
494
495 assert(tr);
496
497 /* Checks whether applying this transaction means that
498 * existing jobs would be replaced */
499
500 HASHMAP_FOREACH(j, tr->jobs, i) {
501
502 /* Assume merged */
503 assert(!j->transaction_prev);
504 assert(!j->transaction_next);
505
506 if (j->unit->job &&
75778e21
MS
507 !job_type_is_superset(j->type, j->unit->job->type)) {
508
509 dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
510 return -EEXIST;
511 }
512 }
513
514 return 0;
515}
516
517static void transaction_minimize_impact(Transaction *tr) {
055163ad
MS
518 Job *j;
519 Iterator i;
520
75778e21
MS
521 assert(tr);
522
523 /* Drops all unnecessary jobs that reverse already active jobs
524 * or that stop a running service. */
525
055163ad
MS
526rescan:
527 HASHMAP_FOREACH(j, tr->jobs, i) {
528 LIST_FOREACH(transaction, j, j) {
529 bool stops_running_service, changes_existing_job;
75778e21 530
055163ad
MS
531 /* If it matters, we shouldn't drop it */
532 if (j->matters_to_anchor)
533 continue;
75778e21 534
055163ad
MS
535 /* Would this stop a running service?
536 * Would this change an existing job?
537 * If so, let's drop this entry */
75778e21 538
055163ad
MS
539 stops_running_service =
540 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
75778e21 541
055163ad
MS
542 changes_existing_job =
543 j->unit->job &&
544 job_type_is_conflicting(j->type, j->unit->job->type);
75778e21 545
055163ad
MS
546 if (!stops_running_service && !changes_existing_job)
547 continue;
75778e21 548
055163ad 549 if (stops_running_service)
66870f90
ZJS
550 log_debug_unit(j->unit->id,
551 "%s/%s would stop a running service.",
552 j->unit->id, job_type_to_string(j->type));
75778e21 553
055163ad 554 if (changes_existing_job)
66870f90
ZJS
555 log_debug_unit(j->unit->id,
556 "%s/%s would change existing job.",
557 j->unit->id, job_type_to_string(j->type));
75778e21 558
055163ad 559 /* Ok, let's get rid of this */
66870f90
ZJS
560 log_debug_unit(j->unit->id,
561 "Deleting %s/%s to minimize impact.",
562 j->unit->id, job_type_to_string(j->type));
75778e21 563
055163ad
MS
564 transaction_delete_job(tr, j, true);
565 goto rescan;
75778e21 566 }
055163ad 567 }
75778e21
MS
568}
569
570static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
571 Iterator i;
572 Job *j;
573 int r;
574
575 /* Moves the transaction jobs to the set of active jobs */
576
577 if (mode == JOB_ISOLATE) {
578
579 /* When isolating first kill all installed jobs which
580 * aren't part of the new transaction */
75778e21
MS
581 HASHMAP_FOREACH(j, m->jobs, i) {
582 assert(j->installed);
583
584 if (hashmap_get(tr->jobs, j->unit))
585 continue;
586
5273510e
MS
587 /* Not invalidating recursively. Avoids triggering
588 * OnFailure= actions of dependent jobs. Also avoids
589 * invalidating our iterator. */
590 job_finish_and_invalidate(j, JOB_CANCELED, false);
75778e21
MS
591 }
592 }
593
594 HASHMAP_FOREACH(j, tr->jobs, i) {
595 /* Assume merged */
596 assert(!j->transaction_prev);
597 assert(!j->transaction_next);
598
75778e21
MS
599 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
600 if (r < 0)
601 goto rollback;
602 }
603
604 while ((j = hashmap_steal_first(tr->jobs))) {
656bbffc
MS
605 Job *installed_job;
606
75778e21
MS
607 /* Clean the job dependencies */
608 transaction_unlink_job(tr, j, false);
609
656bbffc
MS
610 installed_job = job_install(j);
611 if (installed_job != j) {
612 /* j has been merged into a previously installed job */
613 if (tr->anchor_job == j)
614 tr->anchor_job = installed_job;
615 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
616 job_free(j);
617 j = installed_job;
618 }
05d576f1 619
75778e21
MS
620 job_add_to_run_queue(j);
621 job_add_to_dbus_queue(j);
622 job_start_timer(j);
c65eb836 623 job_shutdown_magic(j);
75778e21
MS
624 }
625
75778e21
MS
626 return 0;
627
628rollback:
629
d6a093d0 630 HASHMAP_FOREACH(j, tr->jobs, i)
75778e21 631 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
75778e21
MS
632
633 return r;
634}
635
636int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
4e7bd268
MS
637 Iterator i;
638 Job *j;
75778e21
MS
639 int r;
640 unsigned generation = 1;
641
642 assert(tr);
643
644 /* This applies the changes recorded in tr->jobs to
645 * the actual list of jobs, if possible. */
646
4e7bd268
MS
647 /* Reset the generation counter of all installed jobs. The detection of cycles
648 * looks at installed jobs. If they had a non-zero generation from some previous
649 * walk of the graph, the algorithm would break. */
650 HASHMAP_FOREACH(j, m->jobs, i)
651 j->generation = 0;
652
75778e21 653 /* First step: figure out which jobs matter */
0d9989aa 654 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
75778e21
MS
655
656 /* Second step: Try not to stop any running services if
657 * we don't have to. Don't try to reverse running
658 * jobs if we don't have to. */
659 if (mode == JOB_FAIL)
660 transaction_minimize_impact(tr);
661
662 /* Third step: Drop redundant jobs */
663 transaction_drop_redundant(tr);
664
665 for (;;) {
666 /* Fourth step: Let's remove unneeded jobs that might
667 * be lurking. */
668 if (mode != JOB_ISOLATE)
669 transaction_collect_garbage(tr);
670
671 /* Fifth step: verify order makes sense and correct
672 * cycles if necessary and possible */
673 r = transaction_verify_order(tr, &generation, e);
674 if (r >= 0)
675 break;
676
677 if (r != -EAGAIN) {
678 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
679 return r;
680 }
681
682 /* Let's see if the resulting transaction ordering
683 * graph is still cyclic... */
684 }
685
686 for (;;) {
687 /* Sixth step: let's drop unmergeable entries if
688 * necessary and possible, merge entries we can
689 * merge */
690 r = transaction_merge_jobs(tr, e);
691 if (r >= 0)
692 break;
693
694 if (r != -EAGAIN) {
695 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
696 return r;
697 }
698
699 /* Seventh step: an entry got dropped, let's garbage
700 * collect its dependencies. */
701 if (mode != JOB_ISOLATE)
702 transaction_collect_garbage(tr);
703
704 /* Let's see if the resulting transaction still has
705 * unmergeable entries ... */
706 }
707
708 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
709 transaction_drop_redundant(tr);
710
711 /* Ninth step: check whether we can actually apply this */
712 if (mode == JOB_FAIL) {
713 r = transaction_is_destructive(tr, e);
714 if (r < 0) {
715 log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
716 return r;
717 }
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));
75778e21 728
f2b68789
LP
729 if (!hashmap_isempty(m->jobs)) {
730 /* Are there any jobs now? Then make sure we have the
731 * idle pipe around. We don't really care too much
732 * whether this works or not, as the idle pipe is a
733 * feature for cosmetics, not actually useful for
734 * anything beyond that. */
735
736 if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0)
737 pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
738 }
739
75778e21
MS
740 return 0;
741}
742
743static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
744 Job *j, *f;
745
746 assert(tr);
747 assert(unit);
748
749 /* Looks for an existing prospective job and returns that. If
750 * it doesn't exist it is created and added to the prospective
751 * jobs list. */
752
753 f = hashmap_get(tr->jobs, unit);
754
755 LIST_FOREACH(transaction, j, f) {
756 assert(j->unit == unit);
757
758 if (j->type == type) {
759 if (is_new)
760 *is_new = false;
761 return j;
762 }
763 }
764
3c956cfe
MS
765 j = job_new(unit, type);
766 if (!j)
767 return NULL;
75778e21
MS
768
769 j->generation = 0;
770 j->marker = NULL;
771 j->matters_to_anchor = false;
772 j->override = override;
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
1109Transaction *transaction_new(void) {
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 return tr;
1123}
1124
1125void transaction_free(Transaction *tr) {
1126 assert(hashmap_isempty(tr->jobs));
1127 hashmap_free(tr->jobs);
1128 free(tr);
1129}