]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Handle shutdown_clients globally for all services.
authorTimo Sirainen <tss@iki.fi>
Fri, 23 Oct 2009 20:22:53 +0000 (16:22 -0400)
committerTimo Sirainen <tss@iki.fi>
Fri, 23 Oct 2009 20:22:53 +0000 (16:22 -0400)
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

18 files changed:
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

index 5f79c93bdd490495b08032e441fd450de69313fa..5ff64ffce37b24f0b1d0952b02eb345983fdf099 100644 (file)
 
 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);
index 4207e9ac54c07688cd6edfb2ad96911676dfbced..955652297f5c54c2fdd76968f76b163121751d5f 100644 (file)
@@ -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
index 275a969880b99a92118fb8f5284d28fef7ab399f..2c4db59747e82ebe6797677ad6275608e019c4cf 100644 (file)
@@ -13,7 +13,6 @@ enum imap_client_workarounds {
 
 struct imap_settings {
        bool mail_debug;
-       bool shutdown_clients;
 
        /* imap: */
        unsigned int imap_max_line_length;
index 789a8aa6b2b639d32a0641cc5a83bb85d23c72f4..b26cc61cf56aa1c56b1ba4304c3d624cffc90217 100644 (file)
 #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();
index 0e3c04828d5b9000643b942182517c6b46e3480d..1bca16959d723f1d545018c9d1109746bf14572f 100644 (file)
@@ -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;
 
index 46a05afa72b34f44c564392f8e85aa03e2e31a5c..0f657d33f50fc8affd8a4dc7a820b0c110e1898d 100644 (file)
@@ -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);
index a574e6f7ad7977d90e9a1d9e7c87245d07f9c646..8f0bae94d1727ccbc22d07d31beb03495fb44240 100644 (file)
@@ -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 {
index e69d62fbcd3246fe5615f5fad54bff057921501c..ff7efede8989da16b59e5af582420f775427ffb7 100644 (file)
    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)
index 824030bfb38bed11879d3567a428ba8c653f1111..88a649073bbd32cab0a1f91e58f2de9aeb5e5c08 100644 (file)
@@ -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. */
index 2c8aaecfc058338569c12805406512b7ff3fe0d5..9aed0f0953a1d65109441f76e2d291562efe2dbe 100644 (file)
@@ -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();
index d0606a4a8fc7682031c693414eb27a2f9ec87c4a..7b405fc51e84fcf2ad52297c287c1900a61b097b 100644 (file)
@@ -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();
index 658100a0504b146b2bbbc0c22bd8746e9b899f50..87cbedb116802b9f79b03cb860733f05bb96472d 100644 (file)
@@ -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);
 
index 6d4fb1e9bc04de34c2e3646c00c4fec702bc9b77..7bb9a445250b521c8605b9d6781c3e98f5258fc4 100644 (file)
@@ -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);
index a3b180e7dec4623cbe6246f2fbcb2c1ae9ef96a0..ddcbed19066a1f076731d683716c4e6f13a9d73d 100644 (file)
@@ -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 @@ 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);
index 768b5207746003a66ab32d522c74286e999188ee..84601e365565702689a0508807232fc9e8e26b96 100644 (file)
@@ -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)
index 622206863ee0be427412c9d22c7a79c448e394b0..689c452ccc27b085ece324ed0da11d3c5f31b47f 100644 (file)
@@ -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,
index 928b5b1f68c1d1bedb9afa9562a032d10dabc00c..42dd6f06bb27c7476aee98d05c0551c7b7c1c3c4 100644 (file)
@@ -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,
index 02515a5222ca86abbddc071afd3aa577c04ad1f9..4afdc24885443f420ce1b53e0c1b1f68177cbd5a 100644 (file)
@@ -12,7 +12,6 @@ enum pop3_client_workarounds {
 
 struct pop3_settings {
        bool mail_debug;
-       bool shutdown_clients;
 
        /* pop3: */
        bool pop3_no_flag_updates;