From 225690a604e73a7f90859370cc5201b599b0e823 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Wed, 10 May 2017 18:16:16 -0400 Subject: ./tools/notsd-move --- src/shared/install.c | 3041 -------------------------------------------------- 1 file changed, 3041 deletions(-) delete mode 100644 src/shared/install.c (limited to 'src/shared/install.c') diff --git a/src/shared/install.c b/src/shared/install.c deleted file mode 100644 index 96fba6e25b..0000000000 --- a/src/shared/install.c +++ /dev/null @@ -1,3041 +0,0 @@ -/*** - This file is part of systemd. - - Copyright 2011 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty . -***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "alloc-util.h" -#include "conf-files.h" -#include "conf-parser.h" -#include "dirent-util.h" -#include "extract-word.h" -#include "fd-util.h" -#include "fileio.h" -#include "fs-util.h" -#include "hashmap.h" -#include "install-printf.h" -#include "install.h" -#include "locale-util.h" -#include "log.h" -#include "macro.h" -#include "mkdir.h" -#include "path-lookup.h" -#include "path-util.h" -#include "rm-rf.h" -#include "set.h" -#include "special.h" -#include "stat-util.h" -#include "string-table.h" -#include "string-util.h" -#include "strv.h" -#include "unit-name.h" - -#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64 - -typedef enum SearchFlags { - SEARCH_LOAD = 1, - SEARCH_FOLLOW_CONFIG_SYMLINKS = 2, -} SearchFlags; - -typedef struct { - OrderedHashmap *will_process; - OrderedHashmap *have_processed; -} InstallContext; - -typedef enum { - PRESET_UNKNOWN, - PRESET_ENABLE, - PRESET_DISABLE, -} PresetAction; - -typedef struct { - char *pattern; - PresetAction action; -} PresetRule; - -typedef struct { - PresetRule *rules; - size_t n_rules; -} Presets; - -static inline void presets_freep(Presets *p) { - size_t i; - - if (!p) - return; - - for (i = 0; i < p->n_rules; i++) - free(p->rules[i].pattern); - - free(p->rules); - p->n_rules = 0; -} - -static int unit_file_lookup_state(UnitFileScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); - -bool unit_type_may_alias(UnitType type) { - return IN_SET(type, - UNIT_SERVICE, - UNIT_SOCKET, - UNIT_TARGET, - UNIT_DEVICE, - UNIT_TIMER, - UNIT_PATH); -} - -bool unit_type_may_template(UnitType type) { - return IN_SET(type, - UNIT_SERVICE, - UNIT_SOCKET, - UNIT_TARGET, - UNIT_TIMER, - UNIT_PATH); -} - -static const char *unit_file_type_table[_UNIT_FILE_TYPE_MAX] = { - [UNIT_FILE_TYPE_REGULAR] = "regular", - [UNIT_FILE_TYPE_SYMLINK] = "symlink", - [UNIT_FILE_TYPE_MASKED] = "masked", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType); - -static int in_search_path(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - char **i; - - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - STRV_FOREACH(i, p->search_path) - if (path_equal(parent, *i)) - return true; - - return false; -} - -static const char* skip_root(const LookupPaths *p, const char *path) { - char *e; - - assert(p); - assert(path); - - if (!p->root_dir) - return path; - - e = path_startswith(path, p->root_dir); - if (!e) - return NULL; - - /* Make sure the returned path starts with a slash */ - if (e[0] != '/') { - if (e == path || e[-1] != '/') - return NULL; - - e--; - } - - return e; -} - -static int path_is_generator(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - - assert(p); - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->generator) || - path_equal_ptr(parent, p->generator_early) || - path_equal_ptr(parent, p->generator_late); -} - -static int path_is_transient(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - - assert(p); - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->transient); -} - -static int path_is_control(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - - assert(p); - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->persistent_control) || - path_equal_ptr(parent, p->runtime_control); -} - -static int path_is_config(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - - assert(p); - assert(path); - - /* Note that we do *not* have generic checks for /etc or /run in place, since with - * them we couldn't discern configuration from transient or generated units */ - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->persistent_config) || - path_equal_ptr(parent, p->runtime_config); -} - -static int path_is_runtime(const LookupPaths *p, const char *path) { - _cleanup_free_ char *parent = NULL; - const char *rpath; - - assert(p); - assert(path); - - /* Everything in /run is considered runtime. On top of that we also add - * explicit checks for the various runtime directories, as safety net. */ - - rpath = skip_root(p, path); - if (rpath && path_startswith(rpath, "/run")) - return true; - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - return path_equal_ptr(parent, p->runtime_config) || - path_equal_ptr(parent, p->generator) || - path_equal_ptr(parent, p->generator_early) || - path_equal_ptr(parent, p->generator_late) || - path_equal_ptr(parent, p->transient) || - path_equal_ptr(parent, p->runtime_control); -} - -static int path_is_vendor(const LookupPaths *p, const char *path) { - const char *rpath; - - assert(p); - assert(path); - - rpath = skip_root(p, path); - if (!rpath) - return 0; - - if (path_startswith(rpath, "/usr")) - return true; - -#ifdef HAVE_SPLIT_USR - if (path_startswith(rpath, "/lib")) - return true; -#endif - - return path_equal(rpath, SYSTEM_DATA_UNIT_PATH); -} - -int unit_file_changes_add( - UnitFileChange **changes, - unsigned *n_changes, - UnitFileChangeType type, - const char *path, - const char *source) { - - _cleanup_free_ char *p = NULL, *s = NULL; - UnitFileChange *c; - - assert(path); - assert(!changes == !n_changes); - - if (!changes) - return 0; - - c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange)); - if (!c) - return -ENOMEM; - *changes = c; - - p = strdup(path); - if (source) - s = strdup(source); - - if (!p || (source && !s)) - return -ENOMEM; - - path_kill_slashes(p); - if (s) - path_kill_slashes(s); - - c[*n_changes] = (UnitFileChange) { type, p, s }; - p = s = NULL; - (*n_changes) ++; - return 0; -} - -void unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) { - unsigned i; - - assert(changes || n_changes == 0); - - for (i = 0; i < n_changes; i++) { - free(changes[i].path); - free(changes[i].source); - } - - free(changes); -} - -void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, unsigned n_changes, bool quiet) { - unsigned i; - bool logged = false; - - assert(changes || n_changes == 0); - /* If verb is not specified, errors are not allowed! */ - assert(verb || r >= 0); - - for (i = 0; i < n_changes; i++) { - assert(verb || changes[i].type >= 0); - - switch(changes[i].type) { - case UNIT_FILE_SYMLINK: - if (!quiet) - log_info("Created symlink %s %s %s.", - changes[i].path, - special_glyph(ARROW), - changes[i].source); - break; - case UNIT_FILE_UNLINK: - if (!quiet) - log_info("Removed %s.", changes[i].path); - break; - case UNIT_FILE_IS_MASKED: - if (!quiet) - log_info("Unit %s is masked, ignoring.", changes[i].path); - break; - case UNIT_FILE_IS_DANGLING: - if (!quiet) - log_info("Unit %s is an alias to a unit that is not present, ignoring.", - changes[i].path); - break; - case -EEXIST: - if (changes[i].source) - log_error_errno(changes[i].type, - "Failed to %s unit, file %s already exists and is a symlink to %s.", - verb, changes[i].path, changes[i].source); - else - log_error_errno(changes[i].type, - "Failed to %s unit, file %s already exists.", - verb, changes[i].path); - logged = true; - break; - case -ERFKILL: - log_error_errno(changes[i].type, "Failed to %s unit, unit %s is masked.", - verb, changes[i].path); - logged = true; - break; - case -EADDRNOTAVAIL: - log_error_errno(changes[i].type, "Failed to %s unit, unit %s is transient or generated.", - verb, changes[i].path); - logged = true; - break; - case -ELOOP: - log_error_errno(changes[i].type, "Failed to %s unit, refusing to operate on linked unit file %s", - verb, changes[i].path); - logged = true; - break; - default: - assert(changes[i].type < 0); - log_error_errno(changes[i].type, "Failed to %s unit, file %s: %m.", - verb, changes[i].path); - logged = true; - } - } - - if (r < 0 && !logged) - log_error_errno(r, "Failed to %s: %m.", verb); -} - -/** - * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem. - * wc should be the full path in the host file system. - */ -static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) { - assert(path_is_absolute(wd)); - - /* This will give incorrect results if the paths are relative and go outside - * of the chroot. False negatives are possible. */ - - if (!root) - root = "/"; - - a = strjoina(path_is_absolute(a) ? root : wd, "/", a); - b = strjoina(path_is_absolute(b) ? root : wd, "/", b); - return path_equal_or_files_same(a, b); -} - -static int create_symlink( - const LookupPaths *paths, - const char *old_path, - const char *new_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_free_ char *dest = NULL, *dirname = NULL; - const char *rp; - int r; - - assert(old_path); - assert(new_path); - - rp = skip_root(paths, old_path); - if (rp) - old_path = rp; - - /* Actually create a symlink, and remember that we did. Is - * smart enough to check if there's already a valid symlink in - * place. - * - * Returns 1 if a symlink was created or already exists and points to - * the right place, or negative on error. - */ - - mkdir_parents_label(new_path, 0755); - - if (symlink(old_path, new_path) >= 0) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - return 1; - } - - if (errno != EEXIST) { - unit_file_changes_add(changes, n_changes, -errno, new_path, NULL); - return -errno; - } - - r = readlink_malloc(new_path, &dest); - if (r < 0) { - /* translate EINVAL (non-symlink exists) to EEXIST */ - if (r == -EINVAL) - r = -EEXIST; - - unit_file_changes_add(changes, n_changes, r, new_path, NULL); - return r; - } - - dirname = dirname_malloc(new_path); - if (!dirname) - return -ENOMEM; - - if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path)) - return 1; - - if (!force) { - unit_file_changes_add(changes, n_changes, -EEXIST, new_path, dest); - return -EEXIST; - } - - r = symlink_atomic(old_path, new_path); - if (r < 0) { - unit_file_changes_add(changes, n_changes, r, new_path, NULL); - return r; - } - - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, new_path, NULL); - unit_file_changes_add(changes, n_changes, UNIT_FILE_SYMLINK, new_path, old_path); - - return 1; -} - -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_ops); - if (r < 0) - return r; - - n = strdup(p); - if (!n) - return -ENOMEM; - - path_kill_slashes(n); - - r = set_consume(*remove_symlinks_to, n); - if (r == -EEXIST) - return 0; - if (r < 0) - return r; - - return 1; -} - -static int remove_marked_symlinks_fd( - Set *remove_symlinks_to, - int fd, - const char *path, - const char *config_path, - const LookupPaths *lp, - bool dry_run, - bool *restart, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(remove_symlinks_to); - assert(fd >= 0); - assert(path); - assert(config_path); - assert(lp); - assert(restart); - - d = fdopendir(fd); - if (!d) { - safe_close(fd); - return -errno; - } - - rewinddir(d); - - FOREACH_DIRENT(de, d, return -errno) { - - dirent_ensure_type(d, de); - - if (de->d_type == DT_DIR) { - _cleanup_free_ char *p = NULL; - int nfd, q; - - 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) { - safe_close(nfd); - return -ENOMEM; - } - - /* This will close nfd, regardless whether it succeeds or not */ - q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, lp, dry_run, restart, changes, n_changes); - if (q < 0 && r == 0) - r = q; - - } else if (de->d_type == DT_LNK) { - _cleanup_free_ char *p = NULL, *dest = NULL; - const char *rp; - bool found; - int q; - - if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) - continue; - - p = path_make_absolute(de->d_name, path); - if (!p) - return -ENOMEM; - path_kill_slashes(p); - - q = readlink_malloc(p, &dest); - if (q == -ENOENT) - continue; - if (q < 0) { - if (r == 0) - r = q; - continue; - } - - /* We remove all links pointing to a file or path that is marked, as well as all files sharing - * the same name as a file that is marked. */ - - found = set_contains(remove_symlinks_to, dest) || - set_contains(remove_symlinks_to, basename(dest)) || - set_contains(remove_symlinks_to, de->d_name); - - if (!found) - continue; - - if (!dry_run) { - if (unlinkat(fd, de->d_name, 0) < 0 && errno != ENOENT) { - if (r == 0) - r = -errno; - unit_file_changes_add(changes, n_changes, -errno, p, NULL); - continue; - } - - (void) rmdir_parents(p, config_path); - } - - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, p, NULL); - - /* Now, remember the full path (but with the root prefix removed) of - * the symlink we just removed, and remove any symlinks to it, too. */ - - rp = skip_root(lp, p); - q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p); - if (q < 0) - return q; - if (q > 0 && !dry_run) - *restart = true; - } - } - - return r; -} - -static int remove_marked_symlinks( - Set *remove_symlinks_to, - const char *config_path, - const LookupPaths *lp, - bool dry_run, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_close_ int fd = -1; - bool restart; - int r = 0; - - assert(config_path); - assert(lp); - - if (set_size(remove_symlinks_to) <= 0) - return 0; - - fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC); - if (fd < 0) - return errno == ENOENT ? 0 : -errno; - - do { - int q, cfd; - restart = false; - - cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3); - if (cfd < 0) - return -errno; - - /* This takes possession of cfd and closes it */ - q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, lp, dry_run, &restart, changes, n_changes); - if (r == 0) - r = q; - } while (restart); - - return r; -} - -static int find_symlinks_fd( - const char *root_dir, - const char *name, - int fd, - const char *path, - const char *config_path, - const LookupPaths *lp, - bool *same_name_link) { - - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - int r = 0; - - assert(name); - assert(fd >= 0); - assert(path); - assert(config_path); - assert(lp); - assert(same_name_link); - - d = fdopendir(fd); - if (!d) { - safe_close(fd); - return -errno; - } - - FOREACH_DIRENT(de, d, return -errno) { - - dirent_ensure_type(d, de); - - if (de->d_type == DT_DIR) { - _cleanup_free_ char *p = NULL; - int nfd, q; - - 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) { - safe_close(nfd); - return -ENOMEM; - } - - /* This will close nfd, regardless whether it succeeds or not */ - q = find_symlinks_fd(root_dir, name, nfd, p, config_path, lp, same_name_link); - if (q > 0) - return 1; - if (r == 0) - r = q; - - } else if (de->d_type == DT_LNK) { - _cleanup_free_ char *p = NULL, *dest = NULL; - bool found_path, found_dest, b = false; - int q; - - /* Acquire symlink name */ - p = path_make_absolute(de->d_name, path); - if (!p) - return -ENOMEM; - - /* Acquire symlink destination */ - q = readlink_malloc(p, &dest); - if (q == -ENOENT) - continue; - if (q < 0) { - if (r == 0) - r = q; - continue; - } - - /* Make absolute */ - if (!path_is_absolute(dest)) { - char *x; - - x = prefix_root(root_dir, dest); - if (!x) - return -ENOMEM; - - free(dest); - dest = x; - } - - /* 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(basename(dest), name); - - if (found_path && found_dest) { - _cleanup_free_ char *t = NULL; - - /* Filter out same name links in the main - * config path */ - t = path_make_absolute(name, config_path); - if (!t) - return -ENOMEM; - - b = path_equal(t, p); - } - - if (b) - *same_name_link = true; - else if (found_path || found_dest) - return 1; - } - } - - return r; -} - -static int find_symlinks( - const char *root_dir, - const char *name, - const char *config_path, - const LookupPaths *lp, - 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); - if (fd < 0) { - if (IN_SET(errno, ENOENT, ENOTDIR, EACCES)) - return 0; - return -errno; - } - - /* This takes possession of fd and closes it */ - return find_symlinks_fd(root_dir, name, fd, config_path, config_path, lp, same_name_link); -} - -static int find_symlinks_in_scope( - UnitFileScope scope, - const LookupPaths *paths, - const char *name, - UnitFileState *state) { - - bool same_name_link_runtime = false, same_name_link = false; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(paths); - assert(name); - - /* First look in the persistent config path */ - r = find_symlinks(paths->root_dir, name, paths->persistent_config, paths, &same_name_link); - if (r < 0) - return r; - if (r > 0) { - *state = UNIT_FILE_ENABLED; - return r; - } - - /* Then look in runtime config path */ - r = find_symlinks(paths->root_dir, name, paths->runtime_config, paths, &same_name_link_runtime); - if (r < 0) - return r; - if (r > 0) { - *state = UNIT_FILE_ENABLED_RUNTIME; - return r; - } - - /* Hmm, we didn't find it, but maybe we found the same name - * link? */ - if (same_name_link) { - *state = UNIT_FILE_LINKED; - return 1; - } - if (same_name_link_runtime) { - *state = UNIT_FILE_LINKED_RUNTIME; - return 1; - } - - return 0; -} - -static void install_info_free(UnitFileInstallInfo *i) { - - if (!i) - return; - - free(i->name); - free(i->path); - strv_free(i->aliases); - strv_free(i->wanted_by); - strv_free(i->required_by); - strv_free(i->also); - free(i->default_instance); - free(i->symlink_target); - free(i); -} - -static OrderedHashmap* install_info_hashmap_free(OrderedHashmap *m) { - UnitFileInstallInfo *i; - - if (!m) - return NULL; - - while ((i = ordered_hashmap_steal_first(m))) - install_info_free(i); - - return ordered_hashmap_free(m); -} - -static void install_context_done(InstallContext *c) { - assert(c); - - c->will_process = install_info_hashmap_free(c->will_process); - c->have_processed = install_info_hashmap_free(c->have_processed); -} - -static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) { - UnitFileInstallInfo *i; - - i = ordered_hashmap_get(c->have_processed, name); - if (i) - return i; - - return ordered_hashmap_get(c->will_process, name); -} - -static int install_info_may_process( - UnitFileInstallInfo *i, - const LookupPaths *paths, - UnitFileChange **changes, - unsigned *n_changes) { - assert(i); - assert(paths); - - /* Checks whether the loaded unit file is one we should process, or is masked, - * transient or generated and thus not subject to enable/disable operations. */ - - if (i->type == UNIT_FILE_TYPE_MASKED) { - unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL); - return -ERFKILL; - } - if (path_is_generator(paths, i->path) || - path_is_transient(paths, i->path)) { - unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL); - return -EADDRNOTAVAIL; - } - - return 0; -} - -/** - * Adds a new UnitFileInstallInfo entry under name in the InstallContext.will_process - * hashmap, or retrieves the existing one if already present. - * - * Returns negative on error, 0 if the unit was already known, 1 otherwise. - */ -static int install_info_add( - InstallContext *c, - const char *name, - const char *path, - bool auxiliary, - UnitFileInstallInfo **ret) { - - UnitFileInstallInfo *i = NULL; - int r; - - assert(c); - assert(name || path); - - if (!name) - name = basename(path); - - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; - - i = install_info_find(c, name); - if (i) { - i->auxiliary = i->auxiliary && auxiliary; - - if (ret) - *ret = i; - return 0; - } - - r = ordered_hashmap_ensure_allocated(&c->will_process, &string_hash_ops); - if (r < 0) - return r; - - i = new0(UnitFileInstallInfo, 1); - if (!i) - return -ENOMEM; - i->type = _UNIT_FILE_TYPE_INVALID; - i->auxiliary = auxiliary; - - 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 = ordered_hashmap_put(c->will_process, i->name, i); - if (r < 0) - goto fail; - - if (ret) - *ret = i; - - return 1; - -fail: - install_info_free(i); - return r; -} - -static int config_parse_alias( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - const char *name; - UnitType type; - - assert(filename); - assert(lvalue); - assert(rvalue); - - name = basename(filename); - type = unit_name_to_type(name); - if (!unit_type_may_alias(type)) - return log_syntax(unit, LOG_WARNING, filename, line, 0, - "Alias= is not allowed for %s units, ignoring.", - unit_type_to_string(type)); - - return config_parse_strv(unit, filename, line, section, section_line, - lvalue, ltype, rvalue, data, userdata); -} - -static int config_parse_also( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - UnitFileInstallInfo *info = userdata, *alsoinfo = NULL; - InstallContext *c = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - for (;;) { - _cleanup_free_ char *word = NULL, *printed = NULL; - - r = extract_first_word(&rvalue, &word, NULL, 0); - if (r < 0) - return r; - if (r == 0) - break; - - r = install_full_printf(info, word, &printed); - if (r < 0) - return r; - - if (!unit_name_is_valid(printed, UNIT_NAME_ANY)) - return -EINVAL; - - r = install_info_add(c, printed, NULL, true, &alsoinfo); - if (r < 0) - return r; - - r = strv_push(&info->also, printed); - if (r < 0) - return r; - - printed = NULL; - } - - return 0; -} - -static int config_parse_default_instance( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - UnitFileInstallInfo *i = data; - const char *name; - _cleanup_free_ char *printed = NULL; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - - name = basename(filename); - if (unit_name_is_valid(name, UNIT_NAME_INSTANCE)) - /* When enabling an instance, we might be using a template unit file, - * but we should ignore DefaultInstance silently. */ - return 0; - if (!unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) - return log_syntax(unit, LOG_WARNING, filename, line, 0, - "DefaultInstance= only makes sense for template units, ignoring."); - - r = install_full_printf(i, rvalue, &printed); - if (r < 0) - return r; - - if (!unit_instance_is_valid(printed)) - return -EINVAL; - - return free_and_replace(i->default_instance, printed); -} - -static int unit_file_load( - InstallContext *c, - UnitFileInstallInfo *info, - const char *path, - SearchFlags flags) { - - const ConfigTableItem items[] = { - { "Install", "Alias", config_parse_alias, 0, &info->aliases }, - { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, - { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, - { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, - { "Install", "Also", config_parse_also, 0, c }, - {} - }; - - const char *name; - UnitType type; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_close_ int fd = -1; - struct stat st; - int r; - - assert(info); - assert(path); - - name = basename(path); - type = unit_name_to_type(name); - if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && - !unit_type_may_template(type)) - return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type)); - - if (!(flags & SEARCH_LOAD)) { - r = lstat(path, &st); - if (r < 0) - return -errno; - - if (null_or_empty(&st)) - info->type = UNIT_FILE_TYPE_MASKED; - else if (S_ISREG(st.st_mode)) - info->type = UNIT_FILE_TYPE_REGULAR; - else if (S_ISLNK(st.st_mode)) - return -ELOOP; - else if (S_ISDIR(st.st_mode)) - return -EISDIR; - else - return -ENOTTY; - - return 0; - } - - /* c is only needed if we actually load the file */ - assert(c); - - fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW); - if (fd < 0) - return -errno; - if (fstat(fd, &st) < 0) - return -errno; - if (null_or_empty(&st)) { - info->type = UNIT_FILE_TYPE_MASKED; - return 0; - } - if (S_ISDIR(st.st_mode)) - return -EISDIR; - if (!S_ISREG(st.st_mode)) - return -ENOTTY; - - f = fdopen(fd, "re"); - if (!f) - return -errno; - fd = -1; - - r = config_parse(NULL, path, f, - NULL, - config_item_table_lookup, items, - true, true, false, info); - if (r < 0) - return log_debug_errno(r, "Failed to parse %s: %m", info->name); - - info->type = UNIT_FILE_TYPE_REGULAR; - - return - (int) strv_length(info->aliases) + - (int) strv_length(info->wanted_by) + - (int) strv_length(info->required_by); -} - -static int unit_file_load_or_readlink( - InstallContext *c, - UnitFileInstallInfo *info, - const char *path, - const char *root_dir, - SearchFlags flags) { - - _cleanup_free_ char *target = NULL; - int r; - - r = unit_file_load(c, info, path, flags); - if (r != -ELOOP) - return r; - - /* This is a symlink, let's read it. */ - - r = readlink_malloc(path, &target); - if (r < 0) - return r; - - if (path_equal(target, "/dev/null")) - info->type = UNIT_FILE_TYPE_MASKED; - else { - const char *bn; - UnitType a, b; - - bn = basename(target); - - if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) { - - if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN)) - return -EINVAL; - - } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { - - if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) - return -EINVAL; - - } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) { - - if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) - return -EINVAL; - } else - return -EINVAL; - - /* Enforce that the symlink destination does not - * change the unit file type. */ - - a = unit_name_to_type(info->name); - b = unit_name_to_type(bn); - if (a < 0 || b < 0 || a != b) - return -EINVAL; - - if (path_is_absolute(target)) - /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */ - info->symlink_target = prefix_root(root_dir, target); - else - /* This is a relative path, take it relative to the dir the symlink is located in. */ - info->symlink_target = file_in_same_dir(path, target); - if (!info->symlink_target) - return -ENOMEM; - - info->type = UNIT_FILE_TYPE_SYMLINK; - } - - return 0; -} - -static int unit_file_search( - InstallContext *c, - UnitFileInstallInfo *info, - const LookupPaths *paths, - SearchFlags flags) { - - _cleanup_free_ char *template = NULL; - char **p; - int r; - - assert(info); - assert(paths); - - /* Was this unit already loaded? */ - if (info->type != _UNIT_FILE_TYPE_INVALID) - return 0; - - if (info->path) - return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags); - - assert(info->name); - - STRV_FOREACH(p, paths->search_path) { - _cleanup_free_ char *path = NULL; - - path = strjoin(*p, "/", info->name, NULL); - if (!path) - return -ENOMEM; - - r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); - if (r >= 0) { - info->path = path; - path = NULL; - return r; - } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES)) - return r; - } - - if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { - /* Unit file doesn't exist, however instance - * enablement was requested. We will check if it is - * possible to load template unit file. */ - - r = unit_name_template(info->name, &template); - if (r < 0) - return r; - - STRV_FOREACH(p, paths->search_path) { - _cleanup_free_ char *path = NULL; - - path = strjoin(*p, "/", template, NULL); - if (!path) - return -ENOMEM; - - r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); - if (r >= 0) { - info->path = path; - path = NULL; - return r; - } else if (!IN_SET(r, -ENOENT, -ENOTDIR, -EACCES)) - return r; - } - } - - log_debug("Cannot find unit %s%s%s.", info->name, template ? " or " : "", strempty(template)); - return -ENOENT; -} - -static int install_info_follow( - InstallContext *c, - UnitFileInstallInfo *i, - const char *root_dir, - SearchFlags flags) { - - assert(c); - assert(i); - - if (i->type != UNIT_FILE_TYPE_SYMLINK) - return -EINVAL; - if (!i->symlink_target) - return -EINVAL; - - /* If the basename doesn't match, the caller should add a - * complete new entry for this. */ - - if (!streq(basename(i->symlink_target), i->name)) - return -EXDEV; - - free_and_replace(i->path, i->symlink_target); - i->type = _UNIT_FILE_TYPE_INVALID; - - return unit_file_load_or_readlink(c, i, i->path, root_dir, flags); -} - -/** - * Search for the unit file. If the unit name is a symlink, follow the symlink to the - * target, maybe more than once. Propagate the instance name if present. - */ -static int install_info_traverse( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, - UnitFileInstallInfo *start, - SearchFlags flags, - UnitFileInstallInfo **ret) { - - UnitFileInstallInfo *i; - unsigned k = 0; - int r; - - assert(paths); - assert(start); - assert(c); - - r = unit_file_search(c, start, paths, flags); - if (r < 0) - return r; - - i = start; - while (i->type == UNIT_FILE_TYPE_SYMLINK) { - /* Follow the symlink */ - - if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) - return -ELOOP; - - if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) { - r = path_is_config(paths, i->path); - if (r < 0) - return r; - if (r > 0) - return -ELOOP; - } - - r = install_info_follow(c, i, paths->root_dir, flags); - if (r == -EXDEV) { - _cleanup_free_ char *buffer = NULL; - const char *bn; - - /* Target has a different name, create a new - * install info object for that, and continue - * with that. */ - - bn = basename(i->symlink_target); - - if (unit_name_is_valid(i->name, UNIT_NAME_INSTANCE) && - unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) { - - _cleanup_free_ char *instance = NULL; - - r = unit_name_to_instance(i->name, &instance); - if (r < 0) - return r; - - r = unit_name_replace_instance(bn, instance, &buffer); - if (r < 0) - return r; - - bn = buffer; - } - - r = install_info_add(c, bn, NULL, false, &i); - if (r < 0) - return r; - - /* Try again, with the new target we found. */ - r = unit_file_search(c, i, paths, flags); - if (r == -ENOENT) - /* Translate error code to highlight this specific case */ - return -ENOLINK; - } - - if (r < 0) - return r; - } - - if (ret) - *ret = i; - - return 0; -} - -/** - * Call install_info_add() with name_or_path as the path (if name_or_path starts with "/") - * or the name (otherwise). root_dir is prepended to the path. - */ -static int install_info_add_auto( - InstallContext *c, - const LookupPaths *paths, - const char *name_or_path, - UnitFileInstallInfo **ret) { - - assert(c); - assert(name_or_path); - - if (path_is_absolute(name_or_path)) { - const char *pp; - - pp = prefix_roota(paths->root_dir, name_or_path); - - return install_info_add(c, NULL, pp, false, ret); - } else - return install_info_add(c, name_or_path, NULL, false, ret); -} - -static int install_info_discover( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, - const char *name, - SearchFlags flags, - UnitFileInstallInfo **ret, - UnitFileChange **changes, - unsigned *n_changes) { - - UnitFileInstallInfo *i; - int r; - - assert(c); - assert(paths); - assert(name); - - r = install_info_add_auto(c, paths, name, &i); - if (r >= 0) - r = install_info_traverse(scope, c, paths, i, flags, ret); - - if (r < 0) - unit_file_changes_add(changes, n_changes, r, name, NULL); - return r; -} - -static int install_info_symlink_alias( - UnitFileInstallInfo *i, - const LookupPaths *paths, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - char **s; - int r = 0, q; - - assert(i); - assert(paths); - assert(config_path); - - STRV_FOREACH(s, i->aliases) { - _cleanup_free_ char *alias_path = NULL, *dst = NULL; - - q = install_full_printf(i, *s, &dst); - if (q < 0) - return q; - - alias_path = path_make_absolute(dst, config_path); - if (!alias_path) - return -ENOMEM; - - q = create_symlink(paths, i->path, alias_path, force, changes, n_changes); - if (r == 0) - r = q; - } - - return r; -} - -static int install_info_symlink_wants( - UnitFileInstallInfo *i, - const LookupPaths *paths, - const char *config_path, - char **list, - const char *suffix, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_free_ char *buf = NULL; - const char *n; - char **s; - int r = 0, q; - - assert(i); - assert(paths); - assert(config_path); - - if (strv_isempty(list)) - return 0; - - if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE)) { - UnitFileInstallInfo instance = { - .type = _UNIT_FILE_TYPE_INVALID, - }; - _cleanup_free_ char *path = NULL; - - /* Don't install any symlink if there's no default - * instance configured */ - - if (!i->default_instance) - return 0; - - r = unit_name_replace_instance(i->name, i->default_instance, &buf); - if (r < 0) - return r; - - instance.name = buf; - r = unit_file_search(NULL, &instance, paths, SEARCH_FOLLOW_CONFIG_SYMLINKS); - if (r < 0) - return r; - - path = instance.path; - instance.path = NULL; - - if (instance.type == UNIT_FILE_TYPE_MASKED) { - unit_file_changes_add(changes, n_changes, -ERFKILL, path, NULL); - return -ERFKILL; - } - - n = buf; - } else - n = i->name; - - STRV_FOREACH(s, list) { - _cleanup_free_ char *path = NULL, *dst = NULL; - - q = install_full_printf(i, *s, &dst); - if (q < 0) - return q; - - if (!unit_name_is_valid(dst, UNIT_NAME_ANY)) { - r = -EINVAL; - continue; - } - - path = strjoin(config_path, "/", dst, suffix, n, NULL); - if (!path) - return -ENOMEM; - - q = create_symlink(paths, i->path, path, true, changes, n_changes); - if (r == 0) - r = q; - } - - return r; -} - -static int install_info_symlink_link( - UnitFileInstallInfo *i, - const LookupPaths *paths, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_free_ char *path = NULL; - int r; - - assert(i); - assert(paths); - assert(config_path); - assert(i->path); - - r = in_search_path(paths, i->path); - if (r < 0) - return r; - if (r > 0) - return 0; - - path = strjoin(config_path, "/", i->name, NULL); - if (!path) - return -ENOMEM; - - return create_symlink(paths, i->path, path, force, changes, n_changes); -} - -static int install_info_apply( - UnitFileInstallInfo *i, - const LookupPaths *paths, - const char *config_path, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - int r, q; - - assert(i); - assert(paths); - assert(config_path); - - if (i->type != UNIT_FILE_TYPE_REGULAR) - return 0; - - r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes); - - q = install_info_symlink_wants(i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes); - if (r == 0) - r = q; - - q = install_info_symlink_wants(i, paths, config_path, i->required_by, ".requires/", changes, n_changes); - if (r == 0) - r = q; - - q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); - /* Do not count links to the unit file towards the "carries_install_info" count */ - if (r == 0 && q < 0) - r = q; - - return r; -} - -static int install_context_apply( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, - const char *config_path, - bool force, - SearchFlags flags, - UnitFileChange **changes, - unsigned *n_changes) { - - UnitFileInstallInfo *i; - int r; - - assert(c); - assert(paths); - assert(config_path); - - if (ordered_hashmap_isempty(c->will_process)) - return 0; - - r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); - if (r < 0) - return r; - - r = 0; - while ((i = ordered_hashmap_first(c->will_process))) { - int q; - - q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); - if (q < 0) - return q; - - r = install_info_traverse(scope, c, paths, i, flags, NULL); - if (r < 0) { - unit_file_changes_add(changes, n_changes, r, i->name, NULL); - return r; - } - - /* We can attempt to process a masked unit when a different unit - * that we were processing specifies it in Also=. */ - if (i->type == UNIT_FILE_TYPE_MASKED) { - unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_MASKED, i->path, NULL); - if (r >= 0) - /* Assume that something *could* have been enabled here, - * avoid "empty [Install] section" warning. */ - r += 1; - continue; - } - - if (i->type != UNIT_FILE_TYPE_REGULAR) - continue; - - q = install_info_apply(i, paths, config_path, force, changes, n_changes); - if (r >= 0) { - if (q < 0) - r = q; - else - r += q; - } - } - - return r; -} - -static int install_context_mark_for_removal( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, - Set **remove_symlinks_to, - const char *config_path) { - - UnitFileInstallInfo *i; - int r; - - assert(c); - assert(paths); - assert(config_path); - - /* Marks all items for removal */ - - if (ordered_hashmap_isempty(c->will_process)) - return 0; - - r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); - if (r < 0) - return r; - - while ((i = ordered_hashmap_first(c->will_process))) { - - r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); - if (r < 0) - return r; - - r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); - if (r == -ENOLINK) { - log_debug_errno(r, "Name %s leads to a dangling symlink, ignoring.", i->name); - continue; - } else if (r == -ENOENT && i->auxiliary) { - /* some unit specified in Also= or similar is missing */ - log_debug_errno(r, "Auxiliary unit %s not found, ignoring.", i->name); - continue; - } else if (r < 0) - return log_debug_errno(r, "Failed to find unit %s: %m", i->name); - - if (i->type != UNIT_FILE_TYPE_REGULAR) { - log_debug("Unit %s has type %s, ignoring.", - i->name, - unit_file_type_to_string(i->type) ?: "invalid"); - continue; - } - - r = mark_symlink_for_removal(remove_symlinks_to, i->name); - if (r < 0) - return r; - } - - return 0; -} - -int unit_file_mask( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - const char *config_path; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - int q; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) { - if (r == 0) - r = -EINVAL; - continue; - } - - path = path_make_absolute(*i, config_path); - if (!path) - return -ENOMEM; - - q = create_symlink(&paths, "/dev/null", path, !!(flags & UNIT_FILE_FORCE), changes, n_changes); - if (q < 0 && r >= 0) - r = q; - } - - return r; -} - -int unit_file_unmask( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - _cleanup_free_ char **todo = NULL; - size_t n_todo = 0, n_allocated = 0; - const char *config_path; - char **i; - bool dry_run; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; - dry_run = !!(flags & UNIT_FILE_DRY_RUN); - - STRV_FOREACH(i, files) { - _cleanup_free_ char *path = NULL; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) - return -EINVAL; - - path = path_make_absolute(*i, config_path); - if (!path) - return -ENOMEM; - - r = null_or_empty_path(path); - if (r == -ENOENT) - continue; - if (r < 0) - return r; - if (r == 0) - continue; - - if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) - return -ENOMEM; - - todo[n_todo++] = *i; - } - - strv_uniq(todo); - - r = 0; - STRV_FOREACH(i, todo) { - _cleanup_free_ char *path = NULL; - const char *rp; - - path = path_make_absolute(*i, config_path); - if (!path) - return -ENOMEM; - - if (!dry_run && unlink(path) < 0) { - if (errno != ENOENT) { - if (r >= 0) - r = -errno; - unit_file_changes_add(changes, n_changes, -errno, path, NULL); - } - - continue; - } - - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - - rp = skip_root(&paths, path); - q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path); - if (q < 0) - return q; - } - - q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes); - if (r >= 0) - r = q; - - return r; -} - -int unit_file_link( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_free_ char **todo = NULL; - size_t n_todo = 0, n_allocated = 0; - const char *config_path; - char **i; - int r, q; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; - - STRV_FOREACH(i, files) { - _cleanup_free_ char *full = NULL; - struct stat st; - char *fn; - - if (!path_is_absolute(*i)) - return -EINVAL; - - fn = basename(*i); - if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) - return -EINVAL; - - full = prefix_root(paths.root_dir, *i); - if (!full) - return -ENOMEM; - - if (lstat(full, &st) < 0) - return -errno; - if (S_ISLNK(st.st_mode)) - return -ELOOP; - if (S_ISDIR(st.st_mode)) - return -EISDIR; - if (!S_ISREG(st.st_mode)) - return -ENOTTY; - - q = in_search_path(&paths, *i); - if (q < 0) - return q; - if (q > 0) - continue; - - if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) - return -ENOMEM; - - todo[n_todo++] = *i; - } - - strv_uniq(todo); - - r = 0; - STRV_FOREACH(i, todo) { - _cleanup_free_ char *new_path = NULL; - - new_path = path_make_absolute(basename(*i), config_path); - if (!new_path) - return -ENOMEM; - - q = create_symlink(&paths, *i, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes); - if (q < 0 && r >= 0) - r = q; - } - - return r; -} - -static int path_shall_revert(const LookupPaths *paths, const char *path) { - int r; - - assert(paths); - assert(path); - - /* Checks whether the path is one where the drop-in directories shall be removed. */ - - r = path_is_config(paths, path); - if (r != 0) - return r; - - r = path_is_control(paths, path); - if (r != 0) - return r; - - return path_is_transient(paths, path); -} - -int unit_file_revert( - UnitFileScope scope, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_strv_free_ char **todo = NULL; - size_t n_todo = 0, n_allocated = 0; - char **i; - int r, q; - - /* Puts a unit file back into vendor state. This means: - * - * a) we remove all drop-in snippets added by the user ("config"), add to transient units ("transient"), and - * added via "systemctl set-property" ("control"), but not if the drop-in is generated ("generated"). - * - * c) if there's a vendor unit file (i.e. one in /usr) we remove any configured overriding unit files (i.e. in - * "config", but not in "transient" or "control" or even "generated"). - * - * We remove all that in both the runtime and the persistent directories, if that applies. - */ - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - bool has_vendor = false; - char **p; - - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) - return -EINVAL; - - STRV_FOREACH(p, paths.search_path) { - _cleanup_free_ char *path = NULL, *dropin = NULL; - struct stat st; - - path = path_make_absolute(*i, *p); - if (!path) - return -ENOMEM; - - r = lstat(path, &st); - if (r < 0) { - if (errno != ENOENT) - return -errno; - } else if (S_ISREG(st.st_mode)) { - /* Check if there's a vendor version */ - r = path_is_vendor(&paths, path); - if (r < 0) - return r; - if (r > 0) - has_vendor = true; - } - - dropin = strappend(path, ".d"); - if (!dropin) - return -ENOMEM; - - r = lstat(dropin, &st); - if (r < 0) { - if (errno != ENOENT) - return -errno; - } else if (S_ISDIR(st.st_mode)) { - /* Remove the drop-ins */ - r = path_shall_revert(&paths, dropin); - if (r < 0) - return r; - if (r > 0) { - if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) - return -ENOMEM; - - todo[n_todo++] = dropin; - dropin = NULL; - } - } - } - - if (!has_vendor) - continue; - - /* OK, there's a vendor version, hence drop all configuration versions */ - STRV_FOREACH(p, paths.search_path) { - _cleanup_free_ char *path = NULL; - struct stat st; - - path = path_make_absolute(*i, *p); - if (!path) - return -ENOMEM; - - r = lstat(path, &st); - if (r < 0) { - if (errno != ENOENT) - return -errno; - } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { - r = path_is_config(&paths, path); - if (r < 0) - return r; - if (r > 0) { - if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) - return -ENOMEM; - - todo[n_todo++] = path; - path = NULL; - } - } - } - } - - strv_uniq(todo); - - r = 0; - STRV_FOREACH(i, todo) { - _cleanup_strv_free_ char **fs = NULL; - const char *rp; - char **j; - - (void) get_files_in_directory(*i, &fs); - - q = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL); - if (q < 0 && q != -ENOENT && r >= 0) { - r = q; - continue; - } - - STRV_FOREACH(j, fs) { - _cleanup_free_ char *t = NULL; - - t = strjoin(*i, "/", *j, NULL); - if (!t) - return -ENOMEM; - - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, t, NULL); - } - - unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL); - - rp = skip_root(&paths, *i); - q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i); - if (q < 0) - return q; - } - - q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, false, changes, n_changes); - if (r >= 0) - r = q; - - q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, false, changes, n_changes); - if (r >= 0) - r = q; - - return r; -} - -int unit_file_add_dependency( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - char **files, - const char *target, - UnitDependency dep, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i, *target_info; - const char *config_path; - char **f; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(target); - - if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES)) - return -EINVAL; - - if (!unit_name_is_valid(target, UNIT_NAME_ANY)) - return -EINVAL; - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; - - r = install_info_discover(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &target_info, changes, n_changes); - if (r < 0) - return r; - r = install_info_may_process(target_info, &paths, changes, n_changes); - if (r < 0) - return r; - - assert(target_info->type == UNIT_FILE_TYPE_REGULAR); - - STRV_FOREACH(f, files) { - char ***l; - - r = install_info_discover(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); - if (r < 0) - return r; - r = install_info_may_process(i, &paths, changes, n_changes); - if (r < 0) - return r; - - assert(i->type == UNIT_FILE_TYPE_REGULAR); - - /* We didn't actually load anything from the unit - * file, but instead just add in our new symlink to - * create. */ - - if (dep == UNIT_WANTS) - l = &i->wanted_by; - else - l = &i->required_by; - - strv_free(*l); - *l = strv_new(target_info->name, NULL); - if (!*l) - return -ENOMEM; - } - - return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); -} - -int unit_file_enable( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - const char *config_path; - UnitFileInstallInfo *i; - char **f; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; - - STRV_FOREACH(f, files) { - r = install_info_discover(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); - if (r < 0) - return r; - r = install_info_may_process(i, &paths, changes, n_changes); - if (r < 0) - return r; - - assert(i->type == UNIT_FILE_TYPE_REGULAR); - } - - /* This will return the number of symlink rules that were - supposed to be created, not the ones actually created. This - is useful to determine whether the passed files had any - installation data at all. */ - - return install_context_apply(scope, &c, &paths, config_path, !!(flags & UNIT_FILE_FORCE), SEARCH_LOAD, changes, n_changes); -} - -int unit_file_disable( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - const char *config_path; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; - - STRV_FOREACH(i, files) { - if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) - return -EINVAL; - - r = install_info_add(&c, *i, NULL, false, NULL); - if (r < 0) - return r; - } - - r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path); - if (r < 0) - return r; - - return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes); -} - -int unit_file_reenable( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - char **files, - UnitFileChange **changes, - unsigned *n_changes) { - - char **n; - int r; - size_t l, i; - - /* First, we invoke the disable command with only the basename... */ - l = strv_length(files); - n = newa(char*, l+1); - for (i = 0; i < l; i++) - n[i] = basename(files[i]); - n[i] = NULL; - - r = unit_file_disable(scope, flags, root_dir, n, changes, n_changes); - if (r < 0) - return r; - - /* But the enable command with the full name */ - return unit_file_enable(scope, flags, root_dir, files, changes, n_changes); -} - -int unit_file_set_default( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - const char *name, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; - const char *new_path; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */ - return -EINVAL; - if (streq(name, SPECIAL_DEFAULT_TARGET)) - return -EINVAL; - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - r = install_info_discover(scope, &c, &paths, name, 0, &i, changes, n_changes); - if (r < 0) - return r; - r = install_info_may_process(i, &paths, changes, n_changes); - if (r < 0) - return r; - - new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); - return create_symlink(&paths, i->path, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes); -} - -int unit_file_get_default( - UnitFileScope scope, - const char *root_dir, - char **name) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; - char *n; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, NULL, NULL); - if (r < 0) - return r; - r = install_info_may_process(i, &paths, NULL, 0); - if (r < 0) - return r; - - n = strdup(i->name); - if (!n) - return -ENOMEM; - - *name = n; - return 0; -} - -static int unit_file_lookup_state( - UnitFileScope scope, - const LookupPaths *paths, - const char *name, - UnitFileState *ret) { - - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; - UnitFileState state; - int r; - - assert(paths); - assert(name); - - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; - - r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, NULL, NULL); - if (r < 0) - return r; - - /* Shortcut things, if the caller just wants to know if this unit exists. */ - if (!ret) - return 0; - - switch (i->type) { - - case UNIT_FILE_TYPE_MASKED: - r = path_is_runtime(paths, i->path); - if (r < 0) - return r; - - state = r > 0 ? UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED; - break; - - case UNIT_FILE_TYPE_REGULAR: - r = path_is_generator(paths, i->path); - if (r < 0) - return r; - if (r > 0) { - state = UNIT_FILE_GENERATED; - break; - } - - r = path_is_transient(paths, i->path); - if (r < 0) - return r; - if (r > 0) { - state = UNIT_FILE_TRANSIENT; - break; - } - - r = find_symlinks_in_scope(scope, paths, i->name, &state); - if (r < 0) - return r; - if (r == 0) { - if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i)) - state = UNIT_FILE_DISABLED; - else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i)) - state = UNIT_FILE_INDIRECT; - else - state = UNIT_FILE_STATIC; - } - - break; - - default: - assert_not_reached("Unexpect unit file type."); - } - - *ret = state; - return 0; -} - -int unit_file_get_state( - UnitFileScope scope, - const char *root_dir, - const char *name, - UnitFileState *ret) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(name); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - return unit_file_lookup_state(scope, &paths, name, ret); -} - -int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) { - _cleanup_(install_context_done) InstallContext c = {}; - int r; - - assert(paths); - assert(name); - - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; - - r = install_info_discover(scope, &c, paths, name, 0, NULL, NULL, NULL); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - return 1; -} - -static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) { - _cleanup_(presets_freep) Presets ps = {}; - size_t n_allocated = 0; - _cleanup_strv_free_ char **files = NULL; - char **p; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(presets); - - if (scope == UNIT_FILE_SYSTEM) - r = conf_files_list(&files, ".preset", root_dir, - "/etc/systemd/system-preset", - "/usr/local/lib/systemd/system-preset", - "/usr/lib/systemd/system-preset", -#ifdef HAVE_SPLIT_USR - "/lib/systemd/system-preset", -#endif - NULL); - else if (scope == UNIT_FILE_GLOBAL) - r = conf_files_list(&files, ".preset", root_dir, - "/etc/systemd/user-preset", - "/usr/local/lib/systemd/user-preset", - "/usr/lib/systemd/user-preset", - NULL); - else { - *presets = (Presets){}; - - return 0; - } - - if (r < 0) - return r; - - STRV_FOREACH(p, files) { - _cleanup_fclose_ FILE *f; - char line[LINE_MAX]; - int n = 0; - - f = fopen(*p, "re"); - if (!f) { - if (errno == ENOENT) - continue; - - return -errno; - } - - FOREACH_LINE(line, f, return -errno) { - PresetRule rule = {}; - const char *parameter; - char *l; - - l = strstrip(line); - n++; - - if (isempty(l)) - continue; - if (strchr(COMMENTS, *l)) - continue; - - parameter = first_word(l, "enable"); - if (parameter) { - char *pattern; - - pattern = strdup(parameter); - if (!pattern) - return -ENOMEM; - - rule = (PresetRule) { - .pattern = pattern, - .action = PRESET_ENABLE, - }; - } - - parameter = first_word(l, "disable"); - if (parameter) { - char *pattern; - - pattern = strdup(parameter); - if (!pattern) - return -ENOMEM; - - rule = (PresetRule) { - .pattern = pattern, - .action = PRESET_DISABLE, - }; - } - - if (rule.action) { - if (!GREEDY_REALLOC(ps.rules, n_allocated, ps.n_rules + 1)) - return -ENOMEM; - - ps.rules[ps.n_rules++] = rule; - continue; - } - - log_syntax(NULL, LOG_WARNING, *p, n, 0, "Couldn't parse line '%s'. Ignoring.", line); - } - } - - *presets = ps; - ps = (Presets){}; - - return 0; -} - -static int query_presets(const char *name, const Presets presets) { - PresetAction action = PRESET_UNKNOWN; - size_t i; - - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; - - for (i = 0; i < presets.n_rules; i++) - if (fnmatch(presets.rules[i].pattern, name, FNM_NOESCAPE) == 0) { - action = presets.rules[i].action; - break; - } - - switch (action) { - case PRESET_UNKNOWN: - log_debug("Preset files don't specify rule for %s. Enabling.", name); - return 1; - case PRESET_ENABLE: - log_debug("Preset files say enable %s.", name); - return 1; - case PRESET_DISABLE: - log_debug("Preset files say disable %s.", name); - return 0; - default: - assert_not_reached("invalid preset action"); - } -} - -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) { - _cleanup_(presets_freep) Presets presets = {}; - int r; - - r = read_presets(scope, root_dir, &presets); - if (r < 0) - return r; - - return query_presets(name, presets); -} - -static int execute_preset( - UnitFileScope scope, - InstallContext *plus, - InstallContext *minus, - const LookupPaths *paths, - const char *config_path, - char **files, - UnitFilePresetMode mode, - bool force, - UnitFileChange **changes, - unsigned *n_changes) { - - int r; - - assert(plus); - assert(minus); - assert(paths); - assert(config_path); - - if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - - r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path); - if (r < 0) - return r; - - r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes); - } else - r = 0; - - if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) { - int q; - - /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(scope, plus, paths, config_path, force, SEARCH_LOAD, changes, n_changes); - if (r >= 0) { - if (q < 0) - r = q; - else - r += q; - } - } - - return r; -} - -static int preset_prepare_one( - UnitFileScope scope, - InstallContext *plus, - InstallContext *minus, - LookupPaths *paths, - const char *name, - Presets presets, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_(install_context_done) InstallContext tmp = {}; - UnitFileInstallInfo *i; - int r; - - if (install_info_find(plus, name) || install_info_find(minus, name)) - return 0; - - r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); - if (r < 0) - return r; - if (!streq(name, i->name)) { - log_debug("Skipping %s because is an alias for %s", name, i->name); - return 0; - } - - r = query_presets(name, presets); - if (r < 0) - return r; - - if (r > 0) { - r = install_info_discover(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); - if (r < 0) - return r; - - r = install_info_may_process(i, paths, changes, n_changes); - if (r < 0) - return r; - } else - r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); - - return r; -} - -int unit_file_preset( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - char **files, - UnitFilePresetMode mode, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(presets_freep) Presets presets = {}; - const char *config_path; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(mode < _UNIT_FILE_PRESET_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; - - r = read_presets(scope, root_dir, &presets); - if (r < 0) - return r; - - STRV_FOREACH(i, files) { - r = preset_prepare_one(scope, &plus, &minus, &paths, *i, presets, changes, n_changes); - if (r < 0) - return r; - } - - return execute_preset(scope, &plus, &minus, &paths, config_path, files, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes); -} - -int unit_file_preset_all( - UnitFileScope scope, - UnitFileFlags flags, - const char *root_dir, - UnitFilePresetMode mode, - UnitFileChange **changes, - unsigned *n_changes) { - - _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - _cleanup_(presets_freep) Presets presets = {}; - const char *config_path = NULL; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(mode < _UNIT_FILE_PRESET_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; - - r = read_presets(scope, root_dir, &presets); - if (r < 0) - return r; - - STRV_FOREACH(i, paths.search_path) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir(*i); - if (!d) { - if (errno == ENOENT) - continue; - - return -errno; - } - - FOREACH_DIRENT(de, d, return -errno) { - - if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) - continue; - - dirent_ensure_type(d, de); - - if (!IN_SET(de->d_type, DT_LNK, DT_REG)) - continue; - - /* we don't pass changes[] in, because we want to handle errors on our own */ - r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, presets, NULL, 0); - if (r == -ERFKILL) - r = unit_file_changes_add(changes, n_changes, - UNIT_FILE_IS_MASKED, de->d_name, NULL); - else if (r == -ENOLINK) - r = unit_file_changes_add(changes, n_changes, - UNIT_FILE_IS_DANGLING, de->d_name, NULL); - if (r < 0) - return r; - } - } - - return execute_preset(scope, &plus, &minus, &paths, config_path, NULL, mode, !!(flags & UNIT_FILE_FORCE), changes, n_changes); -} - -static void unit_file_list_free_one(UnitFileList *f) { - if (!f) - return; - - free(f->path); - free(f); -} - -Hashmap* unit_file_list_free(Hashmap *h) { - UnitFileList *i; - - while ((i = hashmap_steal_first(h))) - unit_file_list_free_one(i); - - return hashmap_free(h); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one); - -int unit_file_get_list( - UnitFileScope scope, - const char *root_dir, - Hashmap *h, - char **states, - char **patterns) { - - _cleanup_lookup_paths_free_ LookupPaths paths = {}; - char **i; - int r; - - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - assert(h); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - STRV_FOREACH(i, paths.search_path) { - _cleanup_closedir_ DIR *d = NULL; - struct dirent *de; - - d = opendir(*i); - if (!d) { - if (errno == ENOENT) - continue; - if (IN_SET(errno, ENOTDIR, EACCES)) { - log_debug("Failed to open \"%s\": %m", *i); - continue; - } - - return -errno; - } - - FOREACH_DIRENT(de, d, return -errno) { - _cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL; - - if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) - continue; - - if (!strv_fnmatch_or_empty(patterns, de->d_name, FNM_NOESCAPE)) - continue; - - if (hashmap_get(h, de->d_name)) - continue; - - dirent_ensure_type(d, de); - - if (!IN_SET(de->d_type, DT_LNK, DT_REG)) - continue; - - f = new0(UnitFileList, 1); - if (!f) - return -ENOMEM; - - f->path = path_make_absolute(de->d_name, *i); - if (!f->path) - return -ENOMEM; - - r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state); - if (r < 0) - f->state = UNIT_FILE_BAD; - - if (!strv_isempty(states) && - !strv_contains(states, unit_file_state_to_string(f->state))) - continue; - - r = hashmap_put(h, basename(f->path), f); - if (r < 0) - return r; - - f = NULL; /* prevent cleanup */ - } - } - - return 0; -} - -static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = { - [UNIT_FILE_ENABLED] = "enabled", - [UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime", - [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", - [UNIT_FILE_INDIRECT] = "indirect", - [UNIT_FILE_GENERATED] = "generated", - [UNIT_FILE_TRANSIENT] = "transient", - [UNIT_FILE_BAD] = "bad", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_file_state, UnitFileState); - -static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = { - [UNIT_FILE_SYMLINK] = "symlink", - [UNIT_FILE_UNLINK] = "unlink", - [UNIT_FILE_IS_MASKED] = "masked", - [UNIT_FILE_IS_DANGLING] = "dangling", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_file_change_type, UnitFileChangeType); - -static const char* const unit_file_preset_mode_table[_UNIT_FILE_PRESET_MAX] = { - [UNIT_FILE_PRESET_FULL] = "full", - [UNIT_FILE_PRESET_ENABLE_ONLY] = "enable-only", - [UNIT_FILE_PRESET_DISABLE_ONLY] = "disable-only", -}; - -DEFINE_STRING_TABLE_LOOKUP(unit_file_preset_mode, UnitFilePresetMode); -- cgit v1.2.3-54-g00ecf