# HG changeset patch # User Timo Sirainen # Date 1493739643 -10800 # Node ID b914c4c07644e796b7679fd2e10b5706a8f85215 # Parent 38826e4865707724f634e03d276a5f8f28860f38 master: Send SIGQUIT to processes running at deinit to close socket listeners. This allows Dovecot to be restarted even when some lmtp/doveadm process is running for a long time. Otherwise it would keep the inet_listener socket open and prevent the new Dovecot from binding to the port. diff -r 38826e486570 -r b914c4c07644 src/master/service-monitor.c --- a/src/master/service-monitor.c Tue May 02 16:13:08 2017 +0300 +++ b/src/master/service-monitor.c Tue May 02 18:40:43 2017 +0300 @@ -597,6 +597,56 @@ } } +static bool service_processes_close_listeners(struct service *service) +{ + struct service_process *process = service->processes; + bool ret = FALSE; + + for (; process != NULL; process = process->next) { + if (kill(process->pid, SIGQUIT) == 0) + ret = TRUE; + else if (errno != ESRCH) { + service_error(service, "kill(%s, SIGQUIT) failed: %m", + dec2str(process->pid)); + } + } + return ret; +} + +static bool +service_list_processes_close_listeners(struct service_list *service_list) +{ + struct service *const *servicep; + bool ret = FALSE; + + array_foreach(&service_list->services, servicep) { + if (service_processes_close_listeners(*servicep)) + ret = TRUE; + } + return ret; +} + +static void services_monitor_wait_and_kill(struct service_list *service_list) +{ + /* 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); + + /* Even if the waiting stopped early because all the process_avail==0, + it can mean that there are processes that have the listener socket + open (just not actively being listened to). We'll need to make sure + that those sockets are closed before we exit, so that a restart + won't fail. Do this by sending SIGQUIT to all the child processes + that are left, which are handled by lib-master to immediately close + the listener in the signal handler itself. */ + if (service_list_processes_close_listeners(service_list)) { + /* SIGQUITs were sent. wait a little bit to make sure they're + also processed before quitting. */ + usleep(100000); + } +} + void services_monitor_stop(struct service_list *service_list, bool wait) { struct service *const *services; @@ -604,12 +654,8 @@ array_foreach(&service_list->services, services) service_monitor_close_dead_pipe(*services); - 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); - } + if (wait) + services_monitor_wait_and_kill(service_list); if (service_list->io_master != NULL) io_remove(&service_list->io_master);