mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03: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:
parent
24c2c5689d
commit
a6d8474f39
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user