install.c revision 0ffce503cd6e5a5ff5ba5cd1cc23684cfb8bb9e3
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann/***
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann This file is part of systemd.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Copyright 2011 Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann systemd is free software; you can redistribute it and/or modify it
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann under the terms of the GNU Lesser General Public License as published by
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann the Free Software Foundation; either version 2.1 of the License, or
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann (at your option) any later version.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann systemd is distributed in the hope that it will be useful, but
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann WITHOUT ANY WARRANTY; without even the implied warranty <of
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Lesser General Public License for more details.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann You should have received a copy of the GNU Lesser General Public License
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann along with systemd; If not, see <http://www.gnu.org/licenses/>.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann***/
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <errno.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <fcntl.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <unistd.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <string.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include <fnmatch.h>
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "util.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "mkdir.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "hashmap.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "set.h"
0a6f50c0afdfc434b492493bd9efab20cbee8623Thomas Hindoe Paaboel Andersen#include "path-util.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "path-lookup.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "strv.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "unit-name.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "install.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "conf-parser.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "conf-files.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "specifier.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "install-printf.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann#include "special.h"
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmanntypedef struct {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann OrderedHashmap *will_install;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann OrderedHashmap *have_installed;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann} InstallContext;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int in_search_path(const char *path, char **search) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *parent = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = path_get_parent(path, &parent);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return strv_contains(search, parent);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int lookup_paths_init_from_scope(LookupPaths *paths,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileScope scope,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *root_dir) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(paths);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope >= 0);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope < _UNIT_FILE_SCOPE_MAX);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering zero(*paths);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return lookup_paths_init(paths,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann scope == UNIT_FILE_USER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann root_dir,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann NULL, NULL, NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char *p = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope >= 0);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope < _UNIT_FILE_SCOPE_MAX);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(ret);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
e23f4bb525991c5908be0d0e7f8374c964d9996cDavid Herrmann switch (scope) {
e23f4bb525991c5908be0d0e7f8374c964d9996cDavid Herrmann
e23f4bb525991c5908be0d0e7f8374c964d9996cDavid Herrmann case UNIT_FILE_SYSTEM:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
e23f4bb525991c5908be0d0e7f8374c964d9996cDavid Herrmann if (runtime)
e23f4bb525991c5908be0d0e7f8374c964d9996cDavid Herrmann p = path_join(root_dir, "/run/systemd/system", NULL);
e23f4bb525991c5908be0d0e7f8374c964d9996cDavid Herrmann else
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann p = path_join(root_dir, SYSTEM_CONFIG_UNIT_PATH, NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case UNIT_FILE_GLOBAL:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (root_dir)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (runtime)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann p = strdup("/run/systemd/user");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann p = strdup(USER_CONFIG_UNIT_PATH);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann case UNIT_FILE_USER:
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (root_dir)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (runtime)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = user_runtime_dir(&p);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = user_config_home(&p);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r <= 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r < 0 ? r : -ENOENT;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann default:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert_not_reached("Bad scope");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering if (!p)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *ret = p;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int add_file_change(
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileChange **changes,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann unsigned *n_changes,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileChangeType type,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *path,
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann const char *source) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileChange *c;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann unsigned i;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(!changes == !n_changes);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!changes)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!c)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *changes = c;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann i = *n_changes;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann c[i].type = type;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann c[i].path = strdup(path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!c[i].path)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann path_kill_slashes(c[i].path);
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (source) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann c[i].source = strdup(source);
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering if (!c[i].source) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(c[i].path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann path_kill_slashes(c[i].path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } else
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann c[i].source = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *n_changes = i+1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int mark_symlink_for_removal(
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Set **remove_symlinks_to,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *p) {
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char *n;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(p);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann n = strdup(p);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!n)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann path_kill_slashes(n);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = set_consume(*remove_symlinks_to, n);
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r == -EEXIST ? 0 : r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int remove_marked_symlinks_fd(
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering Set *remove_symlinks_to,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int fd,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *path,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *config_path,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool *deleted,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileChange **changes,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann unsigned *n_changes,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char** instance_whitelist) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_closedir_ DIR *d = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r = 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(remove_symlinks_to);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(fd >= 0);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(deleted);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann d = fdopendir(fd);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!d) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann safe_close(fd);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann rewinddir(d);
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann for (;;) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct dirent *de;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann errno = 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann de = readdir(d);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!de && errno != 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!de)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (ignore_file(de->d_name))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann dirent_ensure_type(d, de);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (de->d_type == DT_DIR) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int nfd, q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *p = NULL;
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (nfd < 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (errno == ENOENT)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann r = -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann p = path_make_absolute(de->d_name, path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!p) {
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann safe_close(nfd);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* This will close nfd, regardless whether it succeeds or not */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, deleted, changes, n_changes, instance_whitelist);
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann if (q < 0 && r == 0)
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann r = q;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann } else if (de->d_type == DT_LNK) {
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann _cleanup_free_ char *p = NULL, *dest = NULL;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann int q;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann bool found;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (unit_name_is_instance(de->d_name) &&
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering instance_whitelist &&
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann !strv_contains(instance_whitelist, de->d_name)) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *w;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* OK, the file is not listed directly
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * in the whitelist, so let's check if
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * the template of it might be
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * listed. */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann w = unit_name_template(de->d_name);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!w)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!strv_contains(instance_whitelist, w))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann p = path_make_absolute(de->d_name, path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!p)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = readlink_and_canonicalize(p, &dest);
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann if (q < 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q == -ENOENT)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann if (r == 0)
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann r = q;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann found =
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann set_get(remove_symlinks_to, dest) ||
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann set_get(remove_symlinks_to, basename(dest));
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann if (!found)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann if (unlink(p) < 0 && errno != ENOENT) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -errno;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann path_kill_slashes(p);
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann rmdir_parents(p, config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!set_get(remove_symlinks_to, p)) {
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering q = mark_symlink_for_removal(&remove_symlinks_to, p);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q < 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } else
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *deleted = true;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering}
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poetteringstatic int remove_marked_symlinks(
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Set *remove_symlinks_to,
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering const char *config_path,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileChange **changes,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann unsigned *n_changes,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char** instance_whitelist) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_close_ int fd = -1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r = 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool deleted;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (set_size(remove_symlinks_to) <= 0)
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering return 0;
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (fd < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann do {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int q, cfd;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann deleted = false;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann cfd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (cfd < 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* This takes possession of cfd and closes it */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &deleted, changes, n_changes, instance_whitelist);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering r = q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } while (deleted);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int find_symlinks_fd(
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *name,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int fd,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *path,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *config_path,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool *same_name_link) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r = 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_closedir_ DIR *d = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(name);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(fd >= 0);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(path);
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann assert(config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(same_name_link);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann d = fdopendir(fd);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!d) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann safe_close(fd);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann for (;;) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct dirent *de;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann errno = 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann de = readdir(d);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!de && errno != 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!de)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (ignore_file(de->d_name))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann dirent_ensure_type(d, de);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (de->d_type == DT_DIR) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int nfd, q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *p = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (nfd < 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (errno == ENOENT)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann p = path_make_absolute(de->d_name, path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!p) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann safe_close(nfd);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* This will close nfd, regardless whether it succeeds or not */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = find_symlinks_fd(name, nfd, p, config_path, same_name_link);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q > 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = q;
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } else if (de->d_type == DT_LNK) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *p = NULL, *dest = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool found_path, found_dest, b = false;
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann int q;
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Acquire symlink name */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann p = path_make_absolute(de->d_name, path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!p)
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann return -ENOMEM;
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann /* Acquire symlink destination */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = readlink_and_canonicalize(p, &dest);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q < 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q == -ENOENT)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Check if the symlink itself matches what we
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * are looking for */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (path_is_absolute(name))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann found_path = path_equal(p, name);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann found_path = streq(de->d_name, name);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Check if what the symlink points to
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * matches what we are looking for */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (path_is_absolute(name))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann found_dest = path_equal(dest, name);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann found_dest = streq(basename(dest), name);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (found_path && found_dest) {
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann _cleanup_free_ char *t = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Filter out same name links in the main
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * config path */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann t = path_make_absolute(name, config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!t)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann b = path_equal(t, p);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann if (b)
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann *same_name_link = true;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else if (found_path || found_dest)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann }
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann}
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmannstatic int find_symlinks(
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann const char *name,
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann const char *config_path,
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann bool *same_name_link) {
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann int fd;
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann assert(name);
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann assert(config_path);
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann assert(same_name_link);
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann if (fd < 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (errno == ENOENT)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* This takes possession of fd and closes it */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return find_symlinks_fd(name, fd, config_path, config_path, same_name_link);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int find_symlinks_in_scope(
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileScope scope,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *root_dir,
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann const char *name,
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann UnitFileState *state) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *path = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool same_name_link_runtime = false, same_name_link = false;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope >= 0);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope < _UNIT_FILE_SCOPE_MAX);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(name);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* First look in runtime config path */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = get_config_path(scope, true, root_dir, &path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann r = find_symlinks(name, path, &same_name_link_runtime);
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann if (r < 0)
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann return r;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann else if (r > 0) {
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann *state = UNIT_FILE_ENABLED_RUNTIME;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann return r;
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann }
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann /* Then look in the normal config path */
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann r = get_config_path(scope, false, root_dir, &path);
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann if (r < 0)
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = find_symlinks(name, path, &same_name_link);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else if (r > 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *state = UNIT_FILE_ENABLED;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Hmm, we didn't find it, but maybe we found the same name
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * link? */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (same_name_link_runtime) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *state = UNIT_FILE_LINKED_RUNTIME;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } else if (same_name_link) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann *state = UNIT_FILE_LINKED;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 1;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return 0;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint unit_file_mask(
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileScope scope,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool runtime,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *root_dir,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char **files,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool force,
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering UnitFileChange **changes,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann unsigned *n_changes) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char **i;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *prefix = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering assert(scope >= 0);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope < _UNIT_FILE_SCOPE_MAX);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = get_config_path(scope, runtime, root_dir, &prefix);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann STRV_FOREACH(i, files) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *path = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann path = path_make_absolute(*i, prefix);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!path) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (symlink("/dev/null", path) >= 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (errno == EEXIST) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (null_or_empty_path(path) > 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (force) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (symlink_atomic("/dev/null", path) >= 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -EEXIST;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } else {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint unit_file_unmask(
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileScope scope,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool runtime,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *root_dir,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char **files,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileChange **changes,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann unsigned *n_changes) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char **i, *config_path = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r, q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Set *remove_symlinks_to = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope >= 0);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope < _UNIT_FILE_SCOPE_MAX);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = get_config_path(scope, runtime, root_dir, &config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann goto finish;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann STRV_FOREACH(i, files) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char *path;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering r = -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann path = path_make_absolute(*i, config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!path) {
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering r = -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann break;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = null_or_empty_path(path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q > 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (unlink(path) >= 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann mark_symlink_for_removal(&remove_symlinks_to, path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering if (q != -ENOENT && r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(path);
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poetteringfinish:
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann set_free_free(remove_symlinks_to);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannint unit_file_link(
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileScope scope,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool runtime,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *root_dir,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char **files,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann bool force,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileChange **changes,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann unsigned *n_changes) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_lookup_paths_free_ LookupPaths paths = {};
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char **i;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *config_path = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann int r, q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope >= 0);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(scope < _UNIT_FILE_SCOPE_MAX);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = lookup_paths_init_from_scope(&paths, scope, root_dir);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = get_config_path(scope, runtime, root_dir, &config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann STRV_FOREACH(i, files) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *path = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann char *fn;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann struct stat st;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann fn = basename(*i);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!path_is_absolute(*i) ||
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann !unit_name_is_valid(fn, TEMPLATE_VALID)) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -EINVAL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (lstat(*i, &st) < 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!S_ISREG(st.st_mode)) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -ENOENT;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = in_search_path(*i, paths.unit_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q < 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q > 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering path = path_make_absolute(fn, config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!path)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return -ENOMEM;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (symlink(*i, path) >= 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (errno == EEXIST) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_free_ char *dest = NULL;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = readlink_and_make_absolute(path, &dest);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q < 0 && errno != ENOENT) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = q;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (q >= 0 && path_equal(dest, *i))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (force) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (symlink_atomic(*i, path) >= 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann continue;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -EEXIST;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann } else {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = -errno;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering return r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannvoid unit_file_list_free(Hashmap *h) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann UnitFileList *i;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann while ((i = hashmap_steal_first(h))) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(i->path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(i);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann hashmap_free(h);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann}
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannvoid unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann unsigned i;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann assert(changes || n_changes == 0);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!changes)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann for (i = 0; i < n_changes; i++) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(changes[i].path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann free(changes[i].source);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann }
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann
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);
strv_free(i->required_by);
free(i->default_instance);
free(i);
}
static void install_info_hashmap_free(OrderedHashmap *m) {
InstallInfo *i;
if (!m)
return;
while ((i = ordered_hashmap_steal_first(m)))
install_info_free(i);
ordered_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 = basename(path);
if (!unit_name_is_valid(name, TEMPLATE_VALID))
return -EINVAL;
if (ordered_hashmap_get(c->have_installed, name) ||
ordered_hashmap_get(c->will_install, name))
return 0;
r = ordered_hashmap_ensure_allocated(&c->will_install, &string_hash_ops);
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 = ordered_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 *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
size_t l;
const char *word, *state;
InstallContext *c = data;
assert(filename);
assert(lvalue);
assert(rvalue);
FOREACH_WORD_QUOTED(word, l, rvalue, state) {
_cleanup_free_ char *n;
int r;
n = strndup(word, l);
if (!n)
return -ENOMEM;
r = install_info_add(c, n, NULL);
if (r < 0)
return r;
}
if (!isempty(state))
log_syntax(unit, LOG_ERR, filename, line, EINVAL,
"Trailing garbage, ignoring.");
return 0;
}
static int config_parse_user(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
InstallInfo *i = data;
char *printed;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
r = install_full_printf(i, rvalue, &printed);
if (r < 0)
return r;
free(i->user);
i->user = printed;
return 0;
}
static int config_parse_default_instance(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
InstallInfo *i = data;
char *printed;
int r;
assert(filename);
assert(lvalue);
assert(rvalue);
r = install_full_printf(i, rvalue, &printed);
if (r < 0)
return r;
if (!unit_instance_is_valid(printed)) {
free(printed);
return -EINVAL;
}
free(i->default_instance);
i->default_instance = printed;
return 0;
}
static int unit_file_load(
InstallContext *c,
InstallInfo *info,
const char *path,
const char *root_dir,
bool allow_symlink,
bool load) {
const ConfigTableItem items[] = {
{ "Install", "Alias", config_parse_strv, 0, &info->aliases },
{ "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by },
{ "Install", "RequiredBy", config_parse_strv, 0, &info->required_by },
{ "Install", "DefaultInstance", config_parse_default_instance, 0, info },
{ "Install", "Also", config_parse_also, 0, c },
{ "Exec", "User", config_parse_user, 0, info },
{}
};
_cleanup_fclose_ FILE *f = NULL;
int fd, r;
assert(c);
assert(info);
assert(path);
if (!isempty(root_dir))
path = strappenda(root_dir, "/", path);
if (!load) {
r = access(path, F_OK) ? -errno : 0;
return r;
}
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|(allow_symlink ? 0 : O_NOFOLLOW));
if (fd < 0)
return -errno;
f = fdopen(fd, "re");
if (!f) {
safe_close(fd);
return -ENOMEM;
}
r = config_parse(NULL, path, f,
NULL,
config_item_table_lookup, items,
true, true, false, info);
if (r < 0)
return r;
return
(int) strv_length(info->aliases) +
(int) strv_length(info->wanted_by) +
(int) strv_length(info->required_by);
}
static int unit_file_search(
InstallContext *c,
InstallInfo *info,
LookupPaths *paths,
const char *root_dir,
bool allow_symlink,
bool load) {
char **p;
int r;
assert(c);
assert(info);
assert(paths);
if (info->path)
return unit_file_load(c, info, info->path, root_dir, allow_symlink, load);
assert(info->name);
STRV_FOREACH(p, paths->unit_path) {
_cleanup_free_ char *path = NULL;
path = strjoin(*p, "/", info->name, NULL);
if (!path)
return -ENOMEM;
r = unit_file_load(c, info, path, root_dir, allow_symlink, load);
if (r >= 0) {
info->path = path;
path = NULL;
return r;
}
if (r != -ENOENT && r != -ELOOP)
return r;
}
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 = unit_name_template(info->name);
if (!template)
return -ENOMEM;
STRV_FOREACH(p, paths->unit_path) {
_cleanup_free_ char *path = NULL;
path = strjoin(*p, "/", template, NULL);
if (!path)
return -ENOMEM;
r = unit_file_load(c, info, path, root_dir, allow_symlink, load);
if (r >= 0) {
info->path = path;
path = NULL;
return r;
}
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) {
_cleanup_(install_context_done) InstallContext c = {};
InstallInfo *i;
int r;
assert(paths);
assert(name);
r = install_info_add_auto(&c, name);
if (r < 0)
return r;
assert_se(i = ordered_hashmap_first(c.will_install));
r = unit_file_search(&c, i, paths, root_dir, allow_symlink, true);
if (r >= 0)
r =
(int) strv_length(i->aliases) +
(int) strv_length(i->wanted_by) +
(int) strv_length(i->required_by);
return r;
}
static int create_symlink(
const char *old_path,
const char *new_path,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_free_ char *dest = NULL;
int r;
assert(old_path);
assert(new_path);
mkdir_parents_label(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))
return 0;
if (!force)
return -EEXIST;
r = symlink_atomic(old_path, new_path);
if (r < 0)
return r;
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;
}
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) {
_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(i->path, alias_path, force, changes, n_changes);
if (r == 0)
r = q;
}
return r;
}
static int install_info_symlink_wants(
InstallInfo *i,
const char *config_path,
char **list,
const char *suffix,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_free_ char *buf = NULL;
const char *n;
char **s;
int r = 0, q;
assert(i);
assert(config_path);
if (unit_name_is_template(i->name)) {
/* Don't install any symlink if there's no default
* instance configured */
if (!i->default_instance)
return 0;
buf = unit_name_replace_instance(i->name, i->default_instance);
if (!buf)
return -ENOMEM;
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, TEMPLATE_VALID)) {
r = -EINVAL;
continue;
}
path = strjoin(config_path, "/", dst, suffix, n, NULL);
if (!path)
return -ENOMEM;
q = create_symlink(i->path, path, force, changes, n_changes);
if (r == 0)
r = q;
}
return r;
}
static int install_info_symlink_link(
InstallInfo *i,
LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_free_ char *path = NULL;
int r;
assert(i);
assert(paths);
assert(config_path);
assert(i->path);
r = in_search_path(i->path, paths->unit_path);
if (r != 0)
return r;
path = strjoin(config_path, "/", i->name, NULL);
if (!path)
return -ENOMEM;
return create_symlink(i->path, path, force, changes, n_changes);
}
static int install_info_apply(
InstallInfo *i,
LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
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, i->wanted_by, ".wants/", force, changes, n_changes);
if (r == 0)
r = q;
q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes);
if (r == 0)
r = q;
q = install_info_symlink_link(i, paths, config_path, root_dir, 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, q;
assert(c);
assert(paths);
assert(config_path);
if (!ordered_hashmap_isempty(c->will_install)) {
r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
if (r < 0)
return r;
}
r = 0;
while ((i = ordered_hashmap_first(c->will_install))) {
assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
q = unit_file_search(c, i, paths, root_dir, false, true);
if (q < 0) {
if (r >= 0)
r = q;
return r;
} else if (r >= 0)
r += q;
q = install_info_apply(i, paths, config_path, root_dir, 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, q;
assert(c);
assert(paths);
assert(config_path);
/* Marks all items for removal */
if (!ordered_hashmap_isempty(c->will_install)) {
r = ordered_hashmap_ensure_allocated(&c->have_installed, &string_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_reserve(c->have_installed, ordered_hashmap_size(c->will_install));
if (r < 0)
return r;
}
r = 0;
while ((i = ordered_hashmap_first(c->will_install))) {
assert_se(ordered_hashmap_move_one(c->have_installed, c->will_install, i->name) == 0);
q = unit_file_search(c, i, paths, root_dir, false, true);
if (q == -ENOENT) {
/* do nothing */
} else if (q < 0) {
if (r >= 0)
r = q;
return r;
} else if (r >= 0)
r += q;
if (unit_name_is_instance(i->name)) {
char *unit_file;
if (i->path) {
unit_file = basename(i->path);
if (unit_name_is_instance(unit_file))
/* unit file named as instance exists, thus all symlinks
* pointing to it will be removed */
q = mark_symlink_for_removal(remove_symlinks_to, i->name);
else
/* does not exist, thus we will mark for removal symlinks
* to template unit file */
q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
} else {
/* If i->path is not set, it means that we didn't actually find
* the unit file. But we can still remove symlinks to the
* nonexistent template. */
unit_file = unit_name_template(i->name);
if (!unit_file)
return log_oom();
q = mark_symlink_for_removal(remove_symlinks_to, unit_file);
free(unit_file);
}
} else
q = mark_symlink_for_removal(remove_symlinks_to, i->name);
if (r >= 0 && q < 0)
r = q;
}
return r;
}
int unit_file_add_dependency(
UnitFileScope scope,
bool runtime,
const char *root_dir,
char **files,
char *target,
UnitDependency dep,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
_cleanup_free_ char *config_path = NULL;
char **i;
int r;
InstallInfo *info;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
return r;
STRV_FOREACH(i, files) {
UnitFileState state;
state = unit_file_get_state(scope, root_dir, *i);
if (state < 0) {
log_error("Failed to get unit file state for %s: %s", *i, strerror(-state));
return state;
}
if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
log_error("Failed to enable unit: Unit %s is masked", *i);
return -ENOTSUP;
}
r = install_info_add_auto(&c, *i);
if (r < 0)
return r;
}
if (!ordered_hashmap_isempty(c.will_install)) {
r = ordered_hashmap_ensure_allocated(&c.have_installed, &string_hash_ops);
if (r < 0)
return r;
r = ordered_hashmap_reserve(c.have_installed, ordered_hashmap_size(c.will_install));
if (r < 0)
return r;
}
while ((info = ordered_hashmap_first(c.will_install))) {
assert_se(ordered_hashmap_move_one(c.have_installed, c.will_install, info->name) == 0);
r = unit_file_search(&c, info, &paths, root_dir, false, false);
if (r < 0)
return r;
if (dep == UNIT_WANTS)
r = strv_extend(&info->wanted_by, target);
else if (dep == UNIT_REQUIRES)
r = strv_extend(&info->required_by, target);
else
r = -EINVAL;
if (r < 0)
return r;
r = install_info_apply(info, &paths, config_path, root_dir, force, changes, n_changes);
if (r < 0)
return r;
}
return 0;
}
int unit_file_enable(
UnitFileScope scope,
bool runtime,
const char *root_dir,
char **files,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
char **i;
_cleanup_free_ char *config_path = NULL;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
return r;
STRV_FOREACH(i, files) {
UnitFileState state;
/* We only want to know if this unit is masked, so we ignore
* errors from unit_file_get_state, deferring other checks.
* This allows templated units to be enabled on the fly. */
state = unit_file_get_state(scope, root_dir, *i);
if (state == UNIT_FILE_MASKED || state == UNIT_FILE_MASKED_RUNTIME) {
log_error("Failed to enable unit: Unit %s is masked", *i);
return -ENOTSUP;
}
r = install_info_add_auto(&c, *i);
if (r < 0)
return r;
}
/* 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(&c, &paths, config_path, root_dir, force, changes, n_changes);
}
int unit_file_disable(
UnitFileScope scope,
bool runtime,
const char *root_dir,
char **files,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
char **i;
_cleanup_free_ char *config_path = NULL;
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
return r;
STRV_FOREACH(i, files) {
r = install_info_add_auto(&c, *i);
if (r < 0)
return r;
}
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, files);
if (r >= 0)
r = q;
return r;
}
int unit_file_reenable(
UnitFileScope scope,
bool runtime,
const char *root_dir,
char **files,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
int r;
r = unit_file_disable(scope, runtime, root_dir, files,
changes, n_changes);
if (r < 0)
return r;
return unit_file_enable(scope, runtime, root_dir, files, force,
changes, n_changes);
}
int unit_file_set_default(
UnitFileScope scope,
const char *root_dir,
const char *file,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_(install_context_done) InstallContext c = {};
_cleanup_free_ char *config_path = NULL;
char *path;
int r;
InstallInfo *i = NULL;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(file);
if (unit_name_to_type(file) != UNIT_TARGET)
return -EINVAL;
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
r = get_config_path(scope, false, root_dir, &config_path);
if (r < 0)
return r;
r = install_info_add_auto(&c, file);
if (r < 0)
return r;
assert_se(i = ordered_hashmap_first(c.will_install));
r = unit_file_search(&c, i, &paths, root_dir, false, true);
if (r < 0)
return r;
path = strappenda(config_path, "/" SPECIAL_DEFAULT_TARGET);
r = create_symlink(i->path, path, force, changes, n_changes);
if (r < 0)
return r;
return 0;
}
int unit_file_get_default(
UnitFileScope scope,
const char *root_dir,
char **name) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
char **p;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
STRV_FOREACH(p, paths.unit_path) {
_cleanup_free_ char *path = NULL, *tmp = NULL;
char *n;
path = path_join(root_dir, *p, SPECIAL_DEFAULT_TARGET);
if (!path)
return -ENOMEM;
r = readlink_malloc(path, &tmp);
if (r == -ENOENT)
continue;
else if (r == -EINVAL)
/* not a symlink */
n = strdup(SPECIAL_DEFAULT_TARGET);
else if (r < 0)
return r;
else
n = strdup(basename(tmp));
if (!n)
return -ENOMEM;
*name = n;
return 0;
}
return -ENOENT;
}
UnitFileState unit_file_get_state(
UnitFileScope scope,
const char *root_dir,
const char *name) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
UnitFileState state = _UNIT_FILE_STATE_INVALID;
char **i;
_cleanup_free_ char *path = NULL;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
if (root_dir && scope != UNIT_FILE_SYSTEM)
return -EINVAL;
if (!unit_name_is_valid(name, TEMPLATE_VALID))
return -EINVAL;
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
STRV_FOREACH(i, paths.unit_path) {
struct stat st;
char *partial;
free(path);
path = NULL;
path = path_join(root_dir, *i, name);
if (!path)
return -ENOMEM;
if (root_dir)
partial = path + strlen(root_dir);
else
partial = path;
/*
* Search for a unit file in our default paths, to
* be sure, that there are no broken symlinks.
*/
if (lstat(path, &st) < 0) {
r = -errno;
if (errno != ENOENT)
return r;
if (!unit_name_is_instance(name))
continue;
} else {
if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
return -ENOENT;
r = null_or_empty_path(path);
if (r < 0 && r != -ENOENT)
return r;
else if (r > 0) {
state = path_startswith(*i, "/run") ?
UNIT_FILE_MASKED_RUNTIME : UNIT_FILE_MASKED;
return state;
}
}
r = find_symlinks_in_scope(scope, root_dir, name, &state);
if (r < 0)
return r;
else if (r > 0)
return state;
r = unit_file_can_install(&paths, root_dir, partial, true);
if (r < 0 && errno != ENOENT)
return r;
else if (r > 0)
return UNIT_FILE_DISABLED;
else if (r == 0)
return UNIT_FILE_STATIC;
}
return r < 0 ? r : state;
}
int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name) {
_cleanup_strv_free_ char **files = NULL;
char **p;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(name);
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
return 1;
if (r < 0)
return r;
STRV_FOREACH(p, files) {
_cleanup_fclose_ FILE *f;
f = fopen(*p, "re");
if (!f) {
if (errno == ENOENT)
continue;
return -errno;
}
for (;;) {
char line[LINE_MAX], *l;
if (!fgets(line, sizeof(line), f))
break;
l = strstrip(line);
if (!*l)
continue;
if (strchr(COMMENTS "\n", *l))
continue;
if (first_word(l, "enable")) {
l += 6;
l += strspn(l, WHITESPACE);
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) {
log_debug("Preset file says disable %s.", name);
return 0;
}
} else
log_debug("Couldn't parse line '%s'", l);
}
}
/* Default is "enable" */
log_debug("Preset file doesn't say anything about %s, enabling.", name);
return 1;
}
int unit_file_preset(
UnitFileScope scope,
bool runtime,
const char *root_dir,
char **files,
UnitFilePresetMode mode,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_free_ char *config_path = NULL;
char **i;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MAX);
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
return r;
STRV_FOREACH(i, files) {
if (!unit_name_is_valid(*i, TEMPLATE_VALID))
return -EINVAL;
r = unit_file_query_preset(scope, root_dir, *i);
if (r < 0)
return r;
if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
r = install_info_add_auto(&plus, *i);
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 = 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, files);
if (r == 0)
r = q;
}
if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
/* Returns number of symlinks that where supposed to be installed. */
q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
if (r == 0)
r = q;
}
return r;
}
int unit_file_preset_all(
UnitFileScope scope,
bool runtime,
const char *root_dir,
UnitFilePresetMode mode,
bool force,
UnitFileChange **changes,
unsigned *n_changes) {
_cleanup_(install_context_done) InstallContext plus = {}, minus = {};
_cleanup_lookup_paths_free_ LookupPaths paths = {};
_cleanup_free_ char *config_path = NULL;
char **i;
int r, q;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(mode < _UNIT_FILE_PRESET_MAX);
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
r = get_config_path(scope, runtime, root_dir, &config_path);
if (r < 0)
return r;
STRV_FOREACH(i, paths.unit_path) {
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *units_dir;
units_dir = path_join(root_dir, *i, NULL);
if (!units_dir)
return -ENOMEM;
d = opendir(units_dir);
if (!d) {
if (errno == ENOENT)
continue;
return -errno;
}
for (;;) {
struct dirent *de;
errno = 0;
de = readdir(d);
if (!de && errno != 0)
return -errno;
if (!de)
break;
if (ignore_file(de->d_name))
continue;
if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
continue;
dirent_ensure_type(d, de);
if (de->d_type != DT_REG)
continue;
r = unit_file_query_preset(scope, root_dir, de->d_name);
if (r < 0)
return r;
if (r && mode != UNIT_FILE_PRESET_DISABLE_ONLY)
r = install_info_add_auto(&plus, de->d_name);
else if (!r && mode != UNIT_FILE_PRESET_ENABLE_ONLY)
r = install_info_add_auto(&minus, de->d_name);
else
r = 0;
if (r < 0)
return r;
}
}
r = 0;
if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
_cleanup_set_free_free_ Set *remove_symlinks_to = NULL;
r = install_context_mark_for_removal(&minus, &paths, &remove_symlinks_to, config_path, root_dir);
q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, NULL);
if (r == 0)
r = q;
}
if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
q = install_context_apply(&plus, &paths, config_path, root_dir, force, changes, n_changes);
if (r == 0)
r = q;
}
return r;
}
static void unit_file_list_free_one(UnitFileList *f) {
if (!f)
return;
free(f->path);
free(f);
}
DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one);
int unit_file_get_list(
UnitFileScope scope,
const char *root_dir,
Hashmap *h) {
_cleanup_lookup_paths_free_ LookupPaths paths = {};
char **i;
int r;
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(h);
if (root_dir && scope != UNIT_FILE_SYSTEM)
return -EINVAL;
if (root_dir) {
r = access(root_dir, F_OK);
if (r < 0)
return -errno;
}
r = lookup_paths_init_from_scope(&paths, scope, root_dir);
if (r < 0)
return r;
STRV_FOREACH(i, paths.unit_path) {
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *units_dir;
units_dir = path_join(root_dir, *i, NULL);
if (!units_dir)
return -ENOMEM;
d = opendir(units_dir);
if (!d) {
if (errno == ENOENT)
continue;
return -errno;
}
for (;;) {
_cleanup_(unit_file_list_free_onep) UnitFileList *f = NULL;
struct dirent *de;
_cleanup_free_ char *path = NULL;
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;
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, units_dir);
if (!f->path)
return -ENOMEM;
r = null_or_empty_path(f->path);
if (r < 0 && r != -ENOENT)
return r;
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)
return r;
else if (r > 0) {
f->state = UNIT_FILE_ENABLED;
goto found;
}
path = path_make_absolute(de->d_name, *i);
if (!path)
return -ENOMEM;
r = unit_file_can_install(&paths, root_dir, path, true);
if (r == -EINVAL || /* Invalid setting? */
r == -EBADMSG || /* Invalid format? */
r == -ENOENT /* Included file not found? */)
f->state = UNIT_FILE_INVALID;
else if (r < 0)
return r;
else if (r > 0)
f->state = UNIT_FILE_DISABLED;
else
f->state = UNIT_FILE_STATIC;
found:
r = hashmap_put(h, basename(f->path), f);
if (r < 0)
return r;
f = NULL; /* prevent cleanup */
}
}
return r;
}
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_INVALID] = "invalid",
};
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",
};
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);