LCOV - code coverage report
Current view: top level - core - transaction.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 289 470 61.5 %
Date: 2015-07-29 18:47:03 Functions: 19 23 82.6 %

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

Generated by: LCOV version 1.11