Skip to content

Commit

Permalink
master: Send SIGQUIT to processes running at deinit to close socket l…
Browse files Browse the repository at this point in the history
…isteners.

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.
  • Loading branch information
sirainen authored and GitLab committed May 9, 2017
1 parent 4cbe2b4 commit f86e2ce
Showing 1 changed file with 52 additions and 6 deletions.
58 changes: 52 additions & 6 deletions src/master/service-monitor.c
Expand Up @@ -597,19 +597,65 @@ static void services_monitor_wait(struct service_list *service_list)
}
}

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;

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);
Expand Down

0 comments on commit f86e2ce

Please sign in to comment.