]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Keep track of what Dovecot instances have been executed and their instance_name.
authorTimo Sirainen <tss@iki.fi>
Mon, 6 Feb 2012 19:26:55 +0000 (21:26 +0200)
committerTimo Sirainen <tss@iki.fi>
Mon, 6 Feb 2012 19:26:55 +0000 (21:26 +0200)
doveadm instance command can be used to list/remove them.

src/doveadm/Makefile.am
src/doveadm/doveadm-instance.c [new file with mode: 0644]
src/doveadm/doveadm.c
src/doveadm/doveadm.h
src/lib-master/Makefile.am
src/lib-master/master-instance.c [new file with mode: 0644]
src/lib-master/master-instance.h [new file with mode: 0644]
src/master/main.c

index bcbe4b93c633fbe7919ede2765b918ad40e79c07..f49fd6c30d31b3bcd9b605f1b448b5968da7a46d 100644 (file)
@@ -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 (file)
index 0000000..18bf18b
--- /dev/null
@@ -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 <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+
+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", "<base dir>" }
+};
+
+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]);
+}
index dcc530963ca70aa14da0907a9c249a1c461c691e..785870cda9b67aaf5f9cd5c07e2279bdf38c406f 100644 (file)
@@ -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();
index 7eb2cb42b71a84937888080398c6280f6cfc6d84..d32fdb689883b8e223a0c9120dcae429bc05b3fc 100644 (file)
@@ -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
index f2e7f3e7314a798028347e28d18eb8c7c98beed2..5632b4fab20294e43a53aefc98cfd0adec825780 100644 (file)
@@ -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 (file)
index 0000000..a064356
--- /dev/null
@@ -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 <unistd.h>
+#include <fcntl.h>
+
+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;
+
+       /* <last used> <name> <base dir> */
+       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 (file)
index 0000000..bbd11f6
--- /dev/null
@@ -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
index 82ca715bf9cedb3771849fda976c952a47715193..46a26dd9d8c734c27918c42cde863655d9b7b8fa 100644 (file)
@@ -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);