diff options
-rw-r--r-- | man/systemd-tmpfiles.xml | 30 | ||||
-rw-r--r-- | src/basic/conf-files.c | 34 | ||||
-rw-r--r-- | src/basic/conf-files.h | 3 | ||||
-rw-r--r-- | src/sysusers/sysusers.c | 2 | ||||
-rw-r--r-- | src/tmpfiles/tmpfiles.c | 110 |
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 */ |