LCOV - code coverage report
Current view: top level - shared - install.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 1309 1727 75.8 %
Date: 2019-08-23 13:36:53 Functions: 77 80 96.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 1015 1798 56.5 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <dirent.h>
       4                 :            : #include <errno.h>
       5                 :            : #include <fcntl.h>
       6                 :            : #include <fnmatch.h>
       7                 :            : #include <limits.h>
       8                 :            : #include <stddef.h>
       9                 :            : #include <stdio.h>
      10                 :            : #include <stdlib.h>
      11                 :            : #include <string.h>
      12                 :            : #include <sys/stat.h>
      13                 :            : #include <unistd.h>
      14                 :            : 
      15                 :            : #include "alloc-util.h"
      16                 :            : #include "conf-files.h"
      17                 :            : #include "conf-parser.h"
      18                 :            : #include "def.h"
      19                 :            : #include "dirent-util.h"
      20                 :            : #include "extract-word.h"
      21                 :            : #include "fd-util.h"
      22                 :            : #include "fileio.h"
      23                 :            : #include "fs-util.h"
      24                 :            : #include "hashmap.h"
      25                 :            : #include "install-printf.h"
      26                 :            : #include "install.h"
      27                 :            : #include "locale-util.h"
      28                 :            : #include "log.h"
      29                 :            : #include "macro.h"
      30                 :            : #include "mkdir.h"
      31                 :            : #include "path-lookup.h"
      32                 :            : #include "path-util.h"
      33                 :            : #include "rm-rf.h"
      34                 :            : #include "set.h"
      35                 :            : #include "special.h"
      36                 :            : #include "stat-util.h"
      37                 :            : #include "string-table.h"
      38                 :            : #include "string-util.h"
      39                 :            : #include "strv.h"
      40                 :            : #include "unit-file.h"
      41                 :            : 
      42                 :            : #define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
      43                 :            : 
      44                 :            : typedef enum SearchFlags {
      45                 :            :         SEARCH_LOAD                   = 1 << 0,
      46                 :            :         SEARCH_FOLLOW_CONFIG_SYMLINKS = 1 << 1,
      47                 :            :         SEARCH_DROPIN                 = 1 << 2,
      48                 :            : } SearchFlags;
      49                 :            : 
      50                 :            : typedef struct {
      51                 :            :         OrderedHashmap *will_process;
      52                 :            :         OrderedHashmap *have_processed;
      53                 :            : } InstallContext;
      54                 :            : 
      55                 :            : typedef enum {
      56                 :            :         PRESET_UNKNOWN,
      57                 :            :         PRESET_ENABLE,
      58                 :            :         PRESET_DISABLE,
      59                 :            : } PresetAction;
      60                 :            : 
      61                 :            : typedef struct {
      62                 :            :         char *pattern;
      63                 :            :         PresetAction action;
      64                 :            :         char **instances;
      65                 :            : } PresetRule;
      66                 :            : 
      67                 :            : typedef struct {
      68                 :            :         PresetRule *rules;
      69                 :            :         size_t n_rules;
      70                 :            : } Presets;
      71                 :            : 
      72                 :       2072 : static bool unit_file_install_info_has_rules(const UnitFileInstallInfo *i) {
      73         [ -  + ]:       2072 :         assert(i);
      74                 :            : 
      75                 :       2072 :         return !strv_isempty(i->aliases) ||
      76   [ +  +  +  + ]:       3200 :                !strv_isempty(i->wanted_by) ||
      77         [ +  + ]:       1128 :                !strv_isempty(i->required_by);
      78                 :            : }
      79                 :            : 
      80                 :       1104 : static bool unit_file_install_info_has_also(const UnitFileInstallInfo *i) {
      81         [ -  + ]:       1104 :         assert(i);
      82                 :            : 
      83                 :       1104 :         return !strv_isempty(i->also);
      84                 :            : }
      85                 :            : 
      86                 :         56 : static void presets_freep(Presets *p) {
      87                 :            :         size_t i;
      88                 :            : 
      89         [ -  + ]:         56 :         if (!p)
      90                 :          0 :                 return;
      91                 :            : 
      92         [ +  + ]:        128 :         for (i = 0; i < p->n_rules; i++) {
      93                 :         72 :                 free(p->rules[i].pattern);
      94                 :         72 :                 strv_free(p->rules[i].instances);
      95                 :            :         }
      96                 :            : 
      97                 :         56 :         free(p->rules);
      98                 :         56 :         p->n_rules = 0;
      99                 :            : }
     100                 :            : 
     101                 :            : static const char *const unit_file_type_table[_UNIT_FILE_TYPE_MAX] = {
     102                 :            :         [UNIT_FILE_TYPE_REGULAR] = "regular",
     103                 :            :         [UNIT_FILE_TYPE_SYMLINK] = "symlink",
     104                 :            :         [UNIT_FILE_TYPE_MASKED] = "masked",
     105                 :            : };
     106                 :            : 
     107   [ +  -  -  + ]:          8 : DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType);
     108                 :            : 
     109                 :        124 : static int in_search_path(const LookupPaths *p, const char *path) {
     110                 :        124 :         _cleanup_free_ char *parent = NULL;
     111                 :            :         char **i;
     112                 :            : 
     113         [ -  + ]:        124 :         assert(path);
     114                 :            : 
     115                 :        124 :         parent = dirname_malloc(path);
     116         [ -  + ]:        124 :         if (!parent)
     117                 :          0 :                 return -ENOMEM;
     118                 :            : 
     119   [ +  -  +  + ]:       1372 :         STRV_FOREACH(i, p->search_path)
     120         [ +  + ]:       1356 :                 if (path_equal(parent, *i))
     121                 :        108 :                         return true;
     122                 :            : 
     123                 :         16 :         return false;
     124                 :            : }
     125                 :            : 
     126                 :       1168 : static const char* skip_root(const LookupPaths *p, const char *path) {
     127                 :            :         char *e;
     128                 :            : 
     129         [ -  + ]:       1168 :         assert(p);
     130         [ -  + ]:       1168 :         assert(path);
     131                 :            : 
     132         [ +  + ]:       1168 :         if (!p->root_dir)
     133                 :        676 :                 return path;
     134                 :            : 
     135                 :        492 :         e = path_startswith(path, p->root_dir);
     136         [ +  + ]:        492 :         if (!e)
     137                 :          8 :                 return NULL;
     138                 :            : 
     139                 :            :         /* Make sure the returned path starts with a slash */
     140         [ +  - ]:        484 :         if (e[0] != '/') {
     141   [ +  -  -  + ]:        484 :                 if (e == path || e[-1] != '/')
     142                 :          0 :                         return NULL;
     143                 :            : 
     144                 :        484 :                 e--;
     145                 :            :         }
     146                 :            : 
     147                 :        484 :         return e;
     148                 :            : }
     149                 :            : 
     150                 :       2776 : static int path_is_generator(const LookupPaths *p, const char *path) {
     151                 :       2776 :         _cleanup_free_ char *parent = NULL;
     152                 :            : 
     153         [ -  + ]:       2776 :         assert(p);
     154         [ -  + ]:       2776 :         assert(path);
     155                 :            : 
     156                 :       2776 :         parent = dirname_malloc(path);
     157         [ -  + ]:       2776 :         if (!parent)
     158                 :          0 :                 return -ENOMEM;
     159                 :            : 
     160         [ +  - ]:       5524 :         return path_equal_ptr(parent, p->generator) ||
     161   [ +  +  +  + ]:       5524 :                path_equal_ptr(parent, p->generator_early) ||
     162                 :       2748 :                path_equal_ptr(parent, p->generator_late);
     163                 :            : }
     164                 :            : 
     165                 :       2736 : static int path_is_transient(const LookupPaths *p, const char *path) {
     166                 :       2736 :         _cleanup_free_ char *parent = NULL;
     167                 :            : 
     168         [ -  + ]:       2736 :         assert(p);
     169         [ -  + ]:       2736 :         assert(path);
     170                 :            : 
     171                 :       2736 :         parent = dirname_malloc(path);
     172         [ -  + ]:       2736 :         if (!parent)
     173                 :          0 :                 return -ENOMEM;
     174                 :            : 
     175                 :       2736 :         return path_equal_ptr(parent, p->transient);
     176                 :            : }
     177                 :            : 
     178                 :          0 : static int path_is_control(const LookupPaths *p, const char *path) {
     179                 :          0 :         _cleanup_free_ char *parent = NULL;
     180                 :            : 
     181         [ #  # ]:          0 :         assert(p);
     182         [ #  # ]:          0 :         assert(path);
     183                 :            : 
     184                 :          0 :         parent = dirname_malloc(path);
     185         [ #  # ]:          0 :         if (!parent)
     186                 :          0 :                 return -ENOMEM;
     187                 :            : 
     188   [ #  #  #  # ]:          0 :         return path_equal_ptr(parent, p->persistent_control) ||
     189                 :          0 :                path_equal_ptr(parent, p->runtime_control);
     190                 :            : }
     191                 :            : 
     192                 :         48 : static int path_is_config(const LookupPaths *p, const char *path, bool check_parent) {
     193                 :         48 :         _cleanup_free_ char *parent = NULL;
     194                 :            : 
     195         [ -  + ]:         48 :         assert(p);
     196         [ -  + ]:         48 :         assert(path);
     197                 :            : 
     198                 :            :         /* Note that we do *not* have generic checks for /etc or /run in place, since with
     199                 :            :          * them we couldn't discern configuration from transient or generated units */
     200                 :            : 
     201         [ +  - ]:         48 :         if (check_parent) {
     202                 :         48 :                 parent = dirname_malloc(path);
     203         [ -  + ]:         48 :                 if (!parent)
     204                 :          0 :                         return -ENOMEM;
     205                 :            : 
     206                 :         48 :                 path = parent;
     207                 :            :         }
     208                 :            : 
     209   [ +  +  -  + ]:         88 :         return path_equal_ptr(path, p->persistent_config) ||
     210                 :         40 :                path_equal_ptr(path, p->runtime_config);
     211                 :            : }
     212                 :            : 
     213                 :        904 : static int path_is_runtime(const LookupPaths *p, const char *path, bool check_parent) {
     214                 :        904 :         _cleanup_free_ char *parent = NULL;
     215                 :            :         const char *rpath;
     216                 :            : 
     217         [ -  + ]:        904 :         assert(p);
     218         [ -  + ]:        904 :         assert(path);
     219                 :            : 
     220                 :            :         /* Everything in /run is considered runtime. On top of that we also add
     221                 :            :          * explicit checks for the various runtime directories, as safety net. */
     222                 :            : 
     223                 :        904 :         rpath = skip_root(p, path);
     224   [ +  -  +  + ]:        904 :         if (rpath && path_startswith(rpath, "/run"))
     225                 :          4 :                 return true;
     226                 :            : 
     227         [ +  + ]:        900 :         if (check_parent) {
     228                 :         20 :                 parent = dirname_malloc(path);
     229         [ -  + ]:         20 :                 if (!parent)
     230                 :          0 :                         return -ENOMEM;
     231                 :            : 
     232                 :         20 :                 path = parent;
     233                 :            :         }
     234                 :            : 
     235         [ +  - ]:       1800 :         return path_equal_ptr(path, p->runtime_config) ||
     236         [ +  - ]:       1800 :                path_equal_ptr(path, p->generator) ||
     237         [ +  - ]:       1800 :                path_equal_ptr(path, p->generator_early) ||
     238         [ +  - ]:       1800 :                path_equal_ptr(path, p->generator_late) ||
     239   [ +  -  -  + ]:       2700 :                path_equal_ptr(path, p->transient) ||
     240                 :        900 :                path_equal_ptr(path, p->runtime_control);
     241                 :            : }
     242                 :            : 
     243                 :         16 : static int path_is_vendor(const LookupPaths *p, const char *path) {
     244                 :            :         const char *rpath;
     245                 :            : 
     246         [ -  + ]:         16 :         assert(p);
     247         [ -  + ]:         16 :         assert(path);
     248                 :            : 
     249                 :         16 :         rpath = skip_root(p, path);
     250         [ -  + ]:         16 :         if (!rpath)
     251                 :          0 :                 return 0;
     252                 :            : 
     253         [ +  + ]:         16 :         if (path_startswith(rpath, "/usr"))
     254                 :         12 :                 return true;
     255                 :            : 
     256                 :            : #if HAVE_SPLIT_USR
     257                 :            :         if (path_startswith(rpath, "/lib"))
     258                 :            :                 return true;
     259                 :            : #endif
     260                 :            : 
     261                 :          4 :         return path_equal(rpath, SYSTEM_DATA_UNIT_PATH);
     262                 :            : }
     263                 :            : 
     264                 :        488 : int unit_file_changes_add(
     265                 :            :                 UnitFileChange **changes,
     266                 :            :                 size_t *n_changes,
     267                 :            :                 UnitFileChangeType type,
     268                 :            :                 const char *path,
     269                 :            :                 const char *source) {
     270                 :            : 
     271                 :        488 :         _cleanup_free_ char *p = NULL, *s = NULL;
     272                 :            :         UnitFileChange *c;
     273                 :            : 
     274         [ -  + ]:        488 :         assert(path);
     275         [ -  + ]:        488 :         assert(!changes == !n_changes);
     276                 :            : 
     277         [ +  + ]:        488 :         if (!changes)
     278                 :        236 :                 return 0;
     279                 :            : 
     280                 :        252 :         c = reallocarray(*changes, *n_changes + 1, sizeof(UnitFileChange));
     281         [ -  + ]:        252 :         if (!c)
     282                 :          0 :                 return -ENOMEM;
     283                 :        252 :         *changes = c;
     284                 :            : 
     285                 :        252 :         p = strdup(path);
     286         [ +  + ]:        252 :         if (source)
     287                 :        148 :                 s = strdup(source);
     288                 :            : 
     289   [ +  -  +  +  :        252 :         if (!p || (source && !s))
                   -  + ]
     290                 :          0 :                 return -ENOMEM;
     291                 :            : 
     292                 :        252 :         path_simplify(p, false);
     293         [ +  + ]:        252 :         if (s)
     294                 :        148 :                 path_simplify(s, false);
     295                 :            : 
     296                 :        252 :         c[*n_changes] = (UnitFileChange) { type, p, s };
     297                 :        252 :         p = s = NULL;
     298                 :        252 :         (*n_changes) ++;
     299                 :        252 :         return 0;
     300                 :            : }
     301                 :            : 
     302                 :        176 : void unit_file_changes_free(UnitFileChange *changes, size_t n_changes) {
     303                 :            :         size_t i;
     304                 :            : 
     305   [ +  +  -  + ]:        176 :         assert(changes || n_changes == 0);
     306                 :            : 
     307         [ +  + ]:        428 :         for (i = 0; i < n_changes; i++) {
     308                 :        252 :                 free(changes[i].path);
     309                 :        252 :                 free(changes[i].source);
     310                 :            :         }
     311                 :            : 
     312                 :        176 :         free(changes);
     313                 :        176 : }
     314                 :            : 
     315                 :          0 : void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet) {
     316                 :            :         size_t i;
     317                 :          0 :         bool logged = false;
     318                 :            : 
     319   [ #  #  #  # ]:          0 :         assert(changes || n_changes == 0);
     320                 :            :         /* If verb is not specified, errors are not allowed! */
     321   [ #  #  #  # ]:          0 :         assert(verb || r >= 0);
     322                 :            : 
     323         [ #  # ]:          0 :         for (i = 0; i < n_changes; i++) {
     324   [ #  #  #  # ]:          0 :                 assert(verb || changes[i].type >= 0);
     325                 :            : 
     326   [ #  #  #  #  :          0 :                 switch(changes[i].type) {
          #  #  #  #  #  
                      # ]
     327                 :          0 :                 case UNIT_FILE_SYMLINK:
     328         [ #  # ]:          0 :                         if (!quiet)
     329         [ #  # ]:          0 :                                 log_info("Created symlink %s %s %s.",
     330                 :            :                                          changes[i].path,
     331                 :            :                                          special_glyph(SPECIAL_GLYPH_ARROW),
     332                 :            :                                          changes[i].source);
     333                 :          0 :                         break;
     334                 :          0 :                 case UNIT_FILE_UNLINK:
     335         [ #  # ]:          0 :                         if (!quiet)
     336         [ #  # ]:          0 :                                 log_info("Removed %s.", changes[i].path);
     337                 :          0 :                         break;
     338                 :          0 :                 case UNIT_FILE_IS_MASKED:
     339         [ #  # ]:          0 :                         if (!quiet)
     340         [ #  # ]:          0 :                                 log_info("Unit %s is masked, ignoring.", changes[i].path);
     341                 :          0 :                         break;
     342                 :          0 :                 case UNIT_FILE_IS_DANGLING:
     343         [ #  # ]:          0 :                         if (!quiet)
     344         [ #  # ]:          0 :                                 log_info("Unit %s is an alias to a unit that is not present, ignoring.",
     345                 :            :                                          changes[i].path);
     346                 :          0 :                         break;
     347                 :          0 :                 case -EEXIST:
     348         [ #  # ]:          0 :                         if (changes[i].source)
     349         [ #  # ]:          0 :                                 log_error_errno(changes[i].type,
     350                 :            :                                                 "Failed to %s unit, file %s already exists and is a symlink to %s.",
     351                 :            :                                                 verb, changes[i].path, changes[i].source);
     352                 :            :                         else
     353         [ #  # ]:          0 :                                 log_error_errno(changes[i].type,
     354                 :            :                                                 "Failed to %s unit, file %s already exists.",
     355                 :            :                                                 verb, changes[i].path);
     356                 :          0 :                         logged = true;
     357                 :          0 :                         break;
     358                 :          0 :                 case -ERFKILL:
     359         [ #  # ]:          0 :                         log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.",
     360                 :            :                                         verb, changes[i].path);
     361                 :          0 :                         logged = true;
     362                 :          0 :                         break;
     363                 :          0 :                 case -EADDRNOTAVAIL:
     364         [ #  # ]:          0 :                         log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.",
     365                 :            :                                         verb, changes[i].path);
     366                 :          0 :                         logged = true;
     367                 :          0 :                         break;
     368                 :          0 :                 case -ELOOP:
     369         [ #  # ]:          0 :                         log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s",
     370                 :            :                                         verb, changes[i].path);
     371                 :          0 :                         logged = true;
     372                 :          0 :                         break;
     373                 :            : 
     374                 :          0 :                 case -ENOENT:
     375         [ #  # ]:          0 :                         log_error_errno(changes[i].type, "Failed to %s unit, unit %s does not exist.", verb, changes[i].path);
     376                 :          0 :                         logged = true;
     377                 :          0 :                         break;
     378                 :            : 
     379                 :          0 :                 default:
     380         [ #  # ]:          0 :                         assert(changes[i].type < 0);
     381         [ #  # ]:          0 :                         log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.",
     382                 :            :                                         verb, changes[i].path);
     383                 :          0 :                         logged = true;
     384                 :            :                 }
     385                 :            :         }
     386                 :            : 
     387   [ #  #  #  # ]:          0 :         if (r < 0 && !logged)
     388         [ #  # ]:          0 :                 log_error_errno(r, "Failed to %s: %m.", verb);
     389                 :          0 : }
     390                 :            : 
     391                 :            : /**
     392                 :            :  * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem.
     393                 :            :  * wc should be the full path in the host file system.
     394                 :            :  */
     395                 :          8 : static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) {
     396         [ -  + ]:          8 :         assert(path_is_absolute(wd));
     397                 :            : 
     398                 :            :         /* This will give incorrect results if the paths are relative and go outside
     399                 :            :          * of the chroot. False negatives are possible. */
     400                 :            : 
     401         [ -  + ]:          8 :         if (!root)
     402                 :          0 :                 root = "/";
     403                 :            : 
     404   [ +  -  +  +  :         56 :         a = strjoina(path_is_absolute(a) ? root : wd, "/", a);
          +  -  -  +  -  
             +  +  +  +  
                      - ]
     405   [ +  -  +  +  :         56 :         b = strjoina(path_is_absolute(b) ? root : wd, "/", b);
          +  -  -  +  -  
             +  +  +  +  
                      - ]
     406                 :          8 :         return path_equal_or_files_same(a, b, 0);
     407                 :            : }
     408                 :            : 
     409                 :        156 : static int create_symlink(
     410                 :            :                 const LookupPaths *paths,
     411                 :            :                 const char *old_path,
     412                 :            :                 const char *new_path,
     413                 :            :                 bool force,
     414                 :            :                 UnitFileChange **changes,
     415                 :            :                 size_t *n_changes) {
     416                 :            : 
     417                 :        156 :         _cleanup_free_ char *dest = NULL, *dirname = NULL;
     418                 :            :         const char *rp;
     419                 :            :         int r;
     420                 :            : 
     421         [ -  + ]:        156 :         assert(old_path);
     422         [ -  + ]:        156 :         assert(new_path);
     423                 :            : 
     424                 :        156 :         rp = skip_root(paths, old_path);
     425         [ +  + ]:        156 :         if (rp)
     426                 :        148 :                 old_path = rp;
     427                 :            : 
     428                 :            :         /* Actually create a symlink, and remember that we did. Is
     429                 :            :          * smart enough to check if there's already a valid symlink in
     430                 :            :          * place.
     431                 :            :          *
     432                 :            :          * Returns 1 if a symlink was created or already exists and points to
     433                 :            :          * the right place, or negative on error.
     434                 :            :          */
     435                 :            : 
     436                 :        156 :         mkdir_parents_label(new_path, 0755);
     437                 :            : 
     438         [ +  + ]:        156 :         if (symlink(old_path, new_path) >= 0) {
     439                 :        148 :                 unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
     440                 :        148 :                 return 1;
     441                 :            :         }
     442                 :            : 
     443         [ -  + ]:          8 :         if (errno != EEXIST) {
     444                 :          0 :                 unit_file_changes_add(changes, n_changes, -errno, new_path, NULL);
     445                 :          0 :                 return -errno;
     446                 :            :         }
     447                 :            : 
     448                 :          8 :         r = readlink_malloc(new_path, &dest);
     449         [ -  + ]:          8 :         if (r < 0) {
     450                 :            :                 /* translate EINVAL (non-symlink exists) to EEXIST */
     451         [ #  # ]:          0 :                 if (r == -EINVAL)
     452                 :          0 :                         r = -EEXIST;
     453                 :            : 
     454                 :          0 :                 unit_file_changes_add(changes, n_changes, r, new_path, NULL);
     455                 :          0 :                 return r;
     456                 :            :         }
     457                 :            : 
     458                 :          8 :         dirname = dirname_malloc(new_path);
     459         [ -  + ]:          8 :         if (!dirname)
     460                 :          0 :                 return -ENOMEM;
     461                 :            : 
     462         [ +  - ]:          8 :         if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path)) {
     463         [ +  - ]:          8 :                 log_debug("Symlink %s → %s already exists", new_path, dest);
     464                 :          8 :                 return 1;
     465                 :            :         }
     466                 :            : 
     467         [ #  # ]:          0 :         if (!force) {
     468                 :          0 :                 unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest);
     469                 :          0 :                 return -EEXIST;
     470                 :            :         }
     471                 :            : 
     472                 :          0 :         r = symlink_atomic(old_path, new_path);
     473         [ #  # ]:          0 :         if (r < 0) {
     474                 :          0 :                 unit_file_changes_add(changes, n_changes, r, new_path, NULL);
     475                 :          0 :                 return r;
     476                 :            :         }
     477                 :            : 
     478                 :          0 :         unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
     479                 :          0 :         unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
     480                 :            : 
     481                 :          0 :         return 1;
     482                 :            : }
     483                 :            : 
     484                 :        232 : static int mark_symlink_for_removal(
     485                 :            :                 Set **remove_symlinks_to,
     486                 :            :                 const char *p) {
     487                 :            : 
     488                 :            :         char *n;
     489                 :            :         int r;
     490                 :            : 
     491         [ -  + ]:        232 :         assert(p);
     492                 :            : 
     493                 :        232 :         r = set_ensure_allocated(remove_symlinks_to, &path_hash_ops);
     494         [ -  + ]:        232 :         if (r < 0)
     495                 :          0 :                 return r;
     496                 :            : 
     497                 :        232 :         n = strdup(p);
     498         [ -  + ]:        232 :         if (!n)
     499                 :          0 :                 return -ENOMEM;
     500                 :            : 
     501                 :        232 :         path_simplify(n, false);
     502                 :            : 
     503                 :        232 :         r = set_consume(*remove_symlinks_to, n);
     504         [ -  + ]:        232 :         if (r == -EEXIST)
     505                 :          0 :                 return 0;
     506         [ -  + ]:        232 :         if (r < 0)
     507                 :          0 :                 return r;
     508                 :            : 
     509                 :        232 :         return 1;
     510                 :            : }
     511                 :            : 
     512                 :        248 : static int remove_marked_symlinks_fd(
     513                 :            :                 Set *remove_symlinks_to,
     514                 :            :                 int fd,
     515                 :            :                 const char *path,
     516                 :            :                 const char *config_path,
     517                 :            :                 const LookupPaths *lp,
     518                 :            :                 bool dry_run,
     519                 :            :                 bool *restart,
     520                 :            :                 UnitFileChange **changes,
     521                 :            :                 size_t *n_changes) {
     522                 :            : 
     523                 :        248 :         _cleanup_closedir_ DIR *d = NULL;
     524                 :            :         struct dirent *de;
     525                 :        248 :         int r = 0;
     526                 :            : 
     527         [ -  + ]:        248 :         assert(remove_symlinks_to);
     528         [ -  + ]:        248 :         assert(fd >= 0);
     529         [ -  + ]:        248 :         assert(path);
     530         [ -  + ]:        248 :         assert(config_path);
     531         [ -  + ]:        248 :         assert(lp);
     532         [ -  + ]:        248 :         assert(restart);
     533                 :            : 
     534                 :        248 :         d = fdopendir(fd);
     535         [ -  + ]:        248 :         if (!d) {
     536                 :          0 :                 safe_close(fd);
     537                 :          0 :                 return -errno;
     538                 :            :         }
     539                 :            : 
     540                 :        248 :         rewinddir(d);
     541                 :            : 
     542   [ +  +  -  +  :       1304 :         FOREACH_DIRENT(de, d, return -errno) {
                   +  + ]
     543                 :            : 
     544                 :        560 :                 dirent_ensure_type(d, de);
     545                 :            : 
     546         [ +  + ]:        560 :                 if (de->d_type == DT_DIR) {
     547      [ +  -  - ]:        128 :                         _cleanup_free_ char *p = NULL;
     548                 :            :                         int nfd, q;
     549                 :            : 
     550                 :        128 :                         nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
     551         [ -  + ]:        128 :                         if (nfd < 0) {
     552         [ #  # ]:          0 :                                 if (errno == ENOENT)
     553                 :          0 :                                         continue;
     554                 :            : 
     555         [ #  # ]:          0 :                                 if (r == 0)
     556                 :          0 :                                         r = -errno;
     557                 :          0 :                                 continue;
     558                 :            :                         }
     559                 :            : 
     560                 :        128 :                         p = path_make_absolute(de->d_name, path);
     561         [ -  + ]:        128 :                         if (!p) {
     562                 :          0 :                                 safe_close(nfd);
     563                 :          0 :                                 return -ENOMEM;
     564                 :            :                         }
     565                 :            : 
     566                 :            :                         /* This will close nfd, regardless whether it succeeds or not */
     567                 :        128 :                         q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, dry_run, restart, changes, n_changes);
     568   [ -  +  #  # ]:        128 :                         if (q < 0 && r == 0)
     569                 :          0 :                                 r = q;
     570                 :            : 
     571         [ +  - ]:        432 :                 } else if (de->d_type == DT_LNK) {
     572   [ +  +  -  +  :        784 :                         _cleanup_free_ char *p = NULL, *dest = NULL;
                   +  - ]
     573                 :            :                         const char *rp;
     574                 :            :                         bool found;
     575                 :            :                         int q;
     576                 :            : 
     577         [ -  + ]:        432 :                         if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
     578                 :          0 :                                 continue;
     579                 :            : 
     580                 :        432 :                         p = path_make_absolute(de->d_name, path);
     581         [ -  + ]:        432 :                         if (!p)
     582                 :          0 :                                 return -ENOMEM;
     583                 :        432 :                         path_simplify(p, false);
     584                 :            : 
     585                 :        432 :                         q = readlink_malloc(p, &dest);
     586         [ -  + ]:        432 :                         if (q == -ENOENT)
     587                 :          0 :                                 continue;
     588         [ -  + ]:        432 :                         if (q < 0) {
     589         [ #  # ]:          0 :                                 if (r == 0)
     590                 :          0 :                                         r = q;
     591                 :          0 :                                 continue;
     592                 :            :                         }
     593                 :            : 
     594                 :            :                         /* We remove all links pointing to a file or path that is marked, as well as all files sharing
     595                 :            :                          * the same name as a file that is marked. */
     596                 :            : 
     597         [ +  + ]:        864 :                         found = set_contains(remove_symlinks_to, dest) ||
     598   [ +  -  +  + ]:        864 :                                 set_contains(remove_symlinks_to, basename(dest)) ||
     599                 :        360 :                                 set_contains(remove_symlinks_to, de->d_name);
     600                 :            : 
     601         [ +  + ]:        432 :                         if (!found)
     602                 :        352 :                                 continue;
     603                 :            : 
     604         [ +  - ]:         80 :                         if (!dry_run) {
     605   [ -  +  #  # ]:         80 :                                 if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) {
     606         [ #  # ]:          0 :                                         if (r == 0)
     607                 :          0 :                                                 r = -errno;
     608                 :          0 :                                         unit_file_changes_add(changes, n_changes, -errno, p, NULL);
     609                 :          0 :                                         continue;
     610                 :            :                                 }
     611                 :            : 
     612                 :         80 :                                 (void) rmdir_parents(p, config_path);
     613                 :            :                         }
     614                 :            : 
     615                 :         80 :                         unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
     616                 :            : 
     617                 :            :                         /* Now, remember the full path (but with the root prefix removed) of
     618                 :            :                          * the symlink we just removed, and remove any symlinks to it, too. */
     619                 :            : 
     620                 :         80 :                         rp = skip_root(lp, p);
     621         [ +  - ]:         80 :                         q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p);
     622         [ -  + ]:         80 :                         if (q < 0)
     623                 :          0 :                                 return q;
     624   [ +  -  +  - ]:         80 :                         if (q > 0 && !dry_run)
     625                 :         80 :                                 *restart = true;
     626                 :            :                 }
     627                 :            :         }
     628                 :            : 
     629                 :        248 :         return r;
     630                 :            : }
     631                 :            : 
     632                 :         96 : static int remove_marked_symlinks(
     633                 :            :                 Set *remove_symlinks_to,
     634                 :            :                 const char *config_path,
     635                 :            :                 const LookupPaths *lp,
     636                 :            :                 bool dry_run,
     637                 :            :                 UnitFileChange **changes,
     638                 :            :                 size_t *n_changes) {
     639                 :            : 
     640                 :         96 :         _cleanup_close_ int fd = -1;
     641                 :            :         bool restart;
     642                 :         96 :         int r = 0;
     643                 :            : 
     644         [ -  + ]:         96 :         assert(config_path);
     645         [ -  + ]:         96 :         assert(lp);
     646                 :            : 
     647         [ +  + ]:         96 :         if (set_size(remove_symlinks_to) <= 0)
     648                 :         20 :                 return 0;
     649                 :            : 
     650                 :         76 :         fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
     651         [ -  + ]:         76 :         if (fd < 0)
     652         [ #  # ]:          0 :                 return errno == ENOENT ? 0 : -errno;
     653                 :            : 
     654                 :            :         do {
     655                 :            :                 int q, cfd;
     656                 :        120 :                 restart = false;
     657                 :            : 
     658                 :        120 :                 cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
     659         [ -  + ]:        120 :                 if (cfd < 0)
     660                 :          0 :                         return -errno;
     661                 :            : 
     662                 :            :                 /* This takes possession of cfd and closes it */
     663                 :        120 :                 q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, dry_run, &restart, changes, n_changes);
     664         [ +  - ]:        120 :                 if (r == 0)
     665                 :        120 :                         r = q;
     666         [ +  + ]:        120 :         } while (restart);
     667                 :            : 
     668                 :         76 :         return r;
     669                 :            : }
     670                 :            : 
     671                 :       1248 : static int is_symlink_with_known_name(const UnitFileInstallInfo *i, const char *name) {
     672                 :            :         int r;
     673                 :            : 
     674         [ +  + ]:       1248 :         if (streq(name, i->name))
     675                 :        680 :                 return true;
     676                 :            : 
     677         [ +  + ]:        568 :         if (strv_contains(i->aliases, name))
     678                 :         88 :                 return true;
     679                 :            : 
     680                 :            :         /* Look for template symlink matching DefaultInstance */
     681   [ +  +  +  - ]:        480 :         if (i->default_instance && unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
     682         [ +  + ]:         80 :                 _cleanup_free_ char *s = NULL;
     683                 :            : 
     684                 :         80 :                 r = unit_name_replace_instance(i->name, i->default_instance, &s);
     685         [ -  + ]:         80 :                 if (r < 0) {
     686         [ #  # ]:          0 :                         if (r != -EINVAL)
     687                 :          0 :                                 return r;
     688                 :            : 
     689         [ +  + ]:         80 :                 } else if (streq(name, s))
     690                 :         16 :                         return true;
     691                 :            :         }
     692                 :            : 
     693                 :        464 :         return false;
     694                 :            : }
     695                 :            : 
     696                 :     227792 : static int find_symlinks_fd(
     697                 :            :                 const char *root_dir,
     698                 :            :                 const UnitFileInstallInfo *i,
     699                 :            :                 bool match_aliases,
     700                 :            :                 bool ignore_same_name,
     701                 :            :                 int fd,
     702                 :            :                 const char *path,
     703                 :            :                 const char *config_path,
     704                 :            :                 bool *same_name_link) {
     705                 :            : 
     706                 :     227792 :         _cleanup_closedir_ DIR *d = NULL;
     707                 :            :         struct dirent *de;
     708                 :     227792 :         int r = 0;
     709                 :            : 
     710         [ -  + ]:     227792 :         assert(i);
     711         [ -  + ]:     227792 :         assert(fd >= 0);
     712         [ -  + ]:     227792 :         assert(path);
     713         [ -  + ]:     227792 :         assert(config_path);
     714         [ -  + ]:     227792 :         assert(same_name_link);
     715                 :            : 
     716                 :     227792 :         d = fdopendir(fd);
     717         [ -  + ]:     227792 :         if (!d) {
     718                 :          0 :                 safe_close(fd);
     719                 :          0 :                 return -errno;
     720                 :            :         }
     721                 :            : 
     722   [ +  +  -  +  :    2987224 :         FOREACH_DIRENT(de, d, return -errno) {
                   +  + ]
     723                 :            : 
     724                 :    2307840 :                 dirent_ensure_type(d, de);
     725                 :            : 
     726         [ +  + ]:    2307840 :                 if (de->d_type == DT_DIR) {
     727      [ +  -  + ]:     204448 :                         _cleanup_free_ char *p = NULL;
     728                 :            :                         int nfd, q;
     729                 :            : 
     730                 :     204448 :                         nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
     731         [ -  + ]:     204448 :                         if (nfd < 0) {
     732         [ #  # ]:          0 :                                 if (errno == ENOENT)
     733                 :          0 :                                         continue;
     734                 :            : 
     735         [ #  # ]:          0 :                                 if (r == 0)
     736                 :          0 :                                         r = -errno;
     737                 :          0 :                                 continue;
     738                 :            :                         }
     739                 :            : 
     740                 :     204448 :                         p = path_make_absolute(de->d_name, path);
     741         [ -  + ]:     204448 :                         if (!p) {
     742                 :          0 :                                 safe_close(nfd);
     743                 :          0 :                                 return -ENOMEM;
     744                 :            :                         }
     745                 :            : 
     746                 :            :                         /* This will close nfd, regardless whether it succeeds or not */
     747                 :     204448 :                         q = find_symlinks_fd(root_dir, i, match_aliases, ignore_same_name, nfd,
     748                 :            :                                              p, config_path, same_name_link);
     749         [ +  + ]:     204448 :                         if (q > 0)
     750                 :        972 :                                 return 1;
     751         [ +  - ]:     203476 :                         if (r == 0)
     752                 :     203476 :                                 r = q;
     753                 :            : 
     754         [ +  + ]:    2103392 :                 } else if (de->d_type == DT_LNK) {
     755   [ +  +  -  +  :     635976 :                         _cleanup_free_ char *p = NULL, *dest = NULL;
                   +  - ]
     756                 :     634592 :                         bool found_path = false, found_dest, b = false;
     757                 :            :                         int q;
     758                 :            : 
     759                 :            :                         /* Acquire symlink name */
     760                 :     634592 :                         p = path_make_absolute(de->d_name, path);
     761         [ -  + ]:     634592 :                         if (!p)
     762                 :          0 :                                 return -ENOMEM;
     763                 :            : 
     764                 :            :                         /* Acquire symlink destination */
     765                 :     634592 :                         q = readlink_malloc(p, &dest);
     766         [ -  + ]:     634592 :                         if (q == -ENOENT)
     767                 :          0 :                                 continue;
     768         [ -  + ]:     634592 :                         if (q < 0) {
     769         [ #  # ]:          0 :                                 if (r == 0)
     770                 :          0 :                                         r = q;
     771                 :          0 :                                 continue;
     772                 :            :                         }
     773                 :            : 
     774                 :            :                         /* Make absolute */
     775         [ +  + ]:     634592 :                         if (!path_is_absolute(dest)) {
     776                 :            :                                 char *x;
     777                 :            : 
     778                 :     340592 :                                 x = path_join(root_dir, dest);
     779         [ -  + ]:     340592 :                                 if (!x)
     780                 :          0 :                                         return -ENOMEM;
     781                 :            : 
     782                 :     340592 :                                 free_and_replace(dest, x);
     783                 :            :                         }
     784                 :            : 
     785         [ -  + ]:     634592 :                         assert(unit_name_is_valid(i->name, UNIT_NAME_ANY));
     786         [ +  + ]:     634592 :                         if (!ignore_same_name)
     787                 :            :                                 /* Check if the symlink itself matches what we are looking for.
     788                 :            :                                  *
     789                 :            :                                  * If ignore_same_name is specified, we are in one of the directories which
     790                 :            :                                  * have lower priority than the unit file, and even if a file or symlink with
     791                 :            :                                  * this name was found, we should ignore it. */
     792                 :     617356 :                                  found_path = streq(de->d_name, i->name);
     793                 :            : 
     794                 :            :                         /* Check if what the symlink points to matches what we are looking for */
     795                 :     634592 :                         found_dest = streq(basename(dest), i->name);
     796                 :            : 
     797   [ +  +  +  + ]:     634592 :                         if (found_path && found_dest) {
     798         [ +  - ]:        952 :                                 _cleanup_free_ char *t = NULL;
     799                 :            : 
     800                 :            :                                 /* Filter out same name links in the main
     801                 :            :                                  * config path */
     802                 :        952 :                                 t = path_make_absolute(i->name, config_path);
     803         [ -  + ]:        952 :                                 if (!t)
     804                 :          0 :                                         return -ENOMEM;
     805                 :            : 
     806                 :        952 :                                 b = path_equal(t, p);
     807                 :            :                         }
     808                 :            : 
     809         [ +  + ]:     634592 :                         if (b)
     810                 :         76 :                                 *same_name_link = true;
     811   [ +  +  +  + ]:     634516 :                         else if (found_path || found_dest) {
     812         [ +  + ]:       1848 :                                 if (!match_aliases)
     813                 :        600 :                                         return 1;
     814                 :            : 
     815                 :            :                                 /* Check if symlink name is in the set of names used by [Install] */
     816                 :       1248 :                                 q = is_symlink_with_known_name(i, de->d_name);
     817         [ -  + ]:       1248 :                                 if (q < 0)
     818                 :          0 :                                         return q;
     819         [ +  + ]:       1248 :                                 if (q > 0)
     820                 :        784 :                                         return 1;
     821                 :            :                         }
     822                 :            :                 }
     823                 :            :         }
     824                 :            : 
     825                 :     225436 :         return r;
     826                 :            : }
     827                 :            : 
     828                 :      52876 : static int find_symlinks(
     829                 :            :                 const char *root_dir,
     830                 :            :                 const UnitFileInstallInfo *i,
     831                 :            :                 bool match_name,
     832                 :            :                 bool ignore_same_name,
     833                 :            :                 const char *config_path,
     834                 :            :                 bool *same_name_link) {
     835                 :            : 
     836                 :            :         int fd;
     837                 :            : 
     838         [ -  + ]:      52876 :         assert(i);
     839         [ -  + ]:      52876 :         assert(config_path);
     840         [ -  + ]:      52876 :         assert(same_name_link);
     841                 :            : 
     842                 :      52876 :         fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC);
     843         [ +  + ]:      52876 :         if (fd < 0) {
     844   [ +  -  +  - ]:      29532 :                 if (IN_SET(errno, ENOENT, ENOTDIR, EACCES))
     845                 :      29532 :                         return 0;
     846                 :          0 :                 return -errno;
     847                 :            :         }
     848                 :            : 
     849                 :            :         /* This takes possession of fd and closes it */
     850                 :      23344 :         return find_symlinks_fd(root_dir, i, match_name, ignore_same_name, fd,
     851                 :            :                                 config_path, config_path, same_name_link);
     852                 :            : }
     853                 :            : 
     854                 :       4712 : static int find_symlinks_in_scope(
     855                 :            :                 UnitFileScope scope,
     856                 :            :                 const LookupPaths *paths,
     857                 :            :                 const UnitFileInstallInfo *i,
     858                 :            :                 bool match_name,
     859                 :            :                 UnitFileState *state) {
     860                 :            : 
     861                 :       4712 :         bool same_name_link_runtime = false, same_name_link_config = false;
     862                 :       4712 :         bool enabled_in_runtime = false, enabled_at_all = false;
     863                 :       4712 :         bool ignore_same_name = false;
     864                 :            :         char **p;
     865                 :            :         int r;
     866                 :            : 
     867         [ -  + ]:       4712 :         assert(paths);
     868         [ -  + ]:       4712 :         assert(i);
     869                 :            : 
     870                 :            :         /* As we iterate over the list of search paths in paths->search_path, we may encounter "same name"
     871                 :            :          * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are
     872                 :            :          * effectively masked, so we should ignore them. */
     873                 :            : 
     874   [ +  -  +  + ]:      57064 :         STRV_FOREACH(p, paths->search_path)  {
     875                 :      52876 :                 bool same_name_link = false;
     876                 :            : 
     877                 :      52876 :                 r = find_symlinks(paths->root_dir, i, match_name, ignore_same_name, *p, &same_name_link);
     878         [ -  + ]:      52876 :                 if (r < 0)
     879                 :        524 :                         return r;
     880         [ +  + ]:      52876 :                 if (r > 0) {
     881                 :            :                         /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
     882                 :            : 
     883         [ +  + ]:       1384 :                         if (path_equal_ptr(*p, paths->persistent_config)) {
     884                 :            :                                 /* This is the best outcome, let's return it immediately. */
     885                 :        524 :                                 *state = UNIT_FILE_ENABLED;
     886                 :        524 :                                 return 1;
     887                 :            :                         }
     888                 :            : 
     889                 :            :                         /* look for global enablement of user units */
     890   [ -  +  #  # ]:        860 :                         if (scope == UNIT_FILE_USER && path_is_user_config_dir(*p)) {
     891                 :          0 :                                 *state = UNIT_FILE_ENABLED;
     892                 :          0 :                                 return 1;
     893                 :            :                         }
     894                 :            : 
     895                 :        860 :                         r = path_is_runtime(paths, *p, false);
     896         [ -  + ]:        860 :                         if (r < 0)
     897                 :          0 :                                 return r;
     898         [ +  + ]:        860 :                         if (r > 0)
     899                 :          4 :                                 enabled_in_runtime = true;
     900                 :            :                         else
     901                 :        856 :                                 enabled_at_all = true;
     902                 :            : 
     903         [ +  + ]:      51492 :                 } else if (same_name_link) {
     904         [ +  + ]:         32 :                         if (path_equal_ptr(*p, paths->persistent_config))
     905                 :          8 :                                 same_name_link_config = true;
     906                 :            :                         else {
     907                 :         24 :                                 r = path_is_runtime(paths, *p, false);
     908         [ -  + ]:         24 :                                 if (r < 0)
     909                 :          0 :                                         return r;
     910         [ -  + ]:         24 :                                 if (r > 0)
     911                 :          0 :                                         same_name_link_runtime = true;
     912                 :            :                         }
     913                 :            :                 }
     914                 :            : 
     915                 :            :                 /* Check if next iteration will be "below" the unit file (either a regular file
     916                 :            :                  * or a symlink), and hence should be ignored */
     917   [ +  +  +  + ]:      52352 :                 if (!ignore_same_name && path_startswith(i->path, *p))
     918                 :       4156 :                         ignore_same_name = true;
     919                 :            :         }
     920                 :            : 
     921         [ +  + ]:       4188 :         if (enabled_in_runtime) {
     922                 :          4 :                 *state = UNIT_FILE_ENABLED_RUNTIME;
     923                 :          4 :                 return 1;
     924                 :            :         }
     925                 :            : 
     926                 :            :         /* Here's a special rule: if the unit we are looking for is an instance, and it symlinked in the search path
     927                 :            :          * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only
     928                 :            :          * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate
     929                 :            :          * something, and hence are a much stronger concept. */
     930   [ +  +  +  + ]:       4184 :         if (enabled_at_all && unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) {
     931                 :          4 :                 *state = UNIT_FILE_STATIC;
     932                 :          4 :                 return 1;
     933                 :            :         }
     934                 :            : 
     935                 :            :         /* Hmm, we didn't find it, but maybe we found the same name
     936                 :            :          * link? */
     937         [ +  + ]:       4180 :         if (same_name_link_config) {
     938                 :          8 :                 *state = UNIT_FILE_LINKED;
     939                 :          8 :                 return 1;
     940                 :            :         }
     941         [ -  + ]:       4172 :         if (same_name_link_runtime) {
     942                 :          0 :                 *state = UNIT_FILE_LINKED_RUNTIME;
     943                 :          0 :                 return 1;
     944                 :            :         }
     945                 :            : 
     946                 :       4172 :         return 0;
     947                 :            : }
     948                 :            : 
     949                 :       4100 : static void install_info_free(UnitFileInstallInfo *i) {
     950                 :            : 
     951         [ -  + ]:       4100 :         if (!i)
     952                 :          0 :                 return;
     953                 :            : 
     954                 :       4100 :         free(i->name);
     955                 :       4100 :         free(i->path);
     956                 :       4100 :         strv_free(i->aliases);
     957                 :       4100 :         strv_free(i->wanted_by);
     958                 :       4100 :         strv_free(i->required_by);
     959                 :       4100 :         strv_free(i->also);
     960                 :       4100 :         free(i->default_instance);
     961                 :       4100 :         free(i->symlink_target);
     962                 :       4100 :         free(i);
     963                 :            : }
     964                 :            : 
     965                 :       3316 : static void install_context_done(InstallContext *c) {
     966         [ -  + ]:       3316 :         assert(c);
     967                 :            : 
     968         [ +  + ]:       7120 :         c->will_process = ordered_hashmap_free_with_destructor(c->will_process, install_info_free);
     969         [ +  + ]:       3612 :         c->have_processed = ordered_hashmap_free_with_destructor(c->have_processed, install_info_free);
     970                 :       3316 : }
     971                 :            : 
     972                 :       4512 : static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) {
     973                 :            :         UnitFileInstallInfo *i;
     974                 :            : 
     975                 :       4512 :         i = ordered_hashmap_get(c->have_processed, name);
     976         [ -  + ]:       4512 :         if (i)
     977                 :          0 :                 return i;
     978                 :            : 
     979                 :       4512 :         return ordered_hashmap_get(c->will_process, name);
     980                 :            : }
     981                 :            : 
     982                 :        124 : static int install_info_may_process(
     983                 :            :                 const UnitFileInstallInfo *i,
     984                 :            :                 const LookupPaths *paths,
     985                 :            :                 UnitFileChange **changes,
     986                 :            :                 size_t *n_changes) {
     987         [ -  + ]:        124 :         assert(i);
     988         [ -  + ]:        124 :         assert(paths);
     989                 :            : 
     990                 :            :         /* Checks whether the loaded unit file is one we should process, or is masked,
     991                 :            :          * transient or generated and thus not subject to enable/disable operations. */
     992                 :            : 
     993         [ +  + ]:        124 :         if (i->type == UNIT_FILE_TYPE_MASKED) {
     994                 :          4 :                 unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL);
     995                 :          4 :                 return -ERFKILL;
     996                 :            :         }
     997   [ +  -  -  + ]:        240 :         if (path_is_generator(paths, i->path) ||
     998                 :        120 :             path_is_transient(paths, i->path)) {
     999                 :          0 :                 unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL);
    1000                 :          0 :                 return -EADDRNOTAVAIL;
    1001                 :            :         }
    1002                 :            : 
    1003                 :        120 :         return 0;
    1004                 :            : }
    1005                 :            : 
    1006                 :            : /**
    1007                 :            :  * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process
    1008                 :            :  * hashmap, or retrieves the existing one if already present.
    1009                 :            :  *
    1010                 :            :  * Returns negative on error, 0 if the unit was already known, 1 otherwise.
    1011                 :            :  */
    1012                 :       4136 : static int install_info_add(
    1013                 :            :                 InstallContext *c,
    1014                 :            :                 const char *name,
    1015                 :            :                 const char *path,
    1016                 :            :                 bool auxiliary,
    1017                 :            :                 UnitFileInstallInfo **ret) {
    1018                 :            : 
    1019                 :       4136 :         UnitFileInstallInfo *i = NULL;
    1020                 :            :         int r;
    1021                 :            : 
    1022         [ -  + ]:       4136 :         assert(c);
    1023   [ +  +  -  + ]:       4136 :         assert(name || path);
    1024                 :            : 
    1025         [ +  + ]:       4136 :         if (!name)
    1026                 :          4 :                 name = basename(path);
    1027                 :            : 
    1028         [ -  + ]:       4136 :         if (!unit_name_is_valid(name, UNIT_NAME_ANY))
    1029                 :          0 :                 return -EINVAL;
    1030                 :            : 
    1031                 :       4136 :         i = install_info_find(c, name);
    1032         [ +  + ]:       4136 :         if (i) {
    1033   [ +  +  +  - ]:         36 :                 i->auxiliary = i->auxiliary && auxiliary;
    1034                 :            : 
    1035         [ +  - ]:         36 :                 if (ret)
    1036                 :         36 :                         *ret = i;
    1037                 :         36 :                 return 0;
    1038                 :            :         }
    1039                 :            : 
    1040                 :       4100 :         r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops);
    1041         [ -  + ]:       4100 :         if (r < 0)
    1042                 :          0 :                 return r;
    1043                 :            : 
    1044                 :       4100 :         i = new(UnitFileInstallInfo, 1);
    1045         [ -  + ]:       4100 :         if (!i)
    1046                 :          0 :                 return -ENOMEM;
    1047                 :            : 
    1048                 :       4100 :         *i = (UnitFileInstallInfo) {
    1049                 :            :                 .type = _UNIT_FILE_TYPE_INVALID,
    1050                 :            :                 .auxiliary = auxiliary,
    1051                 :            :         };
    1052                 :            : 
    1053                 :       4100 :         i->name = strdup(name);
    1054         [ -  + ]:       4100 :         if (!i->name) {
    1055                 :          0 :                 r = -ENOMEM;
    1056                 :          0 :                 goto fail;
    1057                 :            :         }
    1058                 :            : 
    1059         [ +  + ]:       4100 :         if (path) {
    1060                 :          4 :                 i->path = strdup(path);
    1061         [ -  + ]:          4 :                 if (!i->path) {
    1062                 :          0 :                         r = -ENOMEM;
    1063                 :          0 :                         goto fail;
    1064                 :            :                 }
    1065                 :            :         }
    1066                 :            : 
    1067                 :       4100 :         r = ordered_hashmap_put(c->will_process, i->name, i);
    1068         [ -  + ]:       4100 :         if (r < 0)
    1069                 :          0 :                 goto fail;
    1070                 :            : 
    1071         [ +  + ]:       4100 :         if (ret)
    1072                 :       4060 :                 *ret = i;
    1073                 :            : 
    1074                 :       4100 :         return 1;
    1075                 :            : 
    1076                 :          0 : fail:
    1077                 :          0 :         install_info_free(i);
    1078                 :          0 :         return r;
    1079                 :            : }
    1080                 :            : 
    1081                 :        144 : static int config_parse_alias(
    1082                 :            :                 const char *unit,
    1083                 :            :                 const char *filename,
    1084                 :            :                 unsigned line,
    1085                 :            :                 const char *section,
    1086                 :            :                 unsigned section_line,
    1087                 :            :                 const char *lvalue,
    1088                 :            :                 int ltype,
    1089                 :            :                 const char *rvalue,
    1090                 :            :                 void *data,
    1091                 :            :                 void *userdata) {
    1092                 :            : 
    1093                 :            :         UnitType type;
    1094                 :            : 
    1095         [ -  + ]:        144 :         assert(unit);
    1096         [ -  + ]:        144 :         assert(filename);
    1097         [ -  + ]:        144 :         assert(lvalue);
    1098         [ -  + ]:        144 :         assert(rvalue);
    1099                 :            : 
    1100                 :        144 :         type = unit_name_to_type(unit);
    1101         [ -  + ]:        144 :         if (!unit_type_may_alias(type))
    1102         [ #  # ]:          0 :                 return log_syntax(unit, LOG_WARNING, filename, line, 0,
    1103                 :            :                                   "Alias= is not allowed for %s units, ignoring.",
    1104                 :            :                                   unit_type_to_string(type));
    1105                 :            : 
    1106                 :        144 :         return config_parse_strv(unit, filename, line, section, section_line,
    1107                 :            :                                  lvalue, ltype, rvalue, data, userdata);
    1108                 :            : }
    1109                 :            : 
    1110                 :        184 : static int config_parse_also(
    1111                 :            :                 const char *unit,
    1112                 :            :                 const char *filename,
    1113                 :            :                 unsigned line,
    1114                 :            :                 const char *section,
    1115                 :            :                 unsigned section_line,
    1116                 :            :                 const char *lvalue,
    1117                 :            :                 int ltype,
    1118                 :            :                 const char *rvalue,
    1119                 :            :                 void *data,
    1120                 :            :                 void *userdata) {
    1121                 :            : 
    1122                 :        184 :         UnitFileInstallInfo *info = userdata, *alsoinfo = NULL;
    1123                 :        184 :         InstallContext *c = data;
    1124                 :            :         int r;
    1125                 :            : 
    1126         [ -  + ]:        184 :         assert(unit);
    1127         [ -  + ]:        184 :         assert(filename);
    1128         [ -  + ]:        184 :         assert(lvalue);
    1129         [ -  + ]:        184 :         assert(rvalue);
    1130                 :            : 
    1131                 :        200 :         for (;;) {
    1132   [ +  -  +  +  :        568 :                 _cleanup_free_ char *word = NULL, *printed = NULL;
                   -  + ]
    1133                 :            : 
    1134                 :        384 :                 r = extract_first_word(&rvalue, &word, NULL, 0);
    1135         [ -  + ]:        384 :                 if (r < 0)
    1136                 :          0 :                         return r;
    1137         [ +  + ]:        384 :                 if (r == 0)
    1138                 :        184 :                         break;
    1139                 :            : 
    1140                 :        200 :                 r = install_full_printf(info, word, &printed);
    1141         [ -  + ]:        200 :                 if (r < 0)
    1142                 :          0 :                         return r;
    1143                 :            : 
    1144         [ -  + ]:        200 :                 if (!unit_name_is_valid(printed, UNIT_NAME_ANY))
    1145                 :          0 :                         return -EINVAL;
    1146                 :            : 
    1147                 :        200 :                 r = install_info_add(c, printed, NULL, true, &alsoinfo);
    1148         [ -  + ]:        200 :                 if (r < 0)
    1149                 :          0 :                         return r;
    1150                 :            : 
    1151                 :        200 :                 r = strv_push(&info->also, printed);
    1152         [ -  + ]:        200 :                 if (r < 0)
    1153                 :          0 :                         return r;
    1154                 :            : 
    1155                 :        200 :                 printed = NULL;
    1156                 :            :         }
    1157                 :            : 
    1158                 :        184 :         return 0;
    1159                 :            : }
    1160                 :            : 
    1161                 :        296 : static int config_parse_default_instance(
    1162                 :            :                 const char *unit,
    1163                 :            :                 const char *filename,
    1164                 :            :                 unsigned line,
    1165                 :            :                 const char *section,
    1166                 :            :                 unsigned section_line,
    1167                 :            :                 const char *lvalue,
    1168                 :            :                 int ltype,
    1169                 :            :                 const char *rvalue,
    1170                 :            :                 void *data,
    1171                 :            :                 void *userdata) {
    1172                 :            : 
    1173                 :        296 :         UnitFileInstallInfo *i = data;
    1174                 :        296 :         _cleanup_free_ char *printed = NULL;
    1175                 :            :         int r;
    1176                 :            : 
    1177         [ -  + ]:        296 :         assert(unit);
    1178         [ -  + ]:        296 :         assert(filename);
    1179         [ -  + ]:        296 :         assert(lvalue);
    1180         [ -  + ]:        296 :         assert(rvalue);
    1181                 :            : 
    1182         [ +  + ]:        296 :         if (unit_name_is_valid(unit, UNIT_NAME_INSTANCE))
    1183                 :            :                 /* When enabling an instance, we might be using a template unit file,
    1184                 :            :                  * but we should ignore DefaultInstance silently. */
    1185                 :        200 :                 return 0;
    1186         [ -  + ]:         96 :         if (!unit_name_is_valid(unit, UNIT_NAME_TEMPLATE))
    1187         [ #  # ]:          0 :                 return log_syntax(unit, LOG_WARNING, filename, line, 0,
    1188                 :            :                                   "DefaultInstance= only makes sense for template units, ignoring.");
    1189                 :            : 
    1190                 :         96 :         r = install_full_printf(i, rvalue, &printed);
    1191         [ -  + ]:         96 :         if (r < 0)
    1192                 :          0 :                 return r;
    1193                 :            : 
    1194         [ -  + ]:         96 :         if (!unit_instance_is_valid(printed))
    1195                 :          0 :                 return -EINVAL;
    1196                 :            : 
    1197                 :         96 :         return free_and_replace(i->default_instance, printed);
    1198                 :            : }
    1199                 :            : 
    1200                 :      45176 : static int unit_file_load(
    1201                 :            :                 InstallContext *c,
    1202                 :            :                 UnitFileInstallInfo *info,
    1203                 :            :                 const char *path,
    1204                 :            :                 const char *root_dir,
    1205                 :            :                 SearchFlags flags) {
    1206                 :            : 
    1207                 :     180704 :         const ConfigTableItem items[] = {
    1208                 :      45176 :                 { "Install", "Alias",           config_parse_alias,            0, &info->aliases           },
    1209                 :      45176 :                 { "Install", "WantedBy",        config_parse_strv,             0, &info->wanted_by         },
    1210                 :      45176 :                 { "Install", "RequiredBy",      config_parse_strv,             0, &info->required_by       },
    1211                 :            :                 { "Install", "DefaultInstance", config_parse_default_instance, 0, info                     },
    1212                 :            :                 { "Install", "Also",            config_parse_also,             0, c                        },
    1213                 :            :                 {}
    1214                 :            :         };
    1215                 :            : 
    1216                 :            :         UnitType type;
    1217                 :      45176 :         _cleanup_fclose_ FILE *f = NULL;
    1218                 :      45176 :         _cleanup_close_ int fd = -1;
    1219                 :            :         struct stat st;
    1220                 :            :         int r;
    1221                 :            : 
    1222         [ -  + ]:      45176 :         assert(info);
    1223         [ -  + ]:      45176 :         assert(path);
    1224                 :            : 
    1225         [ +  + ]:      45176 :         if (!(flags & SEARCH_DROPIN)) {
    1226                 :            :                 /* Loading or checking for the main unit file… */
    1227                 :            : 
    1228                 :      45052 :                 type = unit_name_to_type(info->name);
    1229         [ -  + ]:      45052 :                 if (type < 0)
    1230                 :          0 :                         return -EINVAL;
    1231   [ +  +  -  + ]:      45052 :                 if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type))
    1232         [ #  # ]:          0 :                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
    1233                 :            :                                                "Unit type %s cannot be templated.", unit_type_to_string(type));
    1234                 :            : 
    1235         [ +  + ]:      45052 :                 if (!(flags & SEARCH_LOAD)) {
    1236                 :       4624 :                         r = lstat(path, &st);
    1237         [ +  + ]:       4624 :                         if (r < 0)
    1238                 :       4200 :                                 return -errno;
    1239                 :            : 
    1240         [ -  + ]:        424 :                         if (null_or_empty(&st))
    1241                 :          0 :                                 info->type = UNIT_FILE_TYPE_MASKED;
    1242         [ +  + ]:        424 :                         else if (S_ISREG(st.st_mode))
    1243                 :        308 :                                 info->type = UNIT_FILE_TYPE_REGULAR;
    1244         [ +  - ]:        116 :                         else if (S_ISLNK(st.st_mode))
    1245                 :        116 :                                 return -ELOOP;
    1246         [ #  # ]:          0 :                         else if (S_ISDIR(st.st_mode))
    1247                 :          0 :                                 return -EISDIR;
    1248                 :            :                         else
    1249                 :          0 :                                 return -ENOTTY;
    1250                 :            : 
    1251                 :        308 :                         return 0;
    1252                 :            :                 }
    1253                 :            : 
    1254                 :      40428 :                 fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
    1255         [ +  + ]:      40428 :                 if (fd < 0)
    1256                 :      37596 :                         return -errno;
    1257                 :            :         } else {
    1258                 :            :                 /* Operating on a drop-in file. If we aren't supposed to load the unit file drop-ins don't matter, let's hence shortcut this. */
    1259                 :            : 
    1260         [ +  + ]:        124 :                 if (!(flags & SEARCH_LOAD))
    1261                 :          4 :                         return 0;
    1262                 :            : 
    1263                 :        120 :                 fd = chase_symlinks_and_open(path, root_dir, 0, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
    1264         [ -  + ]:        120 :                 if (fd < 0)
    1265                 :          0 :                         return fd;
    1266                 :            :         }
    1267                 :            : 
    1268         [ -  + ]:       2952 :         if (fstat(fd, &st) < 0)
    1269                 :          0 :                 return -errno;
    1270                 :            : 
    1271         [ -  + ]:       2952 :         if (null_or_empty(&st)) {
    1272         [ #  # ]:          0 :                 if ((flags & SEARCH_DROPIN) == 0)
    1273                 :          0 :                         info->type = UNIT_FILE_TYPE_MASKED;
    1274                 :            : 
    1275                 :          0 :                 return 0;
    1276                 :            :         }
    1277                 :            : 
    1278                 :       2952 :         r = stat_verify_regular(&st);
    1279         [ -  + ]:       2952 :         if (r < 0)
    1280                 :          0 :                 return r;
    1281                 :            : 
    1282                 :       2952 :         f = fdopen(fd, "r");
    1283         [ -  + ]:       2952 :         if (!f)
    1284                 :          0 :                 return -errno;
    1285                 :       2952 :         fd = -1;
    1286                 :            : 
    1287                 :            :         /* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
    1288         [ -  + ]:       2952 :         assert(c);
    1289                 :            : 
    1290                 :       2952 :         r = config_parse(info->name, path, f,
    1291                 :            :                          NULL,
    1292                 :            :                          config_item_table_lookup, items,
    1293                 :            :                          CONFIG_PARSE_RELAXED|CONFIG_PARSE_ALLOW_INCLUDE, info);
    1294         [ -  + ]:       2952 :         if (r < 0)
    1295         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to parse %s: %m", info->name);
    1296                 :            : 
    1297         [ +  + ]:       2952 :         if ((flags & SEARCH_DROPIN) == 0)
    1298                 :       2832 :                 info->type = UNIT_FILE_TYPE_REGULAR;
    1299                 :            : 
    1300                 :            :         return
    1301                 :       5904 :                 (int) strv_length(info->aliases) +
    1302                 :       5904 :                 (int) strv_length(info->wanted_by) +
    1303                 :       2952 :                 (int) strv_length(info->required_by);
    1304                 :            : }
    1305                 :            : 
    1306                 :      45176 : static int unit_file_load_or_readlink(
    1307                 :            :                 InstallContext *c,
    1308                 :            :                 UnitFileInstallInfo *info,
    1309                 :            :                 const char *path,
    1310                 :            :                 const char *root_dir,
    1311                 :            :                 SearchFlags flags) {
    1312                 :            : 
    1313                 :      45176 :         _cleanup_free_ char *target = NULL;
    1314                 :            :         int r;
    1315                 :            : 
    1316                 :      45176 :         r = unit_file_load(c, info, path, root_dir, flags);
    1317   [ +  +  -  + ]:      45176 :         if (r != -ELOOP || (flags & SEARCH_DROPIN))
    1318                 :      44532 :                 return r;
    1319                 :            : 
    1320                 :            :         /* This is a symlink, let's read it. */
    1321                 :            : 
    1322                 :        644 :         r = readlink_malloc(path, &target);
    1323         [ -  + ]:        644 :         if (r < 0)
    1324                 :          0 :                 return r;
    1325                 :            : 
    1326         [ +  + ]:        644 :         if (path_equal(target, "/dev/null"))
    1327                 :         24 :                 info->type = UNIT_FILE_TYPE_MASKED;
    1328                 :            :         else {
    1329                 :            :                 const char *bn;
    1330                 :            :                 UnitType a, b;
    1331                 :            : 
    1332                 :        620 :                 bn = basename(target);
    1333                 :            : 
    1334         [ +  + ]:        620 :                 if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) {
    1335                 :            : 
    1336         [ -  + ]:        512 :                         if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN))
    1337                 :          0 :                                 return -EINVAL;
    1338                 :            : 
    1339         [ +  + ]:        108 :                 } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
    1340                 :            : 
    1341         [ -  + ]:         68 :                         if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE))
    1342                 :          0 :                                 return -EINVAL;
    1343                 :            : 
    1344         [ +  - ]:         40 :                 } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) {
    1345                 :            : 
    1346         [ -  + ]:         40 :                         if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE))
    1347                 :          0 :                                 return -EINVAL;
    1348                 :            :                 } else
    1349                 :          0 :                         return -EINVAL;
    1350                 :            : 
    1351                 :            :                 /* Enforce that the symlink destination does not
    1352                 :            :                  * change the unit file type. */
    1353                 :            : 
    1354                 :        620 :                 a = unit_name_to_type(info->name);
    1355                 :        620 :                 b = unit_name_to_type(bn);
    1356   [ +  -  +  -  :        620 :                 if (a < 0 || b < 0 || a != b)
                   -  + ]
    1357                 :          0 :                         return -EINVAL;
    1358                 :            : 
    1359         [ +  + ]:        620 :                 if (path_is_absolute(target))
    1360                 :            :                         /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */
    1361                 :        208 :                         info->symlink_target = path_join(root_dir, target);
    1362                 :            :                 else
    1363                 :            :                         /* This is a relative path, take it relative to the dir the symlink is located in. */
    1364                 :        412 :                         info->symlink_target = file_in_same_dir(path, target);
    1365         [ -  + ]:        620 :                 if (!info->symlink_target)
    1366                 :          0 :                         return -ENOMEM;
    1367                 :            : 
    1368                 :        620 :                 info->type = UNIT_FILE_TYPE_SYMLINK;
    1369                 :            :         }
    1370                 :            : 
    1371                 :        644 :         return 0;
    1372                 :            : }
    1373                 :            : 
    1374                 :       4200 : static int unit_file_search(
    1375                 :            :                 InstallContext *c,
    1376                 :            :                 UnitFileInstallInfo *info,
    1377                 :            :                 const LookupPaths *paths,
    1378                 :            :                 SearchFlags flags) {
    1379                 :            : 
    1380                 :       4200 :         const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL;
    1381                 :       4200 :         _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
    1382                 :       4200 :         _cleanup_free_ char *template = NULL;
    1383                 :       4200 :         bool found_unit = false;
    1384                 :            :         int r, result;
    1385                 :            :         char **p;
    1386                 :            : 
    1387         [ -  + ]:       4200 :         assert(info);
    1388         [ -  + ]:       4200 :         assert(paths);
    1389                 :            : 
    1390                 :            :         /* Was this unit already loaded? */
    1391         [ +  + ]:       4200 :         if (info->type != _UNIT_FILE_TYPE_INVALID)
    1392                 :        276 :                 return 0;
    1393                 :            : 
    1394         [ +  + ]:       3924 :         if (info->path)
    1395                 :          4 :                 return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags);
    1396                 :            : 
    1397         [ -  + ]:       3920 :         assert(info->name);
    1398                 :            : 
    1399         [ +  + ]:       3920 :         if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) {
    1400                 :        320 :                 r = unit_name_template(info->name, &template);
    1401         [ -  + ]:        320 :                 if (r < 0)
    1402                 :          0 :                         return r;
    1403                 :            :         }
    1404                 :            : 
    1405   [ +  -  +  + ]:      42036 :         STRV_FOREACH(p, paths->search_path) {
    1406      [ +  -  + ]:      41500 :                 _cleanup_free_ char *path = NULL;
    1407                 :            : 
    1408                 :      41500 :                 path = path_join(*p, info->name);
    1409         [ -  + ]:      41500 :                 if (!path)
    1410                 :          0 :                         return -ENOMEM;
    1411                 :            : 
    1412                 :      41500 :                 r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
    1413         [ +  + ]:      41500 :                 if (r >= 0) {
    1414                 :       3384 :                         info->path = TAKE_PTR(path);
    1415                 :       3384 :                         result = r;
    1416                 :       3384 :                         found_unit = true;
    1417                 :       3384 :                         break;
    1418   [ +  -  -  + ]:      38116 :                 } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
    1419                 :          0 :                         return r;
    1420                 :            :         }
    1421                 :            : 
    1422   [ +  +  +  + ]:       3920 :         if (!found_unit && template) {
    1423                 :            : 
    1424                 :            :                 /* Unit file doesn't exist, however instance
    1425                 :            :                  * enablement was requested.  We will check if it is
    1426                 :            :                  * possible to load template unit file. */
    1427                 :            : 
    1428   [ +  -  +  + ]:       3464 :                 STRV_FOREACH(p, paths->search_path) {
    1429      [ +  -  + ]:       3448 :                         _cleanup_free_ char *path = NULL;
    1430                 :            : 
    1431                 :       3448 :                         path = path_join(*p, template);
    1432         [ -  + ]:       3448 :                         if (!path)
    1433                 :          0 :                                 return -ENOMEM;
    1434                 :            : 
    1435                 :       3448 :                         r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
    1436         [ +  + ]:       3448 :                         if (r >= 0) {
    1437                 :        296 :                                 info->path = TAKE_PTR(path);
    1438                 :        296 :                                 result = r;
    1439                 :        296 :                                 found_unit = true;
    1440                 :        296 :                                 break;
    1441   [ +  -  -  + ]:       3152 :                         } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES))
    1442                 :          0 :                                 return r;
    1443                 :            :                 }
    1444                 :            :         }
    1445                 :            : 
    1446         [ +  + ]:       3920 :         if (!found_unit)
    1447   [ +  -  +  + ]:        240 :                 return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
    1448                 :            :                                        "Cannot find unit %s%s%s.",
    1449                 :            :                                        info->name, template ? " or " : "", strempty(template));
    1450                 :            : 
    1451         [ +  + ]:       3680 :         if (info->type == UNIT_FILE_TYPE_MASKED)
    1452                 :         24 :                 return result;
    1453                 :            : 
    1454                 :            :         /* Search for drop-in directories */
    1455                 :            : 
    1456   [ +  +  +  -  :      18280 :         dropin_dir_name = strjoina(info->name, ".d");
          -  +  -  +  +  
                +  +  - ]
    1457   [ +  -  +  + ]:      47484 :         STRV_FOREACH(p, paths->search_path) {
    1458                 :            :                 char *path;
    1459                 :            : 
    1460                 :      43828 :                 path = path_join(*p, dropin_dir_name);
    1461         [ -  + ]:      43828 :                 if (!path)
    1462                 :          0 :                         return -ENOMEM;
    1463                 :            : 
    1464                 :      43828 :                 r = strv_consume(&dirs, path);
    1465         [ -  + ]:      43828 :                 if (r < 0)
    1466                 :          0 :                         return r;
    1467                 :            :         }
    1468                 :            : 
    1469         [ +  + ]:       3656 :         if (template) {
    1470   [ +  +  +  -  :       1520 :                 dropin_template_dir_name = strjoina(template, ".d");
          -  +  -  +  +  
                +  +  - ]
    1471   [ +  -  +  + ]:       3952 :                 STRV_FOREACH(p, paths->search_path) {
    1472                 :            :                         char *path;
    1473                 :            : 
    1474                 :       3648 :                         path = path_join(*p, dropin_template_dir_name);
    1475         [ -  + ]:       3648 :                         if (!path)
    1476                 :          0 :                                 return -ENOMEM;
    1477                 :            : 
    1478                 :       3648 :                         r = strv_consume(&dirs, path);
    1479         [ -  + ]:       3648 :                         if (r < 0)
    1480                 :          0 :                                 return r;
    1481                 :            :                 }
    1482                 :            :         }
    1483                 :            : 
    1484                 :            :         /* Load drop-in conf files */
    1485                 :            : 
    1486                 :       3656 :         r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) dirs);
    1487         [ -  + ]:       3656 :         if (r < 0)
    1488         [ #  # ]:          0 :                 return log_debug_errno(r, "Failed to get list of conf files: %m");
    1489                 :            : 
    1490   [ +  -  +  + ]:       3780 :         STRV_FOREACH(p, files) {
    1491                 :        124 :                 r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags | SEARCH_DROPIN);
    1492         [ -  + ]:        124 :                 if (r < 0)
    1493         [ #  # ]:          0 :                         return log_debug_errno(r, "Failed to load conf file %s: %m", *p);
    1494                 :            :         }
    1495                 :            : 
    1496                 :       3656 :         return result;
    1497                 :            : }
    1498                 :            : 
    1499                 :        656 : static int install_info_follow(
    1500                 :            :                 InstallContext *c,
    1501                 :            :                 UnitFileInstallInfo *i,
    1502                 :            :                 const char *root_dir,
    1503                 :            :                 SearchFlags flags,
    1504                 :            :                 bool ignore_different_name) {
    1505                 :            : 
    1506         [ -  + ]:        656 :         assert(c);
    1507         [ -  + ]:        656 :         assert(i);
    1508                 :            : 
    1509         [ -  + ]:        656 :         if (i->type != UNIT_FILE_TYPE_SYMLINK)
    1510                 :          0 :                 return -EINVAL;
    1511         [ -  + ]:        656 :         if (!i->symlink_target)
    1512                 :          0 :                 return -EINVAL;
    1513                 :            : 
    1514                 :            :         /* If the basename doesn't match, the caller should add a
    1515                 :            :          * complete new entry for this. */
    1516                 :            : 
    1517   [ +  +  +  + ]:        656 :         if (!ignore_different_name && !streq(basename(i->symlink_target), i->name))
    1518                 :        556 :                 return -EXDEV;
    1519                 :            : 
    1520                 :        100 :         free_and_replace(i->path, i->symlink_target);
    1521                 :        100 :         i->type = _UNIT_FILE_TYPE_INVALID;
    1522                 :            : 
    1523                 :        100 :         return unit_file_load_or_readlink(c, i, i->path, root_dir, flags);
    1524                 :            : }
    1525                 :            : 
    1526                 :            : /**
    1527                 :            :  * Search for the unit file. If the unit name is a symlink, follow the symlink to the
    1528                 :            :  * target, maybe more than once. Propagate the instance name if present.
    1529                 :            :  */
    1530                 :       3640 : static int install_info_traverse(
    1531                 :            :                 UnitFileScope scope,
    1532                 :            :                 InstallContext *c,
    1533                 :            :                 const LookupPaths *paths,
    1534                 :            :                 UnitFileInstallInfo *start,
    1535                 :            :                 SearchFlags flags,
    1536                 :            :                 UnitFileInstallInfo **ret) {
    1537                 :            : 
    1538                 :            :         UnitFileInstallInfo *i;
    1539                 :       3640 :         unsigned k = 0;
    1540                 :            :         int r;
    1541                 :            : 
    1542         [ -  + ]:       3640 :         assert(paths);
    1543         [ -  + ]:       3640 :         assert(start);
    1544         [ -  + ]:       3640 :         assert(c);
    1545                 :            : 
    1546                 :       3640 :         r = unit_file_search(c, start, paths, flags);
    1547         [ +  + ]:       3640 :         if (r < 0)
    1548                 :        240 :                 return r;
    1549                 :            : 
    1550                 :       3400 :         i = start;
    1551         [ +  + ]:       4052 :         while (i->type == UNIT_FILE_TYPE_SYMLINK) {
    1552                 :            :                 /* Follow the symlink */
    1553                 :            : 
    1554         [ -  + ]:        652 :                 if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
    1555                 :          0 :                         return -ELOOP;
    1556                 :            : 
    1557         [ +  + ]:        652 :                 if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) {
    1558                 :         28 :                         r = path_is_config(paths, i->path, true);
    1559         [ -  + ]:         28 :                         if (r < 0)
    1560                 :          0 :                                 return r;
    1561         [ -  + ]:         28 :                         if (r > 0)
    1562                 :          0 :                                 return -ELOOP;
    1563                 :            :                 }
    1564                 :            : 
    1565                 :        652 :                 r = install_info_follow(c, i, paths->root_dir, flags, false);
    1566         [ +  + ]:        652 :                 if (r == -EXDEV) {
    1567      [ +  -  + ]:        556 :                         _cleanup_free_ char *buffer = NULL;
    1568                 :            :                         const char *bn;
    1569                 :            : 
    1570                 :            :                         /* Target has a different name, create a new
    1571                 :            :                          * install info object for that, and continue
    1572                 :            :                          * with that. */
    1573                 :            : 
    1574                 :        556 :                         bn = basename(i->symlink_target);
    1575                 :            : 
    1576         [ +  + ]:        556 :                         if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) &&
    1577         [ +  - ]:         72 :                             unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) {
    1578                 :            : 
    1579      [ +  -  + ]:         72 :                                 _cleanup_free_ char *instance = NULL;
    1580                 :            : 
    1581                 :         72 :                                 r = unit_name_to_instance(i->name, &instance);
    1582         [ -  + ]:         72 :                                 if (r < 0)
    1583                 :          0 :                                         return r;
    1584                 :            : 
    1585                 :         72 :                                 r = unit_name_replace_instance(bn, instance, &buffer);
    1586         [ -  + ]:         72 :                                 if (r < 0)
    1587                 :          0 :                                         return r;
    1588                 :            : 
    1589         [ +  + ]:         72 :                                 if (streq(buffer, i->name)) {
    1590                 :            : 
    1591                 :            :                                         /* We filled in the instance, and the target stayed the same? If so, then let's
    1592                 :            :                                          * honour the link as it is. */
    1593                 :            : 
    1594                 :          4 :                                         r = install_info_follow(c, i, paths->root_dir, flags, true);
    1595         [ -  + ]:          4 :                                         if (r < 0)
    1596                 :          0 :                                                 return r;
    1597                 :            : 
    1598                 :          4 :                                         continue;
    1599                 :            :                                 }
    1600                 :            : 
    1601                 :         68 :                                 bn = buffer;
    1602                 :            :                         }
    1603                 :            : 
    1604                 :        552 :                         r = install_info_add(c, bn, NULL, false, &i);
    1605         [ -  + ]:        552 :                         if (r < 0)
    1606                 :          0 :                                 return r;
    1607                 :            : 
    1608                 :            :                         /* Try again, with the new target we found. */
    1609                 :        552 :                         r = unit_file_search(c, i, paths, flags);
    1610         [ -  + ]:        552 :                         if (r == -ENOENT)
    1611                 :            :                                 /* Translate error code to highlight this specific case */
    1612                 :          0 :                                 return -ENOLINK;
    1613                 :            :                 }
    1614                 :            : 
    1615         [ -  + ]:        648 :                 if (r < 0)
    1616                 :          0 :                         return r;
    1617                 :            :         }
    1618                 :            : 
    1619         [ +  + ]:       3400 :         if (ret)
    1620                 :       3100 :                 *ret = i;
    1621                 :            : 
    1622                 :       3400 :         return 0;
    1623                 :            : }
    1624                 :            : 
    1625                 :            : /**
    1626                 :            :  * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/")
    1627                 :            :  * or the name (otherwise). root_dir is prepended to the path.
    1628                 :            :  */
    1629                 :       3344 : static int install_info_add_auto(
    1630                 :            :                 InstallContext *c,
    1631                 :            :                 const LookupPaths *paths,
    1632                 :            :                 const char *name_or_path,
    1633                 :            :                 UnitFileInstallInfo **ret) {
    1634                 :            : 
    1635         [ -  + ]:       3344 :         assert(c);
    1636         [ -  + ]:       3344 :         assert(name_or_path);
    1637                 :            : 
    1638         [ +  + ]:       3344 :         if (path_is_absolute(name_or_path)) {
    1639                 :            :                 const char *pp;
    1640                 :            : 
    1641   [ +  -  -  +  :          4 :                 pp = prefix_roota(paths->root_dir, name_or_path);
          -  +  -  +  -  
          +  +  -  -  +  
                   -  + ]
    1642                 :            : 
    1643                 :          4 :                 return install_info_add(c, NULL, pp, false, ret);
    1644                 :            :         } else
    1645                 :       3340 :                 return install_info_add(c, name_or_path, NULL, false, ret);
    1646                 :            : }
    1647                 :            : 
    1648                 :       3344 : static int install_info_discover(
    1649                 :            :                 UnitFileScope scope,
    1650                 :            :                 InstallContext *c,
    1651                 :            :                 const LookupPaths *paths,
    1652                 :            :                 const char *name,
    1653                 :            :                 SearchFlags flags,
    1654                 :            :                 UnitFileInstallInfo **ret,
    1655                 :            :                 UnitFileChange **changes,
    1656                 :            :                 size_t *n_changes) {
    1657                 :            : 
    1658                 :            :         UnitFileInstallInfo *i;
    1659                 :            :         int r;
    1660                 :            : 
    1661         [ -  + ]:       3344 :         assert(c);
    1662         [ -  + ]:       3344 :         assert(paths);
    1663         [ -  + ]:       3344 :         assert(name);
    1664                 :            : 
    1665                 :       3344 :         r = install_info_add_auto(c, paths, name, &i);
    1666         [ +  - ]:       3344 :         if (r >= 0)
    1667                 :       3344 :                 r = install_info_traverse(scope, c, paths, i, flags, ret);
    1668                 :            : 
    1669         [ +  + ]:       3344 :         if (r < 0)
    1670                 :        240 :                 unit_file_changes_add(changes, n_changes, r, name, NULL);
    1671                 :       3344 :         return r;
    1672                 :            : }
    1673                 :            : 
    1674                 :        124 : static int install_info_discover_and_check(
    1675                 :            :                         UnitFileScope scope,
    1676                 :            :                         InstallContext *c,
    1677                 :            :                         const LookupPaths *paths,
    1678                 :            :                         const char *name,
    1679                 :            :                         SearchFlags flags,
    1680                 :            :                         UnitFileInstallInfo **ret,
    1681                 :            :                         UnitFileChange **changes,
    1682                 :            :                         size_t *n_changes) {
    1683                 :            : 
    1684                 :            :         int r;
    1685                 :            : 
    1686                 :        124 :         r = install_info_discover(scope, c, paths, name, flags, ret, changes, n_changes);
    1687         [ +  + ]:        124 :         if (r < 0)
    1688                 :          4 :                 return r;
    1689                 :            : 
    1690         [ +  - ]:        120 :         return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes);
    1691                 :            : }
    1692                 :            : 
    1693                 :        120 : static int install_info_symlink_alias(
    1694                 :            :                 UnitFileInstallInfo *i,
    1695                 :            :                 const LookupPaths *paths,
    1696                 :            :                 const char *config_path,
    1697                 :            :                 bool force,
    1698                 :            :                 UnitFileChange **changes,
    1699                 :            :                 size_t *n_changes) {
    1700                 :            : 
    1701                 :            :         char **s;
    1702                 :        120 :         int r = 0, q;
    1703                 :            : 
    1704         [ -  + ]:        120 :         assert(i);
    1705         [ -  + ]:        120 :         assert(paths);
    1706         [ -  + ]:        120 :         assert(config_path);
    1707                 :            : 
    1708   [ -  +  #  # ]:        120 :         STRV_FOREACH(s, i->aliases) {
    1709   [ #  #  #  # ]:          0 :                 _cleanup_free_ char *alias_path = NULL, *dst = NULL;
    1710                 :            : 
    1711                 :          0 :                 q = install_full_printf(i, *s, &dst);
    1712         [ #  # ]:          0 :                 if (q < 0)
    1713                 :          0 :                         return q;
    1714                 :            : 
    1715                 :          0 :                 alias_path = path_make_absolute(dst, config_path);
    1716         [ #  # ]:          0 :                 if (!alias_path)
    1717                 :          0 :                         return -ENOMEM;
    1718                 :            : 
    1719                 :          0 :                 q = create_symlink(paths, i->path, alias_path, force, changes, n_changes);
    1720         [ #  # ]:          0 :                 if (r == 0)
    1721                 :          0 :                         r = q;
    1722                 :            :         }
    1723                 :            : 
    1724                 :        120 :         return r;
    1725                 :            : }
    1726                 :            : 
    1727                 :        240 : static int install_info_symlink_wants(
    1728                 :            :                 UnitFileInstallInfo *i,
    1729                 :            :                 const LookupPaths *paths,
    1730                 :            :                 const char *config_path,
    1731                 :            :                 char **list,
    1732                 :            :                 const char *suffix,
    1733                 :            :                 UnitFileChange **changes,
    1734                 :            :                 size_t *n_changes) {
    1735                 :            : 
    1736                 :        240 :         _cleanup_free_ char *buf = NULL;
    1737                 :            :         const char *n;
    1738                 :            :         char **s;
    1739                 :        240 :         int r = 0, q;
    1740                 :            : 
    1741         [ -  + ]:        240 :         assert(i);
    1742         [ -  + ]:        240 :         assert(paths);
    1743         [ -  + ]:        240 :         assert(config_path);
    1744                 :            : 
    1745         [ +  + ]:        240 :         if (strv_isempty(list))
    1746                 :        128 :                 return 0;
    1747                 :            : 
    1748         [ +  + ]:        112 :         if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) {
    1749                 :          8 :                 UnitFileInstallInfo instance = {
    1750                 :            :                         .type = _UNIT_FILE_TYPE_INVALID,
    1751                 :            :                 };
    1752         [ +  - ]:          8 :                 _cleanup_free_ char *path = NULL;
    1753                 :            : 
    1754                 :            :                 /* If this is a template, and we have no instance, don't do anything */
    1755         [ -  + ]:          8 :                 if (!i->default_instance)
    1756                 :          0 :                         return 1;
    1757                 :            : 
    1758                 :          8 :                 r = unit_name_replace_instance(i->name, i->default_instance, &buf);
    1759         [ -  + ]:          8 :                 if (r < 0)
    1760                 :          0 :                         return r;
    1761                 :            : 
    1762                 :          8 :                 instance.name = buf;
    1763                 :          8 :                 r = unit_file_search(NULL, &instance, paths, SEARCH_FOLLOW_CONFIG_SYMLINKS);
    1764         [ -  + ]:          8 :                 if (r < 0)
    1765                 :          0 :                         return r;
    1766                 :            : 
    1767                 :          8 :                 path = TAKE_PTR(instance.path);
    1768                 :            : 
    1769         [ -  + ]:          8 :                 if (instance.type == UNIT_FILE_TYPE_MASKED) {
    1770                 :          0 :                         unit_file_changes_add(changes, n_changes, -ERFKILL, path, NULL);
    1771                 :          0 :                         return -ERFKILL;
    1772                 :            :                 }
    1773                 :            : 
    1774                 :          8 :                 n = buf;
    1775                 :            :         } else
    1776                 :        104 :                 n = i->name;
    1777                 :            : 
    1778   [ +  -  +  + ]:        244 :         STRV_FOREACH(s, list) {
    1779   [ +  -  -  +  :        132 :                 _cleanup_free_ char *path = NULL, *dst = NULL;
                   -  - ]
    1780                 :            : 
    1781                 :        132 :                 q = install_full_printf(i, *s, &dst);
    1782         [ -  + ]:        132 :                 if (q < 0)
    1783                 :          0 :                         return q;
    1784                 :            : 
    1785         [ -  + ]:        132 :                 if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) {
    1786                 :          0 :                         r = -EINVAL;
    1787                 :          0 :                         continue;
    1788                 :            :                 }
    1789                 :            : 
    1790                 :        132 :                 path = strjoin(config_path, "/", dst, suffix, n);
    1791         [ -  + ]:        132 :                 if (!path)
    1792                 :          0 :                         return -ENOMEM;
    1793                 :            : 
    1794                 :        132 :                 q = create_symlink(paths, i->path, path, true, changes, n_changes);
    1795         [ +  + ]:        132 :                 if (r == 0)
    1796                 :        112 :                         r = q;
    1797                 :            :         }
    1798                 :            : 
    1799                 :        112 :         return r;
    1800                 :            : }
    1801                 :            : 
    1802                 :        120 : static int install_info_symlink_link(
    1803                 :            :                 UnitFileInstallInfo *i,
    1804                 :            :                 const LookupPaths *paths,
    1805                 :            :                 const char *config_path,
    1806                 :            :                 bool force,
    1807                 :            :                 UnitFileChange **changes,
    1808                 :            :                 size_t *n_changes) {
    1809                 :            : 
    1810                 :        120 :         _cleanup_free_ char *path = NULL;
    1811                 :            :         int r;
    1812                 :            : 
    1813         [ -  + ]:        120 :         assert(i);
    1814         [ -  + ]:        120 :         assert(paths);
    1815         [ -  + ]:        120 :         assert(config_path);
    1816         [ -  + ]:        120 :         assert(i->path);
    1817                 :            : 
    1818                 :        120 :         r = in_search_path(paths, i->path);
    1819         [ -  + ]:        120 :         if (r < 0)
    1820                 :          0 :                 return r;
    1821         [ +  + ]:        120 :         if (r > 0)
    1822                 :        108 :                 return 0;
    1823                 :            : 
    1824                 :         12 :         path = path_join(config_path, i->name);
    1825         [ -  + ]:         12 :         if (!path)
    1826                 :          0 :                 return -ENOMEM;
    1827                 :            : 
    1828                 :         12 :         return create_symlink(paths, i->path, path, force, changes, n_changes);
    1829                 :            : }
    1830                 :            : 
    1831                 :        120 : static int install_info_apply(
    1832                 :            :                 UnitFileInstallInfo *i,
    1833                 :            :                 const LookupPaths *paths,
    1834                 :            :                 const char *config_path,
    1835                 :            :                 bool force,
    1836                 :            :                 UnitFileChange **changes,
    1837                 :            :                 size_t *n_changes) {
    1838                 :            : 
    1839                 :            :         int r, q;
    1840                 :            : 
    1841         [ -  + ]:        120 :         assert(i);
    1842         [ -  + ]:        120 :         assert(paths);
    1843         [ -  + ]:        120 :         assert(config_path);
    1844                 :            : 
    1845         [ -  + ]:        120 :         if (i->type != UNIT_FILE_TYPE_REGULAR)
    1846                 :          0 :                 return 0;
    1847                 :            : 
    1848                 :        120 :         r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes);
    1849                 :            : 
    1850                 :        120 :         q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes);
    1851         [ +  - ]:        120 :         if (r == 0)
    1852                 :        120 :                 r = q;
    1853                 :            : 
    1854                 :        120 :         q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes);
    1855         [ +  + ]:        120 :         if (r == 0)
    1856                 :          8 :                 r = q;
    1857                 :            : 
    1858                 :        120 :         q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
    1859                 :            :         /* Do not count links to the unit file towards the "carries_install_info" count */
    1860   [ +  +  -  + ]:        120 :         if (r == 0 && q < 0)
    1861                 :          0 :                 r = q;
    1862                 :            : 
    1863                 :        120 :         return r;
    1864                 :            : }
    1865                 :            : 
    1866                 :        108 : static int install_context_apply(
    1867                 :            :                 UnitFileScope scope,
    1868                 :            :                 InstallContext *c,
    1869                 :            :                 const LookupPaths *paths,
    1870                 :            :                 const char *config_path,
    1871                 :            :                 bool force,
    1872                 :            :                 SearchFlags flags,
    1873                 :            :                 UnitFileChange **changes,
    1874                 :            :                 size_t *n_changes) {
    1875                 :            : 
    1876                 :            :         UnitFileInstallInfo *i;
    1877                 :            :         int r;
    1878                 :            : 
    1879         [ -  + ]:        108 :         assert(c);
    1880         [ -  + ]:        108 :         assert(paths);
    1881         [ -  + ]:        108 :         assert(config_path);
    1882                 :            : 
    1883         [ +  + ]:        108 :         if (ordered_hashmap_isempty(c->will_process))
    1884                 :          8 :                 return 0;
    1885                 :            : 
    1886                 :        100 :         r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
    1887         [ -  + ]:        100 :         if (r < 0)
    1888                 :          0 :                 return r;
    1889                 :            : 
    1890                 :        100 :         r = 0;
    1891         [ +  + ]:        248 :         while ((i = ordered_hashmap_first(c->will_process))) {
    1892                 :            :                 int q;
    1893                 :            : 
    1894                 :        148 :                 q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
    1895         [ -  + ]:        148 :                 if (q < 0)
    1896                 :          0 :                         return q;
    1897                 :            : 
    1898                 :        148 :                 q = install_info_traverse(scope, c, paths, i, flags, NULL);
    1899         [ -  + ]:        148 :                 if (q < 0) {
    1900                 :          0 :                         unit_file_changes_add(changes, n_changes, r, i->name, NULL);
    1901                 :          0 :                         return q;
    1902                 :            :                 }
    1903                 :            : 
    1904                 :            :                 /* We can attempt to process a masked unit when a different unit
    1905                 :            :                  * that we were processing specifies it in Also=. */
    1906         [ -  + ]:        148 :                 if (i->type == UNIT_FILE_TYPE_MASKED) {
    1907                 :          0 :                         unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL);
    1908         [ #  # ]:          0 :                         if (r >= 0)
    1909                 :            :                                 /* Assume that something *could* have been enabled here,
    1910                 :            :                                  * avoid "empty [Install] section" warning. */
    1911                 :          0 :                                 r += 1;
    1912                 :          0 :                         continue;
    1913                 :            :                 }
    1914                 :            : 
    1915         [ +  + ]:        148 :                 if (i->type != UNIT_FILE_TYPE_REGULAR)
    1916                 :         28 :                         continue;
    1917                 :            : 
    1918                 :        120 :                 q = install_info_apply(i, paths, config_path, force, changes, n_changes);
    1919         [ +  - ]:        120 :                 if (r >= 0) {
    1920         [ -  + ]:        120 :                         if (q < 0)
    1921                 :          0 :                                 r = q;
    1922                 :            :                         else
    1923                 :        120 :                                 r += q;
    1924                 :            :                 }
    1925                 :            :         }
    1926                 :            : 
    1927                 :        100 :         return r;
    1928                 :            : }
    1929                 :            : 
    1930                 :         68 : static int install_context_mark_for_removal(
    1931                 :            :                 UnitFileScope scope,
    1932                 :            :                 InstallContext *c,
    1933                 :            :                 const LookupPaths *paths,
    1934                 :            :                 Set **remove_symlinks_to,
    1935                 :            :                 const char *config_path,
    1936                 :            :                 UnitFileChange **changes,
    1937                 :            :                 size_t *n_changes) {
    1938                 :            : 
    1939                 :            :         UnitFileInstallInfo *i;
    1940                 :            :         int r;
    1941                 :            : 
    1942         [ -  + ]:         68 :         assert(c);
    1943         [ -  + ]:         68 :         assert(paths);
    1944         [ -  + ]:         68 :         assert(config_path);
    1945                 :            : 
    1946                 :            :         /* Marks all items for removal */
    1947                 :            : 
    1948         [ +  + ]:         68 :         if (ordered_hashmap_isempty(c->will_process))
    1949                 :         12 :                 return 0;
    1950                 :            : 
    1951                 :         56 :         r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops);
    1952         [ -  + ]:         56 :         if (r < 0)
    1953                 :          0 :                 return r;
    1954                 :            : 
    1955         [ +  + ]:        204 :         while ((i = ordered_hashmap_first(c->will_process))) {
    1956                 :            : 
    1957                 :        148 :                 r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name);
    1958         [ -  + ]:        148 :                 if (r < 0)
    1959                 :          0 :                         return r;
    1960                 :            : 
    1961                 :        148 :                 r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
    1962         [ -  + ]:        148 :                 if (r == -ENOLINK) {
    1963         [ #  # ]:          0 :                         log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name);
    1964         [ #  # ]:          0 :                         unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, i->path ?: i->name, NULL);
    1965         [ -  + ]:        148 :                 } else if (r == -ENOENT) {
    1966                 :            : 
    1967         [ #  # ]:          0 :                         if (i->auxiliary)  /* some unit specified in Also= or similar is missing */
    1968         [ #  # ]:          0 :                                 log_debug_errno(r, "Auxiliary unit of %s not found, removing name.", i->name);
    1969                 :            :                         else {
    1970         [ #  # ]:          0 :                                 log_debug_errno(r, "Unit %s not found, removing name.", i->name);
    1971         [ #  # ]:          0 :                                 unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
    1972                 :            :                         }
    1973                 :            : 
    1974         [ -  + ]:        148 :                 } else if (r < 0) {
    1975         [ #  # ]:          0 :                         log_debug_errno(r, "Failed to find unit %s, removing name: %m", i->name);
    1976         [ #  # ]:          0 :                         unit_file_changes_add(changes, n_changes, r, i->path ?: i->name, NULL);
    1977         [ -  + ]:        148 :                 } else if (i->type == UNIT_FILE_TYPE_MASKED) {
    1978         [ #  # ]:          0 :                         log_debug("Unit file %s is masked, ignoring.", i->name);
    1979         [ #  # ]:          0 :                         unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path ?: i->name, NULL);
    1980                 :          0 :                         continue;
    1981         [ +  + ]:        148 :                 } else if (i->type != UNIT_FILE_TYPE_REGULAR) {
    1982   [ +  -  +  - ]:          8 :                         log_debug("Unit %s has type %s, ignoring.", i->name, unit_file_type_to_string(i->type) ?: "invalid");
    1983                 :          8 :                         continue;
    1984                 :            :                 }
    1985                 :            : 
    1986                 :        140 :                 r = mark_symlink_for_removal(remove_symlinks_to, i->name);
    1987         [ -  + ]:        140 :                 if (r < 0)
    1988                 :          0 :                         return r;
    1989                 :            :         }
    1990                 :            : 
    1991                 :         56 :         return 0;
    1992                 :            : }
    1993                 :            : 
    1994                 :          4 : int unit_file_mask(
    1995                 :            :                 UnitFileScope scope,
    1996                 :            :                 UnitFileFlags flags,
    1997                 :            :                 const char *root_dir,
    1998                 :            :                 char **files,
    1999                 :            :                 UnitFileChange **changes,
    2000                 :            :                 size_t *n_changes) {
    2001                 :            : 
    2002                 :          4 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2003                 :            :         const char *config_path;
    2004                 :            :         char **i;
    2005                 :            :         int r;
    2006                 :            : 
    2007         [ -  + ]:          4 :         assert(scope >= 0);
    2008         [ -  + ]:          4 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2009                 :            : 
    2010                 :          4 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2011         [ -  + ]:          4 :         if (r < 0)
    2012                 :          0 :                 return r;
    2013                 :            : 
    2014         [ -  + ]:          4 :         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
    2015         [ -  + ]:          4 :         if (!config_path)
    2016                 :          0 :                 return -ENXIO;
    2017                 :            : 
    2018   [ +  -  +  + ]:          8 :         STRV_FOREACH(i, files) {
    2019      [ +  -  - ]:          4 :                 _cleanup_free_ char *path = NULL;
    2020                 :            :                 int q;
    2021                 :            : 
    2022         [ -  + ]:          4 :                 if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
    2023         [ #  # ]:          0 :                         if (r == 0)
    2024                 :          0 :                                 r = -EINVAL;
    2025                 :          0 :                         continue;
    2026                 :            :                 }
    2027                 :            : 
    2028                 :          4 :                 path = path_make_absolute(*i, config_path);
    2029         [ -  + ]:          4 :                 if (!path)
    2030                 :          0 :                         return -ENOMEM;
    2031                 :            : 
    2032                 :          4 :                 q = create_symlink(&paths, "/dev/null", path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
    2033   [ -  +  #  # ]:          4 :                 if (q < 0 && r >= 0)
    2034                 :          0 :                         r = q;
    2035                 :            :         }
    2036                 :            : 
    2037                 :          4 :         return r;
    2038                 :            : }
    2039                 :            : 
    2040                 :          4 : int unit_file_unmask(
    2041                 :            :                 UnitFileScope scope,
    2042                 :            :                 UnitFileFlags flags,
    2043                 :            :                 const char *root_dir,
    2044                 :            :                 char **files,
    2045                 :            :                 UnitFileChange **changes,
    2046                 :            :                 size_t *n_changes) {
    2047                 :            : 
    2048                 :          4 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2049                 :          4 :         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
    2050                 :          4 :         _cleanup_strv_free_ char **todo = NULL;
    2051                 :          4 :         size_t n_todo = 0, n_allocated = 0;
    2052                 :            :         const char *config_path;
    2053                 :            :         char **i;
    2054                 :            :         bool dry_run;
    2055                 :            :         int r, q;
    2056                 :            : 
    2057         [ -  + ]:          4 :         assert(scope >= 0);
    2058         [ -  + ]:          4 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2059                 :            : 
    2060                 :          4 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2061         [ -  + ]:          4 :         if (r < 0)
    2062                 :          0 :                 return r;
    2063                 :            : 
    2064         [ -  + ]:          4 :         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
    2065         [ -  + ]:          4 :         if (!config_path)
    2066                 :          0 :                 return -ENXIO;
    2067                 :            : 
    2068                 :          4 :         dry_run = !!(flags & UNIT_FILE_DRY_RUN);
    2069                 :            : 
    2070   [ +  -  +  + ]:          8 :         STRV_FOREACH(i, files) {
    2071      [ +  -  - ]:          4 :                 _cleanup_free_ char *path = NULL;
    2072                 :            : 
    2073         [ -  + ]:          4 :                 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
    2074                 :          0 :                         return -EINVAL;
    2075                 :            : 
    2076                 :          4 :                 path = path_make_absolute(*i, config_path);
    2077         [ -  + ]:          4 :                 if (!path)
    2078                 :          0 :                         return -ENOMEM;
    2079                 :            : 
    2080                 :          4 :                 r = null_or_empty_path(path);
    2081         [ -  + ]:          4 :                 if (r == -ENOENT)
    2082                 :          0 :                         continue;
    2083         [ -  + ]:          4 :                 if (r < 0)
    2084                 :          0 :                         return r;
    2085         [ -  + ]:          4 :                 if (r == 0)
    2086                 :          0 :                         continue;
    2087                 :            : 
    2088         [ -  + ]:          4 :                 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
    2089                 :          0 :                         return -ENOMEM;
    2090                 :            : 
    2091                 :          4 :                 todo[n_todo] = strdup(*i);
    2092         [ -  + ]:          4 :                 if (!todo[n_todo])
    2093                 :          0 :                         return -ENOMEM;
    2094                 :            : 
    2095                 :          4 :                 n_todo++;
    2096                 :            :         }
    2097                 :            : 
    2098                 :          4 :         strv_uniq(todo);
    2099                 :            : 
    2100                 :          4 :         r = 0;
    2101   [ +  -  +  + ]:          8 :         STRV_FOREACH(i, todo) {
    2102      [ +  -  - ]:          4 :                 _cleanup_free_ char *path = NULL;
    2103                 :            :                 const char *rp;
    2104                 :            : 
    2105                 :          4 :                 path = path_make_absolute(*i, config_path);
    2106         [ -  + ]:          4 :                 if (!path)
    2107                 :          0 :                         return -ENOMEM;
    2108                 :            : 
    2109   [ +  -  -  + ]:          4 :                 if (!dry_run && unlink(path) < 0) {
    2110         [ #  # ]:          0 :                         if (errno != ENOENT) {
    2111         [ #  # ]:          0 :                                 if (r >= 0)
    2112                 :          0 :                                         r = -errno;
    2113                 :          0 :                                 unit_file_changes_add(changes, n_changes, -errno, path, NULL);
    2114                 :            :                         }
    2115                 :            : 
    2116                 :          0 :                         continue;
    2117                 :            :                 }
    2118                 :            : 
    2119                 :          4 :                 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
    2120                 :            : 
    2121                 :          4 :                 rp = skip_root(&paths, path);
    2122         [ +  - ]:          4 :                 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
    2123         [ -  + ]:          4 :                 if (q < 0)
    2124                 :          0 :                         return q;
    2125                 :            :         }
    2126                 :            : 
    2127                 :          4 :         q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
    2128         [ +  - ]:          4 :         if (r >= 0)
    2129                 :          4 :                 r = q;
    2130                 :            : 
    2131                 :          4 :         return r;
    2132                 :            : }
    2133                 :            : 
    2134                 :          4 : int unit_file_link(
    2135                 :            :                 UnitFileScope scope,
    2136                 :            :                 UnitFileFlags flags,
    2137                 :            :                 const char *root_dir,
    2138                 :            :                 char **files,
    2139                 :            :                 UnitFileChange **changes,
    2140                 :            :                 size_t *n_changes) {
    2141                 :            : 
    2142                 :          4 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2143                 :          4 :         _cleanup_strv_free_ char **todo = NULL;
    2144                 :          4 :         size_t n_todo = 0, n_allocated = 0;
    2145                 :            :         const char *config_path;
    2146                 :            :         char **i;
    2147                 :            :         int r, q;
    2148                 :            : 
    2149         [ -  + ]:          4 :         assert(scope >= 0);
    2150         [ -  + ]:          4 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2151                 :            : 
    2152                 :          4 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2153         [ -  + ]:          4 :         if (r < 0)
    2154                 :          0 :                 return r;
    2155                 :            : 
    2156         [ -  + ]:          4 :         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
    2157         [ -  + ]:          4 :         if (!config_path)
    2158                 :          0 :                 return -ENXIO;
    2159                 :            : 
    2160   [ +  -  +  + ]:          8 :         STRV_FOREACH(i, files) {
    2161      [ +  -  - ]:          4 :                 _cleanup_free_ char *full = NULL;
    2162                 :            :                 struct stat st;
    2163                 :            :                 char *fn;
    2164                 :            : 
    2165         [ -  + ]:          4 :                 if (!path_is_absolute(*i))
    2166                 :          0 :                         return -EINVAL;
    2167                 :            : 
    2168                 :          4 :                 fn = basename(*i);
    2169         [ -  + ]:          4 :                 if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
    2170                 :          0 :                         return -EINVAL;
    2171                 :            : 
    2172                 :          4 :                 full = path_join(paths.root_dir, *i);
    2173         [ -  + ]:          4 :                 if (!full)
    2174                 :          0 :                         return -ENOMEM;
    2175                 :            : 
    2176         [ -  + ]:          4 :                 if (lstat(full, &st) < 0)
    2177                 :          0 :                         return -errno;
    2178                 :          4 :                 r = stat_verify_regular(&st);
    2179         [ -  + ]:          4 :                 if (r < 0)
    2180                 :          0 :                         return r;
    2181                 :            : 
    2182                 :          4 :                 q = in_search_path(&paths, *i);
    2183         [ -  + ]:          4 :                 if (q < 0)
    2184                 :          0 :                         return q;
    2185         [ -  + ]:          4 :                 if (q > 0)
    2186                 :          0 :                         continue;
    2187                 :            : 
    2188         [ -  + ]:          4 :                 if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
    2189                 :          0 :                         return -ENOMEM;
    2190                 :            : 
    2191                 :          4 :                 todo[n_todo] = strdup(*i);
    2192         [ -  + ]:          4 :                 if (!todo[n_todo])
    2193                 :          0 :                         return -ENOMEM;
    2194                 :            : 
    2195                 :          4 :                 n_todo++;
    2196                 :            :         }
    2197                 :            : 
    2198                 :          4 :         strv_uniq(todo);
    2199                 :            : 
    2200                 :          4 :         r = 0;
    2201   [ +  -  +  + ]:          8 :         STRV_FOREACH(i, todo) {
    2202         [ +  - ]:          4 :                 _cleanup_free_ char *new_path = NULL;
    2203                 :            : 
    2204                 :          4 :                 new_path = path_make_absolute(basename(*i), config_path);
    2205         [ -  + ]:          4 :                 if (!new_path)
    2206                 :          0 :                         return -ENOMEM;
    2207                 :            : 
    2208                 :          4 :                 q = create_symlink(&paths, *i, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
    2209   [ -  +  #  # ]:          4 :                 if (q < 0 && r >= 0)
    2210                 :          0 :                         r = q;
    2211                 :            :         }
    2212                 :            : 
    2213                 :          4 :         return r;
    2214                 :            : }
    2215                 :            : 
    2216                 :          4 : static int path_shall_revert(const LookupPaths *paths, const char *path) {
    2217                 :            :         int r;
    2218                 :            : 
    2219         [ -  + ]:          4 :         assert(paths);
    2220         [ -  + ]:          4 :         assert(path);
    2221                 :            : 
    2222                 :            :         /* Checks whether the path is one where the drop-in directories shall be removed. */
    2223                 :            : 
    2224                 :          4 :         r = path_is_config(paths, path, true);
    2225         [ +  - ]:          4 :         if (r != 0)
    2226                 :          4 :                 return r;
    2227                 :            : 
    2228                 :          0 :         r = path_is_control(paths, path);
    2229         [ #  # ]:          0 :         if (r != 0)
    2230                 :          0 :                 return r;
    2231                 :            : 
    2232                 :          0 :         return path_is_transient(paths, path);
    2233                 :            : }
    2234                 :            : 
    2235                 :         12 : int unit_file_revert(
    2236                 :            :                 UnitFileScope scope,
    2237                 :            :                 const char *root_dir,
    2238                 :            :                 char **files,
    2239                 :            :                 UnitFileChange **changes,
    2240                 :            :                 size_t *n_changes) {
    2241                 :            : 
    2242                 :         12 :         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
    2243                 :         12 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2244                 :         12 :         _cleanup_strv_free_ char **todo = NULL;
    2245                 :         12 :         size_t n_todo = 0, n_allocated = 0;
    2246                 :            :         char **i;
    2247                 :            :         int r, q;
    2248                 :            : 
    2249                 :            :         /* Puts a unit file back into vendor state. This means:
    2250                 :            :          *
    2251                 :            :          * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and
    2252                 :            :          *    added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated").
    2253                 :            :          *
    2254                 :            :          * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in
    2255                 :            :          *    "config", but not in "transient" or "control" or even "generated").
    2256                 :            :          *
    2257                 :            :          * We remove all that in both the runtime and the persistent directories, if that applies.
    2258                 :            :          */
    2259                 :            : 
    2260                 :         12 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2261         [ -  + ]:         12 :         if (r < 0)
    2262                 :          0 :                 return r;
    2263                 :            : 
    2264   [ +  -  +  + ]:         24 :         STRV_FOREACH(i, files) {
    2265                 :         12 :                 bool has_vendor = false;
    2266                 :            :                 char **p;
    2267                 :            : 
    2268         [ -  + ]:         12 :                 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
    2269                 :          0 :                         return -EINVAL;
    2270                 :            : 
    2271   [ +  -  +  + ]:        156 :                 STRV_FOREACH(p, paths.search_path) {
    2272   [ +  -  +  - ]:        144 :                         _cleanup_free_ char *path = NULL, *dropin = NULL;
    2273                 :            :                         struct stat st;
    2274                 :            : 
    2275                 :        144 :                         path = path_make_absolute(*i, *p);
    2276         [ -  + ]:        144 :                         if (!path)
    2277                 :          0 :                                 return -ENOMEM;
    2278                 :            : 
    2279                 :        144 :                         r = lstat(path, &st);
    2280         [ +  + ]:        144 :                         if (r < 0) {
    2281         [ -  + ]:        128 :                                 if (errno != ENOENT)
    2282                 :          0 :                                         return -errno;
    2283         [ +  - ]:         16 :                         } else if (S_ISREG(st.st_mode)) {
    2284                 :            :                                 /* Check if there's a vendor version */
    2285                 :         16 :                                 r = path_is_vendor(&paths, path);
    2286         [ -  + ]:         16 :                                 if (r < 0)
    2287                 :          0 :                                         return r;
    2288         [ +  + ]:         16 :                                 if (r > 0)
    2289                 :         12 :                                         has_vendor = true;
    2290                 :            :                         }
    2291                 :            : 
    2292                 :        144 :                         dropin = strjoin(path, ".d");
    2293         [ -  + ]:        144 :                         if (!dropin)
    2294                 :          0 :                                 return -ENOMEM;
    2295                 :            : 
    2296                 :        144 :                         r = lstat(dropin, &st);
    2297         [ +  + ]:        144 :                         if (r < 0) {
    2298         [ -  + ]:        140 :                                 if (errno != ENOENT)
    2299                 :          0 :                                         return -errno;
    2300         [ +  - ]:          4 :                         } else if (S_ISDIR(st.st_mode)) {
    2301                 :            :                                 /* Remove the drop-ins */
    2302                 :          4 :                                 r = path_shall_revert(&paths, dropin);
    2303         [ -  + ]:          4 :                                 if (r < 0)
    2304                 :          0 :                                         return r;
    2305         [ +  - ]:          4 :                                 if (r > 0) {
    2306         [ -  + ]:          4 :                                         if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
    2307                 :          0 :                                                 return -ENOMEM;
    2308                 :            : 
    2309                 :          4 :                                         todo[n_todo++] = TAKE_PTR(dropin);
    2310                 :            :                                 }
    2311                 :            :                         }
    2312                 :            :                 }
    2313                 :            : 
    2314         [ -  + ]:         12 :                 if (!has_vendor)
    2315                 :          0 :                         continue;
    2316                 :            : 
    2317                 :            :                 /* OK, there's a vendor version, hence drop all configuration versions */
    2318   [ +  -  +  + ]:        156 :                 STRV_FOREACH(p, paths.search_path) {
    2319         [ +  - ]:        144 :                         _cleanup_free_ char *path = NULL;
    2320                 :            :                         struct stat st;
    2321                 :            : 
    2322                 :        144 :                         path = path_make_absolute(*i, *p);
    2323         [ -  + ]:        144 :                         if (!path)
    2324                 :          0 :                                 return -ENOMEM;
    2325                 :            : 
    2326                 :        144 :                         r = lstat(path, &st);
    2327         [ +  + ]:        144 :                         if (r < 0) {
    2328         [ -  + ]:        128 :                                 if (errno != ENOENT)
    2329                 :          0 :                                         return -errno;
    2330   [ -  +  #  # ]:         16 :                         } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
    2331                 :         16 :                                 r = path_is_config(&paths, path, true);
    2332         [ -  + ]:         16 :                                 if (r < 0)
    2333                 :          0 :                                         return r;
    2334         [ +  + ]:         16 :                                 if (r > 0) {
    2335         [ -  + ]:          4 :                                         if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
    2336                 :          0 :                                                 return -ENOMEM;
    2337                 :            : 
    2338                 :          4 :                                         todo[n_todo++] = TAKE_PTR(path);
    2339                 :            :                                 }
    2340                 :            :                         }
    2341                 :            :                 }
    2342                 :            :         }
    2343                 :            : 
    2344                 :         12 :         strv_uniq(todo);
    2345                 :            : 
    2346                 :         12 :         r = 0;
    2347   [ +  +  +  + ]:         20 :         STRV_FOREACH(i, todo) {
    2348      [ +  -  - ]:          8 :                 _cleanup_strv_free_ char **fs = NULL;
    2349                 :            :                 const char *rp;
    2350                 :            :                 char **j;
    2351                 :            : 
    2352                 :          8 :                 (void) get_files_in_directory(*i, &fs);
    2353                 :            : 
    2354                 :          8 :                 q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL);
    2355   [ -  +  #  #  :          8 :                 if (q < 0 && q != -ENOENT && r >= 0) {
                   #  # ]
    2356                 :          0 :                         r = q;
    2357                 :          0 :                         continue;
    2358                 :            :                 }
    2359                 :            : 
    2360   [ +  +  +  + ]:         12 :                 STRV_FOREACH(j, fs) {
    2361         [ +  - ]:          4 :                         _cleanup_free_ char *t = NULL;
    2362                 :            : 
    2363                 :          4 :                         t = path_join(*i, *j);
    2364         [ -  + ]:          4 :                         if (!t)
    2365                 :          0 :                                 return -ENOMEM;
    2366                 :            : 
    2367                 :          4 :                         unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL);
    2368                 :            :                 }
    2369                 :            : 
    2370                 :          8 :                 unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL);
    2371                 :            : 
    2372                 :          8 :                 rp = skip_root(&paths, *i);
    2373         [ +  - ]:          8 :                 q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i);
    2374         [ -  + ]:          8 :                 if (q < 0)
    2375                 :          0 :                         return q;
    2376                 :            :         }
    2377                 :            : 
    2378                 :         12 :         q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, false, changes, n_changes);
    2379         [ +  - ]:         12 :         if (r >= 0)
    2380                 :         12 :                 r = q;
    2381                 :            : 
    2382                 :         12 :         q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, false, changes, n_changes);
    2383         [ +  - ]:         12 :         if (r >= 0)
    2384                 :         12 :                 r = q;
    2385                 :            : 
    2386                 :         12 :         return r;
    2387                 :            : }
    2388                 :            : 
    2389                 :          4 : int unit_file_add_dependency(
    2390                 :            :                 UnitFileScope scope,
    2391                 :            :                 UnitFileFlags flags,
    2392                 :            :                 const char *root_dir,
    2393                 :            :                 char **files,
    2394                 :            :                 const char *target,
    2395                 :            :                 UnitDependency dep,
    2396                 :            :                 UnitFileChange **changes,
    2397                 :            :                 size_t *n_changes) {
    2398                 :            : 
    2399                 :          4 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2400                 :          4 :         _cleanup_(install_context_done) InstallContext c = {};
    2401                 :            :         UnitFileInstallInfo *i, *target_info;
    2402                 :            :         const char *config_path;
    2403                 :            :         char **f;
    2404                 :            :         int r;
    2405                 :            : 
    2406         [ -  + ]:          4 :         assert(scope >= 0);
    2407         [ -  + ]:          4 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2408         [ -  + ]:          4 :         assert(target);
    2409                 :            : 
    2410   [ +  -  -  + ]:          4 :         if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES))
    2411                 :          0 :                 return -EINVAL;
    2412                 :            : 
    2413         [ -  + ]:          4 :         if (!unit_name_is_valid(target, UNIT_NAME_ANY))
    2414                 :          0 :                 return -EINVAL;
    2415                 :            : 
    2416                 :          4 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2417         [ -  + ]:          4 :         if (r < 0)
    2418                 :          0 :                 return r;
    2419                 :            : 
    2420         [ -  + ]:          4 :         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
    2421         [ -  + ]:          4 :         if (!config_path)
    2422                 :          0 :                 return -ENXIO;
    2423                 :            : 
    2424                 :          4 :         r = install_info_discover_and_check(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS,
    2425                 :            :                                             &target_info, changes, n_changes);
    2426         [ -  + ]:          4 :         if (r < 0)
    2427                 :          0 :                 return r;
    2428                 :            : 
    2429         [ -  + ]:          4 :         assert(target_info->type == UNIT_FILE_TYPE_REGULAR);
    2430                 :            : 
    2431   [ +  -  +  + ]:          8 :         STRV_FOREACH(f, files) {
    2432                 :            :                 char ***l;
    2433                 :            : 
    2434                 :          4 :                 r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS,
    2435                 :            :                                                     &i, changes, n_changes);
    2436         [ -  + ]:          4 :                 if (r < 0)
    2437                 :          0 :                         return r;
    2438                 :            : 
    2439         [ -  + ]:          4 :                 assert(i->type == UNIT_FILE_TYPE_REGULAR);
    2440                 :            : 
    2441                 :            :                 /* We didn't actually load anything from the unit
    2442                 :            :                  * file, but instead just add in our new symlink to
    2443                 :            :                  * create. */
    2444                 :            : 
    2445         [ +  - ]:          4 :                 if (dep == UNIT_WANTS)
    2446                 :          4 :                         l = &i->wanted_by;
    2447                 :            :                 else
    2448                 :          0 :                         l = &i->required_by;
    2449                 :            : 
    2450                 :          4 :                 strv_free(*l);
    2451                 :          4 :                 *l = strv_new(target_info->name);
    2452         [ -  + ]:          4 :                 if (!*l)
    2453                 :          0 :                         return -ENOMEM;
    2454                 :            :         }
    2455                 :            : 
    2456                 :          4 :         return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
    2457                 :            : }
    2458                 :            : 
    2459                 :         80 : int unit_file_enable(
    2460                 :            :                 UnitFileScope scope,
    2461                 :            :                 UnitFileFlags flags,
    2462                 :            :                 const char *root_dir,
    2463                 :            :                 char **files,
    2464                 :            :                 UnitFileChange **changes,
    2465                 :            :                 size_t *n_changes) {
    2466                 :            : 
    2467                 :         80 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2468                 :         80 :         _cleanup_(install_context_done) InstallContext c = {};
    2469                 :            :         const char *config_path;
    2470                 :            :         UnitFileInstallInfo *i;
    2471                 :            :         char **f;
    2472                 :            :         int r;
    2473                 :            : 
    2474         [ -  + ]:         80 :         assert(scope >= 0);
    2475         [ -  + ]:         80 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2476                 :            : 
    2477                 :         80 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2478         [ -  + ]:         80 :         if (r < 0)
    2479                 :          0 :                 return r;
    2480                 :            : 
    2481         [ -  + ]:         80 :         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
    2482         [ -  + ]:         80 :         if (!config_path)
    2483                 :          0 :                 return -ENXIO;
    2484                 :            : 
    2485   [ +  -  +  + ]:        156 :         STRV_FOREACH(f, files) {
    2486                 :         80 :                 r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
    2487                 :            :                                                     &i, changes, n_changes);
    2488         [ +  + ]:         80 :                 if (r < 0)
    2489                 :          4 :                         return r;
    2490                 :            : 
    2491         [ -  + ]:         76 :                 assert(i->type == UNIT_FILE_TYPE_REGULAR);
    2492                 :            :         }
    2493                 :            : 
    2494                 :            :         /* This will return the number of symlink rules that were
    2495                 :            :            supposed to be created, not the ones actually created. This
    2496                 :            :            is useful to determine whether the passed files had any
    2497                 :            :            installation data at all. */
    2498                 :            : 
    2499                 :         76 :         return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_LOAD, changes, n_changes);
    2500                 :            : }
    2501                 :            : 
    2502                 :         40 : int unit_file_disable(
    2503                 :            :                 UnitFileScope scope,
    2504                 :            :                 UnitFileFlags flags,
    2505                 :            :                 const char *root_dir,
    2506                 :            :                 char **files,
    2507                 :            :                 UnitFileChange **changes,
    2508                 :            :                 size_t *n_changes) {
    2509                 :            : 
    2510                 :         40 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2511                 :         40 :         _cleanup_(install_context_done) InstallContext c = {};
    2512                 :         40 :         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
    2513                 :            :         const char *config_path;
    2514                 :            :         char **i;
    2515                 :            :         int r;
    2516                 :            : 
    2517         [ -  + ]:         40 :         assert(scope >= 0);
    2518         [ -  + ]:         40 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2519                 :            : 
    2520                 :         40 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2521         [ -  + ]:         40 :         if (r < 0)
    2522                 :          0 :                 return r;
    2523                 :            : 
    2524         [ -  + ]:         40 :         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
    2525         [ -  + ]:         40 :         if (!config_path)
    2526                 :          0 :                 return -ENXIO;
    2527                 :            : 
    2528   [ +  -  +  + ]:         80 :         STRV_FOREACH(i, files) {
    2529         [ -  + ]:         40 :                 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
    2530                 :          0 :                         return -EINVAL;
    2531                 :            : 
    2532                 :         40 :                 r = install_info_add(&c, *i, NULL, false, NULL);
    2533         [ -  + ]:         40 :                 if (r < 0)
    2534                 :          0 :                         return r;
    2535                 :            :         }
    2536                 :            : 
    2537                 :         40 :         r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes);
    2538         [ -  + ]:         40 :         if (r < 0)
    2539                 :          0 :                 return r;
    2540                 :            : 
    2541                 :         40 :         return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes);
    2542                 :            : }
    2543                 :            : 
    2544                 :          4 : int unit_file_reenable(
    2545                 :            :                 UnitFileScope scope,
    2546                 :            :                 UnitFileFlags flags,
    2547                 :            :                 const char *root_dir,
    2548                 :            :                 char **files,
    2549                 :            :                 UnitFileChange **changes,
    2550                 :            :                 size_t *n_changes) {
    2551                 :            : 
    2552                 :            :         char **n;
    2553                 :            :         int r;
    2554                 :            :         size_t l, i;
    2555                 :            : 
    2556                 :            :         /* First, we invoke the disable command with only the basename... */
    2557                 :          4 :         l = strv_length(files);
    2558   [ -  +  -  + ]:          4 :         n = newa(char*, l+1);
    2559         [ +  + ]:          8 :         for (i = 0; i < l; i++)
    2560                 :          4 :                 n[i] = basename(files[i]);
    2561                 :          4 :         n[i] = NULL;
    2562                 :            : 
    2563                 :          4 :         r = unit_file_disable(scope, flags, root_dir, n, changes, n_changes);
    2564         [ -  + ]:          4 :         if (r < 0)
    2565                 :          0 :                 return r;
    2566                 :            : 
    2567                 :            :         /* But the enable command with the full name */
    2568                 :          4 :         return unit_file_enable(scope, flags, root_dir, files, changes, n_changes);
    2569                 :            : }
    2570                 :            : 
    2571                 :          8 : int unit_file_set_default(
    2572                 :            :                 UnitFileScope scope,
    2573                 :            :                 UnitFileFlags flags,
    2574                 :            :                 const char *root_dir,
    2575                 :            :                 const char *name,
    2576                 :            :                 UnitFileChange **changes,
    2577                 :            :                 size_t *n_changes) {
    2578                 :            : 
    2579                 :          8 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2580                 :          8 :         _cleanup_(install_context_done) InstallContext c = {};
    2581                 :            :         UnitFileInstallInfo *i;
    2582                 :            :         const char *new_path;
    2583                 :            :         int r;
    2584                 :            : 
    2585         [ -  + ]:          8 :         assert(scope >= 0);
    2586         [ -  + ]:          8 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2587         [ -  + ]:          8 :         assert(name);
    2588                 :            : 
    2589         [ -  + ]:          8 :         if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */
    2590                 :          0 :                 return -EINVAL;
    2591         [ -  + ]:          8 :         if (streq(name, SPECIAL_DEFAULT_TARGET))
    2592                 :          0 :                 return -EINVAL;
    2593                 :            : 
    2594                 :          8 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2595         [ -  + ]:          8 :         if (r < 0)
    2596                 :          0 :                 return r;
    2597                 :            : 
    2598                 :          8 :         r = install_info_discover_and_check(scope, &c, &paths, name, 0, &i, changes, n_changes);
    2599         [ +  + ]:          8 :         if (r < 0)
    2600                 :          4 :                 return r;
    2601                 :            : 
    2602   [ +  +  +  -  :         20 :         new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET);
          -  +  -  +  +  
                +  +  - ]
    2603                 :          4 :         return create_symlink(&paths, i->path, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
    2604                 :            : }
    2605                 :            : 
    2606                 :         12 : int unit_file_get_default(
    2607                 :            :                 UnitFileScope scope,
    2608                 :            :                 const char *root_dir,
    2609                 :            :                 char **name) {
    2610                 :            : 
    2611                 :         12 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2612                 :         12 :         _cleanup_(install_context_done) InstallContext c = {};
    2613                 :            :         UnitFileInstallInfo *i;
    2614                 :            :         char *n;
    2615                 :            :         int r;
    2616                 :            : 
    2617         [ -  + ]:         12 :         assert(scope >= 0);
    2618         [ -  + ]:         12 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2619         [ -  + ]:         12 :         assert(name);
    2620                 :            : 
    2621                 :         12 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2622         [ -  + ]:         12 :         if (r < 0)
    2623                 :          0 :                 return r;
    2624                 :            : 
    2625                 :         12 :         r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS,
    2626                 :            :                                   &i, NULL, NULL);
    2627         [ +  + ]:         12 :         if (r < 0)
    2628                 :          8 :                 return r;
    2629                 :          4 :         r = install_info_may_process(i, &paths, NULL, 0);
    2630         [ -  + ]:          4 :         if (r < 0)
    2631                 :          0 :                 return r;
    2632                 :            : 
    2633                 :          4 :         n = strdup(i->name);
    2634         [ -  + ]:          4 :         if (!n)
    2635                 :          0 :                 return -ENOMEM;
    2636                 :            : 
    2637                 :          4 :         *name = n;
    2638                 :          4 :         return 0;
    2639                 :            : }
    2640                 :            : 
    2641                 :       2828 : int unit_file_lookup_state(
    2642                 :            :                 UnitFileScope scope,
    2643                 :            :                 const LookupPaths *paths,
    2644                 :            :                 const char *name,
    2645                 :            :                 UnitFileState *ret) {
    2646                 :            : 
    2647                 :       2828 :         _cleanup_(install_context_done) InstallContext c = {};
    2648                 :            :         UnitFileInstallInfo *i;
    2649                 :            :         UnitFileState state;
    2650                 :            :         int r;
    2651                 :            : 
    2652         [ -  + ]:       2828 :         assert(paths);
    2653         [ -  + ]:       2828 :         assert(name);
    2654                 :            : 
    2655         [ -  + ]:       2828 :         if (!unit_name_is_valid(name, UNIT_NAME_ANY))
    2656                 :          0 :                 return -EINVAL;
    2657                 :            : 
    2658                 :       2828 :         r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
    2659                 :            :                                   &i, NULL, NULL);
    2660         [ +  + ]:       2828 :         if (r < 0)
    2661         [ +  - ]:        132 :                 return log_debug_errno(r, "Failed to discover unit %s: %m", name);
    2662                 :            : 
    2663   [ +  -  -  + ]:       2696 :         assert(IN_SET(i->type, UNIT_FILE_TYPE_REGULAR, UNIT_FILE_TYPE_MASKED));
    2664   [ +  +  +  + ]:       2696 :         log_debug("Found unit %s at %s (%s)", name, strna(i->path),
    2665                 :            :                   i->type == UNIT_FILE_TYPE_REGULAR ? "regular file" : "mask");
    2666                 :            : 
    2667                 :            :         /* Shortcut things, if the caller just wants to know if this unit exists. */
    2668         [ +  + ]:       2696 :         if (!ret)
    2669                 :         20 :                 return 0;
    2670                 :            : 
    2671      [ +  +  - ]:       2676 :         switch (i->type) {
    2672                 :            : 
    2673                 :         20 :         case UNIT_FILE_TYPE_MASKED:
    2674                 :         20 :                 r = path_is_runtime(paths, i->path, true);
    2675         [ -  + ]:         20 :                 if (r < 0)
    2676                 :          0 :                         return r;
    2677                 :            : 
    2678         [ -  + ]:         20 :                 state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
    2679                 :         20 :                 break;
    2680                 :            : 
    2681                 :       2656 :         case UNIT_FILE_TYPE_REGULAR:
    2682                 :       2656 :                 r = path_is_generator(paths, i->path);
    2683         [ -  + ]:       2656 :                 if (r < 0)
    2684                 :          0 :                         return r;
    2685         [ +  + ]:       2656 :                 if (r > 0) {
    2686                 :         40 :                         state = UNIT_FILE_GENERATED;
    2687                 :         40 :                         break;
    2688                 :            :                 }
    2689                 :            : 
    2690                 :       2616 :                 r = path_is_transient(paths, i->path);
    2691         [ -  + ]:       2616 :                 if (r < 0)
    2692                 :          0 :                         return r;
    2693         [ +  + ]:       2616 :                 if (r > 0) {
    2694                 :          4 :                         state = UNIT_FILE_TRANSIENT;
    2695                 :          4 :                         break;
    2696                 :            :                 }
    2697                 :            : 
    2698                 :            :                 /* Check if any of the Alias= symlinks have been created.
    2699                 :            :                  * We ignore other aliases, and only check those that would
    2700                 :            :                  * be created by systemctl enable for this unit. */
    2701                 :       2612 :                 r = find_symlinks_in_scope(scope, paths, i, true, &state);
    2702         [ -  + ]:       2612 :                 if (r < 0)
    2703                 :          0 :                         return r;
    2704         [ +  + ]:       2612 :                 if (r > 0)
    2705                 :        512 :                         break;
    2706                 :            : 
    2707                 :            :                 /* Check if the file is known under other names. If it is,
    2708                 :            :                  * it might be in use. Report that as UNIT_FILE_INDIRECT. */
    2709                 :       2100 :                 r = find_symlinks_in_scope(scope, paths, i, false, &state);
    2710         [ -  + ]:       2100 :                 if (r < 0)
    2711                 :          0 :                         return r;
    2712         [ +  + ]:       2100 :                 if (r > 0)
    2713                 :         28 :                         state = UNIT_FILE_INDIRECT;
    2714                 :            :                 else {
    2715         [ +  + ]:       2072 :                         if (unit_file_install_info_has_rules(i))
    2716                 :        968 :                                 state = UNIT_FILE_DISABLED;
    2717         [ +  + ]:       1104 :                         else if (unit_file_install_info_has_also(i))
    2718                 :         68 :                                 state = UNIT_FILE_INDIRECT;
    2719                 :            :                         else
    2720                 :       1036 :                                 state = UNIT_FILE_STATIC;
    2721                 :            :                 }
    2722                 :            : 
    2723                 :       2100 :                 break;
    2724                 :            : 
    2725                 :          0 :         default:
    2726                 :          0 :                 assert_not_reached("Unexpected unit file type.");
    2727                 :            :         }
    2728                 :            : 
    2729                 :       2676 :         *ret = state;
    2730                 :       2676 :         return 0;
    2731                 :            : }
    2732                 :            : 
    2733                 :        728 : int unit_file_get_state(
    2734                 :            :                 UnitFileScope scope,
    2735                 :            :                 const char *root_dir,
    2736                 :            :                 const char *name,
    2737                 :            :                 UnitFileState *ret) {
    2738                 :            : 
    2739                 :        728 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    2740                 :            :         int r;
    2741                 :            : 
    2742         [ -  + ]:        728 :         assert(scope >= 0);
    2743         [ -  + ]:        728 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2744         [ -  + ]:        728 :         assert(name);
    2745                 :            : 
    2746                 :        728 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    2747         [ -  + ]:        728 :         if (r < 0)
    2748                 :          0 :                 return r;
    2749                 :            : 
    2750                 :        728 :         return unit_file_lookup_state(scope, &paths, name, ret);
    2751                 :            : }
    2752                 :            : 
    2753                 :        100 : int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) {
    2754                 :        100 :         _cleanup_(install_context_done) InstallContext c = {};
    2755                 :            :         int r;
    2756                 :            : 
    2757         [ -  + ]:        100 :         assert(paths);
    2758         [ -  + ]:        100 :         assert(name);
    2759                 :            : 
    2760         [ -  + ]:        100 :         if (!unit_name_is_valid(name, UNIT_NAME_ANY))
    2761                 :          0 :                 return -EINVAL;
    2762                 :            : 
    2763                 :        100 :         r = install_info_discover(scope, &c, paths, name, 0, NULL, NULL, NULL);
    2764         [ +  + ]:        100 :         if (r == -ENOENT)
    2765                 :         96 :                 return 0;
    2766         [ -  + ]:          4 :         if (r < 0)
    2767                 :          0 :                 return r;
    2768                 :            : 
    2769                 :          4 :         return 1;
    2770                 :            : }
    2771                 :            : 
    2772                 :         44 : static int split_pattern_into_name_and_instances(const char *pattern, char **out_unit_name, char ***out_instances) {
    2773                 :         44 :         _cleanup_strv_free_ char **instances = NULL;
    2774                 :         44 :         _cleanup_free_ char *unit_name = NULL;
    2775                 :            :         int r;
    2776                 :            : 
    2777         [ -  + ]:         44 :         assert(pattern);
    2778         [ -  + ]:         44 :         assert(out_instances);
    2779         [ -  + ]:         44 :         assert(out_unit_name);
    2780                 :            : 
    2781                 :         44 :         r = extract_first_word(&pattern, &unit_name, NULL, EXTRACT_RETAIN_ESCAPE);
    2782         [ -  + ]:         44 :         if (r < 0)
    2783                 :          0 :                 return r;
    2784                 :            : 
    2785                 :            :         /* We handle the instances logic when unit name is extracted */
    2786         [ +  + ]:         44 :         if (pattern) {
    2787                 :            :                 /* We only create instances when a rule of templated unit
    2788                 :            :                  * is seen. A rule like enable foo@.service a b c will
    2789                 :            :                  * result in an array of (a, b, c) as instance names */
    2790         [ -  + ]:          8 :                 if (!unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE))
    2791                 :          0 :                         return -EINVAL;
    2792                 :            : 
    2793                 :          8 :                 instances = strv_split(pattern, WHITESPACE);
    2794         [ -  + ]:          8 :                 if (!instances)
    2795                 :          0 :                         return -ENOMEM;
    2796                 :            : 
    2797                 :          8 :                 *out_instances = TAKE_PTR(instances);
    2798                 :            :         }
    2799                 :            : 
    2800                 :         44 :         *out_unit_name = TAKE_PTR(unit_name);
    2801                 :            : 
    2802                 :         44 :         return 0;
    2803                 :            : }
    2804                 :            : 
    2805                 :         28 : static int presets_find_config(UnitFileScope scope, const char *root_dir, char ***files) {
    2806                 :            :         static const char* const system_dirs[] = {CONF_PATHS("systemd/system-preset"), NULL};
    2807                 :            :         static const char* const user_dirs[] = {CONF_PATHS_USR("systemd/user-preset"), NULL};
    2808                 :            :         const char* const* dirs;
    2809                 :            : 
    2810         [ -  + ]:         28 :         assert(scope >= 0);
    2811         [ -  + ]:         28 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2812                 :            : 
    2813         [ +  - ]:         28 :         if (scope == UNIT_FILE_SYSTEM)
    2814                 :         28 :                 dirs = system_dirs;
    2815   [ #  #  #  # ]:          0 :         else if (IN_SET(scope, UNIT_FILE_GLOBAL, UNIT_FILE_USER))
    2816                 :          0 :                 dirs = user_dirs;
    2817                 :            :         else
    2818                 :          0 :                 assert_not_reached("Invalid unit file scope");
    2819                 :            : 
    2820                 :         28 :         return conf_files_list_strv(files, ".preset", root_dir, 0, dirs);
    2821                 :            : }
    2822                 :            : 
    2823                 :         28 : static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) {
    2824                 :         28 :         _cleanup_(presets_freep) Presets ps = {};
    2825                 :         28 :         size_t n_allocated = 0;
    2826                 :         28 :         _cleanup_strv_free_ char **files = NULL;
    2827                 :            :         char **p;
    2828                 :            :         int r;
    2829                 :            : 
    2830         [ -  + ]:         28 :         assert(scope >= 0);
    2831         [ -  + ]:         28 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    2832         [ -  + ]:         28 :         assert(presets);
    2833                 :            : 
    2834                 :         28 :         r = presets_find_config(scope, root_dir, &files);
    2835         [ -  + ]:         28 :         if (r < 0)
    2836                 :          0 :                 return r;
    2837                 :            : 
    2838   [ +  -  +  + ]:         56 :         STRV_FOREACH(p, files) {
    2839      [ +  -  - ]:         28 :                 _cleanup_fclose_ FILE *f;
    2840                 :         28 :                 int n = 0;
    2841                 :            : 
    2842                 :         28 :                 f = fopen(*p, "re");
    2843         [ -  + ]:         28 :                 if (!f) {
    2844         [ #  # ]:          0 :                         if (errno == ENOENT)
    2845                 :          0 :                                 continue;
    2846                 :            : 
    2847                 :          0 :                         return -errno;
    2848                 :            :                 }
    2849                 :            : 
    2850                 :         72 :                 for (;;) {
    2851   [ -  -  +  + ]:        100 :                         _cleanup_free_ char *line = NULL;
    2852                 :        100 :                         PresetRule rule = {};
    2853                 :            :                         const char *parameter;
    2854                 :            :                         char *l;
    2855                 :            : 
    2856                 :        100 :                         r = read_line(f, LONG_LINE_MAX, &line);
    2857         [ -  + ]:        100 :                         if (r < 0)
    2858                 :          0 :                                 return r;
    2859         [ +  + ]:        100 :                         if (r == 0)
    2860                 :         28 :                                 break;
    2861                 :            : 
    2862                 :         72 :                         l = strstrip(line);
    2863                 :         72 :                         n++;
    2864                 :            : 
    2865         [ -  + ]:         72 :                         if (isempty(l))
    2866                 :          0 :                                 continue;
    2867         [ -  + ]:         72 :                         if (strchr(COMMENTS, *l))
    2868                 :          0 :                                 continue;
    2869                 :            : 
    2870                 :         72 :                         parameter = first_word(l, "enable");
    2871         [ +  + ]:         72 :                         if (parameter) {
    2872                 :            :                                 char *unit_name;
    2873                 :         44 :                                 char **instances = NULL;
    2874                 :            : 
    2875                 :            :                                 /* Unit_name will remain the same as parameter when no instances are specified */
    2876                 :         44 :                                 r = split_pattern_into_name_and_instances(parameter, &unit_name, &instances);
    2877         [ -  + ]:         44 :                                 if (r < 0) {
    2878         [ #  # ]:          0 :                                         log_syntax(NULL, LOG_WARNING, *p, n, r, "Couldn't parse line '%s'. Ignoring.", line);
    2879                 :          0 :                                         continue;
    2880                 :            :                                 }
    2881                 :            : 
    2882                 :         44 :                                 rule = (PresetRule) {
    2883                 :            :                                         .pattern = unit_name,
    2884                 :            :                                         .action = PRESET_ENABLE,
    2885                 :            :                                         .instances = instances,
    2886                 :            :                                 };
    2887                 :            :                         }
    2888                 :            : 
    2889                 :         72 :                         parameter = first_word(l, "disable");
    2890         [ +  + ]:         72 :                         if (parameter) {
    2891                 :            :                                 char *pattern;
    2892                 :            : 
    2893                 :         28 :                                 pattern = strdup(parameter);
    2894         [ -  + ]:         28 :                                 if (!pattern)
    2895                 :          0 :                                         return -ENOMEM;
    2896                 :            : 
    2897                 :         28 :                                 rule = (PresetRule) {
    2898                 :            :                                         .pattern = pattern,
    2899                 :            :                                         .action = PRESET_DISABLE,
    2900                 :            :                                 };
    2901                 :            :                         }
    2902                 :            : 
    2903         [ +  - ]:         72 :                         if (rule.action) {
    2904         [ -  + ]:         72 :                                 if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1))
    2905                 :          0 :                                         return -ENOMEM;
    2906                 :            : 
    2907                 :         72 :                                 ps.rules[ps.n_rules++] = rule;
    2908                 :         72 :                                 continue;
    2909                 :            :                         }
    2910                 :            : 
    2911         [ #  # ]:          0 :                         log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line);
    2912                 :            :                 }
    2913                 :            :         }
    2914                 :            : 
    2915                 :         28 :         *presets = ps;
    2916                 :         28 :         ps = (Presets){};
    2917                 :            : 
    2918                 :         28 :         return 0;
    2919                 :            : }
    2920                 :            : 
    2921                 :        260 : static int pattern_match_multiple_instances(
    2922                 :            :                         const PresetRule rule,
    2923                 :            :                         const char *unit_name,
    2924                 :            :                         char ***ret) {
    2925                 :            : 
    2926                 :        260 :         _cleanup_free_ char *templated_name = NULL;
    2927                 :            :         int r;
    2928                 :            : 
    2929                 :            :         /* If no ret is needed or the rule itself does not have instances
    2930                 :            :          * initialized, we return not matching */
    2931   [ +  -  +  + ]:        260 :         if (!ret || !rule.instances)
    2932                 :        204 :                 return 0;
    2933                 :            : 
    2934                 :         56 :         r = unit_name_template(unit_name, &templated_name);
    2935         [ +  + ]:         56 :         if (r < 0)
    2936                 :         44 :                 return r;
    2937         [ +  + ]:         12 :         if (!streq(rule.pattern, templated_name))
    2938                 :          4 :                 return 0;
    2939                 :            : 
    2940                 :            :         /* Compose a list of specified instances when unit name is a template  */
    2941         [ +  + ]:          8 :         if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
    2942                 :          4 :                 _cleanup_free_ char *prefix = NULL;
    2943                 :          4 :                 _cleanup_strv_free_ char **out_strv = NULL;
    2944                 :            :                 char **iter;
    2945                 :            : 
    2946                 :          4 :                 r = unit_name_to_prefix(unit_name, &prefix);
    2947         [ -  + ]:          4 :                 if (r < 0)
    2948                 :          0 :                         return r;
    2949                 :            : 
    2950   [ +  -  +  + ]:         16 :                 STRV_FOREACH(iter, rule.instances) {
    2951         [ +  - ]:         12 :                         _cleanup_free_ char *name = NULL;
    2952                 :         12 :                         r = unit_name_build(prefix, *iter, ".service", &name);
    2953         [ -  + ]:         12 :                         if (r < 0)
    2954                 :          0 :                                 return r;
    2955                 :         12 :                         r = strv_extend(&out_strv, name);
    2956         [ -  + ]:         12 :                         if (r < 0)
    2957                 :          0 :                                 return r;
    2958                 :            :                 }
    2959                 :            : 
    2960                 :          4 :                 *ret = TAKE_PTR(out_strv);
    2961                 :          4 :                 return 1;
    2962                 :            :         } else {
    2963                 :            :                 /* We now know the input unit name is an instance name */
    2964         [ -  + ]:          4 :                 _cleanup_free_ char *instance_name = NULL;
    2965                 :            : 
    2966                 :          4 :                 r = unit_name_to_instance(unit_name, &instance_name);
    2967         [ -  + ]:          4 :                 if (r < 0)
    2968                 :          0 :                         return r;
    2969                 :            : 
    2970         [ +  - ]:          4 :                 if (strv_find(rule.instances, instance_name))
    2971                 :          4 :                         return 1;
    2972                 :            :         }
    2973                 :          0 :         return 0;
    2974                 :            : }
    2975                 :            : 
    2976                 :        116 : static int query_presets(const char *name, const Presets presets, char ***instance_name_list) {
    2977                 :        116 :         PresetAction action = PRESET_UNKNOWN;
    2978                 :            :         size_t i;
    2979                 :            :         char **s;
    2980         [ -  + ]:        116 :         if (!unit_name_is_valid(name, UNIT_NAME_ANY))
    2981                 :          0 :                 return -EINVAL;
    2982                 :            : 
    2983         [ +  - ]:        260 :         for (i = 0; i < presets.n_rules; i++)
    2984   [ +  +  +  + ]:        512 :                 if (pattern_match_multiple_instances(presets.rules[i], name, instance_name_list) > 0 ||
    2985                 :        252 :                     fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) {
    2986                 :        116 :                         action = presets.rules[i].action;
    2987                 :        116 :                         break;
    2988                 :            :                 }
    2989                 :            : 
    2990   [ -  +  +  - ]:        116 :         switch (action) {
    2991                 :          0 :         case PRESET_UNKNOWN:
    2992         [ #  # ]:          0 :                 log_debug("Preset files don't specify rule for %s. Enabling.", name);
    2993                 :          0 :                 return 1;
    2994                 :         20 :         case PRESET_ENABLE:
    2995   [ +  -  +  + ]:         24 :                 if (instance_name_list && *instance_name_list)
    2996   [ +  -  +  + ]:         16 :                         STRV_FOREACH(s, *instance_name_list)
    2997         [ +  - ]:         12 :                                 log_debug("Preset files say enable %s.", *s);
    2998                 :            :                 else
    2999         [ +  - ]:         16 :                         log_debug("Preset files say enable %s.", name);
    3000                 :         20 :                 return 1;
    3001                 :         96 :         case PRESET_DISABLE:
    3002         [ +  - ]:         96 :                 log_debug("Preset files say disable %s.", name);
    3003                 :         96 :                 return 0;
    3004                 :          0 :         default:
    3005                 :          0 :                 assert_not_reached("invalid preset action");
    3006                 :            :         }
    3007                 :            : }
    3008                 :            : 
    3009                 :          0 : int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
    3010                 :          0 :         _cleanup_(presets_freep) Presets presets = {};
    3011                 :            :         int r;
    3012                 :            : 
    3013                 :          0 :         r = read_presets(scope, root_dir, &presets);
    3014         [ #  # ]:          0 :         if (r < 0)
    3015                 :          0 :                 return r;
    3016                 :            : 
    3017                 :          0 :         return query_presets(name, presets, NULL);
    3018                 :            : }
    3019                 :            : 
    3020                 :         28 : static int execute_preset(
    3021                 :            :                 UnitFileScope scope,
    3022                 :            :                 InstallContext *plus,
    3023                 :            :                 InstallContext *minus,
    3024                 :            :                 const LookupPaths *paths,
    3025                 :            :                 const char *config_path,
    3026                 :            :                 char **files,
    3027                 :            :                 UnitFilePresetMode mode,
    3028                 :            :                 bool force,
    3029                 :            :                 UnitFileChange **changes,
    3030                 :            :                 size_t *n_changes) {
    3031                 :            : 
    3032                 :            :         int r;
    3033                 :            : 
    3034         [ -  + ]:         28 :         assert(plus);
    3035         [ -  + ]:         28 :         assert(minus);
    3036         [ -  + ]:         28 :         assert(paths);
    3037         [ -  + ]:         28 :         assert(config_path);
    3038                 :            : 
    3039         [ +  - ]:         28 :         if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
    3040         [ +  - ]:         28 :                 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
    3041                 :            : 
    3042                 :         28 :                 r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes);
    3043         [ -  + ]:         28 :                 if (r < 0)
    3044                 :          0 :                         return r;
    3045                 :            : 
    3046                 :         28 :                 r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
    3047                 :            :         } else
    3048                 :          0 :                 r = 0;
    3049                 :            : 
    3050         [ +  - ]:         28 :         if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
    3051                 :            :                 int q;
    3052                 :            : 
    3053                 :            :                 /* Returns number of symlinks that where supposed to be installed. */
    3054                 :         28 :                 q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
    3055         [ +  - ]:         28 :                 if (r >= 0) {
    3056         [ -  + ]:         28 :                         if (q < 0)
    3057                 :          0 :                                 r = q;
    3058                 :            :                         else
    3059                 :         28 :                                 r += q;
    3060                 :            :                 }
    3061                 :            :         }
    3062                 :            : 
    3063                 :         28 :         return r;
    3064                 :            : }
    3065                 :            : 
    3066                 :        188 : static int preset_prepare_one(
    3067                 :            :                 UnitFileScope scope,
    3068                 :            :                 InstallContext *plus,
    3069                 :            :                 InstallContext *minus,
    3070                 :            :                 LookupPaths *paths,
    3071                 :            :                 const char *name,
    3072                 :            :                 Presets presets,
    3073                 :            :                 UnitFileChange **changes,
    3074                 :            :                 size_t *n_changes) {
    3075                 :            : 
    3076                 :        188 :         _cleanup_(install_context_done) InstallContext tmp = {};
    3077                 :        188 :         _cleanup_strv_free_ char **instance_name_list = NULL;
    3078                 :            :         UnitFileInstallInfo *i;
    3079                 :            :         int r;
    3080                 :            : 
    3081   [ +  -  +  + ]:        188 :         if (install_info_find(plus, name) || install_info_find(minus, name))
    3082                 :          4 :                 return 0;
    3083                 :            : 
    3084                 :        184 :         r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
    3085                 :            :                                   &i, changes, n_changes);
    3086         [ -  + ]:        184 :         if (r < 0)
    3087                 :          0 :                 return r;
    3088         [ +  + ]:        184 :         if (!streq(name, i->name)) {
    3089         [ +  - ]:         68 :                 log_debug("Skipping %s because it is an alias for %s.", name, i->name);
    3090                 :         68 :                 return 0;
    3091                 :            :         }
    3092                 :            : 
    3093                 :        116 :         r = query_presets(name, presets, &instance_name_list);
    3094         [ -  + ]:        116 :         if (r < 0)
    3095                 :          0 :                 return r;
    3096                 :            : 
    3097         [ +  + ]:        116 :         if (r > 0) {
    3098         [ +  + ]:         20 :                 if (instance_name_list) {
    3099                 :            :                         char **s;
    3100   [ +  -  +  + ]:         16 :                         STRV_FOREACH(s, instance_name_list) {
    3101                 :         12 :                                 r = install_info_discover_and_check(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
    3102                 :            :                                                                     &i, changes, n_changes);
    3103         [ -  + ]:         12 :                                 if (r < 0)
    3104                 :          0 :                                         return r;
    3105                 :            :                         }
    3106                 :            :                 } else {
    3107                 :         16 :                         r = install_info_discover_and_check(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS,
    3108                 :            :                                                             &i, changes, n_changes);
    3109         [ -  + ]:         16 :                         if (r < 0)
    3110                 :          0 :                                 return r;
    3111                 :            :                 }
    3112                 :            : 
    3113                 :            :         } else
    3114                 :         96 :                 r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS,
    3115                 :            :                                           &i, changes, n_changes);
    3116                 :            : 
    3117                 :        116 :         return r;
    3118                 :            : }
    3119                 :            : 
    3120                 :         20 : int unit_file_preset(
    3121                 :            :                 UnitFileScope scope,
    3122                 :            :                 UnitFileFlags flags,
    3123                 :            :                 const char *root_dir,
    3124                 :            :                 char **files,
    3125                 :            :                 UnitFilePresetMode mode,
    3126                 :            :                 UnitFileChange **changes,
    3127                 :            :                 size_t *n_changes) {
    3128                 :            : 
    3129                 :         20 :         _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
    3130                 :         20 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    3131                 :         20 :         _cleanup_(presets_freep) Presets presets = {};
    3132                 :            :         const char *config_path;
    3133                 :            :         char **i;
    3134                 :            :         int r;
    3135                 :            : 
    3136         [ -  + ]:         20 :         assert(scope >= 0);
    3137         [ -  + ]:         20 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    3138         [ -  + ]:         20 :         assert(mode < _UNIT_FILE_PRESET_MAX);
    3139                 :            : 
    3140                 :         20 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    3141         [ -  + ]:         20 :         if (r < 0)
    3142                 :          0 :                 return r;
    3143                 :            : 
    3144         [ -  + ]:         20 :         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
    3145         [ -  + ]:         20 :         if (!config_path)
    3146                 :          0 :                 return -ENXIO;
    3147                 :            : 
    3148                 :         20 :         r = read_presets(scope, root_dir, &presets);
    3149         [ -  + ]:         20 :         if (r < 0)
    3150                 :          0 :                 return r;
    3151                 :            : 
    3152   [ +  -  +  + ]:         40 :         STRV_FOREACH(i, files) {
    3153                 :         20 :                 r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes);
    3154         [ -  + ]:         20 :                 if (r < 0)
    3155                 :          0 :                         return r;
    3156                 :            :         }
    3157                 :            : 
    3158                 :         20 :         return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
    3159                 :            : }
    3160                 :            : 
    3161                 :          8 : int unit_file_preset_all(
    3162                 :            :                 UnitFileScope scope,
    3163                 :            :                 UnitFileFlags flags,
    3164                 :            :                 const char *root_dir,
    3165                 :            :                 UnitFilePresetMode mode,
    3166                 :            :                 UnitFileChange **changes,
    3167                 :            :                 size_t *n_changes) {
    3168                 :            : 
    3169                 :          8 :         _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
    3170                 :          8 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    3171                 :          8 :         _cleanup_(presets_freep) Presets presets = {};
    3172                 :          8 :         const char *config_path = NULL;
    3173                 :            :         char **i;
    3174                 :            :         int r;
    3175                 :            : 
    3176         [ -  + ]:          8 :         assert(scope >= 0);
    3177         [ -  + ]:          8 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    3178         [ -  + ]:          8 :         assert(mode < _UNIT_FILE_PRESET_MAX);
    3179                 :            : 
    3180                 :          8 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    3181         [ -  + ]:          8 :         if (r < 0)
    3182                 :          0 :                 return r;
    3183                 :            : 
    3184         [ -  + ]:          8 :         config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
    3185         [ -  + ]:          8 :         if (!config_path)
    3186                 :          0 :                 return -ENXIO;
    3187                 :            : 
    3188                 :          8 :         r = read_presets(scope, root_dir, &presets);
    3189         [ -  + ]:          8 :         if (r < 0)
    3190                 :          0 :                 return r;
    3191                 :            : 
    3192   [ +  -  +  + ]:        104 :         STRV_FOREACH(i, paths.search_path) {
    3193      [ +  +  - ]:         96 :                 _cleanup_closedir_ DIR *d = NULL;
    3194                 :            :                 struct dirent *de;
    3195                 :            : 
    3196                 :         96 :                 d = opendir(*i);
    3197         [ +  + ]:         96 :                 if (!d) {
    3198         [ +  - ]:         72 :                         if (errno == ENOENT)
    3199                 :         72 :                                 continue;
    3200                 :            : 
    3201                 :          0 :                         return -errno;
    3202                 :            :                 }
    3203                 :            : 
    3204   [ +  +  -  +  :        252 :                 FOREACH_DIRENT(de, d, return -errno) {
                   +  + ]
    3205                 :            : 
    3206         [ +  + ]:        180 :                         if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
    3207                 :         12 :                                 continue;
    3208                 :            : 
    3209                 :        168 :                         dirent_ensure_type(d, de);
    3210                 :            : 
    3211   [ +  -  -  + ]:        168 :                         if (!IN_SET(de->d_type, DT_LNK, DT_REG))
    3212                 :          0 :                                 continue;
    3213                 :            : 
    3214                 :            :                         /* we don't pass changes[] in, because we want to handle errors on our own */
    3215                 :        168 :                         r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0);
    3216         [ -  + ]:        168 :                         if (r == -ERFKILL)
    3217                 :          0 :                                 r = unit_file_changes_add(changes, n_changes,
    3218                 :          0 :                                                           UNIT_FILE_IS_MASKED, de->d_name, NULL);
    3219         [ -  + ]:        168 :                         else if (r == -ENOLINK)
    3220                 :          0 :                                 r = unit_file_changes_add(changes, n_changes,
    3221                 :          0 :                                                           UNIT_FILE_IS_DANGLING, de->d_name, NULL);
    3222         [ -  + ]:        168 :                         else if (r == -EADDRNOTAVAIL) /* Ignore generated/transient units when applying preset */
    3223                 :          0 :                                 continue;
    3224         [ -  + ]:        168 :                         if (r < 0)
    3225                 :          0 :                                 return r;
    3226                 :            :                 }
    3227                 :            :         }
    3228                 :            : 
    3229                 :          8 :         return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
    3230                 :            : }
    3231                 :            : 
    3232                 :       2100 : static void unit_file_list_free_one(UnitFileList *f) {
    3233         [ -  + ]:       2100 :         if (!f)
    3234                 :          0 :                 return;
    3235                 :            : 
    3236                 :       2100 :         free(f->path);
    3237                 :       2100 :         free(f);
    3238                 :            : }
    3239                 :            : 
    3240                 :          8 : Hashmap* unit_file_list_free(Hashmap *h) {
    3241         [ +  + ]:       2108 :         return hashmap_free_with_destructor(h, unit_file_list_free_one);
    3242                 :            : }
    3243                 :            : 
    3244         [ -  + ]:       2376 : DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
    3245                 :            : 
    3246                 :          8 : int unit_file_get_list(
    3247                 :            :                 UnitFileScope scope,
    3248                 :            :                 const char *root_dir,
    3249                 :            :                 Hashmap *h,
    3250                 :            :                 char **states,
    3251                 :            :                 char **patterns) {
    3252                 :            : 
    3253                 :          8 :         _cleanup_(lookup_paths_free) LookupPaths paths = {};
    3254                 :            :         char **i;
    3255                 :            :         int r;
    3256                 :            : 
    3257         [ -  + ]:          8 :         assert(scope >= 0);
    3258         [ -  + ]:          8 :         assert(scope < _UNIT_FILE_SCOPE_MAX);
    3259         [ -  + ]:          8 :         assert(h);
    3260                 :            : 
    3261                 :          8 :         r = lookup_paths_init(&paths, scope, 0, root_dir);
    3262         [ -  + ]:          8 :         if (r < 0)
    3263                 :          0 :                 return r;
    3264                 :            : 
    3265   [ +  -  +  + ]:        104 :         STRV_FOREACH(i, paths.search_path) {
    3266      [ +  +  - ]:         96 :                 _cleanup_closedir_ DIR *d = NULL;
    3267                 :            :                 struct dirent *de;
    3268                 :            : 
    3269                 :         96 :                 d = opendir(*i);
    3270         [ +  + ]:         96 :                 if (!d) {
    3271         [ +  - ]:         60 :                         if (errno == ENOENT)
    3272                 :         60 :                                 continue;
    3273   [ #  #  #  # ]:          0 :                         if (IN_SET(errno, ENOTDIR, EACCES)) {
    3274         [ #  # ]:          0 :                                 log_debug_errno(errno, "Failed to open \"%s\": %m", *i);
    3275                 :          0 :                                 continue;
    3276                 :            :                         }
    3277                 :            : 
    3278                 :          0 :                         return -errno;
    3279                 :            :                 }
    3280                 :            : 
    3281   [ +  +  -  +  :       2484 :                 FOREACH_DIRENT(de, d, return -errno) {
                   +  + ]
    3282      [ +  +  - ]:       2376 :                         _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
    3283                 :            : 
    3284         [ +  + ]:       2376 :                         if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
    3285                 :        256 :                                 continue;
    3286                 :            : 
    3287         [ -  + ]:       2120 :                         if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE))
    3288                 :          0 :                                 continue;
    3289                 :            : 
    3290         [ +  + ]:       2120 :                         if (hashmap_get(h, de->d_name))
    3291                 :         20 :                                 continue;
    3292                 :            : 
    3293                 :       2100 :                         dirent_ensure_type(d, de);
    3294                 :            : 
    3295   [ +  -  -  + ]:       2100 :                         if (!IN_SET(de->d_type, DT_LNK, DT_REG))
    3296                 :          0 :                                 continue;
    3297                 :            : 
    3298                 :       2100 :                         f = new0(UnitFileList, 1);
    3299         [ -  + ]:       2100 :                         if (!f)
    3300                 :          0 :                                 return -ENOMEM;
    3301                 :            : 
    3302                 :       2100 :                         f->path = path_make_absolute(de->d_name, *i);
    3303         [ -  + ]:       2100 :                         if (!f->path)
    3304                 :          0 :                                 return -ENOMEM;
    3305                 :            : 
    3306                 :       2100 :                         r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state);
    3307         [ -  + ]:       2100 :                         if (r < 0)
    3308                 :          0 :                                 f->state = UNIT_FILE_BAD;
    3309                 :            : 
    3310         [ -  + ]:       2100 :                         if (!strv_isempty(states) &&
    3311         [ #  # ]:          0 :                             !strv_contains(states, unit_file_state_to_string(f->state)))
    3312                 :          0 :                                 continue;
    3313                 :            : 
    3314                 :       2100 :                         r = hashmap_put(h, basename(f->path), f);
    3315         [ -  + ]:       2100 :                         if (r < 0)
    3316                 :          0 :                                 return r;
    3317                 :            : 
    3318                 :       2100 :                         f = NULL; /* prevent cleanup */
    3319                 :            :                 }
    3320                 :            :         }
    3321                 :            : 
    3322                 :          8 :         return 0;
    3323                 :            : }
    3324                 :            : 
    3325                 :            : static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
    3326                 :            :         [UNIT_FILE_ENABLED] = "enabled",
    3327                 :            :         [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
    3328                 :            :         [UNIT_FILE_LINKED] = "linked",
    3329                 :            :         [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
    3330                 :            :         [UNIT_FILE_MASKED] = "masked",
    3331                 :            :         [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
    3332                 :            :         [UNIT_FILE_STATIC] = "static",
    3333                 :            :         [UNIT_FILE_DISABLED] = "disabled",
    3334                 :            :         [UNIT_FILE_INDIRECT] = "indirect",
    3335                 :            :         [UNIT_FILE_GENERATED] = "generated",
    3336                 :            :         [UNIT_FILE_TRANSIENT] = "transient",
    3337                 :            :         [UNIT_FILE_BAD] = "bad",
    3338                 :            : };
    3339                 :            : 
    3340   [ +  +  +  + ]:       2140 : DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
    3341                 :            : 
    3342                 :            : static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
    3343                 :            :         [UNIT_FILE_SYMLINK] = "symlink",
    3344                 :            :         [UNIT_FILE_UNLINK] = "unlink",
    3345                 :            :         [UNIT_FILE_IS_MASKED] = "masked",
    3346                 :            :         [UNIT_FILE_IS_DANGLING] = "dangling",
    3347                 :            : };
    3348                 :            : 
    3349   [ +  +  +  + ]:         48 : DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
    3350                 :            : 
    3351                 :            : static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = {
    3352                 :            :         [UNIT_FILE_PRESET_FULL] = "full",
    3353                 :            :         [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
    3354                 :            :         [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
    3355                 :            : };
    3356                 :            : 
    3357   [ +  +  +  + ]:         40 : DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);

Generated by: LCOV version 1.14