Currently caching won't work if config has any remote {} blocks.
--HG--
branch : HEAD
#include "ostream.h"
#include "settings-parser.h"
#include "master-service.h"
+#include "master-service-settings.h"
#include "config-request.h"
#include "config-parser.h"
#include "config-connection.h"
#define MAX_INBUF_SIZE 1024
-#define CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION 1
+#define CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION 2
#define CONFIG_CLIENT_PROTOCOL_MINOR_VERSION 0
struct config_connection {
static int config_connection_request(struct config_connection *conn,
const char *const *args)
{
+ struct config_export_context *ctx;
+ struct master_service_settings_output output;
struct config_filter filter;
const char *path, *error, *module = "";
}
o_stream_cork(conn->output);
- if (config_request_handle(&filter, module, CONFIG_DUMP_SCOPE_SET, 0,
- config_request_output, conn->output) < 0) {
+
+ ctx = config_export_init(&filter, module, CONFIG_DUMP_SCOPE_SET, 0,
+ config_request_output, conn->output);
+ config_export_get_output(ctx, &output);
+
+ if (output.service_uses_local)
+ o_stream_send_str(conn->output, "service-uses-local\t");
+ if (output.service_uses_remote)
+ o_stream_send_str(conn->output, "service-uses-remote\t");
+ if (output.used_local)
+ o_stream_send_str(conn->output, "used-local\t");
+ if (output.used_remote)
+ o_stream_send_str(conn->output, "used-remote\t");
+ o_stream_send_str(conn->output, "\n");
+
+ if (config_export_finish(&ctx) < 0) {
config_connection_destroy(conn);
return -1;
}
#include "lib.h"
#include "array.h"
#include "settings-parser.h"
+#include "master-service-settings.h"
#include "config-parser.h"
#include "config-filter.h"
struct config_filter_parser *const *parsers;
};
-bool config_filter_match(const struct config_filter *mask,
- const struct config_filter *filter)
+static bool config_filter_match_service(const struct config_filter *mask,
+ const struct config_filter *filter)
{
if (mask->service != NULL) {
if (filter->service == NULL)
return FALSE;
}
}
+ return TRUE;
+}
+
+static bool config_filter_match_rest(const struct config_filter *mask,
+ const struct config_filter *filter)
+{
if (mask->local_host != NULL) {
if (filter->local_host == NULL)
return FALSE;
return TRUE;
}
+bool config_filter_match(const struct config_filter *mask,
+ const struct config_filter *filter)
+{
+ if (!config_filter_match_service(mask, filter))
+ return FALSE;
+
+ return config_filter_match_rest(mask, filter);
+}
+
bool config_filters_equal(const struct config_filter *f1,
const struct config_filter *f2)
{
static struct config_filter_parser *const *
config_filter_find_all(struct config_filter_context *ctx,
- const struct config_filter *filter)
+ const struct config_filter *filter,
+ struct master_service_settings_output *output_r)
{
ARRAY_TYPE(config_filter_parsers) matches;
unsigned int i;
+ memset(output_r, 0, sizeof(*output_r));
+
t_array_init(&matches, 8);
for (i = 0; ctx->parsers[i] != NULL; i++) {
- if (config_filter_match(&ctx->parsers[i]->filter, filter))
+ const struct config_filter *mask = &ctx->parsers[i]->filter;
+
+ if (!config_filter_match_service(mask, filter))
+ continue;
+
+ if (mask->local_bits > 0)
+ output_r->service_uses_local = TRUE;
+ if (mask->remote_bits > 0)
+ output_r->service_uses_remote = TRUE;
+ if (config_filter_match_rest(mask, filter)) {
+ if (mask->local_bits > 0)
+ output_r->used_local = TRUE;
+ if (mask->remote_bits > 0)
+ output_r->used_remote = TRUE;
array_append(&matches, &ctx->parsers[i], 1);
+ }
}
array_sort(&matches, config_filter_parser_cmp);
(void)array_append_space(&matches);
int config_filter_parsers_get(struct config_filter_context *ctx, pool_t pool,
const struct config_filter *filter,
struct config_module_parser **parsers_r,
+ struct master_service_settings_output *output_r,
const char **error_r)
{
struct config_filter_parser *const *src;
const char *error, **error_p;
unsigned int i, count;
- src = config_filter_find_all(ctx, filter);
+ src = config_filter_find_all(ctx, filter, output_r);
/* all of them should have the same number of parsers.
duplicate our initial parsers from the first match */
#include "network.h"
+struct master_service_settings_output;
+
struct config_filter {
const char *service;
const char *local_host, *remote_host;
int config_filter_parsers_get(struct config_filter_context *ctx, pool_t pool,
const struct config_filter *filter,
struct config_module_parser **parsers_r,
+ struct master_service_settings_output *output_r,
const char **error_r);
void config_filter_parsers_free(struct config_module_parser *parsers);
#include "module-dir.h"
#include "settings-parser.h"
#include "service-settings.h"
+#include "master-service-settings.h"
#include "all-settings.h"
#include "config-filter.h"
#include "config-request.h"
{
struct config_filter_parser *const *parsers;
struct config_module_parser *tmp_parsers;
+ struct master_service_settings_output output;
unsigned int i, count;
pool_t tmp_pool;
int ret = 0;
for (i = 0; i < count && ret == 0; i++) {
if (config_filter_parsers_get(new_filter, tmp_pool,
&parsers[i]->filter,
- &tmp_parsers, error_r) < 0) {
+ &tmp_parsers, &output,
+ error_r) < 0) {
ret = -1;
break;
}
#include "config-parser.h"
#include "config-request.h"
-struct settings_export_context {
+struct config_export_context {
pool_t pool;
string_t *value;
string_t *prefix;
config_request_callback_t *callback;
void *context;
+ const char *module;
enum config_dump_flags flags;
+ struct config_module_parser *parsers;
+ struct master_service_settings_output output;
+
+ bool failed;
};
static bool parsers_are_connected(const struct setting_parser_info *root,
}
static void
-settings_export(struct settings_export_context *ctx,
+settings_export(struct config_export_context *ctx,
const struct setting_parser_info *info,
bool parent_unique_deflist,
const void *set, const void *change_set)
}
}
-int config_request_handle(const struct config_filter *filter,
- const char *module, enum config_dump_scope scope,
- enum config_dump_flags flags,
- config_request_callback_t *callback, void *context)
+struct config_export_context *
+config_export_init(const struct config_filter *filter,
+ const char *module, enum config_dump_scope scope,
+ enum config_dump_flags flags,
+ config_request_callback_t *callback, void *context)
+{
+ struct config_export_context *ctx;
+ const char *error;
+ pool_t pool;
+
+ pool = pool_alloconly_create("config export", 1024*64);
+ ctx = p_new(pool, struct config_export_context, 1);
+ ctx->pool = pool;
+
+ ctx->module = p_strdup(pool, module);
+ ctx->flags = flags;
+ ctx->callback = callback;
+ ctx->context = context;
+ ctx->scope = scope;
+ ctx->value = t_str_new(256);
+ ctx->prefix = t_str_new(64);
+ ctx->keys = hash_table_create(default_pool, ctx->pool, 0,
+ str_hash, (hash_cmp_callback_t *)strcmp);
+
+ if (config_filter_parsers_get(config_filter, ctx->pool, filter,
+ &ctx->parsers, &ctx->output,
+ &error) < 0) {
+ i_error("%s", error);
+ ctx->failed = TRUE;
+ }
+ return ctx;
+}
+
+void config_export_get_output(struct config_export_context *ctx,
+ struct master_service_settings_output *output_r)
+{
+ *output_r = ctx->output;
+}
+
+static void config_export_free(struct config_export_context *ctx)
{
- struct config_module_parser *parsers, *parser;
- struct settings_export_context ctx;
+ if (ctx->parsers != NULL)
+ config_filter_parsers_free(ctx->parsers);
+ hash_table_destroy(&ctx->keys);
+ pool_unref(&ctx->pool);
+}
+
+int config_export_finish(struct config_export_context **_ctx)
+{
+ struct config_export_context *ctx = *_ctx;
+ struct config_module_parser *parser;
const char *error;
unsigned int i;
int ret = 0;
- memset(&ctx, 0, sizeof(ctx));
- ctx.pool = pool_alloconly_create("config request", 1024*64);
+ *_ctx = NULL;
- if (config_filter_parsers_get(config_filter, ctx.pool, filter,
- &parsers, &error) < 0) {
- i_error("%s", error);
- pool_unref(&ctx.pool);
+ if (ctx->failed) {
+ config_export_free(ctx);
return -1;
}
- ctx.flags = flags;
- ctx.callback = callback;
- ctx.context = context;
- ctx.scope = scope;
- ctx.value = t_str_new(256);
- ctx.prefix = t_str_new(64);
- ctx.keys = hash_table_create(default_pool, ctx.pool, 0,
- str_hash, (hash_cmp_callback_t *)strcmp);
-
- for (i = 0; parsers[i].root != NULL; i++) {
- parser = &parsers[i];
- if (*module != '\0' &&
- !config_module_parser_is_in_service(parser, module))
+ for (i = 0; ctx->parsers[i].root != NULL; i++) {
+ parser = &ctx->parsers[i];
+ if (*ctx->module != '\0' &&
+ !config_module_parser_is_in_service(parser, ctx->module))
continue;
- settings_export(&ctx, parser->root, FALSE,
+ settings_export(ctx, parser->root, FALSE,
settings_parser_get(parser->parser),
settings_parser_get_changes(parser->parser));
- if ((flags & CONFIG_DUMP_FLAG_CHECK_SETTINGS) != 0) {
+ if ((ctx->flags & CONFIG_DUMP_FLAG_CHECK_SETTINGS) != 0) {
settings_parse_var_skip(parser->parser);
- if (!settings_parser_check(parser->parser, ctx.pool,
+ if (!settings_parser_check(parser->parser, ctx->pool,
&error)) {
- if ((flags & CONFIG_DUMP_FLAG_CALLBACK_ERRORS) != 0) {
- callback(NULL, error, CONFIG_KEY_ERROR,
- context);
+ if ((ctx->flags & CONFIG_DUMP_FLAG_CALLBACK_ERRORS) != 0) {
+ ctx->callback(NULL, error, CONFIG_KEY_ERROR,
+ ctx->context);
} else {
i_error("%s", error);
ret = -1;
}
}
}
- config_filter_parsers_free(parsers);
- hash_table_destroy(&ctx.keys);
- pool_unref(&ctx.pool);
+ config_export_free(ctx);
return ret;
}
#include "config-filter.h"
enum setting_type;
+struct master_service_settings_output;
enum config_dump_scope {
/* Dump all settings */
const void *default_value,
enum setting_type type, bool dump_default,
bool *dump_r);
-int config_request_handle(const struct config_filter *filter,
- const char *module, enum config_dump_scope scope,
- enum config_dump_flags flags,
- config_request_callback_t *callback, void *context);
+struct config_export_context *
+config_export_init(const struct config_filter *filter,
+ const char *module, enum config_dump_scope scope,
+ enum config_dump_flags flags,
+ config_request_callback_t *callback, void *context);
+void config_export_get_output(struct config_export_context *ctx,
+ struct master_service_settings_output *output_r);
+int config_export_finish(struct config_export_context **ctx);
#endif
static const char *indent_str = " ";
ARRAY_TYPE(const_string) prefixes_arr;
ARRAY_TYPE(prefix_stack) prefix_stack;
+ struct config_export_context *export_ctx;
struct prefix_stack prefix;
struct config_request_get_string_ctx ctx;
const char *const *strings, *const *args, *p, *str, *const *prefixes;
ctx.pool = pool_alloconly_create("config human strings", 10240);
i_array_init(&ctx.strings, 256);
i_array_init(&ctx.errors, 256);
- if (config_request_handle(filter, module, scope,
- CONFIG_DUMP_FLAG_CHECK_SETTINGS |
- CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS |
- CONFIG_DUMP_FLAG_CALLBACK_ERRORS,
- config_request_get_strings, &ctx) < 0)
+ export_ctx = config_export_init(filter, module, scope,
+ CONFIG_DUMP_FLAG_CHECK_SETTINGS |
+ CONFIG_DUMP_FLAG_HIDE_LIST_DEFAULTS |
+ CONFIG_DUMP_FLAG_CALLBACK_ERRORS,
+ config_request_get_strings, &ctx);
+ if (config_export_finish(&export_ctx) < 0)
return -1;
array_sort(&ctx.strings, config_string_cmp);
if (ret2 < 0)
i_fatal("Errors in configuration");
} else {
+ struct config_export_context *ctx;
+
env_put("DOVECONF_ENV=1");
- if (config_request_handle(&filter, module,
- CONFIG_DUMP_SCOPE_SET,
- CONFIG_DUMP_FLAG_CHECK_SETTINGS,
- config_request_putenv, NULL) < 0)
+ ctx = config_export_init(&filter, module,
+ CONFIG_DUMP_SCOPE_SET,
+ CONFIG_DUMP_FLAG_CHECK_SETTINGS,
+ config_request_putenv, NULL);
+ if (config_export_finish(&ctx) < 0)
i_fatal("Invalid configuration");
execvp(exec_args[0], exec_args);
i_fatal("execvp(%s) failed: %m", exec_args[0]);
master-login-auth.c \
master-service.c \
master-service-settings.c \
+ master-service-settings-cache.c \
syslog-util.c
headers = \
--- /dev/null
+/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "hash.h"
+#include "llist.h"
+#include "settings-parser.h"
+#include "master-service-private.h"
+#include "master-service-settings.h"
+#include "master-service-settings-cache.h"
+
+/* we start with just a guess. it's updated later. */
+#define CACHE_INITIAL_ENTRY_POOL_SIZE (1024*16)
+#define CACHE_ADD_ENTRY_POOL_SIZE 1024
+
+struct settings_entry {
+ struct settings_entry *prev, *next;
+
+ pool_t pool;
+ const char *local_host;
+ struct ip_addr local_ip;
+
+ struct setting_parser_context *parser;
+};
+
+struct master_service_settings_cache {
+ pool_t pool;
+
+ struct master_service *service;
+ const char *module;
+ const char *service_name;
+ size_t max_cache_size;
+
+ /* global settings for this service (after they've been read) */
+ struct setting_parser_context *global_parser;
+
+ /* cache for other settings (local_ip/local_host set) */
+ struct settings_entry *oldest, *newest;
+ /* separate list for entries whose parser=global_parser */
+ struct settings_entry *oldest_global, *newest_global;
+ /* local_host, local_ip => struct settings_entry */
+ struct hash_table *local_host_hash;
+ struct hash_table *local_ip_hash;
+
+ /* Initial size for new settings entry pools */
+ size_t approx_entry_pool_size;
+ /* number of bytes malloced by cached settings entries
+ (doesn't count memory used by hash table or global sets) */
+ size_t cache_malloc_size;
+
+ unsigned int done_initial_lookup:1;
+ unsigned int service_uses_local:1;
+ unsigned int service_uses_remote:1;
+};
+
+struct master_service_settings_cache *
+master_service_settings_cache_init(struct master_service *service,
+ const char *module, const char *service_name)
+{
+ struct master_service_settings_cache *cache;
+ pool_t pool;
+
+ pool = pool_alloconly_create("master service settings cache", 1024*32);
+ cache = p_new(pool, struct master_service_settings_cache, 1);
+ cache->pool = pool;
+ cache->service = service;
+ cache->module = p_strdup(pool, module);
+ cache->service_name = p_strdup(pool, service_name);
+ cache->max_cache_size = (size_t)-1;
+ return cache;
+}
+
+void master_service_settings_cache_deinit(struct master_service_settings_cache **_cache)
+{
+ struct master_service_settings_cache *cache = *_cache;
+ struct settings_entry *entry, *next;
+
+ for (entry = cache->oldest_global; entry != NULL; entry = next) {
+ next = entry->next;
+ pool_unref(&entry->pool);
+ }
+ for (entry = cache->oldest; entry != NULL; entry = next) {
+ next = entry->next;
+ pool_unref(&entry->pool);
+ }
+ if (cache->local_host_hash != NULL)
+ hash_table_destroy(&cache->local_host_hash);
+ if (cache->local_ip_hash != NULL)
+ hash_table_destroy(&cache->local_ip_hash);
+ pool_unref(&cache->pool);
+}
+
+static bool
+cache_can_return_global(struct master_service_settings_cache *cache,
+ const struct master_service_settings_input *input)
+{
+ if (cache->service_uses_local) {
+ if (input->local_host != NULL || input->local_ip.family != 0)
+ return FALSE;
+ }
+ if (cache->service_uses_remote) {
+ if (input->remote_host != NULL || input->remote_ip.family != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool
+cache_find(struct master_service_settings_cache *cache,
+ const struct master_service_settings_input *input,
+ const struct setting_parser_context **parser_r)
+{
+ struct settings_entry *entry;
+
+ if (!cache->done_initial_lookup)
+ return FALSE;
+
+ if (cache_can_return_global(cache, input)) {
+ if (cache->global_parser != NULL) {
+ *parser_r = cache->global_parser;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ if (cache->service_uses_remote)
+ return FALSE;
+
+ if (cache->local_host_hash != NULL && input->local_host != NULL) {
+ /* see if we have it already in cache */
+ entry = hash_table_lookup(cache->local_host_hash,
+ input->local_host);
+ } else if (cache->local_ip_hash != NULL &&
+ input->local_ip.family != 0) {
+ entry = hash_table_lookup(cache->local_ip_hash,
+ &input->local_ip);
+ } else {
+ entry = NULL;
+ }
+
+ if (entry != NULL) {
+ *parser_r = entry->parser;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+setting_entry_detach(struct master_service_settings_cache *cache,
+ struct settings_entry *entry)
+{
+
+ DLLIST2_REMOVE(&cache->oldest, &cache->newest, entry);
+ cache->cache_malloc_size -=
+ pool_alloconly_get_total_alloc_size(entry->pool);
+
+ if (entry->local_host != NULL)
+ hash_table_remove(cache->local_host_hash, entry->local_host);
+ if (entry->local_ip.family != 0)
+ hash_table_remove(cache->local_ip_hash, &entry->local_ip);
+}
+
+static void cache_add(struct master_service_settings_cache *cache,
+ const struct master_service_settings_input *input,
+ const struct master_service_settings_output *output,
+ struct setting_parser_context *parser)
+{
+ struct settings_entry *entry;
+ pool_t pool;
+ size_t pool_size;
+ char *entry_local_host;
+
+ if (!output->used_local && !output->used_remote) {
+ /* these are same as global settings */
+ if (cache->global_parser == NULL) {
+ cache->global_parser =
+ settings_parser_dup(parser, cache->pool);
+ }
+ }
+ if (cache->service_uses_remote) {
+ /* for now we don't try to handle caching remote IPs */
+ return;
+ }
+
+ if (input->local_host == NULL && input->local_ip.family == 0)
+ return;
+
+ if (!output->used_local) {
+ /* use global settings, but add local_ip/host to hash tables
+ so we'll find them */
+ pool = pool_alloconly_create("settings global entry", 128);
+ entry = p_new(pool, struct settings_entry, 1);
+ } else if (cache->cache_malloc_size >= cache->max_cache_size) {
+ /* free the oldest and reuse its pool */
+ entry = cache->oldest;
+ pool = entry->pool;
+ setting_entry_detach(cache, entry);
+ p_clear(pool);
+ } else {
+ pool_size = cache->approx_entry_pool_size != 0 ?
+ cache->approx_entry_pool_size :
+ CACHE_INITIAL_ENTRY_POOL_SIZE;
+ pool = pool_alloconly_create("settings entry", pool_size);
+ entry = p_new(pool, struct settings_entry, 1);
+ }
+ entry->pool = pool;
+ entry_local_host = p_strdup(pool, input->local_host);
+ entry->local_host = entry_local_host;
+ entry->local_ip = input->local_ip;
+ if (!output->used_local) {
+ entry->parser = cache->global_parser;
+ DLLIST2_PREPEND(&cache->oldest_global, &cache->newest_global,
+ entry);
+ } else {
+ entry->parser = settings_parser_dup(parser, entry->pool);
+ DLLIST2_PREPEND(&cache->oldest, &cache->newest, entry);
+
+ pool_size = pool_alloconly_get_total_used_size(pool);
+ if (pool_size > cache->approx_entry_pool_size) {
+ cache->approx_entry_pool_size = pool_size +
+ CACHE_ADD_ENTRY_POOL_SIZE;
+ }
+ }
+ cache->cache_malloc_size += pool_alloconly_get_total_alloc_size(pool);
+
+ if (input->local_host != NULL) {
+ if (cache->local_host_hash == NULL) {
+ cache->local_host_hash =
+ hash_table_create(default_pool, cache->pool, 0,
+ str_hash,
+ (hash_cmp_callback_t *)strcmp);
+ }
+ hash_table_insert(cache->local_host_hash,
+ entry_local_host, entry);
+ }
+ if (input->local_ip.family != 0) {
+ if (cache->local_ip_hash == NULL) {
+ cache->local_ip_hash =
+ hash_table_create(default_pool, cache->pool, 0,
+ (hash_callback_t *)net_ip_hash,
+ (hash_cmp_callback_t *)net_ip_cmp);
+ }
+ hash_table_insert(cache->local_ip_hash,
+ &entry->local_ip, entry);
+ }
+}
+
+int master_service_settings_cache_read(struct master_service_settings_cache *cache,
+ const struct master_service_settings_input *input,
+ const struct setting_parser_context **parser_r,
+ const char **error_r)
+{
+ struct master_service_settings_output output;
+ const struct master_service_settings *set;
+
+ i_assert(strcmp(input->module, cache->module) == 0);
+ i_assert(strcmp(input->service, cache->service_name) == 0);
+
+ if (cache_find(cache, input, parser_r))
+ return 0;
+
+ if (master_service_settings_read(cache->service, input,
+ &output, error_r) < 0)
+ return -1;
+
+ if (!cache->done_initial_lookup) {
+ cache->done_initial_lookup = TRUE;
+ cache->service_uses_local = output.service_uses_local;
+ cache->service_uses_remote = output.service_uses_remote;
+
+ set = master_service_settings_get(cache->service);
+ cache->max_cache_size = set->config_cache_size;
+ }
+
+ if (output.used_local && !cache->service_uses_local) {
+ *error_r = "BUG: config unexpectedly returned local settings";
+ return -1;
+ }
+ if (output.used_remote && !cache->service_uses_remote) {
+ *error_r = "BUG: config unexpectedly returned remote settings";
+ return -1;
+ }
+
+ cache_add(cache, input, &output, cache->service->set_parser);
+ *parser_r = cache->service->set_parser;
+ return 0;
+}
--- /dev/null
+#ifndef MASTER_SERVICE_SETTINGS_CACHE_H
+#define MASTER_SERVICE_SETTINGS_CACHE_H
+
+struct master_service_settings_cache *
+master_service_settings_cache_init(struct master_service *service,
+ const char *module,
+ const char *service_name);
+void master_service_settings_cache_deinit(struct master_service_settings_cache **cache);
+
+int master_service_settings_cache_read(struct master_service_settings_cache *cache,
+ const struct master_service_settings_input *input,
+ const struct setting_parser_context **parser_r,
+ const char **error_r);
+
+#endif
#define DOVECOT_CONFIG_SOCKET_PATH PKG_RUNDIR"/config"
#define CONFIG_READ_TIMEOUT_SECS 10
-#define CONFIG_HANDSHAKE "VERSION\tconfig\t1\t0\n"
+#define CONFIG_HANDSHAKE "VERSION\tconfig\t2\t0\n"
#undef DEF
#define DEF(type, name) \
DEF(SET_STR, debug_log_path),
DEF(SET_STR, log_timestamp),
DEF(SET_STR, syslog_facility),
+ DEF(SET_SIZE, config_cache_size),
DEF(SET_BOOL, version_ignore),
DEF(SET_BOOL, shutdown_clients),
.debug_log_path = "",
.log_timestamp = DEFAULT_FAILURE_STAMP_FORMAT,
.syslog_facility = "mail",
+ .config_cache_size = 1024*1024,
.version_ignore = FALSE,
.shutdown_clients = TRUE
};
return 0;
}
+static int
+config_read_reply_header(struct istream *input,
+ struct master_service_settings_output *output_r)
+{
+ const char *line;
+ ssize_t ret;
+
+ while ((ret = i_stream_read(input)) > 0) {
+ line = i_stream_next_line(input);
+ if (line != NULL)
+ break;
+ }
+ if (ret <= 0)
+ return ret;
+
+ T_BEGIN {
+ const char *const *arg = t_strsplit(line, "\t");
+
+ for (; *arg != NULL; arg++) {
+ if (strcmp(*arg, "service-uses-local") == 0)
+ output_r->service_uses_local = TRUE;
+ else if (strcmp(*arg, "service-uses-remote") == 0)
+ output_r->service_uses_remote = TRUE;
+ if (strcmp(*arg, "used-local") == 0)
+ output_r->used_local = TRUE;
+ else if (strcmp(*arg, "used-remote") == 0)
+ output_r->used_remote = TRUE;
+ }
+ } T_END;
+ return 1;
+}
+
int master_service_settings_read(struct master_service *service,
const struct master_service_settings_input *input,
+ struct master_service_settings_output *output_r,
const char **error_r)
{
ARRAY_DEFINE(all_roots, const struct setting_parser_info *);
unsigned int i;
int ret, fd = -1;
time_t now, timeout;
- bool standalone_config_from_socket = FALSE;
+ bool config_socket = FALSE, standalone_config_from_socket = FALSE;
+
+ memset(output_r, 0, sizeof(*output_r));
if (getenv("DOVECONF_ENV") == NULL &&
(service->flags & MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS) == 0) {
(void)close(fd);
return -1;
}
+ config_socket = TRUE;
}
if (service->set_pool != NULL) {
timeout = now + CONFIG_READ_TIMEOUT_SECS;
do {
alarm(timeout - now);
- ret = settings_parse_stream_read(parser, istream);
+ ret = !config_socket ? 1 :
+ config_read_reply_header(istream, output_r);
+ if (ret > 0) {
+ ret = settings_parse_stream_read(parser,
+ istream);
+ }
alarm(0);
if (ret <= 0)
break;
const char **error_r)
{
struct master_service_settings_input input;
+ struct master_service_settings_output output;
memset(&input, 0, sizeof(input));
input.roots = roots;
input.module = service->name;
- return master_service_settings_read(service, &input, error_r);
+ return master_service_settings_read(service, &input, &output, error_r);
}
pool_t master_service_settings_detach(struct master_service *service)
const char *debug_log_path;
const char *log_timestamp;
const char *syslog_facility;
+ uoff_t config_cache_size;
bool version_ignore;
bool shutdown_clients;
};
const char *local_host, *remote_host;
};
+struct master_service_settings_output {
+ /* some settings for this service contain local/remote ip/host
+ specific settings. */
+ unsigned int service_uses_local:1;
+ unsigned int service_uses_remote:1;
+ /* returned settings contain settings specific to given
+ local/remote ip/host */
+ unsigned int used_local:1;
+ unsigned int used_remote:1;
+};
+
extern const struct setting_parser_info master_service_setting_parser_info;
int master_service_settings_read(struct master_service *service,
const struct master_service_settings_input *input,
+ struct master_service_settings_output *output_r,
const char **error_r);
int master_service_settings_read_simple(struct master_service *service,
const struct setting_parser_info **roots,
#include "auth-master.h"
#include "master-service-private.h"
#include "master-service-settings.h"
+#include "master-service-settings-cache.h"
#include "mail-user.h"
#include "mail-namespace.h"
#include "mail-storage.h"
#define MAX_NOWARN_FORWARD_SECS 10
struct mail_storage_service_ctx {
+ pool_t pool;
struct master_service *service;
struct auth_master_connection *conn;
struct auth_master_user_list_ctx *auth_list;
const struct setting_parser_info **set_roots;
enum mail_storage_service_flags flags;
+ const char *set_cache_module, *set_cache_service;
+ struct master_service_settings_cache *set_cache;
+ const struct dynamic_settings_parser *set_cache_dyn_parsers;
+ struct setting_parser_info *set_cache_dyn_parsers_parent;
+ const struct setting_parser_info **set_cache_roots;
+
unsigned int debug:1;
};
struct mail_storage_service_user *user)
{
const struct mail_user_settings *user_set;
- void **sets;
-
- sets = master_service_settings_get_others(service);
- user_set = sets[0];
+ user_set = master_service_settings_get_others(service)[0];
T_BEGIN {
string_t *str;
enum mail_storage_service_flags flags)
{
struct mail_storage_service_ctx *ctx;
+ pool_t pool;
unsigned int count;
(void)umask(0077);
mail_storage_register_all();
mailbox_list_register_all();
- ctx = i_new(struct mail_storage_service_ctx, 1);
+ pool = pool_alloconly_create("mail storage service", 2048);
+ ctx = p_new(pool, struct mail_storage_service_ctx, 1);
+ ctx->pool = pool;
ctx->service = service;
ctx->flags = flags;
static struct setting_parser_info *
dyn_parsers_update_parent(pool_t pool,
- struct master_service_settings_input *input)
+ const struct setting_parser_info ***_roots,
+ const struct dynamic_settings_parser **_dyn_parsers)
{
+ const struct dynamic_settings_parser *dyn_parsers = *_dyn_parsers;
+ const const struct setting_parser_info **roots = *_roots;
const struct setting_parser_info *old_parent, **new_roots;
struct setting_parser_info *new_parent, *new_info;
struct dynamic_settings_parser *new_dyn_parsers;
/* settings_parser_info_update() modifies the parent structure.
since we may be using the same structure later, we want it to be
in its original state, so we'll have to copy all structures. */
- old_parent = input->dyn_parsers[0].info->parent;
+ old_parent = dyn_parsers[0].info->parent;
new_parent = p_new(pool, struct setting_parser_info, 1);
*new_parent = *old_parent;
settings_parser_update_children_parent(new_parent, pool);
/* update root */
- for (count = 0; input->roots[count] != NULL; count++) ;
+ for (count = 0; roots[count] != NULL; count++) ;
new_roots = p_new(pool, const struct setting_parser_info *, count + 1);
for (i = 0; i < count; i++) {
- if (input->roots[i] == old_parent)
+ if (roots[i] == old_parent)
new_roots[i] = new_parent;
else
- new_roots[i] = input->roots[i];
+ new_roots[i] = roots[i];
}
- input->roots = new_roots;
+ *_roots = new_roots;
/* update parent in dyn_parsers */
- for (count = 0; input->dyn_parsers[count].name != NULL; count++) ;
+ for (count = 0; dyn_parsers[count].name != NULL; count++) ;
new_dyn_parsers = p_new(pool, struct dynamic_settings_parser, count + 1);
for (i = 0; i < count; i++) {
- new_dyn_parsers[i] = input->dyn_parsers[i];
+ new_dyn_parsers[i] = dyn_parsers[i];
new_info = p_new(pool, struct setting_parser_info, 1);
- *new_info = *input->dyn_parsers[i].info;
+ *new_info = *dyn_parsers[i].info;
new_info->parent = new_parent;
new_dyn_parsers[i].info = new_info;
}
- input->dyn_parsers = new_dyn_parsers;
+ *_dyn_parsers = new_dyn_parsers;
return new_parent;
}
const struct mail_storage_service_input *input,
pool_t pool,
const struct setting_parser_info **user_info_r,
+ const struct setting_parser_context **parser_r,
const char **error_r)
{
struct master_service_settings_input set_input;
+ struct master_service_settings_output set_output;
unsigned int i;
memset(&set_input, 0, sizeof(set_input));
set_input.roots = ctx->set_roots;
- set_input.dyn_parsers = mail_storage_get_dynamic_parsers();
/* settings reader may exec doveconf, which is going to clear
environment, and if we're not doing a userdb lookup we want to
use $HOME */
set_input.preserve_home =
(ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0;
- set_input.dyn_parsers_parent =
- dyn_parsers_update_parent(pool, &set_input);
if (input != NULL) {
set_input.module = input->module;
set_input.local_ip = input->local_ip;
set_input.remote_ip = input->remote_ip;
}
- if (master_service_settings_read(ctx->service, &set_input,
- error_r) < 0) {
- *error_r = t_strdup_printf("Error reading configuration: %s",
- *error_r);
- return -1;
+ if (ctx->set_cache == NULL) {
+ ctx->set_cache_module = p_strdup(ctx->pool, input->module);
+ ctx->set_cache_service = p_strdup(ctx->pool, input->service);
+ ctx->set_cache = master_service_settings_cache_init(
+ ctx->service, input->module, input->service);
+ ctx->set_cache_roots = ctx->set_roots;
+ ctx->set_cache_dyn_parsers =
+ mail_storage_get_dynamic_parsers(ctx->pool);
+ ctx->set_cache_dyn_parsers_parent =
+ dyn_parsers_update_parent(ctx->pool,
+ &ctx->set_cache_roots,
+ &ctx->set_cache_dyn_parsers);
+ }
+
+ if (strcmp(input->module, ctx->set_cache_module) == 0 &&
+ strcmp(input->service, ctx->set_cache_service) == 0) {
+ set_input.roots = ctx->set_cache_roots;
+ set_input.dyn_parsers = ctx->set_cache_dyn_parsers;
+ set_input.dyn_parsers_parent =
+ ctx->set_cache_dyn_parsers_parent;
+ if (master_service_settings_cache_read(ctx->set_cache,
+ &set_input,
+ parser_r, error_r) < 0)
+ return -1;
+ } else {
+ set_input.dyn_parsers = mail_storage_get_dynamic_parsers(pool);
+ set_input.dyn_parsers_parent =
+ dyn_parsers_update_parent(pool, &set_input.roots,
+ &set_input.dyn_parsers);
+ if (master_service_settings_read(ctx->service, &set_input,
+ &set_output, error_r) < 0) {
+ *error_r = t_strdup_printf(
+ "Error reading configuration: %s", *error_r);
+ return -1;
+ }
+ *parser_r = ctx->service->set_parser;
}
for (i = 0; ctx->set_roots[i] != NULL; i++) {
- if (ctx->set_roots[i] == &mail_user_setting_parser_info) {
+ if (strcmp(ctx->set_roots[i]->module_name,
+ mail_user_setting_parser_info.module_name) == 0) {
*user_info_r = set_input.roots[i];
return 0;
}
const struct mail_user_settings *user_set;
const char *const *userdb_fields;
struct auth_user_reply reply;
- void **sets;
+ const struct setting_parser_context *set_parser;
pool_t user_pool, temp_pool;
int ret = 1;
user_pool = pool_alloconly_create("mail storage service user", 1024*5);
if (mail_storage_service_read_settings(ctx, input, user_pool,
- &user_info, error_r) < 0) {
+ &user_info, &set_parser,
+ error_r) < 0) {
pool_unref(&user_pool);
return -1;
}
- sets = settings_parser_get_list(ctx->service->set_parser);
- user_set = sets[1];
+ user_set = settings_parser_get_list(set_parser)[1];
if (ctx->conn == NULL)
mail_storage_service_first_init(ctx, user_info, user_set);
user->input.username = p_strdup(user_pool, username);
user->user_info = user_info;
- user->set_parser =
- settings_parser_dup(ctx->service->set_parser, user_pool);
+ user->set_parser = settings_parser_dup(set_parser, user_pool);
if (!settings_parser_check(user->set_parser, user_pool, error_r))
i_unreached();
- sets = settings_parser_get_list(user->set_parser);
- user->user_set = sets[1];
+ user->user_set = settings_parser_get_list(user->set_parser)[1];
if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) == 0) {
const char *home = getenv("HOME");
{
const struct setting_parser_info *user_info;
const struct mail_user_settings *user_set;
+ const struct setting_parser_context *set_parser;
const char *error;
- void **sets;
pool_t temp_pool;
if (ctx->conn != NULL)
temp_pool = pool_alloconly_create("service all settings", 4096);
if (mail_storage_service_read_settings(ctx, input, temp_pool,
- &user_info, &error) < 0)
+ &user_info, &set_parser,
+ &error) < 0)
i_fatal("%s", error);
- sets = settings_parser_get_list(ctx->service->set_parser);
- user_set = sets[1];
+ user_set = settings_parser_get_list(set_parser)[1];
mail_storage_service_first_init(ctx, user_info, user_set);
pool_unref(&temp_pool);
mail_user_auth_master_conn = NULL;
auth_master_deinit(&ctx->conn);
}
- i_free(ctx);
+ if (ctx->set_cache != NULL)
+ master_service_settings_cache_deinit(&ctx->set_cache);
+ pool_unref(&ctx->pool);
module_dir_unload(&modules);
mail_storage_deinit();
struct master_service;
struct mail_user;
+struct setting_parser_context;
enum mail_storage_service_flags {
/* Fail if we don't drop root privileges */
const struct mail_storage_service_input *input,
pool_t pool,
const struct setting_parser_info **user_info_r,
+ const struct setting_parser_context **parser_r,
const char **error_r);
/* Read settings and initialize context to use them. Do nothing if service is
already initialized. This is mainly necessary when calling _get_auth_conn()
return index_flags;
}
-const struct dynamic_settings_parser *mail_storage_get_dynamic_parsers(void)
+const struct dynamic_settings_parser *
+mail_storage_get_dynamic_parsers(pool_t pool)
{
struct dynamic_settings_parser *parsers;
struct mail_storage *const *storages;
unsigned int i, j, count;
storages = array_get(&mail_storage_classes, &count);
- parsers = t_new(struct dynamic_settings_parser, count + 1);
+ parsers = p_new(pool, struct dynamic_settings_parser, count + 1);
parsers[0].name = MAIL_STORAGE_SET_DRIVER_NAME;
parsers[0].info = &mail_storage_setting_parser_info;
enum mail_index_open_flags
mail_storage_settings_to_index_flags(const struct mail_storage_settings *set);
-const struct dynamic_settings_parser *mail_storage_get_dynamic_parsers(void);
+const struct dynamic_settings_parser *
+mail_storage_get_dynamic_parsers(pool_t pool);
#endif
static void client_read_settings(struct client *client)
{
struct mail_storage_service_input input;
+ const struct setting_parser_context *set_parser;
const char *error;
memset(&input, 0, sizeof(input));
if (mail_storage_service_read_settings(storage_service, &input,
client->pool,
&client->user_set_info,
- &error) < 0)
+ &set_parser, &error) < 0)
i_fatal("%s", error);
- lmtp_settings_dup(client->pool, &client->lmtp_set, &client->set);
+ lmtp_settings_dup(set_parser, client->pool,
+ &client->lmtp_set, &client->set);
}
static void client_generate_session_id(struct client *client)
.dependencies = lmtp_setting_dependencies
};
-void lmtp_settings_dup(pool_t pool, const struct lmtp_settings **lmtp_set_r,
+void lmtp_settings_dup(const struct setting_parser_context *set_parser,
+ pool_t pool, const struct lmtp_settings **lmtp_set_r,
const struct lda_settings **lda_set_r)
{
void **sets;
- sets = master_service_settings_get_others(master_service);
+ sets = settings_parser_get_list(set_parser) + 1;
*lda_set_r = settings_dup(&lda_setting_parser_info, sets[1], pool);
*lmtp_set_r = settings_dup(&lmtp_setting_parser_info, sets[2], pool);
}
extern const struct setting_parser_info lmtp_setting_parser_info;
-void lmtp_settings_dup(pool_t pool, const struct lmtp_settings **lmtp_set_r,
+void lmtp_settings_dup(const struct setting_parser_context *set_parser,
+ pool_t pool, const struct lmtp_settings **lmtp_set_r,
const struct lda_settings **lda_set_r);
#endif
#include "hostpid.h"
#include "var-expand.h"
#include "settings-parser.h"
+#include "master-service.h"
#include "master-service-settings.h"
+#include "master-service-settings-cache.h"
#include "login-settings.h"
#include <stddef.h>
const struct setting_parser_info **login_set_roots = default_login_set_roots;
+static struct master_service_settings_cache *set_cache;
+
/* <settings checks> */
static int ssl_settings_check(void *_set ATTR_UNUSED, const char **error_r)
{
}
struct login_settings *
-login_settings_read(struct master_service *service, pool_t pool,
+login_settings_read(pool_t pool,
const struct ip_addr *local_ip,
const struct ip_addr *remote_ip,
const char *local_host,
{
struct master_service_settings_input input;
const char *error;
+ const struct setting_parser_context *parser;
+ void *const *cache_sets;
void **sets;
- unsigned int i;
+ unsigned int i, count;
memset(&input, 0, sizeof(input));
input.roots = login_set_roots;
if (remote_ip != NULL)
input.remote_ip = *remote_ip;
- /* this function always clears the previous settings pool. since we're
- doing per-connection lookups, we always need to duplicate the
- settings using another pool. */
- if (master_service_settings_read(service, &input, &error) < 0)
+ if (set_cache == NULL) {
+ set_cache = master_service_settings_cache_init(master_service,
+ input.module,
+ input.service);
+ }
+
+ if (master_service_settings_cache_read(set_cache, &input,
+ &parser, &error) < 0)
i_fatal("Error reading configuration: %s", error);
- sets = master_service_settings_get_others(service);
- for (i = 0; sets[i] != NULL; i++) {
- sets[i] = settings_dup(input.roots[i], sets[i], pool);
+ cache_sets = settings_parser_get_list(parser) + 1;
+ for (count = 0; cache_sets[count] != NULL; count++) ;
+ i_assert(input.roots[count] == NULL);
+ sets = p_new(pool, void *, count + 1);
+ for (i = 0; i < count; i++) {
+ sets[i] = settings_dup(input.roots[i], cache_sets[i], pool);
if (!settings_check(input.roots[i], pool, sets[i], &error)) {
const char *name = input.roots[i]->module_name;
i_fatal("settings_check(%s) failed: %s",
*other_settings_r = sets + 1;
return sets[0];
}
+
+void login_settings_deinit(void)
+{
+ if (set_cache != NULL)
+ master_service_settings_cache_deinit(&set_cache);
+}
#ifndef LOGIN_SETTINGS_H
#define LOGIN_SETTINGS_H
-struct master_service;
-
struct login_settings {
const char *login_trusted_networks;
const char *login_greeting;
extern const struct setting_parser_info login_setting_parser_info;
struct login_settings *
-login_settings_read(struct master_service *service, pool_t pool,
+login_settings_read(pool_t pool,
const struct ip_addr *local_ip,
const struct ip_addr *remote_ip,
const char *local_host,
void ***other_settings_r);
+void login_settings_deinit(void);
#endif
}
pool = pool_alloconly_create("login client", 5*1024);
- set = login_settings_read(master_service, pool, &local_ip,
+ set = login_settings_read(pool, &local_ip,
&conn->remote_ip, NULL, &other_sets);
if (!ssl_connections && !conn->ssl) {
anvil_client_deinit(&anvil);
if (auth_client_to != NULL)
timeout_remove(&auth_client_to);
+ login_settings_deinit();
}
int main(int argc, char *argv[])
set_pool = pool_alloconly_create("global login settings", 4096);
global_login_settings =
- login_settings_read(master_service, set_pool, NULL, NULL, NULL,
+ login_settings_read(set_pool, NULL, NULL, NULL,
&global_other_settings);
/* main_preinit() needs to know the client limit, which is set by
#include "safe-memset.h"
#include "hash.h"
#include "llist.h"
-#include "master-service.h"
#include "master-interface.h"
#include "client-common.h"
#include "ssl-proxy.h"
host = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
client = proxy->client;
- client->set = login_settings_read(master_service, client->pool,
+ client->set = login_settings_read(client->pool,
&client->local_ip, &client->ip, host,
&other_sets);
ctx = ssl_server_context_get(client->set);
void *context ATTR_UNUSED)
{
struct master_service_settings_input input;
+ struct master_service_settings_output output;
const struct master_settings *set;
void **sets;
struct service_list *new_services;
input.roots = set_roots;
input.module = MASTER_SERVICE_NAME;
input.config_path = services_get_config_socket_path(services);
- if (master_service_settings_read(master_service, &input, &error) < 0) {
+ if (master_service_settings_read(master_service, &input,
+ &output, &error) < 0) {
i_error("Error reading configuration: %s", error);
return;
}