]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
master: anvil process now stays alive across SIGHUPs.
authorTimo Sirainen <tss@iki.fi>
Tue, 27 Oct 2009 03:45:18 +0000 (23:45 -0400)
committerTimo Sirainen <tss@iki.fi>
Tue, 27 Oct 2009 03:45:18 +0000 (23:45 -0400)
--HG--
branch : HEAD

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

index 229b00790626a8de9afb99ca4acba118c5e67714..00f2c58db0e13e32cf2a056211e10cd70d7caba9 100644 (file)
@@ -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 @@ sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
        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 @@ sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
 
        /* 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 @@ static void main_deinit(void)
        i_free(pidfile_path);
 
        services_destroy(services);
+       service_anvil_global_deinit();
        service_pids_deinit();
 }
 
@@ -740,6 +753,7 @@ int main(int argc, char *argv[])
        /* 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);
 
index b8bc648f7c40626ed42a78e4413070f7c489f35d..32e4ef278836b0ca3d44b393042f3b846a7efbb7 100644 (file)
 
 #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 @@ anvil_input_fd_discard(struct service_list *service_list, int fd)
        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_list *service_list)
+static void anvil_input_nonblocking_discard(struct service_anvil_global *anvil)
 {
-       anvil_input_fd_discard(service_list,
-                              service_list->nonblocking_anvil_fd[0]);
+       anvil_input_fd_discard(anvil, anvil->nonblocking_fd[0]);
 }
 
-static void service_list_anvil_discard_input(struct service_list *service_list)
+static void service_list_anvil_discard_input(struct service_anvil_global *anvil)
 {
-       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);
+       if (anvil->io_blocking != NULL)
+               return;
+
+       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 @@ service_process_write_anvil_kill(int fd, struct service_process *process)
        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);
+void service_anvil_process_created(struct service_process *process)
+{
+       struct service_anvil_global *anvil = service_anvil_global;
+       const char *error;
 
-       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;
+       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 services_anvil_init(struct service_list *service_list)
+void service_anvil_process_destroyed(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);
+       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;
 }
index 7c467abe4c61990bd95182cfd71fb71b246286ec..19d1b6cf831b912e5f1a9af63ac782ae0c691bca 100644 (file)
@@ -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;
 
-void service_anvil_process_created(struct service *service);
-void service_anvil_process_destroyed(struct service *service);
+       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;
+
+       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
index ec53da1dbb70f3e9eae2916ecd40f31039882ecd..69f917475acfeaa232e30e249ab3e1545d24c64f 100644 (file)
@@ -211,12 +211,32 @@ static int listener_equals(const struct service_listener *l1,
 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 @@ int services_listen_using(struct service_list *new_service_list,
 
        /* 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: {
index f61088a08424b0c7adad48bda5bab3ff5e4b58e8..451f0345e69e971830757b3ab6aa8af78fcaeab4 100644 (file)
@@ -309,8 +309,8 @@ void services_monitor_start(struct service_list *service_list)
        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 @@ void services_monitor_start(struct service_list *service_list)
                        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 @@ void service_monitor_stop(struct service *service)
        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 @@ static void service_process_failure(struct service_process *process, int status)
        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 @@ void services_monitor_reap_children(void)
                        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) {
index 5630bf1adba4ece92034fda478ff7a5be15eabb5..7a4d84e2a7087cc9f99a0364e43a1c0d628b4005 100644 (file)
@@ -63,9 +63,9 @@ service_dup_fds(struct service *service)
        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 @@ service_dup_fds(struct service *service)
                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 *service_process_create(struct service *service)
        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 @@ struct service_process *service_process_create(struct service *service)
                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 @@ void service_process_destroy(struct service_process *process)
                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);
 
index fb82edd030b4283f4c051e7f2e72ceea298b5437..ce24f4626c4f269043bca1be42ad81a09218a4c1 100644 (file)
@@ -245,6 +245,11 @@ service_create(pool_t pool, const struct service_settings *set,
        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 @@ service_lookup(struct service_list *service_list, const char *name)
        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 @@ int services_create(const struct master_settings *set,
                return -1;
        }
 
-       if (service_list_init_anvil(service_list, error_r) < 0)
-               return -1;
-
        *services_r = service_list;
        return 0;
 }
@@ -518,7 +534,6 @@ void services_destroy(struct service_list *service_list)
         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) {
index 8a9d6df285735d55459fcdf438ba240faf891cc5..5fc9d71c0cf89c935b0ce829604a48370b7cd5fe 100644 (file)
@@ -116,13 +116,6 @@ struct service_list {
        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 service_throttle(struct service *service, unsigned int secs);
 void services_throttle_time_sensitives(struct service_list *list,
                                       unsigned int secs);
 
-/* Find 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);