Merge pull request #22649 from keszybz/symlink-enablement-yet-again-punish-me-harder

Fixups to the unit enablement logic
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2022-03-29 21:10:03 +02:00
committed by GitHub
78 changed files with 2503 additions and 1372 deletions
+38 -56
View File
@@ -16,9 +16,8 @@ static int parse_env_file_internal(
FILE *f,
const char *fname,
int (*push) (const char *filename, unsigned line,
const char *key, char *value, void *userdata, int *n_pushed),
void *userdata,
int *n_pushed) {
const char *key, char *value, void *userdata),
void *userdata) {
size_t n_key = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX;
_cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL;
@@ -99,7 +98,7 @@ static int parse_env_file_internal(
if (last_key_whitespace != SIZE_MAX)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
r = push(fname, line, key, value, userdata);
if (r < 0)
return r;
@@ -142,7 +141,7 @@ static int parse_env_file_internal(
if (last_key_whitespace != SIZE_MAX)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
r = push(fname, line, key, value, userdata);
if (r < 0)
return r;
@@ -261,7 +260,7 @@ static int parse_env_file_internal(
if (last_key_whitespace != SIZE_MAX)
key[last_key_whitespace] = 0;
r = push(fname, line, key, value, userdata, n_pushed);
r = push(fname, line, key, value, userdata);
if (r < 0)
return r;
@@ -299,8 +298,7 @@ static int check_utf8ness_and_warn(
static int parse_env_file_push(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
void *userdata) {
const char *k;
va_list aq, *ap = userdata;
@@ -322,9 +320,6 @@ static int parse_env_file_push(
free(*v);
*v = value;
if (n_pushed)
(*n_pushed)++;
return 1;
}
}
@@ -340,16 +335,13 @@ int parse_env_filev(
const char *fname,
va_list ap) {
int r, n_pushed = 0;
int r;
va_list aq;
va_copy(aq, ap);
r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed);
r = parse_env_file_internal(f, fname, parse_env_file_push, &aq);
va_end(aq);
if (r < 0)
return r;
return n_pushed;
return r;
}
int parse_env_file_sentinel(
@@ -370,8 +362,7 @@ int parse_env_file_sentinel(
static int load_env_file_push(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
void *userdata) {
char ***m = userdata;
char *p;
int r;
@@ -388,78 +379,69 @@ static int load_env_file_push(
if (r < 0)
return r;
if (n_pushed)
(*n_pushed)++;
free(value);
return 0;
}
int load_env_file(FILE *f, const char *fname, char ***rl) {
char **m = NULL;
_cleanup_strv_free_ char **m = NULL;
int r;
r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL);
if (r < 0) {
strv_free(m);
r = parse_env_file_internal(f, fname, load_env_file_push, &m);
if (r < 0)
return r;
}
*rl = m;
*rl = TAKE_PTR(m);
return 0;
}
static int load_env_file_push_pairs(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
char ***m = userdata;
void *userdata) {
char ***m = ASSERT_PTR(userdata);
int r;
r = check_utf8ness_and_warn(filename, line, key, value);
if (r < 0)
return r;
/* Check if the key is present */
for (char **t = *m; t && *t; t += 2)
if (streq(t[0], key)) {
if (value)
return free_and_replace(t[1], value);
else
return free_and_strdup(t+1, "");
}
r = strv_extend(m, key);
if (r < 0)
return -ENOMEM;
return r;
if (!value) {
r = strv_extend(m, "");
if (r < 0)
return -ENOMEM;
} else {
r = strv_push(m, value);
if (r < 0)
return r;
}
if (n_pushed)
(*n_pushed)++;
return 0;
if (value)
return strv_push(m, value);
else
return strv_extend(m, "");
}
int load_env_file_pairs(FILE *f, const char *fname, char ***rl) {
char **m = NULL;
_cleanup_strv_free_ char **m = NULL;
int r;
r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL);
if (r < 0) {
strv_free(m);
r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m);
if (r < 0)
return r;
}
*rl = m;
*rl = TAKE_PTR(m);
return 0;
}
static int merge_env_file_push(
const char *filename, unsigned line,
const char *key, char *value,
void *userdata,
int *n_pushed) {
void *userdata) {
char ***env = userdata;
char *expanded_value;
@@ -488,7 +470,7 @@ static int merge_env_file_push(
log_debug("%s:%u: setting %s=%s", filename, line, key, value);
return load_env_file_push(filename, line, key, value, env, n_pushed);
return load_env_file_push(filename, line, key, value, env);
}
int merge_env_file(
@@ -500,7 +482,7 @@ int merge_env_file(
* plus "extended" substitutions, unlike other exported parsing functions.
*/
return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL);
return parse_env_file_internal(f, fname, merge_env_file_push, env);
}
static void write_env_var(FILE *f, const char *v) {
+11 -5
View File
@@ -170,13 +170,19 @@ int open_extension_release(const char *root, const char *extension, char **ret_p
}
}
} else {
FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") {
r = chase_symlinks(p, root, CHASE_PREFIX_ROOT,
const char *var = secure_getenv("SYSTEMD_OS_RELEASE");
if (var)
r = chase_symlinks(var, root, 0,
ret_path ? &q : NULL,
ret_fd ? &fd : NULL);
if (r != -ENOENT)
break;
}
else
FOREACH_STRING(path, "/etc/os-release", "/usr/lib/os-release") {
r = chase_symlinks(path, root, CHASE_PREFIX_ROOT,
ret_path ? &q : NULL,
ret_fd ? &fd : NULL);
if (r != -ENOENT)
break;
}
}
if (r < 0)
return r;
+71 -61
View File
@@ -232,7 +232,7 @@ bool path_is_user_config_dir(const char *path) {
}
static int acquire_generator_dirs(
UnitFileScope scope,
LookupScope scope,
const char *tempdir,
char **generator,
char **generator_early,
@@ -244,17 +244,17 @@ static int acquire_generator_dirs(
assert(generator);
assert(generator_early);
assert(generator_late);
assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
assert(IN_SET(scope, LOOKUP_SCOPE_SYSTEM, LOOKUP_SCOPE_USER, LOOKUP_SCOPE_GLOBAL));
if (scope == UNIT_FILE_GLOBAL)
if (scope == LOOKUP_SCOPE_GLOBAL)
return -EOPNOTSUPP;
if (tempdir)
prefix = tempdir;
else if (scope == UNIT_FILE_SYSTEM)
else if (scope == LOOKUP_SCOPE_SYSTEM)
prefix = "/run/systemd";
else {
/* UNIT_FILE_USER */
/* LOOKUP_SCOPE_USER */
const char *e;
e = getenv("XDG_RUNTIME_DIR");
@@ -288,21 +288,21 @@ static int acquire_generator_dirs(
}
static int acquire_transient_dir(
UnitFileScope scope,
LookupScope scope,
const char *tempdir,
char **ret) {
char *transient;
assert(ret);
assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL));
assert(IN_SET(scope, LOOKUP_SCOPE_SYSTEM, LOOKUP_SCOPE_USER, LOOKUP_SCOPE_GLOBAL));
if (scope == UNIT_FILE_GLOBAL)
if (scope == LOOKUP_SCOPE_GLOBAL)
return -EOPNOTSUPP;
if (tempdir)
transient = path_join(tempdir, "transient");
else if (scope == UNIT_FILE_SYSTEM)
else if (scope == LOOKUP_SCOPE_SYSTEM)
transient = strdup("/run/systemd/transient");
else
return xdg_user_runtime_dir(ret, "/systemd/transient");
@@ -313,7 +313,7 @@ static int acquire_transient_dir(
return 0;
}
static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) {
static int acquire_config_dirs(LookupScope scope, char **persistent, char **runtime) {
_cleanup_free_ char *a = NULL, *b = NULL;
int r;
@@ -322,17 +322,17 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru
switch (scope) {
case UNIT_FILE_SYSTEM:
case LOOKUP_SCOPE_SYSTEM:
a = strdup(SYSTEM_CONFIG_UNIT_DIR);
b = strdup("/run/systemd/system");
break;
case UNIT_FILE_GLOBAL:
case LOOKUP_SCOPE_GLOBAL:
a = strdup(USER_CONFIG_UNIT_DIR);
b = strdup("/run/systemd/user");
break;
case UNIT_FILE_USER:
case LOOKUP_SCOPE_USER:
r = xdg_user_config_dir(&a, "/systemd/user");
if (r < 0 && r != -ENXIO)
return r;
@@ -364,7 +364,7 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru
return 0;
}
static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) {
static int acquire_control_dirs(LookupScope scope, char **persistent, char **runtime) {
_cleanup_free_ char *a = NULL;
int r;
@@ -373,7 +373,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r
switch (scope) {
case UNIT_FILE_SYSTEM: {
case LOOKUP_SCOPE_SYSTEM: {
_cleanup_free_ char *b = NULL;
a = strdup("/etc/systemd/system.control");
@@ -389,7 +389,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r
break;
}
case UNIT_FILE_USER:
case LOOKUP_SCOPE_USER:
r = xdg_user_config_dir(&a, "/systemd/user.control");
if (r < 0 && r != -ENXIO)
return r;
@@ -406,7 +406,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r
break;
case UNIT_FILE_GLOBAL:
case LOOKUP_SCOPE_GLOBAL:
return -EOPNOTSUPP;
default:
@@ -419,7 +419,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r
}
static int acquire_attached_dirs(
UnitFileScope scope,
LookupScope scope,
char **ret_persistent,
char **ret_runtime) {
@@ -429,7 +429,7 @@ static int acquire_attached_dirs(
assert(ret_runtime);
/* Portable services are not available to regular users for now. */
if (scope != UNIT_FILE_SYSTEM)
if (scope != LOOKUP_SCOPE_SYSTEM)
return -EOPNOTSUPP;
a = strdup("/etc/systemd/system.attached");
@@ -508,8 +508,8 @@ static int get_paths_from_environ(const char *var, char ***paths, bool *append)
}
int lookup_paths_init(
LookupPaths *p,
UnitFileScope scope,
LookupPaths *lp,
LookupScope scope,
LookupPathsFlags flags,
const char *root_dir) {
@@ -526,16 +526,16 @@ int lookup_paths_init(
_cleanup_strv_free_ char **paths = NULL;
int r;
assert(p);
assert(lp);
assert(scope >= 0);
assert(scope < _UNIT_FILE_SCOPE_MAX);
assert(scope < _LOOKUP_SCOPE_MAX);
#if HAVE_SPLIT_USR
flags |= LOOKUP_PATHS_SPLIT_USR;
#endif
if (!empty_or_root(root_dir)) {
if (scope == UNIT_FILE_USER)
if (scope == LOOKUP_SCOPE_USER)
return -EINVAL;
r = is_dir(root_dir, true);
@@ -560,8 +560,8 @@ int lookup_paths_init(
if (r < 0)
return r;
if (scope == UNIT_FILE_USER) {
r = acquire_config_dirs(UNIT_FILE_GLOBAL, &global_persistent_config, &global_runtime_config);
if (scope == LOOKUP_SCOPE_USER) {
r = acquire_config_dirs(LOOKUP_SCOPE_GLOBAL, &global_persistent_config, &global_runtime_config);
if (r < 0)
return r;
}
@@ -606,7 +606,7 @@ int lookup_paths_init(
switch (scope) {
case UNIT_FILE_SYSTEM:
case LOOKUP_SCOPE_SYSTEM:
add = strv_new(
/* If you modify this you also want to modify
* systemdsystemunitpath= in systemd.pc.in! */
@@ -629,7 +629,7 @@ int lookup_paths_init(
STRV_IFNOTNULL(generator_late));
break;
case UNIT_FILE_GLOBAL:
case LOOKUP_SCOPE_GLOBAL:
add = strv_new(
/* If you modify this you also want to modify
* systemduserunitpath= in systemd.pc.in, and
@@ -652,7 +652,7 @@ int lookup_paths_init(
STRV_IFNOTNULL(generator_late));
break;
case UNIT_FILE_USER:
case LOOKUP_SCOPE_USER:
add = user_dirs(persistent_config, runtime_config,
global_persistent_config, global_runtime_config,
generator, generator_early, generator_late,
@@ -716,7 +716,7 @@ int lookup_paths_init(
if (r < 0)
return -ENOMEM;
*p = (LookupPaths) {
*lp = (LookupPaths) {
.search_path = strv_uniq(TAKE_PTR(paths)),
.persistent_config = TAKE_PTR(persistent_config),
@@ -741,46 +741,56 @@ int lookup_paths_init(
return 0;
}
void lookup_paths_free(LookupPaths *p) {
if (!p)
return;
int lookup_paths_init_or_warn(LookupPaths *lp, LookupScope scope, LookupPathsFlags flags, const char *root_dir) {
int r;
p->search_path = strv_free(p->search_path);
p->persistent_config = mfree(p->persistent_config);
p->runtime_config = mfree(p->runtime_config);
p->persistent_attached = mfree(p->persistent_attached);
p->runtime_attached = mfree(p->runtime_attached);
p->generator = mfree(p->generator);
p->generator_early = mfree(p->generator_early);
p->generator_late = mfree(p->generator_late);
p->transient = mfree(p->transient);
p->persistent_control = mfree(p->persistent_control);
p->runtime_control = mfree(p->runtime_control);
p->root_dir = mfree(p->root_dir);
p->temporary_dir = mfree(p->temporary_dir);
r = lookup_paths_init(lp, scope, flags, root_dir);
if (r < 0)
return log_error_errno(r, "Failed to initialize unit search paths%s%s: %m",
isempty(root_dir) ? "" : " for root directory ", strempty(root_dir));
return r;
}
void lookup_paths_log(LookupPaths *p) {
assert(p);
void lookup_paths_free(LookupPaths *lp) {
if (!lp)
return;
if (strv_isempty(p->search_path)) {
lp->search_path = strv_free(lp->search_path);
lp->persistent_config = mfree(lp->persistent_config);
lp->runtime_config = mfree(lp->runtime_config);
lp->persistent_attached = mfree(lp->persistent_attached);
lp->runtime_attached = mfree(lp->runtime_attached);
lp->generator = mfree(lp->generator);
lp->generator_early = mfree(lp->generator_early);
lp->generator_late = mfree(lp->generator_late);
lp->transient = mfree(lp->transient);
lp->persistent_control = mfree(lp->persistent_control);
lp->runtime_control = mfree(lp->runtime_control);
lp->root_dir = mfree(lp->root_dir);
lp->temporary_dir = mfree(lp->temporary_dir);
}
void lookup_paths_log(LookupPaths *lp) {
assert(lp);
if (strv_isempty(lp->search_path)) {
log_debug("Ignoring unit files.");
p->search_path = strv_free(p->search_path);
lp->search_path = strv_free(lp->search_path);
} else {
_cleanup_free_ char *t = NULL;
t = strv_join(p->search_path, "\n\t");
t = strv_join(lp->search_path, "\n\t");
log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t));
}
}
char **generator_binary_paths(UnitFileScope scope) {
char **generator_binary_paths(LookupScope scope) {
bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */
_cleanup_strv_free_ char **paths = NULL;
int r;
@@ -795,15 +805,15 @@ char **generator_binary_paths(UnitFileScope scope) {
switch (scope) {
case UNIT_FILE_SYSTEM:
case LOOKUP_SCOPE_SYSTEM:
add = strv_new("/run/systemd/system-generators",
"/etc/systemd/system-generators",
"/usr/local/lib/systemd/system-generators",
SYSTEM_GENERATOR_DIR);
break;
case UNIT_FILE_GLOBAL:
case UNIT_FILE_USER:
case LOOKUP_SCOPE_GLOBAL:
case LOOKUP_SCOPE_USER:
add = strv_new("/run/systemd/user-generators",
"/etc/systemd/user-generators",
"/usr/local/lib/systemd/user-generators",
+13 -7
View File
@@ -3,10 +3,7 @@
#include <stdbool.h>
typedef struct LookupPaths LookupPaths;
#include "def.h"
#include "unit-file.h"
#include "macro.h"
typedef enum LookupPathsFlags {
@@ -15,7 +12,15 @@ typedef enum LookupPathsFlags {
LOOKUP_PATHS_SPLIT_USR = 1 << 2,
} LookupPathsFlags;
struct LookupPaths {
typedef enum LookupScope {
LOOKUP_SCOPE_SYSTEM,
LOOKUP_SCOPE_GLOBAL,
LOOKUP_SCOPE_USER,
_LOOKUP_SCOPE_MAX,
_LOOKUP_SCOPE_INVALID = -EINVAL,
} LookupScope;
typedef struct LookupPaths {
/* Where we look for unit files. This includes the individual special paths below, but also any vendor
* supplied, static unit file paths. */
char **search_path;
@@ -52,9 +57,10 @@ struct LookupPaths {
/* A temporary directory when running in test mode, to be nuked */
char *temporary_dir;
};
} LookupPaths;
int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir);
int lookup_paths_init(LookupPaths *lp, LookupScope scope, LookupPathsFlags flags, const char *root_dir);
int lookup_paths_init_or_warn(LookupPaths *lp, LookupScope scope, LookupPathsFlags flags, const char *root_dir);
int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs);
int xdg_user_runtime_dir(char **ret, const char *suffix);
@@ -67,7 +73,7 @@ bool path_is_user_config_dir(const char *path);
void lookup_paths_log(LookupPaths *p);
void lookup_paths_free(LookupPaths *p);
char **generator_binary_paths(UnitFileScope scope);
char **generator_binary_paths(LookupScope scope);
char **env_generator_binary_paths(bool is_system);
#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network"))
+10 -5
View File
@@ -127,17 +127,22 @@ bool null_or_empty(struct stat *st) {
return false;
}
int null_or_empty_path(const char *fn) {
int null_or_empty_path_with_root(const char *fn, const char *root) {
struct stat st;
int r;
assert(fn);
/* If we have the path, let's do an easy text comparison first. */
if (path_equal(fn, "/dev/null"))
/* A symlink to /dev/null or an empty file?
* When looking under root_dir, we can't expect /dev/ to be mounted,
* so let's see if the path is a (possibly dangling) symlink to /dev/null. */
if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null"))
return true;
if (stat(fn, &st) < 0)
return -errno;
r = chase_symlinks_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st, NULL);
if (r < 0)
return r;
return null_or_empty(&st);
}
+5 -1
View File
@@ -31,9 +31,13 @@ static inline int dir_is_populated(const char *path) {
}
bool null_or_empty(struct stat *st) _pure_;
int null_or_empty_path(const char *fn);
int null_or_empty_path_with_root(const char *fn, const char *root);
int null_or_empty_fd(int fd);
static inline int null_or_empty_path(const char *fn) {
return null_or_empty_path_with_root(fn, NULL);
}
int path_is_read_only_fs(const char *path);
int files_same(const char *filea, const char *fileb, int flags);
+154 -87
View File
@@ -69,7 +69,7 @@ int unit_symlink_name_compatible(const char *symlink, const char *target, bool i
return 0;
}
int unit_validate_alias_symlink_and_warn(const char *filename, const char *target) {
int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target) {
const char *src, *dst;
_cleanup_free_ char *src_instance = NULL, *dst_instance = NULL;
UnitType src_unit_type, dst_unit_type;
@@ -82,7 +82,8 @@ int unit_validate_alias_symlink_and_warn(const char *filename, const char *targe
*
* -EINVAL is returned if the something is wrong with the source filename or the source unit type is
* not allowed to symlink,
* -EXDEV if the target filename is not a valid unit name or doesn't match the source.
* -EXDEV if the target filename is not a valid unit name or doesn't match the source,
* -ELOOP for an alias to self.
*/
src = basename(filename);
@@ -92,51 +93,56 @@ int unit_validate_alias_symlink_and_warn(const char *filename, const char *targe
src_name_type = unit_name_to_instance(src, &src_instance);
if (src_name_type < 0)
return log_notice_errno(src_name_type,
"%s: not a valid unit name \"%s\": %m", filename, src);
return log_full_errno(log_level, src_name_type,
"%s: not a valid unit name \"%s\": %m", filename, src);
src_unit_type = unit_name_to_type(src);
assert(src_unit_type >= 0); /* unit_name_to_instance() checked the suffix already */
if (!unit_type_may_alias(src_unit_type))
return log_notice_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: symlinks are not allowed for units of this type, rejecting.",
filename);
return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL),
"%s: symlinks are not allowed for units of this type, rejecting.",
filename);
if (src_name_type != UNIT_NAME_PLAIN &&
!unit_type_may_template(src_unit_type))
return log_notice_errno(SYNTHETIC_ERRNO(EINVAL),
"%s: templates not allowed for %s units, rejecting.",
filename, unit_type_to_string(src_unit_type));
return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL),
"%s: templates not allowed for %s units, rejecting.",
filename, unit_type_to_string(src_unit_type));
/* dst checks */
if (streq(src, dst))
return log_debug_errno(SYNTHETIC_ERRNO(ELOOP),
"%s: unit self-alias: %s → %s, ignoring.",
filename, src, dst);
dst_name_type = unit_name_to_instance(dst, &dst_instance);
if (dst_name_type < 0)
return log_notice_errno(dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type,
"%s points to \"%s\" which is not a valid unit name: %m",
filename, dst);
return log_full_errno(log_level, dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type,
"%s points to \"%s\" which is not a valid unit name: %m",
filename, dst);
if (!(dst_name_type == src_name_type ||
(src_name_type == UNIT_NAME_INSTANCE && dst_name_type == UNIT_NAME_TEMPLATE)))
return log_notice_errno(SYNTHETIC_ERRNO(EXDEV),
"%s: symlink target name type \"%s\" does not match source, rejecting.",
filename, dst);
return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV),
"%s: symlink target name type \"%s\" does not match source, rejecting.",
filename, dst);
if (dst_name_type == UNIT_NAME_INSTANCE) {
assert(src_instance);
assert(dst_instance);
if (!streq(src_instance, dst_instance))
return log_notice_errno(SYNTHETIC_ERRNO(EXDEV),
"%s: unit symlink target \"%s\" instance name doesn't match, rejecting.",
filename, dst);
return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV),
"%s: unit symlink target \"%s\" instance name doesn't match, rejecting.",
filename, dst);
}
dst_unit_type = unit_name_to_type(dst);
if (dst_unit_type != src_unit_type)
return log_notice_errno(SYNTHETIC_ERRNO(EXDEV),
"%s: symlink target \"%s\" has incompatible suffix, rejecting.",
filename, dst);
return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV),
"%s: symlink target \"%s\" has incompatible suffix, rejecting.",
filename, dst);
return 0;
}
@@ -259,6 +265,110 @@ static int directory_name_is_valid(const char *name) {
return false;
}
int unit_file_resolve_symlink(
const char *root_dir,
char **search_path,
const char *dir,
int dirfd,
const char *filename,
bool resolve_destination_target,
char **ret_destination) {
_cleanup_free_ char *target = NULL, *simplified = NULL, *dst = NULL, *_dir = NULL, *_filename = NULL;
int r;
/* This can be called with either dir+dirfd valid and filename just a name,
* or !dir && dirfd==AT_FDCWD, and filename being a full path.
*
* If resolve_destination_target is true, an absolute path will be returned.
* If not, an absolute path is returned for linked unit files, and a relative
* path otherwise.
*
* Returns an error, false if this is an alias, true if it's a linked unit file. */
assert(filename);
assert(ret_destination);
assert(dir || path_is_absolute(filename));
assert(dirfd >= 0 || dirfd == AT_FDCWD);
r = readlinkat_malloc(dirfd, filename, &target);
if (r < 0)
return log_warning_errno(r, "Failed to read symlink %s%s%s: %m",
dir, dir ? "/" : "", filename);
if (!dir) {
r = path_extract_directory(filename, &_dir);
if (r < 0)
return r;
dir = _dir;
r = path_extract_filename(filename, &_filename);
if (r < 0)
return r;
if (r == O_DIRECTORY)
return log_warning_errno(SYNTHETIC_ERRNO(EISDIR),
"Unexpected path to a directory \"%s\", refusing.", filename);
filename = _filename;
}
bool is_abs = path_is_absolute(target);
if (root_dir || !is_abs) {
char *target_abs = path_join(is_abs ? root_dir : dir, target);
if (!target_abs)
return log_oom();
free_and_replace(target, target_abs);
}
/* Get rid of "." and ".." components in target path */
r = chase_symlinks(target, root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL);
if (r < 0)
return log_warning_errno(r, "Failed to resolve symlink %s/%s pointing to %s: %m",
dir, filename, target);
assert(path_is_absolute(simplified));
/* Check if the symlink remain inside of of our search path.
* If yes, it is an alias. Verify that it is valid.
*
* If no, then this is a linked unit file or mask, and we don't care about the target name
* when loading units, and we return the link *source* (resolve_destination_target == false);
* When this is called for installation purposes, we want the final destination,
* so we return the *target*.
*/
const char *tail = path_startswith_strv(simplified, search_path);
if (tail) { /* An alias */
_cleanup_free_ char *target_name = NULL;
r = path_extract_filename(simplified, &target_name);
if (r < 0)
return r;
r = unit_validate_alias_symlink_or_warn(LOG_NOTICE, filename, simplified);
if (r < 0)
return r;
if (is_path(tail))
log_warning("Suspicious symlink %s/%s→%s, treating as alias.",
dir, filename, simplified);
dst = resolve_destination_target ? TAKE_PTR(simplified) : TAKE_PTR(target_name);
} else {
log_debug("Linked unit file: %s/%s → %s", dir, filename, simplified);
if (resolve_destination_target)
dst = TAKE_PTR(simplified);
else {
dst = path_join(dir, filename);
if (!dst)
return log_oom();
}
}
*ret_destination = TAKE_PTR(dst);
return !tail; /* true if linked unit file */
}
int unit_file_build_name_map(
const LookupPaths *lp,
uint64_t *cache_timestamp_hash,
@@ -308,10 +418,9 @@ int unit_file_build_name_map(
FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) {
_unused_ _cleanup_free_ char *_filename_free = NULL;
_cleanup_free_ char *simplified = NULL;
bool symlink_to_dir = false;
const char *dst = NULL;
char *filename;
_cleanup_free_ char *dst = NULL;
bool symlink_to_dir = false;
/* We only care about valid units and dirs with certain suffixes, let's ignore the
* rest. */
@@ -395,77 +504,35 @@ int unit_file_build_name_map(
/* We don't explicitly check for alias loops here. unit_ids_map_get() which
* limits the number of hops should be used to access the map. */
_cleanup_free_ char *target = NULL;
r = readlinkat_malloc(dirfd(d), de->d_name, &target);
if (r < 0) {
log_warning_errno(r, "Failed to read symlink %s/%s, ignoring: %m",
*dir, de->d_name);
r = unit_file_resolve_symlink(lp->root_dir, lp->search_path,
*dir, dirfd(d), de->d_name,
/* resolve_destination_target= */ false,
&dst);
if (r == -ENOMEM)
return r;
if (r < 0) /* we ignore other errors here */
continue;
}
const bool is_abs = path_is_absolute(target);
if (lp->root_dir || !is_abs) {
char *target_abs = path_join(is_abs ? lp->root_dir : *dir, target);
if (!target_abs)
return log_oom();
free_and_replace(target, target_abs);
}
/* Get rid of "." and ".." components in target path */
r = chase_symlinks(target, lp->root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL);
if (r < 0) {
log_warning_errno(r, "Failed to resolve symlink %s pointing to %s, ignoring: %m",
filename, target);
continue;
}
/* Check if the symlink goes outside of our search path.
* If yes, it's a linked unit file or mask, and we don't care about the target name.
* Let's just store the link source directly.
* If not, let's verify that it's a good symlink. */
char *tail = path_startswith_strv(simplified, lp->search_path);
if (!tail) {
log_debug("%s: linked unit file: %s → %s",
__func__, filename, simplified);
dst = filename;
} else {
bool self_alias;
dst = basename(simplified);
self_alias = streq(dst, de->d_name);
if (is_path(tail))
log_full(self_alias ? LOG_DEBUG : LOG_WARNING,
"Suspicious symlink %s→%s, treating as alias.",
filename, simplified);
r = unit_validate_alias_symlink_and_warn(filename, simplified);
if (r < 0)
continue;
if (self_alias) {
/* A self-alias that has no effect */
log_debug("%s: self-alias: %s/%s → %s, ignoring.",
__func__, *dir, de->d_name, dst);
continue;
}
log_debug("%s: alias: %s/%s → %s", __func__, *dir, de->d_name, dst);
}
} else {
dst = filename;
dst = TAKE_PTR(_filename_free); /* Grab the copy we made previously, if available. */
if (!dst) {
dst = strdup(filename);
if (!dst)
return log_oom();
}
log_debug("%s: normal unit file: %s", __func__, dst);
}
r = hashmap_put_strdup(&ids, de->d_name, dst);
_cleanup_free_ char *key = strdup(de->d_name);
if (!key)
return log_oom();
r = hashmap_ensure_put(&ids, &string_hash_ops_free_free, key, dst);
if (r < 0)
return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m",
de->d_name, dst);
key = dst = NULL;
}
}
+12 -11
View File
@@ -4,12 +4,11 @@
#include <stdbool.h>
#include "hashmap.h"
#include "path-lookup.h"
#include "time-util.h"
#include "unit-name.h"
typedef enum UnitFileState UnitFileState;
typedef enum UnitFileScope UnitFileScope;
typedef struct LookupPaths LookupPaths;
enum UnitFileState {
UNIT_FILE_ENABLED,
@@ -29,21 +28,23 @@ enum UnitFileState {
_UNIT_FILE_STATE_INVALID = -EINVAL,
};
enum UnitFileScope {
UNIT_FILE_SYSTEM,
UNIT_FILE_GLOBAL,
UNIT_FILE_USER,
_UNIT_FILE_SCOPE_MAX,
_UNIT_FILE_SCOPE_INVALID = -EINVAL,
};
bool unit_type_may_alias(UnitType type) _const_;
bool unit_type_may_template(UnitType type) _const_;
int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation);
int unit_validate_alias_symlink_and_warn(const char *filename, const char *target);
int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target);
bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new);
int unit_file_resolve_symlink(
const char *root_dir,
char **search_path,
const char *dir,
int dirfd,
const char *filename,
bool resolve_destination_target,
char **ret_destination);
int unit_file_build_name_map(
const LookupPaths *lp,
uint64_t *cache_timestamp_hash,