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