Mercurial > dovecot > core-2.2
changeset 3620:3360cc019737 HEAD
Implemented new signal handling framework, which makes handling signals much
easier.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 25 Sep 2005 14:07:32 +0300 |
parents | 86ddbe18538c |
children | 3ae2df67459c |
files | src/auth/auth-cache.c src/auth/main.c src/deliver/deliver.c src/imap/main.c src/lib/lib-signals.c src/lib/lib-signals.h src/login-common/main.c src/master/main.c src/pop3/main.c |
diffstat | 9 files changed, 292 insertions(+), 146 deletions(-) [+] |
line wrap: on
line diff
--- a/src/auth/auth-cache.c Sun Sep 25 13:52:27 2005 +0300 +++ b/src/auth/auth-cache.c Sun Sep 25 14:07:32 2005 +0300 @@ -24,7 +24,6 @@ size_t size_left; unsigned int ttl_secs; - unsigned int hup_count, usr2_count; unsigned int hit_count, miss_count; }; @@ -97,22 +96,49 @@ i_free(node); } +static void sig_auth_cache_clear(int signo __attr_unused__, void *context) +{ + struct auth_cache *cache = context; + + i_info("SIGHUP received, clearing cache"); + auth_cache_clear(cache); +} + +static void sig_auth_cache_stats(int signo __attr_unused__, void *context) +{ + struct auth_cache *cache = context; + unsigned int total_count; + + total_count = cache->hit_count + cache->miss_count; + i_info("Authentication cache hits %u/%u (%u%%)", + cache->hit_count, total_count, + cache->hit_count * 100 / total_count); + + /* reset hit counter */ + cache->hit_count = cache->miss_count = 0; +} + struct auth_cache *auth_cache_new(size_t max_size, unsigned int ttl_secs) { struct auth_cache *cache; cache = i_new(struct auth_cache, 1); - cache->hup_count = lib_signal_hup_count; cache->hash = hash_create(default_pool, default_pool, 0, str_hash, (hash_cmp_callback_t *)strcmp); cache->size_left = max_size; cache->ttl_secs = ttl_secs; + + lib_signals_set_handler(SIGHUP, TRUE, sig_auth_cache_clear, cache); + lib_signals_set_handler(SIGUSR2, TRUE, sig_auth_cache_stats, cache); return cache; } void auth_cache_free(struct auth_cache *cache) { - auth_cache_clear(cache); + lib_signals_unset_handler(SIGHUP, sig_auth_cache_clear, cache); + lib_signals_unset_handler(SIGUSR2, sig_auth_cache_stats, cache); + + auth_cache_clear(cache); hash_destroy(cache->hash); i_free(cache); } @@ -122,8 +148,6 @@ while (cache->tail != NULL) auth_cache_node_destroy(cache, cache->tail); hash_clear(cache->hash, FALSE); - - cache->hup_count = lib_signal_hup_count; } const char *auth_cache_lookup(struct auth_cache *cache, @@ -132,29 +156,9 @@ { string_t *str; struct cache_node *node; - unsigned int total_count; *expired_r = FALSE; - if (cache->hup_count != lib_signal_hup_count) { - /* SIGHUP received - clear cache */ - i_info("SIGHUP received, clearing cache"); - auth_cache_clear(cache); - return NULL; - } - - if (cache->usr2_count != lib_signal_usr2_count) { - cache->usr2_count = lib_signal_usr2_count; - - total_count = cache->hit_count + cache->miss_count; - i_info("Authentication cache hits %u/%u (%u%%)", - cache->hit_count, total_count, - cache->hit_count * 100 / total_count); - - /* reset hit counter */ - cache->hit_count = cache->miss_count = 0; - } - str = t_str_new(256); var_expand(str, key, auth_request_get_var_expand_table(request, str_escape));
--- a/src/auth/main.c Sun Sep 25 13:52:27 2005 +0300 +++ b/src/auth/main.c Sun Sep 25 14:07:32 2005 +0300 @@ -34,8 +34,12 @@ static struct auth *auth; static struct auth_worker_client *worker_client; -static void sig_quit(int signo __attr_unused__) +static void sig_die(int signo, void *context __attr_unused__) { + /* warn about being killed because of some signal, except SIGINT (^C) + which is too common at least while testing :) */ + if (signo != SIGINT) + i_warning("Killed with signal %d", signo); io_loop_stop(ioloop); } @@ -195,7 +199,11 @@ struct auth_master_listener *listener; process_start_time = ioloop_time; - lib_init_signals(sig_quit); + + lib_signals_init(); + lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGPIPE, FALSE, NULL, NULL); mech_init(); auth_init(auth); @@ -246,9 +254,6 @@ static void main_deinit(void) { - if (lib_signal_kill != 0) - i_warning("Killed with signal %d", lib_signal_kill); - if (worker_client != NULL) auth_worker_client_unref(worker_client); else @@ -263,6 +268,7 @@ password_schemes_deinit(); random_deinit(); + lib_signals_deinit(); closelog(); }
--- a/src/deliver/deliver.c Sun Sep 25 13:52:27 2005 +0300 +++ b/src/deliver/deliver.c Sun Sep 25 14:07:32 2005 +0300 @@ -38,8 +38,12 @@ static struct ioloop *ioloop; static int return_value = EX_SOFTWARE; -static void sig_quit(int signo __attr_unused__) +static void sig_die(int signo, void *context __attr_unused__) { + /* warn about being killed because of some signal, except SIGINT (^C) + which is too common at least while testing :) */ + if (signo != SIGINT) + i_warning("Killed with signal %d", signo); io_loop_stop(ioloop); } @@ -349,7 +353,10 @@ const char *str; lib_init(); - lib_init_signals(sig_quit); + lib_signals_init(); + lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGPIPE, FALSE, NULL, NULL); ioloop = io_loop_create(default_pool); destination = NULL; @@ -453,6 +460,7 @@ mail_storage_destroy(storage); mail_storage_deinit(); io_loop_destroy(ioloop); + lib_signals_deinit(); lib_deinit(); return EX_OK;
--- a/src/imap/main.c Sun Sep 25 13:52:27 2005 +0300 +++ b/src/imap/main.c Sun Sep 25 14:07:32 2005 +0300 @@ -50,8 +50,12 @@ string_t *capability_string; -static void sig_quit(int signo __attr_unused__) +static void sig_die(int signo, void *context __attr_unused__) { + /* warn about being killed because of some signal, except SIGINT (^C) + which is too common at least while testing :) */ + if (signo != SIGINT) + i_warning("Killed with signal %d", signo); io_loop_stop(ioloop); } @@ -130,7 +134,10 @@ struct client *client; const char *user, *str; - lib_init_signals(sig_quit); + lib_signals_init(); + lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGPIPE, FALSE, NULL, NULL); user = getenv("USER"); if (user == NULL) { @@ -186,11 +193,6 @@ static void main_deinit(void) { - /* warn about being killed because of some signal, except SIGINT (^C) - which is too common at least while testing :) */ - if (lib_signal_kill != 0 && lib_signal_kill != 2) - i_warning("Killed with signal %d", lib_signal_kill); - module_dir_unload(modules); commands_deinit(); @@ -201,6 +203,7 @@ str_free(capability_string); + lib_signals_deinit(); closelog(); }
--- a/src/lib/lib-signals.c Sun Sep 25 13:52:27 2005 +0300 +++ b/src/lib/lib-signals.c Sun Sep 25 14:07:32 2005 +0300 @@ -1,96 +1,196 @@ /* Copyright (c) 2001-2003 Timo Sirainen */ #include "lib.h" +#include "ioloop.h" +#include "fd-close-on-exec.h" #include "lib-signals.h" -#include <stdio.h> #include <signal.h> +#include <unistd.h> + +#define MAX_SIGNAL_VALUE 31 + +struct signal_handler { + signal_handler_t *handler; + void *context; -int lib_signal_kill; -unsigned int lib_signal_hup_count; -unsigned int lib_signal_usr1_count, lib_signal_usr2_count; + int delayed; + struct signal_handler *next; +}; -static void (*quit_handler) (int); +/* Remember that these are accessed inside signal handler which may be called + even while we're initializing/deinitializing. Try hard to keep everything + in consistent state. */ +static struct signal_handler *signal_handlers[MAX_SIGNAL_VALUE+1]; +static int sig_pipe_fd[2]; -static void sig_counter(int signo) +static struct io *io_sig; + +static void sig_handler(int signo) { -#ifndef HAVE_SIGACTION - /* some systems may have changed the signal handler to default one */ - signal(signo, sig_counter); -#endif + struct signal_handler *h; + int delayed_sent = FALSE; + + if (signo < 0 || signo > MAX_SIGNAL_VALUE) + return; + + /* remember that we're inside a signal handler which might have been + called at any time. don't do anything that's unsafe. */ + for (h = signal_handlers[signo]; h != NULL; h = h->next) { + if (!h->delayed) + h->handler(signo, h->context); + else if (!delayed_sent) { + int saved_errno = errno; + unsigned char signo_byte = signo; + + if (write(sig_pipe_fd[1], &signo_byte, 1) != 1) + i_error("write(sigpipe) failed: %m"); + delayed_sent = TRUE; + errno = saved_errno; + } + } +} + +static void signal_read(void *context __attr_unused__) +{ + unsigned char signal_buf[512]; + unsigned char signal_mask[MAX_SIGNAL_VALUE+1]; + ssize_t i, ret; + int signo; - switch (signo) { - case SIGHUP: - lib_signal_hup_count++; - break; - case SIGUSR1: - lib_signal_usr1_count++; - break; - case SIGUSR2: - lib_signal_usr2_count++; - break; + ret = read(sig_pipe_fd[0], signal_buf, sizeof(signal_buf)); + if (ret > 0) { + memset(signal_mask, 0, sizeof(signal_mask)); + + /* move them to mask first to avoid calling same handler + multiple times */ + for (i = 0; i < ret; i++) { + signo = signal_buf[i]; + if (signo > MAX_SIGNAL_VALUE) { + i_panic("sigpipe contains signal %d > %d", + signo, MAX_SIGNAL_VALUE); + } + signal_mask[signo] = 1; + } + + /* call the delayed handlers */ + for (signo = 0; signo < MAX_SIGNAL_VALUE; signo++) { + if (signal_mask[signo] > 0) { + struct signal_handler *h = + signal_handlers[signo]; + + for (; h != NULL; h = h->next) { + if (h->delayed) + h->handler(signo, h->context); + } + } + } + } else if (ret < 0) { + if (errno != EAGAIN) + i_fatal("read(sigpipe) failed: %m"); + } else { + i_fatal("read(sigpipe) failed: EOF"); } } -static void sig_quit(int signo) +void lib_signals_set_handler(int signo, int delayed, + signal_handler_t *handler, void *context) { - /* if we get killed after this, just die instead of coming back here. */ - signal(SIGINT, SIG_DFL); - signal(SIGTERM, SIG_DFL); + struct signal_handler *h; + + if (signo < 0 || signo > MAX_SIGNAL_VALUE) { + i_panic("Trying to set signal %d handler, but max is %d", + signo, MAX_SIGNAL_VALUE); + } + + if (signal_handlers[signo] == NULL) { + /* first handler for this signal */ + struct sigaction act; + + if (sigemptyset(&act.sa_mask) < 0) + i_fatal("sigemptyset(): %m"); + act.sa_flags = 0; + act.sa_handler = handler != NULL ? sig_handler : SIG_IGN; + if (sigaction(signo, &act, NULL) < 0) + i_fatal("sigaction(%d): %m", signo); - lib_signal_kill = signo; - quit_handler(signo); + if (handler == NULL) { + /* we're ignoring the handler, just return */ + return; + } + } + i_assert(sig_handler != NULL); + + if (delayed && sig_pipe_fd[0] == -1) { + /* first delayed handler */ + if (pipe(sig_pipe_fd) < 0) + i_fatal("pipe() failed: %m"); + fd_close_on_exec(sig_pipe_fd[0], TRUE); + fd_close_on_exec(sig_pipe_fd[1], TRUE); + io_sig = io_add(sig_pipe_fd[0], IO_READ, signal_read, NULL); + } + + h = i_new(struct signal_handler, 1); + h->handler = handler; + h->context = context; + h->delayed = delayed; + + /* atomically set to signal_handlers[] list */ + h->next = signal_handlers[signo]; + signal_handlers[signo] = h; } -void lib_init_signals(void (*sig_quit_handler) (int)) +void lib_signals_unset_handler(int signo, signal_handler_t *handler, + void *context) { -#ifdef HAVE_SIGACTION - struct sigaction act; -#endif - - lib_signal_kill = 0; - lib_signal_hup_count = 0; - quit_handler = sig_quit_handler; + struct signal_handler *h, **p; - /* signal() behaviour is a bit inconsistent between systems - after the signal handler has been called. If the signal - isn't ignored, or your handler doesn't kill the program, - sigaction() should be used. */ -#ifdef HAVE_SIGACTION - if (sigemptyset(&act.sa_mask) < 0) - i_fatal("sigemptyset(): %m"); - act.sa_flags = 0; - act.sa_handler = sig_counter; - while (sigaction(SIGHUP, &act, NULL) < 0) { - if (errno != EINTR) - i_fatal("sigaction(): %m"); - } - while (sigaction(SIGUSR1, &act, NULL) < 0) { - if (errno != EINTR) - i_fatal("sigaction(): %m"); - } - while (sigaction(SIGUSR2, &act, NULL) < 0) { - if (errno != EINTR) - i_fatal("sigaction(): %m"); + for (p = &signal_handlers[signo]; *p != NULL; p = &(*p)->next) { + if ((*p)->handler == handler && (*p)->context == context) { + h = *p; + *p = h->next; + i_free(h); + return; + } } - /* we want to just ignore SIGALRM, but to get it to abort syscalls - with EINTR we can't just set it to SIG_IGN. sig_counter handler - is good enough. */ - while (sigaction(SIGALRM, &act, NULL) < 0) { - if (errno != EINTR) - i_fatal("sigaction(): %m"); + i_panic("lib_signals_unset_handler(%d, %p, %p): handler not found", + signo, (void *)handler, context); +} + +void lib_signals_init(void) +{ + sig_pipe_fd[0] = sig_pipe_fd[1] = -1; + io_sig = NULL; + + memset(signal_handlers, 0, sizeof(signal_handlers)); +} + +void lib_signals_deinit(void) +{ + struct signal_handler *handlers, *h; + int i; + + for (i = 0; i < MAX_SIGNAL_VALUE; i++) { + if (signal_handlers[i] != NULL) { + /* atomically remove from signal_handlers[] list */ + handlers = signal_handlers[i]; + signal_handlers[i] = NULL; + + while (handlers != NULL) { + h = handlers; + handlers = h->next; + i_free(h); + } + } } -#else - signal(SIGHUP, sig_counter); - signal(SIGUSR1, sig_counter); - signal(SIGUSR2, sig_counter); - signal(SIGALRM, sig_counter); -#endif - /* these signals should be called only once, so it's safe to use - signal() */ - signal(SIGINT, sig_quit); - signal(SIGTERM, sig_quit); - signal(SIGPIPE, SIG_IGN); + if (io_sig != NULL) + io_remove(io_sig); + if (sig_pipe_fd[0] != -1) { + if (close(sig_pipe_fd[0]) < 0) + i_error("close(sigpipe) failed: %m"); + if (close(sig_pipe_fd[1]) < 0) + i_error("close(sigpipe) failed: %m"); + } }
--- a/src/lib/lib-signals.h Sun Sep 25 13:52:27 2005 +0300 +++ b/src/lib/lib-signals.h Sun Sep 25 14:07:32 2005 +0300 @@ -1,10 +1,19 @@ #ifndef __LIB_SIGNALS_H #define __LIB_SIGNALS_H -extern int lib_signal_kill; -extern unsigned int lib_signal_hup_count; -extern unsigned int lib_signal_usr1_count, lib_signal_usr2_count; +#include <signal.h> + +typedef void signal_handler_t(int signo, void *context); -void lib_init_signals(void (*sig_quit_handler) (int)); +/* Set signal handler for specific signal. If delayed is TRUE, the handler + will be called later, ie. not as a real signal handler. If handler is NULL, + the signal is ignored. */ +void lib_signals_set_handler(int signo, int delayed, + signal_handler_t *handler, void *context); +void lib_signals_unset_handler(int signo, + signal_handler_t *handler, void *context); + +void lib_signals_init(void); +void lib_signals_deinit(void); #endif
--- a/src/login-common/main.c Sun Sep 25 13:52:27 2005 +0300 +++ b/src/login-common/main.c Sun Sep 25 14:07:32 2005 +0300 @@ -74,8 +74,12 @@ master_notify_finished(); } -static void sig_quit(int signo __attr_unused__) +static void sig_die(int signo, void *context __attr_unused__) { + /* warn about being killed because of some signal, except SIGINT (^C) + which is too common at least while testing :) */ + if (signo != SIGINT) + i_warning("Killed with signal %d", signo); io_loop_stop(ioloop); } @@ -170,7 +174,10 @@ { const char *value; - lib_init_signals(sig_quit); + lib_signals_init(); + lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGPIPE, FALSE, NULL, NULL); disable_plaintext_auth = getenv("DISABLE_PLAINTEXT_AUTH") != NULL; process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL; @@ -238,9 +245,6 @@ static void main_deinit(void) { - if (lib_signal_kill != 0) - i_warning("Killed with signal %d", lib_signal_kill); - if (io_listen != NULL) io_remove(io_listen); if (io_ssl_listen != NULL) io_remove(io_ssl_listen); @@ -251,6 +255,7 @@ clients_deinit(); master_deinit(); + lib_signals_deinit(); closelog(); }
--- a/src/master/main.c Sun Sep 25 13:52:27 2005 +0300 +++ b/src/master/main.c Sun Sep 25 14:07:32 2005 +0300 @@ -34,8 +34,6 @@ static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf"; static struct timeout *to; -static unsigned int settings_reload_hup_count = 0; -static unsigned int log_reopen_usr1_count = 0; static const char *env_tz; struct ioloop *ioloop; @@ -95,11 +93,6 @@ execv(executable, (char **)argv); } -static void sig_quit(int signo __attr_unused__) -{ - io_loop_stop(ioloop); -} - static void set_logfile(struct settings *set) { if (set->log_path == NULL) @@ -134,6 +127,27 @@ } } +static void sig_die(int signo, void *context __attr_unused__) +{ + /* warn about being killed because of some signal, except SIGINT (^C) + which is too common at least while testing :) */ + if (signo != SIGINT) + i_warning("Killed with signal %d", signo); + io_loop_stop(ioloop); +} + +static void sig_reload_settings(int signo __attr_unused__, + void *context __attr_unused__) +{ + settings_reload(); +} + +static void sig_reopen_logs(int signo __attr_unused__, + void *context __attr_unused__) +{ + set_logfile(settings_root->defaults); +} + static const char *get_exit_status_message(enum fatal_exit_status status) { switch (status) { @@ -161,15 +175,6 @@ pid_t pid; int status, process_type; - if (lib_signal_hup_count != settings_reload_hup_count) { - settings_reload_hup_count = lib_signal_hup_count; - settings_reload(); - } - if (lib_signal_usr1_count != log_reopen_usr1_count) { - log_reopen_usr1_count = lib_signal_usr1_count; - set_logfile(settings_root->defaults); - } - while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { /* get the type and remove from hash */ process_type = PID_GET_PROCESS_TYPE(pid); @@ -511,7 +516,12 @@ log_init(); - lib_init_signals(sig_quit); + lib_signals_init(); + lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGPIPE, FALSE, NULL, NULL); + lib_signals_set_handler(SIGHUP, TRUE, sig_reload_settings, NULL); + lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL); pids = hash_create(default_pool, default_pool, 128, NULL, NULL); to = timeout_add(100, timeout_handler, NULL); @@ -526,9 +536,6 @@ static void main_deinit(void) { - if (lib_signal_kill != 0) - i_warning("Killed with signal %d", lib_signal_kill); - (void)unlink(t_strconcat(settings_root->defaults->base_dir, "/master.pid", NULL)); @@ -545,6 +552,7 @@ i_error("close(null_fd) failed: %m"); hash_destroy(pids); + lib_signals_deinit(); log_deinit(); closelog(); }
--- a/src/pop3/main.c Sun Sep 25 13:52:27 2005 +0300 +++ b/src/pop3/main.c Sun Sep 25 14:07:32 2005 +0300 @@ -45,8 +45,12 @@ const char *uidl_format, *logout_format; enum uidl_keys uidl_keymask; -static void sig_quit(int signo __attr_unused__) +static void sig_die(int signo, void *context __attr_unused__) { + /* warn about being killed because of some signal, except SIGINT (^C) + which is too common at least while testing :) */ + if (signo != SIGINT) + i_warning("Killed with signal %d", signo); io_loop_stop(ioloop); } @@ -147,7 +151,10 @@ struct mail_storage *storage; const char *str, *mail; - lib_init_signals(sig_quit); + lib_signals_init(); + lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL); + lib_signals_set_handler(SIGPIPE, FALSE, NULL, NULL); if (getenv("USER") == NULL) i_fatal("USER environment missing"); @@ -236,17 +243,13 @@ static void main_deinit(void) { - /* warn about being killed because of some signal, except SIGINT (^C) - which is too common at least while testing :) */ - if (lib_signal_kill != 0 && lib_signal_kill != 2) - i_warning("Killed with signal %d", lib_signal_kill); - module_dir_unload(modules); clients_deinit(); mail_storage_deinit(); random_deinit(); + lib_signals_deinit(); closelog(); }