]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/shared/install.c
Merge pull request #9147 from keszybz/runtime-enablement
[thirdparty/systemd.git] / src / shared / install.c
index a75a045d43d1ad1d43c01ec037f6fc4a4ad4d6f1..e715338ac59aa72affa8a4e0d555899792886d20 100644 (file)
@@ -3,19 +3,6 @@
   This file is part of systemd.
 
   Copyright 2011 Lennart Poettering
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty <of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
 #include <dirent.h>
@@ -59,8 +46,9 @@
 #define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
 
 typedef enum SearchFlags {
-        SEARCH_LOAD = 1,
-        SEARCH_FOLLOW_CONFIG_SYMLINKS = 2,
+        SEARCH_LOAD                   = 1U << 0,
+        SEARCH_FOLLOW_CONFIG_SYMLINKS = 1U << 1,
+        SEARCH_DROPIN                 = 1U << 2,
 } SearchFlags;
 
 typedef struct {
@@ -111,8 +99,6 @@ static inline void presets_freep(Presets *p) {
         p->n_rules = 0;
 }
 
-static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret);
-
 bool unit_type_may_alias(UnitType type) {
         return IN_SET(type,
                       UNIT_SERVICE,
@@ -297,7 +283,7 @@ static int path_is_vendor(const LookupPaths *p, const char *path) {
 
 int unit_file_changes_add(
                 UnitFileChange **changes,
-                unsigned *n_changes,
+                size_t *n_changes,
                 UnitFileChangeType type,
                 const char *path,
                 const char *source) {
@@ -311,7 +297,7 @@ int unit_file_changes_add(
         if (!changes)
                 return 0;
 
-        c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
+        c = reallocarray(*changes, *n_changes + 1, sizeof(UnitFileChange));
         if (!c)
                 return -ENOMEM;
         *changes = c;
@@ -323,9 +309,9 @@ int unit_file_changes_add(
         if (!p || (source && !s))
                 return -ENOMEM;
 
-        path_kill_slashes(p);
+        path_simplify(p, false);
         if (s)
-                path_kill_slashes(s);
+                path_simplify(s, false);
 
         c[*n_changes] = (UnitFileChange) { type, p, s };
         p = s = NULL;
@@ -333,8 +319,8 @@ int unit_file_changes_add(
         return 0;
 }
 
-void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
-        unsigned i;
+void unit_file_changes_free(UnitFileChange *changes, size_t n_changes) {
+        size_t i;
 
         assert(changes || n_changes == 0);
 
@@ -346,8 +332,8 @@ void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
         free(changes);
 }
 
-void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet) {
-        unsigned i;
+void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet) {
+        size_t i;
         bool logged = false;
 
         assert(changes || n_changes == 0);
@@ -446,7 +432,7 @@ static int create_symlink(
                 const char *new_path,
                 bool force,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         _cleanup_free_ char *dest = NULL, *dirname = NULL;
         const char *rp;
@@ -522,7 +508,7 @@ static int mark_symlink_for_removal(
 
         assert(p);
 
-        r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops);
+        r = set_ensure_allocated(remove_symlinks_to, &path_hash_ops);
         if (r < 0)
                 return r;
 
@@ -530,7 +516,7 @@ static int mark_symlink_for_removal(
         if (!n)
                 return -ENOMEM;
 
-        path_kill_slashes(n);
+        path_simplify(n, false);
 
         r = set_consume(*remove_symlinks_to, n);
         if (r == -EEXIST)
@@ -550,7 +536,7 @@ static int remove_marked_symlinks_fd(
                 bool dry_run,
                 bool *restart,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         _cleanup_closedir_ DIR *d = NULL;
         struct dirent *de;
@@ -612,7 +598,7 @@ static int remove_marked_symlinks_fd(
                         p = path_make_absolute(de->d_name, path);
                         if (!p)
                                 return -ENOMEM;
-                        path_kill_slashes(p);
+                        path_simplify(p, false);
 
                         q = readlink_malloc(p, &dest);
                         if (q == -ENOENT)
@@ -667,7 +653,7 @@ static int remove_marked_symlinks(
                 const LookupPaths *lp,
                 bool dry_run,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         _cleanup_close_ int fd = -1;
         bool restart;
@@ -908,16 +894,13 @@ static int find_symlinks_in_scope(
                 if (r > 0) {
                         /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */
 
-                        r = path_is_config(paths, *p, false);
-                        if (r < 0)
-                                return r;
-                        if (r > 0) {
+                        if (path_equal_ptr(*p, paths->persistent_config)) {
                                 /* This is the best outcome, let's return it immediately. */
                                 *state = UNIT_FILE_ENABLED;
                                 return 1;
                         }
 
-                        /* look for globally enablement of user units */
+                        /* look for global enablement of user units */
                         if (scope == UNIT_FILE_USER && path_is_user_config_dir(*p)) {
                                 *state = UNIT_FILE_ENABLED;
                                 return 1;
@@ -932,11 +915,7 @@ static int find_symlinks_in_scope(
                                 enabled_at_all = true;
 
                 } else if (same_name_link) {
-
-                        r = path_is_config(paths, *p, false);
-                        if (r < 0)
-                                return r;
-                        if (r > 0)
+                        if (path_equal_ptr(*p, paths->persistent_config))
                                 same_name_link_config = true;
                         else {
                                 r = path_is_runtime(paths, *p, false);
@@ -992,23 +971,11 @@ static void install_info_free(UnitFileInstallInfo *i) {
         free(i);
 }
 
-static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) {
-        UnitFileInstallInfo *i;
-
-        if (!m)
-                return NULL;
-
-        while ((i = ordered_hashmap_steal_first(m)))
-                install_info_free(i);
-
-        return ordered_hashmap_free(m);
-}
-
 static void install_context_done(InstallContext *c) {
         assert(c);
 
-        c->will_process = install_info_hashmap_free(c->will_process);
-        c->have_processed = install_info_hashmap_free(c->have_processed);
+        c->will_process = ordered_hashmap_free_with_destructor(c->will_process, install_info_free);
+        c->have_processed = ordered_hashmap_free_with_destructor(c->have_processed, install_info_free);
 }
 
 static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) {
@@ -1025,7 +992,7 @@ static int install_info_may_process(
                 UnitFileInstallInfo *i,
                 const LookupPaths *paths,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
         assert(i);
         assert(paths);
 
@@ -1240,6 +1207,7 @@ static int unit_file_load(
                 InstallContext *c,
                 UnitFileInstallInfo *info,
                 const char *path,
+                const char *root_dir,
                 SearchFlags flags) {
 
         const ConfigTableItem items[] = {
@@ -1260,52 +1228,72 @@ static int unit_file_load(
         assert(info);
         assert(path);
 
-        type = unit_name_to_type(info->name);
-        if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) &&
-            !unit_type_may_template(type))
-                return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type));
+        if (!(flags & SEARCH_DROPIN)) {
+                /* Loading or checking for the main unit fileā€¦ */
 
-        if (!(flags & SEARCH_LOAD)) {
-                r = lstat(path, &st);
-                if (r < 0)
+                type = unit_name_to_type(info->name);
+                if (type < 0)
+                        return -EINVAL;
+                if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type)) {
+                        log_error("Unit type %s cannot be templated.", unit_type_to_string(type));
+                        return -EINVAL;
+                }
+
+                if (!(flags & SEARCH_LOAD)) {
+                        r = lstat(path, &st);
+                        if (r < 0)
+                                return -errno;
+
+                        if (null_or_empty(&st))
+                                info->type = UNIT_FILE_TYPE_MASKED;
+                        else if (S_ISREG(st.st_mode))
+                                info->type = UNIT_FILE_TYPE_REGULAR;
+                        else if (S_ISLNK(st.st_mode))
+                                return -ELOOP;
+                        else if (S_ISDIR(st.st_mode))
+                                return -EISDIR;
+                        else
+                                return -ENOTTY;
+
+                        return 0;
+                }
+
+                fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
+                if (fd < 0)
                         return -errno;
+        } else {
+                /* 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. */
 
-                if (null_or_empty(&st))
-                        info->type = UNIT_FILE_TYPE_MASKED;
-                else if (S_ISREG(st.st_mode))
-                        info->type = UNIT_FILE_TYPE_REGULAR;
-                else if (S_ISLNK(st.st_mode))
-                        return -ELOOP;
-                else if (S_ISDIR(st.st_mode))
-                        return -EISDIR;
-                else
-                        return -ENOTTY;
+                if (!(flags & SEARCH_LOAD))
+                        return 0;
 
-                return 0;
+                fd = chase_symlinks_and_open(path, root_dir, 0, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
+                if (fd < 0)
+                        return fd;
         }
 
-        /* c is only needed if we actually load the file */
-        assert(c);
-
-        fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
-        if (fd < 0)
-                return -errno;
         if (fstat(fd, &st) < 0)
                 return -errno;
+
         if (null_or_empty(&st)) {
-                info->type = UNIT_FILE_TYPE_MASKED;
+                if ((flags & SEARCH_DROPIN) == 0)
+                        info->type = UNIT_FILE_TYPE_MASKED;
+
                 return 0;
         }
-        if (S_ISDIR(st.st_mode))
-                return -EISDIR;
-        if (!S_ISREG(st.st_mode))
-                return -ENOTTY;
+
+        r = stat_verify_regular(&st);
+        if (r < 0)
+                return r;
 
         f = fdopen(fd, "re");
         if (!f)
                 return -errno;
         fd = -1;
 
+        /* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
+        assert(c);
+
         r = config_parse(info->name, path, f,
                          NULL,
                          config_item_table_lookup, items,
@@ -1313,7 +1301,8 @@ static int unit_file_load(
         if (r < 0)
                 return log_debug_errno(r, "Failed to parse %s: %m", info->name);
 
-        info->type = UNIT_FILE_TYPE_REGULAR;
+        if ((flags & SEARCH_DROPIN) == 0)
+                info->type = UNIT_FILE_TYPE_REGULAR;
 
         return
                 (int) strv_length(info->aliases) +
@@ -1331,8 +1320,8 @@ static int unit_file_load_or_readlink(
         _cleanup_free_ char *target = NULL;
         int r;
 
-        r = unit_file_load(c, info, path, flags);
-        if (r != -ELOOP)
+        r = unit_file_load(c, info, path, root_dir, flags);
+        if (r != -ELOOP || (flags & SEARCH_DROPIN))
                 return r;
 
         /* This is a symlink, let's read it. */
@@ -1395,16 +1384,12 @@ static int unit_file_search(
                 const LookupPaths *paths,
                 SearchFlags flags) {
 
+        const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL;
+        _cleanup_strv_free_ char **dirs = NULL, **files = NULL;
         _cleanup_free_ char *template = NULL;
-        _cleanup_strv_free_ char **dirs = NULL;
-        _cleanup_strv_free_ char **files = NULL;
-        const char *dropin_dir_name = NULL;
-        const char *dropin_template_dir_name = NULL;
-
-        char **p;
-        int r;
-        int result;
         bool found_unit = false;
+        int r, result;
+        char **p;
 
         assert(info);
         assert(paths);
@@ -1432,10 +1417,8 @@ static int unit_file_search(
                         return -ENOMEM;
 
                 r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
-
                 if (r >= 0) {
-                        info->path = path;
-                        path = NULL;
+                        info->path = TAKE_PTR(path);
                         result = r;
                         found_unit = true;
                         break;
@@ -1458,8 +1441,7 @@ static int unit_file_search(
 
                         r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
                         if (r >= 0) {
-                                info->path = path;
-                                path = NULL;
+                                info->path = TAKE_PTR(path);
                                 result = r;
                                 found_unit = true;
                                 break;
@@ -1473,6 +1455,9 @@ static int unit_file_search(
                 return -ENOENT;
         }
 
+        if (info->type == UNIT_FILE_TYPE_MASKED)
+                return result;
+
         /* Search for drop-in directories */
 
         dropin_dir_name = strjoina(info->name, ".d");
@@ -1510,7 +1495,7 @@ static int unit_file_search(
                 return log_debug_errno(r, "Failed to get list of conf files: %m");
 
         STRV_FOREACH(p, files) {
-                r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags);
+                r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags | SEARCH_DROPIN);
                 if (r < 0)
                         return log_debug_errno(r, "Failed to load conf file %s: %m", *p);
         }
@@ -1675,7 +1660,7 @@ static int install_info_discover(
                 SearchFlags flags,
                 UnitFileInstallInfo **ret,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         UnitFileInstallInfo *i;
         int r;
@@ -1699,7 +1684,7 @@ static int install_info_symlink_alias(
                 const char *config_path,
                 bool force,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         char **s;
         int r = 0, q;
@@ -1734,7 +1719,7 @@ static int install_info_symlink_wants(
                 char **list,
                 const char *suffix,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         _cleanup_free_ char *buf = NULL;
         const char *n;
@@ -1763,8 +1748,7 @@ static int install_info_symlink_wants(
                 if (r < 0)
                         return r;
 
-                path = instance.path;
-                instance.path = NULL;
+                path = TAKE_PTR(instance.path);
 
                 if (instance.type == UNIT_FILE_TYPE_MASKED) {
                         unit_file_changes_add(changes, n_changes, -ERFKILL, path, NULL);
@@ -1805,7 +1789,7 @@ static int install_info_symlink_link(
                 const char *config_path,
                 bool force,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         _cleanup_free_ char *path = NULL;
         int r;
@@ -1834,7 +1818,7 @@ static int install_info_apply(
                 const char *config_path,
                 bool force,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         int r, q;
 
@@ -1871,7 +1855,7 @@ static int install_context_apply(
                 bool force,
                 SearchFlags flags,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         UnitFileInstallInfo *i;
         int r;
@@ -1932,16 +1916,14 @@ static int install_context_mark_for_removal(
                 InstallContext *c,
                 const LookupPaths *paths,
                 Set **remove_symlinks_to,
-                const char *config_path,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         UnitFileInstallInfo *i;
         int r;
 
         assert(c);
         assert(paths);
-        assert(config_path);
 
         /* Marks all items for removal */
 
@@ -1997,9 +1979,9 @@ int unit_file_mask(
                 const char *root_dir,
                 char **files,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         const char *config_path;
         char **i;
         int r;
@@ -2043,15 +2025,15 @@ int unit_file_unmask(
                 const char *root_dir,
                 char **files,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
         _cleanup_strv_free_ char **todo = NULL;
         size_t n_todo = 0, n_allocated = 0;
         const char *config_path;
         char **i;
-        bool dry_run;
+        bool dry_run = !!(flags & UNIT_FILE_DRY_RUN);
         int r, q;
 
         assert(scope >= 0);
@@ -2061,73 +2043,71 @@ int unit_file_unmask(
         if (r < 0)
                 return r;
 
-        config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
-        if (!config_path)
-                return -ENXIO;
-
-        dry_run = !!(flags & UNIT_FILE_DRY_RUN);
-
         STRV_FOREACH(i, files) {
-                _cleanup_free_ char *path = NULL;
-
                 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
                         return -EINVAL;
 
-                path = path_make_absolute(*i, config_path);
-                if (!path)
-                        return -ENOMEM;
+                FOREACH_STRING(config_path, paths.runtime_config, paths.persistent_config) {
+                        _cleanup_free_ char *path = NULL;
 
-                r = null_or_empty_path(path);
-                if (r == -ENOENT)
-                        continue;
-                if (r < 0)
-                        return r;
-                if (r == 0)
-                        continue;
+                        path = path_make_absolute(*i, config_path);
+                        if (!path)
+                                return -ENOMEM;
 
-                if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
-                        return -ENOMEM;
+                        r = null_or_empty_path(path);
+                        if (r == -ENOENT)
+                                continue;
+                        if (r < 0)
+                                return r;
+                        if (r == 0)
+                                continue;
 
-                todo[n_todo] = strdup(*i);
-                if (!todo[n_todo])
-                        return -ENOMEM;
+                        if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
+                                return -ENOMEM;
 
-                n_todo++;
+                        todo[n_todo] = strdup(*i);
+                        if (!todo[n_todo])
+                                return -ENOMEM;
+
+                        n_todo++;
+                }
         }
 
         strv_uniq(todo);
 
         r = 0;
-        STRV_FOREACH(i, todo) {
-                _cleanup_free_ char *path = NULL;
-                const char *rp;
+        FOREACH_STRING(config_path, paths.runtime_config, paths.persistent_config) {
+                STRV_FOREACH(i, todo) {
+                        _cleanup_free_ char *path = NULL;
+                        const char *rp;
 
-                path = path_make_absolute(*i, config_path);
-                if (!path)
-                        return -ENOMEM;
+                        path = path_make_absolute(*i, config_path);
+                        if (!path)
+                                return -ENOMEM;
 
-                if (!dry_run && unlink(path) < 0) {
-                        if (errno != ENOENT) {
-                                if (r >= 0)
-                                        r = -errno;
-                                unit_file_changes_add(changes, n_changes, -errno, path, NULL);
+                        if (!dry_run && unlink(path) < 0) {
+                                if (errno != ENOENT) {
+                                        if (r >= 0)
+                                                r = -errno;
+                                        unit_file_changes_add(changes, n_changes, -errno, path, NULL);
+                                }
+
+                                continue;
                         }
 
-                        continue;
-                }
+                        unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
 
-                unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+                        rp = skip_root(&paths, path);
+                        q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
+                        if (q < 0)
+                                return q;
+                }
 
-                rp = skip_root(&paths, path);
-                q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path);
-                if (q < 0)
-                        return q;
+                q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
+                if (r >= 0)
+                        r = q;
         }
 
-        q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
-        if (r >= 0)
-                r = q;
-
         return r;
 }
 
@@ -2137,9 +2117,9 @@ int unit_file_link(
                 const char *root_dir,
                 char **files,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_strv_free_ char **todo = NULL;
         size_t n_todo = 0, n_allocated = 0;
         const char *config_path;
@@ -2175,12 +2155,9 @@ int unit_file_link(
 
                 if (lstat(full, &st) < 0)
                         return -errno;
-                if (S_ISLNK(st.st_mode))
-                        return -ELOOP;
-                if (S_ISDIR(st.st_mode))
-                        return -EISDIR;
-                if (!S_ISREG(st.st_mode))
-                        return -ENOTTY;
+                r = stat_verify_regular(&st);
+                if (r < 0)
+                        return r;
 
                 q = in_search_path(&paths, *i);
                 if (q < 0)
@@ -2240,10 +2217,10 @@ int unit_file_revert(
                 const char *root_dir,
                 char **files,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_strv_free_ char **todo = NULL;
         size_t n_todo = 0, n_allocated = 0;
         char **i;
@@ -2309,8 +2286,7 @@ int unit_file_revert(
                                         if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
                                                 return -ENOMEM;
 
-                                        todo[n_todo++] = dropin;
-                                        dropin = NULL;
+                                        todo[n_todo++] = TAKE_PTR(dropin);
                                 }
                         }
                 }
@@ -2339,8 +2315,7 @@ int unit_file_revert(
                                         if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2))
                                                 return -ENOMEM;
 
-                                        todo[n_todo++] = path;
-                                        path = NULL;
+                                        todo[n_todo++] = TAKE_PTR(path);
                                 }
                         }
                 }
@@ -2399,9 +2374,9 @@ int unit_file_add_dependency(
                 const char *target,
                 UnitDependency dep,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_(install_context_done) InstallContext c = {};
         UnitFileInstallInfo *i, *target_info;
         const char *config_path;
@@ -2473,9 +2448,9 @@ int unit_file_enable(
                 const char *root_dir,
                 char **files,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_(install_context_done) InstallContext c = {};
         const char *config_path;
         UnitFileInstallInfo *i;
@@ -2519,11 +2494,12 @@ int unit_file_disable(
                 const char *root_dir,
                 char **files,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_(install_context_done) InstallContext c = {};
         _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+        bool dry_run = !!(flags & UNIT_FILE_DRY_RUN);
         const char *config_path;
         char **i;
         int r;
@@ -2535,10 +2511,6 @@ int unit_file_disable(
         if (r < 0)
                 return r;
 
-        config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
-        if (!config_path)
-                return -ENXIO;
-
         STRV_FOREACH(i, files) {
                 if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
                         return -EINVAL;
@@ -2548,11 +2520,17 @@ int unit_file_disable(
                         return r;
         }
 
-        r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes);
+        r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, changes, n_changes);
         if (r < 0)
                 return r;
 
-        return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes);
+        FOREACH_STRING(config_path, paths.runtime_config, paths.persistent_config) {
+                r = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
 }
 
 int unit_file_reenable(
@@ -2561,7 +2539,7 @@ int unit_file_reenable(
                 const char *root_dir,
                 char **files,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         char **n;
         int r;
@@ -2588,9 +2566,9 @@ int unit_file_set_default(
                 const char *root_dir,
                 const char *name,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_(install_context_done) InstallContext c = {};
         UnitFileInstallInfo *i;
         const char *new_path;
@@ -2625,7 +2603,7 @@ int unit_file_get_default(
                 const char *root_dir,
                 char **name) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_(install_context_done) InstallContext c = {};
         UnitFileInstallInfo *i;
         char *n;
@@ -2655,7 +2633,7 @@ int unit_file_get_default(
         return 0;
 }
 
-static int unit_file_lookup_state(
+int unit_file_lookup_state(
                 UnitFileScope scope,
                 const LookupPaths *paths,
                 const char *name,
@@ -2749,7 +2727,7 @@ int unit_file_get_state(
                 const char *name,
                 UnitFileState *ret) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         int r;
 
         assert(scope >= 0);
@@ -2793,25 +2771,31 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres
         assert(scope < _UNIT_FILE_SCOPE_MAX);
         assert(presets);
 
-        if (scope == UNIT_FILE_SYSTEM)
+        switch (scope) {
+        case UNIT_FILE_SYSTEM:
                 r = conf_files_list(&files, ".preset", root_dir, 0,
                                     "/etc/systemd/system-preset",
+                                    "/run/systemd/system-preset",
                                     "/usr/local/lib/systemd/system-preset",
                                     "/usr/lib/systemd/system-preset",
 #if HAVE_SPLIT_USR
                                     "/lib/systemd/system-preset",
 #endif
                                     NULL);
-        else if (scope == UNIT_FILE_GLOBAL)
+                break;
+
+        case UNIT_FILE_GLOBAL:
+        case UNIT_FILE_USER:
                 r = conf_files_list(&files, ".preset", root_dir, 0,
                                     "/etc/systemd/user-preset",
+                                    "/run/systemd/user-preset",
                                     "/usr/local/lib/systemd/user-preset",
                                     "/usr/lib/systemd/user-preset",
                                     NULL);
-        else {
-                *presets = (Presets){};
+                break;
 
-                return 0;
+        default:
+                assert_not_reached("Invalid unit file scope");
         }
 
         if (r < 0)
@@ -2930,45 +2914,45 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char
 
 static int execute_preset(
                 UnitFileScope scope,
+                UnitFileFlags flags,
                 InstallContext *plus,
                 InstallContext *minus,
                 const LookupPaths *paths,
-                const char *config_path,
                 char **files,
                 UnitFilePresetMode mode,
-                bool force,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
-        int r;
+        const char *config_path;
+        bool force = !!(flags & UNIT_FILE_FORCE);
+        bool runtime = !!(flags & UNIT_FILE_RUNTIME);
+        int r = 0, q;
 
         assert(plus);
         assert(minus);
         assert(paths);
-        assert(config_path);
 
         if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
                 _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
 
-                r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes);
-                if (r < 0)
-                        return r;
+                q = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, changes, n_changes);
+                if (q < 0)
+                        return q;
 
-                r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
-        } else
-                r = 0;
+                FOREACH_STRING(config_path, paths->runtime_config, paths->persistent_config) {
+                        q = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes);
+                        if (r == 0)
+                                r = q;
+                }
+        }
 
         if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
-                int q;
-
                 /* Returns number of symlinks that where supposed to be installed. */
-                q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes);
-                if (r >= 0) {
-                        if (q < 0)
-                                r = q;
-                        else
-                                r += q;
-                }
+                q = install_context_apply(scope, plus, paths,
+                                          runtime ? paths->runtime_config : paths->persistent_config,
+                                          force, SEARCH_LOAD, changes, n_changes);
+                if (r == 0)
+                        r = q;
         }
 
         return r;
@@ -2982,7 +2966,7 @@ static int preset_prepare_one(
                 const char *name,
                 Presets presets,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         _cleanup_(install_context_done) InstallContext tmp = {};
         UnitFileInstallInfo *i;
@@ -3027,12 +3011,11 @@ int unit_file_preset(
                 char **files,
                 UnitFilePresetMode mode,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_(presets_freep) Presets presets = {};
-        const char *config_path;
         char **i;
         int r;
 
@@ -3044,10 +3027,6 @@ int unit_file_preset(
         if (r < 0)
                 return r;
 
-        config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
-        if (!config_path)
-                return -ENXIO;
-
         r = read_presets(scope, root_dir, &presets);
         if (r < 0)
                 return r;
@@ -3058,7 +3037,7 @@ int unit_file_preset(
                         return r;
         }
 
-        return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
+        return execute_preset(scope, flags, &plus, &minus, &paths, files, mode, changes, n_changes);
 }
 
 int unit_file_preset_all(
@@ -3067,12 +3046,11 @@ int unit_file_preset_all(
                 const char *root_dir,
                 UnitFilePresetMode mode,
                 UnitFileChange **changes,
-                unsigned *n_changes) {
+                size_t *n_changes) {
 
         _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         _cleanup_(presets_freep) Presets presets = {};
-        const char *config_path = NULL;
         char **i;
         int r;
 
@@ -3084,10 +3062,6 @@ int unit_file_preset_all(
         if (r < 0)
                 return r;
 
-        config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config;
-        if (!config_path)
-                return -ENXIO;
-
         r = read_presets(scope, root_dir, &presets);
         if (r < 0)
                 return r;
@@ -3105,7 +3079,6 @@ int unit_file_preset_all(
                 }
 
                 FOREACH_DIRENT(de, d, return -errno) {
-
                         if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY))
                                 continue;
 
@@ -3129,7 +3102,7 @@ int unit_file_preset_all(
                 }
         }
 
-        return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes);
+        return execute_preset(scope, flags, &plus, &minus, &paths, NULL, mode, changes, n_changes);
 }
 
 static void unit_file_list_free_one(UnitFileList *f) {
@@ -3141,12 +3114,7 @@ static void unit_file_list_free_one(UnitFileList *f) {
 }
 
 Hashmap* unit_file_list_free(Hashmap *h) {
-        UnitFileList *i;
-
-        while ((i = hashmap_steal_first(h)))
-                unit_file_list_free_one(i);
-
-        return hashmap_free(h);
+        return hashmap_free_with_destructor(h, unit_file_list_free_one);
 }
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
@@ -3158,7 +3126,7 @@ int unit_file_get_list(
                 char **states,
                 char **patterns) {
 
-        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(lookup_paths_free) LookupPaths paths = {};
         char **i;
         int r;