Mercurial > dovecot > core-2.2
changeset 9859:f9ca1a1ebcf8 HEAD
config: Several fixes. Now per-ip settings work properly.
doveconf parameters were also changed. Now it's possible to ask
configuration for a specified filter.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 03 Sep 2009 16:36:38 -0400 |
parents | 2e502c0e23e7 |
children | d3d8ba13faa6 |
files | src/config/config-connection.c src/config/config-filter.c src/config/config-filter.h src/config/config-parser.c src/config/config-request.c src/config/config-request.h src/config/doveconf.c |
diffstat | 7 files changed, 303 insertions(+), 193 deletions(-) [+] |
line wrap: on
line diff
--- a/src/config/config-connection.c Thu Sep 03 16:35:19 2009 -0400 +++ b/src/config/config-connection.c Thu Sep 03 16:36:38 2009 -0400 @@ -102,8 +102,11 @@ } o_stream_cork(conn->output); - config_request_handle(&filter, module, 0, config_request_output, - conn->output); + if (config_request_handle(&filter, module, CONFIG_DUMP_SCOPE_SET, FALSE, + config_request_output, conn->output) < 0) { + config_connection_destroy(conn); + return -1; + } o_stream_send_str(conn->output, "\n"); o_stream_uncork(conn->output); return 0;
--- a/src/config/config-filter.c Thu Sep 03 16:35:19 2009 -0400 +++ b/src/config/config-filter.c Thu Sep 03 16:36:38 2009 -0400 @@ -2,12 +2,13 @@ #include "lib.h" #include "array.h" +#include "settings-parser.h" #include "config-parser.h" #include "config-filter.h" struct config_filter_context { pool_t pool; - struct config_filter_parser_list *const *parsers; + struct config_filter_parser *const *parsers; }; bool config_filter_match(const struct config_filter *mask, @@ -75,47 +76,123 @@ } void config_filter_add_all(struct config_filter_context *ctx, - struct config_filter_parser_list *const *parsers) + struct config_filter_parser *const *parsers) { ctx->parsers = parsers; } -static int filter_cmp(const struct config_filter *f1, - const struct config_filter *f2) +static int +config_filter_parser_cmp(struct config_filter_parser *const *p1, + struct config_filter_parser *const *p2) { - int ret; + const struct config_filter *f1 = &(*p1)->filter, *f2 = &(*p2)->filter; + + /* remote_ip and local_ips are first, although it doesn't really + matter which one comes first */ + if (f1->local_bits > f2->local_bits) + return -1; + if (f1->local_bits < f2->local_bits) + return 1; - ret = f2->remote_bits - f1->remote_bits; - if (ret != 0) - return ret; + if (f1->remote_bits > f2->remote_bits) + return -1; + if (f1->remote_bits < f2->remote_bits) + return 1; + + if (f1->service != NULL && f2->service == NULL) + return -1; + if (f1->service == NULL && f2->service != NULL) + return 1; + return 0; +} - ret = f2->local_bits - f1->local_bits; - if (ret != 0) - return ret; +static struct config_filter_parser *const * +config_filter_find_all(struct config_filter_context *ctx, + const struct config_filter *filter) +{ + ARRAY_TYPE(config_filter_parsers) matches; + unsigned int i; - if (f1->service != NULL) - return -1; - else - return 1; + t_array_init(&matches, 8); + for (i = 0; ctx->parsers[i] != NULL; i++) { + if (config_filter_match(&ctx->parsers[i]->filter, filter)) + array_append(&matches, &ctx->parsers[i], 1); + } + array_sort(&matches, config_filter_parser_cmp); + (void)array_append_space(&matches); + return array_idx(&matches, 0); +} + +static bool +config_filter_is_superset(const struct config_filter *sup, + const struct config_filter *filter) +{ + /* assume that both of the filters match the same subset, so we don't + need to compare IPs and service name. */ + if (sup->local_bits < filter->local_bits) + return FALSE; + if (sup->remote_bits < filter->remote_bits) + return FALSE; + if (sup->service != NULL && filter->service == NULL) + return FALSE; + return TRUE; } -const struct config_filter_parser_list * -config_filter_find(struct config_filter_context *ctx, - const struct config_filter *filter) +static int +config_module_parser_apply_changes(struct config_module_parser *dest, + const struct config_filter_parser *src, + pool_t pool, const char **error_r) { - struct config_filter_parser_list *best = NULL; unsigned int i; - /* find the filter that best matches what we have. - FIXME: this can't really work. we'd want to merge changes from - different matches.. requires something larger after all. */ - for (i = 0; ctx->parsers[i] != NULL; i++) { - if (!config_filter_match(&ctx->parsers[i]->filter, filter)) - continue; + for (i = 0; dest[i].module_name != NULL; i++) { + if (settings_parser_apply_changes(dest[i].parser, + src->parsers[i].parser, pool, + error_r) < 0) { + *error_r = t_strdup_printf("Conflict in setting %s", + *error_r); + return -1; + } + } + return 0; +} + +int config_filter_get_parsers(struct config_filter_context *ctx, pool_t pool, + const struct config_filter *filter, + const struct config_module_parser **parsers_r, + const char **error_r) +{ + struct config_filter_parser *const *src; + struct config_module_parser *dest; + const char *error, **error_p; + unsigned int i, count; + + src = config_filter_find_all(ctx, filter); - if (best == NULL || - filter_cmp(&best->filter, &ctx->parsers[i]->filter) > 0) - best = ctx->parsers[i]; + /* all of them should have the same number of parsers. + duplicate our initial parsers from the first match */ + for (count = 0; src[0]->parsers[count].module_name != NULL; count++) ; + dest = p_new(pool, struct config_module_parser, count + 1); + for (i = 0; i < count; i++) { + dest[i] = src[0]->parsers[i]; + dest[i].parser = + settings_parser_dup(src[0]->parsers[i].parser, pool); } - return best; + + /* apply the changes from rest of the matches */ + for (i = 1; src[i] != NULL; i++) { + if (config_filter_is_superset(&src[i-1]->filter, + &src[i]->filter)) + error_p = NULL; + else + error_p = &error; + + if (config_module_parser_apply_changes(dest, src[i], pool, + error_p) < 0) { + *error_r = error; + return -1; + } + } + *parsers_r = dest; + return 0; }
--- a/src/config/config-filter.h Thu Sep 03 16:35:19 2009 -0400 +++ b/src/config/config-filter.h Thu Sep 03 16:36:38 2009 -0400 @@ -9,23 +9,25 @@ unsigned int local_bits, remote_bits; }; -struct config_filter_parser_list { +struct config_filter_parser { struct config_filter filter; /* NULL-terminated array of parsers */ struct config_module_parser *parsers; }; +ARRAY_DEFINE_TYPE(config_filter_parsers, struct config_filter_parser *); struct config_filter_context *config_filter_init(pool_t pool); void config_filter_deinit(struct config_filter_context **ctx); /* Replace filter's parsers with given parser list. */ void config_filter_add_all(struct config_filter_context *ctx, - struct config_filter_parser_list *const *parsers); + struct config_filter_parser *const *parsers); -/* Find the filter that best matches what we have. */ -const struct config_filter_parser_list * -config_filter_find(struct config_filter_context *ctx, - const struct config_filter *filter); +/* Build new parsers from all existing ones matching the given filter. */ +int config_filter_get_parsers(struct config_filter_context *ctx, pool_t pool, + const struct config_filter *filter, + const struct config_module_parser **parsers_r, + const char **error_r); /* Returns TRUE if filter matches mask. */ bool config_filter_match(const struct config_filter *mask,
--- a/src/config/config-parser.c Thu Sep 03 16:35:19 2009 -0400 +++ b/src/config/config-parser.c Thu Sep 03 16:36:38 2009 -0400 @@ -23,9 +23,12 @@ #define IS_WHITE(c) ((c) == ' ' || (c) == '\t') -struct config_filter_stack { - struct config_filter_stack *prev; +struct config_section_stack { + struct config_section_stack *prev; + struct config_filter filter; + /* module_name=NULL-terminated list of parsers */ + struct config_module_parser *parsers; unsigned int pathlen; }; @@ -41,16 +44,18 @@ pool_t pool; const char *path; - ARRAY_DEFINE(all_parsers, struct config_filter_parser_list *); - /* parsers matching cur_filter */ - ARRAY_TYPE(config_module_parsers) cur_parsers; + ARRAY_DEFINE(all_parsers, struct config_filter_parser *); struct config_module_parser *root_parsers; - struct config_filter_stack *cur_filter; + struct config_section_stack *cur_section; struct input_stack *cur_input; struct config_filter_context *filter; }; +static const enum settings_parser_flags settings_parser_flags = + SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS | + SETTINGS_PARSER_FLAG_TRACK_CHANGES; + struct config_module_parser *config_module_parsers; struct config_filter_context *config_filter; @@ -93,15 +98,15 @@ } static int -config_parsers_parse_line(struct config_module_parser *parsers, - const char *key, const char *line, - const char *section_name, const char **error_r) +config_apply_line(struct parser_context *ctx, const char *key, + const char *line, const char *section_name, + const char **error_r) { struct config_module_parser *l; bool found = FALSE; int ret; - for (l = parsers; l->module_name != NULL; l++) { + for (l = ctx->cur_section->parsers; l->module_name != NULL; l++) { ret = settings_parse_line(l->parser, line); if (ret > 0) { found = TRUE; @@ -116,23 +121,6 @@ *error_r = t_strconcat("Unknown setting: ", key, NULL); return -1; } - return 0; -} - -static int -config_apply_line(struct parser_context *ctx, const char *key, - const char *line, const char *section_name, - const char **error_r) -{ - struct config_module_parser *const *parsers; - unsigned int i, count; - - parsers = array_get(&ctx->cur_parsers, &count); - for (i = 0; i < count; i++) { - if (config_parsers_parse_line(parsers[i], key, line, - section_name, error_r) < 0) - return -1; - } *error_r = NULL; return 0; } @@ -153,61 +141,72 @@ } static struct config_module_parser * -config_module_parsers_dup(pool_t pool, const struct config_module_parser *src) +config_module_parsers_init(pool_t pool) { struct config_module_parser *dest; unsigned int i, count; - for (count = 0; src[count].module_name != NULL; count++) ; + for (count = 0; all_roots[count].module_name != NULL; count++) ; dest = p_new(pool, struct config_module_parser, count + 1); for (i = 0; i < count; i++) { - dest[i] = src[i]; - dest[i].parser = settings_parser_dup(src[i].parser, pool); + dest[i].module_name = all_roots[i].module_name; + dest[i].root = all_roots[i].root; + dest[i].parser = settings_parser_init(pool, all_roots[i].root, + settings_parser_flags); } return dest; } -static struct config_filter_parser_list * +static void config_add_new_parser(struct parser_context *ctx) { - struct config_filter_parser_list *parser; - struct config_module_parser *const *cur_parsers; - unsigned int count; - - parser = p_new(ctx->pool, struct config_filter_parser_list, 1); - parser->filter = ctx->cur_filter->filter; + struct config_section_stack *cur_section = ctx->cur_section; + struct config_filter_parser *parser; - cur_parsers = array_get(&ctx->cur_parsers, &count); - if (count == 0) { - /* first one */ - parser->parsers = ctx->root_parsers; - } else { - /* duplicate the first settings list */ - parser->parsers = - config_module_parsers_dup(ctx->pool, cur_parsers[0]); - } + parser = p_new(ctx->pool, struct config_filter_parser, 1); + parser->filter = cur_section->filter; + parser->parsers = cur_section->prev == NULL ? ctx->root_parsers : + config_module_parsers_init(ctx->pool); + array_append(&ctx->all_parsers, &parser, 1); - array_append(&ctx->all_parsers, &parser, 1); - return parser; + cur_section->parsers = parser->parsers; } -static void config_add_new_filter(struct parser_context *ctx) +static struct config_section_stack * +config_add_new_section(struct parser_context *ctx) { - struct config_filter_stack *filter; + struct config_section_stack *section; + + section = p_new(ctx->pool, struct config_section_stack, 1); + section->prev = ctx->cur_section; + section->filter = ctx->cur_section->filter; + section->parsers = ctx->cur_section->parsers; + return section; +} - filter = p_new(ctx->pool, struct config_filter_stack, 1); - filter->prev = ctx->cur_filter; - filter->filter = ctx->cur_filter->filter; - ctx->cur_filter = filter; +static struct config_filter_parser * +config_filter_parser_find(struct parser_context *ctx, + const struct config_filter *filter) +{ + struct config_filter_parser *const *parsers; + unsigned int i, count; + + parsers = array_get(&ctx->all_parsers, &count); + for (i = 0; i < count; i++) { + if (config_filters_equal(&parsers[i]->filter, filter)) + return parsers[i]; + } + return NULL; } static bool -config_filter_add_new_parser(struct parser_context *ctx, +config_filter_add_new_filter(struct parser_context *ctx, const char *key, const char *value, const char **error_r) { - struct config_filter *filter = &ctx->cur_filter->filter; + struct config_filter *filter = &ctx->cur_section->filter; + struct config_filter_parser *parser; if (strcmp(key, "protocol") == 0) { filter->service = p_strdup(ctx->pool, value); @@ -223,69 +222,51 @@ return FALSE; } - config_add_new_parser(ctx); + parser = config_filter_parser_find(ctx, filter); + if (parser != NULL) + ctx->cur_section->parsers = parser->parsers; + else + config_add_new_parser(ctx); return TRUE; } -static void config_update_cur_parsers(struct parser_context *ctx) +static int +config_filter_parser_check(struct parser_context *ctx, + const struct config_module_parser *p, + const char **error_r) { - struct config_filter_parser_list *const *all_parsers; - unsigned int i, count; - bool full_found = FALSE; - - array_clear(&ctx->cur_parsers); - - all_parsers = array_get(&ctx->all_parsers, &count); - for (i = 0; i < count; i++) { - if (!config_filter_match(&ctx->cur_filter->filter, - &all_parsers[i]->filter)) - continue; - - if (config_filters_equal(&all_parsers[i]->filter, - &ctx->cur_filter->filter)) { - array_insert(&ctx->cur_parsers, 0, - &all_parsers[i]->parsers, 1); - full_found = TRUE; - } else { - array_append(&ctx->cur_parsers, - &all_parsers[i]->parsers, 1); - } - } - i_assert(full_found); -} - -static int -config_filter_parser_list_check(struct parser_context *ctx, - struct config_filter_parser_list *parser, - const char **error_r) -{ - struct config_module_parser *l = parser->parsers; - const char *errormsg; - - for (; l->module_name != NULL; l++) { - if (!settings_parser_check(l->parser, ctx->pool, &errormsg)) { - *error_r = t_strdup_printf( - "Error in configuration file %s: %s", - ctx->path, errormsg); + for (; p->module_name != NULL; p++) { + if (!settings_parser_check(p->parser, ctx->pool, error_r)) return -1; - } } return 0; } static int -config_all_parsers_check(struct parser_context *ctx, const char **error_r) +config_all_parsers_check(struct parser_context *ctx, + struct config_filter_context *new_filter, + const char **error_r) { - struct config_filter_parser_list *const *parsers; + struct config_filter_parser *const *parsers; + const struct config_module_parser *tmp_parsers; unsigned int i, count; + pool_t tmp_pool; + tmp_pool = pool_alloconly_create("config parsers check", 10240); parsers = array_get(&ctx->all_parsers, &count); + i_assert(count > 0 && parsers[count-1] == NULL); + count--; for (i = 0; i < count; i++) { - if (config_filter_parser_list_check(ctx, parsers[i], - error_r) < 0) - return -1; + p_clear(tmp_pool); + if (config_filter_get_parsers(new_filter, tmp_pool, + &parsers[i]->filter, + &tmp_parsers, error_r) < 0) + break; + + if (config_filter_parser_check(ctx, tmp_parsers, error_r) < 0) + break; } - return 0; + return i == count ? 0 : -1; } static int @@ -514,12 +495,31 @@ return CONFIG_LINE_TYPE_SECTION_BEGIN; } +static int config_parse_finish(struct parser_context *ctx, const char **error_r) +{ + struct config_filter_context *new_filter; + const char *error; + + new_filter = config_filter_init(ctx->pool); + (void)array_append_space(&ctx->all_parsers); + config_filter_add_all(new_filter, array_idx(&ctx->all_parsers, 0)); + + if (config_all_parsers_check(ctx, new_filter, &error) < 0) { + *error_r = t_strdup_printf("Error in configuration file %s: %s", + ctx->path, error); + return -1; + } + + if (config_filter != NULL) + config_filter_deinit(&config_filter); + config_module_parsers = ctx->root_parsers; + config_filter = new_filter; + return 0; +} + int config_parse_file(const char *path, bool expand_files, const char **error_r) { - enum settings_parser_flags parser_flags = - SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS | - SETTINGS_PARSER_FLAG_TRACK_CHANGES; struct input_stack root; struct parser_context ctx; unsigned int pathlen = 0; @@ -548,14 +548,12 @@ ctx.root_parsers[i].root = all_roots[i].root; ctx.root_parsers[i].parser = settings_parser_init(ctx.pool, all_roots[i].root, - parser_flags); + settings_parser_flags); } - t_array_init(&ctx.cur_parsers, 128); p_array_init(&ctx.all_parsers, ctx.pool, 128); - ctx.cur_filter = p_new(ctx.pool, struct config_filter_stack, 1); + ctx.cur_section = p_new(ctx.pool, struct config_section_stack, 1); config_add_new_parser(&ctx); - config_update_cur_parsers(&ctx); memset(&root, 0, sizeof(root)); root.path = path; @@ -591,13 +589,12 @@ (void)config_apply_line(&ctx, key, str_c(str), NULL, &errormsg); break; case CONFIG_LINE_TYPE_SECTION_BEGIN: - config_add_new_filter(&ctx); - ctx.cur_filter->pathlen = pathlen; + ctx.cur_section = config_add_new_section(&ctx); + ctx.cur_section->pathlen = pathlen; - if (config_filter_add_new_parser(&ctx, key, value, + if (config_filter_add_new_filter(&ctx, key, value, &errormsg)) { - /* new real filter */ - config_update_cur_parsers(&ctx); + /* new filter */ break; } @@ -620,12 +617,11 @@ pathlen = str_len(str); break; case CONFIG_LINE_TYPE_SECTION_END: - if (ctx.cur_filter->prev == NULL) + if (ctx.cur_section->prev == NULL) errormsg = "Unexpected '}'"; else { - pathlen = ctx.cur_filter->pathlen; - ctx.cur_filter = ctx.cur_filter->prev; - config_update_cur_parsers(&ctx); + pathlen = ctx.cur_section->pathlen; + ctx.cur_section = ctx.cur_section->prev; } break; case CONFIG_LINE_TYPE_INCLUDE: @@ -652,21 +648,11 @@ if (line == NULL && ctx.cur_input != NULL) goto prevfile; - if (ret == 0) { - if (config_all_parsers_check(&ctx, error_r) < 0) - ret = -1; - } + if (ret == 0) + ret = config_parse_finish(&ctx, error_r); if (ret < 0) { pool_unref(&ctx.pool); return -1; } - - if (config_filter != NULL) - config_filter_deinit(&config_filter); - config_module_parsers = ctx.root_parsers; - - (void)array_append_space(&ctx.all_parsers); - config_filter = config_filter_init(ctx.pool); - config_filter_add_all(config_filter, array_idx(&ctx.all_parsers, 0)); return 1; }
--- a/src/config/config-request.c Thu Sep 03 16:35:19 2009 -0400 +++ b/src/config/config-request.c Thu Sep 03 16:36:38 2009 -0400 @@ -231,16 +231,26 @@ } } -void config_request_handle(const struct config_filter *filter, - const char *module, enum config_dump_scope scope, - config_request_callback_t *callback, void *context) +int config_request_handle(const struct config_filter *filter, + const char *module, enum config_dump_scope scope, + bool check_settings, + config_request_callback_t *callback, void *context) { const struct config_module_parser *l; - const struct config_filter_parser_list *list; struct settings_export_context ctx; + const char *error; + int ret = 0; memset(&ctx, 0, sizeof(ctx)); ctx.pool = pool_alloconly_create("config request", 10240); + + if (config_filter_get_parsers(config_filter, ctx.pool, filter, + &l, &error) < 0) { + i_error("%s", error); + pool_unref(&ctx.pool); + return -1; + } + ctx.callback = callback; ctx.context = context; ctx.scope = scope; @@ -249,15 +259,24 @@ ctx.keys = hash_table_create(default_pool, ctx.pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); - list = config_filter_find(config_filter, filter); - for (l = list->parsers; l->module_name != NULL; l++) { - if (*module == '\0' || - config_module_parser_is_in_service(l, module)) { - settings_export(&ctx, l->root, - settings_parser_get(l->parser), - settings_parser_get_changes(l->parser)); + for (; l->module_name != NULL; l++) { + if (*module != '\0' && + !config_module_parser_is_in_service(l, module)) + continue; + + settings_export(&ctx, l->root, settings_parser_get(l->parser), + settings_parser_get_changes(l->parser)); + + if (check_settings) { + if (!settings_parser_check(l->parser, ctx.pool, + &error)) { + i_error("%s", error); + ret = -1; + break; + } } } hash_table_destroy(&ctx.keys); pool_unref(&ctx.pool); + return ret; }
--- a/src/config/config-request.h Thu Sep 03 16:35:19 2009 -0400 +++ b/src/config/config-request.h Thu Sep 03 16:36:38 2009 -0400 @@ -15,8 +15,9 @@ typedef void config_request_callback_t(const char *key, const char *value, bool list, void *context); -void config_request_handle(const struct config_filter *filter, - const char *module, enum config_dump_scope scope, - config_request_callback_t *callback, void *context); +int config_request_handle(const struct config_filter *filter, + const char *module, enum config_dump_scope scope, + bool check_settings, + config_request_callback_t *callback, void *context); #endif
--- a/src/config/doveconf.c Thu Sep 03 16:35:19 2009 -0400 +++ b/src/config/doveconf.c Thu Sep 03 16:36:38 2009 -0400 @@ -77,8 +77,9 @@ ctx.pool = pool_alloconly_create("config human strings", 10240); i_array_init(&ctx.strings, 256); - config_request_handle(filter, module, scope, - config_request_get_strings, &ctx); + if (config_request_handle(filter, module, scope, TRUE, + config_request_get_strings, &ctx) < 0) + return; array_sort(&ctx.strings, config_string_cmp); strings = array_get(&ctx.strings, &count); @@ -200,6 +201,23 @@ return ""; } +static void filter_parse_arg(struct config_filter *filter, const char *arg) +{ + if (strncmp(arg, "service=", 8) == 0) + filter->service = arg + 8; + else if (strncmp(arg, "lip=", 4) == 0) { + if (net_parse_range(arg + 4, &filter->local_net, + &filter->local_bits) < 0) + i_fatal("lip: Invalid network mask"); + } else if (strncmp(arg, "rip=", 4) == 0) { + if (net_parse_range(arg + 4, &filter->remote_net, + &filter->remote_bits) < 0) + i_fatal("rip: Invalid network mask"); + } else { + i_fatal("Unknown filter argument: %s", arg); + } +} + int main(int argc, char *argv[]) { enum config_dump_scope scope = CONFIG_DUMP_SCOPE_ALL; @@ -214,7 +232,7 @@ MASTER_SERVICE_FLAG_STANDALONE, argc, argv); i_set_failure_prefix("doveconf: "); - getopt_str = t_strconcat("am:nNp:e", + getopt_str = t_strconcat("af:m:nN:e", master_service_getopt_string(), NULL); while ((c = getopt(argc, argv, getopt_str)) > 0) { if (c == 'e') @@ -222,6 +240,9 @@ switch (c) { case 'a': break; + case 'f': + filter_parse_arg(&filter, optarg); + break; case 'm': module = optarg; break; @@ -231,9 +252,6 @@ case 'N': scope = CONFIG_DUMP_SCOPE_SET; break; - case 'p': - filter.service = optarg; - break; default: if (!master_service_parse_option(master_service, c, optarg)) @@ -242,9 +260,11 @@ } config_path = master_service_get_config_path(master_service); - if (argv[optind] != NULL) + if (argv[optind] != NULL) { + if (c != 'e') + i_fatal("Unknown argument: %s", argv[optind]); exec_args = &argv[optind]; - else { + } else { /* print the config file path before parsing it, so in case of errors it's still shown */ printf("# "VERSION": %s\n", config_path); @@ -268,8 +288,10 @@ config_dump_human(&filter, module, scope); } else { env_put("DOVECONF_ENV=1"); - config_request_handle(&filter, module, 0, - config_request_putenv, NULL); + if (config_request_handle(&filter, module, + CONFIG_DUMP_SCOPE_SET, TRUE, + config_request_putenv, NULL) < 0) + i_fatal("Invalid configuration"); execvp(exec_args[0], exec_args); i_fatal("execvp(%s) failed: %m", exec_args[0]); }