changeset 10171:7f0ccd367351 HEAD

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.
author Timo Sirainen <tss@iki.fi>
date Fri, 23 Oct 2009 16:22:53 -0400
parents 47fdfd49af13
children a768005d1549
files src/dict/main.c src/imap/imap-settings.c src/imap/imap-settings.h src/imap/main.c src/lib-master/master-service-private.h src/lib-master/master-service-settings.c src/lib-master/master-service-settings.h src/lib-master/master-service.c src/lib-master/master-service.h src/log/main.c src/login-common/login-proxy.c src/login-common/login-proxy.h src/login-common/main.c src/master/service.c src/pop3-login/client.c src/pop3/main.c src/pop3/pop3-settings.c src/pop3/pop3-settings.h
diffstat 18 files changed, 143 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/src/dict/main.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/dict/main.c	Fri Oct 23 16:22:53 2009 -0400
@@ -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 @@
 	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);
--- a/src/imap/imap-settings.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/imap/imap-settings.c	Fri Oct 23 16:22:53 2009 -0400
@@ -21,7 +21,6 @@
 
 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 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
--- a/src/imap/imap-settings.h	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/imap/imap-settings.h	Fri Oct 23 16:22:53 2009 -0400
@@ -13,7 +13,6 @@
 
 struct imap_settings {
 	bool mail_debug;
-	bool shutdown_clients;
 
 	/* imap: */
 	unsigned int imap_max_line_length;
--- a/src/imap/main.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/imap/main.c	Fri Oct 23 16:22:53 2009 -0400
@@ -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 @@
 	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);
 
-	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 @@
 	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();
--- a/src/lib-master/master-service-private.h	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/lib-master/master-service-private.h	Fri Oct 23 16:22:53 2009 -0400
@@ -35,6 +35,9 @@
 	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;
 
--- a/src/lib-master/master-service-settings.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/lib-master/master-service-settings.c	Fri Oct 23 16:22:53 2009 -0400
@@ -31,6 +31,7 @@
 	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 @@
 	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 @@
 		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);
--- a/src/lib-master/master-service-settings.h	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/lib-master/master-service-settings.h	Fri Oct 23 16:22:53 2009 -0400
@@ -14,6 +14,7 @@
 	const char *log_timestamp;
 	const char *syslog_facility;
 	bool version_ignore;
+	bool shutdown_clients;
 };
 
 struct master_service_settings_input {
--- a/src/lib-master/master-service.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/lib-master/master-service.c	Fri Oct 23 16:22:53 2009 -0400
@@ -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 @@
 	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 @@
 
 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 @@
 	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)
--- a/src/lib-master/master-service.h	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/lib-master/master-service.h	Fri Oct 23 16:22:53 2009 -0400
@@ -65,6 +65,11 @@
    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. */
--- a/src/log/main.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/log/main.c	Fri Oct 23 16:22:53 2009 -0400
@@ -54,6 +54,10 @@
 
 	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();
--- a/src/login-common/login-proxy.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/login-common/login-proxy.c	Fri Oct 23 16:22:53 2009 -0400
@@ -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 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 @@
 	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 @@
 	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 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 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 @@
 	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();
--- a/src/login-common/login-proxy.h	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/login-common/login-proxy.h	Fri Oct 23 16:22:53 2009 -0400
@@ -60,6 +60,8 @@
 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);
 
--- a/src/login-common/main.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/login-common/main.c	Fri Oct 23 16:22:53 2009 -0400
@@ -27,6 +27,11 @@
 
 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 @@
 
 	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);
--- a/src/master/service.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/master/service.c	Fri Oct 23 16:22:53 2009 -0400
@@ -16,7 +16,7 @@
 #include <unistd.h>
 #include <signal.h>
 
-#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 @@
 	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);
--- a/src/pop3-login/client.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/pop3-login/client.c	Fri Oct 23 16:22:53 2009 -0400
@@ -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 @@
 	} 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)
--- a/src/pop3/main.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/pop3/main.c	Fri Oct 23 16:22:53 2009 -0400
@@ -25,6 +25,11 @@
 
 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 @@
 	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);
 
-	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 @@
 	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,
--- a/src/pop3/pop3-settings.c	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/pop3/pop3-settings.c	Fri Oct 23 16:22:53 2009 -0400
@@ -21,7 +21,6 @@
 
 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 pop3_settings pop3_default_settings = {
 	MEMBER(mail_debug) FALSE,
-	MEMBER(shutdown_clients) TRUE,
 
 	MEMBER(pop3_no_flag_updates) FALSE,
 	MEMBER(pop3_enable_last) FALSE,
--- a/src/pop3/pop3-settings.h	Fri Oct 23 16:19:34 2009 -0400
+++ b/src/pop3/pop3-settings.h	Fri Oct 23 16:22:53 2009 -0400
@@ -12,7 +12,6 @@
 
 struct pop3_settings {
 	bool mail_debug;
-	bool shutdown_clients;
 
 	/* pop3: */
 	bool pop3_no_flag_updates;