]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/transaction.c
build-sys: create ntp-units.d dropin directories on install
[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
189 log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
190 log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
191
192 if (j->type == JOB_STOP) {
193
194 if (job_is_conflicted_by(j))
195 d = k;
196 else
197 d = j;
198
199 } else if (k->type == JOB_STOP) {
200
201 if (job_is_conflicted_by(k))
202 d = j;
203 else
204 d = k;
205 } else
206 d = j;
207
208 } else if (!j->matters_to_anchor)
209 d = j;
210 else if (!k->matters_to_anchor)
211 d = k;
212 else
213 return -ENOEXEC;
214
215 /* Ok, we can drop one, so let's do so. */
216 log_debug("Fixing conflicting jobs by deleting job %s/%s", d->unit->id, job_type_to_string(d->type));
217 transaction_delete_job(tr, d, true);
218 return 0;
219 }
220
221 return -EINVAL;
222}
223
224static int transaction_merge_jobs(Transaction *tr, DBusError *e) {
225 Job *j;
226 Iterator i;
227 int r;
228
229 assert(tr);
230
231 /* First step, check whether any of the jobs for one specific
232 * task conflict. If so, try to drop one of them. */
233 HASHMAP_FOREACH(j, tr->jobs, i) {
234 JobType t;
235 Job *k;
236
237 t = j->type;
238 LIST_FOREACH(transaction, k, j->transaction_next) {
e0209d83 239 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
75778e21
MS
240 continue;
241
242 /* OK, we could not merge all jobs for this
243 * action. Let's see if we can get rid of one
244 * of them */
245
246 r = delete_one_unmergeable_job(tr, j);
247 if (r >= 0)
248 /* Ok, we managed to drop one, now
249 * let's ask our callers to call us
250 * again after garbage collecting */
251 return -EAGAIN;
252
253 /* We couldn't merge anything. Failure */
254 dbus_set_error(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING, "Transaction contains conflicting jobs '%s' and '%s' for %s. Probably contradicting requirement dependencies configured.",
255 job_type_to_string(t), job_type_to_string(k->type), k->unit->id);
256 return r;
257 }
258 }
259
260 /* Second step, merge the jobs. */
261 HASHMAP_FOREACH(j, tr->jobs, i) {
262 JobType t = j->type;
263 Job *k;
264
e0209d83 265 /* Merge all transaction jobs for j->unit */
75778e21 266 LIST_FOREACH(transaction, k, j->transaction_next)
e0209d83 267 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
75778e21 268
75778e21 269 while ((k = j->transaction_next)) {
656bbffc 270 if (tr->anchor_job == k) {
75778e21
MS
271 transaction_merge_and_delete_job(tr, k, j, t);
272 j = k;
273 } else
274 transaction_merge_and_delete_job(tr, j, k, t);
275 }
276
75778e21
MS
277 assert(!j->transaction_next);
278 assert(!j->transaction_prev);
279 }
280
281 return 0;
282}
283
284static void transaction_drop_redundant(Transaction *tr) {
055163ad
MS
285 Job *j;
286 Iterator i;
75778e21 287
055163ad
MS
288 /* Goes through the transaction and removes all jobs of the units
289 * whose jobs are all noops. If not all of a unit's jobs are
290 * redundant, they are kept. */
75778e21 291
055163ad 292 assert(tr);
75778e21 293
055163ad
MS
294rescan:
295 HASHMAP_FOREACH(j, tr->jobs, i) {
296 Job *k;
75778e21 297
055163ad 298 LIST_FOREACH(transaction, k, j) {
75778e21 299
055163ad
MS
300 if (tr->anchor_job == k ||
301 !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
302 (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type)))
303 goto next_unit;
75778e21
MS
304 }
305
055163ad
MS
306 /* log_debug("Found redundant job %s/%s, dropping.", j->unit->id, job_type_to_string(j->type)); */
307 transaction_delete_job(tr, j, false);
308 goto rescan;
309 next_unit:;
310 }
75778e21
MS
311}
312
313static bool unit_matters_to_anchor(Unit *u, Job *j) {
314 assert(u);
315 assert(!j->transaction_prev);
316
317 /* Checks whether at least one of the jobs for this unit
318 * matters to the anchor. */
319
320 LIST_FOREACH(transaction, j, j)
321 if (j->matters_to_anchor)
322 return true;
323
324 return false;
325}
326
327static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, DBusError *e) {
328 Iterator i;
329 Unit *u;
330 int r;
331
332 assert(tr);
333 assert(j);
334 assert(!j->transaction_prev);
335
336 /* Does a recursive sweep through the ordering graph, looking
337 * for a cycle. If we find cycle we try to break it. */
338
339 /* Have we seen this before? */
340 if (j->generation == generation) {
341 Job *k, *delete;
342
343 /* If the marker is NULL we have been here already and
344 * decided the job was loop-free from here. Hence
345 * shortcut things and return right-away. */
346 if (!j->marker)
347 return 0;
348
349 /* So, the marker is not NULL and we already have been
350 * here. We have a cycle. Let's try to break it. We go
351 * backwards in our path and try to find a suitable
352 * job to remove. We use the marker to find our way
353 * back, since smart how we are we stored our way back
354 * in there. */
355 log_warning("Found ordering cycle on %s/%s", j->unit->id, job_type_to_string(j->type));
356
357 delete = NULL;
358 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
359
360 log_info("Walked on cycle path to %s/%s", k->unit->id, job_type_to_string(k->type));
361
362 if (!delete &&
75778e21
MS
363 !unit_matters_to_anchor(k->unit, k)) {
364 /* Ok, we can drop this one, so let's
365 * do so. */
366 delete = k;
367 }
368
369 /* Check if this in fact was the beginning of
370 * the cycle */
371 if (k == j)
372 break;
373 }
374
375
376 if (delete) {
377 log_warning("Breaking ordering cycle by deleting job %s/%s", delete->unit->id, job_type_to_string(delete->type));
378 transaction_delete_unit(tr, delete->unit);
379 return -EAGAIN;
380 }
381
382 log_error("Unable to break cycle");
383
384 dbus_set_error(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC, "Transaction order is cyclic. See system logs for details.");
385 return -ENOEXEC;
386 }
387
388 /* Make the marker point to where we come from, so that we can
389 * find our way backwards if we want to break a cycle. We use
390 * a special marker for the beginning: we point to
391 * ourselves. */
392 j->marker = from ? from : j;
393 j->generation = generation;
394
395 /* We assume that the the dependencies are bidirectional, and
396 * hence can ignore UNIT_AFTER */
397 SET_FOREACH(u, j->unit->dependencies[UNIT_BEFORE], i) {
398 Job *o;
399
400 /* Is there a job for this unit? */
401 o = hashmap_get(tr->jobs, u);
402 if (!o) {
403 /* Ok, there is no job for this in the
404 * transaction, but maybe there is already one
405 * running? */
406 o = u->job;
407 if (!o)
408 continue;
409 }
410
411 r = transaction_verify_order_one(tr, o, j, generation, e);
412 if (r < 0)
413 return r;
414 }
415
416 /* Ok, let's backtrack, and remember that this entry is not on
417 * our path anymore. */
418 j->marker = NULL;
419
420 return 0;
421}
422
423static int transaction_verify_order(Transaction *tr, unsigned *generation, DBusError *e) {
424 Job *j;
425 int r;
426 Iterator i;
427 unsigned g;
428
429 assert(tr);
430 assert(generation);
431
432 /* Check if the ordering graph is cyclic. If it is, try to fix
433 * that up by dropping one of the jobs. */
434
435 g = (*generation)++;
436
437 HASHMAP_FOREACH(j, tr->jobs, i)
438 if ((r = transaction_verify_order_one(tr, j, NULL, g, e)) < 0)
439 return r;
440
441 return 0;
442}
443
444static void transaction_collect_garbage(Transaction *tr) {
055163ad
MS
445 Iterator i;
446 Job *j;
75778e21
MS
447
448 assert(tr);
449
450 /* Drop jobs that are not required by any other job */
451
055163ad
MS
452rescan:
453 HASHMAP_FOREACH(j, tr->jobs, i) {
454 if (tr->anchor_job == j || j->object_list) {
455 /* log_debug("Keeping job %s/%s because of %s/%s", */
456 /* j->unit->id, job_type_to_string(j->type), */
457 /* j->object_list->subject ? j->object_list->subject->unit->id : "root", */
458 /* j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); */
459 continue;
75778e21
MS
460 }
461
055163ad
MS
462 /* log_debug("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); */
463 transaction_delete_job(tr, j, true);
464 goto rescan;
465 }
75778e21
MS
466}
467
468static int transaction_is_destructive(Transaction *tr, DBusError *e) {
469 Iterator i;
470 Job *j;
471
472 assert(tr);
473
474 /* Checks whether applying this transaction means that
475 * existing jobs would be replaced */
476
477 HASHMAP_FOREACH(j, tr->jobs, i) {
478
479 /* Assume merged */
480 assert(!j->transaction_prev);
481 assert(!j->transaction_next);
482
483 if (j->unit->job &&
75778e21
MS
484 !job_type_is_superset(j->type, j->unit->job->type)) {
485
486 dbus_set_error(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE, "Transaction is destructive.");
487 return -EEXIST;
488 }
489 }
490
491 return 0;
492}
493
494static void transaction_minimize_impact(Transaction *tr) {
055163ad
MS
495 Job *j;
496 Iterator i;
497
75778e21
MS
498 assert(tr);
499
500 /* Drops all unnecessary jobs that reverse already active jobs
501 * or that stop a running service. */
502
055163ad
MS
503rescan:
504 HASHMAP_FOREACH(j, tr->jobs, i) {
505 LIST_FOREACH(transaction, j, j) {
506 bool stops_running_service, changes_existing_job;
75778e21 507
055163ad
MS
508 /* If it matters, we shouldn't drop it */
509 if (j->matters_to_anchor)
510 continue;
75778e21 511
055163ad
MS
512 /* Would this stop a running service?
513 * Would this change an existing job?
514 * If so, let's drop this entry */
75778e21 515
055163ad
MS
516 stops_running_service =
517 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
75778e21 518
055163ad
MS
519 changes_existing_job =
520 j->unit->job &&
521 job_type_is_conflicting(j->type, j->unit->job->type);
75778e21 522
055163ad
MS
523 if (!stops_running_service && !changes_existing_job)
524 continue;
75778e21 525
055163ad
MS
526 if (stops_running_service)
527 log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
75778e21 528
055163ad
MS
529 if (changes_existing_job)
530 log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
75778e21 531
055163ad
MS
532 /* Ok, let's get rid of this */
533 log_debug("Deleting %s/%s to minimize impact.", j->unit->id, job_type_to_string(j->type));
75778e21 534
055163ad
MS
535 transaction_delete_job(tr, j, true);
536 goto rescan;
75778e21 537 }
055163ad 538 }
75778e21
MS
539}
540
541static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) {
542 Iterator i;
543 Job *j;
544 int r;
545
546 /* Moves the transaction jobs to the set of active jobs */
547
548 if (mode == JOB_ISOLATE) {
549
550 /* When isolating first kill all installed jobs which
551 * aren't part of the new transaction */
75778e21
MS
552 HASHMAP_FOREACH(j, m->jobs, i) {
553 assert(j->installed);
554
555 if (hashmap_get(tr->jobs, j->unit))
556 continue;
557
5273510e
MS
558 /* Not invalidating recursively. Avoids triggering
559 * OnFailure= actions of dependent jobs. Also avoids
560 * invalidating our iterator. */
561 job_finish_and_invalidate(j, JOB_CANCELED, false);
75778e21
MS
562 }
563 }
564
565 HASHMAP_FOREACH(j, tr->jobs, i) {
566 /* Assume merged */
567 assert(!j->transaction_prev);
568 assert(!j->transaction_next);
569
75778e21
MS
570 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
571 if (r < 0)
572 goto rollback;
573 }
574
575 while ((j = hashmap_steal_first(tr->jobs))) {
656bbffc
MS
576 Job *installed_job;
577
75778e21
MS
578 /* Clean the job dependencies */
579 transaction_unlink_job(tr, j, false);
580
656bbffc
MS
581 installed_job = job_install(j);
582 if (installed_job != j) {
583 /* j has been merged into a previously installed job */
584 if (tr->anchor_job == j)
585 tr->anchor_job = installed_job;
586 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
587 job_free(j);
588 j = installed_job;
589 }
05d576f1 590
75778e21
MS
591 job_add_to_run_queue(j);
592 job_add_to_dbus_queue(j);
593 job_start_timer(j);
75778e21
MS
594 }
595
75778e21
MS
596 return 0;
597
598rollback:
599
d6a093d0 600 HASHMAP_FOREACH(j, tr->jobs, i)
75778e21 601 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
75778e21
MS
602
603 return r;
604}
605
606int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
4e7bd268
MS
607 Iterator i;
608 Job *j;
75778e21
MS
609 int r;
610 unsigned generation = 1;
611
612 assert(tr);
613
614 /* This applies the changes recorded in tr->jobs to
615 * the actual list of jobs, if possible. */
616
4e7bd268
MS
617 /* Reset the generation counter of all installed jobs. The detection of cycles
618 * looks at installed jobs. If they had a non-zero generation from some previous
619 * walk of the graph, the algorithm would break. */
620 HASHMAP_FOREACH(j, m->jobs, i)
621 j->generation = 0;
622
75778e21 623 /* First step: figure out which jobs matter */
0d9989aa 624 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
75778e21
MS
625
626 /* Second step: Try not to stop any running services if
627 * we don't have to. Don't try to reverse running
628 * jobs if we don't have to. */
629 if (mode == JOB_FAIL)
630 transaction_minimize_impact(tr);
631
632 /* Third step: Drop redundant jobs */
633 transaction_drop_redundant(tr);
634
635 for (;;) {
636 /* Fourth step: Let's remove unneeded jobs that might
637 * be lurking. */
638 if (mode != JOB_ISOLATE)
639 transaction_collect_garbage(tr);
640
641 /* Fifth step: verify order makes sense and correct
642 * cycles if necessary and possible */
643 r = transaction_verify_order(tr, &generation, e);
644 if (r >= 0)
645 break;
646
647 if (r != -EAGAIN) {
648 log_warning("Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error(e, r));
649 return r;
650 }
651
652 /* Let's see if the resulting transaction ordering
653 * graph is still cyclic... */
654 }
655
656 for (;;) {
657 /* Sixth step: let's drop unmergeable entries if
658 * necessary and possible, merge entries we can
659 * merge */
660 r = transaction_merge_jobs(tr, e);
661 if (r >= 0)
662 break;
663
664 if (r != -EAGAIN) {
665 log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
666 return r;
667 }
668
669 /* Seventh step: an entry got dropped, let's garbage
670 * collect its dependencies. */
671 if (mode != JOB_ISOLATE)
672 transaction_collect_garbage(tr);
673
674 /* Let's see if the resulting transaction still has
675 * unmergeable entries ... */
676 }
677
678 /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
679 transaction_drop_redundant(tr);
680
681 /* Ninth step: check whether we can actually apply this */
682 if (mode == JOB_FAIL) {
683 r = transaction_is_destructive(tr, e);
684 if (r < 0) {
685 log_notice("Requested transaction contradicts existing jobs: %s", bus_error(e, r));
686 return r;
687 }
688 }
689
690 /* Tenth step: apply changes */
691 r = transaction_apply(tr, m, mode);
692 if (r < 0) {
693 log_warning("Failed to apply transaction: %s", strerror(-r));
694 return r;
695 }
696
697 assert(hashmap_isempty(tr->jobs));
75778e21 698
f2b68789
LP
699 if (!hashmap_isempty(m->jobs)) {
700 /* Are there any jobs now? Then make sure we have the
701 * idle pipe around. We don't really care too much
702 * whether this works or not, as the idle pipe is a
703 * feature for cosmetics, not actually useful for
704 * anything beyond that. */
705
706 if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0)
707 pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
708 }
709
75778e21
MS
710 return 0;
711}
712
713static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool override, bool *is_new) {
714 Job *j, *f;
715
716 assert(tr);
717 assert(unit);
718
719 /* Looks for an existing prospective job and returns that. If
720 * it doesn't exist it is created and added to the prospective
721 * jobs list. */
722
723 f = hashmap_get(tr->jobs, unit);
724
725 LIST_FOREACH(transaction, j, f) {
726 assert(j->unit == unit);
727
728 if (j->type == type) {
729 if (is_new)
730 *is_new = false;
731 return j;
732 }
733 }
734
3c956cfe
MS
735 j = job_new(unit, type);
736 if (!j)
737 return NULL;
75778e21
MS
738
739 j->generation = 0;
740 j->marker = NULL;
741 j->matters_to_anchor = false;
742 j->override = override;
743
744 LIST_PREPEND(Job, transaction, f, j);
745
746 if (hashmap_replace(tr->jobs, unit, f) < 0) {
747 LIST_REMOVE(Job, transaction, f, j);
748 job_free(j);
749 return NULL;
750 }
751
752 if (is_new)
753 *is_new = true;
754
755 /* log_debug("Added job %s/%s to transaction.", unit->id, job_type_to_string(type)); */
756
757 return j;
758}
759
760static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
761 assert(tr);
762 assert(j);
763
764 if (j->transaction_prev)
765 j->transaction_prev->transaction_next = j->transaction_next;
766 else if (j->transaction_next)
767 hashmap_replace(tr->jobs, j->unit, j->transaction_next);
768 else
769 hashmap_remove_value(tr->jobs, j->unit, j);
770
771 if (j->transaction_next)
772 j->transaction_next->transaction_prev = j->transaction_prev;
773
774 j->transaction_prev = j->transaction_next = NULL;
775
776 while (j->subject_list)
e6eda1f2 777 job_dependency_free(j->subject_list);
75778e21
MS
778
779 while (j->object_list) {
780 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
781
e6eda1f2 782 job_dependency_free(j->object_list);
75778e21
MS
783
784 if (other && delete_dependencies) {
785 log_debug("Deleting job %s/%s as dependency of job %s/%s",
786 other->unit->id, job_type_to_string(other->type),
787 j->unit->id, job_type_to_string(j->type));
788 transaction_delete_job(tr, other, delete_dependencies);
789 }
790 }
791}
792
793int transaction_add_job_and_dependencies(
794 Transaction *tr,
795 JobType type,
796 Unit *unit,
797 Job *by,
798 bool matters,
799 bool override,
800 bool conflicts,
801 bool ignore_requirements,
802 bool ignore_order,
b94fbd30 803 DBusError *e) {
75778e21
MS
804 Job *ret;
805 Iterator i;
806 Unit *dep;
807 int r;
808 bool is_new;
809
810 assert(tr);
811 assert(type < _JOB_TYPE_MAX);
e0209d83 812 assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
75778e21
MS
813 assert(unit);
814
815 /* log_debug("Pulling in %s/%s from %s/%s", */
816 /* unit->id, job_type_to_string(type), */
817 /* by ? by->unit->id : "NA", */
818 /* by ? job_type_to_string(by->type) : "NA"); */
819
820 if (unit->load_state != UNIT_LOADED &&
821 unit->load_state != UNIT_ERROR &&
822 unit->load_state != UNIT_MASKED) {
823 dbus_set_error(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
824 return -EINVAL;
825 }
826
827 if (type != JOB_STOP && unit->load_state == UNIT_ERROR) {
828 dbus_set_error(e, BUS_ERROR_LOAD_FAILED,
829 "Unit %s failed to load: %s. "
830 "See system logs and 'systemctl status %s' for details.",
831 unit->id,
832 strerror(-unit->load_error),
833 unit->id);
834 return -EINVAL;
835 }
836
837 if (type != JOB_STOP && unit->load_state == UNIT_MASKED) {
838 dbus_set_error(e, BUS_ERROR_MASKED, "Unit %s is masked.", unit->id);
59e132a7 839 return -EADDRNOTAVAIL;
75778e21
MS
840 }
841
842 if (!unit_job_is_applicable(unit, type)) {
843 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);
844 return -EBADR;
845 }
846
847 /* First add the job. */
848 ret = transaction_add_one_job(tr, type, unit, override, &is_new);
849 if (!ret)
850 return -ENOMEM;
851
852 ret->ignore_order = ret->ignore_order || ignore_order;
853
854 /* Then, add a link to the job. */
e6eda1f2
MS
855 if (by) {
856 if (!job_dependency_new(by, ret, matters, conflicts))
857 return -ENOMEM;
858 } else {
859 /* If the job has no parent job, it is the anchor job. */
4483f694
MS
860 assert(!tr->anchor_job);
861 tr->anchor_job = ret;
b94fbd30 862 }
e0209d83
MS
863
864 if (is_new && !ignore_requirements && type != JOB_NOP) {
75778e21
MS
865 Set *following;
866
867 /* If we are following some other unit, make sure we
868 * add all dependencies of everybody following. */
869 if (unit_following_set(ret->unit, &following) > 0) {
870 SET_FOREACH(dep, following, i) {
b94fbd30 871 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
75778e21
MS
872 if (r < 0) {
873 log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
874
875 if (e)
876 dbus_error_free(e);
877 }
878 }
879
880 set_free(following);
881 }
882
883 /* Finally, recursively add in all dependencies. */
e0209d83 884 if (type == JOB_START || type == JOB_RESTART) {
75778e21 885 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
b94fbd30 886 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
887 if (r < 0) {
888 if (r != -EBADR)
889 goto fail;
890
891 if (e)
892 dbus_error_free(e);
893 }
894 }
895
7f2cddae 896 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
b94fbd30 897 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
898 if (r < 0) {
899 if (r != -EBADR)
900 goto fail;
901
902 if (e)
903 dbus_error_free(e);
904 }
905 }
906
907 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES_OVERRIDABLE], i) {
b94fbd30 908 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
75778e21 909 if (r < 0) {
59e132a7
LP
910 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
911 "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
75778e21
MS
912
913 if (e)
914 dbus_error_free(e);
915 }
916 }
917
918 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
b94fbd30 919 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
75778e21 920 if (r < 0) {
59e132a7
LP
921 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
922 "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
75778e21
MS
923
924 if (e)
925 dbus_error_free(e);
926 }
927 }
928
929 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
b94fbd30 930 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, 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_REQUISITE_OVERRIDABLE], i) {
b94fbd30 941 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
75778e21 942 if (r < 0) {
59e132a7
LP
943 log_full(r == -EADDRNOTAVAIL ? LOG_DEBUG : LOG_WARNING,
944 "Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
75778e21
MS
945
946 if (e)
947 dbus_error_free(e);
948 }
949 }
950
951 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
b94fbd30 952 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
75778e21
MS
953 if (r < 0) {
954 if (r != -EBADR)
955 goto fail;
956
957 if (e)
958 dbus_error_free(e);
959 }
960 }
961
962 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
b94fbd30 963 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
75778e21
MS
964 if (r < 0) {
965 log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
966
967 if (e)
968 dbus_error_free(e);
969 }
970 }
971
972 }
973
e0209d83 974 if (type == JOB_STOP || type == JOB_RESTART) {
75778e21
MS
975
976 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
b94fbd30 977 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
75778e21
MS
978 if (r < 0) {
979 if (r != -EBADR)
980 goto fail;
981
982 if (e)
983 dbus_error_free(e);
984 }
985 }
986
987 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BOUND_BY], i) {
b94fbd30 988 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, 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
e0209d83 999 if (type == JOB_RELOAD) {
75778e21 1000
7f2cddae 1001 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
b94fbd30 1002 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
75778e21
MS
1003 if (r < 0) {
1004 log_warning("Cannot add dependency reload job for unit %s, ignoring: %s", dep->id, bus_error(e, r));
1005
1006 if (e)
1007 dbus_error_free(e);
1008 }
1009 }
1010 }
1011
e0209d83 1012 /* JOB_VERIFY_STARTED require no dependency handling */
75778e21
MS
1013 }
1014
75778e21
MS
1015 return 0;
1016
1017fail:
1018 return r;
1019}
1020
1021int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
1022 Iterator i;
1023 Unit *u;
1024 char *k;
1025 int r;
1026
1027 assert(tr);
1028 assert(m);
1029
1030 HASHMAP_FOREACH_KEY(u, k, m->units, i) {
1031
1032 /* ignore aliases */
1033 if (u->id != k)
1034 continue;
1035
1036 if (u->ignore_on_isolate)
1037 continue;
1038
1039 /* No need to stop inactive jobs */
1040 if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
1041 continue;
1042
1043 /* Is there already something listed for this? */
1044 if (hashmap_get(tr->jobs, u))
1045 continue;
1046
4483f694 1047 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
75778e21
MS
1048 if (r < 0)
1049 log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->id, strerror(-r));
1050 }
1051
1052 return 0;
1053}
1054
1055Transaction *transaction_new(void) {
1056 Transaction *tr;
1057
1058 tr = new0(Transaction, 1);
1059 if (!tr)
1060 return NULL;
1061
1062 tr->jobs = hashmap_new(trivial_hash_func, trivial_compare_func);
1063 if (!tr->jobs) {
1064 free(tr);
1065 return NULL;
1066 }
1067
1068 return tr;
1069}
1070
1071void transaction_free(Transaction *tr) {
1072 assert(hashmap_isempty(tr->jobs));
1073 hashmap_free(tr->jobs);
1074 free(tr);
1075}