Mercurial > dovecot > core-2.2
changeset 10200:01676e67cf38 HEAD
master: anvil process now stays alive across SIGHUPs.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 26 Oct 2009 23:45:18 -0400 |
parents | 9d13e9f78d52 |
children | b0017c5dd411 |
files | src/master/main.c src/master/service-anvil.c src/master/service-anvil.h src/master/service-listen.c src/master/service-monitor.c src/master/service-process.c src/master/service.c src/master/service.h |
diffstat | 8 files changed, 216 insertions(+), 132 deletions(-) [+] |
line wrap: on
line diff
--- a/src/master/main.c Mon Oct 26 23:41:54 2009 -0400 +++ b/src/master/main.c Mon Oct 26 23:45:18 2009 -0400 @@ -14,6 +14,7 @@ #include "askpass.h" #include "capabilities.h" #include "service.h" +#include "service-anvil.h" #include "service-listen.h" #include "service-monitor.h" #include "service-process.h" @@ -295,6 +296,7 @@ const struct master_settings *set; void **sets; struct service_list *new_services; + struct service *service; const char *error; i_warning("SIGHUP received - reloading configuration"); @@ -334,7 +336,17 @@ /* switch to new configuration. */ services_monitor_stop(services); - (void)services_listen_using(new_services, services); + if (services_listen_using(new_services, services) < 0) { + services_monitor_start(services); + return; + } + + /* anvil never dies. it just gets moved to the new services list */ + service = service_lookup_type(services, SERVICE_TYPE_ANVIL); + if (service != NULL) { + while (service->processes != NULL) + service_process_destroy(service->processes); + } services_destroy(services); services = new_services; @@ -416,6 +428,7 @@ i_free(pidfile_path); services_destroy(services); + service_anvil_global_deinit(); service_pids_deinit(); } @@ -740,6 +753,7 @@ /* create service structures from settings. if there are any errors in service configuration we'll catch it here. */ service_pids_init(); + service_anvil_global_init(); if (services_create(set, child_process_env, &services, &error) < 0) i_fatal("%s", error);
--- a/src/master/service-anvil.c Mon Oct 26 23:41:54 2009 -0400 +++ b/src/master/service-anvil.c Mon Oct 26 23:45:18 2009 -0400 @@ -13,17 +13,18 @@ #define ANVIL_HANDSHAKE "VERSION\tanvil\t1\t0\n" +struct service_anvil_global *service_anvil_global; + static void -service_list_anvil_discard_input_stop(struct service_list *service_list) +service_list_anvil_discard_input_stop(struct service_anvil_global *anvil) { - if (service_list->anvil_io_blocking != NULL) { - io_remove(&service_list->anvil_io_blocking); - io_remove(&service_list->anvil_io_nonblocking); + if (anvil->io_blocking != NULL) { + io_remove(&anvil->io_blocking); + io_remove(&anvil->io_nonblocking); } } -static void -anvil_input_fd_discard(struct service_list *service_list, int fd) +static void anvil_input_fd_discard(struct service_anvil_global *anvil, int fd) { char buf[1024]; ssize_t ret; @@ -31,30 +32,29 @@ ret = read(fd, buf, sizeof(buf)); if (ret <= 0) { i_error("read(anvil fd) failed: %m"); - service_list_anvil_discard_input_stop(service_list); + service_list_anvil_discard_input_stop(anvil); } } -static void anvil_input_blocking_discard(struct service_list *service_list) +static void anvil_input_blocking_discard(struct service_anvil_global *anvil) { - anvil_input_fd_discard(service_list, - service_list->blocking_anvil_fd[0]); + anvil_input_fd_discard(anvil, anvil->blocking_fd[0]); +} + +static void anvil_input_nonblocking_discard(struct service_anvil_global *anvil) +{ + anvil_input_fd_discard(anvil, anvil->nonblocking_fd[0]); } -static void anvil_input_nonblocking_discard(struct service_list *service_list) +static void service_list_anvil_discard_input(struct service_anvil_global *anvil) { - anvil_input_fd_discard(service_list, - service_list->nonblocking_anvil_fd[0]); -} + if (anvil->io_blocking != NULL) + return; -static void service_list_anvil_discard_input(struct service_list *service_list) -{ - service_list->anvil_io_blocking = - io_add(service_list->blocking_anvil_fd[0], IO_READ, - anvil_input_blocking_discard, service_list); - service_list->anvil_io_nonblocking = - io_add(service_list->nonblocking_anvil_fd[0], IO_READ, - anvil_input_nonblocking_discard, service_list); + anvil->io_blocking = io_add(anvil->blocking_fd[0], IO_READ, + anvil_input_blocking_discard, anvil); + anvil->io_nonblocking = io_add(anvil->nonblocking_fd[0], IO_READ, + anvil_input_nonblocking_discard, anvil); } static int anvil_send_handshake(int fd, const char **error_r) @@ -89,71 +89,90 @@ return 0; } -int service_list_init_anvil(struct service_list *service_list, - const char **error_r) +void service_anvil_monitor_start(struct service_list *service_list) { - if (pipe(service_list->blocking_anvil_fd) < 0) { - *error_r = t_strdup_printf("pipe() failed: %m"); - return -1; - } - if (pipe(service_list->nonblocking_anvil_fd) < 0) { - (void)close(service_list->blocking_anvil_fd[0]); - (void)close(service_list->blocking_anvil_fd[1]); - *error_r = t_strdup_printf("pipe() failed: %m"); - return -1; + struct service *service; + + if (service_anvil_global->process_count == 0) + service_list_anvil_discard_input(service_anvil_global); + else { + service = service_lookup_type(service_list, SERVICE_TYPE_ANVIL); + service_process_create(service); } - fd_set_nonblock(service_list->nonblocking_anvil_fd[1], TRUE); - - fd_close_on_exec(service_list->blocking_anvil_fd[0], TRUE); - fd_close_on_exec(service_list->blocking_anvil_fd[1], TRUE); - fd_close_on_exec(service_list->nonblocking_anvil_fd[0], TRUE); - fd_close_on_exec(service_list->nonblocking_anvil_fd[1], TRUE); - - i_assert(service_list->anvil_kills == NULL); - service_list->anvil_kills = - service_process_notify_init(service_list->nonblocking_anvil_fd[1], - service_process_write_anvil_kill); - return 0; } -void services_anvil_init(struct service_list *service_list) +void service_anvil_process_created(struct service_process *process) { - /* this can't be in _init_anvil() because we can't do io_add()s - before forking with kqueue. */ - service_list_anvil_discard_input(service_list); + struct service_anvil_global *anvil = service_anvil_global; + const char *error; + + service_anvil_global->pid = process->pid; + service_anvil_global->uid = process->uid; + service_anvil_global->process_count++; + service_list_anvil_discard_input_stop(anvil); + + if (anvil_send_handshake(anvil->blocking_fd[1], &error) < 0 || + anvil_send_handshake(anvil->nonblocking_fd[1], &error) < 0) + service_error(process->service, "%s", error); +} + +void service_anvil_process_destroyed(struct service_process *process) +{ + i_assert(service_anvil_global->process_count > 0); + if (--service_anvil_global->process_count == 0) + service_list_anvil_discard_input(service_anvil_global); + + if (service_anvil_global->pid == process->pid) + service_anvil_global->pid = 0; } -void service_list_deinit_anvil(struct service_list *service_list) +void service_anvil_global_init(void) { - service_list_anvil_discard_input_stop(service_list); - service_process_notify_deinit(&service_list->anvil_kills); - if (close(service_list->blocking_anvil_fd[0]) < 0) - i_error("close(anvil) failed: %m"); - if (close(service_list->blocking_anvil_fd[1]) < 0) - i_error("close(anvil) failed: %m"); - if (close(service_list->nonblocking_anvil_fd[0]) < 0) - i_error("close(anvil) failed: %m"); - if (close(service_list->nonblocking_anvil_fd[1]) < 0) - i_error("close(anvil) failed: %m"); - service_list->blocking_anvil_fd[0] = -1; + struct service_anvil_global *anvil; + + anvil = i_new(struct service_anvil_global, 1); + if (pipe(anvil->status_fd) < 0) + i_fatal("pipe() failed: %m"); + if (pipe(anvil->blocking_fd) < 0) + i_fatal("pipe() failed: %m"); + if (pipe(anvil->nonblocking_fd) < 0) + i_fatal("pipe() failed: %m"); + fd_set_nonblock(anvil->status_fd[0], TRUE); + fd_set_nonblock(anvil->status_fd[1], TRUE); + fd_set_nonblock(anvil->nonblocking_fd[1], TRUE); + + fd_close_on_exec(anvil->status_fd[0], TRUE); + fd_close_on_exec(anvil->status_fd[1], TRUE); + fd_close_on_exec(anvil->blocking_fd[0], TRUE); + fd_close_on_exec(anvil->blocking_fd[1], TRUE); + fd_close_on_exec(anvil->nonblocking_fd[0], TRUE); + fd_close_on_exec(anvil->nonblocking_fd[1], TRUE); + + anvil->kills = + service_process_notify_init(anvil->nonblocking_fd[1], + service_process_write_anvil_kill); + service_anvil_global = anvil; } -void service_anvil_process_created(struct service *service) +void service_anvil_global_deinit(void) { - struct service_list *list = service->list; - const char *error; - - service_list_anvil_discard_input_stop(service->list); + struct service_anvil_global *anvil = service_anvil_global; - if (anvil_send_handshake(list->blocking_anvil_fd[1], &error) < 0 || - anvil_send_handshake(list->nonblocking_anvil_fd[1], &error) < 0) - service_error(service, "%s", error); -} + service_list_anvil_discard_input_stop(anvil); + service_process_notify_deinit(&anvil->kills); + if (close(anvil->blocking_fd[0]) < 0) + i_error("close(anvil) failed: %m"); + if (close(anvil->blocking_fd[1]) < 0) + i_error("close(anvil) failed: %m"); + if (close(anvil->nonblocking_fd[0]) < 0) + i_error("close(anvil) failed: %m"); + if (close(anvil->nonblocking_fd[1]) < 0) + i_error("close(anvil) failed: %m"); + if (close(anvil->status_fd[0]) < 0) + i_error("close(anvil) failed: %m"); + if (close(anvil->status_fd[1]) < 0) + i_error("close(anvil) failed: %m"); + i_free(anvil); -void service_anvil_process_destroyed(struct service *service) -{ - if (service->process_count == 0 && - service->list->anvil_io_blocking == NULL && - service->list->blocking_anvil_fd[0] != -1) - service_list_anvil_discard_input(service->list); + service_anvil_global = NULL; }
--- a/src/master/service-anvil.h Mon Oct 26 23:41:54 2009 -0400 +++ b/src/master/service-anvil.h Mon Oct 26 23:45:18 2009 -0400 @@ -1,12 +1,30 @@ #ifndef SERVICE_ANVIL_H #define SERVICE_ANVIL_H -int service_list_init_anvil(struct service_list *service_list, - const char **error_r); -void service_list_deinit_anvil(struct service_list *service_list); -void services_anvil_init(struct service_list *service_list); +struct service_anvil_global { + pid_t pid; + unsigned int uid; + + int status_fd[2]; + /* passed to child processes */ + int blocking_fd[2]; + /* used by master process to notify about dying processes */ + int nonblocking_fd[2]; + + struct service_process_notify *kills; + struct io *io_blocking, *io_nonblocking; -void service_anvil_process_created(struct service *service); -void service_anvil_process_destroyed(struct service *service); + unsigned int process_count; +}; + +extern struct service_anvil_global *service_anvil_global; + +void service_anvil_monitor_start(struct service_list *service_list); + +void service_anvil_process_created(struct service_process *process); +void service_anvil_process_destroyed(struct service_process *process); + +void service_anvil_global_init(void); +void service_anvil_global_deinit(void); #endif
--- a/src/master/service-listen.c Mon Oct 26 23:41:54 2009 -0400 +++ b/src/master/service-listen.c Mon Oct 26 23:45:18 2009 -0400 @@ -211,12 +211,32 @@ int services_listen_using(struct service_list *new_service_list, struct service_list *old_service_list) { - struct service *const *services; + struct service *const *services, *old_service, *new_service; ARRAY_DEFINE(new_listeners_arr, struct service_listener *); ARRAY_DEFINE(old_listeners_arr, struct service_listener *); struct service_listener *const *new_listeners, *const *old_listeners; unsigned int i, j, count, new_count, old_count; + /* rescue anvil's UNIX socket listener */ + new_service = service_lookup_type(new_service_list, SERVICE_TYPE_ANVIL); + old_service = service_lookup_type(old_service_list, SERVICE_TYPE_ANVIL); + if (old_service != NULL && new_service != NULL) { + new_listeners = array_get(&new_service->listeners, &new_count); + old_listeners = array_get(&old_service->listeners, &old_count); + for (i = 0; i < old_count && i < new_count; i++) { + if (new_listeners[i]->type != old_listeners[i]->type) + break; + } + if (i != new_count && i != old_count) { + i_error("Can't change anvil's listeners on the fly"); + return -1; + } + for (i = 0; i < new_count; i++) { + new_listeners[i]->fd = old_listeners[i]->fd; + old_listeners[i]->fd = -1; + } + } + /* first create an arrays of all listeners to make things easier */ t_array_init(&new_listeners_arr, 64); services = array_get(&new_service_list->services, &count); @@ -246,10 +266,11 @@ /* close what's left */ for (j = 0; j < old_count; j++) { - if (old_listeners[j]->fd != -1) { - if (close(old_listeners[j]->fd) < 0) - i_error("close(listener) failed: %m"); - } + if (old_listeners[j]->fd == -1) + continue; + + 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: {
--- a/src/master/service-monitor.c Mon Oct 26 23:41:54 2009 -0400 +++ b/src/master/service-monitor.c Mon Oct 26 23:45:18 2009 -0400 @@ -309,8 +309,8 @@ struct service *const *services; unsigned int i, count; - services_anvil_init(service_list); services_log_init(service_list); + service_anvil_monitor_start(service_list); services = array_get(&service_list->services, &count); for (i = 0; i < count; i++) { @@ -329,7 +329,8 @@ fd_close_on_exec(services[i]->status_fd[0], TRUE); net_set_nonblock(services[i]->status_fd[1], TRUE); fd_close_on_exec(services[i]->status_fd[1], TRUE); - + } + if (services[i]->io_status == NULL) { services[i]->io_status = io_add(services[i]->status_fd[0], IO_READ, service_status_input, services[i]); @@ -351,7 +352,8 @@ if (service->io_status != NULL) io_remove(&service->io_status); - if (service->status_fd[0] != -1) { + if (service->status_fd[0] != -1 && + service->type != SERVICE_TYPE_ANVIL) { for (i = 0; i < 2; i++) { if (close(service->status_fd[i]) < 0) { service_error(service, @@ -395,8 +397,7 @@ if (process->total_count == 0) service_monitor_throttle(service); - if (service->list->anvil_kills != NULL) - service_process_notify_add(service->list->anvil_kills, process); + service_process_notify_add(service_anvil_global->kills, process); } void services_monitor_reap_children(void) @@ -424,6 +425,8 @@ service_process_failure(process, status); } service_destroyed = service->list->destroyed; + if (service->type == SERVICE_TYPE_ANVIL) + service_anvil_process_destroyed(process); service_process_destroy(process); if (!service_destroyed) {
--- a/src/master/service-process.c Mon Oct 26 23:41:54 2009 -0400 +++ b/src/master/service-process.c Mon Oct 26 23:45:18 2009 -0400 @@ -63,9 +63,9 @@ case SERVICE_TYPE_ANVIL: /* nonblocking anvil fd must be the first one. anvil treats it as the master's fd */ - dup2_append(&dups, service->list->nonblocking_anvil_fd[0], + dup2_append(&dups, service_anvil_global->nonblocking_fd[0], MASTER_LISTEN_FD_FIRST + n++); - dup2_append(&dups, service->list->blocking_anvil_fd[0], + dup2_append(&dups, service_anvil_global->blocking_fd[0], MASTER_LISTEN_FD_FIRST + n++); socket_listener_count += 2; break; @@ -100,7 +100,7 @@ dup2_append(&dups, service->login_notify_fd, MASTER_LOGIN_NOTIFY_FD); } - dup2_append(&dups, service->list->blocking_anvil_fd[1], + dup2_append(&dups, service_anvil_global->blocking_fd[1], MASTER_ANVIL_FD); dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD); @@ -231,13 +231,23 @@ struct service_process *process; unsigned int uid = ++uid_counter; pid_t pid; + bool process_forked; if (service->to_throttle != NULL) { /* throttling service, don't create new processes */ return NULL; } - pid = fork(); + if (service->type == SERVICE_TYPE_ANVIL && + service_anvil_global->pid != 0) { + pid = service_anvil_global->pid; + uid = service_anvil_global->uid; + process_forked = FALSE; + } else { + pid = fork(); + process_forked = TRUE; + } + if (pid < 0) { service_error(service, "fork() failed: %m"); return NULL; @@ -250,30 +260,27 @@ process_exec(service->executable, NULL); } - switch (service->type) { - case SERVICE_TYPE_ANVIL: - service_anvil_process_created(service); - /* fall through */ - default: - process = i_new(struct service_process, 1); - process->service = service; - break; - } - - DLLIST_PREPEND(&service->processes, process); + process = i_new(struct service_process, 1); + process->service = service; process->refcount = 1; process->pid = pid; process->uid = uid; - process->to_status = - timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000, - service_process_status_timeout, process); + if (process_forked) { + process->to_status = + timeout_add(SERVICE_FIRST_STATUS_TIMEOUT_SECS * 1000, + service_process_status_timeout, process); + } process->available_count = service->client_limit; service->process_count++; service->process_avail++; + DLLIST_PREPEND(&service->processes, process); service_list_ref(service->list); hash_table_insert(service_pids, &process->pid, process); + + if (service->type == SERVICE_TYPE_ANVIL && process_forked) + service_anvil_process_created(process); return process; } @@ -294,15 +301,6 @@ timeout_remove(&process->to_status); if (process->to_idle != NULL) timeout_remove(&process->to_idle); - - switch (process->service->type) { - case SERVICE_TYPE_ANVIL: - service_anvil_process_destroyed(service); - break; - default: - break; - } - if (service->list->log_byes != NULL) service_process_notify_add(service->list->log_byes, process);
--- a/src/master/service.c Mon Oct 26 23:41:54 2009 -0400 +++ b/src/master/service.c Mon Oct 26 23:45:18 2009 -0400 @@ -245,6 +245,11 @@ service->log_process_internal_fd = -1; service->login_notify_fd = -1; + if (service->type == SERVICE_TYPE_ANVIL) { + service->status_fd[0] = service_anvil_global->status_fd[0]; + service->status_fd[1] = service_anvil_global->status_fd[1]; + } + if (array_is_created(&set->unix_listeners)) unix_listeners = array_get(&set->unix_listeners, &unix_count); else { @@ -325,6 +330,20 @@ return NULL; } +struct service * +service_lookup_type(struct service_list *service_list, enum service_type type) +{ + struct service *const *services; + unsigned int i, count; + + services = array_get(&service_list->services, &count); + for (i = 0; i < count; i++) { + if (services[i]->type == type) + return services[i]; + } + return NULL; +} + static bool service_want(struct service_settings *set) { char *const *proto; @@ -407,9 +426,6 @@ return -1; } - if (service_list_init_anvil(service_list, error_r) < 0) - return -1; - *services_r = service_list; return 0; } @@ -518,7 +534,6 @@ services_monitor_reap_children(); services_monitor_stop(service_list); - service_list_deinit_anvil(service_list); if (service_list->refcount > 1 && service_list->service_set->shutdown_clients) {
--- a/src/master/service.h Mon Oct 26 23:41:54 2009 -0400 +++ b/src/master/service.h Mon Oct 26 23:45:18 2009 -0400 @@ -116,13 +116,6 @@ int master_log_fd[2]; struct service_process_notify *log_byes; - /* passed to child processes */ - int blocking_anvil_fd[2]; - /* used by master process to notify about dying processes */ - int nonblocking_anvil_fd[2]; - struct service_process_notify *anvil_kills; - struct io *anvil_io_blocking, *anvil_io_nonblocking; - ARRAY_DEFINE(services, struct service *); unsigned int destroyed:1; @@ -159,9 +152,12 @@ void services_throttle_time_sensitives(struct service_list *list, unsigned int secs); -/* Find a service by name. */ +/* Find service by name. */ struct service * service_lookup(struct service_list *service_list, const char *name); +/* Find service by type */ +struct service * +service_lookup_type(struct service_list *service_list, enum service_type type); void service_error(struct service *service, const char *format, ...) ATTR_FORMAT(2, 3);