install.c revision 0ffce503cd6e5a5ff5ba5cd1cc23684cfb8bb9e3
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann This file is part of systemd.
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann Copyright 2011 Lennart Poettering
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 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 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 Herrmanntypedef struct {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int in_search_path(const char *path, char **search) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int lookup_paths_init_from_scope(LookupPaths *paths,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *root_dir) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannstatic int get_config_path(UnitFileScope scope, bool runtime, const char *root_dir, char **ret) {
e23f4bb525991c5908be0d0e7f8374c964d9996cDavid Herrmann p = path_join(root_dir, "/run/systemd/system", NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann p = path_join(root_dir, SYSTEM_CONFIG_UNIT_PATH, NULL);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r < 0 ? r : -ENOENT;
05bae4a60c32e29797597979cee2f3684eb3bc1eDavid Herrmann const char *source) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann c = realloc(*changes, (*n_changes + 1) * sizeof(UnitFileChange));
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann const char *p) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = set_ensure_allocated(remove_symlinks_to, &string_hash_ops);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann return r == -EEXIST ? 0 : r;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
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 if (!unit_name_is_valid(de->d_name, TEMPLATE_VALID))
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann !strv_contains(instance_whitelist, de->d_name)) {
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 add_file_change(changes, n_changes, UNIT_FILE_UNLINK, p, NULL);
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering q = mark_symlink_for_removal(&remove_symlinks_to, p);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
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 nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
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 /* Acquire symlink name */
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann /* Acquire symlink destination */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Check if the symlink itself matches what we
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * are looking for */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Check if what the symlink points to
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * matches what we are looking for */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Filter out same name links in the main
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann * config path */
7447362c530e3f7128f16a35d1e43da4251144ccDavid Herrmann fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
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 bool same_name_link_runtime = false, same_name_link = false;
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* First look in runtime config path */
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = get_config_path(scope, true, root_dir, &path);
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann r = find_symlinks(name, path, &same_name_link_runtime);
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann else if (r > 0) {
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann /* Then look in the normal config path */
c4bc1a8434f2a34840ea6f63064fa998ecfae738David Herrmann r = get_config_path(scope, false, root_dir, &path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = find_symlinks(name, path, &same_name_link);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann else if (r > 0) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann /* Hmm, we didn't find it, but maybe we found the same name
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = get_config_path(scope, runtime, root_dir, &prefix);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, "/dev/null");
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 r = get_config_path(scope, runtime, root_dir, &config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann if (!unit_name_is_valid(*i, TEMPLATE_VALID)) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann mark_symlink_for_removal(&remove_symlinks_to, path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_UNLINK, path, NULL);
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering if (q != -ENOENT && r == 0)
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann q = remove_marked_symlinks(remove_symlinks_to, config_path, changes, n_changes, files);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann _cleanup_lookup_paths_free_ LookupPaths paths = {};
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = lookup_paths_init_from_scope(&paths, scope, root_dir);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann r = get_config_path(scope, runtime, root_dir, &config_path);
d27efd93841a2ac2127fd53321368cc3f975c564Lennart Poettering path = path_make_absolute(fn, config_path);
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann add_file_change(changes, n_changes, UNIT_FILE_SYMLINK, path, *i);
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 while ((i = hashmap_steal_first(h))) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmannvoid unit_file_changes_free(UnitFileChange *changes, unsigned n_changes) {
c0395aeb903cde25bd9e81fba3334f63335fe0efDavid Herrmann for (i = 0; i < n_changes; i++) {
assert(i);
free(i);
InstallInfo *i;
while ((i = ordered_hashmap_steal_first(m)))
assert(c);
static int install_info_add(
InstallContext *c,
const char *name,
const char *path) {
assert(c);
if (!name)
return -EINVAL;
return -ENOMEM;
if (!i->name) {
r = -ENOMEM;
goto fail;
if (path) {
if (!i->path) {
r = -ENOMEM;
goto fail;
goto fail;
fail:
static int install_info_add_auto(
InstallContext *c,
const char *name_or_path) {
assert(c);
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;
_cleanup_free_ char *n;
return -ENOMEM;
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) {
char *printed;
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) {
char *printed;
return -EINVAL;
static int unit_file_load(
InstallContext *c,
const char *path,
const char *root_dir,
bool allow_symlink,
bool load) {
int fd, r;
assert(c);
if (!load) {
if (fd < 0)
return -errno;
return -ENOMEM;
NULL,
true, true, false, info);
static int unit_file_search(
InstallContext *c,
const char *root_dir,
bool allow_symlink,
bool load) {
assert(c);
if (!path)
return -ENOMEM;
if (!template)
return -ENOMEM;
if (!path)
return -ENOMEM;
return -ENOENT;
static int unit_file_can_install(
const char *root_dir,
const char *name,
bool allow_symlink) {
InstallInfo *i;
static int create_symlink(
const char *old_path,
const char *new_path,
bool force,
unsigned *n_changes) {
return -errno;
if (!force)
return -EEXIST;
static int install_info_symlink_alias(
InstallInfo *i,
const char *config_path,
bool force,
unsigned *n_changes) {
assert(i);
if (!alias_path)
return -ENOMEM;
static int install_info_symlink_wants(
InstallInfo *i,
const char *config_path,
char **list,
const char *suffix,
bool force,
unsigned *n_changes) {
assert(i);
if (!i->default_instance)
if (!buf)
return -ENOMEM;
n = buf;
n = i->name;
r = -EINVAL;
if (!path)
return -ENOMEM;
static int install_info_symlink_link(
InstallInfo *i,
const char *config_path,
const char *root_dir,
bool force,
unsigned *n_changes) {
assert(i);
if (!path)
return -ENOMEM;
static int install_info_apply(
InstallInfo *i,
const char *config_path,
const char *root_dir,
bool force,
unsigned *n_changes) {
assert(i);
q = install_info_symlink_wants(i, config_path, i->required_by, ".requires/", force, changes, n_changes);
static int install_context_apply(
InstallContext *c,
const char *config_path,
const char *root_dir,
bool force,
unsigned *n_changes) {
InstallInfo *i;
assert(c);
static int install_context_mark_for_removal(
InstallContext *c,
const char *config_path,
const char *root_dir) {
InstallInfo *i;
assert(c);
if (q == -ENOENT) {
char *unit_file;
if (i->path) {
if (!unit_file)
return log_oom();
bool runtime,
const char *root_dir,
char **files,
char *target,
bool force,
unsigned *n_changes) {
if (state < 0) {
return state;
return -ENOTSUP;
r = install_info_add_auto(&c, *i);
r = -EINVAL;
int unit_file_enable(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
return -ENOTSUP;
r = install_info_add_auto(&c, *i);
int unit_file_disable(
bool runtime,
const char *root_dir,
char **files,
unsigned *n_changes) {
r = install_info_add_auto(&c, *i);
int unit_file_reenable(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
const char *root_dir,
const char *file,
bool force,
unsigned *n_changes) {
char *path;
return -EINVAL;
const char *root_dir,
char **name) {
if (!path)
return -ENOMEM;
if (r == -ENOENT)
else if (r == -EINVAL)
return -ENOMEM;
*name = n;
return -ENOENT;
const char *root_dir,
const char *name) {
return -EINVAL;
return -EINVAL;
char *partial;
if (!path)
return -ENOMEM;
if (root_dir)
r = -errno;
return -ENOENT;
if (r < 0 && r != -ENOENT)
return state;
return state;
return UNIT_FILE_DISABLED;
return UNIT_FILE_STATIC;
return r < 0 ? r : state;
"/etc/systemd/system-preset",
"/usr/local/lib/systemd/system-preset",
"/usr/lib/systemd/system-preset",
#ifdef HAVE_SPLIT_USR
"/lib/systemd/system-preset",
NULL);
"/etc/systemd/user-preset",
"/usr/local/lib/systemd/user-preset",
"/usr/lib/systemd/user-preset",
NULL);
return -errno;
int unit_file_preset(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
return -EINVAL;
int unit_file_preset_all(
bool runtime,
const char *root_dir,
bool force,
unsigned *n_changes) {
if (!units_dir)
return -ENOMEM;
return -errno;
errno = 0;
return -errno;
if (!de)
free(f);
int unit_file_get_list(
const char *root_dir,
Hashmap *h) {
assert(h);
return -EINVAL;
if (root_dir) {
return -errno;
if (!units_dir)
return -ENOMEM;
return -errno;
errno = 0;
return -errno;
if (!de)
return -ENOMEM;
if (!f->path)
return -ENOMEM;
if (r < 0 && r != -ENOENT)
f->state =
goto found;
goto found;
if (!path)
return -ENOMEM;