1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-27 18:55:40 +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:
Zbigniew Jędrzejewski-Szmek 2018-02-05 14:53:11 +01:00
parent 24c2c5689d
commit a6d8474f39
5 changed files with 134 additions and 45 deletions

View File

@ -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>

View File

@ -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);
}

View File

@ -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);

View File

@ -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");

View File

@ -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 */