From: Timo Sirainen Date: Mon, 6 Feb 2012 19:26:55 +0000 (+0200) Subject: Keep track of what Dovecot instances have been executed and their instance_name. X-Git-Tag: 2.1.rc6~72 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d47a87c927ca77e780480f4eaf575511698d42c8;p=thirdparty%2Fdovecot%2Fcore.git Keep track of what Dovecot instances have been executed and their instance_name. doveadm instance command can be used to list/remove them. --- diff --git a/src/doveadm/Makefile.am b/src/doveadm/Makefile.am index bcbe4b93c6..f49fd6c30d 100644 --- a/src/doveadm/Makefile.am +++ b/src/doveadm/Makefile.am @@ -91,6 +91,7 @@ doveadm_SOURCES = \ doveadm-dump-log.c \ doveadm-dump-mailboxlog.c \ doveadm-dump-thread.c \ + doveadm-instance.c \ doveadm-kick.c \ doveadm-log.c \ doveadm-master.c \ diff --git a/src/doveadm/doveadm-instance.c b/src/doveadm/doveadm-instance.c new file mode 100644 index 0000000000..18bf18b1bb --- /dev/null +++ b/src/doveadm/doveadm-instance.c @@ -0,0 +1,110 @@ +/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "master-instance.h" +#include "doveadm.h" +#include "doveadm-print.h" + +#include +#include +#include + +extern struct doveadm_cmd doveadm_cmd_instance[]; + +static void instance_cmd_help(doveadm_command_t *cmd) ATTR_NORETURN; + +static bool pid_file_read(const char *path) +{ + char buf[32]; + int fd; + ssize_t ret; + pid_t pid; + bool found = FALSE; + + fd = open(path, O_RDONLY); + if (fd == -1) { + if (errno != ENOENT) + i_error("open(%s) failed: %m", path); + return FALSE; + } + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) + i_error("read(%s) failed: %m", path); + else if (ret > 0 && buf[ret-1] == '\n') { + buf[ret-1] = '\0'; + if (str_to_pid(buf, &pid) == 0) { + found = !(pid == getpid() || + (kill(pid, 0) < 0 && errno == ESRCH)); + } + } + (void)close(fd); + return found; +} + +static void cmd_instance_list(int argc ATTR_UNUSED, char *argv[] ATTR_UNUSED) +{ + struct master_instance_list *list; + struct master_instance_list_iter *iter; + const struct master_instance *inst; + const char *pidfile_path; + + doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); + doveadm_print_header("path", "path", DOVEADM_PRINT_HEADER_FLAG_EXPAND); + doveadm_print_header_simple("name"); + doveadm_print_header_simple("running"); + + list = master_instance_list_init(MASTER_INSTANCE_PATH); + iter = master_instance_list_iterate_init(list); + while ((inst = master_instance_iterate_list_next(iter)) != NULL) { + doveadm_print(inst->base_dir); + doveadm_print(inst->name); + pidfile_path = t_strconcat(inst->base_dir, "/master.pid", NULL); + if (pid_file_read(pidfile_path)) + doveadm_print("yes"); + else + doveadm_print("no"); + } + master_instance_iterate_list_deinit(&iter); + master_instance_list_deinit(&list); +} + +static void cmd_instance_remove(int argc, char *argv[]) +{ + struct master_instance_list *list; + int ret; + + if (argc != 2) + instance_cmd_help(cmd_instance_remove); + + list = master_instance_list_init(MASTER_INSTANCE_PATH); + if ((ret = master_instance_list_remove(list, argv[1])) < 0) + i_error("Failed to remove instance"); + else if (ret == 0) + i_error("Instance already didn't exist"); + master_instance_list_deinit(&list); +} + +struct doveadm_cmd doveadm_cmd_instance[] = { + { cmd_instance_list, "instance list", "" }, + { cmd_instance_remove, "instance remove", "" } +}; + +static void instance_cmd_help(doveadm_command_t *cmd) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(doveadm_cmd_instance); i++) { + if (doveadm_cmd_instance[i].cmd == cmd) + help(&doveadm_cmd_instance[i]); + } + i_unreached(); +} + +void doveadm_register_instance_commands(void) +{ + unsigned int i; + + for (i = 0; i < N_ELEMENTS(doveadm_cmd_instance); i++) + doveadm_register_cmd(&doveadm_cmd_instance[i]); +} diff --git a/src/doveadm/doveadm.c b/src/doveadm/doveadm.c index dcc530963c..785870cda9 100644 --- a/src/doveadm/doveadm.c +++ b/src/doveadm/doveadm.c @@ -321,6 +321,7 @@ int main(int argc, char *argv[]) } else { quick_init = FALSE; doveadm_register_director_commands(); + doveadm_register_instance_commands(); doveadm_register_mount_commands(); doveadm_register_proxy_commands(); doveadm_register_log_commands(); diff --git a/src/doveadm/doveadm.h b/src/doveadm/doveadm.h index 7eb2cb42b7..d32fdb6898 100644 --- a/src/doveadm/doveadm.h +++ b/src/doveadm/doveadm.h @@ -38,6 +38,7 @@ void doveadm_master_send_signal(int signo); void doveadm_register_director_commands(void); void doveadm_register_proxy_commands(void); void doveadm_register_log_commands(void); +void doveadm_register_instance_commands(void); void doveadm_register_mount_commands(void); #endif diff --git a/src/lib-master/Makefile.am b/src/lib-master/Makefile.am index f2e7f3e731..5632b4fab2 100644 --- a/src/lib-master/Makefile.am +++ b/src/lib-master/Makefile.am @@ -14,6 +14,7 @@ libmaster_la_SOURCES = \ ipc-client.c \ ipc-server.c \ master-auth.c \ + master-instance.c \ master-login.c \ master-login-auth.c \ master-service.c \ @@ -27,6 +28,7 @@ headers = \ ipc-client.h \ ipc-server.h \ master-auth.h \ + master-instance.h \ master-interface.h \ master-login.h \ master-login-auth.h \ diff --git a/src/lib-master/master-instance.c b/src/lib-master/master-instance.c new file mode 100644 index 0000000000..a0643567aa --- /dev/null +++ b/src/lib-master/master-instance.c @@ -0,0 +1,331 @@ +/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ + +#include "lib.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 +#include + +struct master_instance_list { + pool_t pool; + const char *path; + + ARRAY_DEFINE(instances, struct master_instance); +}; + +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_list_drop_stale(struct master_instance_list *list) +{ + const struct master_instance *instances; + unsigned int i, count; + time_t stale_timestamp = time(NULL) - MASTER_INSTANCE_AUTO_STALE_SECS; + + instances = array_get(&list->instances, &count); + for (i = 0; i < count; ) { + if (instances[i].name[0] == '\0' && + instances[i].last_used < stale_timestamp) { + array_delete(&list->instances, i, 1); + instances = array_get(&list->instances, &count); + } else { + i++; + } + } +} + +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; + + /* */ + 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]); + 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); + + 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, '\n'); + (void)o_stream_send(output, str_data(str), str_len(str)); + } + o_stream_uncork(output); + (void)o_stream_flush(output); + if (output->last_failed_errno != 0) { + errno = output->last_failed_errno; + i_error("write(%s) failed: %m", path); + return -1; + } + return 0; +} + +static int master_instance_write_init(struct master_instance_list *list, + struct dotlock **dotlock_r) +{ + int fd; + + *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) { + (void)file_dotlock_delete(dotlock_r); + return -1; + } + 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; + + master_instance_list_drop_stale(list); + + T_BEGIN { + ret = master_instance_list_write(list, fd, lock_path); + } T_END; + if (ret < 0) { + (void)file_dotlock_delete(dotlock); + return -1; + } + if (fdatasync(fd) < 0) { + i_error("fdatasync(%s) failed: %m", lock_path); + (void)file_dotlock_delete(dotlock); + return -1; + } + 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); + + 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 */ + (void)file_dotlock_delete(&dotlock); + 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) { + (void)file_dotlock_delete(&dotlock); + return 0; + } + return master_instance_write_finish(list, fd, &dotlock) < 0 ? -1 : 1; +} + +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(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(list); + master_instance_list_drop_stale(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); +} diff --git a/src/lib-master/master-instance.h b/src/lib-master/master-instance.h new file mode 100644 index 0000000000..bbd11f66e9 --- /dev/null +++ b/src/lib-master/master-instance.h @@ -0,0 +1,43 @@ +#ifndef MASTER_INSTANCE_H +#define MASTER_INSTANCE_H + +#define MASTER_INSTANCE_PATH PKG_STATEDIR"/instances" +/* Delete instances whose last_used is older than this and has no name. */ +#define MASTER_INSTANCE_AUTO_STALE_SECS (3600*24) + +struct master_instance_list; + +struct master_instance { + time_t last_used; + const char *name; + const char *base_dir; +}; + +struct master_instance_list *master_instance_list_init(const char *path); +void master_instance_list_deinit(struct master_instance_list **list); + +/* Add/update last_used timestamp for an instance. Returns 0 if ok, + -1 if I/O error. */ +int master_instance_list_update(struct master_instance_list *list, + const char *base_dir); +/* Set instance's name. Returns 1 if ok, 0 if name was already used for + another instance (base_dir) or -1 if I/O error. */ +int master_instance_list_set_name(struct master_instance_list *list, + const char *base_dir, const char *name); +/* Remove instance. Returns 1 if ok, 0 if it didn't exist or -1 if I/O error. */ +int master_instance_list_remove(struct master_instance_list *list, + const char *base_dir); + +/* Find instance by its name. */ +const struct master_instance * +master_instance_list_find_by_name(struct master_instance_list *list, + const char *name); + +/* Iterate through existing instances. */ +struct master_instance_list_iter * +master_instance_list_iterate_init(struct master_instance_list *list); +const struct master_instance * +master_instance_iterate_list_next(struct master_instance_list_iter *iter); +void master_instance_iterate_list_deinit(struct master_instance_list_iter **iter); + +#endif diff --git a/src/master/main.c b/src/master/main.c index 82ca715bf9..46a26dd9d8 100644 --- a/src/master/main.c +++ b/src/master/main.c @@ -13,6 +13,7 @@ #include "execv-const.h" #include "mountpoint-list.h" #include "restrict-process-size.h" +#include "master-instance.h" #include "master-service.h" #include "master-service-settings.h" #include "askpass.h" @@ -46,6 +47,8 @@ int null_fd, global_master_dead_pipe_fd[2]; struct service_list *services; static char *pidfile_path; +static struct master_instance_list *instances; +static struct timeout *to_instance; static failure_callback_t *orig_fatal_callback; static failure_callback_t *orig_error_callback; @@ -318,6 +321,29 @@ static void mountpoints_update(const struct master_settings *set) mountpoint_list_deinit(&mountpoints); } +static void instance_update_now(struct master_instance_list *list) +{ + int ret; + + ret = master_instance_list_set_name(list, services->set->base_dir, + services->set->instance_name); + if (ret == 0) { + /* duplicate instance names. allow without warning.. */ + master_instance_list_update(list, services->set->base_dir); + } + + if (to_instance != NULL) + timeout_remove(&to_instance); + to_instance = timeout_add((3600*12 + rand()%(60*30)) * 1000, + instance_update_now, list); +} + +static void instance_update(void) +{ + instances = master_instance_list_init(MASTER_INSTANCE_PATH); + instance_update_now(instances); +} + static void sig_settings_reload(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) @@ -510,6 +536,7 @@ static void main_init(const struct master_settings *set) create_pid_file(pidfile_path); create_config_symlink(set); mountpoints_update(set); + instance_update(); services_monitor_start(services); } @@ -526,6 +553,10 @@ static void global_dead_pipe_close(void) static void main_deinit(void) { + instance_update_now(instances); + timeout_remove(&to_instance); + master_instance_list_deinit(&instances); + /* kill services and wait for them to die before unlinking pid file */ global_dead_pipe_close(); services_destroy(services, TRUE);