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