LCOV - code coverage report
Current view: top level - core - transaction.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 327 500 65.4 %
Date: 2019-08-23 13:36:53 Functions: 20 25 80.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 282 572 49.3 %

           Branch data     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                 :        456 : static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) {
      17         [ -  + ]:        456 :         assert(tr);
      18         [ -  + ]:        456 :         assert(j);
      19                 :            : 
      20                 :            :         /* Deletes one job from the transaction */
      21                 :            : 
      22                 :        456 :         transaction_unlink_job(tr, j, delete_dependencies);
      23                 :            : 
      24                 :        456 :         job_free(j);
      25                 :        456 : }
      26                 :            : 
      27                 :          8 : 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         [ +  + ]:         16 :         while ((j = hashmap_get(tr->jobs, u)))
      34                 :          8 :                 transaction_delete_job(tr, j, true);
      35                 :          8 : }
      36                 :            : 
      37                 :         16 : void transaction_abort(Transaction *tr) {
      38                 :            :         Job *j;
      39                 :            : 
      40         [ -  + ]:         16 :         assert(tr);
      41                 :            : 
      42         [ +  + ]:        108 :         while ((j = hashmap_first(tr->jobs)))
      43                 :         92 :                 transaction_delete_job(tr, j, false);
      44                 :            : 
      45         [ -  + ]:         16 :         assert(hashmap_isempty(tr->jobs));
      46                 :         16 : }
      47                 :            : 
      48                 :        504 : 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                 :        504 :         j->matters_to_anchor = true;
      57                 :        504 :         j->generation = generation;
      58                 :            : 
      59         [ +  + ]:       1340 :         LIST_FOREACH(subject, l, j->subject_list) {
      60                 :            : 
      61                 :            :                 /* This link does not matter */
      62         [ +  + ]:        836 :                 if (!l->matters)
      63                 :        200 :                         continue;
      64                 :            : 
      65                 :            :                 /* This unit has already been marked */
      66         [ +  + ]:        636 :                 if (l->object->generation == generation)
      67                 :        204 :                         continue;
      68                 :            : 
      69                 :        432 :                 transaction_find_jobs_that_matter_to_anchor(l->object, generation);
      70                 :            :         }
      71                 :        504 : }
      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                 :         64 : static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) {
     220                 :            :         Job *j;
     221                 :            :         Iterator i;
     222                 :            :         int r;
     223                 :            : 
     224         [ -  + ]:         64 :         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         [ +  + ]:        364 :         HASHMAP_FOREACH(j, tr->jobs, i) {
     229                 :            :                 JobType t;
     230                 :            :                 Job *k;
     231                 :            : 
     232                 :        300 :                 t = j->type;
     233         [ -  + ]:        300 :                 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         [ +  + ]:        364 :         HASHMAP_FOREACH(j, tr->jobs, i) {
     260                 :        300 :                 JobType t = j->type;
     261                 :            :                 Job *k;
     262                 :            : 
     263                 :            :                 /* Merge all transaction jobs for j->unit */
     264         [ -  + ]:        300 :                 LIST_FOREACH(transaction, k, j->transaction_next)
     265         [ #  # ]:          0 :                         assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
     266                 :            : 
     267         [ -  + ]:        300 :                 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         [ -  + ]:        300 :                 assert(!j->transaction_next);
     276         [ -  + ]:        300 :                 assert(!j->transaction_prev);
     277                 :            :         }
     278                 :            : 
     279                 :         64 :         return 0;
     280                 :            : }
     281                 :            : 
     282                 :        136 : 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         [ -  + ]:        136 :         assert(tr);
     289                 :            : 
     290                 :            :         do {
     291                 :            :                 Iterator i;
     292                 :            :                 Job *j;
     293                 :            : 
     294                 :        472 :                 again = false;
     295                 :            : 
     296         [ +  + ]:       2239 :                 HASHMAP_FOREACH(j, tr->jobs, i) {
     297                 :       2103 :                         bool keep = false;
     298                 :            :                         Job *k;
     299                 :            : 
     300         [ +  + ]:       2439 :                         LIST_FOREACH(transaction, k, j)
     301         [ +  + ]:       2103 :                                 if (tr->anchor_job == k ||
     302         [ +  + ]:       1796 :                                     !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
     303   [ +  +  +  + ]:        372 :                                     (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) {
     304                 :       1767 :                                         keep = true;
     305                 :       1767 :                                         break;
     306                 :            :                                 }
     307                 :            : 
     308         [ +  + ]:       2103 :                         if (!keep) {
     309         [ +  + ]:        336 :                                 log_trace("Found redundant job %s/%s, dropping from transaction.",
     310                 :            :                                           j->unit->id, job_type_to_string(j->type));
     311                 :        336 :                                 transaction_delete_job(tr, j, false);
     312                 :        336 :                                 again = true;
     313                 :        336 :                                 break;
     314                 :            :                         }
     315                 :            :                 }
     316         [ +  + ]:        472 :         } while (again);
     317                 :        136 : }
     318                 :            : 
     319                 :         34 : _pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) {
     320         [ -  + ]:         34 :         assert(u);
     321         [ -  + ]:         34 :         assert(!j->transaction_prev);
     322                 :            : 
     323                 :            :         /* Checks whether at least one of the jobs for this unit
     324                 :            :          * matters to the anchor. */
     325                 :            : 
     326         [ +  + ]:         42 :         LIST_FOREACH(transaction, j, j)
     327         [ +  + ]:         34 :                 if (j->matters_to_anchor)
     328                 :         26 :                         return true;
     329                 :            : 
     330                 :          8 :         return false;
     331                 :            : }
     332                 :            : 
     333                 :         16 : static char* merge_unit_ids(const char* unit_log_field, char **pairs) {
     334                 :         16 :         char **unit_id, **job_type, *ans = NULL;
     335                 :         16 :         size_t alloc = 0, size = 0, next;
     336                 :            : 
     337   [ +  -  +  +  :         60 :         STRV_FOREACH_PAIR(unit_id, job_type, pairs) {
                   +  - ]
     338                 :         44 :                 next = strlen(unit_log_field) + strlen(*unit_id);
     339         [ -  + ]:         44 :                 if (!GREEDY_REALLOC(ans, alloc, size + next + 1)) {
     340                 :          0 :                         return mfree(ans);
     341                 :            :                 }
     342                 :            : 
     343                 :         44 :                 sprintf(ans + size, "%s%s", unit_log_field, *unit_id);
     344         [ +  - ]:         44 :                 if (*(unit_id+1))
     345                 :         44 :                         ans[size + next] =  '\n';
     346                 :         44 :                 size += next + 1;
     347                 :            :         }
     348                 :            : 
     349                 :         16 :         return ans;
     350                 :            : }
     351                 :            : 
     352                 :        621 : 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         [ -  + ]:        621 :         assert(tr);
     364         [ -  + ]:        621 :         assert(j);
     365         [ -  + ]:        621 :         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         [ +  + ]:        621 :         if (j->generation == generation) {
     372                 :        240 :                 Job *k, *delete = NULL;
     373                 :        240 :                 _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         [ +  + ]:        240 :                 if (!j->marker)
     380                 :        224 :                         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   [ +  -  +  -  :         44 :                 for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
                   +  - ]
     389                 :            : 
     390                 :            :                         /* For logging below */
     391         [ -  + ]:         44 :                         if (strv_push_pair(&array, k->unit->id, (char*) job_type_to_string(k->type)) < 0)
     392                 :          0 :                                 log_oom();
     393                 :            : 
     394   [ +  +  +  -  :         44 :                         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                 :          8 :                                 delete = k;
     397                 :            : 
     398                 :            :                         /* Check if this in fact was the beginning of the cycle */
     399         [ +  + ]:         44 :                         if (k == j)
     400                 :         16 :                                 break;
     401                 :            :                 }
     402                 :            : 
     403                 :         16 :                 unit_ids = merge_unit_ids(j->manager->unit_log_field, array); /* ignore error */
     404                 :            : 
     405   [ +  -  +  +  :         60 :                 STRV_FOREACH_PAIR(unit_id, job_type, array)
                   +  - ]
     406                 :            :                         /* logging for j not k here to provide a consistent narrative */
     407         [ +  + ]:         44 :                         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         [ +  + ]:         16 :                 if (delete) {
     415                 :            :                         const char *status;
     416                 :            :                         /* logging for j not k here to provide a consistent narrative */
     417                 :          8 :                         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         [ -  + ]:          8 :                         if (log_get_show_color())
     424                 :          0 :                                 status = ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL;
     425                 :            :                         else
     426                 :          8 :                                 status = " SKIP ";
     427                 :            : 
     428                 :          8 :                         unit_status_printf(delete->unit, status,
     429                 :            :                                            "Ordering cycle found, skipping %s");
     430                 :          8 :                         transaction_delete_unit(tr, delete->unit);
     431                 :          8 :                         return -EAGAIN;
     432                 :            :                 }
     433                 :            : 
     434                 :          8 :                 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                 :          8 :                 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         [ +  + ]:        381 :         j->marker = from ? from : j;
     448                 :        381 :         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         [ +  + ]:       1017 :         for (d = 0; d < ELEMENTSOF(directions); d++) {
     455         [ +  + ]:       2401 :                 HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[directions[d]], i) {
     456                 :            :                         Job *o;
     457                 :            : 
     458                 :            :                         /* Is there a job for this unit? */
     459                 :       1765 :                         o = hashmap_get(tr->jobs, u);
     460         [ +  + ]:       1765 :                         if (!o) {
     461                 :            :                                 /* Ok, there is no job for this in the
     462                 :            :                                  * transaction, but maybe there is already one
     463                 :            :                                  * running? */
     464                 :       1230 :                                 o = u->job;
     465         [ +  + ]:       1230 :                                 if (!o)
     466                 :       1186 :                                         continue;
     467                 :            :                         }
     468                 :            : 
     469                 :            :                         /* Cut traversing if the job j is not really *before* o. */
     470         [ +  + ]:        579 :                         if (job_compare(j, o, directions[d]) >= 0)
     471                 :        279 :                                 continue;
     472                 :            : 
     473                 :        300 :                         r = transaction_verify_order_one(tr, o, j, generation, e);
     474         [ +  + ]:        300 :                         if (r < 0)
     475                 :         63 :                                 return r;
     476                 :            :                 }
     477                 :            :         }
     478                 :            : 
     479                 :            :         /* Ok, let's backtrack, and remember that this entry is not on
     480                 :            :          * our path anymore. */
     481                 :        318 :         j->marker = NULL;
     482                 :            : 
     483                 :        318 :         return 0;
     484                 :            : }
     485                 :            : 
     486                 :         80 : 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         [ -  + ]:         80 :         assert(tr);
     493         [ -  + ]:         80 :         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                 :         80 :         g = (*generation)++;
     499                 :            : 
     500         [ +  + ]:        385 :         HASHMAP_FOREACH(j, tr->jobs, i) {
     501                 :        321 :                 r = transaction_verify_order_one(tr, j, NULL, g, e);
     502         [ +  + ]:        321 :                 if (r < 0)
     503                 :         16 :                         return r;
     504                 :            :         }
     505                 :            : 
     506                 :         64 :         return 0;
     507                 :            : }
     508                 :            : 
     509                 :         80 : static void transaction_collect_garbage(Transaction *tr) {
     510                 :            :         bool again;
     511                 :            : 
     512         [ -  + ]:         80 :         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                 :         88 :                 again = false;
     521                 :            : 
     522         [ +  + ]:        530 :                 HASHMAP_FOREACH(j, tr->jobs, i) {
     523         [ +  + ]:        450 :                         if (tr->anchor_job == j)
     524                 :         82 :                                 continue;
     525                 :            : 
     526         [ +  + ]:        368 :                         if (!j->object_list) {
     527         [ +  - ]:          8 :                                 log_trace("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type));
     528                 :          8 :                                 transaction_delete_job(tr, j, true);
     529                 :          8 :                                 again = true;
     530                 :          8 :                                 break;
     531                 :            :                         }
     532                 :            : 
     533   [ +  +  +  -  :        360 :                         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         [ +  + ]:         88 :         } while (again);
     540                 :         80 : }
     541                 :            : 
     542                 :         64 : static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_error *e) {
     543                 :            :         Iterator i;
     544                 :            :         Job *j;
     545                 :            : 
     546         [ -  + ]:         64 :         assert(tr);
     547                 :            : 
     548                 :            :         /* Checks whether applying this transaction means that
     549                 :            :          * existing jobs would be replaced */
     550                 :            : 
     551         [ +  + ]:        346 :         HASHMAP_FOREACH(j, tr->jobs, i) {
     552                 :            : 
     553                 :            :                 /* Assume merged */
     554         [ -  + ]:        290 :                 assert(!j->transaction_prev);
     555         [ -  + ]:        290 :                 assert(!j->transaction_next);
     556                 :            : 
     557   [ +  +  +  +  :        290 :                 if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
                   -  + ]
     558         [ +  + ]:         73 :                     job_type_is_conflicting(j->unit->job->type, j->type))
     559                 :          8 :                         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                 :         16 :                                                  tr->anchor_job->unit->id, job_type_to_string(tr->anchor_job->type),
     562                 :          8 :                                                  j->unit->id, job_type_to_string(j->unit->job->type), job_type_to_string(j->type));
     563                 :            :         }
     564                 :            : 
     565                 :         56 :         return 0;
     566                 :            : }
     567                 :            : 
     568                 :         24 : static void transaction_minimize_impact(Transaction *tr) {
     569                 :            :         Job *j;
     570                 :            :         Iterator i;
     571                 :            : 
     572         [ -  + ]:         24 :         assert(tr);
     573                 :            : 
     574                 :            :         /* Drops all unnecessary jobs that reverse already active jobs
     575                 :            :          * or that stop a running service. */
     576                 :            : 
     577                 :         24 : rescan:
     578         [ +  + ]:        295 :         HASHMAP_FOREACH(j, tr->jobs, i) {
     579         [ +  + ]:        538 :                 LIST_FOREACH(transaction, j, j) {
     580                 :            :                         bool stops_running_service, changes_existing_job;
     581                 :            : 
     582                 :            :                         /* If it matters, we shouldn't drop it */
     583         [ +  + ]:        271 :                         if (j->matters_to_anchor)
     584                 :        178 :                                 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                 :         93 :                         stops_running_service =
     591   [ +  +  -  + ]:         93 :                                 j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
     592                 :            : 
     593                 :         93 :                         changes_existing_job =
     594         [ +  + ]:        137 :                                 j->unit->job &&
     595         [ +  + ]:         44 :                                 job_type_is_conflicting(j->type, j->unit->job->type);
     596                 :            : 
     597   [ +  -  +  + ]:         93 :                         if (!stops_running_service && !changes_existing_job)
     598                 :         89 :                                 continue;
     599                 :            : 
     600         [ -  + ]:          4 :                         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         [ +  - ]:          4 :                         if (changes_existing_job)
     606         [ +  - ]:          4 :                                 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         [ +  - ]:          4 :                         log_unit_debug(j->unit,
     612                 :            :                                        "Deleting %s/%s to minimize impact.",
     613                 :            :                                        j->unit->id, job_type_to_string(j->type));
     614                 :            : 
     615                 :          4 :                         transaction_delete_job(tr, j, true);
     616                 :          4 :                         goto rescan;
     617                 :            :                 }
     618                 :            :         }
     619                 :         24 : }
     620                 :            : 
     621                 :         56 : 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   [ -  +  -  + ]:         56 :         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         [ +  + ]:        328 :         HASHMAP_FOREACH(j, tr->jobs, i) {
     654                 :            :                 /* Assume merged */
     655         [ -  + ]:        272 :                 assert(!j->transaction_prev);
     656         [ -  + ]:        272 :                 assert(!j->transaction_next);
     657                 :            : 
     658                 :        272 :                 r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j);
     659         [ -  + ]:        272 :                 if (r < 0)
     660                 :          0 :                         goto rollback;
     661                 :            :         }
     662                 :            : 
     663         [ +  + ]:        328 :         while ((j = hashmap_steal_first(tr->jobs))) {
     664                 :            :                 Job *installed_job;
     665                 :            : 
     666                 :            :                 /* Clean the job dependencies */
     667                 :        272 :                 transaction_unlink_job(tr, j, false);
     668                 :            : 
     669                 :        272 :                 installed_job = job_install(j);
     670         [ +  + ]:        272 :                 if (installed_job != j) {
     671                 :            :                         /* j has been merged into a previously installed job */
     672         [ +  + ]:         72 :                         if (tr->anchor_job == j)
     673                 :          8 :                                 tr->anchor_job = installed_job;
     674                 :         72 :                         hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
     675                 :         72 :                         job_free(j);
     676                 :         72 :                         j = installed_job;
     677                 :            :                 }
     678                 :            : 
     679                 :        272 :                 job_add_to_run_queue(j);
     680                 :        272 :                 job_add_to_dbus_queue(j);
     681                 :        272 :                 job_start_timer(j, false);
     682                 :        272 :                 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         [ -  + ]:        272 :                 if (affected_jobs)
     687                 :          0 :                         (void) set_put(affected_jobs, j);
     688                 :            :         }
     689                 :            : 
     690                 :         56 :         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                 :         72 : 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                 :         72 :         unsigned generation = 1;
     711                 :            : 
     712         [ -  + ]:         72 :         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         [ +  + ]:        228 :         HASHMAP_FOREACH(j, m->jobs, i)
     721                 :        156 :                 j->generation = 0;
     722                 :            : 
     723                 :            :         /* First step: figure out which jobs matter */
     724                 :         72 :         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         [ +  + ]:         72 :         if (mode == JOB_FAIL)
     730                 :         24 :                 transaction_minimize_impact(tr);
     731                 :            : 
     732                 :            :         /* Third step: Drop redundant jobs */
     733                 :         72 :         transaction_drop_redundant(tr);
     734                 :            : 
     735                 :            :         for (;;) {
     736                 :            :                 /* Fourth step: Let's remove unneeded jobs that might
     737                 :            :                  * be lurking. */
     738         [ +  - ]:         80 :                 if (mode != JOB_ISOLATE)
     739                 :         80 :                         transaction_collect_garbage(tr);
     740                 :            : 
     741                 :            :                 /* Fifth step: verify order makes sense and correct
     742                 :            :                  * cycles if necessary and possible */
     743                 :         80 :                 r = transaction_verify_order(tr, &generation, e);
     744         [ +  + ]:         80 :                 if (r >= 0)
     745                 :         64 :                         break;
     746                 :            : 
     747         [ +  + ]:         16 :                 if (r != -EAGAIN)
     748         [ +  - ]:          8 :                         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                 :         64 :                 r = transaction_merge_jobs(tr, e);
     759         [ +  - ]:         64 :                 if (r >= 0)
     760                 :         64 :                         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                 :         64 :         transaction_drop_redundant(tr);
     776                 :            : 
     777                 :            :         /* Ninth step: check whether we can actually apply this */
     778                 :         64 :         r = transaction_is_destructive(tr, mode, e);
     779         [ +  + ]:         64 :         if (r < 0)
     780         [ +  - ]:          8 :                 return log_notice_errno(r, "Requested transaction contradicts existing jobs: %s", bus_error_message(e, r));
     781                 :            : 
     782                 :            :         /* Tenth step: apply changes */
     783                 :         56 :         r = transaction_apply(tr, m, mode, affected_jobs);
     784         [ -  + ]:         56 :         if (r < 0)
     785         [ #  # ]:          0 :                 return log_warning_errno(r, "Failed to apply transaction: %m");
     786                 :            : 
     787         [ -  + ]:         56 :         assert(hashmap_isempty(tr->jobs));
     788                 :            : 
     789         [ +  - ]:         56 :         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   [ +  +  +  - ]:         56 :                 if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 &&
     797   [ +  -  +  - ]:         36 :                     m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) {
     798                 :         36 :                         (void) pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
     799                 :         36 :                         (void) pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
     800                 :            :                 }
     801                 :            :         }
     802                 :            : 
     803                 :         56 :         return 0;
     804                 :            : }
     805                 :            : 
     806                 :       1244 : static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) {
     807                 :            :         Job *j, *f;
     808                 :            : 
     809         [ -  + ]:       1244 :         assert(tr);
     810         [ -  + ]:       1244 :         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                 :       1244 :         f = hashmap_get(tr->jobs, unit);
     817                 :            : 
     818         [ +  + ]:       1244 :         LIST_FOREACH(transaction, j, f) {
     819         [ -  + ]:        516 :                 assert(j->unit == unit);
     820                 :            : 
     821         [ +  - ]:        516 :                 if (j->type == type) {
     822         [ +  - ]:        516 :                         if (is_new)
     823                 :        516 :                                 *is_new = false;
     824                 :        516 :                         return j;
     825                 :            :                 }
     826                 :            :         }
     827                 :            : 
     828                 :        728 :         j = job_new(unit, type);
     829         [ -  + ]:        728 :         if (!j)
     830                 :          0 :                 return NULL;
     831                 :            : 
     832                 :        728 :         j->generation = 0;
     833                 :        728 :         j->marker = NULL;
     834                 :        728 :         j->matters_to_anchor = false;
     835                 :        728 :         j->irreversible = tr->irreversible;
     836                 :            : 
     837   [ -  +  -  + ]:        728 :         LIST_PREPEND(transaction, f, j);
     838                 :            : 
     839         [ -  + ]:        728 :         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         [ +  - ]:        728 :         if (is_new)
     846                 :        728 :                 *is_new = true;
     847                 :            : 
     848         [ +  + ]:        728 :         log_trace("Added job %s/%s to transaction.", unit->id, job_type_to_string(type));
     849                 :            : 
     850                 :        728 :         return j;
     851                 :            : }
     852                 :            : 
     853                 :        728 : static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
     854         [ -  + ]:        728 :         assert(tr);
     855         [ -  + ]:        728 :         assert(j);
     856                 :            : 
     857         [ -  + ]:        728 :         if (j->transaction_prev)
     858                 :          0 :                 j->transaction_prev->transaction_next = j->transaction_next;
     859         [ -  + ]:        728 :         else if (j->transaction_next)
     860                 :          0 :                 hashmap_replace(tr->jobs, j->unit, j->transaction_next);
     861                 :            :         else
     862                 :        728 :                 hashmap_remove_value(tr->jobs, j->unit, j);
     863                 :            : 
     864         [ -  + ]:        728 :         if (j->transaction_next)
     865                 :          0 :                 j->transaction_next->transaction_prev = j->transaction_prev;
     866                 :            : 
     867                 :        728 :         j->transaction_prev = j->transaction_next = NULL;
     868                 :            : 
     869         [ +  + ]:        974 :         while (j->subject_list)
     870                 :        246 :                 job_dependency_free(j->subject_list);
     871                 :            : 
     872         [ +  + ]:       1654 :         while (j->object_list) {
     873         [ +  + ]:        926 :                 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
     874                 :            : 
     875                 :        926 :                 job_dependency_free(j->object_list);
     876                 :            : 
     877   [ +  +  +  + ]:        926 :                 if (other && delete_dependencies) {
     878         [ +  - ]:          8 :                         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                 :          8 :                         transaction_delete_job(tr, other, delete_dependencies);
     883                 :            :                 }
     884                 :            :         }
     885                 :        728 : }
     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                 :       1524 : 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         [ -  + ]:       1524 :         assert(tr);
     931         [ -  + ]:       1524 :         assert(type < _JOB_TYPE_MAX);
     932         [ -  + ]:       1524 :         assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
     933         [ -  + ]:       1524 :         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         [ -  + ]:       1524 :         if (MANAGER_IS_RELOADING(unit->manager))
     940                 :          0 :                 unit_coldplug(unit);
     941                 :            : 
     942         [ +  + ]:       1524 :         if (by)
     943         [ +  + ]:       1452 :                 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   [ +  -  -  + ]:       1524 :         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         [ +  + ]:       1524 :         if (type != JOB_STOP) {
     951                 :        984 :                 r = bus_unit_validate_load_state(unit, e);
     952         [ +  + ]:        984 :                 if (r < 0)
     953                 :        280 :                         return r;
     954                 :            :         }
     955                 :            : 
     956         [ -  + ]:       1244 :         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                 :       1244 :         ret = transaction_add_one_job(tr, type, unit, &is_new);
     963         [ -  + ]:       1244 :         if (!ret)
     964                 :          0 :                 return -ENOMEM;
     965                 :            : 
     966   [ +  -  -  + ]:       1244 :         ret->ignore_order = ret->ignore_order || ignore_order;
     967                 :            : 
     968                 :            :         /* Then, add a link to the job. */
     969         [ +  + ]:       1244 :         if (by) {
     970         [ -  + ]:       1172 :                 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         [ -  + ]:         72 :                 assert(!tr->anchor_job);
     975                 :         72 :                 tr->anchor_job = ret;
     976                 :            :         }
     977                 :            : 
     978   [ +  +  +  -  :       1244 :         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         [ -  + ]:        728 :                 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   [ +  +  +  + ]:        728 :                 if (IN_SET(type, JOB_START, JOB_RESTART)) {
    1000         [ +  + ]:        928 :                         HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_REQUIRES], i) {
    1001                 :        432 :                                 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, true, false, false, ignore_order, e);
    1002         [ -  + ]:        432 :                                 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         [ -  + ]:        496 :                         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         [ +  + ]:        984 :                         HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_WANTS], i) {
    1021                 :        488 :                                 r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, false, false, false, ignore_order, e);
    1022         [ +  + ]:        488 :                                 if (r < 0) {
    1023                 :            :                                         /* unit masked, job type not applicable and unit not found are not considered as errors. */
    1024   [ +  -  +  -  :        280 :                                         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                 :        280 :                                         sd_bus_error_free(e);
    1029                 :            :                                 }
    1030                 :            :                         }
    1031                 :            : 
    1032         [ -  + ]:        496 :                         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         [ +  + ]:       1016 :                         HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTS], i) {
    1043                 :        520 :                                 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, true, true, false, ignore_order, e);
    1044         [ -  + ]:        520 :                                 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         [ +  + ]:        500 :                         HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[UNIT_CONFLICTED_BY], i) {
    1053                 :          4 :                                 r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, false, false, false, ignore_order, e);
    1054         [ -  + ]:          4 :                                 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   [ +  +  +  + ]:        728 :                 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         [ +  + ]:        236 :                         ptype = type == JOB_RESTART ? JOB_TRY_RESTART : type;
    1079                 :            : 
    1080         [ +  + ]:       1180 :                         for (j = 0; j < ELEMENTSOF(propagate_deps); j++)
    1081         [ +  + ]:        952 :                                 HASHMAP_FOREACH_KEY(v, dep, ret->unit->dependencies[propagate_deps[j]], i) {
    1082                 :            :                                         JobType nt;
    1083                 :            : 
    1084                 :          8 :                                         nt = job_type_collapse(ptype, dep);
    1085         [ -  + ]:          8 :                                         if (nt == JOB_NOP)
    1086                 :          0 :                                                 continue;
    1087                 :            : 
    1088                 :          8 :                                         r = transaction_add_job_and_dependencies(tr, nt, dep, ret, true, false, false, ignore_order, e);
    1089         [ -  + ]:          8 :                                         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         [ -  + ]:        728 :                 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                 :       1244 :         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                 :         72 : Transaction *transaction_new(bool irreversible) {
    1145                 :            :         Transaction *tr;
    1146                 :            : 
    1147                 :         72 :         tr = new0(Transaction, 1);
    1148         [ -  + ]:         72 :         if (!tr)
    1149                 :          0 :                 return NULL;
    1150                 :            : 
    1151                 :         72 :         tr->jobs = hashmap_new(NULL);
    1152         [ -  + ]:         72 :         if (!tr->jobs)
    1153                 :          0 :                 return mfree(tr);
    1154                 :            : 
    1155                 :         72 :         tr->irreversible = irreversible;
    1156                 :            : 
    1157                 :         72 :         return tr;
    1158                 :            : }
    1159                 :            : 
    1160                 :         72 : void transaction_free(Transaction *tr) {
    1161         [ -  + ]:         72 :         assert(hashmap_isempty(tr->jobs));
    1162                 :         72 :         hashmap_free(tr->jobs);
    1163                 :         72 :         free(tr);
    1164                 :         72 : }

Generated by: LCOV version 1.14