]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/transaction.c
unit: rename BindTo= to BindsTo=
[thirdparty/systemd.git] / src / core / transaction.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include "transaction.h"
26 #include "bus-errors.h"
27
28 static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
29
30 static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) {
31 assert(tr);
32 assert(j);
33
34 /* Deletes one job from the transaction */
35
36 transaction_unlink_job(tr, j, delete_dependencies);
37
38 job_free(j);
39 }
40
41 static void transaction_delete_unit(Transaction *tr, Unit *u) {
42 Job *j;
43
44 /* Deletes all jobs associated with a certain unit from the
45 * transaction */
46
47 while ((j = hashmap_get(tr->jobs, u)))
48 transaction_delete_job(tr, j, true);
49 }
50
51 void transaction_abort(Transaction *tr) {
52 Job *j;
53
54 assert(tr);
55
56 while ((j = hashmap_first(tr->jobs)))
57 transaction_delete_job(tr, j, false);
58
59 assert(hashmap_isempty(tr->jobs));
60 }
61
62 static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
63 JobDependency *l;
64
65 /* A recursive sweep through the graph that marks all units
66 * that matter to the anchor job, i.e. are directly or
67 * indirectly a dependency of the anchor job via paths that
68 * are fully marked as mattering. */
69
70 j->matters_to_anchor = true;
71 j->generation = generation;
72
73 LIST_FOREACH(subject, l, j->subject_list) {
74
75 /* This link does not matter */
76 if (!l->matters)
77 continue;
78
79 /* This unit has already been marked */
80 if (l->object->generation == generation)
81 continue;
82
83 transaction_find_jobs_that_matter_to_anchor(l->object, generation);
84 }
85 }
86
87 static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) {
88 JobDependency *l, *last;
89
90 assert(j);
91 assert(other);
92 assert(j->unit == other->unit);
93 assert(!j->installed);
94
95 /* Merges 'other' into 'j' and then deletes 'other'. */
96
97 j->type = t;
98 j->state = JOB_WAITING;
99 j->override = j->override || other->override;
100
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
141 static 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
156 static 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
224 static 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) {
239 if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
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
265 /* Merge all transaction jobs for j->unit */
266 LIST_FOREACH(transaction, k, j->transaction_next)
267 assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
268
269 while ((k = j->transaction_next)) {
270 if (tr->anchor_job == k) {
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
277 assert(!j->transaction_next);
278 assert(!j->transaction_prev);
279 }
280
281 return 0;
282 }
283
284 static void transaction_drop_redundant(Transaction *tr) {
285 Job *j;
286 Iterator i;
287
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. */
291
292 assert(tr);
293
294 rescan:
295 HASHMAP_FOREACH(j, tr->jobs, i) {
296 Job *k;
297
298 LIST_FOREACH(transaction, k, j) {
299
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;
304 }
305
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 }
311 }
312
313 static 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
327 static 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 &&
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
423 static 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
444 static void transaction_collect_garbage(Transaction *tr) {
445 Iterator i;
446 Job *j;
447
448 assert(tr);
449
450 /* Drop jobs that are not required by any other job */
451
452 rescan:
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;
460 }
461
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 }
466 }
467
468 static 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 &&
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
494 static void transaction_minimize_impact(Transaction *tr) {
495 Job *j;
496 Iterator i;
497
498 assert(tr);
499
500 /* Drops all unnecessary jobs that reverse already active jobs
501 * or that stop a running service. */
502
503 rescan:
504 HASHMAP_FOREACH(j, tr->jobs, i) {
505 LIST_FOREACH(transaction, j, j) {
506 bool stops_running_service, changes_existing_job;
507
508 /* If it matters, we shouldn't drop it */
509 if (j->matters_to_anchor)
510 continue;
511
512 /* Would this stop a running service?
513 * Would this change an existing job?
514 * If so, let's drop this entry */
515
516 stops_running_service =
517 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
518
519 changes_existing_job =
520 j->unit->job &&
521 job_type_is_conflicting(j->type, j->unit->job->type);
522
523 if (!stops_running_service && !changes_existing_job)
524 continue;
525
526 if (stops_running_service)
527 log_debug("%s/%s would stop a running service.", j->unit->id, job_type_to_string(j->type));
528
529 if (changes_existing_job)
530 log_debug("%s/%s would change existing job.", j->unit->id, job_type_to_string(j->type));
531
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));
534
535 transaction_delete_job(tr, j, true);
536 goto rescan;
537 }
538 }
539 }
540
541 static 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 */
552 HASHMAP_FOREACH(j, m->jobs, i) {
553 assert(j->installed);
554
555 if (hashmap_get(tr->jobs, j->unit))
556 continue;
557
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);
562 }
563 }
564
565 HASHMAP_FOREACH(j, tr->jobs, i) {
566 /* Assume merged */
567 assert(!j->transaction_prev);
568 assert(!j->transaction_next);
569
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))) {
576 Job *installed_job;
577
578 /* Clean the job dependencies */
579 transaction_unlink_job(tr, j, false);
580
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 }
590
591 job_add_to_run_queue(j);
592 job_add_to_dbus_queue(j);
593 job_start_timer(j);
594 }
595
596 return 0;
597
598 rollback:
599
600 HASHMAP_FOREACH(j, tr->jobs, i)
601 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
602
603 return r;
604 }
605
606 int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e) {
607 Iterator i;
608 Job *j;
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
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
623 /* First step: figure out which jobs matter */
624 transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
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));
698
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
710 return 0;
711 }
712
713 static 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
735 j = job_new(unit, type);
736 if (!j)
737 return NULL;
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
760 static 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)
777 job_dependency_free(j->subject_list);
778
779 while (j->object_list) {
780 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
781
782 job_dependency_free(j->object_list);
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
793 int 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,
803 DBusError *e) {
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);
812 assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
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);
839 return -EADDRNOTAVAIL;
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. */
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. */
860 assert(!tr->anchor_job);
861 tr->anchor_job = ret;
862 }
863
864 if (is_new && !ignore_requirements && type != JOB_NOP) {
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) {
871 r = transaction_add_job_and_dependencies(tr, type, dep, ret, false, override, false, false, ignore_order, e);
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. */
884 if (type == JOB_START || type == JOB_RESTART) {
885 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
886 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
887 if (r < 0) {
888 if (r != -EBADR)
889 goto fail;
890
891 if (e)
892 dbus_error_free(e);
893 }
894 }
895
896 SET_FOREACH(dep, ret->unit->dependencies[UNIT_BINDS_TO], i) {
897 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, override, false, false, ignore_order, e);
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) {
908 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, !override, override, false, false, ignore_order, e);
909 if (r < 0) {
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));
912
913 if (e)
914 dbus_error_free(e);
915 }
916 }
917
918 SET_FOREACH(dep, ret->unit->dependencies[UNIT_WANTS], i) {
919 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, false, ignore_order, e);
920 if (r < 0) {
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));
923
924 if (e)
925 dbus_error_free(e);
926 }
927 }
928
929 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE], i) {
930 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e);
931 if (r < 0) {
932 if (r != -EBADR)
933 goto fail;
934
935 if (e)
936 dbus_error_free(e);
937 }
938 }
939
940 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUISITE_OVERRIDABLE], i) {
941 r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e);
942 if (r < 0) {
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));
945
946 if (e)
947 dbus_error_free(e);
948 }
949 }
950
951 SET_FOREACH(dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
952 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e);
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) {
963 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e);
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
974 if (type == JOB_STOP || type == JOB_RESTART) {
975
976 SET_FOREACH(dep, ret->unit->dependencies[UNIT_REQUIRED_BY], i) {
977 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
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) {
988 r = transaction_add_job_and_dependencies(tr, type, dep, ret, true, override, false, false, ignore_order, e);
989 if (r < 0) {
990 if (r != -EBADR)
991 goto fail;
992
993 if (e)
994 dbus_error_free(e);
995 }
996 }
997 }
998
999 if (type == JOB_RELOAD) {
1000
1001 SET_FOREACH(dep, ret->unit->dependencies[UNIT_PROPAGATES_RELOAD_TO], i) {
1002 r = transaction_add_job_and_dependencies(tr, JOB_RELOAD, dep, ret, false, override, false, false, ignore_order, e);
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
1012 /* JOB_VERIFY_STARTED require no dependency handling */
1013 }
1014
1015 return 0;
1016
1017 fail:
1018 return r;
1019 }
1020
1021 int 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
1047 r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, true, false, false, false, false, NULL);
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
1055 Transaction *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
1071 void transaction_free(Transaction *tr) {
1072 assert(hashmap_isempty(tr->jobs));
1073 hashmap_free(tr->jobs);
1074 free(tr);
1075 }