install.c revision 0ec0deaa30d0e68430f03fa6f32affa576481d18
feb2c590898bd03d4dfd8a77c429a471776527c5Julian Kigwana/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
208f1c0d2a321a44c504ed47bbe0378ea2a9d1f2Phil Ostler This file is part of systemd.
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"
typedef enum SearchFlags {
} SearchFlags;
if (!parent)
return -ENOMEM;
char *p = NULL;
switch (scope) {
case UNIT_FILE_SYSTEM:
if (runtime)
case UNIT_FILE_GLOBAL:
if (root_dir)
return -EINVAL;
if (runtime)
case UNIT_FILE_USER:
if (root_dir)
return -EINVAL;
if (runtime)
r = user_runtime_dir(&p);
r = user_config_home(&p);
return -ENOENT;
return -ENOMEM;
*ret = p;
switch (scope) {
case UNIT_FILE_SYSTEM:
case UNIT_FILE_GLOBAL:
case UNIT_FILE_USER: {
r = user_config_home(&p);
p = mfree(p);
r = user_runtime_dir(&p);
return -EINVAL;
return -ENOTDIR;
unsigned *n_changes,
const char *path,
const char *source) {
UnitFileChange *c;
if (!changes)
return -ENOMEM;
*changes = c;
i = *n_changes;
if (!c[i].path)
return -ENOMEM;
if (source) {
if (!c[i].source) {
return -ENOMEM;
if (!changes)
for (i = 0; i < n_changes; 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 mark_symlink_for_removal(
assert(p);
n = strdup(p);
return -ENOMEM;
if (r == -EEXIST)
static int remove_marked_symlinks_fd(
int fd,
const char *path,
const char *config_path,
bool *restart,
unsigned *n_changes) {
return -errno;
rewinddir(d);
int nfd, q;
if (nfd < 0) {
r = -errno;
return -ENOMEM;
q = remove_marked_symlinks_fd(remove_symlinks_to, nfd, p, config_path, restart, changes, n_changes);
bool found;
return -ENOMEM;
if (q == -ENOENT)
found =
if (!found)
r = -errno;
*restart = true;
static int remove_marked_symlinks(
const char *config_path,
unsigned *n_changes) {
bool restart;
if (fd < 0)
return -errno;
int q, cfd;
restart = false;
if (cfd < 0)
return -errno;
q = remove_marked_symlinks_fd(remove_symlinks_to, cfd, config_path, config_path, &restart, changes, n_changes);
} while (restart);
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) {
return -errno;
int nfd, q;
if (nfd < 0) {
r = -errno;
return -ENOMEM;
return -ENOMEM;
if (q == -ENOENT)
return -ENOMEM;
dest = x;
return -ENOMEM;
b = path_equal(t, p);
*same_name_link = true;
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 -errno;
static int find_symlinks_in_scope(
const char *root_dir,
const char *name,
if (same_name_link) {
if (same_name_link_runtime) {
free(i);
return NULL;
while ((i = ordered_hashmap_steal_first(m)))
return ordered_hashmap_free(m);
assert(c);
static int install_info_add(
InstallContext *c,
const char *name,
const char *path,
assert(c);
if (!name)
return -EINVAL;
if (ret)
*ret = i;
return -ENOMEM;
if (!i->name) {
r = -ENOMEM;
goto fail;
if (path) {
if (!i->path) {
r = -ENOMEM;
goto fail;
goto fail;
if (ret)
*ret = i;
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) {
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,
assert(c);
return -errno;
return -ELOOP;
return -EISDIR;
return -ENOTTY;
if (fd < 0)
return -errno;
return -errno;
return -EISDIR;
return -ENOTTY;
return -errno;
NULL,
true, true, false, info);
static int unit_file_load_or_readlink(
InstallContext *c,
const char *path,
const char *root_dir,
if (r != -ELOOP)
const char *bn;
UnitType a, b;
return -EINVAL;
return -EINVAL;
return -EINVAL;
return -EINVAL;
return -EINVAL;
static int unit_file_search(
InstallContext *c,
const char *root_dir,
assert(c);
if (!path)
return -ENOMEM;
if (r != -ENOENT)
if (!path)
return -ENOMEM;
if (r != -ENOENT)
return -ENOENT;
static int install_info_follow(
InstallContext *c,
const char *root_dir,
assert(c);
assert(i);
return -EINVAL;
if (!i->symlink_target)
return -EINVAL;
return -EXDEV;
static int install_info_traverse(
InstallContext *c,
const char *root_dir,
assert(c);
i = start;
if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX)
return -ELOOP;
return -ELOOP;
const char *bn;
if (r != -EXDEV)
if (ret)
*ret = i;
static int install_info_discover(
InstallContext *c,
const char *root_dir,
const char *name,
assert(c);
static int install_info_symlink_alias(
const char *config_path,
bool force,
unsigned *n_changes) {
assert(i);
if (!alias_path)
return -ENOMEM;
static int install_info_symlink_wants(
const char *config_path,
char **list,
const char *suffix,
bool force,
unsigned *n_changes) {
assert(i);
if (!i->default_instance)
n = buf;
n = i->name;
r = -EINVAL;
if (!path)
return -ENOMEM;
static int install_info_symlink_link(
const char *config_path,
const char *root_dir,
bool force,
unsigned *n_changes) {
assert(i);
if (!path)
return -ENOMEM;
static int install_info_apply(
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) {
assert(c);
static int install_context_mark_for_removal(
InstallContext *c,
const char *config_path,
const char *root_dir) {
assert(c);
r = install_info_traverse(scope, c, root_dir, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL);
int unit_file_mask(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
r = -EINVAL;
if (!path)
return -ENOMEM;
int unit_file_unmask(
bool runtime,
const char *root_dir,
char **files,
unsigned *n_changes) {
return -EINVAL;
if (!path)
return -ENOMEM;
if (r == -ENOENT)
return -ENOMEM;
if (!path)
return -ENOMEM;
r = -errno;
int unit_file_link(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
char *fn;
if (!path_is_absolute(*i))
return -EINVAL;
return -EINVAL;
if (!full)
return -ENOMEM;
return -errno;
return -ELOOP;
return -EISDIR;
return -ENOTTY;
return -ENOMEM;
if (!path)
return -ENOMEM;
bool runtime,
const char *root_dir,
char **files,
const char *target,
bool force,
unsigned *n_changes) {
return -EINVAL;
return -EINVAL;
r = install_info_discover(scope, &c, root_dir, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info);
return -ESHUTDOWN;
return -ESHUTDOWN;
l = &i->wanted_by;
l = &i->required_by;
strv_free(*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) {
return -ESHUTDOWN;
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) {
return -EINVAL;
r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, root_dir);
int unit_file_reenable(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
size_t l, i;
n[i] = NULL;
const char *root_dir,
const char *name,
bool force,
unsigned *n_changes) {
const char *path;
return -EINVAL;
return -EINVAL;
return -ESHUTDOWN;
const char *root_dir,
char **name) {
r = install_info_discover(scope, &c, root_dir, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
return -ESHUTDOWN;
return -ENOMEM;
*name = n;
const char *root_dir,
const char *name,
return -EINVAL;
r = install_info_discover(scope, &c, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
if (!ret)
switch (i->type) {
case UNIT_FILE_TYPE_MASKED:
case UNIT_FILE_TYPE_REGULAR:
if (UNIT_FILE_INSTALL_INFO_HAS_RULES(i))
else if (UNIT_FILE_INSTALL_INFO_HAS_ALSO(i))
int unit_file_get_state(
const char *root_dir,
const char *name,
return -EINVAL;
"/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;
const char *parameter;
if (isempty(l))
if (parameter) {
if (parameter) {
static int execute_preset(
const char *config_path,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, root_dir);
q = install_context_apply(scope, plus, paths, config_path, root_dir, force, SEARCH_LOAD, changes, n_changes);
static int preset_prepare_one(
const char *root_dir,
const char *name) {
r = install_info_discover(scope, plus, root_dir, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i);
return -ESHUTDOWN;
int unit_file_preset(
bool runtime,
const char *root_dir,
char **files,
bool force,
unsigned *n_changes) {
return -EINVAL;
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) {
if (!units_dir)
return -ENOMEM;
return -errno;
return execute_preset(scope, &plus, &minus, &paths, config_path, root_dir, NULL, mode, force, changes, n_changes);
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) {
assert(h);
if (!units_dir)
return -ENOMEM;
return -errno;
return -ENOMEM;
if (!f->path)
return -ENOMEM;