From: Timo Sirainen Date: Fri, 4 Sep 2009 21:06:58 +0000 (-0400) Subject: master: Several fixes to handling SIGHUPs. X-Git-Tag: 2.0.alpha1~176 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3b959c98e05e780de2a063a4a9d8d393dc61ed58;p=thirdparty%2Fdovecot%2Fcore.git master: Several fixes to handling SIGHUPs. --HG-- branch : HEAD --- diff --git a/src/master/common.h b/src/master/common.h index 027993e333..89e46b06bc 100644 --- a/src/master/common.h +++ b/src/master/common.h @@ -12,6 +12,7 @@ extern gid_t master_gid; extern bool auth_success_written; extern bool core_dumps_disabled; extern int null_fd; +extern struct service_list *services; void process_exec(const char *cmd, const char *extra_args[]) ATTR_NORETURN; diff --git a/src/master/main.c b/src/master/main.c index 25a9135dbc..2463dd8e76 100644 --- a/src/master/main.c +++ b/src/master/main.c @@ -38,9 +38,9 @@ gid_t master_gid; bool auth_success_written; bool core_dumps_disabled; int null_fd; +struct service_list *services; static char *pidfile_path; -static struct service_list *services; static fatal_failure_callback_t *orig_fatal_callback; static const char *child_process_env[3]; /* @UNSAFE */ diff --git a/src/master/service-auth-server.c b/src/master/service-auth-server.c index 89eb7a98ae..cdbff6d2ce 100644 --- a/src/master/service-auth-server.c +++ b/src/master/service-auth-server.c @@ -83,6 +83,22 @@ auth_process_lookup_request(struct service_process_auth_server *process, return request; } +static struct service * +auth_process_get_dest_service(struct service_process_auth_source *process) +{ + struct service *service = process->process.service; + + if (!service->list->destroyed) + return service->auth_dest_service; + + service = service_lookup(services, service->set->auth_dest_service); + if (service == NULL) { + i_warning("service(%s): Lost destination service %s", + service->set->name, service->set->auth_dest_service); + } + return service; +} + static int auth_process_input_user(struct service_process_auth_server *process, const char *args) { @@ -103,12 +119,12 @@ auth_process_input_user(struct service_process_auth_server *process, const char request = auth_process_lookup_request(process, id); if (request != NULL) { - struct service *dest_service = - request->process->process.service->auth_dest_service; + struct service *dest_service; struct service_process *dest_process; - dest_process = service_process_create(dest_service, list + 1, - request); + dest_service = auth_process_get_dest_service(request->process); + dest_process = dest_service == NULL ? NULL : + service_process_create(dest_service, list + 1, request); status = dest_process != NULL ? MASTER_AUTH_STATUS_OK : MASTER_AUTH_STATUS_INTERNAL_ERROR; diff --git a/src/master/service-listen.c b/src/master/service-listen.c index 4345134f15..699bee8cf1 100644 --- a/src/master/service-listen.c +++ b/src/master/service-listen.c @@ -190,16 +190,11 @@ static int listener_equals(const struct service_listener *l1, switch (l1->type) { case SERVICE_LISTENER_UNIX: case SERVICE_LISTENER_FIFO: - if (strcmp(l1->set.fileset.set->path, - l2->set.fileset.set->path) != 0) - return FALSE; - if (l1->set.fileset.set->mode != l2->set.fileset.set->mode) - return FALSE; - if (l1->set.fileset.uid != l2->set.fileset.uid) - return FALSE; - if (l1->set.fileset.gid != l2->set.fileset.gid) - return FALSE; - return TRUE; + /* We could just keep using the same listener, but it's more + likely to cause problems if old process accepts a connection + before it knows that it should die. So just always unlink + and recreate unix/fifo listeners. */ + return FALSE; case SERVICE_LISTENER_INET: if (memcmp(&l1->set.inetset.ip, &l2->set.inetset.ip, sizeof(l1->set.inetset.ip)) != 0) @@ -253,6 +248,18 @@ int services_listen_using(struct service_list *new_service_list, if (close(old_listeners[j]->fd) < 0) i_error("close(listener) failed: %m"); } + switch (old_listeners[j]->type) { + case SERVICE_LISTENER_UNIX: + case SERVICE_LISTENER_FIFO: { + const char *path = + old_listeners[j]->set.fileset.set->path; + if (unlink(path) < 0) + i_error("unlink(%s) failed: %m", path); + break; + } + case SERVICE_LISTENER_INET: + break; + } } /* and let services_listen() deal with the remaining fds */ diff --git a/src/master/service-monitor.c b/src/master/service-monitor.c index 4a17482942..a8765d73bf 100644 --- a/src/master/service-monitor.c +++ b/src/master/service-monitor.c @@ -277,6 +277,7 @@ void services_monitor_reap_children(void) struct service *service; pid_t pid; int status; + bool service_destroyed; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { process = hash_table_lookup(service_pids, &pid); @@ -294,10 +295,13 @@ void services_monitor_reap_children(void) } else { service_process_failure(process, status); } + service_destroyed = service->list->destroyed; service_process_destroy(process); - service_monitor_start_extra_avail(service); - if (service->to_throttle == NULL) - service_monitor_listen_start(service); + if (!service_destroyed) { + service_monitor_start_extra_avail(service); + if (service->to_throttle == NULL) + service_monitor_listen_start(service); + } } } diff --git a/src/master/service.c b/src/master/service.c index e8ac70e836..9bf35353a9 100644 --- a/src/master/service.c +++ b/src/master/service.c @@ -313,7 +313,7 @@ static int pid_hash_cmp(const void *p1, const void *p2) *pid1 > *pid2 ? 1 : 0; } -static struct service * +struct service * service_lookup(struct service_list *service_list, const char *name) { struct service *const *services; @@ -455,22 +455,35 @@ void service_signal(struct service *service, int signo) static void services_kill_timeout(struct service_list *service_list) { - struct service *const *services; + struct service *const *services, *log_service; unsigned int i, count; + bool sigterm_log; int sig; - if (!service_list->sigterm_sent) + if (!service_list->sigterm_sent || !service_list->sigterm_sent_to_log) sig = SIGTERM; else sig = SIGKILL; + sigterm_log = service_list->sigterm_sent; service_list->sigterm_sent = TRUE; i_warning("Processes aren't dying after reload, sending %s.", sig == SIGTERM ? "SIGTERM" : "SIGKILL"); + log_service = NULL; services = array_get(&service_list->services, &count); - for (i = 0; i < count; i++) - service_signal(services[i], sig); + for (i = 0; i < count; i++) { + if (services[i]->type == SERVICE_TYPE_LOG) + log_service = services[i]; + else + service_signal(services[i], sig); + } + /* kill log service later so it could still have a chance of logging + something */ + if (log_service != NULL && sigterm_log) { + service_signal(log_service, sig); + service_list->sigterm_sent_to_log = TRUE; + } } void services_destroy(struct service_list *service_list) @@ -487,6 +500,7 @@ void services_destroy(struct service_list *service_list) services_kill_timeout, service_list); } + service_list->destroyed = TRUE; service_list_unref(service_list); } diff --git a/src/master/service.h b/src/master/service.h index 10b6c9286a..a28eeae744 100644 --- a/src/master/service.h +++ b/src/master/service.h @@ -119,7 +119,9 @@ struct service_list { ARRAY_DEFINE(services, struct service *); + unsigned int destroyed:1; unsigned int sigterm_sent:1; + unsigned int sigterm_sent_to_log:1; }; extern struct hash_table *service_pids; @@ -147,6 +149,10 @@ void service_throttle(struct service *service, unsigned int secs); void services_throttle_time_sensitives(struct service_list *list, unsigned int secs); +/* Find a service by name. */ +struct service * +service_lookup(struct service_list *service_list, const char *name); + void service_error(struct service *service, const char *format, ...) ATTR_FORMAT(2, 3);