summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--man/systemd-tmpfiles.xml30
-rw-r--r--src/basic/conf-files.c34
-rw-r--r--src/basic/conf-files.h3
-rw-r--r--src/sysusers/sysusers.c2
-rw-r--r--src/tmpfiles/tmpfiles.c110
5 files changed, 134 insertions, 45 deletions
diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml
index 24a08b0354..85cb89dc46 100644
--- a/man/systemd-tmpfiles.xml
+++ b/man/systemd-tmpfiles.xml
@@ -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>
diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c
index 08ede2c766..8b4129e1cd 100644
--- a/src/basic/conf-files.c
+++ b/src/basic/conf-files.c
@@ -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);
}
diff --git a/src/basic/conf-files.h b/src/basic/conf-files.h
index ddee727826..5dc83578e4 100644
--- a/src/basic/conf-files.h
+++ b/src/basic/conf-files.h
@@ -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);
diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c
index cf16d2b55b..fab54b5040 100644
--- a/src/sysusers/sysusers.c
+++ b/src/sysusers/sysusers.c
@@ -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");
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index 38cbb739c0..29e66b0a87 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -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;
-
- 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;
+ /* 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;
- 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 */