]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-master: Added support for caching config lookups.
authorTimo Sirainen <tss@iki.fi>
Sat, 20 Feb 2010 15:16:41 +0000 (17:16 +0200)
committerTimo Sirainen <tss@iki.fi>
Sat, 20 Feb 2010 15:16:41 +0000 (17:16 +0200)
Currently caching won't work if config has any remote {} blocks.

--HG--
branch : HEAD

24 files changed:
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
src/lib-master/Makefile.am
src/lib-master/master-service-settings-cache.c [new file with mode: 0644]
src/lib-master/master-service-settings-cache.h [new file with mode: 0644]
src/lib-master/master-service-settings.c
src/lib-master/master-service-settings.h
src/lib-storage/mail-storage-service.c
src/lib-storage/mail-storage-service.h
src/lib-storage/mail-storage-settings.c
src/lib-storage/mail-storage-settings.h
src/lmtp/client.c
src/lmtp/lmtp-settings.c
src/lmtp/lmtp-settings.h
src/login-common/login-settings.c
src/login-common/login-settings.h
src/login-common/main.c
src/login-common/ssl-proxy-openssl.c
src/master/main.c

index 94cced4d5b7e3a589563e6e10086cf079f555386..4e1943c8dac449b14d25a945a6f5504ee236e93d 100644 (file)
@@ -6,6 +6,7 @@
 #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"
@@ -15,7 +16,7 @@
 
 #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 {
@@ -65,6 +66,8 @@ config_request_output(const char *key, const char *value,
 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 = "";
 
@@ -106,8 +109,22 @@ static int config_connection_request(struct config_connection *conn,
        }
 
        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;
        }
index b63dbf5a146c61016abd0acc45e5e95067635dab..1866e49eca084ed623309d32e65c6af9fd40aeae 100644 (file)
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "array.h"
 #include "settings-parser.h"
+#include "master-service-settings.h"
 #include "config-parser.h"
 #include "config-filter.h"
 
@@ -11,8 +12,8 @@ struct config_filter_context {
        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)
@@ -26,6 +27,12 @@ bool config_filter_match(const struct config_filter *mask,
                                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;
@@ -56,6 +63,15 @@ bool config_filter_match(const struct config_filter *mask,
        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)
 {
@@ -134,15 +150,32 @@ config_filter_parser_cmp(struct config_filter_parser *const *p1,
 
 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);
@@ -187,6 +220,7 @@ config_module_parser_apply_changes(struct config_module_parser *dest,
 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;
@@ -194,7 +228,7 @@ int config_filter_parsers_get(struct config_filter_context *ctx, pool_t pool,
        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 */
index bec0f46e214687ebe47aca7ea5e8182457f34f10..d2156559e07762cba6d1f15d580765d2e0357574 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "network.h"
 
+struct master_service_settings_output;
+
 struct config_filter {
        const char *service;
        const char *local_host, *remote_host;
@@ -29,6 +31,7 @@ void config_filter_add_all(struct config_filter_context *ctx,
 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);
 
index 05d4141f750b2186f5de15e640eea37cc304f202..034006f2970427bca740cdb93643e197bfb87f52 100644 (file)
@@ -9,6 +9,7 @@
 #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"
@@ -321,6 +322,7 @@ config_all_parsers_check(struct parser_context *ctx,
 {
        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;
@@ -332,7 +334,8 @@ config_all_parsers_check(struct parser_context *ctx,
        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;
                }
index 75875227dc80545850f52a6fff7b1daffb9aaa43..29f0c15b8a93e49ef3358d9c9fc4ca5a99d20715 100644 (file)
@@ -11,7 +11,7 @@
 #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;
@@ -21,7 +21,12 @@ struct settings_export_context {
        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,
@@ -157,7 +162,7 @@ bool config_export_type(string_t *str, const void *value,
 }
 
 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)
@@ -305,53 +310,85 @@ settings_export(struct settings_export_context *ctx,
        }
 }
 
-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;
@@ -360,8 +397,6 @@ int config_request_handle(const struct config_filter *filter,
                        }
                }
        }
-       config_filter_parsers_free(parsers);
-       hash_table_destroy(&ctx.keys);
-       pool_unref(&ctx.pool);
+       config_export_free(ctx);
        return ret;
 }
index bbc97b266a68fce95d1f36283ae682887d52b218..85f40fbb30bf27617724bbd0931fb5b45f3282b3 100644 (file)
@@ -4,6 +4,7 @@
 #include "config-filter.h"
 
 enum setting_type;
+struct master_service_settings_output;
 
 enum config_dump_scope {
        /* Dump all settings */
@@ -36,9 +37,13 @@ bool config_export_type(string_t *str, const void *value,
                        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
index b488dd3c887a7002acf6253ef6b4222cdf1086ed..87e2c41c50325f9a954cdcf2eb2840dd53a556c3 100644 (file)
@@ -114,6 +114,7 @@ static int config_connection_request_human(struct ostream *output,
        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;
@@ -127,11 +128,12 @@ static int config_connection_request_human(struct ostream *output,
        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);
@@ -419,11 +421,14 @@ int main(int argc, char *argv[])
                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]);
index c6b3f3d20aa33dbbb40a57f64d6636e4a0c5d1bf..bfa9d985e84dc5925957b02e65597952fe89ad66 100644 (file)
@@ -16,6 +16,7 @@ libmaster_la_SOURCES = \
        master-login-auth.c \
        master-service.c \
        master-service-settings.c \
+       master-service-settings-cache.c \
        syslog-util.c
 
 headers = \
diff --git a/src/lib-master/master-service-settings-cache.c b/src/lib-master/master-service-settings-cache.c
new file mode 100644 (file)
index 0000000..0206540
--- /dev/null
@@ -0,0 +1,286 @@
+/* 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;
+}
diff --git a/src/lib-master/master-service-settings-cache.h b/src/lib-master/master-service-settings-cache.h
new file mode 100644 (file)
index 0000000..200dad4
--- /dev/null
@@ -0,0 +1,15 @@
+#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
index 197b487b39b1d3e6499558f47835a9bf77599fcb..13b89ef0c64c7b52230b0199e19b89cb292c7101 100644 (file)
@@ -20,7 +20,7 @@
 #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) \
@@ -32,6 +32,7 @@ static const struct setting_define master_service_setting_defines[] = {
        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),
 
@@ -44,6 +45,7 @@ static const struct master_service_settings master_service_default_settings = {
        .debug_log_path = "",
        .log_timestamp = DEFAULT_FAILURE_STAMP_FORMAT,
        .syslog_facility = "mail",
+       .config_cache_size = 1024*1024,
        .version_ignore = FALSE,
        .shutdown_clients = TRUE
 };
@@ -218,8 +220,41 @@ master_service_apply_config_overrides(struct master_service *service,
        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 *);
@@ -231,7 +266,9 @@ int master_service_settings_read(struct master_service *service,
        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) {
@@ -244,6 +281,7 @@ int master_service_settings_read(struct master_service *service,
                        (void)close(fd);
                        return -1;
                }
+               config_socket = TRUE;
        }
 
        if (service->set_pool != NULL) {
@@ -279,7 +317,12 @@ int master_service_settings_read(struct master_service *service,
                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;
@@ -358,11 +401,12 @@ int master_service_settings_read_simple(struct master_service *service,
                                        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)
index 000ab019799d52761a2a23e609d7b5f8b7c1f191..c1d1e86986376f85b2364cdb0d795ac281475f5b 100644 (file)
@@ -13,6 +13,7 @@ struct master_service_settings {
        const char *debug_log_path;
        const char *log_timestamp;
        const char *syslog_facility;
+       uoff_t config_cache_size;
        bool version_ignore;
        bool shutdown_clients;
 };
@@ -31,10 +32,22 @@ struct master_service_settings_input {
        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,
index 80a12660437fc9869f4d73e1846ddde266f2f4ec..1c7d0b3c6ff3d0e7379c20563eaddaf92295a91b 100644 (file)
@@ -14,6 +14,7 @@
 #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;
 };
 
@@ -418,11 +426,8 @@ mail_storage_service_init_log(struct master_service *service,
                              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;
 
@@ -471,6 +476,7 @@ mail_storage_service_init(struct master_service *service,
                          enum mail_storage_service_flags flags)
 {
        struct mail_storage_service_ctx *ctx;
+       pool_t pool;
        unsigned int count;
 
        (void)umask(0077);
@@ -481,7 +487,9 @@ mail_storage_service_init(struct master_service *service,
        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;
 
@@ -539,8 +547,11 @@ settings_parser_update_children_parent(struct setting_parser_info *parent,
 
 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;
@@ -549,34 +560,34 @@ dyn_parsers_update_parent(pool_t pool,
        /* 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;
 }
 
@@ -584,21 +595,20 @@ int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
                                       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;
@@ -607,15 +617,47 @@ int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
                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;
                }
@@ -684,19 +726,19 @@ int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
        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);
@@ -725,13 +767,11 @@ int mail_storage_service_lookup(struct mail_storage_service_ctx *ctx,
        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");
@@ -871,8 +911,8 @@ void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx,
 {
        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)
@@ -880,10 +920,10 @@ void mail_storage_service_init_settings(struct mail_storage_service_ctx *ctx,
 
        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);
@@ -923,7 +963,9 @@ void mail_storage_service_deinit(struct mail_storage_service_ctx **_ctx)
                        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();
index a00dc45e30814e0fd399cdd00c37584734d53de2..46a2f9860f5d29b4b33a5ec4b3068f7d08b9b9d9 100644 (file)
@@ -5,6 +5,7 @@
 
 struct master_service;
 struct mail_user;
+struct setting_parser_context;
 
 enum mail_storage_service_flags {
        /* Fail if we don't drop root privileges */
@@ -51,6 +52,7 @@ int mail_storage_service_read_settings(struct mail_storage_service_ctx *ctx,
                                       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()
index a9772c872550c059c79b2713efcd3d6579cf2a52..05d91c95c05b1eff3c002ce99acd9959cc5087c8 100644 (file)
@@ -250,14 +250,15 @@ mail_storage_settings_to_index_flags(const struct mail_storage_settings *set)
        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;
 
index cd207eb7c0859c8be333b889f5d9121cf0c89c47..0e6da85c61ffa5bc86c55034dd9957fb2046264a 100644 (file)
@@ -87,6 +87,7 @@ const void *mail_storage_get_driver_settings(struct mail_storage *storage);
 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
index ad8c808a699ab9483597ff381c604924a7f9fdb0..1f5ae544af8fd6f017319928f0fac43bb38ba768 100644 (file)
@@ -149,6 +149,7 @@ static void client_raw_user_create(struct client *client)
 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));
@@ -159,10 +160,11 @@ static void client_read_settings(struct client *client)
        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)
index 3878e42a29a9c8baa1a6bf0d49d4a128ae2cb593..ad9f3d49a9c8ef60ec4d932c2477cca8a95f3de2 100644 (file)
@@ -81,12 +81,13 @@ const struct setting_parser_info lmtp_setting_parser_info = {
        .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);
 }
index defae30f5c27c7d5f23b90c306e51f7d40bc8da6..d57f92ec4da1122ce3dc3ff0dd5071836a00496d 100644 (file)
@@ -10,7 +10,8 @@ struct lmtp_settings {
 
 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
index 6970b303410cd7471787a64384aa237ce1720014..3e271391231f4f161702059c54afbf56f81897b6 100644 (file)
@@ -4,7 +4,9 @@
 #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>
@@ -94,6 +96,8 @@ static const struct setting_parser_info *default_login_set_roots[] = {
 
 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)
 {
@@ -173,7 +177,7 @@ login_set_var_expand_table(const struct master_service_settings_input *input)
 }
 
 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,
@@ -181,8 +185,10 @@ login_settings_read(struct master_service *service, pool_t pool,
 {
        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;
@@ -195,15 +201,22 @@ login_settings_read(struct master_service *service, pool_t pool,
        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",
@@ -217,3 +230,9 @@ login_settings_read(struct master_service *service, pool_t pool,
        *other_settings_r = sets + 1;
        return sets[0];
 }
+
+void login_settings_deinit(void)
+{
+       if (set_cache != NULL)
+               master_service_settings_cache_deinit(&set_cache);
+}
index b144f8d7fdd03b914e352b27b249f36d911a13fa..0719c49a3d1c6d4fb06bc09300143cbcc122b3e3 100644 (file)
@@ -1,8 +1,6 @@
 #ifndef LOGIN_SETTINGS_H
 #define LOGIN_SETTINGS_H
 
-struct master_service;
-
 struct login_settings {
        const char *login_trusted_networks;
        const char *login_greeting;
@@ -37,10 +35,11 @@ extern const struct setting_parser_info **login_set_roots;
 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
index 8ffdfd2925dc197ec49d15b9d84e3cc3b83bd017..ae73873c6a117509ddbeb1854c6007c06fc7c5a3 100644 (file)
@@ -109,7 +109,7 @@ client_connected_finish(const struct master_service_connection *conn)
        }
 
        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) {
@@ -318,6 +318,7 @@ static void main_deinit(void)
                anvil_client_deinit(&anvil);
        if (auth_client_to != NULL)
                timeout_remove(&auth_client_to);
+       login_settings_deinit();
 }
 
 int main(int argc, char *argv[])
@@ -351,7 +352,7 @@ 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
index 54e415acd9298ac23dd279624896754535eb187e..7bb0247b9aff1420ee149acb851fe8a1d678433a 100644 (file)
@@ -9,7 +9,6 @@
 #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"
@@ -1077,7 +1076,7 @@ static void ssl_servername_callback(SSL *ssl, int *al ATTR_UNUSED,
        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);
index 9e94a15c81d3d50e3b3992204245ddb8b3c7108f..b566054fd7bdb2d448daf7b1594b45a26fc66ff2 100644 (file)
@@ -295,6 +295,7 @@ sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
                    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;
@@ -319,7 +320,8 @@ sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
        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;
        }