chiark / gitweb /
install: when looking for a unit file for enabling, search for templates only after...
[elogind.git] / src / shared / install.c
index 40dc7bebe8b6a438fe0fbb59dc422186223d13cf..b300be8680d53425d667b73c82a57d0c39c6a5c4 100644 (file)
@@ -287,30 +287,31 @@ static int remove_marked_symlinks_fd(
                                 set_get(remove_symlinks_to, dest) ||
                                 set_get(remove_symlinks_to, basename(dest));
 
-                        if (found) {
+                        if (!found)
+                                continue;
 
-                                if (unlink(p) < 0 && errno != ENOENT) {
 
-                                        if (r == 0)
-                                                r = -errno;
-                                        continue;
-                                }
+                        if (unlink(p) < 0 && errno != ENOENT) {
+
+                                if (r == 0)
+                                        r = -errno;
+                                continue;
+                        }
 
-                                rmdir_parents(p, config_path);
+                        rmdir_parents(p, config_path);
 
-                                path_kill_slashes(p);
+                        path_kill_slashes(p);
 
-                                add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
+                        add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
 
-                                if (!set_get(remove_symlinks_to, p)) {
+                        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;
-                                }
+                                q = mark_symlink_for_removal(&remove_symlinks_to, p);
+                                if (q < 0) {
+                                        if (r == 0)
+                                                r = q;
+                                } else
+                                        *deleted = true;
                         }
                 }
         }
@@ -417,10 +418,8 @@ static int find_symlinks_fd(
 
                         /* 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;
 
@@ -595,7 +594,6 @@ int unit_file_mask(
 
                 if (symlink("/dev/null", path) >= 0) {
                         add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
-
                         continue;
                 }
 
@@ -605,13 +603,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;
                                 }
                         }
@@ -762,7 +756,6 @@ int unit_file_link(
                         _cleanup_free_ char *dest = NULL;
 
                         q = readlink_and_make_absolute(path, &dest);
-
                         if (q < 0 && errno != ENOENT) {
                                 if (r == 0)
                                         r = q;
@@ -773,13 +766,9 @@ int unit_file_link(
                                 continue;
 
                         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);
-
                                         continue;
                                 }
                         }
@@ -1046,67 +1035,69 @@ static int unit_file_search(
         assert(paths);
 
         if (info->path) {
-                char *full_path = NULL;
+                const char *path;
 
-                if (!isempty(root_dir))
-                        full_path = strappenda(root_dir, info->path);
+                if (isempty(root_dir))
+                        path = info->path;
+                else
+                        path = strappenda(root_dir, info->path);
 
-                return unit_file_load(c, info, full_path ?: info->path, allow_symlink);
+                return unit_file_load(c, info, path, allow_symlink);
         }
 
         assert(info->name);
 
         STRV_FOREACH(p, paths->unit_path) {
-                _cleanup_free_ char *path = NULL, *full_path = NULL;
+                _cleanup_free_ char *path = NULL;
 
-                path = strjoin(*p, "/", info->name, NULL);
+                if (isempty(root_dir))
+                        path = strjoin(*p, "/", info->name, NULL);
+                else
+                        path = strjoin(root_dir, "/", *p, "/", info->name, NULL);
                 if (!path)
                         return -ENOMEM;
 
-                if (!isempty(root_dir)) {
-                        full_path = strappend(root_dir, path);
-                        if (!full_path)
-                                return -ENOMEM;
-                }
-
-                r = unit_file_load(c, info, full_path ?: path, allow_symlink);
+                r = unit_file_load(c, info, path, allow_symlink);
                 if (r >= 0) {
                         info->path = path;
                         path = NULL;
-                } else if (r == -ENOENT && unit_name_is_instance(info->name)) {
-                        /* Unit file doesn't exist, however instance enablement was requested.
-                         * We will check if it is possible to load template unit file. */
-                        _cleanup_free_ char *template = NULL, *template_dir = NULL;
+                        return r;
+                }
+                if (r != -ENOENT && r != -ELOOP)
+                        return r;
+        }
 
-                        template = unit_name_template(info->name);
-                        if (!template)
-                                return -ENOMEM;
+        if (unit_name_is_instance(info->name)) {
+
+                /* Unit file doesn't exist, however instance
+                 * enablement was requested.  We will check if it is
+                 * possible to load template unit file. */
+
+                _cleanup_free_ char *template = NULL, *template_dir = NULL;
+
+                template = unit_name_template(info->name);
+                if (!template)
+                        return -ENOMEM;
 
-                        /* We will reuse path variable since we don't need it anymore. */
-                        template_dir = path;
-                        *(strrchr(template_dir, '/') + 1) = '\0';
+                STRV_FOREACH(p, paths->unit_path) {
+                        _cleanup_free_ char *path = NULL;
 
-                        path = strappend(template_dir, template);
+                        if (isempty(root_dir))
+                                path = strjoin(*p, "/", template, NULL);
+                        else
+                                path = strjoin(root_dir, "/", *p, "/", template, NULL);
                         if (!path)
                                 return -ENOMEM;
 
-                        if (!isempty(root_dir)) {
-                                free(full_path);
-                                full_path = strappend(root_dir, path);
-                                if (!full_path)
-                                        return -ENOMEM;
-                        }
-
-                        /* Let's try to load template unit. */
-                        r = unit_file_load(c, info, full_path ?: path, allow_symlink);
+                        r = unit_file_load(c, info, path, allow_symlink);
                         if (r >= 0) {
                                 info->path = path;
                                 path = NULL;
+                                return r;
                         }
+                        if (r != -ENOENT && r != -ELOOP)
+                                return r;
                 }
-
-                if (r != -ENOENT && r != -ELOOP)
-                        return r;
         }
 
         return -ENOENT;
@@ -1175,17 +1166,14 @@ static int create_symlink(
         if (!force)
                 return -EEXIST;
 
-        r = unlink(new_path);
-        if (r < 0 && errno != ENOENT)
-                return -errno;
+        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(
@@ -1302,8 +1290,8 @@ static int install_info_symlink_link(
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
-        int r;
         _cleanup_free_ char *path = NULL;
+        int r;
 
         assert(i);
         assert(paths);
@@ -1314,11 +1302,11 @@ 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);
-        return r;
+        return create_symlink(i->path, path, force, changes, n_changes);
 }
 
 static int install_info_apply(
@@ -1749,7 +1737,7 @@ UnitFileState unit_file_get_state(
         return r < 0 ? r : state;
 }
 
-int unit_file_query_preset(UnitFileScope scope, const char *name) {
+int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
         _cleanup_strv_free_ char **files = NULL;
         char **i;
         int r;
@@ -1759,7 +1747,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
         assert(name);
 
         if (scope == UNIT_FILE_SYSTEM)
-                r = conf_files_list(&files, ".preset", NULL,
+                r = conf_files_list(&files, ".preset", root_dir,
                                     "/etc/systemd/system-preset",
                                     "/usr/local/lib/systemd/system-preset",
                                     "/usr/lib/systemd/system-preset",
@@ -1768,7 +1756,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", NULL,
+                r = conf_files_list(&files, ".preset", root_dir,
                                     "/etc/systemd/user-preset",
                                     "/usr/local/lib/systemd/user-preset",
                                     "/usr/lib/systemd/user-preset",
@@ -1780,9 +1768,16 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
                 return r;
 
         STRV_FOREACH(i, files) {
+                _cleanup_free_ char *buf = NULL;
                 _cleanup_fclose_ FILE *f;
+                const char *p;
 
-                f = fopen(*i, "re");
+                if (root_dir)
+                        p = buf = strjoin(root_dir, "/", *i, NULL);
+                else
+                        p = *i;
+
+                f = fopen(p, "re");
                 if (!f) {
                         if (errno == ENOENT)
                                 continue;
@@ -1807,15 +1802,19 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
                                 l += 6;
                                 l += strspn(l, WHITESPACE);
 
-                                if (fnmatch(l, name, FNM_NOESCAPE) == 0)
+                                if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
+                                        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)
+                                if (fnmatch(l, name, FNM_NOESCAPE) == 0) {
+                                        log_debug("Preset file says disable %s.", name);
                                         return 0;
+                                }
 
                         } else
                                 log_debug("Couldn't parse line '%s'", l);
@@ -1823,6 +1822,7 @@ int unit_file_query_preset(UnitFileScope scope, const char *name) {
         }
 
         /* Default is "enable" */
+        log_debug("Preset file doesn't say anything about %s, enabling.", name);
         return 1;
 }
 
@@ -1831,12 +1831,12 @@ int unit_file_preset(
                 bool runtime,
                 const char *root_dir,
                 char **files,
+                UnitFilePresetMode mode,
                 bool force,
                 UnitFileChange **changes,
                 unsigned *n_changes) {
 
         _cleanup_install_context_done_ InstallContext plus = {}, minus = {};
-        _cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
         _cleanup_lookup_paths_free_ LookupPaths paths = {};
         _cleanup_free_ char *config_path = NULL;
         char **i;
@@ -1844,6 +1844,7 @@ int unit_file_preset(
 
         assert(scope >= 0);
         assert(scope < _UNIT_FILE_SCOPE_MAX);
+        assert(mode < _UNIT_FILE_PRESET_MODE_MAX);
 
         r = lookup_paths_init_from_scope(&paths, scope, root_dir);
         if (r < 0)
@@ -1858,29 +1859,145 @@ int unit_file_preset(
                 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)
                         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)
                         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);
+
+                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_MODE_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 *buf = NULL;
+                const char *units_dir;
+
+                if (!isempty(root_dir)) {
+                        buf = strjoin(root_dir, "/", *i, NULL);
+                        if (!buf)
+                                return -ENOMEM;
+
+                        units_dir = buf;
+                } else
+                        units_dir = *i;
+
+                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;
 }
@@ -1937,8 +2054,8 @@ int unit_file_get_list(
                 }
 
                 for (;;) {
-                        struct dirent *de;
                         _cleanup_unitfilelist_free_ UnitFileList *f = NULL;
+                        struct dirent *de;
 
                         errno = 0;
                         de = readdir(d);
@@ -2031,3 +2148,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_MODE_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);