Merge pull request #22649 from keszybz/symlink-enablement-yet-again-punish-me-harder
Fixups to the unit enablement logic
This commit is contained in:
+38
-56
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user