LCOV - code coverage report
Current view: top level - core - transaction.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 327 500 65.4 %
Date: 2019-08-22 15:41:25 Functions: 20 25 80.0 %

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

Generated by: LCOV version 1.14