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