}
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;
#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,
}
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;
- ret = f2->remote_bits - f1->remote_bits;
- if (ret != 0)
- return ret;
+ /* 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->local_bits - f1->local_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)
+ if (f1->service != NULL && f2->service == NULL)
return -1;
- else
+ if (f1->service == NULL && f2->service != NULL)
return 1;
+ return 0;
}
-const struct config_filter_parser_list *
-config_filter_find(struct config_filter_context *ctx,
- const struct config_filter *filter)
+static struct config_filter_parser *const *
+config_filter_find_all(struct config_filter_context *ctx,
+ const struct config_filter *filter)
{
- struct config_filter_parser_list *best = NULL;
+ ARRAY_TYPE(config_filter_parsers) matches;
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. */
+ t_array_init(&matches, 8);
for (i = 0; ctx->parsers[i] != NULL; i++) {
- if (!config_filter_match(&ctx->parsers[i]->filter, filter))
- continue;
+ 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;
+}
+
+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)
+{
+ unsigned int i;
+
+ 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);
+
+ /* 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);
+ }
- if (best == NULL ||
- filter_cmp(&best->filter, &ctx->parsers[i]->filter) > 0)
- best = ctx->parsers[i];
+ /* 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;
+ }
}
- return best;
+ *parsers_r = dest;
+ return 0;
}
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,
#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;
};
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;
}
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;
*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;
}
}
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;
+ struct config_section_stack *cur_section = ctx->cur_section;
+ struct config_filter_parser *parser;
- parser = p_new(ctx->pool, struct config_filter_parser_list, 1);
- parser->filter = ctx->cur_filter->filter;
+ 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);
- 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]);
- }
+ cur_section->parsers = parser->parsers;
+}
- array_append(&ctx->all_parsers, &parser, 1);
- return parser;
+static struct config_section_stack *
+config_add_new_section(struct parser_context *ctx)
+{
+ 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;
}
-static void config_add_new_filter(struct parser_context *ctx)
+static struct config_filter_parser *
+config_filter_parser_find(struct parser_context *ctx,
+ const struct config_filter *filter)
{
- struct config_filter_stack *filter;
+ struct config_filter_parser *const *parsers;
+ unsigned int i, count;
- 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;
+ 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);
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)
-{
- 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)
+config_filter_parser_check(struct parser_context *ctx,
+ const struct config_module_parser *p,
+ 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
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;
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;
(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;
}
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:
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;
}
}
}
-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;
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;
}
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
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);
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;
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')
switch (c) {
case 'a':
break;
+ case 'f':
+ filter_parse_arg(&filter, optarg);
+ break;
case 'm':
module = optarg;
break;
case 'N':
scope = CONFIG_DUMP_SCOPE_SET;
break;
- case 'p':
- filter.service = optarg;
- break;
default:
if (!master_service_parse_option(master_service,
c, optarg))
}
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);
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]);
}