changeset 1273:2cf2e08a6ee9 HEAD

Somewhat working code to support loading Dovecot from inetd and such. It still needs possibility to specify which login process to use, and LOGIN and AUTHENTICATE commands shouldn't be allowed before we're connected to auth process.
author Timo Sirainen <tss@iki.fi>
date Tue, 04 Mar 2003 06:02:56 +0200
parents 634b5ef110da
children f7fc5d52ac7c
files src/login-common/Makefile.am src/login-common/main.c src/login-common/master.c src/login-common/master.h src/master/auth-process.c src/master/common.h src/master/login-process.c src/master/mail-process.c src/master/main.c src/master/master-login-interface.h
diffstat 10 files changed, 335 insertions(+), 110 deletions(-) [+]
line wrap: on
line diff
--- a/src/login-common/Makefile.am	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/login-common/Makefile.am	Tue Mar 04 06:02:56 2003 +0200
@@ -1,7 +1,9 @@
 noinst_LIBRARIES = liblogin-common.a
 
 INCLUDES = \
-	-I$(top_srcdir)/src/lib
+	-I$(top_srcdir)/src/lib \
+	-DPKG_RUNDIR=\""$(localstatedir)/run/$(PACKAGE)"\" \
+	-DSBINDIR=\""$(sbindir)"\"
 
 liblogin_common_a_SOURCES = \
 	auth-common.c \
--- a/src/login-common/main.c	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/login-common/main.c	Tue Mar 04 06:02:56 2003 +0200
@@ -23,7 +23,7 @@
 static struct ioloop *ioloop;
 static struct io *io_listen, *io_ssl_listen;
 static int main_refcount;
-static int closing_down;
+static int is_inetd, closing_down;
 
 void main_ref(void)
 {
@@ -99,7 +99,6 @@
 
 static void login_accept_ssl(void *context __attr_unused__)
 {
-	struct client *client;
 	struct ip_addr ip;
 	int fd, fd_ssl;
 
@@ -124,7 +123,7 @@
 	if (fd_ssl == -1)
 		net_disconnect(fd);
 	else
-		client = client_create(fd_ssl, &ip, TRUE);
+		(void)client_create(fd_ssl, &ip, TRUE);
 }
 
 static void open_logfile(const char *name)
@@ -185,25 +184,29 @@
 
 	io_listen = io_ssl_listen = NULL;
 
-	if (net_getsockname(LOGIN_LISTEN_FD, NULL, NULL) == 0) {
-		io_listen = io_add(LOGIN_LISTEN_FD, IO_READ,
-				   login_accept, NULL);
-	}
-
-	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");
+	if (!is_inetd) {
+		if (net_getsockname(LOGIN_LISTEN_FD, NULL, NULL) == 0) {
+			io_listen = io_add(LOGIN_LISTEN_FD, IO_READ,
+					   login_accept, NULL);
 		}
 
-		io_ssl_listen = io_add(LOGIN_SSL_LISTEN_FD, IO_READ,
-				       login_accept_ssl, NULL);
+		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);
+		}
+
+		/* initialize master last - it sends the "we're ok"
+		   notification */
+		master_init(LOGIN_MASTER_SOCKET_FD);
 	}
-
-	/* initialize master last - it sends the "we're ok" notification */
-	master_init();
 }
 
 static void main_deinit(void)
@@ -226,23 +229,54 @@
 int main(int argc __attr_unused__, char *argv[], char *envp[])
 {
 	const char *name;
+	struct ip_addr ip;
+	int fd = -1, master_fd = -1;
+
+	is_inetd = getenv("DOVECOT_MASTER") == NULL;
 
 #ifdef DEBUG
-        fd_debug_verify_leaks(4, 1024);
+	if (!is_inetd)
+		fd_debug_verify_leaks(4, 1024);
 #endif
 	/* NOTE: we start rooted, so keep the code minimal until
 	   restrict_access_by_env() is called */
 	lib_init();
 
+	if (is_inetd) {
+		/* running from inetd. create master process before
+		   dropping privileges */
+		master_fd = master_connect();
+	}
+
 	name = strrchr(argv[0], '/');
 	drop_privileges(name == NULL ? argv[0] : name+1);
 
 	process_title_init(argv, envp);
 	ioloop = io_loop_create(system_pool);
 
-	main_init();
-        io_loop_run(ioloop);
-	main_deinit();
+	if (is_inetd) {
+		master_init(master_fd);
+
+		if (net_getsockname(1, &ip, NULL) < 0) {
+			i_fatal("%s can be started only through dovecot "
+				"master process, inetd or equilevant", argv[0]);
+		}
+
+		if (argc < 2 || strcmp(argv[1], "--ssl") != 0)
+			fd = 1;
+		else
+			fd = ssl_proxy_new(fd, &ip);
+	}
+
+	if (fd != -1 || !is_inetd) {
+		main_init();
+
+		if (fd != -1)
+			(void)client_create(fd, &ip, TRUE);
+
+		io_loop_run(ioloop);
+		main_deinit();
+	}
 
 	io_loop_destroy(ioloop);
 	lib_deinit();
--- a/src/login-common/master.c	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/login-common/master.c	Tue Mar 04 06:02:56 2003 +0200
@@ -5,11 +5,14 @@
 #include "ioloop.h"
 #include "network.h"
 #include "fdpass.h"
+#include "istream.h"
+#include "env-util.h"
 #include "master.h"
 #include "client-common.h"
 
 #include <unistd.h>
 
+static int master_fd;
 static struct io *io_master;
 static struct hash_table *master_requests;
 
@@ -42,8 +45,7 @@
 	req.auth_id = auth_id;
 	req.ip = client->ip;
 
-	if (fd_send(LOGIN_MASTER_SOCKET_FD,
-		    client->fd, &req, sizeof(req)) != sizeof(req))
+	if (fd_send(master_fd, client->fd, &req, sizeof(req)) != sizeof(req))
 		i_fatal("fd_send(%d) failed: %m", client->fd);
 
 	client->master_callback = callback;
@@ -60,8 +62,7 @@
 	memset(&req, 0, sizeof(req));
 
 	/* sending -1 as fd does the notification */
-	if (fd_send(LOGIN_MASTER_SOCKET_FD,
-		    -1, &req, sizeof(req)) != sizeof(req))
+	if (fd_send(master_fd, -1, &req, sizeof(req)) != sizeof(req))
 		i_fatal("fd_send(-1) failed: %m");
 }
 
@@ -70,8 +71,9 @@
 	if (io_master == NULL)
 		return;
 
-	if (close(LOGIN_MASTER_SOCKET_FD) < 0)
+	if (close(master_fd) < 0)
 		i_fatal("close(master) failed: %m");
+	master_fd = -1;
 
 	io_remove(io_master);
 	io_master = NULL;
@@ -83,11 +85,94 @@
 	clients_destroy_all();
 }
 
+static void master_exec(int fd)
+{
+	char *argv[] = { "dovecot", "--inetd", NULL };
+
+	switch (fork()) {
+	case -1:
+		i_fatal("fork() failed: %m");
+	case 0:
+		if (dup2(fd, 0) < 0)
+			i_fatal("master_exec: dup2(%d, 0) failed: %m", fd);
+		(void)close(fd);
+
+		if (setsid() < 0)
+			i_fatal("setsid() failed: %m");
+
+		execv(SBINDIR"/dovecot", argv);
+		i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
+			       SBINDIR"/dovecot");
+	default:
+		(void)close(fd);
+	}
+}
+
+static void master_read_env(int fd)
+{
+	struct istream *input;
+	const char *line;
+
+	env_clean();
+
+	/* read environment variable lines until empty line comes */
+	input = i_stream_create_file(fd, default_pool, 8192, FALSE);
+	do {
+		switch (i_stream_read(input)) {
+		case -1:
+			i_fatal("EOF while reading environment from master");
+		case -2:
+			i_fatal("Too large environment line from master");
+		}
+
+		while ((line = i_stream_next_line(input)) != NULL &&
+		       *line != '\0')
+			env_put(line);
+	} while (line == NULL);
+
+	i_stream_unref(input);
+}
+
+int master_connect(void)
+{
+	const char *path = PKG_RUNDIR"/master";
+	int i, fd = -1;
+
+	for (i = 0; i < 5 && fd == -1; i++) {
+		fd = net_connect_unix(path);
+		if (fd != -1)
+			break;
+
+		if (errno == ECONNREFUSED) {
+			if (unlink(path) < 0)
+				i_error("unlink(%s) failed: %m", path);
+		} else if (errno != ENOENT) {
+			i_fatal("Can't connect to master UNIX socket %s: %m",
+				path);
+		}
+
+		/* need to create it */
+		fd = net_listen_unix(path);
+		if (fd != -1) {
+			master_exec(fd);
+			fd = -1;
+		} else if (errno != EADDRINUSE) {
+			i_fatal("Can't create master UNIX socket %s: %m", path);
+		}
+	}
+
+	if (fd == -1)
+		i_fatal("Couldn't use/create UNIX socket %s", path);
+
+	master_read_env(fd);
+	return fd;
+}
+
 static void master_input(void *context __attr_unused__)
 {
 	int ret;
 
-	ret = net_receive(LOGIN_MASTER_SOCKET_FD, master_buf + master_pos,
+	ret = net_receive(master_fd, master_buf + master_pos,
 			  sizeof(master_buf) - master_pos);
 	if (ret < 0) {
 		/* master died, kill all clients logging in */
@@ -104,19 +189,20 @@
 	master_pos = 0;
 }
 
-void master_init(void)
+void master_init(int fd)
 {
 	main_ref();
 
+	master_fd = fd;
 	master_requests = hash_create(default_pool, default_pool,
 				      0, NULL, NULL);
 
         master_pos = 0;
-	io_master = io_add(LOGIN_MASTER_SOCKET_FD, IO_READ, master_input, NULL);
+	io_master = io_add(master_fd, IO_READ, master_input, NULL);
 
 	/* just a note to master that we're ok. if we die before,
 	   master should shutdown itself. */
-        master_notify_finished();
+	master_notify_finished();
 }
 
 void master_deinit(void)
--- a/src/login-common/master.h	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/login-common/master.h	Tue Mar 04 06:02:56 2003 +0200
@@ -16,7 +16,10 @@
 /* Close connection to master process */
 void master_close(void);
 
-void master_init(void);
+/* inetd: Connect to existing master process, or create new one. */
+int master_connect(void);
+
+void master_init(int fd);
 void master_deinit(void);
 
 #endif
--- a/src/master/auth-process.c	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/master/auth-process.c	Tue Mar 04 06:02:56 2003 +0200
@@ -274,6 +274,7 @@
 
 	if (pid != 0) {
 		/* master */
+		net_set_nonblock(fd[0], TRUE);
 		fd_close_on_exec(fd[0], TRUE);
 		auth_process_new(pid, fd[0], group);
 		(void)close(fd[1]);
@@ -292,7 +293,7 @@
 	if (dup2(null_fd, 1) < 0)
 		i_fatal("login: dup2(1) failed: %m");
 
-	clean_child_process();
+	child_process_init_env();
 
 	/* move login communication handle to 3. do it last so we can be
 	   sure it's not closed afterwards. */
@@ -304,8 +305,7 @@
 	for (i = 0; i <= 3; i++)
 		fd_close_on_exec(i, FALSE);
 
-	/* setup access environment - needs to be done after
-	   clean_child_process() since it clears environment */
+	/* setup access environment */
 	restrict_access_set_env(group->set->user, pwd->pw_uid, pwd->pw_gid,
 				group->set->chroot);
 
--- a/src/master/common.h	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/master/common.h	Tue Mar 04 06:02:56 2003 +0200
@@ -30,7 +30,10 @@
 
 extern struct ioloop *ioloop;
 extern struct hash_table *pids;
-extern int null_fd, mail_fd[FD_MAX];
+extern int null_fd, mail_fd[FD_MAX], inetd_login_fd;
+
+#define IS_INETD() \
+	(inetd_login_fd != -1)
 
 /* processes */
 #define PID_GET_PROCESS_TYPE(pid) \
@@ -42,7 +45,7 @@
 #define PID_REMOVE_PROCESS_TYPE(pid) \
 	hash_remove(pids, POINTER_CAST(pid))
 
-void clean_child_process(void);
+void child_process_init_env(void);
 
 /* misc */
 #define VALIDATE_STR(str) \
--- a/src/master/login-process.c	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/master/login-process.c	Tue Mar 04 06:02:56 2003 +0200
@@ -58,8 +58,9 @@
 	struct ip_addr ip;
 };
 
-static unsigned int auth_id_counter;
+static unsigned int auth_id_counter, login_pid_counter;
 static struct timeout *to;
+static struct io *io_listen;
 
 static struct hash_table *processes;
 static struct login_group *login_groups;
@@ -220,7 +221,8 @@
 {
 	struct login_process *p;
 
-	PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_LOGIN);
+	i_assert(pid != 0);
+	i_assert(group != NULL);
 
 	p = i_new(struct login_process, 1);
 	p->group = group;
@@ -233,7 +235,9 @@
 					 sizeof(struct master_login_reply)*10,
 					 IO_PRIORITY_DEFAULT, FALSE);
 
+	PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_LOGIN);
 	hash_insert(processes, POINTER_CAST(pid), p);
+
 	p->group->processes++;
 	p->group->listening_processes++;
 	return p;
@@ -276,7 +280,8 @@
 		login_process_remove_from_lists(p);
 
 	p->group->processes--;
-	hash_remove(processes, POINTER_CAST(p->pid));
+	if (p->pid != 0)
+		hash_remove(processes, POINTER_CAST(p->pid));
 
 	login_process_unref(p);
 }
@@ -290,6 +295,44 @@
 	i_free(p);
 }
 
+static void login_process_init_env(struct login_group *group, pid_t pid)
+{
+	child_process_init_env();
+
+	/* setup access environment - needs to be done after
+	   clean_child_process() since it clears environment */
+	restrict_access_set_env(group->set->user,
+				group->set->uid, set->login_gid,
+				set->login_chroot ? set->login_dir : NULL);
+
+	env_put("DOVECOT_MASTER=1");
+
+	if (!set->ssl_disable) {
+		env_put(t_strconcat("SSL_CERT_FILE=",
+				    set->ssl_cert_file, NULL));
+		env_put(t_strconcat("SSL_KEY_FILE=", set->ssl_key_file, NULL));
+		env_put(t_strconcat("SSL_PARAM_FILE=",
+				    set->ssl_parameters_file, NULL));
+	}
+
+	if (set->disable_plaintext_auth)
+		env_put("DISABLE_PLAINTEXT_AUTH=1");
+	if (set->verbose_proctitle)
+		env_put("VERBOSE_PROCTITLE=1");
+	if (set->verbose_ssl)
+		env_put("VERBOSE_SSL=1");
+
+	if (group->set->process_per_connection) {
+		env_put("PROCESS_PER_CONNECTION=1");
+		env_put("MAX_LOGGING_USERS=1");
+	} else {
+		env_put(t_strdup_printf("MAX_LOGGING_USERS=%u",
+					group->set->max_logging_users));
+	}
+
+	env_put(t_strdup_printf("PROCESS_UID=%s", dec2str(pid)));
+}
+
 static pid_t create_login_process(struct login_group *group)
 {
 	static const char *argv[] = { NULL, NULL };
@@ -322,44 +365,32 @@
 
 	if (pid != 0) {
 		/* master */
+		net_set_nonblock(fd[0], TRUE);
 		fd_close_on_exec(fd[0], TRUE);
-		login_process_new(group, pid, fd[0]);
+		(void)login_process_new(group, pid, fd[0]);
 		(void)close(fd[1]);
 		return pid;
 	}
 
-	/* move communication handle */
-	if (dup2(fd[1], LOGIN_MASTER_SOCKET_FD) < 0)
-		i_fatal("login: dup2(master) failed: %m");
-	fd_close_on_exec(LOGIN_MASTER_SOCKET_FD, FALSE);
-
 	/* move the listen handle */
 	if (dup2(*group->listen_fd, LOGIN_LISTEN_FD) < 0)
 		i_fatal("login: dup2(listen_fd) failed: %m");
 	fd_close_on_exec(LOGIN_LISTEN_FD, FALSE);
 
 	/* move the SSL listen handle */
-	if (!set->ssl_disable) {
-		if (dup2(*group->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0)
-			i_fatal("login: dup2(ssl_listen_fd) failed: %m");
-	} else {
-		if (dup2(null_fd, LOGIN_SSL_LISTEN_FD) < 0)
-			i_fatal("login: dup2(ssl_listen_fd) failed: %m");
-	}
+	if (dup2(*group->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0)
+		i_fatal("login: dup2(ssl_listen_fd) failed: %m");
 	fd_close_on_exec(LOGIN_SSL_LISTEN_FD, FALSE);
 
-	/* listen_fds are closed by clean_child_process() */
+	/* move communication handle */
+	if (dup2(fd[1], LOGIN_MASTER_SOCKET_FD) < 0)
+		i_fatal("login: dup2(master) failed: %m");
+	fd_close_on_exec(LOGIN_MASTER_SOCKET_FD, FALSE);
 
 	(void)close(fd[0]);
 	(void)close(fd[1]);
 
-	clean_child_process();
-
-	/* setup access environment - needs to be done after
-	   clean_child_process() since it clears environment */
-	restrict_access_set_env(group->set->user,
-				group->set->uid, set->login_gid,
-				set->login_chroot ? set->login_dir : NULL);
+	login_process_init_env(group, getpid());
 
 	if (!set->login_chroot) {
 		/* no chrooting, but still change to the directory */
@@ -367,31 +398,6 @@
 			i_fatal("chdir(%s) failed: %m", set->login_dir);
 	}
 
-	if (!set->ssl_disable) {
-		env_put(t_strconcat("SSL_CERT_FILE=",
-				    set->ssl_cert_file, NULL));
-		env_put(t_strconcat("SSL_KEY_FILE=", set->ssl_key_file, NULL));
-		env_put(t_strconcat("SSL_PARAM_FILE=",
-				    set->ssl_parameters_file, NULL));
-	}
-
-	if (set->disable_plaintext_auth)
-		env_put("DISABLE_PLAINTEXT_AUTH=1");
-	if (set->verbose_proctitle)
-		env_put("VERBOSE_PROCTITLE=1");
-	if (set->verbose_ssl)
-		env_put("VERBOSE_SSL=1");
-
-	if (group->set->process_per_connection) {
-		env_put("PROCESS_PER_CONNECTION=1");
-		env_put("MAX_LOGGING_USERS=1");
-	} else {
-		env_put(t_strdup_printf("MAX_LOGGING_USERS=%u",
-					group->set->max_logging_users));
-	}
-
-	env_put(t_strdup_printf("PROCESS_UID=%s", dec2str(getpid())));
-
 	restrict_process_size(group->set->process_size, 0);
 
 	/* make sure we don't leak syslog fd, but do it last so that
@@ -483,18 +489,82 @@
 		login_group_start_missings(group);
 }
 
+static int login_process_send_env(struct login_process *p)
+{
+	extern char **environ;
+	char **env;
+	size_t len;
+	int ret = 0;
+
+	/* this will clear our environment. luckily we don't need it. */
+	login_process_init_env(p->group, p->pid);
+
+	for (env = environ; *env != NULL; env++) {
+		len = strlen(*env);
+
+		if (o_stream_send(p->output, *env, len) != (ssize_t)len ||
+		    o_stream_send(p->output, "\n", 1) != 1) {
+			ret = -1;
+			break;
+		}
+	}
+
+	if (ret == 0 && o_stream_send(p->output, "\n", 1) != 1)
+		ret = -1;
+
+	env_clean();
+	return ret;
+}
+
+static void inetd_login_accept(void *context __attr_unused__)
+{
+        struct login_process *p;
+	int fd;
+
+	fd = net_accept(inetd_login_fd, NULL, NULL);
+	if (fd < 0) {
+		if (fd < -1)
+			i_fatal("accept(inetd_login_fd) failed: %m");
+	} else {
+		net_set_nonblock(fd, TRUE);
+		fd_close_on_exec(fd, TRUE);
+
+		p = login_process_new(login_groups, ++login_pid_counter, fd);
+		p->initialized = TRUE;;
+
+		if (login_process_send_env(p) < 0) {
+			i_warning("Couldn't send environment to login process");
+			login_process_destroy(p);
+		}
+	}
+}
+
 void login_processes_init(void)
 {
-        auth_id_counter = 0;
+	auth_id_counter = 0;
+        login_pid_counter = 0;
 	login_groups = NULL;
 
 	processes = hash_create(default_pool, default_pool, 128, NULL, NULL);
-	to = timeout_add(1000, login_processes_start_missing, NULL);
+	if (!IS_INETD()) {
+		to = timeout_add(1000, login_processes_start_missing, NULL);
+		io_listen = NULL;
+	} else {
+		/* use the first login group for everyone */
+		login_group_create(set->logins);
+
+		to = NULL;
+		io_listen = io_add(inetd_login_fd, IO_READ,
+				   inetd_login_accept, NULL);
+	}
 }
 
 void login_processes_deinit(void)
 {
-	timeout_remove(to);
+	if (to != NULL)
+		timeout_remove(to);
+	if (io_listen != NULL)
+		io_remove(io_listen);
 
         login_processes_destroy_all();
 	hash_destroy(processes);
--- a/src/master/mail-process.c	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/master/mail-process.c	Tue Mar 04 06:02:56 2003 +0200
@@ -132,7 +132,7 @@
 		return TRUE;
 	}
 
-	clean_child_process();
+	child_process_init_env();
 
 	/* move the client socket into stdin and stdout fds */
 	fd_close_on_exec(socket, FALSE);
--- a/src/master/main.c	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/master/main.c	Tue Mar 04 06:02:56 2003 +0200
@@ -33,7 +33,7 @@
 
 struct ioloop *ioloop;
 struct hash_table *pids;
-int null_fd, mail_fd[FD_MAX];
+int null_fd, mail_fd[FD_MAX], inetd_login_fd;
 
 int validate_str(const char *str, size_t max_len)
 {
@@ -47,7 +47,7 @@
 	return FALSE;
 }
 
-void clean_child_process(void)
+void child_process_init_env(void)
 {
 	/* remove all environment, we don't need them */
 	env_clean();
@@ -216,7 +216,7 @@
 	return ip;
 }
 
-static void open_fds(void)
+static void listen_protocols(void)
 {
 	struct ip_addr *imap_ip, *imaps_ip, *pop3_ip, *pop3s_ip, *ip;
 	const char *const *proto;
@@ -243,15 +243,6 @@
 	if (pop3s_ip == NULL && set->pop3s_listen == NULL)
 		pop3s_ip = pop3_ip;
 
-	/* initialize fds */
-	null_fd = open("/dev/null", O_RDONLY);
-	if (null_fd == -1)
-		i_fatal("Can't open /dev/null: %m");
-	fd_close_on_exec(null_fd, TRUE);
-
-	for (i = 0; i < FD_MAX; i++)
-		mail_fd[i] = -1;
-
 	/* register wanted protocols */
 	for (proto = t_strsplit(set->protocols, " "); *proto != NULL; proto++) {
 		if (strcasecmp(*proto, "imap") == 0) {
@@ -286,6 +277,29 @@
 			fd_close_on_exec(mail_fd[i], TRUE);
 		}
 	}
+}
+
+static void open_fds(void)
+{
+	int i;
+
+	/* initialize fds. */
+	null_fd = open("/dev/null", O_RDONLY);
+	if (null_fd == -1)
+		i_fatal("Can't open /dev/null: %m");
+	fd_close_on_exec(null_fd, TRUE);
+
+	/* make sure all fds between 0..3 are used. */
+	while (null_fd < 4) {
+		null_fd = dup(null_fd);
+		fd_close_on_exec(null_fd, TRUE);
+	}
+
+	for (i = 0; i < FD_MAX; i++)
+		mail_fd[i] = -1;
+
+	if (!IS_INETD())
+		listen_protocols();
 
 	/* close stdin and stdout. close stderr unless we're logging
 	   into /dev/stderr. */
@@ -299,7 +313,7 @@
 	    (set->info_log_path == NULL ||
 	     strcmp(set->info_log_path, "/dev/stderr") != 0)) {
 		if (dup2(null_fd, 2) < 0)
-			i_fatal("dup(0) failed: %m");
+			i_fatal("dup2(2) failed: %m");
 	}
 }
 
@@ -357,8 +371,10 @@
 		i_error("close(null_fd) failed: %m");
 
 	for (i = 0; i < FD_MAX; i++) {
-		if (close(mail_fd[i]) < 0)
-			i_error("close(mail_fd[%d]) failed: %m", i);
+		if (mail_fd[i] != -1) {
+			if (close(mail_fd[i]) < 0)
+				i_error("close(mail_fd[%d]) failed: %m", i);
+		}
 	}
 
 	hash_destroy(pids);
@@ -393,6 +409,7 @@
 
 	lib_init();
 
+        inetd_login_fd = -1;
 	for (i = 1; i < argc; i++) {
 		if (strcmp(argv[i], "-F") == 0) {
 			/* foreground */
@@ -402,6 +419,13 @@
 			i++;
 			if (i == argc) i_fatal("Missing config file argument");
 			configfile = argv[i];
+		} else if (strcmp(argv[i], "--inetd") == 0) {
+			/* starting through inetd. */
+			inetd_login_fd = dup(0);
+			if (inetd_login_fd == -1)
+				i_fatal("dup(0) failed: %m");
+			fd_close_on_exec(inetd_login_fd, TRUE);
+			foreground = TRUE;
 		} else if (strcmp(argv[i], "--version") == 0) {
 			printf("%s\n", VERSION);
 			return 0;
@@ -416,6 +440,9 @@
 	master_settings_read(configfile);
 	open_fds();
 
+	/* we don't need any environment */
+	env_clean();
+
 	if (!foreground)
 		daemonize();
 
--- a/src/master/master-login-interface.h	Tue Mar 04 06:01:37 2003 +0200
+++ b/src/master/master-login-interface.h	Tue Mar 04 06:02:56 2003 +0200
@@ -3,9 +3,9 @@
 
 #include "network.h"
 
-#define LOGIN_MASTER_SOCKET_FD 0
-#define LOGIN_LISTEN_FD 1
-#define LOGIN_SSL_LISTEN_FD 3
+#define LOGIN_LISTEN_FD 0
+#define LOGIN_SSL_LISTEN_FD 1
+#define LOGIN_MASTER_SOCKET_FD 3
 
 struct master_login_request {
 	unsigned int tag;