From: Timo Sirainen Date: Fri, 23 Oct 2009 20:22:53 +0000 (-0400) Subject: Handle shutdown_clients globally for all services. X-Git-Tag: 2.0.alpha3~124 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=86791365b10f45982c88e70f2eb94fd6c3fea151;p=thirdparty%2Fdovecot%2Fcore.git Handle shutdown_clients globally for all services. Delay shutting down processes until it's convenient for them, but if they're not gone in 30 seconds forcibly stop. And if that doesn't help, master will start killing them in 60 seconds. --HG-- branch : HEAD --- diff --git a/src/dict/main.c b/src/dict/main.c index 5f79c93bdd..5ff64ffce3 100644 --- a/src/dict/main.c +++ b/src/dict/main.c @@ -15,6 +15,11 @@ static struct module *modules; +static void dict_die(void) +{ + /* hope that other processes relying on us will die first. */ +} + static void client_connected(const struct master_service_connection *conn) { dict_connection_create(conn->fd); @@ -86,6 +91,7 @@ int main(int argc, char *argv[]) master_service_init_log(master_service, "dict: "); main_preinit(); master_service_init_finish(master_service); + master_service_set_die_callback(master_service, dict_die); main_init(); master_service_run(master_service, client_connected); diff --git a/src/imap/imap-settings.c b/src/imap/imap-settings.c index 4207e9ac54..955652297f 100644 --- a/src/imap/imap-settings.c +++ b/src/imap/imap-settings.c @@ -21,7 +21,6 @@ static bool imap_settings_verify(void *_set, pool_t pool, static struct setting_define imap_setting_defines[] = { DEF(SET_BOOL, mail_debug), - DEF(SET_BOOL, shutdown_clients), DEF(SET_UINT, imap_max_line_length), DEF(SET_UINT, imap_idle_notify_interval), @@ -36,7 +35,6 @@ static struct setting_define imap_setting_defines[] = { static struct imap_settings imap_default_settings = { MEMBER(mail_debug) FALSE, - MEMBER(shutdown_clients) TRUE, /* RFC-2683 recommends at least 8000 bytes. Some clients however don't break large message sets to multiple commands, so we're pretty diff --git a/src/imap/imap-settings.h b/src/imap/imap-settings.h index 275a969880..2c4db59747 100644 --- a/src/imap/imap-settings.h +++ b/src/imap/imap-settings.h @@ -13,7 +13,6 @@ enum imap_client_workarounds { struct imap_settings { bool mail_debug; - bool shutdown_clients; /* imap: */ unsigned int imap_max_line_length; diff --git a/src/imap/main.c b/src/imap/main.c index 789a8aa6b2..b26cc61cf5 100644 --- a/src/imap/main.c +++ b/src/imap/main.c @@ -23,11 +23,44 @@ #define IS_STANDALONE() \ (getenv(MASTER_UID_ENV) == NULL) +#define IMAP_DIE_IDLE_SECS 10 + static struct mail_storage_service_ctx *storage_service; static struct master_login *master_login = NULL; void (*hook_client_created)(struct client **client) = NULL; +static void client_kill_idle(struct client *client) +{ + if (client->output_lock != NULL) + return; + + client_send_line(client, "* BYE Server shutting down."); + client_destroy(client, "Server shutting down."); +} + +static void imap_die(void) +{ + struct client *client, *next; + time_t last_io, now = time(NULL); + time_t stop_timestamp = now - IMAP_DIE_IDLE_SECS; + unsigned int stop_msecs; + + for (client = imap_clients; client != NULL; client = next) { + next = client->next; + + last_io = I_MAX(client->last_input, client->last_output); + if (last_io <= stop_timestamp) + client_kill_idle(client); + else { + timeout_remove(&client->to_idle); + stop_msecs = (last_io - stop_timestamp) * 1000; + client->to_idle = timeout_add(stop_msecs, + client_kill_idle, client); + } + } +} + static void client_add_input(struct client *client, const buffer_t *buf) { struct ostream *output; @@ -94,12 +127,9 @@ client_create_from_input(const struct mail_storage_service_input *input, if (mail_storage_service_lookup_next(storage_service, input, &user, &mail_user, error_r) <= 0) return -1; - set = mail_storage_service_user_get_set(user)[1]; - restrict_access_allow_coredumps(TRUE); - if (set->shutdown_clients) - master_service_set_die_with_master(master_service, TRUE); + set = mail_storage_service_user_get_set(user)[1]; client = client_create(fd_in, fd_out, mail_user, user, set); T_BEGIN { client_add_input(client, input_buf); @@ -203,6 +233,7 @@ int main(int argc, char *argv[]) if (master_getopt(master_service) > 0) return FATAL_DEFAULT; master_service_init_finish(master_service); + master_service_set_die_callback(master_service, imap_die); /* plugins may want to add commands, so this needs to be called early */ commands_init(); diff --git a/src/lib-master/master-service-private.h b/src/lib-master/master-service-private.h index 0e3c04828d..1bca16959d 100644 --- a/src/lib-master/master-service-private.h +++ b/src/lib-master/master-service-private.h @@ -35,6 +35,9 @@ struct master_service { unsigned int total_available_count; struct master_status master_status; + void (*die_callback)(void); + struct timeout *to_die; + void (*avail_overflow_callback)(void); struct timeout *to_overflow_state; diff --git a/src/lib-master/master-service-settings.c b/src/lib-master/master-service-settings.c index 46a05afa72..0f657d33f5 100644 --- a/src/lib-master/master-service-settings.c +++ b/src/lib-master/master-service-settings.c @@ -31,6 +31,7 @@ static struct setting_define master_service_setting_defines[] = { DEF(SET_STR, log_timestamp), DEF(SET_STR, syslog_facility), DEF(SET_BOOL, version_ignore), + DEF(SET_BOOL, shutdown_clients), SETTING_DEFINE_LIST_END }; @@ -41,7 +42,8 @@ static struct master_service_settings master_service_default_settings = { MEMBER(debug_log_path) "", MEMBER(log_timestamp) DEFAULT_FAILURE_STAMP_FORMAT, MEMBER(syslog_facility) "mail", - MEMBER(version_ignore) FALSE + MEMBER(version_ignore) FALSE, + MEMBER(shutdown_clients) TRUE }; struct setting_parser_info master_service_setting_parser_info = { @@ -295,6 +297,9 @@ int master_service_settings_read(struct master_service *service, service->version_string = NULL; } + if (service->set->shutdown_clients) + master_service_set_die_with_master(master_service, TRUE); + /* if we change any settings afterwards, they're in expanded form. especially all settings from userdb are already expanded. */ settings_parse_set_expanded(service->set_parser, TRUE); diff --git a/src/lib-master/master-service-settings.h b/src/lib-master/master-service-settings.h index a574e6f7ad..8f0bae94d1 100644 --- a/src/lib-master/master-service-settings.h +++ b/src/lib-master/master-service-settings.h @@ -14,6 +14,7 @@ struct master_service_settings { const char *log_timestamp; const char *syslog_facility; bool version_ignore; + bool shutdown_clients; }; struct master_service_settings_input { diff --git a/src/lib-master/master-service.c b/src/lib-master/master-service.c index e69d62fbcd..ff7efede89 100644 --- a/src/lib-master/master-service.c +++ b/src/lib-master/master-service.c @@ -32,6 +32,10 @@ just a fallback against race conditions. */ #define MASTER_SERVICE_STATE_CHECK_MSECS 1000 +/* If die callback hasn't managed to stop the service for this many seconds, + force it. */ +#define MASTER_SERVICE_DIE_TIMEOUT_MSECS (30*1000) + struct master_service *master_service; static void master_service_refresh_login_state(struct master_service *service); @@ -221,6 +225,12 @@ void master_service_set_die_with_master(struct master_service *service, service->die_with_master = set; } +void master_service_set_die_callback(struct master_service *service, + void (*callback)(void)) +{ + service->die_callback = callback; +} + bool master_service_parse_option(struct master_service *service, int opt, const char *arg) { @@ -257,11 +267,19 @@ bool master_service_parse_option(struct master_service *service, static void master_service_error(struct master_service *service) { + io_listeners_remove(service); if (service->master_status.available_count == - service->total_available_count || service->die_with_master) - master_service_stop(service); - else - io_listeners_remove(service); + service->total_available_count || service->die_with_master) { + if (service->die_callback == NULL) + master_service_stop(service); + else { + service->to_die = + timeout_add(MASTER_SERVICE_DIE_TIMEOUT_MSECS, + master_service_stop, + service); + service->die_callback(); + } + } } static void master_status_error(void *context) @@ -572,6 +590,8 @@ void master_service_deinit(struct master_service **_service) io_listeners_remove(service); master_service_close_config_fd(service); + if (service->to_die != NULL) + timeout_remove(&service->to_die); if (service->to_overflow_state != NULL) timeout_remove(&service->to_overflow_state); if (service->io_status_error != NULL) diff --git a/src/lib-master/master-service.h b/src/lib-master/master-service.h index 824030bfb3..88a649073b 100644 --- a/src/lib-master/master-service.h +++ b/src/lib-master/master-service.h @@ -65,6 +65,11 @@ void master_service_init_log(struct master_service *service, Normally all existing clients are handled first. */ void master_service_set_die_with_master(struct master_service *service, bool set); +/* Call the given when master connection dies and die_with_master is TRUE. + The callback is expected to shut down the service somewhat soon or it's + done forcibly. If NULL, the service is stopped immediately. */ +void master_service_set_die_callback(struct master_service *service, + void (*callback)(void)); /* Call the given callback when there are no available connections and master has indicated that it can't create any more processes to handle requests. The callback could decide to kill one of the existing connections. */ diff --git a/src/log/main.c b/src/log/main.c index 2c8aaecfc0..9aed0f0953 100644 --- a/src/log/main.c +++ b/src/log/main.c @@ -54,6 +54,10 @@ int main(int argc, char *argv[]) master_service_init_log(master_service, "log: "); master_service_init_finish(master_service); + + /* logging should never die if there are some clients */ + master_service_set_die_with_master(master_service, FALSE); + main_init(); master_service_run(master_service, client_connected); main_deinit(); diff --git a/src/login-common/login-proxy.c b/src/login-common/login-proxy.c index d0606a4a8f..7b405fc51e 100644 --- a/src/login-common/login-proxy.c +++ b/src/login-common/login-proxy.c @@ -15,6 +15,7 @@ #define MAX_PROXY_INPUT_SIZE 4096 #define OUTBUF_THRESHOLD 1024 +#define LOGIN_PROXY_DIE_IDLE_SECS 2 struct login_proxy { struct login_proxy *prev, *next; @@ -25,6 +26,7 @@ struct login_proxy { struct istream *server_input; struct ostream *client_output, *server_output; struct ssl_proxy *ssl_server_proxy; + time_t last_io; struct timeval created; struct timeout *to; @@ -49,6 +51,7 @@ static void server_input(struct login_proxy *proxy) unsigned char buf[OUTBUF_THRESHOLD]; ssize_t ret; + proxy->last_io = ioloop_time; if (o_stream_get_buffer_used_size(proxy->client_output) > OUTBUF_THRESHOLD) { /* client's output buffer is already quite full. @@ -67,6 +70,7 @@ static void proxy_client_input(struct login_proxy *proxy) unsigned char buf[OUTBUF_THRESHOLD]; ssize_t ret; + proxy->last_io = ioloop_time; if (o_stream_get_buffer_used_size(proxy->server_output) > OUTBUF_THRESHOLD) { /* proxy's output buffer is already quite full. @@ -82,6 +86,7 @@ static void proxy_client_input(struct login_proxy *proxy) static int server_output(struct login_proxy *proxy) { + proxy->last_io = ioloop_time; if (o_stream_flush(proxy->server_output) < 0) { login_proxy_free(&proxy); return 1; @@ -100,6 +105,7 @@ static int server_output(struct login_proxy *proxy) static int proxy_client_output(struct login_proxy *proxy) { + proxy->last_io = ioloop_time; if (o_stream_flush(proxy->client_output) < 0) { login_proxy_free(&proxy); return 1; @@ -434,6 +440,32 @@ int login_proxy_starttls(struct login_proxy *proxy) return 0; } +static void proxy_kill_idle(struct login_proxy *proxy) +{ + login_proxy_free(&proxy); +} + +void login_proxy_kill_idle(void) +{ + struct login_proxy *proxy, *next; + time_t now = time(NULL); + time_t stop_timestamp = now - LOGIN_PROXY_DIE_IDLE_SECS; + unsigned int stop_msecs; + + for (proxy = login_proxies; proxy != NULL; proxy = next) { + next = proxy->next; + + if (proxy->last_io <= stop_timestamp) + proxy_kill_idle(proxy); + else { + i_assert(proxy->to == NULL); + stop_msecs = (proxy->last_io - stop_timestamp) * 1000; + proxy->to = timeout_add(stop_msecs, + proxy_kill_idle, proxy); + } + } +} + void login_proxy_init(void) { proxy_state = login_proxy_state_init(); diff --git a/src/login-common/login-proxy.h b/src/login-common/login-proxy.h index 658100a050..87cbedb116 100644 --- a/src/login-common/login-proxy.h +++ b/src/login-common/login-proxy.h @@ -60,6 +60,8 @@ unsigned int login_proxy_get_port(const struct login_proxy *proxy) ATTR_PURE; enum login_proxy_ssl_flags login_proxy_get_ssl_flags(const struct login_proxy *proxy) ATTR_PURE; +void login_proxy_kill_idle(void); + void login_proxy_init(void); void login_proxy_deinit(void); diff --git a/src/login-common/main.c b/src/login-common/main.c index 6d4fb1e9bc..7bb9a44525 100644 --- a/src/login-common/main.c +++ b/src/login-common/main.c @@ -27,6 +27,11 @@ void **global_other_settings; static bool ssl_connections = FALSE; +static void login_die(void) +{ + login_proxy_kill_idle(); +} + static void client_connected(const struct master_service_connection *conn) { struct client *client; @@ -138,6 +143,7 @@ static void main_init(void) master_service_set_avail_overflow_callback(master_service, client_destroy_oldest); + master_service_set_die_callback(master_service, login_die); auth_client = auth_client_init("auth", (unsigned int)getpid(), FALSE); auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL); diff --git a/src/master/service.c b/src/master/service.c index a3b180e7de..ddcbed1906 100644 --- a/src/master/service.c +++ b/src/master/service.c @@ -16,7 +16,7 @@ #include #include -#define SERVICE_DIE_TIMEOUT_MSECS (1000*10) +#define SERVICE_DIE_TIMEOUT_MSECS (1000*60) #define SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS 2 struct hash_table *service_pids; @@ -520,7 +520,8 @@ void services_destroy(struct service_list *service_list) services_monitor_stop(service_list); service_list_deinit_anvil(service_list); - if (service_list->refcount > 1) { + if (service_list->refcount > 1 && + service_list->service_set->shutdown_clients) { service_list->to_kill = timeout_add(SERVICE_DIE_TIMEOUT_MSECS, services_kill_timeout, service_list); diff --git a/src/pop3-login/client.c b/src/pop3-login/client.c index 768b520774..84601e3655 100644 --- a/src/pop3-login/client.c +++ b/src/pop3-login/client.c @@ -11,6 +11,7 @@ #include "safe-memset.h" #include "str.h" #include "strescape.h" +#include "master-service.h" #include "client.h" #include "client-authenticate.h" #include "auth-client.h" @@ -209,10 +210,16 @@ pop3_client_send_line(struct client *client, enum client_cmd_reply reply, } T_END; } +static void pop3_login_die(void) +{ + /* do nothing. pop3 connections typically die pretty quick anyway. */ +} void clients_init(void) { login_set_roots = pop3_login_setting_roots; + /* override the default login_die() */ + master_service_set_die_callback(master_service, pop3_login_die); } void clients_deinit(void) diff --git a/src/pop3/main.c b/src/pop3/main.c index 622206863e..689c452ccc 100644 --- a/src/pop3/main.c +++ b/src/pop3/main.c @@ -25,6 +25,11 @@ static struct master_login *master_login = NULL; void (*hook_client_created)(struct client **client) = NULL; +static void pop3_die(void) +{ + /* do nothing. pop3 connections typically die pretty quick anyway. */ +} + static void client_add_input(struct client *client, const buffer_t *buf) { struct ostream *output; @@ -57,12 +62,9 @@ client_create_from_input(const struct mail_storage_service_input *input, if (mail_storage_service_lookup_next(storage_service, input, &user, &mail_user, error_r) <= 0) return -1; - set = mail_storage_service_user_get_set(user)[1]; - restrict_access_allow_coredumps(TRUE); - if (set->shutdown_clients) - master_service_set_die_with_master(master_service, TRUE); + set = mail_storage_service_user_get_set(user)[1]; client = client_create(fd_in, fd_out, mail_user, user, set); T_BEGIN { client_add_input(client, input_buf); @@ -166,6 +168,7 @@ int main(int argc, char *argv[]) if (master_getopt(master_service) > 0) return FATAL_DEFAULT; master_service_init_finish(master_service); + master_service_set_die_callback(master_service, pop3_die); storage_service = mail_storage_service_init(master_service, diff --git a/src/pop3/pop3-settings.c b/src/pop3/pop3-settings.c index 928b5b1f68..42dd6f06bb 100644 --- a/src/pop3/pop3-settings.c +++ b/src/pop3/pop3-settings.c @@ -21,7 +21,6 @@ static bool pop3_settings_verify(void *_set, pool_t pool, static struct setting_define pop3_setting_defines[] = { DEF(SET_BOOL, mail_debug), - DEF(SET_BOOL, shutdown_clients), DEF(SET_BOOL, pop3_no_flag_updates), DEF(SET_BOOL, pop3_enable_last), @@ -36,7 +35,6 @@ static struct setting_define pop3_setting_defines[] = { static struct pop3_settings pop3_default_settings = { MEMBER(mail_debug) FALSE, - MEMBER(shutdown_clients) TRUE, MEMBER(pop3_no_flag_updates) FALSE, MEMBER(pop3_enable_last) FALSE, diff --git a/src/pop3/pop3-settings.h b/src/pop3/pop3-settings.h index 02515a5222..4afdc24885 100644 --- a/src/pop3/pop3-settings.h +++ b/src/pop3/pop3-settings.h @@ -12,7 +12,6 @@ enum pop3_client_workarounds { struct pop3_settings { bool mail_debug; - bool shutdown_clients; /* pop3: */ bool pop3_no_flag_updates;