Mercurial > dovecot > original-hg > dovecot-1.2
changeset 5887:3f2eb1b9c555 HEAD
Support listening multiple sockets. SIGHUP also doesn't anymore recreate
listener sockets.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 03 Jul 2007 20:04:28 +0300 |
parents | 86f22b84e008 |
children | a4cd1f37d022 |
files | dovecot-example.conf src/login-common/main.c src/master/Makefile.am src/master/dup2-array.c src/master/dup2-array.h src/master/listener.c src/master/listener.h src/master/login-process.c src/master/main.c src/master/master-login-interface.h src/master/master-settings.c src/master/master-settings.h |
diffstat | 12 files changed, 594 insertions(+), 326 deletions(-) [+] |
line wrap: on
line diff
--- a/dovecot-example.conf Tue Jul 03 18:34:31 2007 +0300 +++ b/dovecot-example.conf Tue Jul 03 20:04:28 2007 +0300 @@ -22,10 +22,10 @@ # If you only want to use dovecot-auth, you can set this to "none". #protocols = imap imaps -# IP or host address where to listen in for connections. It's not currently -# possible to specify multiple addresses. "*" listens in all IPv4 interfaces. -# "[::]" listens in all IPv6 interfaces, but may also listen in all IPv4 -# interfaces depending on the operating system. +# A space separated list of IP or host addresses where to listen in for +# connections. "*" listens in all IPv4 interfaces. "[::]" listens in all IPv6 +# interfaces, but may also listen in all IPv4 interfaces depending on the +# operating system. # # If you want to specify ports for each service, you will need to configure # these settings inside the protocol imap/pop3 { ... } section, so you can
--- a/src/login-common/main.c Tue Jul 03 18:34:31 2007 +0300 +++ b/src/login-common/main.c Tue Jul 03 20:04:28 2007 +0300 @@ -2,6 +2,7 @@ #include "common.h" #include "ioloop.h" +#include "array.h" #include "lib-signals.h" #include "randgen.h" #include "restrict-access.h" @@ -29,10 +30,12 @@ static const char *process_name; static struct ioloop *ioloop; -static struct io *io_listen, *io_ssl_listen; static int main_refcount; static bool is_inetd, listening; +static ARRAY_DEFINE(listen_ios, struct io *); +static unsigned int listen_count, ssl_listen_count; + void main_ref(void) { main_refcount++; @@ -71,14 +74,15 @@ io_loop_stop(ioloop); } -static void login_accept(void *context __attr_unused__) +static void login_accept(void *context) { + int listen_fd = POINTER_CAST_TO(context, int); struct ip_addr remote_ip, local_ip; unsigned int remote_port, local_port; struct client *client; int fd; - fd = net_accept(LOGIN_LISTEN_FD, &remote_ip, &remote_port); + fd = net_accept(listen_fd, &remote_ip, &remote_port); if (fd < 0) { if (fd < -1) i_error("accept() failed: %m"); @@ -100,15 +104,16 @@ } } -static void login_accept_ssl(void *context __attr_unused__) +static void login_accept_ssl(void *context) { + int listen_fd = POINTER_CAST_TO(context, int); struct ip_addr remote_ip, local_ip; unsigned int remote_port, local_port; struct client *client; struct ssl_proxy *proxy; int fd, fd_ssl; - fd = net_accept(LOGIN_SSL_LISTEN_FD, &remote_ip, &remote_port); + fd = net_accept(listen_fd, &remote_ip, &remote_port); if (fd < 0) { if (fd < -1) i_error("accept() failed: %m"); @@ -138,7 +143,9 @@ void main_listen_start(void) { - unsigned int current_count; + struct io *io; + unsigned int i, current_count; + int cur_fd; if (listening) return; @@ -156,21 +163,17 @@ return; } - if (net_getsockname(LOGIN_LISTEN_FD, NULL, NULL) == 0) { - io_listen = io_add(LOGIN_LISTEN_FD, IO_READ, - login_accept, NULL); + cur_fd = LOGIN_MASTER_SOCKET_FD + 1; + i_array_init(&listen_ios, listen_count + ssl_listen_count); + for (i = 0; i < listen_count; i++, cur_fd++) { + io = io_add(cur_fd, IO_READ, login_accept, + POINTER_CAST(cur_fd)); + array_append(&listen_ios, &io, 1); } - - if (net_getsockname(LOGIN_SSL_LISTEN_FD, NULL, NULL) == 0) { - if (!ssl_initialized) { - /* this shouldn't happen, master should have - disabled the ssl socket.. */ - i_fatal("BUG: SSL initialization parameters not given " - "while they should have been"); - } - - io_ssl_listen = io_add(LOGIN_SSL_LISTEN_FD, IO_READ, - login_accept_ssl, NULL); + for (i = 0; i < ssl_listen_count; i++, cur_fd++) { + io = io_add(cur_fd, IO_READ, login_accept_ssl, + POINTER_CAST(cur_fd)); + array_append(&listen_ios, &io, 1); } listening = TRUE; @@ -181,30 +184,34 @@ void main_listen_stop(void) { + struct io **ios; + unsigned int i, count; + int cur_fd; + if (!listening) return; - listening = FALSE; - if (io_listen != NULL) { - io_remove(&io_listen); - if (closing_down) { - if (close(LOGIN_LISTEN_FD) < 0) - i_fatal("close(listen) failed: %m"); - } - } + ios = array_get_modifiable(&listen_ios, &count); + for (i = 0; i < count; i++) + io_remove(&ios[i]); + array_free(&listen_ios); - if (io_ssl_listen != NULL) { - io_remove(&io_ssl_listen); - if (closing_down) { - if (close(LOGIN_SSL_LISTEN_FD) < 0) - i_fatal("close(ssl_listen) failed: %m"); + if (closing_down) { + cur_fd = LOGIN_MASTER_SOCKET_FD + 1; + for (i = 0; i < count; i++, cur_fd++) { + if (close(cur_fd) < 0) { + i_fatal("close(listener %d) failed: %m", + cur_fd); + } } } listening = FALSE; - master_notify_state_change(clients_get_count() == 0 ? - LOGIN_STATE_FULL_LOGINS : - LOGIN_STATE_FULL_PRELOGINS); + if (io_loop_is_running(ioloop)) { + master_notify_state_change(clients_get_count() == 0 ? + LOGIN_STATE_FULL_LOGINS : + LOGIN_STATE_FULL_PRELOGINS); + } } void connection_queue_add(unsigned int connection_count) @@ -332,7 +339,18 @@ auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL); clients_init(); - io_listen = io_ssl_listen = NULL; + value = getenv("LISTEN_FDS"); + listen_count = value == NULL ? 0 : atoi(value); + + value = getenv("SSL_LISTEN_FDS"); + ssl_listen_count = value == NULL ? 0 : atoi(value); + + if (!ssl_initialized && ssl_listen_count > 0) { + /* this shouldn't happen, master should have + disabled the ssl socket.. */ + i_fatal("BUG: SSL initialization parameters not given " + "while they should have been"); + } if (!is_inetd) { master_init(LOGIN_MASTER_SOCKET_FD); @@ -342,8 +360,8 @@ static void main_deinit(void) { - if (io_listen != NULL) io_remove(&io_listen); - if (io_ssl_listen != NULL) io_remove(&io_ssl_listen); + closing_down = TRUE; + main_listen_stop(); ssl_proxy_deinit(); login_proxy_deinit(); @@ -370,8 +388,17 @@ is_inetd = getenv("DOVECOT_MASTER") == NULL; #ifdef DEBUG - if (!is_inetd && getenv("GDB") == NULL) - fd_debug_verify_leaks(5, 1024); + if (!is_inetd && getenv("GDB") == NULL) { + const char *env; + + i = LOGIN_MASTER_SOCKET_FD + 1; + env = getenv("LISTEN_FDS"); + if (env != NULL) i += atoi(env); + env = getenv("SSL_LISTEN_FDS"); + if (env != NULL) i += atoi(env); + + fd_debug_verify_leaks(i + 1, 1024); + } #endif /* clear all allocated memory before freeing it. this makes the login processes pretty safe to reuse for new connections since the
--- a/src/master/Makefile.am Tue Jul 03 18:34:31 2007 +0300 +++ b/src/master/Makefile.am Tue Jul 03 20:04:28 2007 +0300 @@ -24,6 +24,8 @@ capabilities-posix.c \ child-process.c \ dict-process.c \ + dup2-array.c \ + listener.c \ log.c \ login-process.c \ mail-process.c \ @@ -38,6 +40,8 @@ capabilities.h \ child-process.h \ dict-process.h \ + dup2-array.h \ + listener.h \ common.h \ log.h \ login-process.h \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/master/dup2-array.c Tue Jul 03 20:04:28 2007 +0300 @@ -0,0 +1,80 @@ +/* Copyright (C) 2007 Timo Sirainen */ + +#include "lib.h" +#include "array.h" +#include "fd-close-on-exec.h" +#include "dup2-array.h" + +#include <unistd.h> + +void dup2_append(ARRAY_TYPE(dup2) *dups, int fd_src, int fd_dest) +{ + struct dup2 d; + + d.fd_src = fd_src; + d.fd_dest = fd_dest; + array_append(dups, &d, 1); +} + +int dup2_array(ARRAY_TYPE(dup2) *dups_arr) +{ + struct dup2 *dups; + bool *moved, moves; + unsigned int i, j, count, conflict; + int fd; + + dups = array_get_modifiable(dups_arr, &count); + + t_push(); + moved = t_new(bool, count); + for (;;) { + conflict = count; + moves = FALSE; + for (i = 0; i < count; i++) { + if (moved[i]) + continue; + + for (j = 0; j < count; j++) { + if (dups[j].fd_src == dups[i].fd_dest && + !moved[j]) { + conflict = j; + break; + } + } + + if (j == count) { + /* no conflicts, move it */ + moved[i] = TRUE; + moves = TRUE; + if (dup2(dups[i].fd_src, dups[i].fd_dest) < 0) { + i_error("dup2(%d, %d) failed: %m", + dups[i].fd_src, + dups[i].fd_dest); + t_pop(); + return -1; + } + } + } + if (conflict == count) + break; + + if (moves) { + /* it's possible that the conflicting fd was + moved already. try again. */ + continue; + } + + /* ok, we have to dup() */ + fd = dup(dups[conflict].fd_src); + if (fd == -1) { + i_error("dup(%d) failed: %m", dups[conflict].fd_src); + t_pop(); + return -1; + } + fd_close_on_exec(fd, TRUE); + dups[conflict].fd_src = fd; + } + t_pop(); + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/master/dup2-array.h Tue Jul 03 20:04:28 2007 +0300 @@ -0,0 +1,13 @@ +#ifndef __DUP2_ARRAY_H +#define __DUP2_ARRAY_H + +struct dup2 { + int fd_src, fd_dest; +}; +ARRAY_DEFINE_TYPE(dup2, struct dup2); + +void dup2_append(ARRAY_TYPE(dup2) *dups, int fd_src, int fd_dest); + +int dup2_array(ARRAY_TYPE(dup2) *dups); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/master/listener.c Tue Jul 03 20:04:28 2007 +0300 @@ -0,0 +1,366 @@ +/* Copyright (C) 2002-2007 Timo Sirainen */ + +#include "common.h" +#include "array.h" +#include "ioloop.h" +#include "fd-close-on-exec.h" +#include "listener.h" + +#include <stdlib.h> +#include <unistd.h> + +static void resolve_ip(const char *set_name, const char *name, + struct ip_addr *ip, unsigned int *port) +{ + struct ip_addr *ip_list; + const char *p; + unsigned int ips_count; + int ret; + + if (*name == '\0') { + /* defaults to "*" or "[::]" */ + ip->family = 0; + return; + } + + if (name[0] == '[') { + /* IPv6 address */ + p = strchr(name, ']'); + if (p == NULL) { + i_fatal("%s: Missing ']' in address %s", + set_name, name); + } + name = t_strdup_until(name+1, p); + + p++; + if (*p == '\0') + p = NULL; + else if (*p != ':') { + i_fatal("%s: Invalid data after ']' in address %s", + set_name, name); + } + } else { + p = strrchr(name, ':'); + if (p != NULL) + name = t_strdup_until(name, p); + } + + if (p != NULL) { + if (!is_numeric(p+1, '\0')) { + i_fatal("%s: Invalid port in address %s", + set_name, name); + } + *port = atoi(p+1); + } + + if (strcmp(name, "*") == 0) { + /* IPv4 any */ + net_get_ip_any4(ip); + return; + } + + if (strcmp(name, "::") == 0) { + /* IPv6 any */ + net_get_ip_any6(ip); + return; + } + + /* Return the first IP if there happens to be multiple. */ + ret = net_gethostbyname(name, &ip_list, &ips_count); + if (ret != 0) { + i_fatal("%s: Can't resolve address %s: %s", + set_name, name, net_gethosterror(ret)); + } + + if (ips_count < 1) + i_fatal("%s: No IPs for address: %s", set_name, name); + + *ip = ip_list[0]; +} + +static void +check_conflicts_set(const struct settings *set, const struct ip_addr *ip, + unsigned int port, const char *name1, const char *name2) +{ + const struct listener *listens = NULL; + unsigned int i, count; + + if (array_is_created(&set->listens)) + listens = array_get(&set->listens, &count); + else + count = 0; + for (i = 0; i < count; i++) { + if (listens[i].fd <= 0 || listens[i].port != port || + !net_ip_compare(&listens[i].ip, ip)) + continue; + + i_fatal("Protocols %s and %s are listening in same ip/port", + name1, name2); + } + + if (array_is_created(&set->ssl_listens)) + listens = array_get(&set->ssl_listens, &count); + else + count = 0; + for (i = 0; i < count; i++) { + if (listens[i].fd <= 0 || listens[i].port != port || + !net_ip_compare(&listens[i].ip, ip)) + continue; + + i_fatal("Protocols %ss and %ss are listening in same ip/port", + name1, name2); + } +} + +static void check_conflicts(const struct ip_addr *ip, unsigned int port, + const char *proto) +{ + struct server_settings *server; + + for (server = settings_root; server != NULL; server = server->next) { + if (server->imap != NULL) { + check_conflicts_set(server->imap, ip, port, + "imap", proto); + } + if (server->pop3 != NULL) { + check_conflicts_set(server->pop3, ip, port, + "pop3", proto); + } + } +} + +static void +listener_init(const char *set_name, const char *listen, + unsigned int default_port, ARRAY_TYPE(listener) *listens_arr) +{ + const char *const *tmp; + struct listener l, *listens; + unsigned int i, count; + + if (!array_is_created(listens_arr)) + i_array_init(listens_arr, 4); + + listens = array_get_modifiable(listens_arr, &count); + for (i = 0; i < count; i++) + listens[i].wanted = FALSE; + + memset(&l, 0, sizeof(l)); + l.fd = -1; + l.wanted = TRUE; + + t_push(); + for (tmp = t_strsplit_spaces(listen, ", "); *tmp != NULL; tmp++) { + l.port = default_port; + resolve_ip(set_name, *tmp, &l.ip, &l.port); + + /* see if it already exists */ + for (i = 0; i < count; i++) { + if (listens[i].port == l.port && + net_ip_compare(&listens[i].ip, &l.ip)) { + listens[i].wanted = TRUE; + break; + } + } + + if (i == count) { + array_append(listens_arr, &l, 1); + listens = array_get_modifiable(listens_arr, &count); + } + } + + /* close unwanted fds */ + for (i = 0; i < count; ) { + if (listens[i].wanted) + i++; + else { + if (listens[i].fd > 0) { + if (close(listens[i].fd) < 0) + i_error("close(listener) failed: %m"); + } + array_delete(listens_arr, i, 1); + listens = array_get_modifiable(listens_arr, &count); + } + } + t_pop(); +} + +static void listener_close_fds(ARRAY_TYPE(listener) *listens_arr) +{ + const struct listener *listens; + unsigned int i, count; + + if (!array_is_created(listens_arr)) + return; + + listens = array_get(listens_arr, &count); + for (i = 0; i < count; i++) { + if (listens[i].fd > 0) { + if (close(listens[i].fd) < 0) + i_error("close(listener) failed: %m"); + } + } + array_free(listens_arr); +} + +static void listen_parse_and_close_unneeded(struct settings *set) +{ + const char *const *proto; + unsigned int default_port; + bool listen = FALSE, ssl_listen = FALSE; + + if (set == NULL) + return; + + /* register wanted protocols */ + proto = t_strsplit_spaces(set->protocols, " "); + for (; *proto != NULL; proto++) { + if (strcasecmp(*proto, "imap") == 0) { + if (set->protocol == MAIL_PROTOCOL_IMAP) + listen = TRUE; + } else if (strcasecmp(*proto, "imaps") == 0) { + if (set->protocol == MAIL_PROTOCOL_IMAP && + !set->ssl_disable) + ssl_listen = TRUE; + } else if (strcasecmp(*proto, "pop3") == 0) { + if (set->protocol == MAIL_PROTOCOL_POP3) + listen = TRUE; + } else if (strcasecmp(*proto, "pop3s") == 0) { + if (set->protocol == MAIL_PROTOCOL_POP3 && + !set->ssl_disable) + ssl_listen = TRUE; + } + } + + if (!listen) + listener_close_fds(&set->listens); + else { + default_port = set->protocol == MAIL_PROTOCOL_IMAP ? 143 : 110; + listener_init("listen", set->listen, default_port, + &set->listens); + } + if (!ssl_listen) + listener_close_fds(&set->ssl_listens); + else { + default_port = set->protocol == MAIL_PROTOCOL_IMAP ? 993 : 995; + listener_init("ssl_listen", set->ssl_listen, default_port, + &set->ssl_listens); + } +} + +static void listen_copy_old(struct settings *old_set, struct settings *new_set) +{ + if (old_set == NULL || new_set == NULL) { + if (old_set != NULL) { + listener_close_fds(&old_set->listens); + listener_close_fds(&old_set->ssl_listens); + } + return; + } + + i_assert(!array_is_created(&new_set->listens)); + i_assert(!array_is_created(&new_set->ssl_listens)); + + new_set->listens = old_set->listens; + new_set->ssl_listens = old_set->ssl_listens; + + old_set->listens.arr.buffer = NULL; + old_set->ssl_listens.arr.buffer = NULL; +} + +static void +listener_array_listen_missing(const char *proto, + ARRAY_TYPE(listener) *listens_arr, bool retry) +{ + struct listener *listens; + unsigned int i, j, count; + + if (!array_is_created(listens_arr)) + return; + + listens = array_get_modifiable(listens_arr, &count); + for (i = 0; i < count; i++) { + if (listens[i].fd > 0) + continue; + + for (j = 0; j < 10; j++) { + listens[i].fd = + net_listen(&listens[i].ip, &listens[i].port, 8); + if (listens[i].fd != -1) + break; + + if (errno == EADDRINUSE) { + /* retry */ + } else if (errno == EINTR && + io_loop_is_running(ioloop)) { + /* SIGHUPing sometimes gets us here. + we don't want to die. */ + } else { + /* error */ + break; + } + + check_conflicts(&listens[i].ip, listens[i].port, proto); + if (!retry) + break; + + /* wait a while and try again. we're SIGHUPing + so we most likely just closed it ourself.. */ + sleep(1); + } + + if (listens[i].fd == -1) { + i_fatal("listen(%s, %d) failed: %m", + net_ip2addr(&listens[i].ip), listens[i].port); + } + net_set_nonblock(listens[i].fd, TRUE); + fd_close_on_exec(listens[i].fd, TRUE); + } +} + +static void +listener_listen_missing(struct settings *set, const char *proto, bool retry) +{ + listener_array_listen_missing(proto, &set->listens, retry); + listener_array_listen_missing(t_strconcat(proto, "s", NULL), + &set->ssl_listens, retry); +} + +void listeners_open_fds(struct server_settings *old_set, bool retry) +{ + struct server_settings *server; + + for (server = settings_root; server != NULL; server = server->next) { + if (old_set != NULL) { + listen_copy_old(old_set->imap, server->imap); + listen_copy_old(old_set->pop3, server->pop3); + } + listen_parse_and_close_unneeded(server->imap); + listen_parse_and_close_unneeded(server->pop3); + + if (old_set != NULL) + old_set = old_set->next; + } + + for (server = settings_root; server != NULL; server = server->next) { + if (server->imap != NULL) + listener_listen_missing(server->imap, "imap", retry); + if (server->pop3 != NULL) + listener_listen_missing(server->pop3, "pop3", retry); + } +} + +void listeners_close_fds(void) +{ + struct server_settings *server; + + for (server = settings_root; server != NULL; server = server->next) { + if (server->imap != NULL) { + listener_close_fds(&server->imap->listens); + listener_close_fds(&server->imap->ssl_listens); + } + if (server->pop3 != NULL) { + listener_close_fds(&server->pop3->listens); + listener_close_fds(&server->pop3->ssl_listens); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/master/listener.h Tue Jul 03 20:04:28 2007 +0300 @@ -0,0 +1,7 @@ +#ifndef __LISTENER_H +#define __LISTENER_H + +void listeners_open_fds(struct server_settings *old_set, bool retry); +void listeners_close_fds(void); + +#endif
--- a/src/master/login-process.c Tue Jul 03 18:34:31 2007 +0300 +++ b/src/master/login-process.c Tue Jul 03 20:04:28 2007 +0300 @@ -1,6 +1,7 @@ /* Copyright (C) 2002 Timo Sirainen */ #include "common.h" +#include "array.h" #include "ioloop.h" #include "hash.h" #include "network.h" @@ -10,6 +11,7 @@ #include "env-util.h" #include "restrict-access.h" #include "restrict-process-size.h" +#include "dup2-array.h" #include "login-process.h" #include "auth-process.h" #include "mail-process.h" @@ -582,10 +584,13 @@ static pid_t create_login_process(struct login_group *group) { struct log_io *log; + const struct listener *listens; unsigned int max_log_lines_per_sec; const char *prefix; pid_t pid; - int fd[2], log_fd; + ARRAY_TYPE(dup2) dups; + unsigned int i, listen_count = 0, ssl_listen_count = 0; + int fd[2], log_fd, cur_fd; if (group->set->login_uid == 0) i_fatal("Login process must not run as root"); @@ -632,36 +637,44 @@ process_names[group->mail_process_type]); log_set_prefix(log, prefix); - /* move the listen handle */ - if (dup2(group->set->listen_fd, LOGIN_LISTEN_FD) < 0) - i_fatal("dup2(listen_fd) failed: %m"); - fd_close_on_exec(LOGIN_LISTEN_FD, FALSE); - - /* move the SSL listen handle */ - if (dup2(group->set->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0) - i_fatal("dup2(ssl_listen_fd) failed: %m"); - fd_close_on_exec(LOGIN_SSL_LISTEN_FD, FALSE); - - /* move communication handle */ - if (dup2(fd[1], LOGIN_MASTER_SOCKET_FD) < 0) - i_fatal("dup2(master) failed: %m"); - fd_close_on_exec(LOGIN_MASTER_SOCKET_FD, FALSE); - - if (dup2(log_fd, STDERR_FILENO) < 0) - i_fatal("dup2(stderr) failed: %m"); - fd_close_on_exec(STDERR_FILENO, FALSE); - + t_array_init(&dups, 16); + dup2_append(&dups, null_fd, STDIN_FILENO); /* redirect writes to stdout also to error log. For example OpenSSL can be made to log its debug messages to stdout. */ - if (dup2(log_fd, STDOUT_FILENO) < 0) - i_fatal("dup2(stdout) failed: %m"); - fd_close_on_exec(STDOUT_FILENO, FALSE); + dup2_append(&dups, log_fd, STDOUT_FILENO); + dup2_append(&dups, log_fd, STDERR_FILENO); + dup2_append(&dups, fd[1], LOGIN_MASTER_SOCKET_FD); + + /* redirect listener fds */ + cur_fd = LOGIN_MASTER_SOCKET_FD + 1; + if (array_is_created(&group->set->listens)) { + listens = array_get(&group->set->listens, &listen_count); + for (i = 0; i < listen_count; i++, cur_fd++) + dup2_append(&dups, listens[i].fd, cur_fd); + } + + if (array_is_created(&group->set->ssl_listens)) { + listens = array_get(&group->set->ssl_listens, + &ssl_listen_count); + for (i = 0; i < ssl_listen_count; i++, cur_fd++) + dup2_append(&dups, listens[i].fd, cur_fd); + } + + if (dup2_array(&dups) < 0) + i_fatal("Failed to dup2() fds"); + + /* don't close any of these */ + while (cur_fd >= 0) + fd_close_on_exec(cur_fd--, FALSE); (void)close(fd[0]); (void)close(fd[1]); login_process_init_env(group, getpid()); + env_put(t_strdup_printf("LISTEN_FDS=%u", listen_count)); + env_put(t_strdup_printf("SSL_LISTEN_FDS=%u", ssl_listen_count)); + if (!group->set->login_chroot) { /* no chrooting, but still change to the directory */ if (chdir(group->set->login_dir) < 0) {
--- a/src/master/main.c Tue Jul 03 18:34:31 2007 +0300 +++ b/src/master/main.c Tue Jul 03 20:04:28 2007 +0300 @@ -1,6 +1,7 @@ /* Copyright (C) 2002 Timo Sirainen */ #include "common.h" +#include "array.h" #include "ioloop.h" #include "lib-signals.h" #include "network.h" @@ -37,9 +38,6 @@ bool gdb; #endif -static void listen_fds_open(bool retry); -static void listen_fds_close(struct server_settings *server); - static void set_logfile(struct settings *set) { int facility; @@ -74,10 +72,8 @@ if (!master_settings_read(configfile, FALSE, FALSE)) i_warning("Invalid configuration, keeping old one"); else { - if (!IS_INETD()) { - listen_fds_close(old_set); - listen_fds_open(TRUE); - } + if (!IS_INETD()) + listeners_open_fds(old_set, TRUE); set_logfile(settings_root->defaults); } } @@ -103,248 +99,6 @@ set_logfile(settings_root->defaults); } -static void resolve_ip(const char *set_name, const char *name, - struct ip_addr *ip, unsigned int *port) -{ - struct ip_addr *ip_list; - const char *p; - unsigned int ips_count; - int ret; - - if (*name == '\0') { - /* defaults to "*" or "[::]" */ - ip->family = 0; - return; - } - - if (name[0] == '[') { - /* IPv6 address */ - p = strchr(name, ']'); - if (p == NULL) { - i_fatal("%s: Missing ']' in address %s", - set_name, name); - } - name = t_strdup_until(name+1, p); - - p++; - if (*p == '\0') - p = NULL; - else if (*p != ':') { - i_fatal("%s: Invalid data after ']' in address %s", - set_name, name); - } - } else { - p = strrchr(name, ':'); - if (p != NULL) - name = t_strdup_until(name, p); - } - - if (p != NULL) { - if (!is_numeric(p+1, '\0')) { - i_fatal("%s: Invalid port in address %s", - set_name, name); - } - *port = atoi(p+1); - } - - if (strcmp(name, "*") == 0) { - /* IPv4 any */ - net_get_ip_any4(ip); - return; - } - - if (strcmp(name, "::") == 0) { - /* IPv6 any */ - net_get_ip_any6(ip); - return; - } - - /* Return the first IP if there happens to be multiple. */ - ret = net_gethostbyname(name, &ip_list, &ips_count); - if (ret != 0) { - i_fatal("%s: Can't resolve address %s: %s", - set_name, name, net_gethosterror(ret)); - } - - if (ips_count < 1) - i_fatal("%s: No IPs for address: %s", set_name, name); - - *ip = ip_list[0]; -} - -static void -check_conflicts_set(const struct settings *set, const struct ip_addr *ip, - unsigned int port, const char *name1, const char *name2) -{ - if (set->listen_port == port && net_ip_compare(ip, &set->listen_ip) && - set->listen_fd > 0) { - i_fatal("Protocols %s and %s are listening in same ip/port", - name1, name2); - } - if (set->ssl_listen_port == port && - net_ip_compare(ip, &set->ssl_listen_ip) && set->ssl_listen_fd > 0) { - i_fatal("Protocols %ss and %s are listening in same ip/port", - name1, name2); - } -} - -static void check_conflicts(const struct ip_addr *ip, unsigned int port, - const char *proto) -{ - struct server_settings *server; - - for (server = settings_root; server != NULL; server = server->next) { - if (server->imap != NULL) { - check_conflicts_set(server->imap, ip, port, - "imap", proto); - } - if (server->pop3 != NULL) { - check_conflicts_set(server->pop3, ip, port, - "pop3", proto); - } - } -} - -static void listen_protocols(struct settings *set, bool retry) -{ - struct ip_addr *ip; - const char *const *proto; - unsigned int port; - int *fd, i; - - set->listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 143 : 110; -#ifdef HAVE_SSL - set->ssl_listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 993 : 995; -#else - set->ssl_listen_port = 0; -#endif - - /* resolve */ - resolve_ip("listen", set->listen, &set->listen_ip, &set->listen_port); - if (!set->ssl_disable) { - resolve_ip("ssl_listen", set->ssl_listen, &set->ssl_listen_ip, - &set->ssl_listen_port); - } - - /* if ssl_listen wasn't explicitly set in the config file, - use the non-ssl IP settings for the ssl listener, too. */ - if (set->ssl_listen_ip.family == 0 && *set->ssl_listen == '\0') - set->ssl_listen_ip = set->listen_ip; - - /* register wanted protocols */ - proto = t_strsplit_spaces(set->protocols, " "); - for (; *proto != NULL; proto++) { - fd = NULL; ip = NULL; port = 0; - if (strcasecmp(*proto, "imap") == 0) { - if (set->protocol == MAIL_PROTOCOL_IMAP) { - fd = &set->listen_fd; - port = set->listen_port; - ip = &set->listen_ip; - } - } else if (strcasecmp(*proto, "imaps") == 0) { - if (set->protocol == MAIL_PROTOCOL_IMAP && - !set->ssl_disable) { - fd = &set->ssl_listen_fd; - port = set->ssl_listen_port; - ip = &set->ssl_listen_ip; - } - } else if (strcasecmp(*proto, "pop3") == 0) { - if (set->protocol == MAIL_PROTOCOL_POP3) { - fd = &set->listen_fd; - port = set->listen_port; - ip = &set->listen_ip; - } - } else if (strcasecmp(*proto, "pop3s") == 0) { - if (set->protocol == MAIL_PROTOCOL_POP3 && - !set->ssl_disable) { - fd = &set->ssl_listen_fd; - port = set->ssl_listen_port; - ip = &set->ssl_listen_ip; - } - } else { - i_fatal("Unknown protocol %s", *proto); - } - - if (fd == NULL) - continue; - - if (*fd != -1) - i_fatal("Protocol %s given more than once", *proto); - - if (port == 0) - *fd = null_fd; - else { - for (i = 0; i < 10; i++) { - *fd = net_listen(ip, &port, 8); - if (*fd != -1) - break; - if (errno == EADDRINUSE) { - /* retry */ - } else if (errno == EINTR && - io_loop_is_running(ioloop)) { - /* SIGHUPing sometimes gets us here. - we don't want to die. */ - } else { - /* error */ - break; - } - - check_conflicts(ip, port, *proto); - if (!retry) - break; - - /* wait a while and try again. we're SIGHUPing - so we most likely just closed it ourself.. */ - sleep(1); - } - - if (*fd == -1) - i_fatal("listen(%d) failed: %m", port); - net_set_nonblock(*fd, TRUE); - fd_close_on_exec(*fd, TRUE); - } - } - - if (set->listen_fd == -1) - set->listen_fd = null_fd; - if (set->ssl_listen_fd == -1) - set->ssl_listen_fd = null_fd; -} - -static void listen_fds_open(bool retry) -{ - struct server_settings *server; - - for (server = settings_root; server != NULL; server = server->next) { - if (server->imap != NULL) - listen_protocols(server->imap, retry); - if (server->pop3 != NULL) - listen_protocols(server->pop3, retry); - } -} - -static void listen_fds_close(struct server_settings *server) -{ - for (; server != NULL; server = server->next) { - if (server->imap != NULL) { - if (server->imap->listen_fd != null_fd && - close(server->imap->listen_fd) < 0) - i_error("close(imap.listen_fd) failed: %m"); - if (server->imap->ssl_listen_fd != null_fd && - close(server->imap->ssl_listen_fd) < 0) - i_error("close(imap.ssl_listen_fd) failed: %m"); - } - if (server->pop3 != NULL) { - if (server->pop3->listen_fd != null_fd && - close(server->pop3->listen_fd) < 0) - i_error("close(pop3.listen_fd) failed: %m"); - if (server->pop3->ssl_listen_fd != null_fd && - close(server->pop3->ssl_listen_fd) < 0) - i_error("close(pop3.ssl_listen_fd) failed: %m"); - } - } -} - static bool have_stderr_set(struct settings *set) { if (*set->log_path != '\0' && @@ -391,7 +145,7 @@ } if (!IS_INETD()) - listen_fds_open(FALSE); + listeners_open_fds(FALSE); /* close stdin and stdout. */ if (dup2(null_fd, 0) < 0) @@ -471,6 +225,8 @@ dict_process_deinit(); ssl_deinit(); + listeners_close_fds(); + if (close(null_fd) < 0) i_error("close(null_fd) failed: %m");
--- a/src/master/master-login-interface.h Tue Jul 03 18:34:31 2007 +0300 +++ b/src/master/master-login-interface.h Tue Jul 03 20:04:28 2007 +0300 @@ -3,8 +3,6 @@ #include "network.h" -#define LOGIN_LISTEN_FD 0 -#define LOGIN_SSL_LISTEN_FD 4 #define LOGIN_MASTER_SOCKET_FD 3 /* Increase the version number every time master_login_request
--- a/src/master/master-settings.c Tue Jul 03 18:34:31 2007 +0300 +++ b/src/master/master-settings.c Tue Jul 03 20:04:28 2007 +0300 @@ -276,8 +276,6 @@ MEMBER(pop3_logout_format) "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s", /* .. */ - MEMBER(listen_fd) -1, - MEMBER(ssl_listen_fd) -1 }; struct auth_settings default_auth_settings = {
--- a/src/master/master-settings.h Tue Jul 03 18:34:31 2007 +0300 +++ b/src/master/master-settings.h Tue Jul 03 20:04:28 2007 +0300 @@ -10,6 +10,14 @@ MAIL_PROTOCOL_LDA }; +struct listener { + struct ip_addr ip; + unsigned int port; + int fd; + bool wanted; +}; +ARRAY_DEFINE_TYPE(listener, struct listener); + struct settings { struct server_settings *server; enum mail_protocol protocol; @@ -124,14 +132,12 @@ const char *pop3_logout_format; /* .. */ - int listen_fd, ssl_listen_fd; + ARRAY_TYPE(listener) listens; + ARRAY_TYPE(listener) ssl_listens; uid_t login_uid, mail_uid_t; gid_t mail_gid_t; - struct ip_addr listen_ip, ssl_listen_ip; - unsigned int listen_port, ssl_listen_port; - const char *imap_generated_capability; ARRAY_DEFINE(plugin_envs, const char *);