mirror of
https://github.com/Dasharo/systemd.git
synced 2026-03-06 15:02:31 -08:00
tmpfiles: allow admin/runtime overrides to runtime config
This is very similar to d16a1c1bb6. For tmpfiles this is much less useful
compared to sysusers, but let's add this anyway for consistency.
This commit is contained in:
@@ -84,12 +84,16 @@
|
||||
</para>
|
||||
|
||||
<para>If invoked with no arguments, it applies all directives from all configuration
|
||||
files. If one or more absolute filenames are passed on the command line, only the
|
||||
directives in these files are applied. If <literal>-</literal> is specified instead
|
||||
of a filename, directives are read from standard input. If only the basename of a
|
||||
configuration file is specified, all configuration directories as specified in
|
||||
files. When invoked with <option>--replace=<replaceable>PATH</replaceable></option>,
|
||||
arguments specified on the command line are used instead of the configuration file
|
||||
<replaceable>PATH</replaceable>. Otherwise, if one or more absolute filenames are
|
||||
passed on the command line, only the directives in these files are applied. If
|
||||
<literal>-</literal> is specified instead of a filename, directives are read from
|
||||
standard input. If only the basename of a configuration file is specified, all
|
||||
configuration directories as specified in
|
||||
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
are searched for a matching file.</para>
|
||||
are searched for a matching file and the file found that has the highest priority is
|
||||
executed.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
@@ -176,6 +180,22 @@
|
||||
consulted.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--replace=<replaceable>PATH</replaceable></option></term>
|
||||
<listitem><para>When this option is given, one ore more positional arguments
|
||||
must be specified. All configuration files found in the directories listed in
|
||||
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
will be read, and the configuration given on the command line will be
|
||||
handled instead of and with the same priority as the configuration file
|
||||
<replaceable>PATH</replaceable>.</para>
|
||||
|
||||
<para>This option is intended to be used when package installation scripts
|
||||
are running and files belonging to that package are not yet available on
|
||||
disk, so their contents must be given on the command line, but the admin
|
||||
configuration might already exist and should be given higher priority.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
|
||||
@@ -154,7 +154,7 @@ static int conf_files_list_strv_internal(char ***strv, const char *suffix, const
|
||||
return 0;
|
||||
}
|
||||
|
||||
int conf_files_insert(char ***strv, const char *root, const char *dirs, const char *path) {
|
||||
int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path) {
|
||||
/* Insert a path into strv, at the place honouring the usual sorting rules:
|
||||
* - we first compare by the basename
|
||||
* - and then we compare by dirname, allowing just one file with the given
|
||||
@@ -174,22 +174,22 @@ int conf_files_insert(char ***strv, const char *root, const char *dirs, const ch
|
||||
|
||||
c = base_cmp(*strv + i, &path);
|
||||
if (c == 0) {
|
||||
const char *dir;
|
||||
char **dir;
|
||||
|
||||
/* Oh, we found our spot and it already contains something. */
|
||||
NULSTR_FOREACH(dir, dirs) {
|
||||
STRV_FOREACH(dir, dirs) {
|
||||
char *p1, *p2;
|
||||
|
||||
p1 = path_startswith((*strv)[i], root);
|
||||
if (p1)
|
||||
/* Skip "/" in dir, because p1 is without "/" too */
|
||||
p1 = path_startswith(p1, dir + 1);
|
||||
/* Skip "/" in *dir, because p1 is without "/" too */
|
||||
p1 = path_startswith(p1, *dir + 1);
|
||||
if (p1)
|
||||
/* Existing entry with higher priority
|
||||
* or same priority, no need to do anything. */
|
||||
return 0;
|
||||
|
||||
p2 = path_startswith(path, dir);
|
||||
p2 = path_startswith(path, *dir);
|
||||
if (p2) {
|
||||
/* Our new entry has higher priority */
|
||||
t = path_join(root, path, NULL);
|
||||
@@ -218,6 +218,18 @@ int conf_files_insert(char ***strv, const char *root, const char *dirs, const ch
|
||||
return r;
|
||||
}
|
||||
|
||||
int conf_files_insert_nulstr(char ***strv, const char *root, const char *dirs, const char *path) {
|
||||
_cleanup_strv_free_ char **d = NULL;
|
||||
|
||||
assert(strv);
|
||||
|
||||
d = strv_split_nulstr(dirs);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
return conf_files_insert(strv, root, d, path);
|
||||
}
|
||||
|
||||
int conf_files_list_strv(char ***strv, const char *suffix, const char *root, unsigned flags, const char* const* dirs) {
|
||||
_cleanup_strv_free_ char **copy = NULL;
|
||||
|
||||
@@ -246,14 +258,14 @@ int conf_files_list(char ***strv, const char *suffix, const char *root, unsigned
|
||||
return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
|
||||
}
|
||||
|
||||
int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, unsigned flags, const char *d) {
|
||||
_cleanup_strv_free_ char **dirs = NULL;
|
||||
int conf_files_list_nulstr(char ***strv, const char *suffix, const char *root, unsigned flags, const char *dirs) {
|
||||
_cleanup_strv_free_ char **d = NULL;
|
||||
|
||||
assert(strv);
|
||||
|
||||
dirs = strv_split_nulstr(d);
|
||||
if (!dirs)
|
||||
d = strv_split_nulstr(dirs);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
return conf_files_list_strv_internal(strv, suffix, root, flags, dirs);
|
||||
return conf_files_list_strv_internal(strv, suffix, root, flags, d);
|
||||
}
|
||||
|
||||
@@ -28,4 +28,5 @@ enum {
|
||||
int conf_files_list(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dir, ...);
|
||||
int conf_files_list_strv(char ***ret, const char *suffix, const char *root, unsigned flags, const char* const* dirs);
|
||||
int conf_files_list_nulstr(char ***ret, const char *suffix, const char *root, unsigned flags, const char *dirs);
|
||||
int conf_files_insert(char ***strv, const char *root, const char *dirs, const char *path);
|
||||
int conf_files_insert(char ***strv, const char *root, char **dirs, const char *path);
|
||||
int conf_files_insert_nulstr(char ***strv, const char *root, const char *dirs, const char *path);
|
||||
|
||||
@@ -1855,7 +1855,7 @@ static int read_config_files(const char* dirs, char **args) {
|
||||
return log_error_errno(r, "Failed to enumerate sysusers.d files: %m");
|
||||
|
||||
if (arg_replace) {
|
||||
r = conf_files_insert(&files, arg_root, dirs, arg_replace);
|
||||
r = conf_files_insert_nulstr(&files, arg_root, dirs, arg_replace);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extend sysusers.d file list: %m");
|
||||
|
||||
|
||||
@@ -171,6 +171,7 @@ static bool arg_boot = false;
|
||||
static char **arg_include_prefixes = NULL;
|
||||
static char **arg_exclude_prefixes = NULL;
|
||||
static char *arg_root = NULL;
|
||||
static char *arg_replace = NULL;
|
||||
|
||||
#define MAX_DEPTH 256
|
||||
|
||||
@@ -2364,6 +2365,7 @@ static void help(void) {
|
||||
" --prefix=PATH Only apply rules with the specified prefix\n"
|
||||
" --exclude-prefix=PATH Ignore rules with the specified prefix\n"
|
||||
" --root=PATH Operate on an alternate filesystem root\n"
|
||||
" --replace=PATH Treat arguments as replacement for PATH\n"
|
||||
, program_invocation_short_name);
|
||||
}
|
||||
|
||||
@@ -2379,6 +2381,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_PREFIX,
|
||||
ARG_EXCLUDE_PREFIX,
|
||||
ARG_ROOT,
|
||||
ARG_REPLACE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -2392,6 +2395,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "prefix", required_argument, NULL, ARG_PREFIX },
|
||||
{ "exclude-prefix", required_argument, NULL, ARG_EXCLUDE_PREFIX },
|
||||
{ "root", required_argument, NULL, ARG_ROOT },
|
||||
{ "replace", required_argument, NULL, ARG_REPLACE },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -2447,6 +2451,16 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_REPLACE:
|
||||
if (!path_is_absolute(optarg) ||
|
||||
!endswith(optarg, ".conf")) {
|
||||
log_error("The argument to --replace= must an absolute path to a config file");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
arg_replace = optarg;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -2459,10 +2473,15 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (arg_replace && optind >= argc) {
|
||||
log_error("When --replace= is given, some configuration items must be specified");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int read_config_file(const char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
|
||||
static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoent, bool *invalid_config) {
|
||||
_cleanup_fclose_ FILE *_f = NULL;
|
||||
FILE *f;
|
||||
char line[LINE_MAX];
|
||||
@@ -2474,11 +2493,11 @@ static int read_config_file(const char **config_dirs, const char *fn, bool ignor
|
||||
assert(fn);
|
||||
|
||||
if (streq(fn, "-")) {
|
||||
log_debug("Reading config from stdin.");
|
||||
log_debug("Reading config from stdin…");
|
||||
fn = "<stdin>";
|
||||
f = stdin;
|
||||
} else {
|
||||
r = search_and_fopen(fn, "re", arg_root, config_dirs, &_f);
|
||||
r = search_and_fopen(fn, "re", arg_root, (const char**) config_dirs, &_f);
|
||||
if (r < 0) {
|
||||
if (ignore_enoent && r == -ENOENT) {
|
||||
log_debug_errno(r, "Failed to open \"%s\", ignoring: %m", fn);
|
||||
@@ -2487,7 +2506,7 @@ static int read_config_file(const char **config_dirs, const char *fn, bool ignor
|
||||
|
||||
return log_error_errno(r, "Failed to open '%s': %m", fn);
|
||||
}
|
||||
log_debug("Reading config file \"%s\".", fn);
|
||||
log_debug("Reading config file \"%s\"…", fn);
|
||||
f = _f;
|
||||
}
|
||||
|
||||
@@ -2550,13 +2569,60 @@ static int read_config_file(const char **config_dirs, const char *fn, bool ignor
|
||||
return r;
|
||||
}
|
||||
|
||||
static int parse_arguments(char **config_dirs, char **args, bool *invalid_config) {
|
||||
char **arg;
|
||||
int r;
|
||||
|
||||
STRV_FOREACH(arg, args) {
|
||||
r = read_config_file(config_dirs, *arg, false, invalid_config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_config_files(char **config_dirs, char **args, bool *invalid_config) {
|
||||
_cleanup_strv_free_ char **files = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
char **f;
|
||||
int r;
|
||||
|
||||
r = conf_files_list_strv(&files, ".conf", arg_root, 0, (const char* const*) config_dirs);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
|
||||
|
||||
if (arg_replace) {
|
||||
r = conf_files_insert(&files, arg_root, config_dirs, arg_replace);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extend tmpfiles.d file list: %m");
|
||||
|
||||
p = path_join(arg_root, arg_replace, NULL);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
STRV_FOREACH(f, files)
|
||||
if (p && path_equal(*f, p)) {
|
||||
log_debug("Parsing arguments at position \"%s\"…", *f);
|
||||
|
||||
r = parse_arguments(config_dirs, args, invalid_config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else
|
||||
/* Just warn, ignore result otherwise.
|
||||
* read_config_file() has some debug output, so no need to print anything. */
|
||||
(void) read_config_file(config_dirs, *f, true, invalid_config);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int r, k;
|
||||
ItemArray *a;
|
||||
Iterator iterator;
|
||||
_cleanup_strv_free_ char **config_dirs = NULL;
|
||||
bool invalid_config = false;
|
||||
char **f;
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
@@ -2602,30 +2668,20 @@ int main(int argc, char *argv[]) {
|
||||
log_debug("Looking for configuration files in (higher priority first:\n\t%s", t);
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
int j;
|
||||
/* If command line arguments are specified along with --replace, read all
|
||||
* configuration files and insert the positional arguments at the specified
|
||||
* place. Otherwise, if command line arguments are specified, execute just
|
||||
* them, and finally, without --replace= or any positional arguments, just
|
||||
* read configuration and execute it.
|
||||
*/
|
||||
if (arg_replace || optind >= argc)
|
||||
r = read_config_files(config_dirs, argv + optind, &invalid_config);
|
||||
else
|
||||
r = parse_arguments(config_dirs, argv + optind, &invalid_config);
|
||||
if (r < 0)
|
||||
goto finish;
|
||||
|
||||
for (j = optind; j < argc; j++) {
|
||||
k = read_config_file((const char**) config_dirs, argv[j], false, &invalid_config);
|
||||
if (k < 0 && r == 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
} else {
|
||||
_cleanup_strv_free_ char **files = NULL;
|
||||
|
||||
r = conf_files_list_strv(&files, ".conf", arg_root, 0, (const char* const*) config_dirs);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to enumerate tmpfiles.d files: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
STRV_FOREACH(f, files) {
|
||||
k = read_config_file((const char**) config_dirs, *f, true, &invalid_config);
|
||||
if (k < 0 && r == 0)
|
||||
r = k;
|
||||
}
|
||||
}
|
||||
|
||||
/* The non-globbing ones usually create things, hence we apply
|
||||
* them first */
|
||||
|
||||
Reference in New Issue
Block a user