]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tmpfiles: allow admin/runtime overrides to runtime config
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 5 Feb 2018 13:53:11 +0000 (14:53 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 5 Feb 2018 14:04:52 +0000 (15:04 +0100)
This is very similar to d16a1c1bb6. For tmpfiles this is much less useful
compared to sysusers, but let's add this anyway for consistency.

man/systemd-tmpfiles.xml
src/basic/conf-files.c
src/basic/conf-files.h
src/sysusers/sysusers.c
src/tmpfiles/tmpfiles.c

index 24a08b0354dc4bd68da7f0561801a29347c1ba99..85cb89dc468aaf48f0edbfd44de444bd801a87b0 100644 (file)
     </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>
         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>
index 08ede2c766a19e4ba92dfa10c3818395066bd5e5..8b4129e1cd5857b8e4c8292910e4262a07281f88 100644 (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);
 }
index ddee7278260a2e1100ae672970f3cf3fc1615f09..5dc83578e4ceea6d70d4caf33d7cc32aae6ff95e 100644 (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);
index cf16d2b55bcd9b07bd2d13fce0e4e5c3c6b94311..fab54b50402aa718a318a9c126cde15738bc141e 100644 (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");
 
index 38cbb739c01f826dd3abd7ee11d125e057897a23..29e66b0a87fdccdaefb0008ad16060f3c522e233 100644 (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;
-
-                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 */