[systemd-commits] 8 commits - .gitignore Makefile.am src/install.c src/install.h src/manager.c src/path-lookup.c src/path-lookup.h src/shutdown.c src/systemctl.c src/test-install.c src/util.c src/util.h TODO

Lennart Poettering lennart at kemper.freedesktop.org
Thu Jul 21 19:31:33 PDT 2011


 .gitignore         |    1 
 Makefile.am        |   16 
 TODO               |    2 
 src/install.c      | 1944 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/install.h      |   86 ++
 src/manager.c      |    4 
 src/path-lookup.c  |   65 +
 src/path-lookup.h  |    2 
 src/shutdown.c     |    4 
 src/systemctl.c    |   34 
 src/test-install.c |  264 +++++++
 src/util.c         |   99 ++
 src/util.h         |    6 
 13 files changed, 2483 insertions(+), 44 deletions(-)

New commits:
commit 2006d9acb1248e4225d31c420520c043b1ae044d
Author: Harald Hoyer <harald at redhat.com>
Date:   Tue Jul 19 12:47:45 2011 +0200

    shutdown: initialize the controlling terminal
    
    Thanks Kay Sievers!

diff --git a/src/shutdown.c b/src/shutdown.c
index 0905ef5..d6b47c5 100644
--- a/src/shutdown.c
+++ b/src/shutdown.c
@@ -276,6 +276,9 @@ static int pivot_to_new_root(void) {
                 log_error("Failed to open /dev/console: %m");
         else {
                 make_stdio(fd);
+		/* initialize the controlling terminal */
+		setsid();
+		ioctl(0, TIOCSCTTY, NULL);
         }
 
         return 0;

commit 3b8a1f3fee7bf83af5bbae541166508cf172b67c
Author: Harald Hoyer <harald at redhat.com>
Date:   Tue Jul 19 12:16:30 2011 +0200

    shutdown: remove close_nointr_nofail after make_stdio call
    
    make_stdio itsselves closes the fd already

diff --git a/src/shutdown.c b/src/shutdown.c
index c16871d..0905ef5 100644
--- a/src/shutdown.c
+++ b/src/shutdown.c
@@ -276,7 +276,6 @@ static int pivot_to_new_root(void) {
                 log_error("Failed to open /dev/console: %m");
         else {
                 make_stdio(fd);
-                close_nointr_nofail(fd);
         }
 
         return 0;

commit f782b8d031fcf6ec6b6ea4a1e0c956e44a7c7298
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jul 22 04:25:25 2011 +0200

    util: use readdir_r() instead of readdir()

diff --git a/src/util.c b/src/util.c
index 4e441a7..328a1ea 100644
--- a/src/util.c
+++ b/src/util.c
@@ -4801,7 +4801,7 @@ static int file_is_conf(const struct dirent *d, const char *suffix) {
 
 static int files_add(Hashmap *h, const char *path, const char *suffix) {
         DIR *dir;
-        struct dirent *de;
+        struct dirent buffer, *de;
         int r = 0;
 
         dir = opendir(path);
@@ -4811,9 +4811,18 @@ static int files_add(Hashmap *h, const char *path, const char *suffix) {
                 return -errno;
         }
 
-        for (de = readdir(dir); de; de = readdir(dir)) {
+        for (;;) {
+                int k;
                 char *p, *f;
-                const char *base;
+
+                k = readdir_r(dir, &buffer, &de);
+                if (k != 0) {
+                        r = -k;
+                        goto finish;
+                }
+
+                if (!de)
+                        break;
 
                 if (!file_is_conf(de, suffix))
                         continue;
@@ -4832,8 +4841,7 @@ static int files_add(Hashmap *h, const char *path, const char *suffix) {
                 free(p);
 
                 log_debug("found: %s\n", f);
-                base = f + strlen(path) + 1;
-                if (hashmap_put(h, base, f) <= 0)
+                if (hashmap_put(h, file_name_from_path(f), f) <= 0)
                         free(f);
         }
 

commit 830964834f330836b9d33752e83de09d4f38da87
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jul 22 04:21:18 2011 +0200

    install: add new installer implementation
    
    This new installer will replace the current code of "systemctl enable"
    but also be available via D-Bus. It adds a couple of new features:
    
    - Mask/Unmask calls
    - Reenable call
    - Preset call
    - Support for enabling units temporarily (i.e. in /run/systemd instead
      of /etc/systemd)
    - Enumeration of installed units
    - Support for out-of-search-path units
    
    systemctl and D-Bus are not hooked up with this yet

diff --git a/.gitignore b/.gitignore
index c02e6bd..6cfb3ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+test-install
 org.freedesktop.hostname1.xml
 org.freedesktop.locale1.xml
 org.freedesktop.timedate1.xml
diff --git a/Makefile.am b/Makefile.am
index 70d1c92..af44b13 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -210,7 +210,8 @@ noinst_PROGRAMS = \
 	test-cgroup \
 	test-env-replace \
 	test-strv \
-        test-login
+        test-login \
+        test-install
 
 if HAVE_PAM
 pamlib_LTLIBRARIES = \
@@ -831,6 +832,19 @@ test_login_LDADD = \
 	libsystemd-basic.la \
         libsystemd-login.la
 
+test_install_SOURCES = \
+	src/test-install.c \
+        src/install.c \
+        src/path-lookup.c \
+        src/unit-name.c
+
+test_install_CFLAGS = \
+	$(AM_CFLAGS) \
+        $(DBUS_CFLAGS)
+
+test_install_LDADD = \
+	libsystemd-basic.la
+
 systemd_logger_SOURCES = \
 	src/logger.c \
 	src/tcpwrap.c
diff --git a/src/install.c b/src/install.c
new file mode 100644
index 0000000..e1c6944
--- /dev/null
+++ b/src/install.c
@@ -0,0 +1,1944 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#include "util.h"
+#include "hashmap.h"
+#include "set.h"
+#include "path-lookup.h"
+#include "strv.h"
+#include "unit-name.h"
+#include "install.h"
+#include "conf-parser.h"
+
+typedef struct {
+        char *name;
+        char *path;
+
+        char **aliases;
+        char **wanted_by;
+} InstallInfo;
+
+typedef struct {
+        Hashmap *will_install;
+        Hashmap *have_installed;
+} InstallContext;
+
+static int lookup_paths_init_from_scope(LookupPaths *paths, UnitFileScope scope) {
+        assert(paths);
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+        zero(*paths);
+
+        return lookup_paths_init(paths,
+                                 scope == UNIT_FILE_SYSTEM ? MANAGER_SYSTEM : MANAGER_USER,
+                                 scope == UNIT_FILE_USER);
+}
+
+static int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
+        char *p = NULL;
+        int r;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+        assert(ret);
+
+        switch (scope) {
+
+        case UNIT_FILE_SYSTEM:
+
+                if (root_dir && runtime)
+                        return -EINVAL;
+
+                if (runtime)
+                        p = strdup("/run/systemd/system");
+                else if (root_dir)
+                        asprintf(&p, "%s/%s", root_dir, SYSTEM_CONFIG_UNIT_PATH);
+                else
+                        p = strdup(SYSTEM_CONFIG_UNIT_PATH);
+
+                break;
+
+        case UNIT_FILE_GLOBAL:
+
+                if (root_dir)
+                        return -EINVAL;
+
+                if (runtime)
+                        p = strdup("/run/systemd/user");
+                else
+                        p = strdup(USER_CONFIG_UNIT_PATH);
+                break;
+
+        case UNIT_FILE_USER:
+
+                if (root_dir || runtime)
+                        return -EINVAL;
+
+                r = user_config_home(&p);
+                if (r <= 0)
+                        return r < 0 ? r : -ENOENT;
+
+                break;
+
+        default:
+                assert_not_reached("Bad scope");
+        }
+
+        if (!p)
+                return -ENOMEM;
+
+        *ret = p;
+        return 0;
+}
+
+static int add_file_change(
+                UnitFileChange **changes,
+                unsigned *n_changes,
+                UnitFileChangeType type,
+                const char *path,
+                const char *source) {
+
+        UnitFileChange *c;
+        unsigned i;
+
+        assert(type >= 0);
+        assert(type < _UNIT_FILE_CHANGE_TYPE_MAX);
+        assert(path);
+        assert(!changes == !n_changes);
+
+        if (!changes)
+                return 0;
+
+        c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
+        if (!c)
+                return -ENOMEM;
+
+        *changes = c;
+        i = *n_changes;
+
+        c[i].type = type;
+        c[i].path = strdup(path);
+        if (!c[i].path)
+                return -ENOMEM;
+
+        if (source) {
+                c[i].source = strdup(source);
+                if (!c[i].source) {
+                        free(c[i].path);
+                        return -ENOMEM;
+                }
+        } else
+                c[i].source = NULL;
+
+        *n_changes = i+1;
+        return 0;
+}
+
+static int mark_symlink_for_removal(
+                Set **remove_symlinks_to,
+                const char *p) {
+
+        char *n;
+        int r;
+
+        assert(p);
+
+        r = set_ensure_allocated(remove_symlinks_to, string_hash_func, string_compare_func);
+        if (r < 0)
+                return r;
+
+        n = strdup(p);
+        if (!n)
+                return -ENOMEM;
+
+        path_kill_slashes(n);
+
+        r = set_put(*remove_symlinks_to, n);
+        if (r < 0) {
+                free(n);
+                return r == -EEXIST ? 0 : r;
+        }
+
+        return 0;
+}
+
+static int remove_marked_symlinks_fd(
+                Set *remove_symlinks_to,
+                int fd,
+                const char *path,
+                const char *config_path,
+                bool *deleted,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        int r = 0;
+        DIR *d;
+        struct dirent buffer, *de;
+
+        assert(remove_symlinks_to);
+        assert(fd >= 0);
+        assert(path);
+        assert(config_path);
+        assert(deleted);
+
+        d = fdopendir(fd);
+        if (!d) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        rewinddir(d);
+
+        for (;;) {
+                int k;
+
+                k = readdir_r(d, &buffer, &de);
+                if (k != 0) {
+                        r = -errno;
+                        break;
+                }
+
+                if (!de)
+                        break;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                dirent_ensure_type(d, de);
+
+                if (de->d_type == DT_DIR) {
+                        int nfd, q;
+                        char *p;
+
+                        nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+                        if (nfd < 0) {
+                                if (errno == ENOENT)
+                                        continue;
+
+                                if (r == 0)
+                                        r = -errno;
+                                continue;
+                        }
+
+                        p = path_make_absolute(de->d_name, path);
+                        if (!p) {
+                                close_nointr_nofail(nfd);
+                                r = -ENOMEM;
+                                break;
+                        }
+
+                        /* 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);
+                        free(p);
+
+                        if (r == 0)
+                                r = q;
+
+                } else if (de->d_type == DT_LNK) {
+                        char *p, *dest;
+                        int q;
+                        bool found;
+
+                        p = path_make_absolute(de->d_name, path);
+                        if (!p) {
+                                r = -ENOMEM;
+                                break;
+                        }
+
+                        q = readlink_and_canonicalize(p, &dest);
+                        if (q < 0) {
+                                free(p);
+
+                                if (q == -ENOENT)
+                                        continue;
+
+                                if (r == 0)
+                                        r = q;
+                                continue;
+                        }
+
+                        found =
+                                set_get(remove_symlinks_to, dest) ||
+                                set_get(remove_symlinks_to, file_name_from_path(dest));
+
+                        if (found) {
+
+                                if (unlink(p) < 0 && errno != ENOENT) {
+
+                                        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;
+                                        }
+                                }
+                        }
+
+                        free(p);
+                        free(dest);
+                }
+        }
+
+        closedir(d);
+
+        return r;
+}
+
+static int remove_marked_symlinks(
+                Set *remove_symlinks_to,
+                const char *config_path,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        int fd, r = 0;
+        bool deleted;
+
+        assert(config_path);
+
+        if (set_size(remove_symlinks_to) <= 0)
+                return 0;
+
+        fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+        if (fd < 0)
+                return -errno;
+
+        do {
+                int q, cfd;
+                deleted = false;
+
+                cfd = dup(fd);
+                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);
+                if (r == 0)
+                        r = q;
+        } while (deleted);
+
+        close_nointr_nofail(fd);
+
+        return r;
+}
+
+static int find_symlinks_fd(
+                const char *name,
+                int fd,
+                const char *path,
+                const char *config_path,
+                bool *same_name_link) {
+
+        int r = 0;
+        DIR *d;
+        struct dirent buffer, *de;
+
+        assert(name);
+        assert(fd >= 0);
+        assert(path);
+        assert(config_path);
+        assert(same_name_link);
+
+        d = fdopendir(fd);
+        if (!d) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        for (;;) {
+                int k;
+
+                k = readdir_r(d, &buffer, &de);
+                if (k != 0) {
+                        r = -errno;
+                        break;
+                }
+
+                if (!de)
+                        break;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                dirent_ensure_type(d, de);
+
+                if (de->d_type == DT_DIR) {
+                        int nfd, q;
+                        char *p;
+
+                        nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+                        if (nfd < 0) {
+                                if (errno == ENOENT)
+                                        continue;
+
+                                if (r == 0)
+                                        r = -errno;
+                                continue;
+                        }
+
+                        p = path_make_absolute(de->d_name, path);
+                        if (!p) {
+                                close_nointr_nofail(nfd);
+                                r = -ENOMEM;
+                                break;
+                        }
+
+                        /* This will close nfd, regardless whether it succeeds or not */
+                        q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
+                        free(p);
+
+                        if (q > 0) {
+                                r = 1;
+                                break;
+                        }
+
+                        if (r == 0)
+                                r = q;
+
+                } else if (de->d_type == DT_LNK) {
+                        char *p, *dest;
+                        bool found_path, found_dest, b = false;
+                        int q;
+
+                        /* Acquire symlink name */
+                        p = path_make_absolute(de->d_name, path);
+                        if (!p) {
+                                r = -ENOMEM;
+                                break;
+                        }
+
+                        /* Acquire symlink destination */
+                        q = readlink_and_canonicalize(p, &dest);
+                        if (q < 0) {
+                                free(p);
+
+                                if (q == -ENOENT)
+                                        continue;
+
+                                if (r == 0)
+                                        r = q;
+                                continue;
+                        }
+
+                        /* Check if the symlink itself matches what we
+                         * are looking for */
+                        if (path_is_absolute(name))
+                                found_path = path_equal(p, name);
+                        else
+                                found_path = streq(de->d_name, name);
+
+                        /* Check if what the symlink points to
+                         * matches what we are looking for */
+                        if (path_is_absolute(name))
+                                found_dest = path_equal(dest, name);
+                        else
+                                found_dest = streq(file_name_from_path(dest), name);
+
+                        free(dest);
+
+                        if (found_path && found_dest) {
+                                char *t;
+
+                                /* Filter out same name links in the main
+                                 * config path */
+                                t = path_make_absolute(name, config_path);
+                                if (!t) {
+                                        free(p);
+                                        free(dest);
+                                        r = -ENOMEM;
+                                        break;
+                                }
+
+                                b = path_equal(t, p);
+                                free(t);
+                        }
+
+                        free(p);
+
+                        if (b)
+                                *same_name_link = true;
+                        else if (found_path || found_dest) {
+                                r = 1;
+                                break;
+                        }
+                }
+        }
+
+        closedir(d);
+
+        return r;
+}
+
+static int find_symlinks(
+                const char *name,
+                const char *config_path,
+                bool *same_name_link) {
+
+        int fd;
+
+        assert(name);
+        assert(config_path);
+        assert(same_name_link);
+
+        fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+        if (fd < 0)
+                return -errno;
+
+        /* This takes possession of fd and closes it */
+        return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
+}
+
+static int find_symlinks_in_scope(
+                UnitFileScope scope,
+                const char *root_dir,
+                const char *name,
+                UnitFileState *state) {
+
+        int r;
+        char *path;
+        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;
+
+                r = find_symlinks(name, path, &same_name_link_runtime);
+                free(path);
+
+                if (r < 0)
+                        return r;
+                else if (r > 0) {
+                        *state = UNIT_FILE_ENABLED_RUNTIME;
+                        return r;
+                }
+        }
+
+        /* Then look in the normal config path */
+        r = get_config_path(scope, false, root_dir, &path);
+        if (r < 0)
+                return r;
+
+        r = find_symlinks(name, path, &same_name_link);
+        free(path);
+
+        if (r < 0)
+                return r;
+        else if (r > 0) {
+                *state = UNIT_FILE_ENABLED;
+                return r;
+        }
+
+        /* Hmm, we didn't find it, but maybe we found the same name
+         * link? */
+        if (same_name_link_runtime) {
+                *state = UNIT_FILE_LINKED_RUNTIME;
+                return 1;
+        } else if (same_name_link) {
+                *state = UNIT_FILE_LINKED;
+                return 1;
+        }
+
+        return 0;
+}
+
+int unit_file_mask(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                char *files[],
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        char **i, *prefix;
+        int r;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+        r = get_config_path(scope, runtime, root_dir, &prefix);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, files) {
+                char *path;
+
+                if (!unit_name_is_valid_no_type(*i, true)) {
+                        if (r == 0)
+                                r = -EINVAL;
+                        continue;
+                }
+
+                path = path_make_absolute(*i, prefix);
+                if (!path) {
+                        r = -ENOMEM;
+                        break;
+                }
+
+                if (symlink("/dev/null", path) >= 0) {
+                        add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
+
+                        free(path);
+                        continue;
+                }
+
+                if (errno == EEXIST) {
+
+                        if (null_or_empty_path(path) > 0) {
+                                free(path);
+                                continue;
+                        }
+
+                        if (force) {
+                                unlink(path);
+
+                                if (symlink("/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");
+
+                                        free(path);
+                                        continue;
+                                }
+                        }
+
+                        if (r == 0)
+                                r = -EEXIST;
+                } else {
+                        if (r == 0)
+                                r = -errno;
+                }
+
+                free(path);
+        }
+
+        free(prefix);
+
+        return r;
+}
+
+int unit_file_unmask(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                char *files[],
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        char **i, *config_path = NULL;
+        int r, q;
+        Set *remove_symlinks_to = NULL;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+        r = get_config_path(scope, runtime, root_dir, &config_path);
+        if (r < 0)
+                goto finish;
+
+        STRV_FOREACH(i, files) {
+                char *path;
+
+                if (!unit_name_is_valid_no_type(*i, true)) {
+                        if (r == 0)
+                                r = -EINVAL;
+                        continue;
+                }
+
+                path = path_make_absolute(*i, config_path);
+                if (!path) {
+                        r = -ENOMEM;
+                        break;
+                }
+
+                q = null_or_empty_path(path);
+                if (q > 0) {
+                        if (unlink(path) >= 0) {
+                                mark_symlink_for_removal(&remove_symlinks_to, path);
+                                add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
+
+                                free(path);
+                                continue;
+                        }
+
+                        q = -errno;
+                }
+
+                if (q != -ENOENT && r == 0)
+                        r = q;
+
+                free(path);
+        }
+
+
+finish:
+        q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
+        if (r == 0)
+                r = q;
+
+        set_free_free(remove_symlinks_to);
+        free(config_path);
+
+        return r;
+}
+
+int unit_file_link(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                char *files[],
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        LookupPaths paths;
+        char **i, *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);
+        if (r < 0)
+                return r;
+
+        r = get_config_path(scope, runtime, root_dir, &config_path);
+        if (r < 0)
+                goto finish;
+
+        STRV_FOREACH(i, files) {
+                char *path, *fn;
+                struct stat st;
+
+                fn = file_name_from_path(*i);
+
+                if (!path_is_absolute(*i) ||
+                    !unit_name_is_valid_no_type(fn, true)) {
+                        if (r == 0)
+                                r = -EINVAL;
+                        continue;
+                }
+
+                if (lstat(*i, &st) < 0) {
+                        if (r == 0)
+                                r = -errno;
+                        continue;
+                }
+
+                if (!S_ISREG(st.st_mode)) {
+                        r = -ENOENT;
+                        continue;
+                }
+
+                q = in_search_path(*i, paths.unit_path);
+                if (q < 0) {
+                        r = q;
+                        break;
+                }
+
+                if (q > 0)
+                        continue;
+
+                path = path_make_absolute(fn, config_path);
+                if (!path) {
+                        r = -ENOMEM;
+                        break;
+                }
+
+                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;
+
+                        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);
+                                continue;
+                        }
+
+                        free(dest);
+
+                        if (force) {
+                                unlink(path);
+
+                                if (symlink(*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;
+                                }
+                        }
+
+                        if (r == 0)
+                                r = -EEXIST;
+                } else {
+                        if (r == 0)
+                                r = -errno;
+                }
+
+                free(path);
+        }
+
+                finish:
+        lookup_paths_free(&paths);
+        free(config_path);
+
+        return r;
+}
+
+void unit_file_list_free(Hashmap *h) {
+        UnitFileList *i;
+
+        while ((i = hashmap_steal_first(h))) {
+                free(i->path);
+                free(i);
+        }
+
+        hashmap_free(h);
+}
+
+void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
+        unsigned i;
+
+        assert(changes || n_changes == 0);
+
+        if (!changes)
+                return;
+
+        for (i = 0; i < n_changes; i++) {
+                free(changes[i].path);
+                free(changes[i].source);
+        }
+
+        free(changes);
+}
+
+static void install_info_free(InstallInfo *i) {
+        assert(i);
+
+        free(i->name);
+        free(i->path);
+        strv_free(i->aliases);
+        strv_free(i->wanted_by);
+        free(i);
+}
+
+static void install_info_hashmap_free(Hashmap *m) {
+        InstallInfo *i;
+
+        if (!m)
+                return;
+
+        while ((i = hashmap_steal_first(m)))
+                install_info_free(i);
+
+        hashmap_free(m);
+}
+
+static void install_context_done(InstallContext *c) {
+        assert(c);
+
+        install_info_hashmap_free(c->will_install);
+        install_info_hashmap_free(c->have_installed);
+
+        c->will_install = c->have_installed = NULL;
+}
+
+static int install_info_add(
+                InstallContext *c,
+                const char *name,
+                const char *path) {
+        InstallInfo *i = NULL;
+        int r;
+
+        assert(c);
+        assert(name || path);
+
+        if (!name)
+                name = file_name_from_path(path);
+
+        if (!unit_name_is_valid_no_type(name, true))
+                return -EINVAL;
+
+        if (hashmap_get(c->have_installed, name) ||
+            hashmap_get(c->will_install, name))
+                return 0;
+
+        r = hashmap_ensure_allocated(&c->will_install, string_hash_func, string_compare_func);
+        if (r < 0)
+                return r;
+
+        i = new0(InstallInfo, 1);
+        if (!i)
+                return -ENOMEM;
+
+        i->name = strdup(name);
+        if (!i->name) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        if (path) {
+                i->path = strdup(path);
+                if (!i->path) {
+                        r = -ENOMEM;
+                        goto fail;
+                }
+        }
+
+        r = hashmap_put(c->will_install, i->name, i);
+        if (r < 0)
+                goto fail;
+
+        return 0;
+
+fail:
+        if (i)
+                install_info_free(i);
+
+        return r;
+}
+
+static int install_info_add_auto(
+                InstallContext *c,
+                const char *name_or_path) {
+
+        assert(c);
+        assert(name_or_path);
+
+        if (path_is_absolute(name_or_path))
+                return install_info_add(c, NULL, name_or_path);
+        else
+                return install_info_add(c, name_or_path, NULL);
+}
+
+static int config_parse_also(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                int ltype,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        char *w;
+        size_t l;
+        char *state;
+        InstallContext *c = data;
+
+        assert(filename);
+        assert(lvalue);
+        assert(rvalue);
+
+        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
+                char *n;
+                int r;
+
+                n = strndup(w, l);
+                if (!n)
+                        return -ENOMEM;
+
+                r = install_info_add(c, n, NULL);
+                if (r < 0) {
+                        free(n);
+                        return r;
+                }
+
+                free(n);
+        }
+
+        return 0;
+}
+
+static int unit_file_load(
+                InstallContext *c,
+                InstallInfo *info,
+                const char *path,
+                bool allow_symlink) {
+
+        const ConfigItem items[] = {
+                { "Alias",    config_parse_strv, 0, &info->aliases,   "Install" },
+                { "WantedBy", config_parse_strv, 0, &info->wanted_by, "Install" },
+                { "Also",     config_parse_also, 0, c,                "Install" },
+                { NULL, NULL, 0, NULL, NULL }
+        };
+
+        int fd;
+        FILE *f;
+        int r;
+
+        assert(c);
+        assert(info);
+        assert(path);
+
+        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);
+                return -ENOMEM;
+        }
+
+        r = config_parse(path, f, NULL, items, true, info);
+        fclose(f);
+        if (r < 0)
+                return r;
+
+        return strv_length(info->aliases) + strv_length(info->wanted_by);
+}
+
+static int unit_file_search(
+                InstallContext *c,
+                InstallInfo *info,
+                LookupPaths *paths,
+                const char *root_dir,
+                bool allow_symlink) {
+
+        char **p;
+        int r;
+
+        assert(c);
+        assert(info);
+        assert(paths);
+
+        if (info->path)
+                return unit_file_load(c, info, info->path, allow_symlink);
+
+        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);
+
+                if (!path)
+                        return -ENOMEM;
+
+                r = unit_file_load(c, info, path, allow_symlink);
+
+                if (r >= 0)
+                        info->path = path;
+                else
+                        free(path);
+
+                if (r != -ENOENT && r != -ELOOP)
+                        return r;
+        }
+
+        return -ENOENT;
+}
+
+static int unit_file_can_install(
+                LookupPaths *paths,
+                const char *root_dir,
+                const char *name,
+                bool allow_symlink) {
+
+        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));
+
+        r = unit_file_search(&c, i, paths, root_dir, allow_symlink);
+
+        if (r >= 0)
+                r = strv_length(i->aliases) + strv_length(i->wanted_by);
+
+        install_context_done(&c);
+
+        return r;
+}
+
+static int create_symlink(
+                const char *old_path,
+                const char *new_path,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        char *dest;
+        int r;
+
+        assert(old_path);
+        assert(new_path);
+
+        mkdir_parents(new_path, 0755);
+
+        if (symlink(old_path, new_path) >= 0) {
+                add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path);
+                return 0;
+        }
+
+        if (errno != EEXIST)
+                return -errno;
+
+        r = readlink_and_make_absolute(new_path, &dest);
+        if (r < 0)
+                return r;
+
+        if (path_equal(dest, old_path)) {
+                free(dest);
+                return 0;
+        }
+
+        free(dest);
+
+        if (force)
+                return -EEXIST;
+
+        unlink(new_path);
+
+        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;
+        }
+
+        return -errno;
+}
+
+static int install_info_symlink_alias(
+                InstallInfo *i,
+                const char *config_path,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        char **s;
+        int r = 0, q;
+
+        assert(i);
+        assert(config_path);
+
+        STRV_FOREACH(s, i->aliases) {
+                char *alias_path;
+
+                alias_path = path_make_absolute(*s, 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;
+        }
+
+        return r;
+}
+
+static int install_info_symlink_wants(
+                InstallInfo *i,
+                const char *config_path,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        char **s;
+        int r = 0, q;
+
+        assert(i);
+        assert(config_path);
+
+        STRV_FOREACH(s, i->wanted_by) {
+                char *path;
+
+                if (!unit_name_is_valid_no_type(*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 (r == 0)
+                        r = q;
+        }
+
+        return r;
+}
+
+static int install_info_symlink_link(
+                InstallInfo *i,
+                LookupPaths *paths,
+                const char *config_path,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        int r;
+        char *path;
+
+        assert(i);
+        assert(paths);
+        assert(config_path);
+        assert(i->path);
+
+        r = in_search_path(i->path, paths->unit_path);
+        if (r != 0)
+                return r;
+
+        if (asprintf(&path, "%s/%s", config_path, i->name) < 0)
+                return -ENOMEM;
+
+        r = create_symlink(i->path, path, force, changes, n_changes);
+        free(path);
+
+        return r;
+}
+
+static int install_info_apply(
+                InstallInfo *i,
+                LookupPaths *paths,
+                const char *config_path,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        int r, q;
+
+        assert(i);
+        assert(paths);
+        assert(config_path);
+
+        r = install_info_symlink_alias(i, config_path, force, changes, n_changes);
+
+        q = install_info_symlink_wants(i, config_path, force, changes, n_changes);
+        if (r == 0)
+                r = q;
+
+        q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes);
+        if (r == 0)
+                r = q;
+
+        return r;
+}
+
+static int install_context_apply(
+                InstallContext *c,
+                LookupPaths *paths,
+                const char *config_path,
+                const char *root_dir,
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        InstallInfo *i;
+        int r = 0, q;
+
+        assert(c);
+        assert(paths);
+        assert(config_path);
+
+        while ((i = hashmap_first(c->will_install))) {
+
+                q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
+                if (q < 0)
+                        return q;
+
+                assert_se(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) {
+                        if (r >= 0)
+                                r = q;
+
+                        return r;
+                } else if (r >= 0)
+                        r += q;
+
+                q = install_info_apply(i, paths, config_path, force, changes, n_changes);
+                if (r >= 0 && q < 0)
+                        r = q;
+        }
+
+        return r;
+}
+
+static int install_context_mark_for_removal(
+                InstallContext *c,
+                LookupPaths *paths,
+                Set **remove_symlinks_to,
+                const char *config_path,
+                const char *root_dir) {
+
+        InstallInfo *i;
+        int r = 0, q;
+
+        assert(c);
+        assert(paths);
+        assert(config_path);
+
+        /* Marks all items for removal */
+
+        while ((i = hashmap_first(c->will_install))) {
+
+                q = hashmap_ensure_allocated(&c->have_installed, string_hash_func, string_compare_func);
+                if (q < 0)
+                        return q;
+
+                assert_se(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) {
+                        if (r >= 0)
+                                r = q;
+
+                        return r;
+                } else if (r >= 0)
+                        r += q;
+
+                q = mark_symlink_for_removal(remove_symlinks_to, i->name);
+                if (r >= 0 && q < 0)
+                        r = q;
+        }
+
+        return r;
+}
+
+int unit_file_enable(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                char *files[],
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        LookupPaths paths;
+        InstallContext c;
+        char **i, *config_path = NULL;
+        int r;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+        zero(paths);
+        zero(c);
+
+        r = lookup_paths_init_from_scope(&paths, scope);
+        if (r < 0)
+                return r;
+
+        r = get_config_path(scope, runtime, root_dir, &config_path);
+        if (r < 0)
+                goto finish;
+
+        STRV_FOREACH(i, files) {
+                r = install_info_add_auto(&c, *i);
+                if (r < 0)
+                        goto finish;
+        }
+
+        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;
+}
+
+int unit_file_disable(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                char *files[],
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        LookupPaths paths;
+        InstallContext c;
+        char **i, *config_path = NULL;
+        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);
+        if (r < 0)
+                return r;
+
+        r = get_config_path(scope, runtime, root_dir, &config_path);
+        if (r < 0)
+                goto finish;
+
+        STRV_FOREACH(i, files) {
+                r = install_info_add_auto(&c, *i);
+                if (r < 0)
+                        goto finish;
+        }
+
+        r = install_context_mark_for_removal(&c, &paths, &remove_symlinks_to, config_path, root_dir);
+
+        q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
+        if (r == 0)
+                r = 1;
+
+finish:
+        install_context_done(&c);
+        lookup_paths_free(&paths);
+        set_free_free(remove_symlinks_to);
+        free(config_path);
+
+        return r;
+}
+
+int unit_file_reenable(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                char *files[],
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        LookupPaths paths;
+        InstallContext c;
+        char **i, *config_path = NULL;
+        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);
+        if (r < 0)
+                return r;
+
+        r = get_config_path(scope, runtime, root_dir, &config_path);
+        if (r < 0)
+                goto finish;
+
+        STRV_FOREACH(i, files) {
+                r = mark_symlink_for_removal(&remove_symlinks_to, *i);
+                if (r < 0)
+                        goto finish;
+
+                r = install_info_add_auto(&c, *i);
+                if (r < 0)
+                        goto finish;
+        }
+
+        r = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes);
+
+        q = install_context_apply(&c, &paths, config_path, root_dir, force, changes, n_changes);
+        if (r == 0)
+                r = q;
+
+finish:
+        lookup_paths_free(&paths);
+        install_context_done(&c);
+        set_free_free(remove_symlinks_to);
+        free(config_path);
+
+        return r;
+}
+
+UnitFileState unit_file_get_state(
+                UnitFileScope scope,
+                const char *root_dir,
+                const char *name) {
+
+        LookupPaths paths;
+        UnitFileState state = _UNIT_FILE_STATE_INVALID;
+        char **i, *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_no_type(name, true))
+                return -EINVAL;
+
+        r = lookup_paths_init_from_scope(&paths, scope);
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, paths.unit_path) {
+                struct stat st;
+
+                free(path);
+                path = NULL;
+
+                if (root_dir)
+                        asprintf(&path, "%s/%s/%s", root_dir, *i, name);
+                else
+                        asprintf(&path, "%s/%s", *i, name);
+
+                if (!path) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (lstat(path, &st) < 0) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        r = -errno;
+                        goto finish;
+                }
+
+                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;
+                }
+
+                r = find_symlinks_in_scope(scope, root_dir, name, &state);
+                if (r < 0) {
+                        goto finish;
+                } else if (r > 0) {
+                        r = 0;
+                        goto finish;
+                }
+
+                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;
+                }
+        }
+
+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 r;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+        assert(name);
+
+        if (scope == UNIT_FILE_SYSTEM)
+                r = conf_files_list(&files, ".preset",
+                                    "/etc/systemd/system.preset",
+                                    "/usr/local/lib/systemd/system.preset",
+                                    "/usr/lib/systemd/system.preset",
+                                    "/lib/systemd/system.preset",
+                                    NULL);
+        else if (scope == UNIT_FILE_GLOBAL)
+                r = conf_files_list(&files, ".preset",
+                                    "/etc/systemd/user.preset",
+                                    "/usr/local/lib/systemd/user.preset",
+                                    "/usr/lib/systemd/user.preset",
+                                    NULL);
+        else
+                return 1;
+
+        if (r < 0)
+                return r;
+
+        STRV_FOREACH(i, files) {
+                FILE *f;
+
+                f = fopen(*i, "re");
+                if (!f) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        r = -errno;
+                        goto finish;
+                }
+
+                for (;;) {
+                        char line[LINE_MAX], *l;
+
+                        if (!fgets(line, sizeof(line), f))
+                                break;
+
+                        l = strstrip(line);
+                        if (!*l)
+                                continue;
+
+                        if (strchr(COMMENTS, *l))
+                                continue;
+
+                        if (first_word(l, "enable")) {
+                                l += 6;
+                                l += strspn(l, WHITESPACE);
+
+                                if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
+                                        r = 1;
+                                        fclose(f);
+                                        goto finish;
+                                }
+                        } 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;
+                                }
+                        } else
+                                log_debug("Couldn't parse line '%s'", l);
+                }
+
+                fclose(f);
+        }
+
+        /* Default is "enable" */
+        r = 1;
+
+finish:
+        strv_free(files);
+
+        return r;
+}
+
+int unit_file_preset(
+                UnitFileScope scope,
+                bool runtime,
+                const char *root_dir,
+                char *files[],
+                bool force,
+                UnitFileChange **changes,
+                unsigned *n_changes) {
+
+        LookupPaths paths;
+        InstallContext plus, minus;
+        char **i, *config_path = NULL;
+        Set *remove_symlinks_to = NULL;
+        int r, q;
+
+        assert(scope >= 0);
+        assert(scope < _UNIT_FILE_SCOPE_MAX);
+
+        zero(paths);
+        zero(plus);
+        zero(minus);
+
+        r = lookup_paths_init_from_scope(&paths, scope);
+        if (r < 0)
+                return r;
+
+        r = get_config_path(scope, runtime, root_dir, &config_path);
+        if (r < 0)
+                goto finish;
+
+        STRV_FOREACH(i, files) {
+
+                if (!unit_name_is_valid_no_type(*i, true)) {
+                        r = -EINVAL;
+                        goto finish;
+                }
+
+                r = unit_file_query_preset(scope, *i);
+                if (r < 0)
+                        goto finish;
+
+                if (r)
+                        r = install_info_add_auto(&plus, *i);
+                else
+                        r = install_info_add_auto(&minus, *i);
+
+                if (r < 0)
+                        goto finish;
+        }
+
+        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);
+        if (r == 0)
+                r = q;
+
+        q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
+        if (r == 0)
+                r = q;
+
+finish:
+        lookup_paths_free(&paths);
+        install_context_done(&plus);
+        install_context_done(&minus);
+        set_free_free(remove_symlinks_to);
+        free(config_path);
+
+        return r;
+}
+
+int unit_file_get_list(
+                UnitFileScope scope,
+                const char *root_dir,
+                Hashmap *h) {
+
+        LookupPaths paths;
+        char **i, *buf = NULL;
+        DIR *d = NULL;
+        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 (r < 0)
+                return r;
+
+        STRV_FOREACH(i, paths.unit_path) {
+                struct dirent buffer, *de;
+                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;
+
+                if (d)
+                        closedir(d);
+
+                d = opendir(units_dir);
+                if (!d) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        r = -errno;
+                        goto finish;
+                }
+
+                for (;;) {
+                        UnitFileList *f;
+
+                        r = readdir_r(d, &buffer, &de);
+                        if (r != 0) {
+                                r = -r;
+                                goto finish;
+                        }
+
+                        if (!de)
+                                break;
+
+                        if (ignore_file(de->d_name))
+                                continue;
+
+                        if (!unit_name_is_valid_no_type(de->d_name, true))
+                                continue;
+
+                        if (hashmap_get(h, de->d_name))
+                                continue;
+
+                        r = dirent_ensure_type(d, de);
+                        if (r < 0) {
+                                if (errno == ENOENT)
+                                        continue;
+
+                                goto finish;
+                        }
+
+                        if (de->d_type != DT_LNK && de->d_type != DT_REG)
+                                continue;
+
+                        f = new0(UnitFileList, 1);
+                        if (!f) {
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        f->path = path_make_absolute(de->d_name, units_dir);
+                        if (!f->path) {
+                                free(f);
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        r = null_or_empty_path(f->path);
+                        if (r < 0) {
+                                free(f->path);
+                                free(f);
+                                goto finish;
+                        } else if (r > 0) {
+                                f->state =
+                                        path_startswith(*i, "/run") ?
+                                        UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
+                                goto found;
+                        }
+
+                        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)
+                                goto found;
+
+                        r = unit_file_can_install(&paths, root_dir, f->path, true);
+                        if (r < 0) {
+                                free(f->path);
+                                free(f);
+                                goto finish;
+                        } else if (r > 0) {
+                                f->state = UNIT_FILE_DISABLED;
+                                goto found;
+                        } else if (r == 0) {
+                                f->state = UNIT_FILE_STATIC;
+                                goto found;
+                        }
+
+                        free(f->path);
+                        free(f);
+                        continue;
+
+                found:
+                        r = hashmap_put(h, file_name_from_path(f->path), f);
+                        if (r < 0) {
+                                free(f->path);
+                                free(f);
+                                goto finish;
+                        }
+                }
+        }
+
+finish:
+        lookup_paths_free(&paths);
+        free(buf);
+
+        if (d)
+                closedir(d);
+
+        return r;
+}
+
+static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
+        [UNIT_FILE_ENABLED] = "enabled",
+        [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtie",
+        [UNIT_FILE_LINKED] = "linked",
+        [UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
+        [UNIT_FILE_MASKED] = "masked",
+        [UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
+        [UNIT_FILE_STATIC] = "static",
+        [UNIT_FILE_DISABLED] = "disabled"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState);
diff --git a/src/install.h b/src/install.h
new file mode 100644
index 0000000..a3eacf5
--- /dev/null
+++ b/src/install.h
@@ -0,0 +1,86 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooinstallhfoo
+#define fooinstallhfoo
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "hashmap.h"
+
+typedef enum UnitFileScope {
+        UNIT_FILE_SYSTEM,
+        UNIT_FILE_GLOBAL,
+        UNIT_FILE_USER,
+        _UNIT_FILE_SCOPE_MAX,
+        _UNIT_FILE_SCOPE_INVALID = -1
+} UnitFileScope;
+
+typedef enum UnitFileState {
+        UNIT_FILE_ENABLED,
+        UNIT_FILE_ENABLED_RUNTIME,
+        UNIT_FILE_LINKED,
+        UNIT_FILE_LINKED_RUNTIME,
+        UNIT_FILE_MASKED,
+        UNIT_FILE_MASKED_RUNTIME,
+        UNIT_FILE_STATIC,
+        UNIT_FILE_DISABLED,
+        _UNIT_FILE_STATE_MAX,
+        _UNIT_FILE_STATE_INVALID = -1
+} UnitFileState;
+
+typedef enum UnitFileChangeType {
+        UNIT_FILE_SYMLINK,
+        UNIT_FILE_UNLINK,
+        _UNIT_FILE_CHANGE_TYPE_MAX,
+        _UNIT_FILE_CHANGE_TYPE_INVALID = -1
+} UnitFileChangeType;
+
+typedef struct UnitFileChange {
+        UnitFileChangeType type;
+        char *path;
+        char *source;
+} UnitFileChange;
+
+typedef struct UnitFileList {
+        char *path;
+        UnitFileState state;
+} UnitFileList;
+
+int unit_file_enable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
+int unit_file_disable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes);
+int unit_file_reenable(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
+int unit_file_link(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
+int unit_file_preset(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
+int unit_file_mask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], bool force, UnitFileChange **changes, unsigned *n_changes);
+int unit_file_unmask(UnitFileScope scope, bool runtime, const char *root_dir, char *files[], UnitFileChange **changes, unsigned *n_changes);
+
+UnitFileState unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename);
+
+int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h);
+
+void unit_file_list_free(Hashmap *h);
+void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes);
+
+int unit_file_query_preset(UnitFileScope scope, const char *name);
+
+const char *unit_file_state_to_string(UnitFileState s);
+UnitFileState unit_file_state_from_string(const char *s);
+
+#endif
diff --git a/src/test-install.c b/src/test-install.c
new file mode 100644
index 0000000..f8e87e0
--- /dev/null
+++ b/src/test-install.c
@@ -0,0 +1,264 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  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 General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "util.h"
+#include "install.h"
+
+static void dump_changes(UnitFileChange *c, unsigned n) {
+        unsigned i;
+
+        assert(n == 0 || c);
+
+        for (i = 0; i < n; i++) {
+                if (c[i].type == UNIT_FILE_UNLINK)
+                        printf("rm '%s'\n", c[i].path);
+                else if (c[i].type == UNIT_FILE_SYMLINK)
+                        printf("ln -s '%s' '%s'\n", c[i].source, c[i].path);
+        }
+}
+
+int main(int argc, char* argv[]) {
+        Hashmap *h;
+        UnitFileList *p;
+        Iterator i;
+        int r;
+        const char *const files[] = { "avahi-daemon.service", NULL };
+        const char *const files2[] = { "/home/lennart/test.service", NULL };
+        UnitFileChange *changes = NULL;
+        unsigned n_changes = 0;
+
+        h = hashmap_new(string_hash_func, string_compare_func);
+        r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h);
+        assert_se(r == 0);
+
+        HASHMAP_FOREACH(p, h, i) {
+                UnitFileState s;
+
+                s = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(p->path));
+
+                assert_se(p->state == s);
+
+                fprintf(stderr, "%s (%s)\n",
+                        p->path,
+                        unit_file_state_to_string(p->state));
+        }
+
+        unit_file_list_free(h);
+
+        log_error("enable");
+
+        r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        log_error("enable2");
+
+        r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_ENABLED);
+
+        log_error("disable");
+
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
+
+        log_error("mask");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
+        assert_se(r >= 0);
+        log_error("mask2");
+        r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
+
+        log_error("unmask");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
+        assert_se(r >= 0);
+        log_error("unmask2");
+        r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
+
+        log_error("mask");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_mask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
+
+        log_error("disable");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
+        assert_se(r >= 0);
+        log_error("disable2");
+        r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_MASKED);
+
+        log_error("umask");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_unmask(UNIT_FILE_SYSTEM, false, NULL, (char**) files, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0]) == UNIT_FILE_DISABLED);
+
+        log_error("enable files2");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_enable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, false, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == UNIT_FILE_ENABLED);
+
+        log_error("disable files2");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == _UNIT_FILE_STATE_INVALID);
+
+        log_error("link files2");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_link(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, false, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == UNIT_FILE_LINKED);
+
+        log_error("disable files2");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == _UNIT_FILE_STATE_INVALID);
+
+        log_error("link files2");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_link(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, false, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == UNIT_FILE_LINKED);
+
+        log_error("reenable files2");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_reenable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, false, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == UNIT_FILE_ENABLED);
+
+        log_error("disable files2");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_disable(UNIT_FILE_SYSTEM, false, NULL, (char**) files2, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files2[0])) == _UNIT_FILE_STATE_INVALID);
+        log_error("preset files");
+        changes = NULL;
+        n_changes = 0;
+
+        r = unit_file_preset(UNIT_FILE_SYSTEM, false, NULL, (char**) files, false, &changes, &n_changes);
+        assert_se(r >= 0);
+
+        dump_changes(changes, n_changes);
+        unit_file_changes_free(changes, n_changes);
+
+        assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, NULL, file_name_from_path(files[0])) == UNIT_FILE_ENABLED);
+
+        return 0;
+}
diff --git a/src/util.c b/src/util.c
index 3187ec3..4e441a7 100644
--- a/src/util.c
+++ b/src/util.c
@@ -1157,6 +1157,29 @@ int readlink_and_make_absolute(const char *p, char **r) {
         return 0;
 }
 
+int readlink_and_canonicalize(const char *p, char **r) {
+        char *t, *s;
+        int j;
+
+        assert(p);
+        assert(r);
+
+        j = readlink_and_make_absolute(p, &t);
+        if (j < 0)
+                return j;
+
+        s = canonicalize_file_name(t);
+        if (s) {
+                free(t);
+                *r = s;
+        } else
+                *r = t;
+
+        path_kill_slashes(*r);
+
+        return 0;
+}
+
 int parent_of_path(const char *path, char **_r) {
         const char *e, *a = NULL, *b = NULL, *p;
         char *r;
@@ -3944,6 +3967,17 @@ bool null_or_empty(struct stat *st) {
         return false;
 }
 
+int null_or_empty_path(const char *fn) {
+        struct stat st;
+
+        assert(fn);
+
+        if (stat(fn, &st) < 0)
+                return -errno;
+
+        return null_or_empty(&st);
+}
+
 DIR *xopendirat(int fd, const char *name, int flags) {
         int nfd;
         DIR *d;
@@ -5268,6 +5302,53 @@ int glob_exists(const char *path) {
         return r;
 }
 
+int dirent_ensure_type(DIR *d, struct dirent *de) {
+        struct stat st;
+
+        assert(d);
+        assert(de);
+
+        if (de->d_type != DT_UNKNOWN)
+                return 0;
+
+        if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+                return -errno;
+
+        de->d_type =
+                S_ISREG(st.st_mode)  ? DT_REG  :
+                S_ISDIR(st.st_mode)  ? DT_DIR  :
+                S_ISLNK(st.st_mode)  ? DT_LNK  :
+                S_ISFIFO(st.st_mode) ? DT_FIFO :
+                S_ISSOCK(st.st_mode) ? DT_SOCK :
+                S_ISCHR(st.st_mode)  ? DT_CHR  :
+                S_ISBLK(st.st_mode)  ? DT_BLK  :
+                                       DT_UNKNOWN;
+
+        return 0;
+}
+
+int in_search_path(const char *path, char **search) {
+        char **i, *parent;
+        int r;
+
+        r = parent_of_path(path, &parent);
+        if (r < 0)
+                return r;
+
+        r = 0;
+
+        STRV_FOREACH(i, search) {
+                if (path_equal(parent, *i)) {
+                        r = 1;
+                        break;
+                }
+        }
+
+        free(parent);
+
+        return r;
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",
diff --git a/src/util.h b/src/util.h
index 7a4bf81..f39c01f 100644
--- a/src/util.h
+++ b/src/util.h
@@ -217,6 +217,7 @@ char **replace_env_argv(char **argv, char **env);
 
 int readlink_malloc(const char *p, char **r);
 int readlink_and_make_absolute(const char *p, char **r);
+int readlink_and_canonicalize(const char *p, char **r);
 
 char *file_name_from_path(const char *p);
 bool is_path(const char *p);
@@ -385,6 +386,7 @@ int wait_for_terminate_and_warn(const char *name, pid_t pid);
 _noreturn_ void freeze(void);
 
 bool null_or_empty(struct stat *st);
+int null_or_empty_path(const char *fn);
 
 DIR *xopendirat(int dirfd, const char *name, int flags);
 
@@ -448,6 +450,10 @@ int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **h
 
 int glob_exists(const char *path);
 
+int dirent_ensure_type(DIR *d, struct dirent *de);
+
+int in_search_path(const char *path, char **search);
+
 #define NULSTR_FOREACH(i, l)                                    \
         for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1)
 

commit 09adcdf71d762803b33bd2064a0fed56cf4072e4
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jul 22 04:20:02 2011 +0200

    systemctl: fix indenting

diff --git a/src/systemctl.c b/src/systemctl.c
index 63eb324..36346ee 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -3731,12 +3731,12 @@ static int remove_marked_symlinks(const char *config_path) {
 
 static int create_symlink(const char *verb, const char *orig_old_path, const char *new_path) {
         int r;
-	const char *old_path;
+        const char *old_path;
 
-	if (arg_root)
-		old_path = orig_old_path+strlen(arg_root);
-	else
-		old_path = orig_old_path;
+        if (arg_root)
+                old_path = orig_old_path+strlen(arg_root);
+        else
+                old_path = orig_old_path;
 
         assert(old_path);
         assert(new_path);
@@ -3949,22 +3949,22 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
 
         STRV_FOREACH(p, paths->unit_path) {
                 int fd;
-		char *path, *should_free;
+                char *path, *should_free;
 
-		if (arg_root)
-			should_free = path = strappend(arg_root, *p);
-		else {
-			should_free = NULL;
-			path = *p;
-		}
+                if (arg_root)
+                        should_free = path = strappend(arg_root, *p);
+                else {
+                        should_free = NULL;
+                        path = *p;
+                }
 
                 if (!(filename = path_make_absolute(i->name, path))) {
                         log_error("Out of memory");
                         return -ENOMEM;
                 }
 
-		if (should_free)
-			free(should_free);
+                if (should_free)
+                        free(should_free);
 
                 /* Ensure that we don't follow symlinks */
                 if ((fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOCTTY)) >= 0)
@@ -4006,7 +4006,7 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
                                 tmp_path = strappend (arg_root, sysv);
                                 exists = access (tmp_path, F_OK) >= 0;
                                 free (tmp_path);
-			} else
+                        } else
                                 exists = access(sysv, F_OK) >= 0;
 
                         if (exists) {
@@ -4030,7 +4030,7 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
                                 if (arg_root)
                                         argv[3] = strappend("--root=", arg_root);
 
-	                        log_info("Executing %s %s %s %s", argv[0], argv[1], strempty(argv[2]), strempty(argv[3]));
+                                log_info("Executing %s %s %s %s", argv[0], argv[1], strempty(argv[2]), strempty(argv[3]));
 
                                 if ((pid = fork()) < 0) {
                                         log_error("Failed to fork: %m");

commit f437d5d203ff9498ac967c8153169fc79ab6b584
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jul 22 04:19:29 2011 +0200

    path-lookup: add both the configured and the fixed search paths in

diff --git a/src/path-lookup.c b/src/path-lookup.c
index 84a9859..e1925f1 100644
--- a/src/path-lookup.c
+++ b/src/path-lookup.c
@@ -53,6 +53,20 @@ int user_config_home(char **config_home) {
 }
 
 static char** user_dirs(void) {
+        const char * const config_unit_paths[] = {
+                "/run/systemd/user",
+                USER_CONFIG_UNIT_PATH,
+                "/etc/systemd/user"
+        };
+
+        const char * const data_unit_paths[] = {
+                "/usr/local/lib/systemd/user",
+                "/usr/local/share/systemd/user",
+                USER_DATA_UNIT_PATH,
+                "/usr/lib/systemd/user",
+                "/usr/share/systemd/user"
+        };
+
         const char *home, *e;
         char *config_home = NULL, *data_home = NULL;
         char **config_dirs = NULL, **data_dirs = NULL;
@@ -103,9 +117,7 @@ static char** user_dirs(void) {
                 data_dirs = strv_split(e, ":");
         else
                 data_dirs = strv_new("/usr/local/share",
-                                     "/usr/local/lib",
                                      "/usr/share",
-                                     "/usr/lib",
                                      NULL);
 
         if (!data_dirs)
@@ -119,12 +131,14 @@ static char** user_dirs(void) {
                 r = t;
         }
 
-        if (!(t = strv_merge_concat(r, config_dirs, "/systemd/user")))
-                goto finish;
-        strv_free(r);
-        r = t;
+        if (!strv_isempty(config_dirs)) {
+                if (!(t = strv_merge_concat(r, config_dirs, "/systemd/user")))
+                        goto finish;
+                strv_free(r);
+                r = t;
+        }
 
-        if (!(t = strv_append(r, USER_CONFIG_UNIT_PATH)))
+        if (!(t = strv_merge(r, (char**) config_unit_paths)))
                 goto fail;
         strv_free(r);
         r = t;
@@ -136,12 +150,14 @@ static char** user_dirs(void) {
                 r = t;
         }
 
-        if (!(t = strv_merge_concat(r, data_dirs, "/systemd/user")))
-                goto fail;
-        strv_free(r);
-        r = t;
+        if (!strv_isempty(data_dirs)) {
+                if (!(t = strv_merge_concat(r, data_dirs, "/systemd/user")))
+                        goto fail;
+                strv_free(r);
+                r = t;
+        }
 
-        if (!(t = strv_append(r, USER_DATA_UNIT_PATH)))
+        if (!(t = strv_merge(r, (char**) data_unit_paths)))
                 goto fail;
         strv_free(r);
         r = t;

commit c800e483748a8bb68ed405094c265954c7e605dc
Author: Lennart Poettering <lennart at poettering.net>
Date:   Fri Jul 22 04:17:38 2011 +0200

    path-lookup: make inclusion of user private units optional

diff --git a/src/manager.c b/src/manager.c
index 3291275..c1242ae 100644
--- a/src/manager.c
+++ b/src/manager.c
@@ -258,7 +258,7 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
         if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
                 goto fail;
 
-        if ((r = lookup_paths_init(&m->lookup_paths, m->running_as)) < 0)
+        if ((r = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
                 goto fail;
 
         if ((r = manager_setup_signals(m)) < 0)
@@ -2822,7 +2822,7 @@ int manager_reload(Manager *m) {
 
         /* Find new unit paths */
         lookup_paths_free(&m->lookup_paths);
-        if ((q = lookup_paths_init(&m->lookup_paths, m->running_as)) < 0)
+        if ((q = lookup_paths_init(&m->lookup_paths, m->running_as, true)) < 0)
                 r = q;
 
         manager_run_generators(m);
diff --git a/src/path-lookup.c b/src/path-lookup.c
index b45467c..84a9859 100644
--- a/src/path-lookup.c
+++ b/src/path-lookup.c
@@ -163,7 +163,7 @@ fail:
         goto finish;
 }
 
-int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as) {
+int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal) {
         const char *e;
         char *t;
 
@@ -181,8 +181,27 @@ int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as) {
                 strv_free(p->unit_path);
 
                 if (running_as == MANAGER_USER) {
-                        if (!(p->unit_path = user_dirs()))
+
+                        if (personal)
+                                p->unit_path = user_dirs();
+                        else
+                                p->unit_path = strv_new(
+                                                /* If you modify this you also want to modify
+                                                 * systemduserunitpath= in systemd.pc.in, and
+                                                 * the arrays in user_dirs() above! */
+                                                "/run/systemd/user",
+                                                USER_CONFIG_UNIT_PATH,
+                                                "/etc/systemd/system",
+                                                "/usr/local/lib/systemd/user",
+                                                "/usr/local/share/systemd/user",
+                                                USER_DATA_UNIT_PATH,
+                                                "/usr/lib/systemd/user",
+                                                "/usr/share/systemd/user",
+                                                NULL);
+
+                        if (!p->unit_path)
                                 return -ENOMEM;
+
                 } else
                         if (!(p->unit_path = strv_new(
                                               /* If you modify this you also want to modify
@@ -192,8 +211,8 @@ int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as) {
                                               "/etc/systemd/system",
                                               "/usr/local/lib/systemd/system",
                                               "/usr/lib/systemd/system",
-                                              "/lib/systemd/system",
                                               SYSTEM_DATA_UNIT_PATH,
+                                              "/lib/systemd/system",
                                               NULL)))
                                 return -ENOMEM;
         }
diff --git a/src/path-lookup.h b/src/path-lookup.h
index e06f979..fc2887d 100644
--- a/src/path-lookup.h
+++ b/src/path-lookup.h
@@ -34,7 +34,7 @@ typedef struct LookupPaths {
 
 int user_config_home(char **config_home);
 
-int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as);
+int lookup_paths_init(LookupPaths *p, ManagerRunningAs running_as, bool personal);
 void lookup_paths_free(LookupPaths *p);
 
 #endif
diff --git a/src/systemctl.c b/src/systemctl.c
index 3767e22..63eb324 100644
--- a/src/systemctl.c
+++ b/src/systemctl.c
@@ -4139,7 +4139,7 @@ static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
         dbus_error_init(&error);
 
         zero(paths);
-        if ((r = lookup_paths_init(&paths, arg_user ? MANAGER_USER : MANAGER_SYSTEM)) < 0) {
+        if ((r = lookup_paths_init(&paths, arg_user ? MANAGER_USER : MANAGER_SYSTEM, true)) < 0) {
                 log_error("Failed to determine lookup paths: %s", strerror(-r));
                 goto finish;
         }

commit d8eaa1144f7ea921ae9eb4bba1f6a7c1797c9435
Author: Lennart Poettering <lennart at poettering.net>
Date:   Mon Jul 18 22:30:02 2011 +0200

    update TODO

diff --git a/TODO b/TODO
index d8af5b1..e8af473 100644
--- a/TODO
+++ b/TODO
@@ -111,8 +111,6 @@ Features:
 
 * fix alsa mixer restore to not print error when no config is stored
 
-* fix upstart reboot compat call
-
 * when failing to start a service due to ratelimiting, try again later, if restart=always is set
 
 * write blog stories about:



More information about the systemd-commits mailing list