From: Timo Sirainen Date: Sun, 2 Mar 2025 08:45:19 +0000 (+0200) Subject: config: Support dovecot_config_version to rename settings or change default settings X-Git-Tag: 2.4.1~112 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=404d78b0754f5983015a1b0b189e3f1acec84a37;p=thirdparty%2Fdovecot%2Fcore.git config: Support dovecot_config_version to rename settings or change default settings --- diff --git a/.gitignore b/.gitignore index 8e0bd569e4..2a64cd6c0a 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,7 @@ doc/wiki/*.txt doc/wiki/Makefile.am src/anvil/anvil src/auth/auth +src/lib-settings/settings-history-core.c src/config/all-settings.c src/config/config src/config/doveconf diff --git a/src/config/config-parser.c b/src/config/config-parser.c index e18bf9da04..2c1a0fb25e 100644 --- a/src/config/config-parser.c +++ b/src/config/config-parser.c @@ -2825,7 +2825,7 @@ int config_parse_file(const char *path, enum config_parse_flags flags, string_t *full_line; char *line; int fd, ret = 0; - bool handled, dump_defaults = (path == NULL); + bool dump_defaults = (path == NULL); *config_r = NULL; @@ -2956,9 +2956,8 @@ prevfile: if (!config_parser_get_version(&ctx, &config_line) && ctx.error == NULL) T_BEGIN { - handled = old_settings_handle(&ctx, &config_line); - if (!handled) - config_parser_apply_line(&ctx, &config_line); + old_settings_handle(&ctx, &config_line); + config_parser_apply_line(&ctx, &config_line); } T_END; if (ctx.error != NULL) { diff --git a/src/config/config-request.c b/src/config/config-request.c index 2eeb80aeba..bb2ff067f3 100644 --- a/src/config/config-request.c +++ b/src/config/config-request.c @@ -224,8 +224,22 @@ settings_export(struct config_export_context *ctx, case SET_STR_NOVARS: case SET_ENUM: { string_t *default_str = NULL; + bool default_changed = FALSE; + const char *old_default; i_assert(info->defaults != NULL); - if (!dump_default || module_parser->change_counters[define_idx] == 0) { + if (module_parser->change_counters[define_idx] <= CONFIG_PARSER_CHANGE_DEFAULTS) { + /* Setting isn't explicitly set. We need to see + if its default has changed. */ + if (old_settings_default(ctx->dovecot_config_version, + def->key, &old_default)) { + default_str = t_str_new(strlen(old_default)); + str_append(default_str, old_default); + default_changed = TRUE; + } + } + + if ((!dump_default || module_parser->change_counters[define_idx] == 0) && + default_str == NULL) { const void *default_value = CONST_PTR_OFFSET(info->defaults, def->offset); @@ -250,7 +264,14 @@ settings_export(struct config_export_context *ctx, actually changed from its default. */ break; } - if (module_parser->change_counters[define_idx] != 0) { + if (module_parser->change_counters[define_idx] > + CONFIG_PARSER_CHANGE_DEFAULTS) { + /* explicitly set */ + str_append(ctx->value, + module_parser->settings[define_idx].str); + } else if (module_parser->change_counters[define_idx] == + CONFIG_PARSER_CHANGE_DEFAULTS && !default_changed) { + /* default not changed by old version checks */ str_append(ctx->value, module_parser->settings[define_idx].str); } else { diff --git a/src/config/old-set-parser.c b/src/config/old-set-parser.c index cf0ebddcf4..2515f2163c 100644 --- a/src/config/old-set-parser.c +++ b/src/config/old-set-parser.c @@ -1,7 +1,9 @@ /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "array.h" #include "master-service.h" +#include "settings-history.h" #include "config-parser-private.h" #include "old-set-parser.h" @@ -27,8 +29,30 @@ obsolete(struct config_parser_context *ctx, const char *str, ...) va_end(args); } -bool old_settings_handle(struct config_parser_context *ctx ATTR_UNUSED, - const struct config_line *line) +static void old_settings_handle_rename(struct config_parser_context *ctx, + struct config_line *line) +{ + struct settings_history *history = settings_history_get(); + const struct setting_history_rename *rename; + + if (ctx->dovecot_config_version[0] == '\0') + return; + + array_foreach(&history->renames, rename) { + if (version_cmp(rename->version, + ctx->dovecot_config_version) <= 0) + break; + if (strcmp(rename->old_key, line->key) == 0) { + obsolete(ctx, "%s has been renamed to %s", + rename->old_key, rename->new_key); + line->key = rename->new_key; + break; + } + } +} + +void old_settings_handle(struct config_parser_context *ctx, + struct config_line *line) { switch (line->type) { case CONFIG_LINE_TYPE_SKIP: @@ -43,7 +67,27 @@ bool old_settings_handle(struct config_parser_context *ctx ATTR_UNUSED, case CONFIG_LINE_TYPE_KEYFILE: case CONFIG_LINE_TYPE_KEYVALUE: case CONFIG_LINE_TYPE_KEYVARIABLE: + old_settings_handle_rename(ctx, line); break; } +} + +bool old_settings_default(const char *dovecot_config_version, + const char *key, const char **old_default_r) +{ + struct settings_history *history = settings_history_get(); + const struct setting_history_default *def; + + if (dovecot_config_version[0] == '\0') + return FALSE; + + array_foreach(&history->defaults, def) { + if (version_cmp(def->version, dovecot_config_version) <= 0) + break; + if (strcmp(def->key, key) == 0) { + *old_default_r = def->old_value; + return TRUE; + } + } return FALSE; } diff --git a/src/config/old-set-parser.h b/src/config/old-set-parser.h index 811a80975a..000f9c9fa8 100644 --- a/src/config/old-set-parser.h +++ b/src/config/old-set-parser.h @@ -5,7 +5,9 @@ struct config_parser_context; -bool old_settings_handle(struct config_parser_context *ctx, - const struct config_line *line); +void old_settings_handle(struct config_parser_context *ctx, + struct config_line *line); +bool old_settings_default(const char *dovecot_config_version, + const char *key, const char **old_default_r); #endif diff --git a/src/lib-settings/Makefile.am b/src/lib-settings/Makefile.am index ae01131cb9..f02649a9a4 100644 --- a/src/lib-settings/Makefile.am +++ b/src/lib-settings/Makefile.am @@ -7,15 +7,28 @@ AM_CPPFLAGS = \ libsettings_la_SOURCES = \ settings.c \ + settings-history.c \ settings-parser.c headers = \ settings.h \ + settings-history.h \ settings-parser.h pkginc_libdir=$(pkgincludedir) pkginc_lib_HEADERS = $(headers) +BUILT_SOURCES = settings-history-core.c + +settings-history.c: settings-history-core.c + +settings-history-core.c: $(top_srcdir)/src/lib-settings/settings-history.pl settings-history-core.txt + $(AM_V_GEN)$(top_srcdir)/src/lib-settings/settings-history.pl settings-history-core.txt $(DOVECOT_PRO_BUILD) > settings-history-core.c || rm -f settings-history-core.c + +EXTRA_DIST = \ + settings-history-core.txt \ + settings-history.pl + test_programs = \ test-settings-parser \ test-settings diff --git a/src/lib-settings/settings-history-core.txt b/src/lib-settings/settings-history-core.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/lib-settings/settings-history.c b/src/lib-settings/settings-history.c new file mode 100644 index 0000000000..a2abb21985 --- /dev/null +++ b/src/lib-settings/settings-history.c @@ -0,0 +1,51 @@ +/* Copyright (c) 2025 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "settings-history.h" + +#include "settings-history-core.c" + +static struct settings_history history; + +static void settings_history_free(void) +{ + array_free(&history.defaults); + array_free(&history.renames); +} + +struct settings_history *settings_history_get(void) +{ + if (array_is_created(&history.defaults)) + return &history; + + i_array_init(&history.defaults, + N_ELEMENTS(settings_history_core_defaults) + 16); + array_append(&history.defaults, + settings_history_core_defaults, + N_ELEMENTS(settings_history_core_defaults)); + i_array_init(&history.renames, + N_ELEMENTS(settings_history_core_renames) + 16); + array_append(&history.renames, + settings_history_core_renames, + N_ELEMENTS(settings_history_core_renames)); + + lib_atexit(settings_history_free); + return &history; +} + +void settings_history_register_defaults( + const struct setting_history_default *defaults, unsigned int count) +{ + struct settings_history *history = settings_history_get(); + + array_append(&history->defaults, defaults, count); +} + +void settings_history_register_renames( + const struct setting_history_rename *renames, unsigned int count) +{ + struct settings_history *history = settings_history_get(); + + array_append(&history->renames, renames, count); +} diff --git a/src/lib-settings/settings-history.h b/src/lib-settings/settings-history.h new file mode 100644 index 0000000000..5379387bd2 --- /dev/null +++ b/src/lib-settings/settings-history.h @@ -0,0 +1,29 @@ +#ifndef SETTINGS_HISTORY_H +#define SETTINGS_HISTORY_H + +struct setting_history_default { + const char *key; + const char *old_value; + const char *version; +}; + +struct setting_history_rename { + const char *old_key, *new_key; + const char *version; +}; + +struct settings_history { + ARRAY(struct setting_history_default) defaults; + ARRAY(struct setting_history_rename) renames; +}; + +struct settings_history *settings_history_get(void); + +/* Register new defaults/renames. The strings are assumed to be statically + allocated, i.e. they are not duplicated. */ +void settings_history_register_defaults( + const struct setting_history_default *defaults, unsigned int count); +void settings_history_register_renames( + const struct setting_history_rename *renames, unsigned int count); + +#endif diff --git a/src/lib-settings/settings-history.pl b/src/lib-settings/settings-history.pl new file mode 100755 index 0000000000..11f7b54636 --- /dev/null +++ b/src/lib-settings/settings-history.pl @@ -0,0 +1,53 @@ +#!/usr/bin/env perl +use strict; +use warnings; +use version; +use 5.010; + +my ($input_path, $pro_edition) = ($ARGV[0], $ARGV[1]); + +# output renames +print "static const struct setting_history_rename settings_history_core_renames[] = {\n"; +my $f; +open $f, "<:encoding(utf8)", $input_path or die "$!"; +my $prev_version = ""; +while (<$f>) { + chomp; + if (/^rename\t/) { + my ($type, $old_key, $new_key, $ce_version, $pro_version) = split("\t"); + my $version = $pro_edition ? $pro_version : $ce_version; + if ($version ne "") { + die "bad version: $version" if $version !~ /^((\d+)\.)*(\d+)$/; + print " { \"$old_key\", \"$new_key\", \"$version\" },\n"; + if ($prev_version ne "") { + die "Bad version sorting order" if version->parse($version) > version->parse($prev_version); + } + $prev_version = $version; + } + } elsif (! /^default\t/) { + die "Unknown line: $_"; + } +} +close $f; +print "};\n"; + +# output defaults +print "static const struct setting_history_default settings_history_core_defaults[] = {\n"; +open $f, "<:encoding(utf8)", $input_path or die "$!"; +while (<$f>) { + chomp; + if (/^default\t/) { + my ($type, $key, $old_value, $ce_version, $pro_version) = split("\t"); + my $version = $pro_edition ? $pro_version : $ce_version; + if ($version ne "") { + die "bad version: $version" if $version !~ /^((\d+)\.)*(\d+)$/; + print " { \"$key\", \"$old_value\", \"$version\" },\n"; + if ($prev_version ne "") { + die "Bad version sorting order" if version->parse($version) > version->parse($prev_version); + } + $prev_version = $version; + } + } +} +close $f; +print "};\n";