From: Zbigniew Jędrzejewski-Szmek Date: Mon, 5 Feb 2018 13:53:11 +0000 (+0100) Subject: tmpfiles: allow admin/runtime overrides to runtime config X-Git-Tag: v238~134^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a6d8474f39d8d92077a4cefab2f355b2e5e00211;p=thirdparty%2Fsystemd.git 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. --- diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index 24a08b0354d..85cb89dc468 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -84,12 +84,16 @@ 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 - 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 , + arguments specified on the command line are used instead of the configuration file + PATH. Otherwise, if one or more absolute filenames are + passed on the command line, only the directives in these files are applied. If + - 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 tmpfiles.d5 - are searched for a matching file. + are searched for a matching file and the file found that has the highest priority is + executed. @@ -176,6 +180,22 @@ consulted. + + + When this option is given, one ore more positional arguments + must be specified. All configuration files found in the directories listed in + tmpfiles.d5 + 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 + PATH. + + 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. + + + diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index 08ede2c766a..8b4129e1cd5 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 ddee7278260..5dc83578e4c 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 cf16d2b55bc..fab54b50402 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 38cbb739c01..29e66b0a87f 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 = ""; 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 */