Mercurial > dovecot > core-2.2
view src/lib-master/master-instance.c @ 14681:ca37d1577291
Added o_stream_nsend*() and related functions to make delayed error handling safer.
Once o_stream_nsend*() is called, o_stream_nfinish() must be called before
stream is destroyed to finish checking if there were any errors. If
something failed and the stream is just wanted to be closed,
o_stream_ignore_last_errors() can be called.
For streams where errors don't really make any difference (network sockets)
you can call o_stream_set_no_error_handling() immediately after creating the
stream.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 25 Jun 2012 00:01:59 +0300 |
parents | efd276ab2577 |
children | a097ef0a9d6d |
line wrap: on
line source
/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "abspath.h" #include "array.h" #include "istream.h" #include "ostream.h" #include "file-dotlock.h" #include "str.h" #include "strescape.h" #include "master-instance.h" #include <unistd.h> #include <fcntl.h> struct master_instance_list { pool_t pool; const char *path; ARRAY_DEFINE(instances, struct master_instance); unsigned int locked:1; unsigned int config_paths_changed:1; }; struct master_instance_list_iter { struct master_instance_list *list; unsigned int idx; }; static const struct dotlock_settings dotlock_set = { .timeout = 1, .stale_timeout = 60 }; struct master_instance_list *master_instance_list_init(const char *path) { struct master_instance_list *list; pool_t pool; pool = pool_alloconly_create("master instances", 256); list = p_new(pool, struct master_instance_list, 1); list->pool = pool; list->path = p_strdup(pool, path); p_array_init(&list->instances, pool, 8); return list; } void master_instance_list_deinit(struct master_instance_list **_list) { struct master_instance_list *list = *_list; *_list = NULL; pool_unref(&list->pool); } static void master_instance_update_config_path(struct master_instance_list *list, struct master_instance *inst) { const char *path, *config_path; /* update instance's config path if it has changed */ path = t_strconcat(inst->base_dir, "/"PACKAGE".conf", NULL); if (t_readlink(path, &config_path) == 0) { if (null_strcmp(inst->config_path, config_path) != 0) { inst->config_path = p_strdup(list->pool, config_path); list->config_paths_changed = TRUE; } } } static int master_instance_list_add_line(struct master_instance_list *list, const char *line) { struct master_instance *inst; const char *const *args; time_t last_used; /* <last used> <name> <base dir> [<config path>] */ args = t_strsplit_tabescaped(line); if (str_array_length(args) < 3) return -1; if (str_to_time(args[0], &last_used) < 0) return -1; inst = array_append_space(&list->instances); inst->last_used = last_used; inst->name = p_strdup(list->pool, args[1]); inst->base_dir = p_strdup(list->pool, args[2]); inst->config_path = p_strdup_empty(list->pool, args[3]); master_instance_update_config_path(list, inst); return 0; } static int master_instance_list_refresh(struct master_instance_list *list) { struct istream *input; const char *line; int fd, ret = 0; array_clear(&list->instances); fd = open(list->path, O_RDONLY); if (fd == -1) { if (errno == ENOENT) return 0; i_error("open(%s) failed: %m", list->path); return -1; } input = i_stream_create_fd(fd, PATH_MAX, TRUE); while ((line = i_stream_read_next_line(input)) != NULL) T_BEGIN { if (master_instance_list_add_line(list, line) < 0) i_error("Invalid line in %s: %s", list->path, line); } T_END; if (input->stream_errno != 0) { i_error("read(%s) failed: %m", line); ret = -1; } i_stream_destroy(&input); return ret; } static int master_instance_list_write(struct master_instance_list *list, int fd, const char *path) { struct ostream *output; const struct master_instance *inst; string_t *str = t_str_new(128); int ret = 0; output = o_stream_create_fd(fd, 0, FALSE); o_stream_cork(output); array_foreach(&list->instances, inst) { str_truncate(str, 0); str_printfa(str, "%ld\t", (long)inst->last_used); str_tabescape_write(str, inst->name); str_append_c(str, '\t'); str_tabescape_write(str, inst->base_dir); str_append_c(str, '\t'); if (inst->config_path != NULL) str_tabescape_write(str, inst->config_path); str_append_c(str, '\n'); o_stream_nsend(output, str_data(str), str_len(str)); } if (o_stream_nfinish(output) < 0) { i_error("write(%s) failed: %m", path); ret = -1; } o_stream_destroy(&output); return ret; } static int master_instance_write_init(struct master_instance_list *list, struct dotlock **dotlock_r) { int fd; i_assert(!list->locked); *dotlock_r = NULL; fd = file_dotlock_open_mode(&dotlock_set, list->path, 0, 0644, (uid_t)-1, (gid_t)-1, dotlock_r); if (fd == -1) { i_error("file_dotlock_open(%s) failed: %m", list->path); return -1; } if (master_instance_list_refresh(list) < 0) { file_dotlock_delete(dotlock_r); return -1; } list->locked = TRUE; return fd; } static int master_instance_write_finish(struct master_instance_list *list, int fd, struct dotlock **dotlock) { const char *lock_path = file_dotlock_get_lock_path(*dotlock); int ret; i_assert(list->locked); T_BEGIN { ret = master_instance_list_write(list, fd, lock_path); } T_END; list->locked = FALSE; if (ret < 0) { file_dotlock_delete(dotlock); return -1; } if (fdatasync(fd) < 0) { i_error("fdatasync(%s) failed: %m", lock_path); file_dotlock_delete(dotlock); return -1; } list->config_paths_changed = FALSE; return file_dotlock_replace(dotlock, 0); } static struct master_instance * master_instance_find(struct master_instance_list *list, const char *base_dir) { struct master_instance *inst; array_foreach_modifiable(&list->instances, inst) { if (strcmp(inst->base_dir, base_dir) == 0) return inst; } return NULL; } int master_instance_list_update(struct master_instance_list *list, const char *base_dir) { struct master_instance *inst; struct dotlock *dotlock; int fd; if ((fd = master_instance_write_init(list, &dotlock)) == -1) return -1; inst = master_instance_find(list, base_dir); if (inst == NULL) { inst = array_append_space(&list->instances); inst->name = ""; inst->base_dir = p_strdup(list->pool, base_dir); } inst->last_used = time(NULL); master_instance_update_config_path(list, inst); return master_instance_write_finish(list, fd, &dotlock); } int master_instance_list_set_name(struct master_instance_list *list, const char *base_dir, const char *name) { const struct master_instance *orig_inst; struct master_instance *inst; struct dotlock *dotlock; int fd; i_assert(*name != '\0'); if ((fd = master_instance_write_init(list, &dotlock)) == -1) return -1; orig_inst = master_instance_list_find_by_name(list, name); if (orig_inst != NULL && strcmp(orig_inst->base_dir, base_dir) != 0) { /* name already used */ file_dotlock_delete(&dotlock); list->locked = FALSE; return 0; } inst = master_instance_find(list, base_dir); if (inst == NULL) { inst = array_append_space(&list->instances); inst->base_dir = p_strdup(list->pool, base_dir); } inst->name = p_strdup(list->pool, name); inst->last_used = time(NULL); return master_instance_write_finish(list, fd, &dotlock) < 0 ? -1 : 1; } int master_instance_list_remove(struct master_instance_list *list, const char *base_dir) { struct dotlock *dotlock; const struct master_instance *instances; unsigned int i, count; int fd; if ((fd = master_instance_write_init(list, &dotlock)) == -1) return -1; instances = array_get(&list->instances, &count); for (i = 0; i < count; i++) { if (strcmp(instances[i].base_dir, base_dir) == 0) { array_delete(&list->instances, i, 1); break; } } if (i == count) { file_dotlock_delete(&dotlock); list->locked = FALSE; return 0; } return master_instance_write_finish(list, fd, &dotlock) < 0 ? -1 : 1; } static int master_instance_list_refresh_and_update(struct master_instance_list *list) { struct dotlock *dotlock; int fd; if (master_instance_list_refresh(list) < 0) return -1; if (list->config_paths_changed && !list->locked) { /* write new config paths */ if ((fd = master_instance_write_init(list, &dotlock)) == -1) return -1; if (master_instance_write_finish(list, fd, &dotlock) < 0) return -1; } return 0; } const struct master_instance * master_instance_list_find_by_name(struct master_instance_list *list, const char *name) { const struct master_instance *inst; i_assert(*name != '\0'); if (array_count(&list->instances) == 0) (void)master_instance_list_refresh_and_update(list); array_foreach(&list->instances, inst) { if (strcmp(inst->name, name) == 0) return inst; } return NULL; } struct master_instance_list_iter * master_instance_list_iterate_init(struct master_instance_list *list) { struct master_instance_list_iter *iter; iter = i_new(struct master_instance_list_iter, 1); iter->list = list; (void)master_instance_list_refresh_and_update(list); return iter; } const struct master_instance * master_instance_iterate_list_next(struct master_instance_list_iter *iter) { if (iter->idx == array_count(&iter->list->instances)) return NULL; return array_idx(&iter->list->instances, iter->idx++); } void master_instance_iterate_list_deinit(struct master_instance_list_iter **_iter) { struct master_instance_list_iter *iter = *_iter; *_iter = NULL; i_free(iter); }