chiark / gitweb /
install, cgtop: adjust hashmap_move_one() callers for -ENOMEM possibility
[elogind.git] / src / shared / install.c
index d2da0d8a2c6375035e12261aeb3b9d9bba79a6ac..0d7c30e29d67415db34c177d1db2db83b2d92927 100644 (file)
 #include "install.h"
 #include "conf-parser.h"
 #include "conf-files.h"
+#include "specifier.h"
+#include "install-printf.h"
+#include "special.h"
 
 typedef struct {
-        char *name;
-        char *path;
+        OrderedHashmap *will_install;
+        OrderedHashmap *have_installed;
+} InstallContext;
 
-        char **aliases;
-        char **wanted_by;
-        char **required_by;
-} InstallInfo;
+static int in_search_path(const char *path, char **search) {
+        _cleanup_free_ char *parent = NULL;
+        int r;
 
-typedef struct {
-        Hashmap *will_install;
-        Hashmap *have_installed;
-} InstallContext;
+        assert(path);
+
+        r = path_get_parent(path, &parent);
+        if (r < 0)
+                return r;
+
+        return strv_contains(search, parent);
+}
 
-static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) {
+static int lookup_paths_init_from_scope(LookupPaths *paths,
+                                        UnitFileScope scope,
+                                        const char *root_dir) {
         assert(paths);
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
@@ -61,6 +70,7 @@ static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope)
         return lookup_paths_init(paths,
                                  scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
                                  scope == UNIT_FILE_USER,
+                                 root_dir,
                                  NULL, NULL, NULL);
 }
 
@@ -76,15 +86,10 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
 
         case UNIT_FILE_SYSTEM:
 
-                if (root_dir && runtime)
-                        asprintf(&p, "%s/run/systemd/system", root_dir);
-                else if (runtime)
-                        p = strdup("/run/systemd/system");
-                else if (root_dir)
-                        asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH);
+                if (runtime)
+                        p = path_join(root_dir, "/run/systemd/system", NULL);
                 else
-                        p = strdup(SYSTEM_CONFIG_UNIT_PATH);
-
+                        p = path_join(root_dir, SYSTEM_CONFIG_UNIT_PATH, NULL);
                 break;
 
         case UNIT_FILE_GLOBAL:
@@ -100,10 +105,14 @@ static int get_config_path(UnitFileScope scope, bool runtime, const char *root_d
 
         case UNIT_FILE_USER:
 
-                if (root_dir || runtime)
+                if (root_dir)
                         return -EINVAL;
 
-                r = user_config_home(&p);
+                if (runtime)
+                        r = user_runtime_dir(&p);
+                else
+                        r = user_config_home(&p);
+
                 if (r <= 0)
                         return r < 0 ? r : -ENOENT;
 
@@ -148,12 +157,16 @@ static int add_file_change(
         if (!c[i].path)
                 return -ENOMEM;
 
+        path_kill_slashes(c[i].path);
+
         if (source) {
                 c[i].source = strdup(source);
                 if (!c[i].source) {
                         free(c[i].path);
                         return -ENOMEM;
                 }
+
+                path_kill_slashes(c[i].path);
         } else
                 c[i].source = NULL;
 
@@ -170,7 +183,7 @@ static int mark_symlink_for_removal(
 
         assert(p);
 
-        r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func);
+        r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops);
         if (r < 0)
                 return r;
 
@@ -180,11 +193,9 @@ static int mark_symlink_for_removal(
 
         path_kill_slashes(n);
 
-        r = set_put(*remove_symlinks_to, n);
-        if (r < 0) {
-                free(n);
+        r = set_consume(*remove_symlinks_to, n);
+        if (r < 0)
                 return r == -EEXIST ? 0 : r;
-        }
 
         return 0;
 }
@@ -197,10 +208,10 @@ static int remove_marked_symlinks_fd(
                 bool *deleted,
                 UnitFileChange **changes,
                 unsigned *n_changes,
-                char** files) {
+                char** instance_whitelist) {
 
+        _cleanup_closedir_ DIR *d = NULL;
         int r = 0;
-        DIR *d;
 
         assert(remove_symlinks_to);
         assert(fd >= 0);
@@ -210,7 +221,7 @@ static int remove_marked_symlinks_fd(
 
         d = fdopendir(fd);
         if (!d) {
-                close_nointr_nofail(fd);
+                safe_close(fd);
                 return -errno;
         }
 
@@ -218,11 +229,10 @@ static int remove_marked_symlinks_fd(
 
         for (;;) {
                 struct dirent *de;
-                union dirent_storage buf;
-                int k;
 
-                k = readdir_r(d, &buf.de, &de);
-                if (k != 0) {
+                errno = 0;
+                de = readdir(d);
+                if (!de && errno != 0) {
                         r = -errno;
                         break;
                 }
@@ -237,7 +247,7 @@ static int remove_marked_symlinks_fd(
 
                 if (de->d_type == DT_DIR) {
                         int nfd, q;
-                        char *p;
+                        _cleanup_free_ char *p = NULL;
 
                         nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
                         if (nfd < 0) {
@@ -251,33 +261,48 @@ static int remove_marked_symlinks_fd(
 
                         p = path_make_absolute(de->d_name, path);
                         if (!p) {
-                                close_nointr_nofail(nfd);
-                                r = -ENOMEM;
-                                break;
+                                safe_close(nfd);
+                                return -ENOMEM;
                         }
 
                         /* This will close nfd, regardless whether it succeeds or not */
-                        q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, files);
-                        free(p);
-
-                        if (r == 0)
+                        q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, instance_whitelist);
+                        if (q < 0 && r == 0)
                                 r = q;
 
                 } else if (de->d_type == DT_LNK) {
-                        char *p, *dest;
+                        _cleanup_free_ char *p = NULL, *dest = NULL;
                         int q;
                         bool found;
 
-                        p = path_make_absolute(de->d_name, path);
-                        if (!p) {
-                                r = -ENOMEM;
-                                break;
+                        if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+                                continue;
+
+                        if (unit_name_is_instance(de->d_name) &&
+                            instance_whitelist &&
+                            !strv_contains(instance_whitelist, de->d_name)) {
+
+                                _cleanup_free_ char *w;
+
+                                /* OK, the file is not listed directly
+                                 * in the whitelist, so let's check if
+                                 * the template of it might be
+                                 * listed. */
+
+                                w = unit_name_template(de->d_name);
+                                if (!w)
+                                        return -ENOMEM;
+
+                                if (!strv_contains(instance_whitelist, w))
+                                        continue;
                         }
 
+                        p = path_make_absolute(de->d_name, path);
+                        if (!p)
+                                return -ENOMEM;
+
                         q = readlink_and_canonicalize(p, &dest);
                         if (q < 0) {
-                                free(p);
-
                                 if (q == -ENOENT)
                                         continue;
 
@@ -288,42 +313,33 @@ static int remove_marked_symlinks_fd(
 
                         found =
                                 set_get(remove_symlinks_to, dest) ||
-                                set_get(remove_symlinks_to, path_get_file_name(dest));
+                                set_get(remove_symlinks_to, basename(dest));
+
+                        if (!found)
+                                continue;
 
-                        if (unit_name_is_instance(p))
-                                found = found && strv_contains(files, path_get_file_name(p));
+                        if (unlink(p) < 0 && errno != ENOENT) {
+                                if (r == 0)
+                                        r = -errno;
+                                continue;
+                        }
 
-                        if (found) {
+                        path_kill_slashes(p);
+                        rmdir_parents(p, config_path);
+                        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
 
-                                if (unlink(p) < 0 && errno != ENOENT) {
+                        if (!set_get(remove_symlinks_to, p)) {
 
+                                q = mark_symlink_for_removal(&remove_symlinks_to, p);
+                                if (q < 0) {
                                         if (r == 0)
-                                                r = -errno;
-                                } else {
-                                        rmdir_parents(p, config_path);
-                                        path_kill_slashes(p);
-
-                                        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
-
-                                        if (!set_get(remove_symlinks_to, p)) {
-
-                                                q = mark_symlink_for_removal(&remove_symlinks_to, p);
-                                                if (q < 0) {
-                                                        if (r == 0)
-                                                                r = q;
-                                                } else
-                                                        *deleted = true;
-                                        }
-                                }
+                                                r = q;
+                                } else
+                                        *deleted = true;
                         }
-
-                        free(p);
-                        free(dest);
                 }
         }
 
-        closedir(d);
-
         return r;
 }
 
@@ -332,9 +348,10 @@ static int remove_marked_symlinks(
                 const char *config_path,
                 UnitFileChange **changes,
                 unsigned *n_changes,
-                char** files) {
+                char** instance_whitelist) {
 
-        int fd, r = 0;
+        _cleanup_close_ int fd = -1;
+        int r = 0;
         bool deleted;
 
         assert(config_path);
@@ -350,20 +367,18 @@ static int remove_marked_symlinks(
                 int q, cfd;
                 deleted = false;
 
-                cfd = dup(fd);
+                cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
                 if (cfd < 0) {
                         r = -errno;
                         break;
                 }
 
                 /* This takes possession of cfd and closes it */
-                q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, files);
+                q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, instance_whitelist);
                 if (r == 0)
                         r = q;
         } while (deleted);
 
-        close_nointr_nofail(fd);
-
         return r;
 }
 
@@ -375,7 +390,7 @@ static int find_symlinks_fd(
                 bool *same_name_link) {
 
         int r = 0;
-        DIR _cleanup_closedir_ *d = NULL;
+        _cleanup_closedir_ DIR *d = NULL;
 
         assert(name);
         assert(fd >= 0);
@@ -385,17 +400,16 @@ static int find_symlinks_fd(
 
         d = fdopendir(fd);
         if (!d) {
-                close_nointr_nofail(fd);
+                safe_close(fd);
                 return -errno;
         }
 
         for (;;) {
-                int k;
                 struct dirent *de;
-                union dirent_storage buf;
 
-                k = readdir_r(d, &buf.de, &de);
-                if (k != 0)
+                errno = 0;
+                de = readdir(d);
+                if (!de && errno != 0)
                         return -errno;
 
                 if (!de)
@@ -408,7 +422,7 @@ static int find_symlinks_fd(
 
                 if (de->d_type == DT_DIR) {
                         int nfd, q;
-                        char _cleanup_free_ *p = NULL;
+                        _cleanup_free_ char *p = NULL;
 
                         nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
                         if (nfd < 0) {
@@ -422,21 +436,19 @@ static int find_symlinks_fd(
 
                         p = path_make_absolute(de->d_name, path);
                         if (!p) {
-                                close_nointr_nofail(nfd);
+                                safe_close(nfd);
                                 return -ENOMEM;
                         }
 
                         /* This will close nfd, regardless whether it succeeds or not */
                         q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
-
                         if (q > 0)
                                 return 1;
-
                         if (r == 0)
                                 r = q;
 
                 } else if (de->d_type == DT_LNK) {
-                        char _cleanup_free_ *p = NULL, *dest = NULL;
+                        _cleanup_free_ char *p = NULL, *dest = NULL;
                         bool found_path, found_dest, b = false;
                         int q;
 
@@ -468,10 +480,10 @@ static int find_symlinks_fd(
                         if (path_is_absolute(name))
                                 found_dest = path_equal(dest, name);
                         else
-                                found_dest = streq(path_get_file_name(dest), name);
+                                found_dest = streq(basename(dest), name);
 
                         if (found_path && found_dest) {
-                                char _cleanup_free_ *t = NULL;
+                                _cleanup_free_ char *t = NULL;
 
                                 /* Filter out same name links in the main
                                  * config path */
@@ -488,8 +500,6 @@ static int find_symlinks_fd(
                                 return 1;
                 }
         }
-
-        return r;
 }
 
 static int find_symlinks(
@@ -521,27 +531,25 @@ static int find_symlinks_in_scope(
                 UnitFileState *state) {
 
         int r;
-        char _cleanup_free_ *path = NULL;
+        _cleanup_free_ char *path = NULL;
         bool same_name_link_runtime = false, same_name_link = false;
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
         assert(name);
 
-        if (scope == UNIT_FILE_SYSTEM || scope == UNIT_FILE_GLOBAL) {
 
-                /* First look in runtime config path */
-                r = get_config_path(scope, true, root_dir, &path);
-                if (r < 0)
-                        return r;
+        /* First look in runtime config path */
+        r = get_config_path(scope, true, root_dir, &path);
+        if (r < 0)
+                return r;
 
-                r = find_symlinks(name, path, &same_name_link_runtime);
-                if (r < 0)
-                        return r;
-                else if (r > 0) {
-                        *state = UNIT_FILE_ENABLED_RUNTIME;
-                        return r;
-                }
+        r = find_symlinks(name, path, &same_name_link_runtime);
+        if (r < 0)
+                return r;
+        else if (r > 0) {
+                *state = UNIT_FILE_ENABLED_RUNTIME;
+                return r;
         }
 
         /* Then look in the normal config path */
@@ -574,13 +582,13 @@ int unit_file_mask(
                 UnitFileScope scope,
                 bool runtime,
                 const char *root_dir,
-                char *files[],
+                char **files,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
         char **i;
-        char _cleanup_free_ *prefix;
+        _cleanup_free_ char *prefix = NULL;
         int r;
 
         assert(scope >= 0);
@@ -591,9 +599,9 @@ int unit_file_mask(
                 return r;
 
         STRV_FOREACH(i, files) {
-                char _cleanup_free_ *path = NULL;
+                _cleanup_free_ char *path = NULL;
 
-                if (!unit_name_is_valid(*i, true)) {
+                if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
                         if (r == 0)
                                 r = -EINVAL;
                         continue;
@@ -607,7 +615,6 @@ int unit_file_mask(
 
                 if (symlink("/dev/null", path) >= 0) {
                         add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
-
                         continue;
                 }
 
@@ -617,13 +624,9 @@ int unit_file_mask(
                                 continue;
 
                         if (force) {
-                                unlink(path);
-
-                                if (symlink("/dev/null", path) >= 0) {
-
+                                if (symlink_atomic("/dev/null", path) >= 0) {
                                         add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
                                         add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
-
                                         continue;
                                 }
                         }
@@ -643,7 +646,7 @@ int unit_file_unmask(
                 UnitFileScope scope,
                 bool runtime,
                 const char *root_dir,
-                char *files[],
+                char **files,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
@@ -661,7 +664,7 @@ int unit_file_unmask(
         STRV_FOREACH(i, files) {
                 char *path;
 
-                if (!unit_name_is_valid(*i, true)) {
+                if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
                         if (r == 0)
                                 r = -EINVAL;
                         continue;
@@ -708,36 +711,36 @@ int unit_file_link(
                 UnitFileScope scope,
                 bool runtime,
                 const char *root_dir,
-                char *files[],
+                char **files,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        LookupPaths paths;
-        char **i, *config_path = NULL;
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        char **i;
+        _cleanup_free_ char *config_path = NULL;
         int r, q;
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
 
-        zero(paths);
-
-        r = lookup_paths_init_from_scope(&paths, scope);
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
         if (r < 0)
                 return r;
 
         r = get_config_path(scope, runtime, root_dir, &config_path);
         if (r < 0)
-                goto finish;
+                return r;
 
         STRV_FOREACH(i, files) {
-                char *path, *fn;
+                _cleanup_free_ char *path = NULL;
+                char *fn;
                 struct stat st;
 
-                fn = path_get_file_name(*i);
+                fn = basename(*i);
 
                 if (!path_is_absolute(*i) ||
-                    !unit_name_is_valid(fn, true)) {
+                    !unit_name_is_valid(fn, TEMPLATE_VALID)) {
                         if (r == 0)
                                 r = -EINVAL;
                         continue;
@@ -755,58 +758,38 @@ int unit_file_link(
                 }
 
                 q = in_search_path(*i, paths.unit_path);
-                if (q < 0) {
-                        r = q;
-                        break;
-                }
+                if (q < 0)
+                        return q;
 
                 if (q > 0)
                         continue;
 
                 path = path_make_absolute(fn, config_path);
-                if (!path) {
-                        r = -ENOMEM;
-                        break;
-                }
+                if (!path)
+                        return -ENOMEM;
 
                 if (symlink(*i, path) >= 0) {
                         add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
-
-                        free(path);
                         continue;
                 }
 
                 if (errno == EEXIST) {
-                        char *dest = NULL;
+                        _cleanup_free_ char *dest = NULL;
 
                         q = readlink_and_make_absolute(path, &dest);
-
                         if (q < 0 && errno != ENOENT) {
-                                free(path);
-
                                 if (r == 0)
                                         r = q;
-
                                 continue;
                         }
 
-                        if (q >= 0 && path_equal(dest, *i)) {
-                                free(dest);
-                                free(path);
+                        if (q >= 0 && path_equal(dest, *i))
                                 continue;
-                        }
-
-                        free(dest);
 
                         if (force) {
-                                unlink(path);
-
-                                if (symlink(*i, path) >= 0) {
-
+                                if (symlink_atomic(*i, path) >= 0) {
                                         add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
                                         add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
-
-                                        free(path);
                                         continue;
                                 }
                         }
@@ -817,14 +800,8 @@ int unit_file_link(
                         if (r == 0)
                                 r = -errno;
                 }
-
-                free(path);
         }
 
-                finish:
-        lookup_paths_free(&paths);
-        free(config_path);
-
         return r;
 }
 
@@ -863,19 +840,20 @@ static void install_info_free(InstallInfo *i) {
         strv_free(i->aliases);
         strv_free(i->wanted_by);
         strv_free(i->required_by);
+        free(i->default_instance);
         free(i);
 }
 
-static void install_info_hashmap_free(Hashmap *m) {
+static void install_info_hashmap_free(OrderedHashmap *m) {
         InstallInfo *i;
 
         if (!m)
                 return;
 
-        while ((i = hashmap_steal_first(m)))
+        while ((i = ordered_hashmap_steal_first(m)))
                 install_info_free(i);
 
-        hashmap_free(m);
+        ordered_hashmap_free(m);
 }
 
 static void install_context_done(InstallContext *c) {
@@ -898,16 +876,16 @@ static int install_info_add(
         assert(name || path);
 
         if (!name)
-                name = path_get_file_name(path);
+                name = basename(path);
 
-        if (!unit_name_is_valid(name, true))
+        if (!unit_name_is_valid(name, TEMPLATE_VALID))
                 return -EINVAL;
 
-        if (hashmap_get(c->have_installed, name) ||
-            hashmap_get(c->will_install, name))
+        if (ordered_hashmap_get(c->have_installed, name) ||
+            ordered_hashmap_get(c->will_install, name))
                 return 0;
 
-        r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func);
+        r = ordered_hashmap_ensure_allocated(&c->will_install, &string_hash_ops);
         if (r < 0)
                 return r;
 
@@ -929,7 +907,7 @@ static int install_info_add(
                 }
         }
 
-        r = hashmap_put(c->will_install, i->name, i);
+        r = ordered_hashmap_put(c->will_install, i->name, i);
         if (r < 0)
                 goto fail;
 
@@ -956,41 +934,106 @@ static int install_info_add_auto(
 }
 
 static int config_parse_also(
+                const char *unit,
                 const char *filename,
                 unsigned line,
                 const char *section,
+                unsigned section_line,
                 const char *lvalue,
                 int ltype,
                 const char *rvalue,
                 void *data,
                 void *userdata) {
 
-        char *w;
         size_t l;
-        char *state;
+        const char *word, *state;
         InstallContext *c = data;
 
         assert(filename);
         assert(lvalue);
         assert(rvalue);
 
-        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                char *n;
+        FOREACH_WORD_QUOTED(word, l, rvalue, state) {
+                _cleanup_free_ char *n;
                 int r;
 
-                n = strndup(w, l);
+                n = strndup(word, l);
                 if (!n)
                         return -ENOMEM;
 
                 r = install_info_add(c, n, NULL);
-                if (r < 0) {
-                        free(n);
+                if (r < 0)
                         return r;
-                }
+        }
+        if (!isempty(state))
+                log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+                           "Trailing garbage, ignoring.");
+
+        return 0;
+}
+
+static int config_parse_user(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        InstallInfo *i = data;
+        char *printed;
+        int r;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = install_full_printf(i, rvalue, &printed);
+        if (r < 0)
+                return r;
+
+        free(i->user);
+        i->user = printed;
+
+        return 0;
+}
+
+static int config_parse_default_instance(
+                const char *unit,
+                const char *filename,
+                unsigned line,
+                const char *section,
+                unsigned section_line,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        InstallInfo *i = data;
+        char *printed;
+        int r;
 
-                free(n);
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        r = install_full_printf(i, rvalue, &printed);
+        if (r < 0)
+                return r;
+
+        if (!unit_instance_is_valid(printed)) {
+                free(printed);
+                return -EINVAL;
         }
 
+        free(i->default_instance);
+        i->default_instance = printed;
+
         return 0;
 }
 
@@ -998,43 +1041,56 @@ static int unit_file_load(
                 InstallContext *c,
                 InstallInfo *info,
                 const char *path,
-                bool allow_symlink) {
+                const char *root_dir,
+                bool allow_symlink,
+                bool load) {
 
         const ConfigTableItem items[] = {
-                { "Install", "Alias",      config_parse_strv, 0, &info->aliases     },
-                { "Install", "WantedBy",   config_parse_strv, 0, &info->wanted_by   },
-                { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
-                { "Install", "Also",       config_parse_also, 0, c                  },
-                { NULL, NULL, NULL, 0, NULL }
+                { "Install", "Alias",           config_parse_strv,             0, &info->aliases           },
+                { "Install", "WantedBy",        config_parse_strv,             0, &info->wanted_by         },
+                { "Install", "RequiredBy",      config_parse_strv,             0, &info->required_by       },
+                { "Install", "DefaultInstance", config_parse_default_instance, 0, info                     },
+                { "Install", "Also",            config_parse_also,             0, c                        },
+                { "Exec",    "User",            config_parse_user,             0, info                     },
+                {}
         };
 
-        int fd;
-        FILE *f;
-        int r;
+        _cleanup_fclose_ FILE *f = NULL;
+        int fd, r;
 
         assert(c);
         assert(info);
         assert(path);
 
+        if (!isempty(root_dir))
+                path = strappenda(root_dir, "/", path);
+
+        if (!load) {
+                r = access(path, F_OK) ? -errno : 0;
+                return r;
+        }
+
         fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
         if (fd < 0)
                 return -errno;
 
         f = fdopen(fd, "re");
         if (!f) {
-                close_nointr_nofail(fd);
+                safe_close(fd);
                 return -ENOMEM;
         }
 
-        r = config_parse(path, f, NULL, config_item_table_lookup, (void*) items, true, info);
-        fclose(f);
+        r = config_parse(NULL, path, f,
+                         NULL,
+                         config_item_table_lookup, items,
+                         true, true, false, info);
         if (r < 0)
                 return r;
 
         return
-                strv_length(info->aliases) +
-                strv_length(info->wanted_by) +
-                strv_length(info->required_by);
+                (int) strv_length(info->aliases) +
+                (int) strv_length(info->wanted_by) +
+                (int) strv_length(info->required_by);
 }
 
 static int unit_file_search(
@@ -1042,7 +1098,8 @@ static int unit_file_search(
                 InstallInfo *info,
                 LookupPaths *paths,
                 const char *root_dir,
-                bool allow_symlink) {
+                bool allow_symlink,
+                bool load) {
 
         char **p;
         int r;
@@ -1052,70 +1109,55 @@ static int unit_file_search(
         assert(paths);
 
         if (info->path)
-                return unit_file_load(c, info, info->path, allow_symlink);
+                return unit_file_load(c, info, info->path, root_dir, allow_symlink, load);
 
         assert(info->name);
 
         STRV_FOREACH(p, paths->unit_path) {
-                char *path = NULL;
-
-                if (isempty(root_dir))
-                        asprintf(&path, "%s/%s", *p, info->name);
-                else
-                        asprintf(&path, "%s/%s/%s", root_dir, *p, info->name);
+                _cleanup_free_ char *path = NULL;
 
+                path = strjoin(*p, "/", info->name, NULL);
                 if (!path)
                         return -ENOMEM;
 
-                r = unit_file_load(c, info, path, allow_symlink);
-
-                if (r >= 0)
+                r = unit_file_load(c, info, path, root_dir, allow_symlink, load);
+                if (r >= 0) {
                         info->path = path;
-                else {
-                        if (r == -ENOENT && unit_name_is_instance(info->name)) {
-                                /* unit file doesn't exist, however instance enablement was request */
-                                /* we will check if it is possible to load template unit file */
-                                char *template = NULL,
-                                     *template_path = NULL,
-                                     *template_dir = NULL;
-
-                                template = unit_name_template(info->name);
-                                if (!template) {
-                                        free(path);
-                                        return -ENOMEM;
-                                }
+                        path = NULL;
+                        return r;
+                }
+                if (r != -ENOENT && r != -ELOOP)
+                        return r;
+        }
 
-                                /* we will reuse path variable since we don't need it anymore */
-                                template_dir = path;
-                                *(strrchr(path, '/') + 1) = '\0';
+        if (unit_name_is_instance(info->name)) {
 
-                                template_path = strjoin(template_dir, template, NULL);
-                                if (!template_path) {
-                                        free(path);
-                                        free(template);
-                                        return -ENOMEM;
-                                }
+                /* Unit file doesn't exist, however instance
+                 * enablement was requested.  We will check if it is
+                 * possible to load template unit file. */
 
-                                /* let's try to load template unit */
-                                r = unit_file_load(c, info, template_path, allow_symlink);
-                                if (r >= 0) {
-                                        info->path = strdup(template_path);
-                                        if (!info->path) {
-                                                free(path);
-                                                free(template);
-                                                free(template_path);
-                                                return -ENOMEM;
-                                        }
-                                }
+                _cleanup_free_ char *template = NULL;
+
+                template = unit_name_template(info->name);
+                if (!template)
+                        return -ENOMEM;
+
+                STRV_FOREACH(p, paths->unit_path) {
+                        _cleanup_free_ char *path = NULL;
+
+                        path = strjoin(*p, "/", template, NULL);
+                        if (!path)
+                                return -ENOMEM;
 
-                                free(template);
-                                free(template_path);
+                        r = unit_file_load(c, info, path, root_dir, allow_symlink, load);
+                        if (r >= 0) {
+                                info->path = path;
+                                path = NULL;
+                                return r;
                         }
-                        free(path);
+                        if (r != -ENOENT && r != -ELOOP)
+                                return r;
                 }
-
-                if (r != -ENOENT && r != -ELOOP)
-                        return r;
         }
 
         return -ENOENT;
@@ -1127,30 +1169,26 @@ static int unit_file_can_install(
                 const char *name,
                 bool allow_symlink) {
 
-        InstallContext c;
+        _cleanup_(install_context_done) InstallContext c = {};
         InstallInfo *i;
         int r;
 
         assert(paths);
         assert(name);
 
-        zero(c);
-
         r = install_info_add_auto(&c, name);
         if (r < 0)
                 return r;
 
-        assert_se(i = hashmap_first(c.will_install));
+        assert_se(i = ordered_hashmap_first(c.will_install));
 
-        r = unit_file_search(&c, i, paths, root_dir, allow_symlink);
+        r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true);
 
         if (r >= 0)
                 r =
-                        strv_length(i->aliases) +
-                        strv_length(i->wanted_by) +
-                        strv_length(i->required_by);
-
-        install_context_done(&c);
+                        (int) strv_length(i->aliases) +
+                        (int) strv_length(i->wanted_by) +
+                        (int) strv_length(i->required_by);
 
         return r;
 }
@@ -1162,7 +1200,7 @@ static int create_symlink(
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        char *dest;
+        _cleanup_free_ char *dest = NULL;
         int r;
 
         assert(old_path);
@@ -1182,25 +1220,20 @@ static int create_symlink(
         if (r < 0)
                 return r;
 
-        if (path_equal(dest, old_path)) {
-                free(dest);
+        if (path_equal(dest, old_path))
                 return 0;
-        }
-
-        free(dest);
 
         if (!force)
                 return -EEXIST;
 
-        unlink(new_path);
+        r = symlink_atomic(old_path, new_path);
+        if (r < 0)
+                return r;
 
-        if (symlink(old_path, new_path) >= 0) {
-                add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
-                add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
-                return 0;
-        }
+        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL);
+        add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
 
-        return -errno;
+        return 0;
 }
 
 static int install_info_symlink_alias(
@@ -1217,16 +1250,17 @@ static int install_info_symlink_alias(
         assert(config_path);
 
         STRV_FOREACH(s, i->aliases) {
-                char *alias_path;
+                _cleanup_free_ char *alias_path = NULL, *dst = NULL;
 
-                alias_path = path_make_absolute(*s, config_path);
+                q = install_full_printf(i, *s, &dst);
+                if (q < 0)
+                        return q;
 
+                alias_path = path_make_absolute(dst, config_path);
                 if (!alias_path)
                         return -ENOMEM;
 
                 q = create_symlink(i->path, alias_path, force, changes, n_changes);
-                free(alias_path);
-
                 if (r == 0)
                         r = q;
         }
@@ -1237,64 +1271,53 @@ static int install_info_symlink_alias(
 static int install_info_symlink_wants(
                 InstallInfo *i,
                 const char *config_path,
+                char **list,
+                const char *suffix,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
+        _cleanup_free_ char *buf = NULL;
+        const char *n;
         char **s;
         int r = 0, q;
 
         assert(i);
         assert(config_path);
 
-        STRV_FOREACH(s, i->wanted_by) {
-                char *path;
-
-                if (!unit_name_is_valid(*s, true)) {
-                        r = -EINVAL;
-                        continue;
-                }
-
-                if (asprintf(&path, "%s/%s.wants/%s", config_path, *s, i->name) < 0)
-                        return -ENOMEM;
-
-                q = create_symlink(i->path, path, force, changes, n_changes);
-                free(path);
+        if (unit_name_is_template(i->name)) {
 
-                if (r == 0)
-                        r = q;
-        }
+                /* Don't install any symlink if there's no default
+                 * instance configured */
 
-        return r;
-}
+                if (!i->default_instance)
+                        return 0;
 
-static int install_info_symlink_requires(
-                InstallInfo *i,
-                const char *config_path,
-                bool force,
-                UnitFileChange **changes,
-                unsigned *n_changes) {
+                buf = unit_name_replace_instance(i->name, i->default_instance);
+                if (!buf)
+                        return -ENOMEM;
 
-        char **s;
-        int r = 0, q;
+                n = buf;
+        } else
+                n = i->name;
 
-        assert(i);
-        assert(config_path);
+        STRV_FOREACH(s, list) {
+                _cleanup_free_ char *path = NULL, *dst = NULL;
 
-        STRV_FOREACH(s, i->required_by) {
-                char *path;
+                q = install_full_printf(i, *s, &dst);
+                if (q < 0)
+                        return q;
 
-                if (!unit_name_is_valid(*s, true)) {
+                if (!unit_name_is_valid(dst, TEMPLATE_VALID)) {
                         r = -EINVAL;
                         continue;
                 }
 
-                if (asprintf(&path, "%s/%s.requires/%s", config_path, *s, i->name) < 0)
+                path = strjoin(config_path, "/", dst, suffix, n, NULL);
+                if (!path)
                         return -ENOMEM;
 
                 q = create_symlink(i->path, path, force, changes, n_changes);
-                free(path);
-
                 if (r == 0)
                         r = q;
         }
@@ -1306,12 +1329,13 @@ static int install_info_symlink_link(
                 InstallInfo *i,
                 LookupPaths *paths,
                 const char *config_path,
+                const char *root_dir,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
+        _cleanup_free_ char *path = NULL;
         int r;
-        char *path;
 
         assert(i);
         assert(paths);
@@ -1322,19 +1346,18 @@ static int install_info_symlink_link(
         if (r != 0)
                 return r;
 
-        if (asprintf(&path, "%s/%s", config_path, i->name) < 0)
+        path = strjoin(config_path, "/", i->name, NULL);
+        if (!path)
                 return -ENOMEM;
 
-        r = create_symlink(i->path, path, force, changes, n_changes);
-        free(path);
-
-        return r;
+        return create_symlink(i->path, path, force, changes, n_changes);
 }
 
 static int install_info_apply(
                 InstallInfo *i,
                 LookupPaths *paths,
                 const char *config_path,
+                const char *root_dir,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
@@ -1347,15 +1370,15 @@ static int install_info_apply(
 
         r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
 
-        q = install_info_symlink_wants(i, config_path, force, changes, n_changes);
+        q = install_info_symlink_wants(i, config_path, i->wanted_by, ".wants/", force, changes, n_changes);
         if (r == 0)
                 r = q;
 
-        q = install_info_symlink_requires(i, config_path, force, changes, n_changes);
+        q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes);
         if (r == 0)
                 r = q;
 
-        q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
+        q = install_info_symlink_link(i, paths, config_path, root_dir, force, changes, n_changes);
         if (r == 0)
                 r = q;
 
@@ -1372,21 +1395,27 @@ static int install_context_apply(
                 unsigned *n_changes) {
 
         InstallInfo *i;
-        int r = 0, q;
+        int r, q;
 
         assert(c);
         assert(paths);
         assert(config_path);
 
-        while ((i = hashmap_first(c->will_install))) {
+        if (!ordered_hashmap_isempty(c->will_install)) {
+                r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
+                if (r < 0)
+                        return r;
 
-                q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
-                if (q < 0)
-                        return q;
+                r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
+                if (r < 0)
+                        return r;
+        }
 
-                assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
+        r = 0;
+        while ((i = ordered_hashmap_first(c->will_install))) {
+                assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
 
-                q = unit_file_search(c, i, paths, root_dir, false);
+                q = unit_file_search(c, i, paths, root_dir, false, true);
                 if (q < 0) {
                         if (r >= 0)
                                 r = q;
@@ -1395,7 +1424,7 @@ static int install_context_apply(
                 } else if (r >= 0)
                         r += q;
 
-                q = install_info_apply(i, paths, config_path, force, changes, n_changes);
+                q = install_info_apply(i, paths, config_path, root_dir, force, changes, n_changes);
                 if (r >= 0 && q < 0)
                         r = q;
         }
@@ -1411,7 +1440,7 @@ static int install_context_mark_for_removal(
                 const char *root_dir) {
 
         InstallInfo *i;
-        int r = 0, q;
+        int r, q;
 
         assert(c);
         assert(paths);
@@ -1419,16 +1448,24 @@ static int install_context_mark_for_removal(
 
         /* Marks all items for removal */
 
-        while ((i = hashmap_first(c->will_install))) {
+        if (!ordered_hashmap_isempty(c->will_install)) {
+                r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
+                if (r < 0)
+                        return r;
 
-                q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
-                if (q < 0)
-                        return q;
+                r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
+                if (r < 0)
+                        return r;
+        }
 
-                assert_se(hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
+        r = 0;
+        while ((i = ordered_hashmap_first(c->will_install))) {
+                assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
 
-                q = unit_file_search(c, i, paths, root_dir, false);
-                if (q < 0) {
+                q = unit_file_search(c, i, paths, root_dir, false, true);
+                if (q == -ENOENT) {
+                        /* do nothing */
+                } else if (q < 0) {
                         if (r >= 0)
                                 r = q;
 
@@ -1437,16 +1474,30 @@ static int install_context_mark_for_removal(
                         r += q;
 
                 if (unit_name_is_instance(i->name)) {
-                        char *unit_file = NULL;
-
-                        unit_file = path_get_file_name(i->path);
+                        char *unit_file;
+
+                        if (i->path) {
+                                unit_file = basename(i->path);
+
+                                if (unit_name_is_instance(unit_file))
+                                        /* unit file named as instance exists, thus all symlinks
+                                         * pointing to it will be removed */
+                                        q = mark_symlink_for_removal(remove_symlinks_to, i->name);
+                                else
+                                        /* does not exist, thus we will mark for removal symlinks
+                                         * to template unit file */
+                                        q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
+                        } else {
+                                /* If i->path is not set, it means that we didn't actually find
+                                 * the unit file. But we can still remove symlinks to the
+                                 * nonexistent template. */
+                                unit_file = unit_name_template(i->name);
+                                if (!unit_file)
+                                        return log_oom();
 
-                        if (unit_name_is_instance(unit_file))
-                                /* unit file named as instance exists, thus all symlinks pointing to it, will be removed */
-                                q = mark_symlink_for_removal(remove_symlinks_to, i->name);
-                        else
-                                /* does not exist, thus we will mark for removal symlinks to template unit file */
                                 q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
+                                free(unit_file);
+                        }
                 } else
                         q = mark_symlink_for_removal(remove_symlinks_to, i->name);
 
@@ -1457,86 +1508,172 @@ static int install_context_mark_for_removal(
         return r;
 }
 
-int unit_file_enable(
+int unit_file_add_dependency(
                 UnitFileScope scope,
                 bool runtime,
                 const char *root_dir,
-                char *files[],
+                char **files,
+                char *target,
+                UnitDependency dep,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        LookupPaths paths;
-        InstallContext c;
-        char **i, *config_path = NULL;
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(install_context_done) InstallContext c = {};
+        _cleanup_free_ char *config_path = NULL;
+        char **i;
         int r;
+        InstallInfo *info;
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
 
-        zero(paths);
-        zero(c);
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+        if (r < 0)
+                return r;
+
+        r = get_config_path(scope, runtime, root_dir, &config_path);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, files) {
+                UnitFileState state;
+
+                state = unit_file_get_state(scope, root_dir, *i);
+                if (state < 0) {
+                        log_error("Failed to get unit file state for %s: %s", *i, strerror(-state));
+                        return state;
+                }
+
+                if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
+                        log_error("Failed to enable unit: Unit %s is masked", *i);
+                        return -ENOTSUP;
+                }
+
+                r = install_info_add_auto(&c, *i);
+                if (r < 0)
+                        return r;
+        }
+
+        if (!ordered_hashmap_isempty(c.will_install)) {
+                r = ordered_hashmap_ensure_allocated(&c.have_installed, &string_hash_ops);
+                if (r < 0)
+                        return r;
+
+                r = ordered_hashmap_reserve(c.have_installed, ordered_hashmap_size(c.will_install));
+                if (r < 0)
+                        return r;
+        }
+
+        while ((info = ordered_hashmap_first(c.will_install))) {
+                assert_se(ordered_hashmap_move_one(c.have_installed, c.will_install, info->name) == 0);
+
+                r = unit_file_search(&c, info, &paths, root_dir, false, false);
+                if (r < 0)
+                        return r;
+
+                if (dep == UNIT_WANTS)
+                        r = strv_extend(&info->wanted_by, target);
+                else if (dep == UNIT_REQUIRES)
+                        r = strv_extend(&info->required_by, target);
+                else
+                        r = -EINVAL;
+
+                if (r < 0)
+                        return r;
+
+                r = install_info_apply(info, &paths, config_path, root_dir, force, changes, n_changes);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
 
-        r = lookup_paths_init_from_scope(&paths, scope);
+int unit_file_enable(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                char **files,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(install_context_done) InstallContext c = {};
+        char **i;
+        _cleanup_free_ char *config_path = NULL;
+        int r;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
         if (r < 0)
                 return r;
 
         r = get_config_path(scope, runtime, root_dir, &config_path);
         if (r < 0)
-                goto finish;
+                return r;
 
         STRV_FOREACH(i, files) {
+                UnitFileState state;
+
+                state = unit_file_get_state(scope, root_dir, *i);
+                if (state < 0) {
+                        log_error("Failed to get unit file state for %s: %s", *i, strerror(-state));
+                        return state;
+                }
+
+                if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
+                        log_error("Failed to enable unit: Unit %s is masked", *i);
+                        return -ENOTSUP;
+                }
+
                 r = install_info_add_auto(&c, *i);
                 if (r < 0)
-                        goto finish;
+                        return r;
         }
 
         /* This will return the number of symlink rules that were
         supposed to be created, not the ones actually created. This is
         useful to determine whether the passed files had any
         installation data at all. */
-        r = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
 
-finish:
-        install_context_done(&c);
-        lookup_paths_free(&paths);
-        free(config_path);
-
-        return r;
+        return install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
 }
 
 int unit_file_disable(
                 UnitFileScope scope,
                 bool runtime,
                 const char *root_dir,
-                char *files[],
+                char **files,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        LookupPaths paths;
-        InstallContext c;
-        char **i, *config_path = NULL;
-        Set *remove_symlinks_to = NULL;
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(install_context_done) InstallContext c = {};
+        char **i;
+        _cleanup_free_ char *config_path = NULL;
+        _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
         int r, q;
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
 
-        zero(paths);
-        zero(c);
-
-        r = lookup_paths_init_from_scope(&paths, scope);
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
         if (r < 0)
                 return r;
 
         r = get_config_path(scope, runtime, root_dir, &config_path);
         if (r < 0)
-                goto finish;
+                return r;
 
         STRV_FOREACH(i, files) {
                 r = install_info_add_auto(&c, *i);
                 if (r < 0)
-                        goto finish;
+                        return r;
         }
 
         r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
@@ -1545,12 +1682,6 @@ int unit_file_disable(
         if (r == 0)
                 r = q;
 
-finish:
-        install_context_done(&c);
-        lookup_paths_free(&paths);
-        set_free_free(remove_symlinks_to);
-        free(config_path);
-
         return r;
 }
 
@@ -1558,55 +1689,114 @@ int unit_file_reenable(
                 UnitFileScope scope,
                 bool runtime,
                 const char *root_dir,
-                char *files[],
+                char **files,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
+        int r;
 
-        LookupPaths paths;
-        InstallContext c;
-        char **i, *config_path = NULL;
-        Set *remove_symlinks_to = NULL;
-        int r, q;
+        r = unit_file_disable(scope, runtime, root_dir, files,
+                              changes, n_changes);
+        if (r < 0)
+                return r;
+
+        return unit_file_enable(scope, runtime, root_dir, files, force,
+                                changes, n_changes);
+}
+
+int unit_file_set_default(
+                UnitFileScope scope,
+                const char *root_dir,
+                const char *file,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_(install_context_done) InstallContext c = {};
+        _cleanup_free_ char *config_path = NULL;
+        char *path;
+        int r;
+        InstallInfo *i = NULL;
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
+        assert(file);
 
-        zero(paths);
-        zero(c);
+        if (unit_name_to_type(file) != UNIT_TARGET)
+                return -EINVAL;
 
-        r = lookup_paths_init_from_scope(&paths, scope);
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
         if (r < 0)
                 return r;
 
-        r = get_config_path(scope, runtime, root_dir, &config_path);
+        r = get_config_path(scope, false, root_dir, &config_path);
         if (r < 0)
-                goto finish;
+                return r;
 
-        STRV_FOREACH(i, files) {
-                r = mark_symlink_for_removal(&remove_symlinks_to, *i);
-                if (r < 0)
-                        goto finish;
+        r = install_info_add_auto(&c, file);
+        if (r < 0)
+                return r;
 
-                r = install_info_add_auto(&c, *i);
-                if (r < 0)
-                        goto finish;
-        }
+        assert_se(i = ordered_hashmap_first(c.will_install));
 
-        r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
+        r = unit_file_search(&c, i, &paths, root_dir, false, true);
+        if (r < 0)
+                return r;
 
-        /* Returns number of symlinks that where supposed to be installed. */
-        q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
-        if (r == 0)
-                r = q;
+        path = strappenda(config_path, "/" SPECIAL_DEFAULT_TARGET);
 
-finish:
-        lookup_paths_free(&paths);
-        install_context_done(&c);
-        set_free_free(remove_symlinks_to);
-        free(config_path);
+        r = create_symlink(i->path, path, force, changes, n_changes);
+        if (r < 0)
+                return r;
 
-        return r;
+        return 0;
+}
+
+int unit_file_get_default(
+                UnitFileScope scope,
+                const char *root_dir,
+                char **name) {
+
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        char **p;
+        int r;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+        assert(name);
+
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(p, paths.unit_path) {
+                _cleanup_free_ char *path = NULL, *tmp = NULL;
+                char *n;
+
+                path = path_join(root_dir, *p, SPECIAL_DEFAULT_TARGET);
+                if (!path)
+                        return -ENOMEM;
+
+                r = readlink_malloc(path, &tmp);
+                if (r == -ENOENT)
+                        continue;
+                else if (r == -EINVAL)
+                        /* not a symlink */
+                        n = strdup(SPECIAL_DEFAULT_TARGET);
+                else if (r < 0)
+                        return r;
+                else
+                        n = strdup(basename(tmp));
+
+                if (!n)
+                        return -ENOMEM;
+
+                *name = n;
+                return 0;
+        }
+
+        return -ENOENT;
 }
 
 UnitFileState unit_file_get_state(
@@ -1614,97 +1804,88 @@ UnitFileState unit_file_get_state(
                 const char *root_dir,
                 const char *name) {
 
-        LookupPaths paths;
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
         UnitFileState state = _UNIT_FILE_STATE_INVALID;
-        char **i, *path = NULL;
+        char **i;
+        _cleanup_free_ char *path = NULL;
         int r;
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
         assert(name);
 
-        zero(paths);
-
         if (root_dir && scope != UNIT_FILE_SYSTEM)
                 return -EINVAL;
 
-        if (!unit_name_is_valid(name, true))
+        if (!unit_name_is_valid(name, TEMPLATE_VALID))
                 return -EINVAL;
 
-        r = lookup_paths_init_from_scope(&paths, scope);
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
         if (r < 0)
                 return r;
 
         STRV_FOREACH(i, paths.unit_path) {
                 struct stat st;
+                char *partial;
 
                 free(path);
                 path = NULL;
 
+                path = path_join(root_dir, *i, name);
+                if (!path)
+                        return -ENOMEM;
+
                 if (root_dir)
-                        asprintf(&path, "%s/%s/%s", root_dir, *i, name);
+                        partial = path + strlen(root_dir);
                 else
-                        asprintf(&path, "%s/%s", *i, name);
-
-                if (!path) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                        partial = path;
 
+                /*
+                 * Search for a unit file in our default paths, to
+                 * be sure, that there are no broken symlinks.
+                 */
                 if (lstat(path, &st) < 0) {
                         r = -errno;
-                        if (errno == ENOENT)
-                                continue;
-
-                        goto finish;
-                }
+                        if (errno != ENOENT)
+                                return r;
 
-                if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
-                        r = -ENOENT;
-                        goto finish;
-                }
-
-                r = null_or_empty_path(path);
-                if (r < 0 && r != -ENOENT)
-                        goto finish;
-                else if (r > 0) {
-                        state = path_startswith(*i, "/run") ?
-                                UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
-                        r = 0;
-                        goto finish;
+                        if (!unit_name_is_instance(name))
+                                continue;
+                } else {
+                        if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
+                                return -ENOENT;
+
+                        r = null_or_empty_path(path);
+                        if (r < 0 && r != -ENOENT)
+                                return r;
+                        else if (r > 0) {
+                                state = path_startswith(*i, "/run") ?
+                                        UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
+                                return state;
+                        }
                 }
 
                 r = find_symlinks_in_scope(scope, root_dir, name, &state);
-                if (r < 0) {
-                        goto finish;
-                } else if (r > 0) {
-                        r = 0;
-                        goto finish;
-                }
+                if (r < 0)
+                        return r;
+                else if (r > 0)
+                        return state;
 
-                r = unit_file_can_install(&paths, root_dir, path, true);
-                if (r < 0 && errno != -ENOENT)
-                        goto finish;
-                else if (r > 0) {
-                        state = UNIT_FILE_DISABLED;
-                        r = 0;
-                        goto finish;
-                } else if (r == 0) {
-                        state = UNIT_FILE_STATIC;
-                        r = 0;
-                        goto finish;
-                }
+                r = unit_file_can_install(&paths, root_dir, partial, true);
+                if (r < 0 && errno != ENOENT)
+                        return r;
+                else if (r > 0)
+                        return UNIT_FILE_DISABLED;
+                else if (r == 0)
+                        return UNIT_FILE_STATIC;
         }
 
-finish:
-        lookup_paths_free(&paths);
-        free(path);
-
         return r < 0 ? r : state;
 }
 
-int unit_file_query_preset(UnitFileScope scope, const char *name) {
-        char **files, **i;
+int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
+        _cleanup_strv_free_ char **files = NULL;
+        char **p;
         int r;
 
         assert(scope >= 0);
@@ -1712,7 +1893,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
         assert(name);
 
         if (scope == UNIT_FILE_SYSTEM)
-                r = conf_files_list(&files, ".preset",
+                r = conf_files_list(&files, ".preset", root_dir,
                                     "/etc/systemd/system-preset",
                                     "/usr/local/lib/systemd/system-preset",
                                     "/usr/lib/systemd/system-preset",
@@ -1721,7 +1902,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
 #endif
                                     NULL);
         else if (scope == UNIT_FILE_GLOBAL)
-                r = conf_files_list(&files, ".preset",
+                r = conf_files_list(&files, ".preset", root_dir,
                                     "/etc/systemd/user-preset",
                                     "/usr/local/lib/systemd/user-preset",
                                     "/usr/lib/systemd/user-preset",
@@ -1732,16 +1913,15 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
         if (r < 0)
                 return r;
 
-        STRV_FOREACH(i, files) {
-                FILE *f;
+        STRV_FOREACH(p, files) {
+                _cleanup_fclose_ FILE *f;
 
-                f = fopen(*i, "re");
+                f = fopen(*p, "re");
                 if (!f) {
                         if (errno == ENOENT)
                                 continue;
 
-                        r = -errno;
-                        goto finish;
+                        return -errno;
                 }
 
                 for (;;) {
@@ -1754,7 +1934,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
                         if (!*l)
                                 continue;
 
-                        if (strchr(COMMENTS, *l))
+                        if (strchr(COMMENTS "\n", *l))
                                 continue;
 
                         if (first_word(l, "enable")) {
@@ -1762,166 +1942,260 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
                                 l += strspn(l, WHITESPACE);
 
                                 if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
-                                        r = 1;
-                                        fclose(f);
-                                        goto finish;
+                                        log_debug("Preset file says enable %s.", name);
+                                        return 1;
                                 }
+
                         } else if (first_word(l, "disable")) {
                                 l += 7;
                                 l += strspn(l, WHITESPACE);
 
                                 if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
-                                        r = 0;
-                                        fclose(f);
-                                        goto finish;
+                                        log_debug("Preset file says disable %s.", name);
+                                        return 0;
                                 }
+
                         } else
                                 log_debug("Couldn't parse line '%s'", l);
                 }
-
-                fclose(f);
         }
 
         /* Default is "enable" */
-        r = 1;
-
-finish:
-        strv_free(files);
-
-        return r;
+        log_debug("Preset file doesn't say anything about %s, enabling.", name);
+        return 1;
 }
 
 int unit_file_preset(
                 UnitFileScope scope,
                 bool runtime,
                 const char *root_dir,
-                char *files[],
+                char **files,
+                UnitFilePresetMode mode,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        LookupPaths paths;
-        InstallContext plus, minus;
-        char **i, *config_path = NULL;
-        Set *remove_symlinks_to = NULL;
+        _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_free_ char *config_path = NULL;
+        char **i;
         int r, q;
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
+        assert(mode < _UNIT_FILE_PRESET_MAX);
 
-        zero(paths);
-        zero(plus);
-        zero(minus);
-
-        r = lookup_paths_init_from_scope(&paths, scope);
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
         if (r < 0)
                 return r;
 
         r = get_config_path(scope, runtime, root_dir, &config_path);
         if (r < 0)
-                goto finish;
+                return r;
 
         STRV_FOREACH(i, files) {
 
-                if (!unit_name_is_valid(*i, true)) {
-                        r = -EINVAL;
-                        goto finish;
-                }
+                if (!unit_name_is_valid(*i, TEMPLATE_VALID))
+                        return -EINVAL;
 
-                r = unit_file_query_preset(scope, *i);
+                r = unit_file_query_preset(scope, root_dir, *i);
                 if (r < 0)
-                        goto finish;
+                        return r;
 
-                if (r)
+                if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
                         r = install_info_add_auto(&plus, *i);
-                else
+                else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
                         r = install_info_add_auto(&minus, *i);
-
+                else
+                        r = 0;
                 if (r < 0)
-                        goto finish;
+                        return r;
         }
 
-        r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
+        r = 0;
 
-        q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
-        if (r == 0)
-                r = q;
+        if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
+                _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
 
-        /* Returns number of symlinks that where supposed to be installed. */
-        q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
-        if (r == 0)
-                r = q;
+                r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
 
-finish:
-        lookup_paths_free(&paths);
-        install_context_done(&plus);
-        install_context_done(&minus);
-        set_free_free(remove_symlinks_to);
-        free(config_path);
+                q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
+                if (r == 0)
+                        r = q;
+        }
+
+        if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
+                /* Returns number of symlinks that where supposed to be installed. */
+                q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
+                if (r == 0)
+                        r = q;
+        }
+
+        return r;
+}
+
+int unit_file_preset_all(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                UnitFilePresetMode mode,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        _cleanup_(install_context_done) InstallContext plus = {}, minus = {};
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        _cleanup_free_ char *config_path = NULL;
+        char **i;
+        int r, q;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+        assert(mode < _UNIT_FILE_PRESET_MAX);
+
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
+        if (r < 0)
+                return r;
+
+        r = get_config_path(scope, runtime, root_dir, &config_path);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, paths.unit_path) {
+                _cleanup_closedir_ DIR *d = NULL;
+                _cleanup_free_ char *units_dir;
+
+                units_dir = path_join(root_dir, *i, NULL);
+                if (!units_dir)
+                        return -ENOMEM;
+
+                d = opendir(units_dir);
+                if (!d) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        return -errno;
+                }
+
+                for (;;) {
+                        struct dirent *de;
+
+                        errno = 0;
+                        de = readdir(d);
+                        if (!de && errno != 0)
+                                return -errno;
+
+                        if (!de)
+                                break;
+
+                        if (ignore_file(de->d_name))
+                                continue;
+
+                        if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
+                                continue;
+
+                        dirent_ensure_type(d, de);
+
+                        if (de->d_type != DT_REG)
+                                continue;
+
+                        r = unit_file_query_preset(scope, root_dir, de->d_name);
+                        if (r < 0)
+                                return r;
+
+                        if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
+                                r = install_info_add_auto(&plus, de->d_name);
+                        else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
+                                r = install_info_add_auto(&minus, de->d_name);
+                        else
+                                r = 0;
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        r = 0;
+
+        if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
+                _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
+
+                r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
+
+                q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL);
+                if (r == 0)
+                        r = q;
+        }
+
+        if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
+                q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
+                if (r == 0)
+                        r = q;
+        }
 
         return r;
 }
 
+static void unit_file_list_free_one(UnitFileList *f) {
+        if (!f)
+                return;
+
+        free(f->path);
+        free(f);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
+
 int unit_file_get_list(
                 UnitFileScope scope,
                 const char *root_dir,
                 Hashmap *h) {
 
-        LookupPaths paths;
-        char **i, *buf = NULL;
-        DIR *d = NULL;
+        _cleanup_lookup_paths_free_ LookupPaths paths = {};
+        char **i;
         int r;
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
         assert(h);
 
-        zero(paths);
-
         if (root_dir && scope != UNIT_FILE_SYSTEM)
                 return -EINVAL;
 
-        r = lookup_paths_init_from_scope(&paths, scope);
+        if (root_dir) {
+                r = access(root_dir, F_OK);
+                if (r < 0)
+                        return -errno;
+        }
+
+        r = lookup_paths_init_from_scope(&paths, scope, root_dir);
         if (r < 0)
                 return r;
 
         STRV_FOREACH(i, paths.unit_path) {
-                const char *units_dir;
-
-                free(buf);
-                buf = NULL;
-
-                if (root_dir) {
-                        if (asprintf(&buf, "%s/%s", root_dir, *i) < 0) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
-                        units_dir = buf;
-                } else
-                        units_dir = *i;
+                _cleanup_closedir_ DIR *d = NULL;
+                _cleanup_free_ char *units_dir;
 
-                if (d)
-                        closedir(d);
+                units_dir = path_join(root_dir, *i, NULL);
+                if (!units_dir)
+                        return -ENOMEM;
 
                 d = opendir(units_dir);
                 if (!d) {
                         if (errno == ENOENT)
                                 continue;
 
-                        r = -errno;
-                        goto finish;
+                        return -errno;
                 }
 
                 for (;;) {
+                        _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
                         struct dirent *de;
-                        union dirent_storage buffer;
-                        UnitFileList *f;
+                        _cleanup_free_ char *path = NULL;
 
-                        r = readdir_r(d, &buffer.de, &de);
-                        if (r != 0) {
-                                r = -r;
-                                goto finish;
-                        }
+                        errno = 0;
+                        de = readdir(d);
+                        if (!de && errno != 0)
+                                return -errno;
 
                         if (!de)
                                 break;
@@ -1929,42 +2203,29 @@ int unit_file_get_list(
                         if (ignore_file(de->d_name))
                                 continue;
 
-                        if (!unit_name_is_valid(de->d_name, true))
+                        if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
                                 continue;
 
                         if (hashmap_get(h, de->d_name))
                                 continue;
 
-                        r = dirent_ensure_type(d, de);
-                        if (r < 0) {
-                                if (r == -ENOENT)
-                                        continue;
-
-                                goto finish;
-                        }
+                        dirent_ensure_type(d, de);
 
-                        if (de->d_type != DT_LNK && de->d_type != DT_REG)
+                        if (!IN_SET(de->d_type, DT_LNK, DT_REG))
                                 continue;
 
                         f = new0(UnitFileList, 1);
-                        if (!f) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
+                        if (!f)
+                                return -ENOMEM;
 
                         f->path = path_make_absolute(de->d_name, units_dir);
-                        if (!f->path) {
-                                free(f);
-                                r = -ENOMEM;
-                                goto finish;
-                        }
+                        if (!f->path)
+                                return -ENOMEM;
 
                         r = null_or_empty_path(f->path);
-                        if (r < 0 && r != -ENOENT) {
-                                free(f->path);
-                                free(f);
-                                goto finish;
-                        } else if (r > 0) {
+                        if (r < 0 && r != -ENOENT)
+                                return r;
+                        else if (r > 0) {
                                 f->state =
                                         path_startswith(*i, "/run") ?
                                         UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
@@ -1972,46 +2233,37 @@ int unit_file_get_list(
                         }
 
                         r = find_symlinks_in_scope(scope, root_dir, de->d_name, &f->state);
-                        if (r < 0) {
-                                free(f->path);
-                                free(f);
-                                goto finish;
-                        } else if (r > 0) {
+                        if (r < 0)
+                                return r;
+                        else if (r > 0) {
                                 f->state = UNIT_FILE_ENABLED;
                                 goto found;
                         }
 
-                        r = unit_file_can_install(&paths, root_dir, f->path, true);
+                        path = path_make_absolute(de->d_name, *i);
+                        if (!path)
+                                return -ENOMEM;
+
+                        r = unit_file_can_install(&paths, root_dir, path, true);
                         if (r == -EINVAL ||  /* Invalid setting? */
                             r == -EBADMSG || /* Invalid format? */
                             r == -ENOENT     /* Included file not found? */)
                                 f->state = UNIT_FILE_INVALID;
-                        else if (r < 0) {
-                                free(f->path);
-                                free(f);
-                                goto finish;
-                        } else if (r > 0)
+                        else if (r < 0)
+                                return r;
+                        else if (r > 0)
                                 f->state = UNIT_FILE_DISABLED;
                         else
                                 f->state = UNIT_FILE_STATIC;
 
                 found:
-                        r = hashmap_put(h, path_get_file_name(f->path), f);
-                        if (r < 0) {
-                                free(f->path);
-                                free(f);
-                                goto finish;
-                        }
+                        r = hashmap_put(h, basename(f->path), f);
+                        if (r < 0)
+                                return r;
+                        f = NULL; /* prevent cleanup */
                 }
         }
 
-finish:
-        lookup_paths_free(&paths);
-        free(buf);
-
-        if (d)
-                closedir(d);
-
         return r;
 }
 
@@ -2035,3 +2287,11 @@ static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX]
 };
 
 DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType);
+
+static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = {
+        [UNIT_FILE_PRESET_FULL] = "full",
+        [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only",
+        [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode);