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