Mercurial > dovecot > original-hg > dovecot-1.2
changeset 8300:9d963e3bbe65 HEAD
Added a simple flat file dict backend.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 19 Oct 2008 02:07:34 +0300 |
parents | da848018d760 |
children | d46579bb7a3c |
files | src/deliver/deliver.c src/imap/main.c src/lib-dict/Makefile.am src/lib-dict/dict-client.h src/lib-dict/dict-file.c src/lib-dict/dict-private.h src/lib-dict/dict.c src/lib-dict/dict.h src/plugins/expire/expire-tool.c src/pop3/main.c |
diffstat | 10 files changed, 433 insertions(+), 15 deletions(-) [+] |
line wrap: on
line diff
--- a/src/deliver/deliver.c Sun Oct 19 02:01:03 2008 +0300 +++ b/src/deliver/deliver.c Sun Oct 19 02:07:34 2008 +0300 @@ -24,7 +24,7 @@ #include "message-address.h" #include "mail-namespace.h" #include "raw-storage.h" -#include "dict-client.h" +#include "dict.h" #include "auth-client.h" #include "mail-send.h" #include "duplicate.h" @@ -1019,7 +1019,7 @@ if (deliver_set->log_format == NULL) deliver_set->log_format = DEFAULT_LOG_FORMAT; - dict_driver_register(&dict_driver_client); + dict_drivers_register_builtin(); duplicate_init(); mail_storage_init(); mail_storage_register_all(); @@ -1147,7 +1147,7 @@ mail_storage_deinit(); duplicate_deinit(); - dict_driver_unregister(&dict_driver_client); + dict_drivers_unregister_builtin(); lib_signals_deinit(); io_loop_destroy(&ioloop);
--- a/src/imap/main.c Sun Oct 19 02:01:03 2008 +0300 +++ b/src/imap/main.c Sun Oct 19 02:07:34 2008 +0300 @@ -12,7 +12,7 @@ #include "fd-close-on-exec.h" #include "process-title.h" #include "module-dir.h" -#include "dict-client.h" +#include "dict.h" #include "mail-storage.h" #include "commands.h" #include "mail-namespace.h" @@ -193,7 +193,7 @@ capability_string = str_new(default_pool, sizeof(CAPABILITY_STRING)+32); str_append(capability_string, CAPABILITY_STRING); - dict_driver_register(&dict_driver_client); + dict_drivers_register_builtin(); mail_storage_init(); mail_storage_register_all(); mailbox_list_register_all(); @@ -272,7 +272,7 @@ module_dir_unload(&modules); commands_deinit(); mail_storage_deinit(); - dict_driver_unregister(&dict_driver_client); + dict_drivers_unregister_builtin(); str_free(&capability_string);
--- a/src/lib-dict/Makefile.am Sun Oct 19 02:01:03 2008 +0300 +++ b/src/lib-dict/Makefile.am Sun Oct 19 02:07:34 2008 +0300 @@ -11,7 +11,8 @@ base_sources = \ dict.c \ - dict-client.c + dict-client.c \ + dict-file.c backend_sources = \ dict-db.c \
--- a/src/lib-dict/dict-client.h Sun Oct 19 02:01:03 2008 +0300 +++ b/src/lib-dict/dict-client.h Sun Oct 19 02:07:34 2008 +0300 @@ -33,8 +33,6 @@ DICT_PROTOCOL_REPLY_FAIL = 'F' }; -extern struct dict dict_driver_client; - const char *dict_client_escape(const char *src); const char *dict_client_unescape(const char *src);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-dict/dict-file.c Sun Oct 19 02:07:34 2008 +0300 @@ -0,0 +1,401 @@ +/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "hash.h" +#include "file-dotlock.h" +#include "nfs-workarounds.h" +#include "istream.h" +#include "ostream.h" +#include "dict-private.h" + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +struct file_dict { + struct dict dict; + pool_t hash_pool; + + char *path; + struct hash_table *hash; + int fd; +}; + +struct file_dict_iterate_context { + struct dict_iterate_context ctx; + + struct hash_iterate_context *iter; + char *path; + unsigned int path_len; + + enum dict_iterate_flags flags; + unsigned int failed:1; +}; + +enum file_dict_change_type { + FILE_DICT_CHANGE_TYPE_SET, + FILE_DICT_CHANGE_TYPE_UNSET, + FILE_DICT_CHANGE_TYPE_INC +}; + +struct file_dict_change { + enum file_dict_change_type type; + const char *key; + union { + const char *str; + long long diff; + } value; +}; + +struct file_dict_transaction_context { + struct dict_transaction_context ctx; + + pool_t pool; + ARRAY_DEFINE(changes, struct file_dict_change); +}; + +static struct dotlock_settings file_dict_dotlock_settings = { + MEMBER(temp_prefix) NULL, + MEMBER(lock_suffix) NULL, + + MEMBER(timeout) 30, + MEMBER(stale_timeout) 5 +}; + +static struct dict *file_dict_init(struct dict *driver, const char *uri, + enum dict_data_type value_type ATTR_UNUSED, + const char *username ATTR_UNUSED) +{ + struct file_dict *dict; + + dict = i_new(struct file_dict, 1); + dict->dict = *driver; + dict->path = i_strdup(uri); + dict->hash_pool = pool_alloconly_create("file dict", 1024); + dict->hash = hash_create(default_pool, dict->hash_pool, 0, str_hash, + (hash_cmp_callback_t *)strcmp); + dict->fd = -1; + return &dict->dict; +} + +static void file_dict_deinit(struct dict *_dict) +{ + struct file_dict *dict = (struct file_dict *)_dict; + + hash_destroy(&dict->hash); + pool_unref(&dict->hash_pool); + i_free(dict->path); + i_free(dict); +} + +static bool file_dict_need_refresh(struct file_dict *dict) +{ + struct stat st1, st2; + + if (dict->fd == -1) + return TRUE; + + nfs_flush_file_handle_cache(dict->path); + if (nfs_safe_stat(dict->path, &st1) < 0) { + i_error("stat(%s) failed: %m", dict->path); + return FALSE; + } + + if (fstat(dict->fd, &st2) < 0) { + if (errno != ESTALE) + i_error("fstat(%s) failed: %m", dict->path); + return TRUE; + } + if (st1.st_ino != st2.st_ino || + !CMP_DEV_T(st1.st_dev, st2.st_dev)) { + /* file changed */ + return TRUE; + } + return FALSE; +} + +static int file_dict_refresh(struct file_dict *dict) +{ + struct istream *input; + char *key, *value; + + if (!file_dict_need_refresh(dict)) + return 0; + + if (dict->fd != -1) { + if (close(dict->fd) < 0) + i_error("close(%s) failed: %m", dict->path); + } + dict->fd = open(dict->path, O_RDONLY); + if (dict->fd == -1) { + if (errno == ENOENT) + return 0; + i_error("open(%s) failed: %m", dict->path); + return -1; + } + + hash_clear(dict->hash, TRUE); + p_clear(dict->hash_pool); + + input = i_stream_create_fd(dict->fd, (size_t)-1, FALSE); + while ((key = i_stream_read_next_line(input)) != NULL && + (value = i_stream_read_next_line(input)) != NULL) { + key = p_strdup(dict->hash_pool, key); + value = p_strdup(dict->hash_pool, value); + hash_insert(dict->hash, key, value); + } + i_stream_destroy(&input); + return 0; +} + +static int file_dict_lookup(struct dict *_dict, pool_t pool, + const char *key, const char **value_r) +{ + struct file_dict *dict = (struct file_dict *)_dict; + + if (file_dict_refresh(dict) < 0) + return -1; + + *value_r = p_strdup(pool, hash_lookup(dict->hash, key)); + return *value_r == NULL ? 0 : 1; +} + +static struct dict_iterate_context * +file_dict_iterate_init(struct dict *_dict, const char *path, + enum dict_iterate_flags flags) +{ + struct file_dict_iterate_context *ctx; + struct file_dict *dict = (struct file_dict *)_dict; + + ctx = i_new(struct file_dict_iterate_context, 1); + ctx->path = i_strdup(path); + ctx->path_len = strlen(path); + ctx->flags = flags; + ctx->iter = hash_iterate_init(dict->hash); + + if (file_dict_refresh(dict) < 0) + ctx->failed = TRUE; + return &ctx->ctx; +} + +static int file_dict_iterate(struct dict_iterate_context *_ctx, + const char **key_r, const char **value_r) +{ + struct file_dict_iterate_context *ctx = + (struct file_dict_iterate_context *)_ctx; + void *key, *value; + + while (hash_iterate(ctx->iter, &key, &value)) { + if (strncmp(ctx->path, key, ctx->path_len) != 0) + continue; + + if ((ctx->flags & DICT_ITERATE_FLAG_RECURSE) == 0 && + strchr(key + ctx->path_len, '/') != NULL) + continue; + + *key_r = key; + *value_r = value; + return 1; + } + return ctx->failed ? -1 : 0; +} + +static void file_dict_iterate_deinit(struct dict_iterate_context *_ctx) +{ + struct file_dict_iterate_context *ctx = + (struct file_dict_iterate_context *)_ctx; + + hash_iterate_deinit(&ctx->iter); + i_free(ctx->path); + i_free(ctx); +} + +static struct dict_transaction_context * +file_dict_transaction_init(struct dict *_dict) +{ + struct file_dict_transaction_context *ctx; + pool_t pool; + + pool = pool_alloconly_create("file dict transaction", 1024); + ctx = p_new(pool, struct file_dict_transaction_context, 1); + ctx->ctx.dict = _dict; + ctx->pool = pool; + p_array_init(&ctx->changes, pool, 32); + return &ctx->ctx; +} + +static void file_dict_apply_changes(struct file_dict_transaction_context *ctx) +{ + struct file_dict *dict = (struct file_dict *)ctx->ctx.dict; + const char *tmp; + char *key, *value, *old_value; + void *orig_key, *orig_value; + const struct file_dict_change *changes; + unsigned int i, count, new_len; + long long diff; + + changes = array_get(&ctx->changes, &count); + for (i = 0; i < count; i++) { + if (hash_lookup_full(dict->hash, changes[i].key, + &orig_key, &orig_value)) { + key = orig_key; + old_value = orig_value; + } else { + key = NULL; + old_value = NULL; + } + value = NULL; + + switch (changes[i].type) { + case FILE_DICT_CHANGE_TYPE_INC: + diff = old_value == NULL ? 0 : + strtoll(old_value, NULL, 10); + diff += changes[i].value.diff; + tmp = t_strdup_printf("%lld", diff); + new_len = strlen(tmp); + if (new_len > strlen(old_value)) + value = p_strdup(dict->hash_pool, tmp); + else { + memcpy(old_value, tmp, new_len + 1); + value = old_value; + } + /* fall through */ + case FILE_DICT_CHANGE_TYPE_SET: + if (key == NULL) + key = p_strdup(dict->hash_pool, changes[i].key); + if (value == NULL) { + value = p_strdup(dict->hash_pool, + changes[i].value.str); + } + hash_update(dict->hash, key, value); + break; + case FILE_DICT_CHANGE_TYPE_UNSET: + if (old_value != NULL) + hash_remove(dict->hash, key); + break; + } + } +} + +static int file_dict_write_changes(struct file_dict_transaction_context *ctx) +{ + struct file_dict *dict = (struct file_dict *)ctx->ctx.dict; + struct dotlock *dotlock; + struct hash_iterate_context *iter; + struct ostream *output; + void *key, *value; + int fd; + + fd = file_dotlock_open(&file_dict_dotlock_settings, dict->path, 0, + &dotlock); + if (fd == -1) { + i_error("file dict commit: file_dotlock_open() failed: %m"); + return -1; + } + /* refresh once more now that we're locked */ + if (file_dict_refresh(dict) < 0) { + file_dotlock_delete(&dotlock); + return -1; + } + file_dict_apply_changes(ctx); + + output = o_stream_create_fd(fd, 0, FALSE); + iter = hash_iterate_init(dict->hash); + while (hash_iterate(iter, &key, &value)) { + o_stream_send_str(output, key); + o_stream_send(output, "\n", 1); + o_stream_send_str(output, value); + o_stream_send(output, "\n", 1); + } + hash_iterate_deinit(&iter); + o_stream_destroy(&output); + + if (file_dotlock_replace(&dotlock, + DOTLOCK_REPLACE_FLAG_DONT_CLOSE_FD) < 0) { + (void)close(fd); + return -1; + } + + if (dict->fd != -1) + (void)close(dict->fd); + dict->fd = fd; + return 0; +} + +static int file_dict_transaction_commit(struct dict_transaction_context *_ctx) +{ + struct file_dict_transaction_context *ctx = + (struct file_dict_transaction_context *)_ctx; + int ret; + + ret = file_dict_write_changes(ctx); + pool_unref(&ctx->pool); + return ret; +} + +static void file_dict_transaction_rollback(struct dict_transaction_context *_ctx) +{ + struct file_dict_transaction_context *ctx = + (struct file_dict_transaction_context *)_ctx; + + pool_unref(&ctx->pool); +} + +static void file_dict_set(struct dict_transaction_context *_ctx, + const char *key, const char *value) +{ + struct file_dict_transaction_context *ctx = + (struct file_dict_transaction_context *)_ctx; + struct file_dict_change *change; + + change = array_append_space(&ctx->changes); + change->type = FILE_DICT_CHANGE_TYPE_SET; + change->key = p_strdup(ctx->pool, key); + change->value.str = p_strdup(ctx->pool, value); +} + +static void file_dict_unset(struct dict_transaction_context *_ctx, + const char *key) +{ + struct file_dict_transaction_context *ctx = + (struct file_dict_transaction_context *)_ctx; + struct file_dict_change *change; + + change = array_append_space(&ctx->changes); + change->type = FILE_DICT_CHANGE_TYPE_UNSET; + change->key = p_strdup(ctx->pool, key); +} + +static void +file_dict_atomic_inc(struct dict_transaction_context *_ctx, + const char *key, long long diff) +{ + struct file_dict_transaction_context *ctx = + (struct file_dict_transaction_context *)_ctx; + struct file_dict_change *change; + + change = array_append_space(&ctx->changes); + change->type = FILE_DICT_CHANGE_TYPE_INC; + change->key = p_strdup(ctx->pool, key); + change->value.diff = diff; +} + +struct dict dict_driver_file = { + MEMBER(name) "file", + { + file_dict_init, + file_dict_deinit, + file_dict_lookup, + file_dict_iterate_init, + file_dict_iterate, + file_dict_iterate_deinit, + file_dict_transaction_init, + file_dict_transaction_commit, + file_dict_transaction_rollback, + file_dict_set, + file_dict_unset, + file_dict_atomic_inc + } +};
--- a/src/lib-dict/dict-private.h Sun Oct 19 02:01:03 2008 +0300 +++ b/src/lib-dict/dict-private.h Sun Oct 19 02:07:34 2008 +0300 @@ -47,4 +47,7 @@ unsigned int changed:1; }; +extern struct dict dict_driver_file; +extern struct dict dict_driver_client; + #endif
--- a/src/lib-dict/dict.c Sun Oct 19 02:01:03 2008 +0300 +++ b/src/lib-dict/dict.c Sun Oct 19 02:07:34 2008 +0300 @@ -51,6 +51,18 @@ array_free(&dict_drivers); } +void dict_drivers_register_builtin(void) +{ + dict_driver_register(&dict_driver_client); + dict_driver_register(&dict_driver_file); +} + +void dict_drivers_unregister_builtin(void) +{ + dict_driver_unregister(&dict_driver_client); + dict_driver_unregister(&dict_driver_file); +} + struct dict *dict_init(const char *uri, enum dict_data_type value_type, const char *username) {
--- a/src/lib-dict/dict.h Sun Oct 19 02:01:03 2008 +0300 +++ b/src/lib-dict/dict.h Sun Oct 19 02:07:34 2008 +0300 @@ -20,6 +20,9 @@ void dict_driver_register(struct dict *driver); void dict_driver_unregister(struct dict *driver); +void dict_drivers_register_builtin(void); +void dict_drivers_unregister_builtin(void); + void dict_drivers_register_all(void); void dict_drivers_unregister_all(void);
--- a/src/plugins/expire/expire-tool.c Sun Oct 19 02:01:03 2008 +0300 +++ b/src/plugins/expire/expire-tool.c Sun Oct 19 02:07:34 2008 +0300 @@ -6,7 +6,7 @@ #include "file-lock.h" #include "randgen.h" #include "lib-signals.h" -#include "dict-client.h" +#include "dict.h" #include "mail-index.h" #include "mail-search-build.h" #include "mail-storage.h" @@ -188,7 +188,7 @@ const char *userp, *mailbox; int ret; - dict_driver_register(&dict_driver_client); + dict_drivers_register_builtin(); mail_storage_init(); mail_storage_register_all(); mailbox_list_register_all(); @@ -295,7 +295,7 @@ auth_connection_deinit(ctx.auth_conn); mail_storage_deinit(); - dict_driver_unregister(&dict_driver_client); + dict_drivers_unregister_builtin(); } int main(int argc ATTR_UNUSED, const char *argv[])
--- a/src/pop3/main.c Sun Oct 19 02:01:03 2008 +0300 +++ b/src/pop3/main.c Sun Oct 19 02:07:34 2008 +0300 @@ -13,7 +13,7 @@ #include "process-title.h" #include "module-dir.h" #include "var-expand.h" -#include "dict-client.h" +#include "dict.h" #include "mail-storage.h" #include "mail-namespace.h" @@ -204,7 +204,7 @@ log_error_callback, NULL); } - dict_driver_register(&dict_driver_client); + dict_drivers_register_builtin(); mail_storage_init(); mail_storage_register_all(); mailbox_list_register_all(); @@ -261,7 +261,7 @@ module_dir_unload(&modules); mail_storage_deinit(); - dict_driver_unregister(&dict_driver_client); + dict_drivers_unregister_builtin(); lib_signals_deinit(); closelog();