install.c revision d986e364c4343240ef77b26a1c9c516f574f015d
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2011 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty <of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <string.h>
#include <unistd.h>
#include "alloc-util.h"
#include "conf-files.h"
#include "conf-parser.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "hashmap.h"
#include "install-printf.h"
#include "install.h"
#include "mkdir.h"
#include "path-lookup.h"
#include "path-util.h"
#include "set.h"
#include "special.h"
#include "stat-util.h"
#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "unit-name.h"
#include "util.h"
#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
typedef enum SearchFlags {
SEARCH_LOAD = 1,
} SearchFlags;
typedef struct {
char **i;
if (!parent)
return -ENOMEM;
STRV_FOREACH(i, search)
if (path_equal(parent, *i))
return true;
return false;
}
char *p = NULL;
int r;
/* This determines where we shall create or remove our
* installation ("configuration") symlinks */
switch (scope) {
case UNIT_FILE_SYSTEM:
if (runtime)
else
break;
case UNIT_FILE_GLOBAL:
if (root_dir)
return -EINVAL;
if (runtime)
else
p = strdup(USER_CONFIG_UNIT_PATH);
break;
case UNIT_FILE_USER:
if (root_dir)
return -EINVAL;
if (runtime)
r = user_runtime_dir(&p);
else
r = user_config_home(&p);
if (r < 0)
return r;
if (r == 0)
return -ENOENT;
break;
default:
assert_not_reached("Bad scope");
}
if (!p)
return -ENOMEM;
*ret = p;
return 0;
}
int r;
/* Checks whether the specified path is intended for
* configuration or is outside of it */
switch (scope) {
case UNIT_FILE_SYSTEM:
case UNIT_FILE_GLOBAL:
case UNIT_FILE_USER: {
_cleanup_free_ char *p = NULL;
r = user_config_home(&p);
if (r < 0)
return r;
if (r > 0 && path_startswith(path, p))
return true;
p = mfree(p);
r = user_runtime_dir(&p);
if (r < 0)
return r;
if (r > 0 && path_startswith(path, p))
return true;
return false;
}
default:
assert_not_reached("Bad scope");
}
}
int r;
/* Verifies that the specified root directory to operate on
* makes sense. Reset it to NULL if it is the root directory
* or set to empty */
return 0;
}
if (scope != UNIT_FILE_SYSTEM)
return -EINVAL;
if (r < 0)
return r;
if (r == 0)
return -ENOTDIR;
return 0;
}
unsigned *n_changes,
const char *path,
const char *source) {
UnitFileChange *c;
unsigned i;
if (!changes)
return 0;
if (!c)
return -ENOMEM;
*changes = c;
i = *n_changes;
if (!c[i].path)
return -ENOMEM;
path_kill_slashes(c[i].path);
if (source) {
if (!c[i].source) {
return -ENOMEM;
}
path_kill_slashes(c[i].path);
} else
*n_changes = i+1;
return 0;
}
unsigned i;
if (!changes)
return;
for (i = 0; i < n_changes; i++) {
}
}
static int create_symlink(
const char *old_path,
const char *new_path,
bool force,
unsigned *n_changes) {
int r;
/* Actually create a symlink, and remember that we did. Is
* smart enough to check if there's already a valid symlink in
* place. */
return 0;
}
return -errno;
if (r < 0)
return r;
return 0;
if (!force)
return -EEXIST;
if (r < 0)
return r;
return 0;
}
static int mark_symlink_for_removal(
const char *p) {
char *n;
int r;
assert(p);
if (r < 0)
return r;
n = strdup(p);
if (!n)
return -ENOMEM;
r = set_consume(*remove_symlinks_to, n);
if (r == -EEXIST)
return 0;
if (r < 0)
return r;
return 1;
}
static int remove_marked_symlinks_fd(
int fd,
const char *path,
const char *config_path,
bool *restart,
unsigned *n_changes) {
int r = 0;
if (!d) {
safe_close(fd);
return -errno;
}
rewinddir(d);
dirent_ensure_type(d, de);
_cleanup_free_ char *p = NULL;
int nfd, q;
if (nfd < 0) {
continue;
if (r == 0)
r = -errno;
continue;
}
if (!p) {
return -ENOMEM;
}
/* This will close nfd, regardless whether it succeeds or not */
q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes);
if (q < 0 && r == 0)
r = q;
bool found;
int q;
continue;
if (!p)
return -ENOMEM;
q = readlink_malloc(p, &dest);
if (q < 0) {
if (q == -ENOENT)
continue;
if (r == 0)
r = q;
continue;
}
/* We remove all links pointing to a file or
* path that is marked, as well as all files
* sharing the same name as a file that is
* marked. */
found =
if (!found)
continue;
if (r == 0)
r = -errno;
continue;
}
(void) rmdir_parents(p, config_path);
q = mark_symlink_for_removal(&remove_symlinks_to, p);
if (q < 0)
return q;
if (q > 0)
*restart = true;
}
}
return r;
}
static int remove_marked_symlinks(
const char *config_path,
unsigned *n_changes) {
bool restart;
int r = 0;
if (set_size(remove_symlinks_to) <= 0)
return 0;
if (fd < 0)
return -errno;
do {
int q, cfd;
restart = false;
if (cfd < 0)
return -errno;
/* This takes possession of cfd and closes it */
q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes);
if (r == 0)
r = q;
} while (restart);
return r;
}
static int find_symlinks_fd(
const char *root_dir,
const char *name,
int fd,
const char *path,
const char *config_path,
bool *same_name_link) {
int r = 0;
if (!d) {
safe_close(fd);
return -errno;
}
dirent_ensure_type(d, de);
_cleanup_free_ char *p = NULL;
int nfd, q;
if (nfd < 0) {
continue;
if (r == 0)
r = -errno;
continue;
}
if (!p) {
return -ENOMEM;
}
/* This will close nfd, regardless whether it succeeds or not */
if (q > 0)
return 1;
if (r == 0)
r = q;
bool found_path, found_dest, b = false;
int q;
/* Acquire symlink name */
if (!p)
return -ENOMEM;
/* Acquire symlink destination */
q = readlink_malloc(p, &dest);
if (q == -ENOENT)
continue;
if (q < 0) {
if (r == 0)
r = q;
continue;
}
/* Make absolute */
if (!path_is_absolute(dest)) {
char *x;
if (!x)
return -ENOMEM;
dest = x;
}
/* Check if the symlink itself matches what we
* are looking for */
if (path_is_absolute(name))
else
/* Check if what the symlink points to
* matches what we are looking for */
if (path_is_absolute(name))
else
if (found_path && found_dest) {
_cleanup_free_ char *t = NULL;
/* Filter out same name links in the main
* config path */
if (!t)
return -ENOMEM;
b = path_equal(t, p);
}
if (b)
*same_name_link = true;
else if (found_path || found_dest)
return 1;
}
}
return r;
}
static int find_symlinks(
const char *root_dir,
const char *name,
const char *config_path,
bool *same_name_link) {
int fd;
if (fd < 0) {
return 0;
return -errno;
}
/* This takes possession of fd and closes it */
}
static int find_symlinks_in_scope(
const char *root_dir,
const char *name,
UnitFileState *state) {
bool same_name_link_runtime = false, same_name_link = false;
int r;
/* First look in the normal config path */
if (r < 0)
return r;
if (r < 0)
return r;
if (r > 0) {
return r;
}
/* Then look in runtime config path */
if (r < 0)
return r;
if (r < 0)
return r;
if (r > 0) {
return r;
}
/* Hmm, we didn't find it, but maybe we found the same name
* link? */
if (same_name_link) {
return 1;
}
if (same_name_link_runtime) {
return 1;
}
return 0;
}
static void install_info_free(UnitFileInstallInfo *i) {
if (!i)
return;
strv_free(i->required_by);
free(i->default_instance);
free(i->symlink_target);
free(i);
}
if (!m)
return NULL;
while ((i = ordered_hashmap_steal_first(m)))
return ordered_hashmap_free(m);
}
static void install_context_done(InstallContext *c) {
assert(c);
}
if (i)
return i;
}
static int install_info_add(
InstallContext *c,
const char *name,
const char *path,
UnitFileInstallInfo **ret) {
UnitFileInstallInfo *i = NULL;
int r;
assert(c);
if (!name)
return -EINVAL;
i = install_info_find(c, name);
if (i) {
if (ret)
*ret = i;
return 0;
}
if (r < 0)
return r;
if (!i)
return -ENOMEM;
i->type = _UNIT_FILE_TYPE_INVALID;
if (!i->name) {
r = -ENOMEM;
goto fail;
}
if (path) {
if (!i->path) {
r = -ENOMEM;
goto fail;
}
}
if (r < 0)
goto fail;
if (ret)
*ret = i;
return 0;
fail:
return r;
}
static int install_info_add_auto(
InstallContext *c,
const char *name_or_path,
UnitFileInstallInfo **ret) {
assert(c);
if (path_is_absolute(name_or_path))
else
}
static int config_parse_also(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
UnitFileInstallInfo *i = userdata;
InstallContext *c = data;
int r;
for (;;) {
if (r < 0)
return r;
if (r == 0)
break;
if (r < 0)
return r;
if (r < 0)
return r;
}
return 0;
}
static int config_parse_default_instance(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
UnitFileInstallInfo *i = data;
char *printed;
int r;
if (r < 0)
return r;
if (!unit_instance_is_valid(printed)) {
return -EINVAL;
}
free(i->default_instance);
i->default_instance = printed;
return 0;
}
static int unit_file_load(
InstallContext *c,
const char *path,
const char *root_dir,
SearchFlags flags) {
const ConfigTableItem items[] = {
{}
};
int r;
assert(c);
if (!(flags & SEARCH_LOAD)) {
if (r < 0)
return -errno;
if (null_or_empty(&st))
return -ELOOP;
return -EISDIR;
else
return -ENOTTY;
return 0;
}
if (fd < 0)
return -errno;
return -errno;
if (null_or_empty(&st)) {
return 0;
}
return -EISDIR;
return -ENOTTY;
if (!f)
return -errno;
fd = -1;
NULL,
true, true, false, info);
if (r < 0)
return r;
return
}
static int unit_file_load_or_readlink(
InstallContext *c,
const char *path,
const char *root_dir,
SearchFlags flags) {
int r;
if (r != -ELOOP)
return r;
/* This is a symlink, let's read it. */
if (r < 0)
return r;
else {
const char *bn;
UnitType a, b;
return -EINVAL;
return -EINVAL;
return -EINVAL;
} else
return -EINVAL;
/* Enforce that the symlink destination does not
* change the unit file type. */
b = unit_name_to_type(bn);
if (a < 0 || b < 0 || a != b)
return -EINVAL;
}
return 0;
}
static int unit_file_search(
InstallContext *c,
const LookupPaths *paths,
const char *root_dir,
SearchFlags flags) {
char **p;
int r;
assert(c);
/* Was this unit already loaded? */
return 0;
if (!path)
return -ENOMEM;
if (r < 0) {
if (r != -ENOENT)
return r;
} else {
return r;
}
}
/* Unit file doesn't exist, however instance
* enablement was requested. We will check if it is
* possible to load template unit file. */
if (r < 0)
return r;
if (!path)
return -ENOMEM;
if (r < 0) {
if (r != -ENOENT)
return r;
} else {
return r;
}
}
}
return -ENOENT;
}
static int install_info_follow(
InstallContext *c,
const char *root_dir,
SearchFlags flags) {
assert(c);
assert(i);
if (i->type != UNIT_FILE_TYPE_SYMLINK)
return -EINVAL;
if (!i->symlink_target)
return -EINVAL;
/* If the basename doesn't match, the caller should add a
* complete new entry for this. */
return -EXDEV;
i->path = i->symlink_target;
i->symlink_target = NULL;
i->type = _UNIT_FILE_TYPE_INVALID;
}
static int install_info_traverse(
InstallContext *c,
const char *root_dir,
const LookupPaths *paths,
UnitFileInstallInfo **ret) {
unsigned k = 0;
int r;
assert(c);
if (r < 0)
return r;
i = start;
while (i->type == UNIT_FILE_TYPE_SYMLINK) {
/* Follow the symlink */
if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
return -ELOOP;
return -ELOOP;
if (r < 0) {
const char *bn;
if (r != -EXDEV)
return r;
/* Target has a different name, create a new
* install info object for that, and continue
* with that. */
if (r < 0)
return r;
if (r < 0)
return r;
}
if (r < 0)
return r;
if (r < 0)
return r;
}
/* Try again, with the new target we found. */
}
if (ret)
*ret = i;
return 0;
}
static int install_info_discover(
InstallContext *c,
const char *root_dir,
const LookupPaths *paths,
const char *name,
UnitFileInstallInfo **ret) {
int r;
assert(c);
r = install_info_add_auto(c, name, &i);
if (r < 0)
return r;
}
static int install_info_symlink_alias(
const char *config_path,
bool force,
unsigned *n_changes) {
char **s;
int r = 0, q;
assert(i);
STRV_FOREACH(s, i->aliases) {
q = install_full_printf(i, *s, &dst);
if (q < 0)
return q;
if (!alias_path)
return -ENOMEM;
if (r == 0)
r = q;
}
return r;
}
static int install_info_symlink_wants(
const char *config_path,
char **list,
const char *suffix,
bool force,
unsigned *n_changes) {
const char *n;
char **s;
int r = 0, q;
assert(i);
/* Don't install any symlink if there's no default
* instance configured */
if (!i->default_instance)
return 0;
if (r < 0)
return r;
n = buf;
} else
n = i->name;
STRV_FOREACH(s, list) {
q = install_full_printf(i, *s, &dst);
if (q < 0)
return q;
r = -EINVAL;
continue;
}
if (!path)
return -ENOMEM;
if (r == 0)
r = q;
}
return r;
}
static int install_info_symlink_link(
const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
unsigned *n_changes) {
int r;
assert(i);
if (r != 0)
return r;
if (!path)
return -ENOMEM;
}
static int install_info_apply(
const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
unsigned *n_changes) {
int r, q;
assert(i);
if (i->type != UNIT_FILE_TYPE_REGULAR)
return 0;
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;
if (r == 0)
r = q;
return r;
}
static int install_context_apply(
InstallContext *c,
const LookupPaths *paths,
const char *config_path,
const char *root_dir,
bool force,
unsigned *n_changes) {
int r;
assert(c);
if (ordered_hashmap_isempty(c->will_process))
return 0;
if (r < 0)
return r;
r = 0;
while ((i = ordered_hashmap_first(c->will_process))) {
int q;
if (q < 0)
return q;
if (r < 0)
return r;
if (i->type != UNIT_FILE_TYPE_REGULAR)
continue;
if (r >= 0) {
if (q < 0)
r = q;
else
r+= q;
}
}
return r;
}
static int install_context_mark_for_removal(
InstallContext *c,
const LookupPaths *paths,
const char *config_path,
const char *root_dir) {
int r;
assert(c);
/* Marks all items for removal */
if (ordered_hashmap_isempty(c->will_process))
return 0;
if (r < 0)
return r;
while ((i = ordered_hashmap_first(c->will_process))) {
if (r < 0)
return r;
r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
if (r < 0)
return r;
if (i->type != UNIT_FILE_TYPE_REGULAR)
continue;
if (r < 0)
return r;
}
return 0;
}
int unit_file_mask(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
char **i;
int r;
if (r < 0)
return r;
if (r < 0)
return r;
STRV_FOREACH(i, files) {
int q;
if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) {
if (r == 0)
r = -EINVAL;
continue;
}
if (!path)
return -ENOMEM;
if (q < 0 && r >= 0)
r = q;
}
return r;
}
int unit_file_unmask(
bool runtime,
const char *root_dir,
char **files,
unsigned *n_changes) {
char **i;
int r, q;
if (r < 0)
return r;
if (r < 0)
return r;
STRV_FOREACH(i, files) {
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
if (!path)
return -ENOMEM;
r = null_or_empty_path(path);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
if (r == 0)
continue;
return -ENOMEM;
}
r = 0;
STRV_FOREACH(i, todo) {
if (!path)
return -ENOMEM;
r = -errno;
} else {
if (q < 0)
return q;
}
}
if (r >= 0)
r = q;
return r;
}
int unit_file_link(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
char **i;
int r, q;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
STRV_FOREACH(i, files) {
char *fn;
if (!path_is_absolute(*i))
return -EINVAL;
return -EINVAL;
if (!full)
return -ENOMEM;
return -errno;
return -ELOOP;
return -EISDIR;
return -ENOTTY;
if (q < 0)
return q;
if (q > 0)
continue;
return -ENOMEM;
}
r = 0;
STRV_FOREACH(i, todo) {
if (!path)
return -ENOMEM;
if (q < 0 && r >= 0)
r = q;
}
return r;
}
bool runtime,
const char *root_dir,
char **files,
const char *target,
bool force,
unsigned *n_changes) {
char **f;
int r;
return -EINVAL;
return -EINVAL;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info);
if (r < 0)
return r;
return -ESHUTDOWN;
STRV_FOREACH(f, files) {
char ***l;
if (r < 0)
return r;
if (i->type == UNIT_FILE_TYPE_MASKED)
return -ESHUTDOWN;
/* We didn't actually load anything from the unit
* file, but instead just add in our new symlink to
* create. */
if (dep == UNIT_WANTS)
l = &i->wanted_by;
else
l = &i->required_by;
strv_free(*l);
if (!*l)
return -ENOMEM;
}
return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes);
}
int unit_file_enable(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
char **f;
int r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
STRV_FOREACH(f, files) {
if (r < 0)
return r;
if (i->type == UNIT_FILE_TYPE_MASKED)
return -ESHUTDOWN;
}
/* This will return the number of symlink rules that were
supposed to be created, not the ones actually created. This
is useful to determine whether the passed files had any
installation data at all. */
return install_context_apply(scope, &c, &paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes);
}
int unit_file_disable(
bool runtime,
const char *root_dir,
char **files,
unsigned *n_changes) {
char **i;
int r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
STRV_FOREACH(i, files) {
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
if (r < 0)
return r;
}
r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir);
if (r < 0)
return r;
}
int unit_file_reenable(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
char **n;
int r;
size_t l, i;
/* First, we invoke the disable command with only the basename... */
l = strv_length(files);
n = newa(char*, l+1);
for (i = 0; i < l; i++)
n[i] = NULL;
if (r < 0)
return r;
/* But the enable command with the full name */
}
const char *root_dir,
const char *name,
bool force,
unsigned *n_changes) {
const char *path;
int r;
return -EINVAL;
return -EINVAL;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
if (i->type == UNIT_FILE_TYPE_MASKED)
return -ESHUTDOWN;
}
const char *root_dir,
char **name) {
char *n;
int r;
if (r < 0)
return r;
if (r < 0)
return r;
r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
if (r < 0)
return r;
if (i->type == UNIT_FILE_TYPE_MASKED)
return -ESHUTDOWN;
if (!n)
return -ENOMEM;
*name = n;
return 0;
}
const char *root_dir,
const LookupPaths *paths,
const char *name,
UnitFileState *ret) {
int r;
return -EINVAL;
if (r < 0)
return r;
r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
if (r < 0)
return r;
/* Shortcut things, if the caller just wants to know if this unit exists. */
if (!ret)
return 0;
switch (i->type) {
case UNIT_FILE_TYPE_MASKED:
break;
case UNIT_FILE_TYPE_REGULAR:
if (r < 0)
return r;
if (r == 0) {
if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i))
else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i))
else
}
break;
default:
assert_not_reached("Unexpect unit file type.");
}
return 0;
}
int unit_file_get_state(
const char *root_dir,
const char *name,
UnitFileState *ret) {
int r;
if (r < 0)
return r;
if (r < 0)
return r;
}
char **p;
int r;
if (r < 0)
return r;
return -EINVAL;
if (scope == UNIT_FILE_SYSTEM)
"/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)
"/etc/systemd/user-preset",
"/usr/local/lib/systemd/user-preset",
"/usr/lib/systemd/user-preset",
NULL);
else
return 1; /* Default is "enable" */
if (r < 0)
return r;
STRV_FOREACH(p, files) {
_cleanup_fclose_ FILE *f;
f = fopen(*p, "re");
if (!f) {
continue;
return -errno;
}
const char *parameter;
char *l;
if (isempty(l))
continue;
continue;
if (parameter) {
return 1;
}
continue;
}
if (parameter) {
return 0;
}
continue;
}
log_debug("Couldn't parse line '%s'", l);
}
}
/* Default is "enable" */
return 1;
}
static int execute_preset(
const LookupPaths *paths,
const char *config_path,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
int r;
if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) {
r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir);
if (r < 0)
return r;
} else
r = 0;
if (mode != UNIT_FILE_PRESET_DISABLE_ONLY) {
int q;
/* Returns number of symlinks that where supposed to be installed. */
q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes);
if (r >= 0) {
if (q < 0)
r = q;
else
r+= q;
}
}
return r;
}
static int preset_prepare_one(
const char *root_dir,
const char *name) {
int r;
return 0;
if (r < 0)
return r;
if (r > 0) {
r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
if (r < 0)
return r;
if (i->type == UNIT_FILE_TYPE_MASKED)
return -ESHUTDOWN;
} else
return r;
}
int unit_file_preset(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
char **i;
int r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
STRV_FOREACH(i, files) {
if (!unit_name_is_valid(*i, UNIT_NAME_ANY))
return -EINVAL;
if (r < 0)
return r;
}
return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, files, mode, force, changes, n_changes);
}
int unit_file_preset_all(
bool runtime,
const char *root_dir,
bool force,
unsigned *n_changes) {
char **i;
int r;
if (r < 0)
return r;
if (r < 0)
return r;
if (r < 0)
return r;
_cleanup_free_ char *units_dir;
if (!units_dir)
return -ENOMEM;
if (!d) {
continue;
return -errno;
}
continue;
dirent_ensure_type(d, de);
continue;
if (r < 0)
return r;
}
}
return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes);
}
static void unit_file_list_free_one(UnitFileList *f) {
if (!f)
return;
free(f);
}
UnitFileList *i;
while ((i = hashmap_steal_first(h)))
return hashmap_free(h);
}
int unit_file_get_list(
const char *root_dir,
Hashmap *h) {
char **i;
int r;
assert(h);
if (r < 0)
return r;
if (r < 0)
return r;
_cleanup_free_ char *units_dir;
if (!units_dir)
return -ENOMEM;
if (!d) {
continue;
return -errno;
}
continue;
continue;
dirent_ensure_type(d, de);
continue;
if (!f)
return -ENOMEM;
if (!f->path)
return -ENOMEM;
if (r < 0)
f->state = UNIT_FILE_BAD;
if (r < 0)
return r;
f = NULL; /* prevent cleanup */
}
}
return 0;
}
static const char* const unit_file_state_table[_UNIT_FILE_STATE_MAX] = {
[UNIT_FILE_ENABLED] = "enabled",
[UNIT_FILE_ENABLED_RUNTIME] = "enabled-runtime",
[UNIT_FILE_LINKED] = "linked",
[UNIT_FILE_LINKED_RUNTIME] = "linked-runtime",
[UNIT_FILE_MASKED] = "masked",
[UNIT_FILE_MASKED_RUNTIME] = "masked-runtime",
[UNIT_FILE_STATIC] = "static",
[UNIT_FILE_DISABLED] = "disabled",
[UNIT_FILE_INDIRECT] = "indirect",
[UNIT_FILE_BAD] = "bad",
};
static const char* const unit_file_change_type_table[_UNIT_FILE_CHANGE_TYPE_MAX] = {
[UNIT_FILE_SYMLINK] = "symlink",
[UNIT_FILE_UNLINK] = "unlink",
};
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",
};