From: Timo Sirainen Date: Thu, 25 Jan 2024 16:25:20 +0000 (+0200) Subject: config: Write default settings before base settings X-Git-Tag: 2.4.1~1138 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=af5e4b143d75d65e6b2557cbc11db80b909e39d5;p=thirdparty%2Fdovecot%2Fcore.git config: Write default settings before base settings This allows the global settings to always override default settings. --- diff --git a/src/config/config-dump-full.c b/src/config/config-dump-full.c index 33030b6f9c..bd73cb5a57 100644 --- a/src/config/config-dump-full.c +++ b/src/config/config-dump-full.c @@ -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; diff --git a/src/config/config-filter.c b/src/config/config-filter.c index eb1102ff53..924b4f0c04 100644 --- a/src/config/config-filter.c +++ b/src/config/config-filter.c @@ -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); diff --git a/src/config/config-filter.h b/src/config/config-filter.h index 28f2259eda..bd0ed4609d 100644 --- a/src/config/config-filter.h +++ b/src/config/config-filter.h @@ -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 diff --git a/src/config/config-parser.c b/src/config/config-parser.c index e5d701d284..09ac33ed23 100644 --- a/src/config/config-parser.c +++ b/src/config/config-parser.c @@ -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) {