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