]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
master: Wait for services to stop listening before unlinking the pid file.
authorTimo Sirainen <tss@iki.fi>
Sat, 5 Nov 2011 17:17:59 +0000 (19:17 +0200)
committerTimo Sirainen <tss@iki.fi>
Sat, 5 Nov 2011 17:17:59 +0000 (19:17 +0200)
src/master/main.c
src/master/service-monitor.c
src/master/service-monitor.h
src/master/service.c
src/master/service.h

index a4cea8b2eecf6e73d10680f0b4e0b15e73489e7f..63c061d4661ab8f12d0c099dedc42f9e1d23bf4f 100644 (file)
@@ -330,7 +330,7 @@ sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
                         services->config->config_file_path);
 
        /* switch to new configuration. */
-       services_monitor_stop(services);
+       services_monitor_stop(services, FALSE);
        if (services_listen_using(new_services, services) < 0) {
                services_monitor_start(services);
                return;
@@ -342,7 +342,7 @@ sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
                while (service->processes != NULL)
                        service_process_destroy(service->processes);
        }
-       services_destroy(services);
+       services_destroy(services, FALSE);
 
        services = new_services;
         services_monitor_start(services);
@@ -455,13 +455,26 @@ static void main_init(const struct master_settings *set)
        services_monitor_start(services);
 }
 
+static void global_dead_pipe_close(void)
+{
+       if (close(global_master_dead_pipe_fd[0]) < 0)
+               i_error("close(global dead pipe) failed: %m");
+       if (close(global_master_dead_pipe_fd[1]) < 0)
+               i_error("close(global dead pipe) failed: %m");
+       global_master_dead_pipe_fd[0] = -1;
+       global_master_dead_pipe_fd[1] = -1;
+}
+
 static void main_deinit(void)
 {
+       /* kill services and wait for them to die before unlinking pid file */
+       global_dead_pipe_close();
+       services_destroy(services, TRUE);
+
        if (unlink(pidfile_path) < 0)
                i_error("unlink(%s) failed: %m", pidfile_path);
        i_free(pidfile_path);
 
-       services_destroy(services);
        service_anvil_global_deinit();
        service_pids_deinit();
 }
index d9d3309f806d3deffdfb844dfcbdc15a2209c75a..036fcc954143acf5c8330e015158152825e765b9 100644 (file)
@@ -23,6 +23,7 @@
 #define SERVICE_STARTUP_FAILURE_THROTTLE_SECS 60
 #define SERVICE_DROP_WARN_INTERVAL_SECS 60
 #define SERVICE_DROP_TIMEOUT_MSECS (10*1000)
+#define MAX_DIE_WAIT_SECS 5
 
 static void service_monitor_start_extra_avail(struct service *service);
 static void service_status_more(struct service_process *process,
@@ -171,8 +172,10 @@ static void service_status_input(struct service *service)
        if (ret <= 0) {
                if (ret == 0)
                        service_error(service, "read(status) failed: EOF");
-               else
+               else if (errno != EAGAIN)
                        service_error(service, "read(status) failed: %m");
+               else
+                       return;
                service_monitor_stop(service);
                return;
        }
@@ -467,7 +470,28 @@ void service_monitor_stop(struct service *service)
                timeout_remove(&service->to_throttle);
 }
 
-void services_monitor_stop(struct service_list *service_list)
+static void services_monitor_wait(struct service_list *service_list)
+{
+       struct service *const *servicep;
+       time_t max_wait_time = time(NULL) + MAX_DIE_WAIT_SECS;
+       bool finished;
+
+       for (;;) {
+               finished = TRUE;
+               services_monitor_reap_children();
+               array_foreach(&service_list->services, servicep) {
+                       if ((*servicep)->status_fd[0] != -1)
+                               service_status_input(*servicep);
+                       if ((*servicep)->process_avail > 0)
+                               finished = FALSE;
+               }
+               if (finished || time(NULL) > max_wait_time)
+                       break;
+               usleep(100000);
+       }
+}
+
+void services_monitor_stop(struct service_list *service_list, bool wait)
 {
        struct service *const *services;
 
@@ -480,6 +504,13 @@ void services_monitor_stop(struct service_list *service_list)
                service_list->master_dead_pipe_fd[1] = -1;
        }
 
+       if (wait) {
+               /* we've notified all children that the master is dead.
+                  now wait for the children to either die or to tell that
+                  they're no longer listening for new connections */
+               services_monitor_wait(service_list);
+       }
+
        array_foreach(&service_list->services, services)
                service_monitor_stop(*services);
 
@@ -516,7 +547,8 @@ void services_monitor_reap_children(void)
                service = process->service;
                if (status == 0) {
                        /* success */
-                       if (service->listen_pending)
+                       if (service->listen_pending &&
+                           !service->list->destroying)
                                service_monitor_listen_start(service);
                        throttle = FALSE;
                } else {
@@ -535,7 +567,8 @@ void services_monitor_reap_children(void)
                        service_monitor_throttle(service);
                service_stopped = service->status_fd[0] == -1;
                if (!service_stopped) {
-                       service_monitor_start_extra_avail(service);
+                       if (!service->list->destroying)
+                               service_monitor_start_extra_avail(service);
                        if (service->to_throttle == NULL)
                                service_monitor_listen_start(service);
                }
index 8e7b5508867520927e6a1cf9accdb7d7aea87f88..848cb3c1e0e9c4895842ff0c255285a3df6b1ce1 100644 (file)
@@ -5,7 +5,7 @@
 void services_monitor_start(struct service_list *service_list);
 
 /* Stop services. */
-void services_monitor_stop(struct service_list *service_list);
+void services_monitor_stop(struct service_list *service_list, bool wait);
 
 /* Call after SIGCHLD has been detected */
 void services_monitor_reap_children(void);
index 7bb03c22337a4941558178543d45dfd5bea1ac90..11cd6bc5e547c916c879345ca713deb4030314e8 100644 (file)
@@ -615,12 +615,13 @@ static void services_kill_timeout(struct service_list *service_list)
        }
 }
 
-void services_destroy(struct service_list *service_list)
+void services_destroy(struct service_list *service_list, bool wait)
 {
        /* make sure we log if child processes died unexpectedly */
-        services_monitor_reap_children();
+       service_list->destroying = TRUE;
+       services_monitor_reap_children();
 
-       services_monitor_stop(service_list);
+       services_monitor_stop(service_list, wait);
 
        if (service_list->refcount > 1 &&
            service_list->service_set->shutdown_clients) {
index 14ebc07618ae21d22144ac89a77557b0119f5b13..15eeb0c6e16e0e557ad314aa1ae9f5f0480ae879 100644 (file)
@@ -126,6 +126,7 @@ struct service_list {
 
        ARRAY_DEFINE(services, struct service *);
 
+       unsigned int destroying:1;
        unsigned int destroyed:1;
        unsigned int sigterm_sent:1;
        unsigned int sigterm_sent_to_log:1;
@@ -138,7 +139,7 @@ int services_create(const struct master_settings *set,
                    struct service_list **services_r, const char **error_r);
 
 /* Destroy services */
-void services_destroy(struct service_list *service_list);
+void services_destroy(struct service_list *service_list, bool wait);
 
 void service_list_ref(struct service_list *service_list);
 void service_list_unref(struct service_list *service_list);