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;
while (service->processes != NULL)
service_process_destroy(service->processes);
}
- services_destroy(services);
+ services_destroy(services, FALSE);
services = new_services;
services_monitor_start(services);
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();
}
#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,
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;
}
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;
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);
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 {
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);
}
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);
}
}
-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) {
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;
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);