2022-06-07 20:24:43 +02:00
|
|
|
From a1c88ab7f5e5ad127833168bbb38340d1b582a88 Mon Sep 17 00:00:00 2001
|
2021-02-19 14:38:02 +01:00
|
|
|
From: Franck Bui <fbui@suse.com>
|
|
|
|
Date: Fri, 22 Jan 2021 14:57:08 +0100
|
2022-06-07 20:24:43 +02:00
|
|
|
Subject: [PATCH 1/1] conf-parser: introduce 'early' drop-ins
|
2021-02-19 14:38:02 +01:00
|
|
|
MIME-Version: 1.0
|
|
|
|
Content-Type: text/plain; charset=UTF-8
|
|
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
|
|
|
|
As formerly known as "downstream conf file drop-ins should never override main
|
|
|
|
user conf file".
|
|
|
|
|
|
|
|
Previously all drop-ins, including those shipped by downstream, shipped in
|
|
|
|
/usr, could override user's main configuration file (located in /etc) because
|
|
|
|
the main file was always parsed first.
|
|
|
|
|
|
|
|
This was problematic for downstreams because their customization should never
|
|
|
|
override the users one in general. Therefore the only way to make this logic
|
|
|
|
usable was by teaching users to never use the main conf files and to put all
|
|
|
|
theirs settings in drop-ins with a higher priority than the one downsteam would
|
|
|
|
use. However customizing the defaults through the main conf file is something
|
|
|
|
very well established since a long time hence this is not something
|
|
|
|
conceivable.
|
|
|
|
|
|
|
|
This patch reworks the way we parse configuration files by introducing "early"
|
|
|
|
conf files (idea from Zbigniew Jędrzejewski-Szmek), which always have a
|
|
|
|
priority lower than the main config file and hence other conf file drop-ins
|
|
|
|
too.
|
|
|
|
|
|
|
|
Early conf files can be located in any locations where regular conf snippets
|
|
|
|
can be installed and are sorted between them using the same sorting rules that
|
|
|
|
apply to other conf files. A conf file is considered as an early one if its
|
|
|
|
filename is prefixed with "__" (double underscore).
|
|
|
|
|
|
|
|
Hence for example, drop-in "/usr/lib/systemd/logind.conf.d/__99-foo.conf" will
|
|
|
|
always be parsed before:
|
|
|
|
|
|
|
|
/etc/systemd/logind.conf
|
|
|
|
/etc/systemd/logind.conf.d/00-foo.conf
|
|
|
|
/usr/lib/systemd/logind.conf.d/00-foo.conf
|
|
|
|
|
|
|
|
This change isn't backwards-compatible, but the '__' prefix is something that
|
|
|
|
is unlikely used. Hence the risk should be very low.
|
|
|
|
|
|
|
|
Unfortunately upstream is not seing this problem as a serious one and accept
|
|
|
|
that vendors' configuration files can take precedence over the main
|
|
|
|
configuration files (placed in /etc). See the following links for the
|
|
|
|
related discussions:
|
|
|
|
|
|
|
|
https://github.com/systemd/systemd/issues/2121 (initial issue report)
|
|
|
|
https://github.com/systemd/systemd/pull/17161 (first attempt to solve this issue)
|
|
|
|
https://github.com/systemd/systemd/pull/18347 (introduction of early drop-in)
|
|
|
|
|
|
|
|
Since SUSE heavily relies on drop-ins to customize some of the upstream default
|
|
|
|
settings, there was no other choice than to diverge from upstream in this
|
|
|
|
regard.
|
|
|
|
|
|
|
|
But it should be noted that these early drop-ins are strictly reserved for SUSE
|
|
|
|
own purpose only. IOW users should never use them and early drop-ins should
|
|
|
|
never be created in /etc but only in /usr. We reserve the right to change or
|
|
|
|
drop this feature at any time.
|
|
|
|
|
|
|
|
Fixes: #2121
|
|
|
|
---
|
2022-06-07 20:24:43 +02:00
|
|
|
src/shared/conf-parser.c | 54 +++++++++++-
|
|
|
|
src/test/test-conf-parser.c | 163 ++++++++++++++++++++++++++++++++++++
|
|
|
|
2 files changed, 213 insertions(+), 4 deletions(-)
|
2021-02-19 14:38:02 +01:00
|
|
|
|
|
|
|
diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c
|
2022-06-07 20:24:43 +02:00
|
|
|
index 6bd3ab38db..1722b90ca0 100644
|
2021-02-19 14:38:02 +01:00
|
|
|
--- a/src/shared/conf-parser.c
|
|
|
|
+++ b/src/shared/conf-parser.c
|
2022-06-07 20:24:43 +02:00
|
|
|
@@ -478,6 +478,7 @@ static int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path,
|
2021-02-19 14:38:02 +01:00
|
|
|
|
|
|
|
static int config_parse_many_files(
|
2021-04-28 14:31:30 +02:00
|
|
|
const char* const* conf_files,
|
2021-02-19 14:38:02 +01:00
|
|
|
+ char **early_files,
|
|
|
|
char **files,
|
|
|
|
const char *sections,
|
|
|
|
ConfigItemLookup lookup,
|
2022-06-07 20:24:43 +02:00
|
|
|
@@ -496,6 +497,20 @@ static int config_parse_many_files(
|
2022-03-28 21:03:59 +02:00
|
|
|
return -ENOMEM;
|
2022-06-07 20:24:43 +02:00
|
|
|
}
|
|
|
|
|
2021-02-19 14:38:02 +01:00
|
|
|
+ STRV_FOREACH(fn, early_files) {
|
2022-03-28 21:03:59 +02:00
|
|
|
+ r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &st);
|
2021-02-19 14:38:02 +01:00
|
|
|
+ if (r < 0)
|
|
|
|
+ return r;
|
2022-06-07 20:24:43 +02:00
|
|
|
+ if (r == 0)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ if (ret_stats_by_path) {
|
|
|
|
+ r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st);
|
|
|
|
+ if (r < 0)
|
|
|
|
+ return r;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
2021-04-28 14:31:30 +02:00
|
|
|
/* First read the first found main config file. */
|
2022-06-07 20:24:43 +02:00
|
|
|
STRV_FOREACH(fn, conf_files) {
|
|
|
|
r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &st);
|
|
|
|
@@ -534,6 +549,27 @@ static int config_parse_many_files(
|
2021-02-19 14:38:02 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
+static int config_parse_split_conf_files(char **files, char ***early_files, char ***late_files) {
|
|
|
|
+
|
|
|
|
+ assert(files);
|
|
|
|
+ assert(early_files);
|
|
|
|
+ assert(late_files);
|
|
|
|
+
|
|
|
|
+ STRV_FOREACH(f, files) {
|
|
|
|
+ char ***s, *p;
|
|
|
|
+
|
|
|
|
+ p = strdup(*f);
|
|
|
|
+ if (!p)
|
|
|
|
+ return log_oom();
|
|
|
|
+
|
|
|
|
+ s = startswith(basename(*f), "__") ? early_files : late_files;
|
|
|
|
+ if (strv_push(s, p) < 0)
|
|
|
|
+ return log_oom();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
/* Parse each config file in the directories specified as nulstr. */
|
|
|
|
int config_parse_many_nulstr(
|
|
|
|
const char *conf_file,
|
2022-06-07 20:24:43 +02:00
|
|
|
@@ -545,15 +581,19 @@ int config_parse_many_nulstr(
|
2021-02-19 14:38:02 +01:00
|
|
|
void *userdata,
|
2022-03-28 21:03:59 +02:00
|
|
|
Hashmap **ret_stats_by_path) {
|
2021-02-19 14:38:02 +01:00
|
|
|
|
|
|
|
- _cleanup_strv_free_ char **files = NULL;
|
|
|
|
+ _cleanup_strv_free_ char **files = NULL, **early_files = NULL, **late_files = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2021-04-28 14:31:30 +02:00
|
|
|
- return config_parse_many_files(STRV_MAKE_CONST(conf_file),
|
|
|
|
- files, sections, lookup, table, flags, userdata,
|
2021-02-19 14:38:02 +01:00
|
|
|
+ r = config_parse_split_conf_files(files, &early_files, &late_files);
|
|
|
|
+ if (r < 0)
|
|
|
|
+ return r;
|
|
|
|
+
|
2021-04-28 14:31:30 +02:00
|
|
|
+ return config_parse_many_files(STRV_MAKE_CONST(conf_file), early_files, late_files,
|
|
|
|
+ sections, lookup, table, flags, userdata,
|
2022-03-28 21:03:59 +02:00
|
|
|
ret_stats_by_path);
|
2021-02-19 14:38:02 +01:00
|
|
|
}
|
|
|
|
|
2022-06-07 20:24:43 +02:00
|
|
|
@@ -590,6 +630,7 @@ int config_parse_many(
|
2021-02-19 14:38:02 +01:00
|
|
|
void *userdata,
|
2022-03-28 21:03:59 +02:00
|
|
|
Hashmap **ret_stats_by_path) {
|
2021-02-19 14:38:02 +01:00
|
|
|
|
2022-06-07 20:24:43 +02:00
|
|
|
+ _cleanup_strv_free_ char **early_files = NULL, **late_files = NULL;
|
|
|
|
_cleanup_strv_free_ char **files = NULL;
|
2021-02-19 14:38:02 +01:00
|
|
|
int r;
|
|
|
|
|
2022-06-07 20:24:43 +02:00
|
|
|
@@ -602,7 +643,12 @@ int config_parse_many(
|
2021-02-19 14:38:02 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2022-03-28 21:03:59 +02:00
|
|
|
- return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path);
|
2021-02-19 14:38:02 +01:00
|
|
|
+ r = config_parse_split_conf_files(files, &early_files, &late_files);
|
|
|
|
+ if (r < 0)
|
|
|
|
+ return r;
|
|
|
|
+
|
2021-04-28 14:31:30 +02:00
|
|
|
+ return config_parse_many_files(conf_files, early_files, late_files,
|
2022-03-28 21:03:59 +02:00
|
|
|
+ sections, lookup, table, flags, userdata, ret_stats_by_path);
|
2021-02-19 14:38:02 +01:00
|
|
|
}
|
|
|
|
|
2022-06-07 20:24:43 +02:00
|
|
|
static int config_get_stats_by_path_one(
|
2021-02-19 14:38:02 +01:00
|
|
|
diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c
|
2022-06-07 20:24:43 +02:00
|
|
|
index e61932ab16..5a5c0d73ff 100644
|
2021-02-19 14:38:02 +01:00
|
|
|
--- a/src/test/test-conf-parser.c
|
|
|
|
+++ b/src/test/test-conf-parser.c
|
|
|
|
@@ -5,6 +5,9 @@
|
|
|
|
#include "fs-util.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "macro.h"
|
|
|
|
+#include "mkdir.h"
|
|
|
|
+#include "path-util.h"
|
|
|
|
+#include "rm-rf.h"
|
|
|
|
#include "string-util.h"
|
|
|
|
#include "strv.h"
|
2022-03-28 21:03:59 +02:00
|
|
|
#include "tests.h"
|
2022-06-07 20:24:43 +02:00
|
|
|
@@ -391,4 +394,164 @@ TEST(config_parse) {
|
2022-03-28 21:03:59 +02:00
|
|
|
test_config_parse_one(i, config_file[i]);
|
2021-02-19 14:38:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
+static void setup_conf_files(const char *root, bool is_main, char **conf_files, char ***ret_conf_dirs) {
|
|
|
|
+
|
|
|
|
+ /* If 'is_main' is true then 'conf_files' should only contain an entry
|
|
|
|
+ * for the main conf file. */
|
|
|
|
+ if (is_main)
|
|
|
|
+ assert_se(strv_length(conf_files) <= 1);
|
|
|
|
+
|
|
|
|
+ STRV_FOREACH(path, conf_files) {
|
|
|
|
+ _cleanup_free_ char *abspath = NULL;
|
|
|
|
+ _cleanup_fclose_ FILE *f = NULL;
|
|
|
|
+
|
|
|
|
+ abspath = path_join(root, *path);
|
|
|
|
+ assert_se(abspath);
|
|
|
|
+
|
|
|
|
+ (void) mkdir_parents(abspath, 0755);
|
|
|
|
+
|
|
|
|
+ f = fopen(abspath, "w");
|
|
|
|
+ assert_se(f);
|
|
|
|
+ fprintf(f,
|
|
|
|
+ "[Section]\n"
|
|
|
|
+ "name=%s\n",
|
|
|
|
+ *path);
|
|
|
|
+
|
|
|
|
+ if (!is_main)
|
|
|
|
+ fprintf(f,
|
|
|
|
+ "%s=%s\n",
|
|
|
|
+ startswith(basename(*path), "__") ? "early" : "late",
|
|
|
|
+ *path);
|
|
|
|
+
|
|
|
|
+ if (ret_conf_dirs) {
|
|
|
|
+ char *d;
|
|
|
|
+
|
|
|
|
+ assert_se((d = dirname_malloc(abspath)));
|
|
|
|
+ assert_se(strv_push(ret_conf_dirs, d) == 0);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (ret_conf_dirs) {
|
|
|
|
+ strv_uniq(*ret_conf_dirs);
|
|
|
|
+ strv_sort(*ret_conf_dirs);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+static void test_config_parse_many_nulstr_one(bool nulstr, const char *main, char **conf_files,
|
|
|
|
+ const char *name, const char *early, const char *late) {
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
|
|
|
+ _cleanup_free_ char *parsed_name = NULL, *parsed_early = NULL, *parsed_late = NULL;
|
|
|
|
+ _cleanup_strv_free_ char **conf_dirs = NULL;
|
|
|
|
+ _cleanup_free_ char *conf_dirs_nulstr = NULL;
|
|
|
|
+ char *conf_file;
|
|
|
|
+ char *tmp_dir;
|
|
|
|
+ size_t size;
|
|
|
|
+ int r;
|
|
|
|
+
|
|
|
|
+ const ConfigTableItem items[] = {
|
|
|
|
+ { "Section", "name", config_parse_string, 0, &parsed_name},
|
|
|
|
+ { "Section", "late", config_parse_string, 0, &parsed_late},
|
|
|
|
+ { "Section", "early", config_parse_string, 0, &parsed_early},
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ tmp_dir = strdupa("/tmp/test-conf-parser-XXXXXX");
|
|
|
|
+ assert_se(mkdtemp(tmp_dir));
|
|
|
|
+
|
|
|
|
+ setup_conf_files(tmp_dir, true, STRV_MAKE(main), NULL);
|
|
|
|
+ setup_conf_files(tmp_dir, false, conf_files, &conf_dirs);
|
|
|
|
+
|
|
|
|
+ conf_file = main ? strjoina(tmp_dir, "/", main) : NULL;
|
|
|
|
+
|
|
|
|
+ if (nulstr) {
|
|
|
|
+ r = strv_make_nulstr(conf_dirs, &conf_dirs_nulstr, &size);
|
|
|
|
+ assert_se(r == 0);
|
|
|
|
+
|
|
|
|
+ r = config_parse_many_nulstr(conf_file, conf_dirs_nulstr,
|
|
|
|
+ "Section\0",
|
|
|
|
+ config_item_table_lookup, items,
|
|
|
|
+ CONFIG_PARSE_WARN,
|
|
|
|
+ NULL,
|
|
|
|
+ NULL);
|
|
|
|
+ } else {
|
2022-06-07 20:24:43 +02:00
|
|
|
+ /* sigh... since commit bdb2d3c6889408c7f26c2eeddbe9021ac53f962c,
|
|
|
|
+ * 'conf_file_dirs' parameter can't be NULL. */
|
|
|
|
+
|
2021-04-28 14:31:30 +02:00
|
|
|
+ r = config_parse_many(STRV_MAKE_CONST(conf_file),
|
2022-06-07 20:24:43 +02:00
|
|
|
+ (const char * const*)(conf_dirs ?: STRV_MAKE_EMPTY),
|
|
|
|
+ "",
|
2021-02-19 14:38:02 +01:00
|
|
|
+ "Section\0",
|
|
|
|
+ config_item_table_lookup, items,
|
|
|
|
+ CONFIG_PARSE_WARN,
|
|
|
|
+ NULL,
|
|
|
|
+ NULL);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ assert_se(r == 0);
|
2022-06-07 20:24:43 +02:00
|
|
|
+
|
|
|
|
+ assert_se((!!name == !!parsed_name));
|
|
|
|
+ assert_se(!name || streq(name, parsed_name));
|
|
|
|
+
|
|
|
|
+ assert_se((!!late == !!parsed_late));
|
|
|
|
+ assert_se(!late || streq(late, parsed_late));
|
|
|
|
+
|
|
|
|
+ assert_se((!!early == !!parsed_early));
|
|
|
|
+ assert_se(!early || streq(early, parsed_early));
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
|
|
|
+ assert_se(rm_rf(tmp_dir, REMOVE_ROOT|REMOVE_PHYSICAL) == 0);
|
|
|
|
+}
|
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+static void test_config_parse_many_nulstr(bool nulstr) {
|
2021-03-15 21:02:29 +01:00
|
|
|
+ log_info("== %s%s ==", __func__, nulstr ? "_nulstr" : "");
|
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+ test_config_parse_many_nulstr_one(nulstr, NULL, NULL, NULL, NULL, NULL);
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+ test_config_parse_many_nulstr_one(nulstr,
|
|
|
|
+ "dir/main.conf", NULL,
|
|
|
|
+ "dir/main.conf", NULL, NULL);
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+ test_config_parse_many_nulstr_one(nulstr,
|
|
|
|
+ NULL, STRV_MAKE("dir1/50-foo.conf"),
|
|
|
|
+ "dir1/50-foo.conf", NULL, "dir1/50-foo.conf");
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+ test_config_parse_many_nulstr_one(nulstr,
|
|
|
|
+ NULL, STRV_MAKE("dir1/__50-foo.conf"),
|
|
|
|
+ "dir1/__50-foo.conf", "dir1/__50-foo.conf", NULL);
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+ test_config_parse_many_nulstr_one(nulstr,
|
|
|
|
+ NULL, STRV_MAKE("dir1/10-foo.conf", "dir1/50-bar.conf"),
|
|
|
|
+ "dir1/50-bar.conf", NULL, "dir1/50-bar.conf");
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+ test_config_parse_many_nulstr_one(nulstr,
|
|
|
|
+ NULL, STRV_MAKE("dir1/50-foo.conf", "dir2/10-bar.conf"),
|
|
|
|
+ "dir1/50-foo.conf", NULL, "dir1/50-foo.conf");
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+ test_config_parse_many_nulstr_one(nulstr,
|
|
|
|
+ NULL, STRV_MAKE("dir1/10-foo.conf", "dir2/10-foo.conf"),
|
|
|
|
+ "dir1/10-foo.conf", NULL, "dir1/10-foo.conf");
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
|
|
|
+ /* Early conf files should never override the main one whatever their
|
|
|
|
+ * priority/location. */
|
2022-03-28 21:03:59 +02:00
|
|
|
+ test_config_parse_many_nulstr_one(nulstr,
|
|
|
|
+ "dir/10-main.conf",
|
|
|
|
+ STRV_MAKE("dir1/__10-foo.conf", "dir2/__99-foo.conf"),
|
|
|
|
+ "dir/10-main.conf", "dir2/__99-foo.conf", NULL);
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
|
|
|
+ /* Late conf files always take precendence over the early conf files
|
|
|
|
+ * and the main one. */
|
2022-03-28 21:03:59 +02:00
|
|
|
+ test_config_parse_many_nulstr_one(nulstr,
|
|
|
|
+ "dir/50-main.conf", STRV_MAKE("dir1/10-foo.conf"),
|
|
|
|
+ "dir1/10-foo.conf", NULL, "dir1/10-foo.conf");
|
|
|
|
+
|
|
|
|
+ test_config_parse_many_nulstr_one(nulstr,
|
|
|
|
+ "dir/10-main.conf",
|
|
|
|
+ STRV_MAKE("dir1/__10-foo.conf", "dir2/__99-foo.conf",
|
|
|
|
+ "dir2/10-foo.conf"),
|
|
|
|
+ "dir2/10-foo.conf", "dir2/__99-foo.conf", "dir2/10-foo.conf");
|
2021-02-19 14:38:02 +01:00
|
|
|
+}
|
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
+TEST(config_parse_many) {
|
|
|
|
+ test_config_parse_many_nulstr(true);
|
|
|
|
+ test_config_parse_many_nulstr(false);
|
|
|
|
+}
|
2021-02-19 14:38:02 +01:00
|
|
|
+
|
2022-03-28 21:03:59 +02:00
|
|
|
DEFINE_TEST_MAIN(LOG_INFO);
|
2021-02-19 14:38:02 +01:00
|
|
|
--
|
2022-06-07 20:24:43 +02:00
|
|
|
2.35.3
|
2021-02-19 14:38:02 +01:00
|
|
|
|