]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
config: Write default settings before base settings
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 25 Jan 2024 16:25:20 +0000 (18:25 +0200)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 12 Feb 2025 10:34:11 +0000 (12:34 +0200)
This allows the global settings to always override default settings.

src/config/config-dump-full.c
src/config/config-filter.c
src/config/config-filter.h
src/config/config-parser.c

index 33030b6f9c10de484efc78dab874b87d41684b9a..bd73cb5a578ebbde60b1161f8cd07309bebc1552 100644 (file)
@@ -240,7 +240,8 @@ config_dump_full_stdout_callback(const struct config_export_setting *set,
        if (!ctx->filter_written) {
                string_t *str = t_str_new(128);
                str_append(str, ":FILTER ");
-               config_dump_full_append_filter(str, ctx->filter, TRUE);
+               if (ctx->filter != NULL)
+                       config_dump_full_append_filter(str, ctx->filter, TRUE);
                str_append_c(str, '\n');
                o_stream_nsend(ctx->output, str_data(str), str_len(str));
                ctx->filter_written = TRUE;
@@ -366,7 +367,8 @@ static int
 config_dump_full_sections(struct config_dump_full_context *ctx,
                          unsigned int parser_idx,
                          const struct setting_parser_info *info,
-                         const string_t *delayed_filter)
+                         const string_t *delayed_filter,
+                         bool dump_defaults)
 {
        struct ostream *output = ctx->output;
        enum config_dump_full_dest dest = ctx->dest;
@@ -382,6 +384,8 @@ config_dump_full_sections(struct config_dump_full_context *ctx,
                const struct config_filter_parser *filter = ctx->filters[i];
                uoff_t start_offset = output->offset;
 
+               if (filter->filter.default_settings != dump_defaults)
+                       continue;
                if (filter->module_parsers[parser_idx].settings == NULL &&
                    filter->module_parsers[parser_idx].delayed_error == NULL)
                        continue;
@@ -427,7 +431,7 @@ config_dump_full_sections(struct config_dump_full_context *ctx,
                }
        }
 
-       if (str_len(delayed_filter) > 0) {
+       if (delayed_filter != NULL && str_len(delayed_filter) > 0) {
                ctx->filter_indexes_be32[ctx->filter_output_count] =
                        0; /* empty/global filter */
                ctx->filter_offsets_be64[ctx->filter_output_count] =
@@ -455,7 +459,6 @@ int config_dump_full(struct config_parsed *config,
 
        struct dump_context dump_ctx = {
                .delayed_output = str_new(default_pool, 256),
-               .filter_written = TRUE,
        };
 
        if (dest == CONFIG_DUMP_FULL_DEST_STDOUT) {
@@ -568,6 +571,14 @@ int config_dump_full(struct config_parsed *config,
                                       sizeof(filter_count));
                }
 
+               /* Write default settings filters */
+               int ret;
+               T_BEGIN {
+                       ret = config_dump_full_sections(&ctx, i, info, NULL, TRUE);
+               } T_END;
+               if (ret < 0)
+                       break;
+
                uoff_t blob_size_offset = output->offset;
                /* Write base settings - add it as an empty filter */
                ctx.filter_indexes_be32[ctx.filter_output_count] = 0;
@@ -576,8 +587,16 @@ int config_dump_full(struct config_parsed *config,
                ctx.filter_output_count++;
 
                if (dest != CONFIG_DUMP_FULL_DEST_STDOUT) {
+                       /* Write a filter for the base settings, even if there
+                          are no settings. This allows lib-settings to apply
+                          setting overrides at the proper position before
+                          defaults. */
                        o_stream_nsend(output, &blob_size, sizeof(blob_size));
                        o_stream_nsend(output, "", 1); /* no error */
+                       dump_ctx.filter_written = TRUE;
+               } else {
+                       /* Make :FILTER visible */
+                       dump_ctx.filter_written = FALSE;
                }
                dump_ctx.info = info;
                if (config_export_parser(export_ctx, i, &error) < 0) {
@@ -590,10 +609,10 @@ int config_dump_full(struct config_parsed *config,
                                break;
                }
 
-               int ret;
+               /* Write non-default settings filters */
                T_BEGIN {
                        ret = config_dump_full_sections(&ctx, i, info,
-                               dump_ctx.delayed_output);
+                               dump_ctx.delayed_output, FALSE);
                } T_END;
                if (ret < 0)
                        break;
index eb1102ff539ac239b31c9483d3143d97e92558eb..924b4f0c04394c3eb8e21a294e4fb5ed03d0e3ba 100644 (file)
@@ -91,8 +91,9 @@ bool config_filter_match(const struct config_filter *mask,
        return mask == NULL && filter == NULL;
 }
 
-bool config_filters_equal(const struct config_filter *f1,
-                         const struct config_filter *f2)
+static bool
+config_filters_equal_without_defaults(const struct config_filter *f1,
+                                     const struct config_filter *f2)
 {
        if (null_strcmp(f1->service, f2->service) != 0)
                return FALSE;
@@ -118,13 +119,25 @@ bool config_filters_equal(const struct config_filter *f1,
                /* Check the parents' compatibility also. However, it's
                   possible that one of these parents is the empty root filter,
                   while the other parent is NULL. These are actually equal. */
-               return config_filters_equal(
+               return config_filters_equal_without_defaults(
                        f1->parent != NULL ? f1->parent : &empty_filter,
                        f2->parent != NULL ? f2->parent : &empty_filter);
        }
        return TRUE;
 }
 
+bool config_filters_equal(const struct config_filter *f1,
+                         const struct config_filter *f2)
+{
+       if (f1->default_settings != f2->default_settings)
+               return FALSE;
+
+       /* For the rest of the settings don't check if the parents'
+          default_settings are equal. This makes it easier for callers to
+          do lookups with the wanted default_settings flag. */
+       return config_filters_equal_without_defaults(f1, f2);
+}
+
 bool config_filter_is_empty(const struct config_filter *filter)
 {
        return config_filters_equal(filter, &empty_filter);
index 28f2259eda783ee98d108dbdfa2a328a5881f50f..bd0ed4609df7bef9daa29bbbc9a95cb1e652a1db 100644 (file)
@@ -26,6 +26,10 @@ struct config_filter {
           their filter_names will be merged into one filter path when dumping
           the config. */
        bool filter_hierarchical;
+
+       /* TRUE if default settings are being accessed. These will be stored in
+          separate filters so they can be ordered before global settings. */
+       bool default_settings;
 };
 
 /* Each unique config_filter (including its parents in hierarchy) has its own
index e5d701d284dd91edef1a4cab8be2277081b56f70..09ac33ed232be4ef3eb8eaacd693c86294981588 100644 (file)
@@ -656,6 +656,8 @@ again:
                   old-set-parser. */
                struct config_filter filter = {
                        .parent = &ctx->cur_section->filter_parser->filter,
+                       .default_settings = (ctx->change_counter ==
+                                            CONFIG_PARSER_CHANGE_DEFAULTS),
                };
                /* find the type of the first prefix/ */
                filter.filter_name = t_strdup_until(key_with_path, p);
@@ -676,13 +678,16 @@ again:
 
                filter_parser = config_filter_parser_find(ctx, &filter);
                if (filter_parser == NULL) {
-                       if (filter.filter_name_array) {
-                               /* don't create new arrays */
+                       if (filter.filter_name_array &&
+                           !filter.default_settings) {
+                               /* don't create new arrays, except for
+                                  default settings */
                                break;
                        }
                        /* Verify that this is a filter_name/ prefix. If not,
                           it should be a list/ */
                        if (l->info->defines[config_key->define_idx].type != SET_FILTER_NAME &&
+                           l->info->defines[config_key->define_idx].type != SET_FILTER_ARRAY &&
                            l->info->defines[config_key->define_idx].type != SET_FILTER_HIERARCHY)
                                break;
 
@@ -1093,6 +1098,9 @@ config_filter_add_new_filter(struct config_parser_context *ctx,
 void config_fill_set_parser(struct setting_parser_context *parser,
                            const struct config_module_parser *p)
 {
+       if (p->change_counters == NULL)
+               return;
+
        for (unsigned int i = 0; p->info->defines[i].key != NULL; i++) {
                if (p->change_counters[i] == 0)
                        continue;
@@ -1143,7 +1151,7 @@ config_filter_parser_check(struct config_parser_context *ctx,
                           struct event *event,
                           struct config_filter_parser *filter_parser)
 {
-       struct config_module_parser *p;
+       struct config_module_parser *p, *default_p, *next_default_p;
        const struct config_filter *filter;
        const char *error = NULL;
        pool_t tmp_pool;
@@ -1161,14 +1169,30 @@ config_filter_parser_check(struct config_parser_context *ctx,
                        event_add_ip(event, "remote_ip", &filter->remote_net);
        }
 
+       /* Defaults are in a separate filter. Merge them with the non-defaults
+          filter before calling check_func()s. */
+       struct config_filter default_filter = filter_parser->filter;
+       default_filter.default_settings = TRUE;
+       struct config_filter_parser *default_filter_parser =
+               config_filter_parser_find(ctx, &default_filter);
+       default_p = default_filter_parser == NULL ? NULL :
+               default_filter_parser->module_parsers;
+
        tmp_pool = pool_alloconly_create(MEMPOOL_GROWING"config parsers check", 1024);
-       for (p = filter_parser->module_parsers; p->info != NULL; p++) {
+       for (p = filter_parser->module_parsers; p->info != NULL;
+            p++, default_p = next_default_p) {
+               next_default_p = default_p == NULL ? NULL : default_p + 1;
                if (p->settings == NULL)
                        continue;
+
+               i_assert(default_p == NULL || default_p->info == p->info);
+
                p_clear(tmp_pool);
                struct setting_parser_context *tmp_parser =
                        settings_parser_init(tmp_pool, p->info,
                                             settings_parser_flags);
+               if (default_p != NULL)
+                       config_fill_set_parser(tmp_parser, default_p);
                config_fill_set_parser(tmp_parser, p);
                T_BEGIN {
                        ok = settings_parser_check(tmp_parser, tmp_pool,
@@ -1273,6 +1297,8 @@ config_all_parsers_check(struct config_parser_context *ctx,
           the config code, so at least for now it's not done. */
        global_ssl_set = get_str_setting(parsers[0], "ssl", "");
        for (i = 0; i < count; i++) {
+               if (parsers[i]->filter.default_settings)
+                       continue;
                ssl_set = get_str_setting(parsers[i], "ssl", global_ssl_set);
                if (strcmp(ssl_set, "no") != 0 &&
                    strcmp(global_ssl_set, "no") == 0 && !ssl_warned) {