changeset 9159:6324a79d3ee1 HEAD

Initial commit for v2.0 master rewrite. Several features are still missing.
author Timo Sirainen <tss@iki.fi>
date Thu, 23 Apr 2009 19:53:44 -0400
parents 69aa6e878a59
children 1cd9808147e3
files .hgignore TODO configure.in src/Makefile.am src/auth/Makefile.am src/auth/auth-client-connection.c src/auth/auth-client-connection.h src/auth/auth-master-connection.c src/auth/auth-master-connection.h src/auth/auth-master-interface.h src/auth/auth-master-listener.c src/auth/auth-master-listener.h src/auth/auth-settings.c src/auth/auth-settings.h src/auth/auth-worker-client.c src/auth/common.h src/auth/db-ldap.c src/auth/main.c src/config/Makefile.am src/config/common.h src/config/config-connection.c src/config/config-connection.h src/config/main.c src/config/settings-get.pl src/imap-login/Makefile.am src/imap-login/client.c src/imap-login/client.h src/imap-login/imap-proxy.c src/imap/main.c src/lda/main.c src/lib-master/Makefile.am src/lib-master/master-auth.c src/lib-master/master-auth.h src/lib-master/master-interface.h src/lib-master/master-service-private.h src/lib-master/master-service-settings.c src/lib-master/master-service.c src/lib-master/master-service.h src/lib-settings/settings-parser.c src/lib-settings/settings-parser.h src/lib-storage/mail-storage-service.c src/lib-storage/mail-storage-settings.c src/lib-storage/mail-storage-settings.h src/lib/failures.c src/lib/failures.h src/lib/restrict-access.c src/lib/restrict-access.h src/lmtp/client.c src/lmtp/main.c src/log/Makefile.am src/log/common.h src/log/log-connection.c src/log/log-connection.h src/log/main.c src/login-common/Makefile.am src/login-common/client-common.h src/login-common/common.h src/login-common/login-proxy.c src/login-common/login-proxy.h src/login-common/login-settings.c src/login-common/login-settings.h src/login-common/main.c src/login-common/master.c src/login-common/master.h src/login-common/sasl-server.c src/login-common/sasl-server.h src/login-common/ssl-proxy-openssl.c src/login-common/ssl-proxy.c src/login-common/ssl-proxy.h src/master/Makefile.am src/master/auth-process.c src/master/auth-process.h src/master/child-process.c src/master/child-process.h src/master/common.h src/master/dict-process.c src/master/dict-process.h src/master/listener.c src/master/listener.h src/master/log.c src/master/log.h src/master/login-process.c src/master/login-process.h src/master/mail-process.c src/master/mail-process.h src/master/main.c src/master/master-login-interface.h src/master/master-settings.c src/master/master-settings.h src/master/service-auth-server.c src/master/service-auth-server.h src/master/service-auth-source.c src/master/service-auth-source.h src/master/service-listen.c src/master/service-listen.h src/master/service-log.c src/master/service-log.h src/master/service-monitor.c src/master/service-monitor.h src/master/service-process.c src/master/service-process.h src/master/service.c src/master/service.h src/master/sysinfo-get.c src/master/sysinfo-get.h src/plugins/convert/convert-tool.c src/pop3-login/Makefile.am src/pop3-login/client.c src/pop3-login/client.h src/pop3-login/pop3-proxy.c src/pop3/main.c
diffstat 111 files changed, 4728 insertions(+), 7184 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Thu Apr 23 14:07:45 2009 -0400
+++ b/.hgignore	Thu Apr 23 19:53:44 2009 -0400
@@ -65,6 +65,7 @@
 src/lib-sql/sql-drivers-register.c
 src/lib-storage/register/mail-storage-register.c
 src/lib-storage/register/mailbox-list-register.c
+src/log/log
 src/lmtp/lmtp
 src/master/dovecot
 src/master/ssl-build-param
--- a/TODO	Thu Apr 23 14:07:45 2009 -0400
+++ b/TODO	Thu Apr 23 19:53:44 2009 -0400
@@ -1,21 +1,30 @@
+ - mail_max_userip_connections
+ - dovecot stop, dovecot reload
+ - make sure status/log messages which are important get through to the server
+ - log prefixes work in a weird way now. failures.c prefixes are used only
+   when not doing internal logging. that won't work in future..
+ - library dependency tracking still broken. .la changes get noticed,
+   .libs/*.a changes not
+ - o_stream_set_file_path()
  - dict pooling
  - config rewrite
-   - go through mail-process.c. nfs test?
-   - support !include and !include_try
-   - add back all setting verification code from master
-   - master_user, acl_groups are now plugin envs.. is it correct?
+   - dovecot -o setting=something overriding
+   - fix dovecot -n, dovecot -a
+   - export only the stuff that's actually wanted by the service. requires
+     some kind of dependencies or something to get e.g. imap to load also mail.
 
 	/* currently non-external transactions can be applied multiple times,
 	   causing multiple increments. */
 	//FIXME:i_assert((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0);
   ^ appears to work now though, probably because of the added syncing stuff..
 
+ - dbox: keep track of total bytes in dbox storage in map header. also if
+   possible keep track of refcount=0 bytes. use these to optimize checks.
  - dbox: save some stuff to map index header so we don't need to keep retrying
    it. like when saving the lowest file_id which to bother checking.
  - transaction log corruption should make sure dovecot.index is rewritten
    and perhaps not delete the file.
  - dbox: test crash-fixing
- - dbox: cleanup tool / remove automatic cleanup. resyncing tool.
  - use backup index in mail_index_fsck()
  - dbox: mail_index_fsck() should perhaps cause dbox to be resynced?
 
@@ -46,7 +55,7 @@
  - add anonymous environment for anon logins
  - fs quota: getquotaroot inbox vs. other-box should return different quotas
    if two quotas are defined
- - deliver: log mailbox name using utf8, not mutf7
+ - lda: log mailbox name using utf8, not mutf7
  - new primes code: are hash tables now being resized too often?
  - auth_log_prefix setting similar to mail_log_prefix
  - LDAP attrs: uid=foo,uid=bar doesn't work
@@ -117,7 +126,7 @@
    - Deny deleting non-empty mailboxes
    - Disable IDLE "still here" notifications
 
- - maildir+pop3/deliver fast updates:
+ - maildir+pop3 fast updates:
    - with locking enabled, pop3 could just keep the one and same sync lock and
      do the whole thing using sync transaction
    - don't update dovecot-uidlist if dovecot.index.cache doesn't exist /
@@ -129,7 +138,7 @@
  - maildir
    - don't allow more than 26 keywords
    - physical separator could be configurable
-   - deliver+maildir: if new mails are in new/ or cur/ they're not added to
+   - lda+maildir: if new mails are in new/ or cur/ they're not added to
      dovecot-uidlist but newly saved mails are, so UIDs will be in wrong order
    - maildir_copy_with_hardlinks: We're currently first hardlinking to tmp/ and
      then rename()ing. This wouldn't be necessary if uidlist syncing noticed
@@ -150,7 +159,6 @@
    http://www.dovecot.org/list/dovecot/2007-April/021532.html
  - EXPUNGE command in read-only mailbox should give an error message if
    there are messages marked as \Deleted?
- - dovecot -o setting=something overriding
  - file_cache: we're growing the mmap in page size blocks, which is horribly
    slow if mremap() doesn't exist.
  - login_max_processes_count shouldn't count proxying processes
--- a/configure.in	Thu Apr 23 14:07:45 2009 -0400
+++ b/configure.in	Thu Apr 23 19:53:44 2009 -0400
@@ -1,5 +1,5 @@
 AC_PREREQ([2.59])
-AC_INIT([Dovecot],[1.3.UNSTABLE],[dovecot@dovecot.org])
+AC_INIT([Dovecot],[2.0.UNSTABLE],[dovecot@dovecot.org])
 AC_CONFIG_SRCDIR([src])
 
 AM_INIT_AUTOMAKE([foreign])
@@ -2200,7 +2200,7 @@
   LIBDOVECOT_STORAGE='$(top_builddir)/src/lib-storage/libdovecot-storage.la'
   LIBDOVECOT_LOGIN='$(top_builddir)/src/login-common/libdovecot-login.la'
 else
-  LIBDOVECOT='$(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib/liblib.la $(LIBICONV)'
+  LIBDOVECOT='$(top_builddir)/src/lib-master/libmaster.la $(top_builddir)/src/lib-settings/libsettings.la $(top_builddir)/src/lib-dict/libdict.la $(top_builddir)/src/lib-imap/libimap.la $(top_builddir)/src/lib-mail/libmail.la $(top_builddir)/src/lib-auth/libauth.la $(top_builddir)/src/lib-charset/libcharset.la $(top_builddir)/src/lib/liblib.la $(LIBICONV)'
   LIBDOVECOT_STORAGE_LAST='$(top_builddir)/src/lib-storage/list/libstorage_list.la $(top_builddir)/src/lib-storage/index/libstorage_index.la $(top_builddir)/src/lib-storage/libstorage.la $(top_builddir)/src/lib-index/libindex.la'
   LIBDOVECOT_STORAGE_FIRST='$(top_builddir)/src/lib-storage/libstorage_service.la $(top_builddir)/src/lib-storage/register/libstorage_register.la'
   LIBDOVECOT_STORAGE="$LIBDOVECOT_STORAGE_FIRST $LINKED_STORAGE_LIBS $LIBDOVECOT_STORAGE_LAST"
@@ -2376,6 +2376,7 @@
 src/auth/Makefile
 src/config/Makefile
 src/lda/Makefile
+src/log/Makefile
 src/lmtp/Makefile
 src/dict/Makefile
 src/imap/Makefile
--- a/src/Makefile.am	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/Makefile.am	Thu Apr 23 19:53:44 2009 -0400
@@ -27,6 +27,7 @@
 	pop3 \
 	lda \
 	lmtp \
+	log \
 	config \
 	tests \
 	util \
--- a/src/auth/Makefile.am	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/Makefile.am	Thu Apr 23 19:53:44 2009 -0400
@@ -23,6 +23,7 @@
 	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-ntlm \
 	-I$(top_srcdir)/src/lib-otp \
+	-I$(top_srcdir)/src/lib-master \
 	-DAUTH_MODULE_DIR=\""$(auth_moduledir)"\" \
 	-DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
 	-DPKG_RUNDIR=\""$(rundir)"\" \
@@ -44,7 +45,7 @@
 	$(LIBDOVECOT_SQL) \
 	$(LIBDOVECOT)
 
-dovecot_auth_LDADD = $(dovecot_auth_libs) $(MODULE_LIBS) $(AUTH_LIBS)
+dovecot_auth_LDADD = $(dovecot_auth_libs) $(AUTH_LIBS) $(MODULE_LIBS)
 dovecot_auth_DEPENDENCIES = $(dovecot_auth_libs)
 
 ldap_sources = db-ldap.c passdb-ldap.c userdb-ldap.c
@@ -54,7 +55,6 @@
 	auth-cache.c \
 	auth-client-connection.c \
 	auth-master-connection.c \
-	auth-master-listener.c \
 	auth-request.c \
 	auth-request-handler.c \
 	auth-settings.c \
@@ -111,7 +111,6 @@
 	auth-client-interface.h \
 	auth-master-interface.h \
 	auth-master-connection.h \
-	auth-master-listener.h \
 	auth-request.h \
 	auth-request-handler.h \
 	auth-settings.h \
--- a/src/auth/auth-client-connection.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/auth-client-connection.c	Thu Apr 23 19:53:44 2009 -0400
@@ -1,11 +1,12 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
+#include "array.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
 #include "network.h"
-#include "array.h"
+#include "hostpid.h"
 #include "str.h"
 #include "str-sanitize.h"
 #include "safe-memset.h"
@@ -13,15 +14,16 @@
 #include "auth-request-handler.h"
 #include "auth-client-interface.h"
 #include "auth-client-connection.h"
-#include "auth-master-listener.h"
 #include "auth-master-connection.h"
 
 #include <stdlib.h>
 
 #define OUTBUF_THROTTLE_SIZE (1024*50)
 
+static ARRAY_DEFINE(auth_client_connections, struct auth_client_connection *);
+static struct timeout *to_clients;
+
 static void auth_client_connection_unref(struct auth_client_connection **_conn);
-
 static void auth_client_input(struct auth_client_connection *conn);
 
 static const char *reply_line_hide_pass(const char *line)
@@ -90,7 +92,7 @@
 		return FALSE;
 	}
 
-	old = auth_client_connection_lookup(conn->listener, pid);
+	old = auth_client_connection_lookup(pid);
 	if (old != NULL) {
 		/* already exists. it's possible that it just reconnected,
 		   see if the old connection is still there. */
@@ -112,7 +114,7 @@
 	conn->request_handler =
 		auth_request_handler_create(conn->auth,
 			auth_callback, conn,
-			array_count(&conn->listener->masters) != 0 ?
+			array_count(&auth_master_connections) != 0 ?
 			auth_master_request_callback : NULL);
 	auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
 
@@ -261,7 +263,7 @@
 }
 
 struct auth_client_connection *
-auth_client_connection_create(struct auth_master_listener *listener, int fd)
+auth_client_connection_create(struct auth *auth, int fd)
 {
 	static unsigned int connect_uid_counter = 0;
 	struct auth_client_connection *conn;
@@ -269,8 +271,7 @@
 	string_t *str;
 
 	conn = i_new(struct auth_client_connection, 1);
-	conn->auth = listener->auth;
-	conn->listener = listener;
+	conn->auth = auth;
 	conn->refcount = 1;
 	conn->connect_uid = ++connect_uid_counter;
 
@@ -281,13 +282,13 @@
 	o_stream_set_flush_callback(conn->output, auth_client_output, conn);
 	conn->io = io_add(fd, IO_READ, auth_client_input, conn);
 
-	array_append(&listener->clients, &conn, 1);
+	array_append(&auth_client_connections, &conn, 1);
 
 	str = t_str_new(128);
-	str_printfa(str, "VERSION\t%u\t%u\nSPID\t%u\nCUID\t%u\nDONE\n",
+	str_printfa(str, "VERSION\t%u\t%u\nSPID\t%s\nCUID\t%u\nDONE\n",
                     AUTH_CLIENT_PROTOCOL_MAJOR_VERSION,
                     AUTH_CLIENT_PROTOCOL_MINOR_VERSION,
-		    listener->pid, conn->connect_uid);
+		    my_pid, conn->connect_uid);
 
 	iov[0].iov_base = str_data(conn->auth->mech_handshake);
 	iov[0].iov_len = str_len(conn->auth->mech_handshake);
@@ -310,10 +311,10 @@
 	if (conn->fd == -1)
 		return;
 
-	clients = array_get(&conn->listener->clients, &count);
+	clients = array_get(&auth_client_connections, &count);
 	for (i = 0; i < count; i++) {
 		if (clients[i] == conn) {
-			array_delete(&conn->listener->clients, i, 1);
+			array_delete(&auth_client_connections, i, 1);
 			break;
 		}
 	}
@@ -347,13 +348,12 @@
 }
 
 struct auth_client_connection *
-auth_client_connection_lookup(struct auth_master_listener *listener,
-			      unsigned int pid)
+auth_client_connection_lookup(unsigned int pid)
 {
 	struct auth_client_connection *const *clients;
 	unsigned int i, count;
 
-	clients = array_get(&listener->clients, &count);
+	clients = array_get(&auth_client_connections, &count);
 	for (i = 0; i < count; i++) {
 		if (clients[i]->pid == pid)
 			return clients[i];
@@ -362,12 +362,12 @@
 	return NULL;
 }
 
-static void request_timeout(struct auth_master_listener *listener)
+static void request_timeout(void *context ATTR_UNUSED)
 {
 	struct auth_client_connection *const *clients;
 	unsigned int i, count;
 
-	clients = array_get(&listener->clients, &count);
+	clients = array_get(&auth_client_connections, &count);
 	for (i = 0; i < count; i++) {
 		if (clients[i]->request_handler != NULL) {
 			auth_request_handler_check_timeouts(
@@ -376,13 +376,22 @@
 	}
 }
 
-void auth_client_connections_init(struct auth_master_listener *listener)
+void auth_client_connections_init(void)
 {
-	listener->to_clients = timeout_add(5000, request_timeout, listener);
+	i_array_init(&auth_client_connections, 16);
+	to_clients = timeout_add(5000, request_timeout, NULL);
 }
 
-void auth_client_connections_deinit(struct auth_master_listener *listener)
+void auth_client_connections_deinit(void)
 {
-	if (listener->to_clients != NULL)
-		timeout_remove(&listener->to_clients);
+	struct auth_client_connection **clients;
+	unsigned int i, count;
+
+	if (to_clients != NULL)
+		timeout_remove(&to_clients);
+
+	clients = array_get_modifiable(&auth_client_connections, &count);
+	for (i = count; i > 0; i--)
+		auth_client_connection_destroy(&clients[i-1]);
+	array_free(&auth_client_connections);
 }
--- a/src/auth/auth-client-connection.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/auth-client-connection.h	Thu Apr 23 19:53:44 2009 -0400
@@ -3,7 +3,6 @@
 
 struct auth_client_connection {
 	struct auth *auth;
-	struct auth_master_listener *listener;
 	int refcount;
 
 	int fd;
@@ -19,14 +18,13 @@
 };
 
 struct auth_client_connection *
-auth_client_connection_create(struct auth_master_listener *listener, int fd);
+auth_client_connection_create(struct auth *auth, int fd);
 void auth_client_connection_destroy(struct auth_client_connection **conn);
 
 struct auth_client_connection *
-auth_client_connection_lookup(struct auth_master_listener *listener,
-			      unsigned int pid);
+auth_client_connection_lookup(unsigned int pid);
 
-void auth_client_connections_init(struct auth_master_listener *listener);
-void auth_client_connections_deinit(struct auth_master_listener *listener);
+void auth_client_connections_init(void);
+void auth_client_connections_deinit(void);
 
 #endif
--- a/src/auth/auth-master-connection.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/auth-master-connection.c	Thu Apr 23 19:53:44 2009 -0400
@@ -2,19 +2,19 @@
 
 #include "common.h"
 #include "array.h"
-#include "buffer.h"
 #include "hash.h"
 #include "str.h"
+#include "hostpid.h"
 #include "str-sanitize.h"
 #include "ioloop.h"
+#include "network.h"
 #include "istream.h"
 #include "ostream.h"
-#include "network.h"
+#include "master-service.h"
 #include "userdb.h"
 #include "auth-request-handler.h"
 #include "auth-master-interface.h"
 #include "auth-client-connection.h"
-#include "auth-master-listener.h"
 #include "auth-master-connection.h"
 
 #include <unistd.h>
@@ -29,6 +29,8 @@
 	struct auth_request *auth_request;
 };
 
+ARRAY_TYPE(auth_master_connections) auth_master_connections;
+
 void auth_master_request_callback(struct auth_stream_reply *reply,
 				  void *context)
 {
@@ -38,7 +40,7 @@
 
 	reply_str = auth_stream_reply_export(reply);
 
-	if (conn->listener->auth->set->debug)
+	if (conn->auth->set->debug)
 		i_info("master out: %s", reply_str);
 
 	iov[0].iov_base = reply_str;
@@ -67,7 +69,7 @@
 	client_pid = (unsigned int)strtoul(list[1], NULL, 10);
 	client_id = (unsigned int)strtoul(list[2], NULL, 10);
 
-	client_conn = auth_client_connection_lookup(conn->listener, client_pid);
+	client_conn = auth_client_connection_lookup(client_pid);
 	if (client_conn == NULL) {
 		i_error("Master requested auth for nonexisting client %u",
 			client_pid);
@@ -105,7 +107,7 @@
 		break;
 	}
 
-	if (conn->listener->auth->set->debug)
+	if (conn->auth->set->debug)
 		i_info("master out: %s", str_c(str));
 
 	str_append_c(str, '\n');
@@ -126,7 +128,7 @@
 		return FALSE;
 	}
 
-	auth_request = auth_request_new_dummy(conn->listener->auth);
+	auth_request = auth_request_new_dummy(conn->auth);
 	auth_request->id = (unsigned int)strtoul(list[0], NULL, 10);
 	auth_request->context = conn;
 
@@ -163,7 +165,7 @@
 static bool
 auth_master_input_line(struct auth_master_connection *conn, const char *line)
 {
-	if (conn->listener->auth->set->debug)
+	if (conn->auth->set->debug)
 		i_info("master in: %s", line);
 
 	if (strncmp(line, "REQUEST\t", 8) == 0)
@@ -248,41 +250,34 @@
 }
 
 struct auth_master_connection *
-auth_master_connection_create(struct auth_master_listener *listener, int fd)
+auth_master_connection_create(struct auth *auth, int fd)
 {
 	struct auth_master_connection *conn;
+	const char *line;
 
 	conn = i_new(struct auth_master_connection, 1);
-	conn->listener = listener;
 	conn->refcount = 1;
 	conn->fd = fd;
+	conn->auth = auth;
 	conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
 	conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
 	o_stream_set_flush_callback(conn->output, master_output, conn);
 	conn->io = io_add(fd, IO_READ, master_input, conn);
 
-	array_append(&listener->masters, &conn, 1);
-	return conn;
-}
-
-void auth_master_connection_send_handshake(struct auth_master_connection *conn)
-{
-	const char *line;
-
-	if (conn->output == NULL)
-		return;
-
-	line = t_strdup_printf("VERSION\t%u\t%u\nSPID\t%u\n",
+	line = t_strdup_printf("VERSION\t%u\t%u\nSPID\t%s\n",
 			       AUTH_MASTER_PROTOCOL_MAJOR_VERSION,
 			       AUTH_MASTER_PROTOCOL_MINOR_VERSION,
-			       conn->listener->pid);
+			       my_pid);
 	(void)o_stream_send_str(conn->output, line);
+
+	array_append(&auth_master_connections, &conn, 1);
+	return conn;
 }
 
 void auth_master_connection_destroy(struct auth_master_connection **_conn)
 {
         struct auth_master_connection *conn = *_conn;
-        struct auth_master_connection *const *conns;
+        struct auth_master_connection *const *masters;
 	unsigned int i, count;
 
 	*_conn = NULL;
@@ -290,6 +285,14 @@
 		return;
 	conn->destroyed = TRUE;
 
+	masters = array_get(&auth_master_connections, &count);
+	for (i = 0; i < count; i++) {
+		if (masters[i] == conn) {
+			array_delete(&auth_master_connections, i, 1);
+			break;
+		}
+	}
+
 	if (conn->input != NULL)
 		i_stream_close(conn->input);
 	if (conn->output != NULL)
@@ -302,16 +305,7 @@
 		conn->fd = -1;
 	}
 
-	conns = array_get(&conn->listener->masters, &count);
-	for (i = 0; i < count; i++) {
-		if (conns[i] == conn) {
-			array_delete(&conn->listener->masters, i, 1);
-			break;
-		}
-	}
-	if (!standalone && auth_master_listeners_masters_left() == 0)
-		io_loop_stop(ioloop);
-
+        master_service_client_connection_destroyed(service);
 	auth_master_connection_unref(&conn);
 }
 
@@ -339,3 +333,19 @@
 
 	i_free(conn);
 }
+
+void auth_master_connections_init(void)
+{
+	i_array_init(&auth_master_connections, 16);
+}
+
+void auth_master_connections_deinit(void)
+{
+	struct auth_master_connection **masters;
+	unsigned int i, count;
+
+	masters = array_get_modifiable(&auth_master_connections, &count);
+	for (i = count; i > 0; i--)
+		auth_master_connection_destroy(&masters[i-1]);
+	array_free(&auth_master_connections);
+}
--- a/src/auth/auth-master-connection.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/auth-master-connection.h	Thu Apr 23 19:53:44 2009 -0400
@@ -4,7 +4,7 @@
 struct auth_stream_reply;
 
 struct auth_master_connection {
-	struct auth_master_listener *listener;
+	struct auth *auth;
 	int refcount;
 
 	int fd;
@@ -15,18 +15,21 @@
 	unsigned int version_received:1;
 	unsigned int destroyed:1;
 };
+ARRAY_DEFINE_TYPE(auth_master_connections, struct auth_master_connection *);
+
+extern ARRAY_TYPE(auth_master_connections) auth_master_connections;
 
 struct auth_master_connection *
-auth_master_connection_create(struct auth_master_listener *listener, int fd);
+auth_master_connection_create(struct auth *auth, int fd);
 void auth_master_connection_destroy(struct auth_master_connection **conn);
 
 void auth_master_connection_ref(struct auth_master_connection *conn);
 void auth_master_connection_unref(struct auth_master_connection **conn);
 
-void auth_master_connection_send_handshake(struct auth_master_connection *conn);
-void auth_master_connections_send_handshake(void);
-
 void auth_master_request_callback(struct auth_stream_reply *reply,
 				  void *context);
 
+void auth_master_connections_init(void);
+void auth_master_connections_deinit(void);
+
 #endif
--- a/src/auth/auth-master-interface.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/auth-master-interface.h	Thu Apr 23 19:53:44 2009 -0400
@@ -1,6 +1,8 @@
 #ifndef AUTH_MASTER_INTERFACE_H
 #define AUTH_MASTER_INTERFACE_H
 
+#include "master-interface.h"
+
 /* Major version changes are not backwards compatible,
    minor version numbers can be ignored. */
 #define AUTH_MASTER_PROTOCOL_MAJOR_VERSION 1
--- a/src/auth/auth-master-listener.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,179 +0,0 @@
-/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "ioloop.h"
-#include "network.h"
-#include "istream.h"
-#include "ostream.h"
-#include "auth-master-listener.h"
-#include "auth-master-connection.h"
-#include "auth-client-connection.h"
-
-#include <unistd.h>
-
-struct auth_master_listener_socket {
-	struct auth_master_listener *listener;
-
-	enum listener_type type;
-	int fd;
-	char *path;
-	struct io *io;
-};
-
-static ARRAY_DEFINE(master_listeners, struct auth_master_listener *);
-
-struct auth_master_listener *auth_master_listener_create(struct auth *auth)
-{
-	struct auth_master_listener *listener;
-
-	listener = i_new(struct auth_master_listener, 1);
-	listener->auth = auth;
-	listener->pid = (unsigned int)getpid();
-	i_array_init(&listener->sockets, 16);
-	i_array_init(&listener->masters, 16);
-	i_array_init(&listener->clients, 16);
-	auth_client_connections_init(listener);
-
-	array_append(&master_listeners, &listener, 1);
-	return listener;
-}
-
-static void
-auth_master_listener_socket_free(struct auth_master_listener_socket *s)
-{
-	if (s->path != NULL) {
-		(void)unlink(s->path);
-		i_free(s->path);
-	}
-
-	io_remove(&s->io);
-	net_disconnect(s->fd);
-	i_free(s);
-}
-
-void auth_master_listener_destroy(struct auth_master_listener *listener)
-{
-	struct auth_master_listener *const *listeners;
-	struct auth_master_listener_socket **sockets;
-	struct auth_master_connection **masters;
-	struct auth_client_connection **clients;
-	unsigned int i, count;
-
-	listeners = array_get(&master_listeners, &count);
-	for (i = 0; i < count; i++) {
-		if (listeners[i] == listener) {
-			array_delete(&master_listeners, i, 1);
-			break;
-		}
-	}
-
-	sockets = array_get_modifiable(&listener->sockets, &count);
-	for (i = count; i > 0; i--)
-		auth_master_listener_socket_free(sockets[i-1]);
-
-	masters = array_get_modifiable(&listener->masters, &count);
-	for (i = count; i > 0; i--)
-		auth_master_connection_destroy(&masters[i-1]);
-
-	clients = array_get_modifiable(&listener->clients, &count);
-	for (i = count; i > 0; i--)
-		auth_client_connection_destroy(&clients[i-1]);
-
-        auth_client_connections_deinit(listener);
-	array_free(&listener->sockets);
-	array_free(&listener->masters);
-	array_free(&listener->clients);
-	i_free(listener);
-}
-
-static void auth_master_listener_accept(struct auth_master_listener_socket *s)
-{
-	struct auth_master_connection *master;
-	int fd;
-
-	fd = net_accept(s->fd, NULL, NULL);
-	if (fd < 0) {
-		if (fd < -1)
-			i_error("accept(type %d) failed: %m", s->type);
-	} else {
-		net_set_nonblock(fd, TRUE);
-
-		switch (s->type) {
-		case LISTENER_CLIENT:
-			(void)auth_client_connection_create(s->listener, fd);
-			break;
-		case LISTENER_MASTER:
-			/* we'll just replace the previous master.. */
-			master = auth_master_connection_create(s->listener, fd);
-			auth_master_connection_send_handshake(master);
-			break;
-		}
-	}
-}
-
-void auth_master_listener_add(struct auth_master_listener *listener,
-			      int fd, const char *path,
-			      enum listener_type type)
-{
-	struct auth_master_listener_socket *s;
-
-	s = i_new(struct auth_master_listener_socket, 1);
-	s->listener = listener;
-	s->fd = fd;
-	s->path = i_strdup(path);
-	s->type = type;
-	s->io = io_add(fd, IO_READ, auth_master_listener_accept, s);
-
-	array_append(&listener->sockets, &s, 1);
-}
-
-static void
-auth_master_listener_send_handshakes(struct auth_master_listener *listener)
-{
-        struct auth_master_connection *const *masters;
-	unsigned int i, count;
-
-	masters = array_get(&listener->masters, &count);
-	for (i = 0; i < count; i++)
-		auth_master_connection_send_handshake(masters[i]);
-}
-
-void auth_master_listeners_send_handshake(void)
-{
-        struct auth_master_listener *const *listeners;
-	unsigned int i, count;
-
-	listeners = array_get(&master_listeners, &count);
-	for (i = 0; i < count; i++)
-		auth_master_listener_send_handshakes(listeners[i]);
-}
-
-bool auth_master_listeners_masters_left(void)
-{
-        struct auth_master_listener *const *listeners;
-	unsigned int i, count;
-
-	listeners = array_get(&master_listeners, &count);
-	for (i = 0; i < count; i++) {
-		if (array_count(&listeners[i]->masters) > 0)
-			return TRUE;
-	}
-	return FALSE;
-}
-
-void auth_master_listeners_init(void)
-{
-	i_array_init(&master_listeners, 2);
-}
-
-void auth_master_listeners_deinit(void)
-{
-        struct auth_master_listener **listeners;
-	unsigned int i, count;
-
-	listeners = array_get_modifiable(&master_listeners, &count);
-	for (i = count; i > 0; i--)
-		auth_master_listener_destroy(listeners[i-1]);
-	array_free(&master_listeners);
-}
--- a/src/auth/auth-master-listener.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-#ifndef AUTH_MASTER_LISTENER_H
-#define AUTH_MASTER_LISTENER_H
-
-enum listener_type {
-	LISTENER_MASTER,
-	LISTENER_CLIENT
-};
-
-struct auth_master_listener {
-	struct auth *auth;
-	unsigned int pid;
-
-	ARRAY_DEFINE(sockets, struct auth_master_listener_socket *);
-	ARRAY_DEFINE(masters, struct auth_master_connection *);
-	ARRAY_DEFINE(clients, struct auth_client_connection *);
-
-	struct timeout *to_clients;
-};
-
-struct auth_master_listener *auth_master_listener_create(struct auth *auth);
-void auth_master_listener_destroy(struct auth_master_listener *listener);
-
-void auth_master_listener_add(struct auth_master_listener *listener,
-			      int fd, const char *path,
-			      enum listener_type type);
-
-void auth_master_listeners_send_handshake(void);
-bool auth_master_listeners_masters_left(void);
-
-void auth_master_listeners_init(void);
-void auth_master_listeners_deinit(void);
-
-#endif
--- a/src/auth/auth-settings.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/auth-settings.c	Thu Apr 23 19:53:44 2009 -0400
@@ -2,114 +2,15 @@
 
 #include "lib.h"
 #include "array.h"
-#include "hostpid.h"
 #include "settings-parser.h"
+#include "master-service-settings.h"
 #include "auth-settings.h"
 
 #include <stddef.h>
 
-extern struct setting_parser_info auth_socket_setting_parser_info;
 extern struct setting_parser_info auth_setting_parser_info;
 extern struct setting_parser_info auth_root_setting_parser_info;
 
-static bool auth_settings_check(void *_set, pool_t pool, const char **error_r);
-
-#undef DEF
-#define DEF(type, name) \
-	{ type, #name, offsetof(struct auth_socket_unix_settings, name), NULL }
-
-static struct setting_define auth_socket_client_setting_defines[] = {
-	DEF(SET_STR, path),
-	DEF(SET_UINT, mode),
-	DEF(SET_STR, user),
-	DEF(SET_STR, group),
-
-	SETTING_DEFINE_LIST_END
-};
-
-static struct auth_socket_unix_settings auth_socket_client_default_settings = {
-	MEMBER(path) "auth-client",
-	MEMBER(mode) 0660,
-	MEMBER(user) "",
-	MEMBER(group) ""
-};
-
-struct setting_parser_info auth_socket_client_setting_parser_info = {
-	MEMBER(defines) auth_socket_client_setting_defines,
-	MEMBER(defaults) &auth_socket_client_default_settings,
-
-	MEMBER(parent) &auth_socket_setting_parser_info,
-	MEMBER(dynamic_parsers) NULL,
-
-	MEMBER(parent_offset) (size_t)-1,
-	MEMBER(type_offset) (size_t)-1,
-	MEMBER(struct_size) sizeof(struct auth_socket_unix_settings)
-};
-
-#undef DEF
-#define DEF(type, name) \
-	{ type, #name, offsetof(struct auth_socket_unix_settings, name), NULL }
-
-static struct setting_define auth_socket_master_setting_defines[] = {
-	DEF(SET_STR, path),
-	DEF(SET_UINT, mode),
-	DEF(SET_STR, user),
-	DEF(SET_STR, group),
-
-	SETTING_DEFINE_LIST_END
-};
-
-static struct auth_socket_unix_settings auth_socket_master_default_settings = {
-	MEMBER(path) "auth-master",
-	MEMBER(mode) 0660,
-	MEMBER(user) "",
-	MEMBER(group) ""
-};
-
-struct setting_parser_info auth_socket_master_setting_parser_info = {
-	MEMBER(defines) auth_socket_master_setting_defines,
-	MEMBER(defaults) &auth_socket_master_default_settings,
-
-	MEMBER(parent) &auth_socket_setting_parser_info,
-	MEMBER(dynamic_parsers) NULL,
-
-	MEMBER(parent_offset) (size_t)-1,
-	MEMBER(type_offset) (size_t)-1,
-	MEMBER(struct_size) sizeof(struct auth_socket_unix_settings)
-};
-
-#undef DEF
-#undef DEFLIST
-#define DEF(type, name) \
-	{ type, #name, offsetof(struct auth_socket_settings, name), NULL }
-#define DEFLIST(field, name, defines) \
-	{ SET_DEFLIST, name, offsetof(struct auth_socket_settings, field), defines }
-
-static struct setting_define auth_socket_setting_defines[] = {
-	DEF(SET_ENUM, type),
-
-	DEFLIST(clients, "client", &auth_socket_client_setting_parser_info),
-	DEFLIST(masters, "master", &auth_socket_master_setting_parser_info),
-
-	SETTING_DEFINE_LIST_END
-};
-
-static struct auth_socket_settings auth_socket_default_settings = {
-	MEMBER(type) "listen:connect"
-};
-
-struct setting_parser_info auth_socket_setting_parser_info = {
-	MEMBER(defines) auth_socket_setting_defines,
-	MEMBER(defaults) &auth_socket_default_settings,
-
-	MEMBER(parent) &auth_setting_parser_info,
-	MEMBER(dynamic_parsers) NULL,
-
-	MEMBER(parent_offset) (size_t)-1,
-	MEMBER(type_offset) offsetof(struct auth_socket_settings, type),
-	MEMBER(struct_size) sizeof(struct auth_socket_settings)
-};
-
 #undef DEF
 #define DEF(type, name) \
 	{ type, #name, offsetof(struct auth_passdb_settings, name), NULL }
@@ -191,7 +92,6 @@
 
 	DEF(SET_UINT, worker_max_count),
 
-	DEFLIST(sockets, "socket", &auth_socket_setting_parser_info),
 	DEFLIST(passdbs, "passdb", &auth_passdb_setting_parser_info),
 	DEFLIST(userdbs, "userdb", &auth_userdb_setting_parser_info),
 
@@ -227,7 +127,6 @@
 
 	MEMBER(worker_max_count) 30,
 
-	MEMBER(sockets) ARRAY_INIT,
 	MEMBER(passdbs) ARRAY_INIT,
 	MEMBER(userdbs) ARRAY_INIT
 };
@@ -242,7 +141,7 @@
 	MEMBER(parent_offset) offsetof(struct auth_settings, root),
 	MEMBER(type_offset) offsetof(struct auth_settings, name),
 	MEMBER(struct_size) sizeof(struct auth_settings),
-	MEMBER(check_func) auth_settings_check
+	MEMBER(check_func) NULL
 };
 
 #undef DEF
@@ -253,14 +152,12 @@
 	{ SET_DEFLIST, name, offsetof(struct auth_root_settings, field), defines }
 
 static struct setting_define auth_root_setting_defines[] = {
-	DEF(SET_STR, base_dir),
 	DEFLIST(auths, "auth", &auth_setting_parser_info),
 
 	SETTING_DEFINE_LIST_END
 };
 
 static struct auth_root_settings auth_root_default_settings = {
-	MEMBER(base_dir) PKG_RUNDIR,
 	MEMBER(auths) ARRAY_INIT
 };
 
@@ -276,76 +173,25 @@
 	MEMBER(struct_size) sizeof(struct auth_root_settings)
 };
 
-static pool_t settings_pool = NULL;
-
-static void fix_base_path(struct auth_settings *set, const char **str)
-{
-	if (*str != NULL && **str != '\0' && **str != '/') {
-		*str = p_strconcat(settings_pool,
-				   set->root->base_dir, "/", *str, NULL);
-	}
-}
-
-/* <settings checks> */
-static bool auth_settings_check(void *_set ATTR_UNUSED, pool_t pool ATTR_UNUSED,
-				const char **error_r ATTR_UNUSED)
+struct auth_settings *
+auth_settings_read(struct master_service *service, const char *name)
 {
-#ifndef CONFIG_BINARY
-	struct auth_settings *set = _set;
-	struct auth_socket_unix_settings *const *u;
-	struct auth_socket_settings *const *sockets;
-	unsigned int i, j, count, count2;
-
-	if (!array_is_created(&set->sockets))
-		return TRUE;
-
-	sockets = array_get(&set->sockets, &count);
-	for (i = 0; i < count; i++) {
-		if (array_is_created(&sockets[i]->masters)) {
-			u = array_get(&sockets[i]->masters, &count2);
-			for (j = 0; j < count2; j++)
-				fix_base_path(set, &u[j]->path);
-		}
-		if (array_is_created(&sockets[i]->clients)) {
-			u = array_get(&sockets[i]->clients, &count2);
-			for (j = 0; j < count2; j++)
-				fix_base_path(set, &u[j]->path);
-		}
-	}
-#endif
-	return TRUE;
-}
-/* </settings checks> */
-
-struct auth_settings *auth_settings_read(const char *name)
-{
-	struct setting_parser_context *parser;
+	static const struct setting_parser_info *set_roots[] = {
+		&auth_root_setting_parser_info,
+		NULL
+	};
+	const char *error;
+	void **sets;
+	struct auth_settings *const *auths;
 	struct auth_root_settings *set;
-	struct auth_settings *const *auths;
-	const char *error;
 	unsigned int i, count;
 
-	if (settings_pool == NULL)
-		settings_pool = pool_alloconly_create("auth settings", 1024);
-	else
-		p_clear(settings_pool);
-
-	parser = settings_parser_init(settings_pool,
-				      &auth_root_setting_parser_info,
-				      SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
-
-	auth_default_settings.gssapi_hostname = my_hostname;
+	if (master_service_settings_read(service, set_roots, NULL, FALSE,
+					 &error) < 0)
+		i_fatal("Error reading configuration: %s", error);
 
-	if (settings_parse_environ(parser) < 0) {
-		i_fatal("Error reading configuration: %s",
-			settings_parser_get_error(parser));
-	}
-
-	if (settings_parser_check(parser, settings_pool, &error) < 0)
-		i_fatal("Invalid settings: %s", error);
-
-	set = settings_parser_get(parser);
-	settings_parser_deinit(&parser);
+	sets = master_service_settings_get_others(service);
+	set = sets[0];
 
 	if (array_is_created(&set->auths)) {
 		auths = array_get(&set->auths, &count);
@@ -355,5 +201,4 @@
 		}
 	}
 	i_fatal("Error reading configuration: No auth section: %s", name);
-	return NULL;
 }
--- a/src/auth/auth-settings.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/auth-settings.h	Thu Apr 23 19:53:44 2009 -0400
@@ -1,19 +1,7 @@
 #ifndef AUTH_SETTINGS_H
 #define AUTH_SETTINGS_H
 
-struct auth_socket_unix_settings {
-	const char *path;
-	unsigned int mode;
-	const char *user;
-	const char *group;
-};
-
-struct auth_socket_settings {
-	const char *type;
-
-	ARRAY_DEFINE(clients, struct auth_socket_unix_settings *);
-	ARRAY_DEFINE(masters, struct auth_socket_unix_settings *);
-};
+struct master_service;
 
 struct auth_passdb_settings {
 	const char *driver;
@@ -55,17 +43,15 @@
 
 	unsigned int worker_max_count;
 
-	ARRAY_DEFINE(sockets, struct auth_socket_settings *);
 	ARRAY_DEFINE(passdbs, struct auth_passdb_settings *);
 	ARRAY_DEFINE(userdbs, struct auth_userdb_settings *);
 };
 
 struct auth_root_settings {
-	const char *base_dir;
-
 	ARRAY_DEFINE(auths, struct auth_settings *);
 };
 
-struct auth_settings *auth_settings_read(const char *name);
+struct auth_settings *
+auth_settings_read(struct master_service *service, const char *name);
 
 #endif
--- a/src/auth/auth-worker-client.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/auth-worker-client.c	Thu Apr 23 19:53:44 2009 -0400
@@ -7,6 +7,7 @@
 #include "istream.h"
 #include "ostream.h"
 #include "str.h"
+#include "master-service.h"
 #include "auth-request.h"
 #include "auth-worker-client.h"
 
@@ -504,7 +505,7 @@
 	net_disconnect(client->fd);
 	client->fd = -1;
 
-	io_loop_stop(ioloop);
+        master_service_client_connection_destroyed(service);
 }
 
 void auth_worker_client_unref(struct auth_worker_client **_client)
--- a/src/auth/common.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/common.h	Thu Apr 23 19:53:44 2009 -0400
@@ -4,12 +4,8 @@
 #include "lib.h"
 #include "auth.h"
 
-#define MASTER_SOCKET_FD 0
-#define CLIENT_LISTEN_FD 3
-#define WORKER_SERVER_FD 4
-
-extern struct ioloop *ioloop;
-extern bool standalone, worker, shutdown_request;
+extern struct master_service *service;
+extern bool worker, shutdown_request;
 extern time_t process_start_time;
 
 #endif
--- a/src/auth/db-ldap.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/db-ldap.c	Thu Apr 23 19:53:44 2009 -0400
@@ -18,6 +18,7 @@
 
 #include <stddef.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 #define HAVE_LDAP_SASL
 #ifdef HAVE_SASL_SASL_H
@@ -657,7 +658,7 @@
 		i_fatal("LDAP: Can't get connection fd: %s",
 			ldap_err2string(ret));
 	}
-	if (conn->fd <= CLIENT_LISTEN_FD) {
+	if (conn->fd <= STDERR_FILENO) {
 		/* Solaris LDAP library seems to be broken */
 		i_fatal("LDAP: Buggy LDAP library returned wrong fd: %d",
 			conn->fd);
--- a/src/auth/main.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/auth/main.c	Thu Apr 23 19:53:44 2009 -0400
@@ -6,11 +6,11 @@
 #include "network.h"
 #include "lib-signals.h"
 #include "restrict-access.h"
-#include "fd-close-on-exec.h"
 #include "child-wait.h"
 #include "sql-api.h"
 #include "module-dir.h"
 #include "randgen.h"
+#include "master-service.h"
 #include "password-scheme.h"
 #include "mech.h"
 #include "auth.h"
@@ -18,202 +18,30 @@
 #include "auth-worker-server.h"
 #include "auth-worker-client.h"
 #include "auth-master-interface.h"
-#include "auth-master-listener.h"
 #include "auth-master-connection.h"
 #include "auth-client-connection.h"
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
-#include <syslog.h>
-#include <pwd.h>
-#include <grp.h>
-#include <sys/stat.h>
+#include <sys/un.h>
 
-struct ioloop *ioloop;
-bool standalone = FALSE, worker = FALSE, shutdown_request = FALSE;
+enum auth_socket_type {
+	AUTH_SOCKET_UNKNOWN = 0,
+	AUTH_SOCKET_CLIENT,
+	AUTH_SOCKET_MASTER
+};
+
+struct master_service *service;
+bool worker = FALSE, shutdown_request = FALSE;
 time_t process_start_time;
 
 static struct module *modules = NULL;
 static struct auth *auth;
 static struct auth_worker_client *worker_client;
-
-static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
-{
-	/* warn about being killed because of some signal, except SIGINT (^C)
-	   which is too common at least while testing :) */
-	if (si->si_signo != SIGINT) {
-		i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
-			  si->si_signo, dec2str(si->si_pid),
-			  dec2str(si->si_uid),
-			  lib_signal_code_to_str(si->si_signo, si->si_code));
-	}
-	io_loop_stop(ioloop);
-}
-
-static void open_logfile(void)
-{
-	const char *env;
-
-	if (getenv("LOG_TO_MASTER") != NULL) {
-		i_set_failure_internal();
-		return;
-	}
-
-	if (getenv("USE_SYSLOG") != NULL) {
-		env = getenv("SYSLOG_FACILITY");
-		i_set_failure_syslog("dovecot-auth", LOG_NDELAY,
-				     env == NULL ? LOG_MAIL : atoi(env));
-	} else {
-		/* log to file or stderr */
-		i_set_failure_file(getenv("LOGFILE"), "dovecot-auth: ");
-	}
-
-	if (getenv("INFOLOGFILE") != NULL)
-		i_set_info_file(getenv("INFOLOGFILE"));
-
-	i_set_failure_timestamp_format(getenv("LOGSTAMP"));
-}
-
-static uid_t get_uid(const char *user)
-{
-	struct passwd *pw;
+static ARRAY_DEFINE(listen_fd_types, enum auth_socket_type);
 
-	if (*user == '\0')
-		return (uid_t)-1;
-	if (is_numeric(user, '\0'))
-		return strtoul(user, NULL, 10);
-
-	errno = 0;
-	if ((pw = getpwnam(user)) == NULL) {
-		if (errno != 0)
-			i_fatal("User '%s' lookup failed: %m", user);
-		setpwent();
-		if (getpwent() == NULL) {
-			if (errno != 0)
-				i_fatal("getpwent() failed: %m");
-			i_fatal("getpwnam() failed for some reason. "
-				"Is auth_process_size set to too low?");
-		}
-		i_fatal("User doesn't exist: %s", user);
-	}
-	return pw->pw_uid;
-}
-
-static gid_t get_gid(const char *group)
-{
-	struct group *gr;
-
-	if (*group == '\0')
-		return (gid_t)-1;
-	if (is_numeric(group, '\0'))
-		return strtoul(group, NULL, 10);
-
-	errno = 0;
-	if ((gr = getgrnam(group)) == NULL) {
-		if (errno != 0)
-			i_fatal("Group '%s' lookup failed: %m", group);
-		else
-			i_fatal("Group doesn't exist: %s", group);
-	}
-	return gr->gr_gid;
-}
-
-static int create_unix_listener(const struct auth_socket_unix_settings *set,
-				int backlog)
+static void main_preinit(struct auth_settings *set)
 {
-	mode_t old_umask;
-	uid_t uid;
-	gid_t gid;
-	int fd;
-
-	old_umask = umask((set->mode ^ 0777) & 0777);
-	fd = net_listen_unix_unlink_stale(set->path, backlog);
-	umask(old_umask);
-	if (fd == -1) {
-		if (errno == EADDRINUSE)
-			i_fatal("Socket already exists: %s", set->path);
-		else
-			i_fatal("net_listen_unix(%s) failed: %m", set->path);
-	}
-
-	uid = get_uid(set->user); gid = get_gid(set->group);
-	if (chown(set->path, uid, gid) < 0) {
-		i_fatal("chown(%s, %s(%s), %s(%s)) failed: %m",
-			set->path, dec2str(uid), set->user,
-			dec2str(gid), set->group);
-	}
-	return fd;
-}
-
-static void
-add_extra_unix_listeners(struct auth_master_listener *listener,
-			 struct auth_socket_unix_settings *const *sets,
-			 unsigned int count, enum listener_type type)
-{
-	unsigned int i;
-	int fd;
-
-	for (i = 0; i < count; i++) {
-		fd = create_unix_listener(sets[i], 128);
-		auth_master_listener_add(listener, fd, sets[i]->path, type);
-	}
-}
-
-static void add_extra_listeners(struct auth *auth)
-{
-	struct auth_master_listener *listener;
-	struct auth_socket_settings *const *sockets;
-	struct auth_socket_unix_settings *const *unix_sockets;
-	unsigned int i, count, count2;
-
-	if (!array_is_created(&auth->set->sockets))
-		return;
-
-	sockets = array_get(&auth->set->sockets, &count);
-	for (i = 0; i < count; i++) {
-		if (strcmp(sockets[i]->type, "listen") != 0)
-			continue;
-
-		listener = auth_master_listener_create(auth);
-
-		if (array_is_created(&sockets[i]->masters)) {
-			unix_sockets = array_get(&sockets[i]->masters, &count2);
-			add_extra_unix_listeners(listener, unix_sockets, count2,
-						 LISTENER_MASTER);
-		}
-		if (array_is_created(&sockets[i]->clients)) {
-			unix_sockets = array_get(&sockets[i]->clients, &count2);
-			add_extra_unix_listeners(listener, unix_sockets, count2,
-						 LISTENER_CLIENT);
-		}
-	}
-}
-
-static void drop_privileges(void)
-{
-	const char *version, *name;
-
-	version = getenv("DOVECOT_VERSION");
-	if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) {
-		i_fatal("Dovecot version mismatch: "
-			"Master is v%s, dovecot-auth is v"PACKAGE_VERSION" "
-			"(if you don't care, set version_ignore=yes)", version);
-	}
-
-	standalone = getenv("DOVECOT_MASTER") == NULL;
-	if (standalone && getenv("AUTH_1") == NULL) {
-		i_fatal("dovecot-auth is usually started through "
-			"dovecot master process. If you wish to run "
-			"it standalone, you'll need to set AUTH_* "
-			"environment variables (AUTH_1 isn't set).");
-	}
-	name = getenv("AUTH_NAME");
-	if (name == NULL)
-		i_fatal("Missing AUTH_NAME environment");
-
-	open_logfile();
-
 	/* Open /dev/urandom before chrooting */
 	random_init();
 
@@ -225,30 +53,22 @@
 	   only by root. Also load all modules here. */
 	passdbs_init();
 	userdbs_init();
-	modules = module_dir_load(AUTH_MODULE_DIR, NULL, TRUE, version);
+	modules = module_dir_load(AUTH_MODULE_DIR, NULL, TRUE,
+				  master_service_get_version_string(service));
 	module_dir_init(modules);
-	auth = auth_preinit(auth_settings_read(name));
-	auth_master_listeners_init();
-	if (!worker)
-		add_extra_listeners(auth);
+	auth = auth_preinit(set);
 
 	/* Password lookups etc. may require roots, allow it. */
 	restrict_access_by_env(NULL, FALSE);
 	restrict_access_allow_coredumps(TRUE);
 }
 
-static void main_init(bool nodaemon)
+static void main_init(void)
 {
-	struct auth_master_listener *listener;
+	i_array_init(&listen_fd_types, 8);
 
         process_start_time = ioloop_time;
 
-	lib_signals_init();
-        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
-        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
-        lib_signals_ignore(SIGPIPE, TRUE);
-        lib_signals_ignore(SIGALRM, FALSE);
-
 	/* If auth caches aren't used, just ignore these signals */
 	lib_signals_ignore(SIGHUP, TRUE);
 	lib_signals_ignore(SIGUSR2, TRUE);
@@ -258,40 +78,16 @@
 	password_schemes_init();
 	auth_init(auth);
 	auth_request_handler_init();
+	auth_master_connections_init();
+	auth_client_connections_init();
 
 	if (worker) {
-		worker_client =
-			auth_worker_client_create(auth, WORKER_SERVER_FD);
-		return;
+		/* workers have only a single connection from the master
+		   auth process */
+		master_service_set_client_limit(service, 1);
+	} else if (getenv("MASTER_AUTH_FD") != NULL) {
+		(void)auth_master_connection_create(auth, MASTER_AUTH_FD);
 	}
-
-	if (getenv("DOVECOT_MASTER") == NULL) {
-		/* starting standalone */
-		if (!nodaemon) {
-			switch (fork()) {
-			case -1:
-				i_fatal("fork() failed: %m");
-			case 0:
-				break;
-			default:
-				exit(0);
-			}
-
-			if (setsid() < 0)
-				i_fatal("setsid() failed: %m");
-
-			if (chdir("/") < 0)
-				i_fatal("chdir(/) failed: %m");
-		}
-	} else {
-		listener = auth_master_listener_create(auth);
-		(void)auth_master_connection_create(listener, MASTER_SOCKET_FD);
-		auth_master_listener_add(listener, CLIENT_LISTEN_FD,
-					 NULL, LISTENER_CLIENT);
-	}
-
-	/* everything initialized, notify masters that all is well */
-	auth_master_listeners_send_handshake();
 }
 
 static void main_deinit(void)
@@ -301,8 +97,9 @@
 	else
 		auth_request_handler_flush_failures(TRUE);
 
+	auth_client_connections_deinit();
+	auth_master_connections_deinit();
         auth_worker_server_deinit();
-	auth_master_listeners_deinit();
 
 	module_dir_unload(&modules);
 	userdbs_deinit();
@@ -314,41 +111,88 @@
 	sql_drivers_deinit();
 	random_deinit();
 
-	child_wait_deinit();
-	lib_signals_deinit();
-	closelog();
+	array_free(&listen_fd_types);
+}
+
+static void worker_connected(const struct master_service_connection *conn)
+{
+        auth_worker_client_create(auth, conn->fd);
 }
 
-int main(int argc ATTR_UNUSED, char *argv[])
+static void client_connected(const struct master_service_connection *conn)
 {
-	bool foreground = FALSE;
+	enum auth_socket_type *type;
+
+	if (worker) {
+		worker_client = auth_worker_client_create(auth, conn->fd);
+		return;
+	}
 
-#ifdef DEBUG
-	if (getenv("GDB") == NULL)
-		fd_debug_verify_leaks(WORKER_SERVER_FD + 1, 1024);
-#endif
-	/* NOTE: we start rooted, so keep the code minimal until
-	   restrict_access_by_env() is called */
-	lib_init();
-	ioloop = io_loop_create();
+	type = array_idx_modifiable(&listen_fd_types, conn->listen_fd);
+	if (*type == AUTH_SOCKET_UNKNOWN) {
+		/* figure out if this is a server or network socket by
+		   checking the socket path name. */
+		struct sockaddr_un sa;
+		socklen_t addrlen = sizeof(sa);
+		size_t len;
 
-	while (argv[1] != NULL) {
-		if (strcmp(argv[1], "-F") == 0)
-			foreground = TRUE;
-		else if (strcmp(argv[1], "-w") == 0)
-			worker = TRUE;
-		argv++;
+		if (getsockname(conn->listen_fd, (void *)&sa, &addrlen) < 0)
+			i_fatal("getsockname(%d) failed: %m", conn->listen_fd);
+		if (sa.sun_family != AF_UNIX) {
+			i_fatal("getsockname(%d) isn't UNIX socket",
+				conn->listen_fd);
+		}
+
+		len = strlen(sa.sun_path);
+		if (len > 7 && strcmp(sa.sun_path + len - 7, "-master") == 0)
+			*type = AUTH_SOCKET_MASTER;
+		else
+			*type = AUTH_SOCKET_CLIENT;
 	}
 
-	T_BEGIN {
-		drop_privileges();
-		main_init(foreground);
-	} T_END;
-        io_loop_run(ioloop);
+	switch (*type) {
+	case AUTH_SOCKET_MASTER:
+		(void)auth_master_connection_create(auth, conn->fd);
+		break;
+	case AUTH_SOCKET_CLIENT:
+		(void)auth_client_connection_create(auth, conn->fd);
+		break;
+	default:
+		i_unreached();
+	}
+}
+
+int main(int argc, char *argv[])
+{
+	const char *getopt_str, *auth_name = "default";
+	int c;
+
+	service = master_service_init("auth", 0, argc, argv);
+	master_service_init_log(service, "auth: ", 0);
+
+        getopt_str = t_strconcat("w", master_service_getopt_string(), NULL);
+	while ((c = getopt(argc, argv, getopt_str)) > 0) {
+		switch (c) {
+		case 'a':
+			auth_name = optarg;
+			break;
+		case 'w':
+			worker = TRUE;
+			break;
+		default:
+			if (!master_service_parse_option(service, c, optarg))
+				exit(FATAL_DEFAULT);
+			break;
+		}
+	}
+
+	main_preinit(auth_settings_read(service, auth_name));
+
+	master_service_init_finish(service);
+	main_init();
+	master_service_run(service, worker ? worker_connected :
+			   client_connected);
 	main_deinit();
-
-	io_loop_destroy(&ioloop);
-	lib_deinit();
-
+	master_service_deinit(&service);
         return 0;
 }
--- a/src/config/Makefile.am	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/config/Makefile.am	Thu Apr 23 19:53:44 2009 -0400
@@ -1,8 +1,11 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
 bin_PROGRAMS = doveconf
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-settings \
+	-I$(top_srcdir)/src/lib-master \
 	-DSYSCONFDIR=\""$(sysconfdir)"\" \
 	-DPKG_RUNDIR=\""$(rundir)"\" \
 	-DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
--- a/src/config/common.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/config/common.h	Thu Apr 23 19:53:44 2009 -0400
@@ -3,7 +3,6 @@
 
 #include "lib.h"
 
-extern struct master_service *service;
 extern ARRAY_TYPE(const_string) config_strings;
 
 #endif
--- a/src/config/config-connection.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/config/config-connection.c	Thu Apr 23 19:53:44 2009 -0400
@@ -3,6 +3,7 @@
 #include "common.h"
 #include "array.h"
 #include "str.h"
+#include "llist.h"
 #include "ioloop.h"
 #include "network.h"
 #include "istream.h"
@@ -20,6 +21,8 @@
 #define CONFIG_CLIENT_PROTOCOL_MINOR_VERSION 0
 
 struct config_connection {
+	struct config_connection *prev, *next;
+
 	int fd;
 	struct istream *input;
 	struct ostream *output;
@@ -29,6 +32,8 @@
 	unsigned int handshaked:1;
 };
 
+struct config_connection *config_connections = NULL;
+
 static const char *const *
 config_connection_next_line(struct config_connection *conn)
 {
@@ -93,14 +98,12 @@
 		conn->version_received = TRUE;
 	}
 
-	t_push();
 	while ((args = config_connection_next_line(conn)) != NULL) {
 		if (args[0] == NULL)
 			continue;
 		if (strcmp(args[0], "REQ") == 0)
 			config_connection_request(conn, args + 1, 0);
 	}
-	t_pop();
 }
 
 struct config_connection *config_connection_create(int fd)
@@ -112,11 +115,14 @@
 	conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
 	conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
 	conn->io = io_add(fd, IO_READ, config_connection_input, conn);
+	DLLIST_PREPEND(&config_connections, conn);
 	return conn;
 }
 
 void config_connection_destroy(struct config_connection *conn)
 {
+	DLLIST_REMOVE(&config_connections, conn);
+
 	io_remove(&conn->io);
 	i_stream_destroy(&conn->input);
 	o_stream_destroy(&conn->output);
@@ -147,3 +153,9 @@
 				    strings[i+1], NULL));
 	} T_END;
 }
+
+void config_connections_destroy_all(void)
+{
+	while (config_connections != NULL)
+		config_connection_destroy(config_connections);
+}
--- a/src/config/config-connection.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/config/config-connection.h	Thu Apr 23 19:53:44 2009 -0400
@@ -13,4 +13,6 @@
 				    enum config_dump_flags flags);
 void config_connection_putenv(void);
 
+void config_connections_destroy_all(void);
+
 #endif
--- a/src/config/main.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/config/main.c	Thu Apr 23 19:53:44 2009 -0400
@@ -1,11 +1,9 @@
 /* Copyright (C) 2005-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
-#include "lib-signals.h"
-#include "ioloop.h"
 #include "array.h"
 #include "env-util.h"
-#include "str.h"
+#include "master-service.h"
 #include "config-connection.h"
 #include "config-parser.h"
 
@@ -14,59 +12,62 @@
 
 ARRAY_TYPE(const_string) config_strings;
 
-static const char *config_path = SYSCONFDIR "/" PACKAGE ".conf";
+static struct master_service *service;
 static pool_t config_pool;
 
-static void main_init(const char *service)
+static void main_init(const char *service_name)
 {
-	if (getenv("LOG_TO_MASTER") != NULL)
-		i_set_failure_internal();
-
 	config_pool = pool_alloconly_create("config parser", 10240);
 	p_array_init(&config_strings, config_pool, 256);
-	config_parse_file(config_pool, &config_strings, config_path, service);
+	config_parse_file(config_pool, &config_strings,
+			  master_service_get_config_path(service),
+			  service_name);
+}
+
+static void client_connected(const struct master_service_connection *conn)
+{
+	config_connection_create(conn->fd);
 }
 
 int main(int argc, char *argv[])
 {
 	enum config_dump_flags flags = 0;
-	struct ioloop *ioloop;
-	const char *service = "";
+	const char *getopt_str, *service_name = "";
 	char **exec_args = NULL;
-	int i;
+	int c;
+
+	service = master_service_init("config", 0, argc, argv);
 
-	lib_init();
-
-	for (i = 1; i < argc; i++) {
-		if (strcmp(argv[i], "-a") == 0) {
+	getopt_str = t_strconcat("anp:e", master_service_getopt_string(), NULL);
+	while ((c = getopt(argc, argv, getopt_str)) > 0) {
+		if (c == 'e')
+			break;
+		switch (c) {
+		case 'a':
 			flags |= CONFIG_DUMP_FLAG_HUMAN |
 				CONFIG_DUMP_FLAG_DEFAULTS;
-		} else if (strcmp(argv[i], "-c") == 0) {
-			/* config file */
-			i++;
-			if (i == argc) i_fatal("Missing config file argument");
-			config_path = argv[i];
-		} else if (strcmp(argv[i], "-n") == 0) {
+			break;
+		case 'n':
 			flags |= CONFIG_DUMP_FLAG_HUMAN;
-		} else if (strcmp(argv[i], "-s") == 0) {
-			/* service */
-			i++;
-			if (i == argc) i_fatal("Missing service argument");
-			service = argv[i];
-		} else if (strcmp(argv[i], "--exec") == 0) {
-			/* <command> [<args>] */
-			i++;
-			if (i == argc) i_fatal("Missing exec binary argument");
-			exec_args = &argv[i];
+			break;
+		case 'p':
+			service_name = optarg;
 			break;
-		} else {
-			i_fatal("Unknown parameter: %s", argv[i]);
+		default:
+			if (!master_service_parse_option(service, c, optarg))
+				exit(FATAL_DEFAULT);
 		}
 	}
+	if (argv[optind] != NULL)
+		exec_args = &argv[optind];
 
-	main_init(service);
-	ioloop = io_loop_create();
-	if (exec_args == NULL)
+	master_service_init_log(service, "doveconf: ", 0);
+	master_service_init_finish(service);
+	main_init(service_name);
+
+	if (master_service_get_socket_count(service) > 0)
+		master_service_run(service, client_connected);
+	else if (exec_args == NULL)
 		config_connection_dump_request(STDOUT_FILENO, "master", flags);
 	else {
 		config_connection_putenv();
@@ -74,8 +75,8 @@
 		execvp(exec_args[0], exec_args);
 		i_fatal("execvp(%s) failed: %m", exec_args[0]);
 	}
+	config_connections_destroy_all();
 	pool_unref(&config_pool);
-	io_loop_destroy(&ioloop);
-	lib_deinit();
+	master_service_deinit(&service);
         return 0;
 }
--- a/src/config/settings-get.pl	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/config/settings-get.pl	Thu Apr 23 19:53:44 2009 -0400
@@ -38,7 +38,7 @@
 	$code .= $_;
       }
 
-      if (/#define.*DEF/ || /^#undef.*DEF/) {
+      if (/#define.*DEF/ || /^#undef.*DEF/ || /ARRAY_DEFINE_TYPE.*_settings/) {
 	$write = 1;
 	$state = 2 if (/\\$/);
       }
--- a/src/imap-login/Makefile.am	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/imap-login/Makefile.am	Thu Apr 23 19:53:44 2009 -0400
@@ -6,6 +6,7 @@
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-auth \
 	-I$(top_srcdir)/src/lib-imap \
+	-I$(top_srcdir)/src/lib-master \
 	-I$(top_srcdir)/src/login-common
 
 imap_login_LDADD = \
--- a/src/imap-login/client.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/imap-login/client.c	Thu Apr 23 19:53:44 2009 -0400
@@ -11,6 +11,8 @@
 #include "strescape.h"
 #include "imap-parser.h"
 #include "imap-id.h"
+#include "master-service.h"
+#include "master-auth.h"
 #include "client.h"
 #include "client-authenticate.h"
 #include "auth-client.h"
@@ -42,10 +44,13 @@
 #  error client idle timeout must be smaller than authentication timeout
 #endif
 
-#define AUTH_WAITING_MSG \
+#define AUTH_SERVER_WAITING_MSG \
 	"* OK Waiting for authentication process to respond.."
+#define AUTH_MASTER_WAITING_MSG \
+	"* OK Waiting for authentication master process to respond.."
 
 const char *login_protocol = "IMAP";
+const char *login_process_name = "imap-login";
 
 static void client_set_title(struct imap_client *client)
 {
@@ -119,7 +124,6 @@
 	int fd_ssl;
 
 	client_ref(client);
-	connection_queue_add(1);
 	if (!client_unref(client) || client->destroyed)
 		return;
 
@@ -132,6 +136,7 @@
 		return;
 	}
 
+	client->common.proxying = TRUE;
 	client->common.tls = TRUE;
 	client->common.secured = TRUE;
 	client_set_title(client);
@@ -410,7 +415,7 @@
 	if (!auth_client_is_connected(auth_client)) {
 		/* we're not yet connected to auth process -
 		   don't allow any commands */
-		client_send_line(client, AUTH_WAITING_MSG);
+		client_send_line(client, AUTH_SERVER_WAITING_MSG);
 		if (client->to_auth_waiting != NULL)
 			timeout_remove(&client->to_auth_waiting);
 
@@ -483,7 +488,8 @@
 
 static void client_auth_waiting_timeout(struct imap_client *client)
 {
-	client_send_line(client, AUTH_WAITING_MSG);
+	client_send_line(client, client->common.master_tag == 0 ?
+			 AUTH_SERVER_WAITING_MSG : AUTH_MASTER_WAITING_MSG);
 	timeout_remove(&client->to_auth_waiting);
 }
 
@@ -502,7 +508,11 @@
 
 	i_assert(fd != -1);
 
-	connection_queue_add(1);
+	if (clients_get_count() >= login_settings->login_max_connections) {
+		/* reached max. users count, kill few of the
+		   oldest connections */
+		client_destroy_oldest();
+	}
 
 	/* always use nonblocking I/O */
 	net_set_nonblock(fd, TRUE);
@@ -524,8 +534,6 @@
 
 	client_link(&client->common);
 
-	main_ref();
-
 	if (auth_client_is_connected(auth_client))
 		client_send_greeting(client);
 	else
@@ -559,10 +567,11 @@
 	if (client->output != NULL)
 		o_stream_close(client->output);
 
-	if (client->common.master_tag != 0)
-		master_request_abort(&client->common);
-
-	if (client->common.auth_request != NULL) {
+	if (client->common.master_tag != 0) {
+		i_assert(client->common.auth_request == NULL);
+		i_assert(client->common.authenticating);
+		master_auth_request_abort(service, client->common.master_tag);
+	} else if (client->common.auth_request != NULL) {
 		i_assert(client->common.authenticating);
 		sasl_server_auth_client_error(&client->common, NULL);
 	} else {
@@ -602,9 +611,6 @@
 		client->common.proxy = NULL;
 	}
 	client_unref(client);
-
-	main_listen_start();
-	main_unref();
 }
 
 void client_destroy_success(struct imap_client *client, const char *reason)
@@ -640,10 +646,14 @@
 	if (client->output != NULL)
 		o_stream_unref(&client->output);
 
+	if (!client->common.proxying) {
+		i_assert(client->common.proxy == NULL);
+		master_service_client_connection_destroyed(service);
+	}
+
 	i_free(client->common.virtual_user);
 	i_free(client->common.auth_mech_name);
 	i_free(client);
-
 	return FALSE;
 }
 
--- a/src/imap-login/client.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/imap-login/client.h	Thu Apr 23 19:53:44 2009 -0400
@@ -2,7 +2,6 @@
 #define CLIENT_H
 
 #include "network.h"
-#include "master.h"
 #include "client-common.h"
 
 /* Disconnect client after idling this many milliseconds */
--- a/src/imap-login/imap-proxy.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/imap-login/imap-proxy.c	Thu Apr 23 19:53:44 2009 -0400
@@ -244,6 +244,7 @@
 		client->common.input = NULL;
 		client->output = NULL;
 		client->common.fd = -1;
+		client->common.proxying = TRUE;
 		client_destroy_success(client, str_c(str));
 		return 1;
 	} else if (strncmp(line, "L ", 2) == 0) {
@@ -362,7 +363,6 @@
 	}
 
 	i_assert(client->refcount > 1);
-	connection_queue_add(1);
 
 	if (client->destroyed) {
 		/* connection_queue_add() decided that we were the oldest
--- a/src/imap/main.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/imap/main.c	Thu Apr 23 19:53:44 2009 -0400
@@ -19,7 +19,7 @@
 #include <unistd.h>
 
 #define IS_STANDALONE() \
-        (getenv("IMAPLOGINTAG") == NULL)
+        (getenv("CLIENT_INPUT") == NULL)
 
 struct client_workaround_list {
 	const char *name;
@@ -70,12 +70,44 @@
 	return client_workarounds;
 }
 
+static void client_add_input(struct client *client, const char *input)
+{
+	buffer_t *buf;
+	const char *tag;
+	unsigned int data_pos;
+
+	buf = input == NULL ? NULL : t_base64_decode_str(input);
+	if (buf != NULL && buf->used > 0) {
+		tag = t_strndup(buf->data, buf->used);
+		data_pos = strlen(tag) + 1;
+		if (data_pos > buf->used &&
+		    !i_stream_add_data(client->input,
+				       CONST_PTR_OFFSET(buf->data, data_pos),
+				       buf->used - data_pos))
+			i_panic("Couldn't add client input to stream");
+	} else {
+		/* IMAPLOGINTAG environment is compatible with mailfront */
+		tag = getenv("IMAPLOGINTAG");
+	}
+
+	if (tag == NULL) {
+		client_send_line(client, t_strconcat(
+			"* PREAUTH [CAPABILITY ",
+			str_c(client->capability_string), "] "
+			"Logged in as ", client->user->username, NULL));
+	} else {
+		client_send_line(client, t_strconcat(
+			tag, " OK [CAPABILITY ",
+			str_c(client->capability_string), "] Logged in", NULL));
+	}
+	(void)client_handle_input(client);
+}
+
 static void main_init(const struct imap_settings *set, struct mail_user *user,
 		      bool dump_capability)
 {
 	struct client *client;
 	struct ostream *output;
-	const char *str, *tag;
 
 	if (set->shutdown_clients && !dump_capability) {
 		/* If master dies, the log fd gets closed and we'll quit */
@@ -96,29 +128,7 @@
 	output = client->output;
 	o_stream_ref(output);
 	o_stream_cork(output);
-
-	/* IMAPLOGINTAG environment is compatible with mailfront */
-	tag = getenv("IMAPLOGINTAG");
-	if (tag == NULL) {
-		client_send_line(client, t_strconcat(
-			"* PREAUTH [CAPABILITY ",
-			str_c(client->capability_string), "] "
-			"Logged in as ", user->username, NULL));
-	} else {
-		client_send_line(client, t_strconcat(
-			tag, " OK [CAPABILITY ",
-			str_c(client->capability_string), "] Logged in", NULL));
-	}
-	str = getenv("CLIENT_INPUT");
-	if (str != NULL) T_BEGIN {
-		buffer_t *buf = t_base64_decode_str(str);
-		if (buf->used > 0) {
-			if (!i_stream_add_data(client->input, buf->data,
-					       buf->used))
-				i_panic("Couldn't add client input to stream");
-			(void)client_handle_input(client);
-		}
-	} T_END;
+	client_add_input(client, getenv("CLIENT_INPUT"));
         o_stream_uncork(output);
 	o_stream_unref(&output);
 }
@@ -130,6 +140,12 @@
 	clients_deinit();
 }
 
+static void client_connected(const struct master_service_connection *conn)
+{
+	/* FIXME: we can't handle this yet */
+	(void)close(conn->fd);
+}
+
 int main(int argc, char *argv[], char *envp[])
 {
 	const struct setting_parser_info *set_roots[] = {
@@ -145,10 +161,6 @@
 	const char *value;
 	int c;
 
-#ifdef DEBUG
-	if (!IS_STANDALONE() && getenv("GDB") == NULL)
-		fd_debug_verify_leaks(3, 1024);
-#endif
 	if (IS_STANDALONE() && getuid() == 0 &&
 	    net_getpeername(1, NULL, NULL) == 0) {
 		printf("* BAD [ALERT] imap binary must not be started from "
@@ -170,7 +182,7 @@
 	service = master_service_init("imap", service_flags, argc, argv);
 	while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
 		if (!master_service_parse_option(service, c, optarg))
-			i_fatal("Unknown argument: %c", c);
+			exit(FATAL_DEFAULT);
 	}
 
 	memset(&input, 0, sizeof(input));
@@ -200,9 +212,11 @@
 	   while initializing */
 	io_loop_set_running(current_ioloop);
 
-	main_init(set, mail_user, dump_capability);
+	T_BEGIN {
+		main_init(set, mail_user, dump_capability);
+	} T_END;
 	if (io_loop_is_running(current_ioloop))
-		master_service_run(service);
+		master_service_run(service, client_connected);
 
 	main_deinit();
 	mail_storage_service_deinit_user();
--- a/src/lda/main.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lda/main.c	Thu Apr 23 19:53:44 2009 -0400
@@ -224,9 +224,6 @@
 
 	service = master_service_init("lda", MASTER_SERVICE_FLAG_STANDALONE,
 				      argc, argv);
-#ifdef SIGXFSZ
-        lib_signals_ignore(SIGXFSZ, TRUE);
-#endif
 
 	memset(&ctx, 0, sizeof(ctx));
 	ctx.pool = pool_alloconly_create("mail deliver context", 256);
@@ -282,8 +279,7 @@
 		default:
 			if (!master_service_parse_option(service, c, optarg)) {
 				print_help();
-				i_fatal_status(EX_USAGE,
-					       "Unknown argument: %c", c);
+				exit(EX_USAGE);
 			}
 			break;
 		}
@@ -325,6 +321,9 @@
 	ctx.dest_user = mail_storage_service_init_user(service, &service_input,
 						       set_roots,
 						       service_flags);
+#ifdef SIGXFSZ
+        lib_signals_ignore(SIGXFSZ, TRUE);
+#endif
 	ctx.set = mail_storage_service_get_settings(service);
         duplicate_init(mail_user_set_get_storage_set(ctx.dest_user->set));
 
--- a/src/lib-master/Makefile.am	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-master/Makefile.am	Thu Apr 23 19:53:44 2009 -0400
@@ -8,11 +8,14 @@
 	-DPKG_RUNDIR=\""$(rundir)"\"
 
 libmaster_la_SOURCES = \
+	master-auth.c \
 	master-service.c \
 	master-service-settings.c \
 	syslog-util.c
 
 noinst_HEADERS = \
+	master-auth.h \
+	master-interface.h \
 	master-service.h \
 	master-service-private.h \
 	master-service-settings.h \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-master/master-auth.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,191 @@
+/* Copyright (C) 2005 Timo Sirainen */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "fdpass.h"
+#include "buffer.h"
+#include "hash.h"
+#include "master-service-private.h"
+#include "master-auth.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+struct master_auth {
+	struct master_service *service;
+	pool_t pool;
+
+	int fd;
+	struct io *io;
+
+	unsigned int tag_counter;
+	struct hash_table *requests;
+
+	char buf[sizeof(struct master_auth_reply)];
+	unsigned int buf_pos;
+
+	/* linked list, node->context is the next pointer */
+	struct master_auth_request_node *free_nodes;
+};
+
+struct master_auth_request_node {
+	master_auth_callback_t *callback;
+	void *context;
+};
+
+static struct master_auth_request_node aborted_node;
+
+unsigned int master_auth_request(struct master_service *service, int fd,
+				 const struct master_auth_request *request,
+				 const unsigned char *data,
+				 master_auth_callback_t *callback,
+				 void *context)
+{
+	struct master_auth *auth = service->auth;
+        struct master_auth_request_node *node;
+	struct master_auth_request req;
+	buffer_t *buf;
+	struct stat st;
+	ssize_t ret;
+
+	i_assert(request->auth_pid != 0);
+
+	req = *request;
+	req.tag = ++auth->tag_counter;
+	if (req.tag == 0)
+		req.tag = ++auth->tag_counter;
+
+	if (fstat(fd, &st) < 0)
+		i_fatal("fstat(auth dest fd) failed: %m");
+	req.ino = st.st_ino;
+
+	buf = buffer_create_dynamic(pool_datastack_create(),
+				    sizeof(req) + req.data_size);
+	buffer_append(buf, &req, sizeof(req));
+	buffer_append(buf, data, req.data_size);
+
+	ret = fd_send(auth->fd, fd, buf->data, buf->used);
+	if (ret < 0)
+		i_fatal("fd_send(%d) failed: %m", fd);
+	if ((size_t)ret != buf->used) {
+		i_fatal("fd_send() sent only %d of %d bytes",
+			(int)ret, (int)buf->used);
+	}
+
+	if (auth->free_nodes == NULL)
+		node = p_new(auth->pool, struct master_auth_request_node, 1);
+	else {
+		node = auth->free_nodes;
+                auth->free_nodes = node->context;
+	}
+	node->callback = callback;
+	node->context = context;
+
+	hash_table_insert(auth->requests, POINTER_CAST(req.tag), node);
+	return req.tag;
+}
+
+void master_auth_request_abort(struct master_service *service, unsigned int tag)
+{
+	struct master_auth *auth = service->auth;
+        struct master_auth_request_node *node;
+
+	node = hash_table_lookup(auth->requests, POINTER_CAST(tag));
+	if (node == NULL)
+		i_panic("master_auth_request_abort(): tag %u not found", tag);
+
+	i_assert(node != &aborted_node);
+	hash_table_update(auth->requests, POINTER_CAST(tag), &aborted_node);
+
+	node->callback = NULL;
+	node->context = auth->free_nodes;
+	auth->free_nodes = node;
+}
+
+static void request_handle(struct master_auth *auth,
+			   struct master_auth_reply *reply)
+{
+        struct master_auth_request_node *node;
+
+	node = hash_table_lookup(auth->requests, POINTER_CAST(reply->tag));
+	if (node == NULL)
+		i_error("Master sent reply with unknown tag %u", reply->tag);
+
+	if (node != &aborted_node) {
+		node->callback(reply, node->context);
+
+		/* the callback may have called master_auth_request_abort(),
+		   which would have put the node to free_nodes list already */
+		if (node->callback != NULL) {
+			node->callback = NULL;
+			node->context = auth->free_nodes;
+			auth->free_nodes = node;
+		}
+	}
+
+	hash_table_remove(auth->requests, POINTER_CAST(reply->tag));
+}
+
+static void master_auth_input(void *context)
+{
+	struct master_auth *auth = context;
+	int ret;
+
+	ret = net_receive(auth->fd, auth->buf + auth->buf_pos,
+			  sizeof(auth->buf) - auth->buf_pos);
+	if (ret < 0) {
+		/* master died, kill all clients logging in */
+                master_service_stop(auth->service);
+		return;
+	}
+
+	auth->buf_pos += ret;
+	if (auth->buf_pos < sizeof(auth->buf))
+		return;
+
+	/* reply is now read */
+	request_handle(auth, (struct master_auth_reply *) auth->buf);
+	auth->buf_pos = 0;
+}
+
+void master_auth_init(struct master_service *service)
+{
+	struct master_auth *auth;
+	struct ip_addr ip;
+	pool_t pool;
+
+	i_assert(service->auth == NULL);
+
+	if (getenv("MASTER_AUTH_FD") == NULL)
+		i_fatal("auth_dest_service setting not set");
+
+	if (net_getsockname(MASTER_AUTH_FD, &ip, NULL) < 0 ||
+	    ip.family != AF_UNIX)
+		i_fatal("MASTER_AUTH_FD not given");
+
+	pool = pool_alloconly_create("master auth", 1024);
+	auth = p_new(pool, struct master_auth, 1);
+	auth->pool = pool;
+	auth->service = service;
+	auth->fd = MASTER_AUTH_FD;
+	auth->requests = hash_table_create(default_pool, pool, 0, NULL, NULL);
+	auth->io = io_add(auth->fd, IO_READ, master_auth_input, auth);
+
+	service->auth = auth;
+}
+
+void master_auth_deinit(struct master_service *service)
+{
+	struct master_auth *auth = service->auth;
+
+	i_assert(service->auth != NULL);
+
+	hash_table_destroy(&auth->requests);
+	if (auth->io != NULL)
+		io_remove(&auth->io);
+	if (close(auth->fd) < 0)
+		i_fatal("close(master auth) failed: %m");
+	pool_unref(&auth->pool);
+	service->auth = NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-master/master-auth.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,26 @@
+#ifndef MASTER_AUTH_H
+#define MASTER_AUTH_H
+
+struct master_service;
+struct master_auth_request;
+struct master_auth_reply;
+
+typedef void master_auth_callback_t(const struct master_auth_reply *reply,
+				    void *context);
+
+/* Send an authentication request. The fd contains the file descriptor to
+   transfer, or -1 if no fd is wanted to be transferred. Returns tag which can
+   be used to abort the request (ie. ignore the reply from master).
+   request->tag is ignored. */
+unsigned int master_auth_request(struct master_service *service, int fd,
+				 const struct master_auth_request *request,
+				 const unsigned char *data,
+				 master_auth_callback_t *callback,
+				 void *context);
+void master_auth_request_abort(struct master_service *service,
+			       unsigned int tag);
+
+void master_auth_init(struct master_service *service);
+void master_auth_deinit(struct master_service *service);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-master/master-interface.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,104 @@
+#ifndef MASTER_INTERFACE_H
+#define MASTER_INTERFACE_H
+
+#include "network.h"
+
+/* We are attempting semi-compatibility with Postfix's master process here.
+   Whether this is useful or not remains to be seen. */
+
+/* Child processes should send status updates whenever they accept a new
+   connection (decrease available_count) and when they close existing
+   connection (increase available_count). */
+struct master_status {
+	pid_t pid;
+	/* uid is used to check for old/invalid status messages */
+	unsigned int uid;
+	/* number of new connections process is currently accepting */
+	unsigned int available_count;
+};
+
+/* When connecting to log service, send this handshake first */
+struct log_service_handshake {
+	/* If magic is invalid, assume the data is already what we want
+	   to log */
+#define MASTER_LOG_MAGIC 0x02ff03fe
+	unsigned int log_magic;
+
+	/* If we're writing log lines more often than this, start throttling */
+	unsigned int max_lines_per_sec;
+
+	/* Add this previs to each logged line */
+	unsigned int prefix_len;
+	/* unsigned char prefix[]; */
+};
+
+/* This should be kept in sync with LOGIN_MAX_INBUF_SIZE. Multiply it by two
+   to make sure there's space to transfer the command tag  */
+#define MASTER_AUTH_MAX_DATA_SIZE (4096*2)
+
+/* Authentication request. File descriptor may be sent along with the
+   request. */
+struct master_auth_request {
+	/* Request tag. Reply is sent back using same tag. */
+	unsigned int tag;
+
+	/* Authentication process and authentication ID. */
+	pid_t auth_pid;
+	unsigned int auth_id;
+
+	/* Local and remote IPs of the connection. The file descriptor
+	   itself may be a local socketpair. */
+	struct ip_addr local_ip, remote_ip;
+
+	/* request follows this many bytes of client input */
+	uint32_t data_size;
+	/* inode of the transferred fd. verified just to be sure that the
+	   correct fd is mapped to the correct struct. */
+	ino_t ino;
+};
+
+enum master_auth_status {
+	MASTER_AUTH_STATUS_OK,
+	MASTER_AUTH_STATUS_INTERNAL_ERROR,
+	/* user reached max. simultaneous connections */
+	MASTER_AUTH_STATUS_MAX_CONNECTIONS
+};
+
+struct master_auth_reply {
+	unsigned int tag;
+	enum master_auth_status status;
+	/* PID of the post-login mail process handling this connection */
+	pid_t mail_pid;
+};
+
+/* getenv(MASTER_UID_ENV) provides master_status.uid value */
+#define MASTER_UID_ENV "GENERATION"
+
+/* getenv(MASTER_CLIENT_LIMIT_ENV) provides maximum
+   master_status.available_count as specified in configuration file */
+#define MASTER_CLIENT_LIMIT_ENV "CLIENT_LIMIT"
+
+/* getenv(MASTER_CONFIG_FILE_ENV) provides path to configuration file/socket */
+#define MASTER_CONFIG_FILE_ENV "CONFIG_FILE"
+
+/* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
+#define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
+
+/* points to /dev/null for now */
+#define MASTER_RESERVED_FD 3
+
+/* Socket for sending master_auth_requests. Also used by auth server process
+   as a master socket. */
+#define MASTER_AUTH_FD 4
+
+/* Shared pipe to master, used to send master_status reports */
+#define MASTER_STATUS_FD 5
+/* First file descriptor where process is expected to be listening.
+   The file descriptor count is given in -s parameter, defaulting to 1.
+
+   master_status.available_count reports how many accept()s we're still
+   accepting. Once no children are listening, master will do it and create
+   new child processes when needed. */
+#define MASTER_LISTEN_FD_FIRST 6
+
+#endif
--- a/src/lib-master/master-service-private.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-master/master-service-private.h	Thu Apr 23 19:53:44 2009 -0400
@@ -1,8 +1,15 @@
 #ifndef MASTER_SERVICE_PRIVATE_H
 #define MASTER_SERVICE_PRIVATE_H
 
+#include "master-interface.h"
 #include "master-service.h"
 
+struct master_service_listener {
+	struct master_service *service;
+	int fd;
+	struct io *io;
+};
+
 struct master_service {
 	struct ioloop *ioloop;
 
@@ -16,12 +23,24 @@
 	const char *config_path;
 	int syslog_facility;
 
+	unsigned int socket_count;
+        struct master_service_listener *listeners;
+
+	struct io *io_status_write, *io_status_error;
+	unsigned int service_count_left;
+	unsigned int total_available_count;
+	struct master_status master_status;
+
+        struct master_auth *auth;
+	master_service_connection_callback_t *callback;
+
 	pool_t set_pool;
 	const struct master_service_settings *set;
 	struct setting_parser_context *set_parser;
 
 	unsigned int keep_environment:1;
 	unsigned int log_directly:1;
+	unsigned int initial_status_sent:1;
 };
 
 #endif
--- a/src/lib-master/master-service-settings.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-master/master-service-settings.c	Thu Apr 23 19:53:44 2009 -0400
@@ -2,6 +2,8 @@
 
 #include "lib.h"
 #include "array.h"
+#include "istream.h"
+#include "write-full.h"
 #include "settings-parser.h"
 #include "master-service-private.h"
 #include "master-service-settings.h"
@@ -9,9 +11,13 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/stat.h>
 
 #define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
 
+#define CONFIG_HANDSHAKE "VERSION\t1\t0\n"
+#define CONFIG_REQUEST_SERVICE "REQ\tservice=%s\n"
+
 #undef DEF
 #define DEF(type, name) \
 	{ type, #name, offsetof(struct master_service_settings, name), NULL }
@@ -57,17 +63,49 @@
 	/* @UNSAFE */
 	conf_argv = t_new(const char *, 6 + (service->argc + 1) + 1);
 	conf_argv[0] = DOVECOT_CONFIG_BIN_PATH;
-	conf_argv[1] = "-s";
+	conf_argv[1] = "-p";
 	conf_argv[2] = service->name;
 	conf_argv[3] = "-c";
 	conf_argv[4] = service->config_path;
-	conf_argv[5] = "--exec";
+	conf_argv[5] = "-e";
 	memcpy(conf_argv+6, service->argv,
 	       (service->argc+1) * sizeof(conf_argv[0]));
 	execv(conf_argv[0], (char **)conf_argv);
 	i_fatal("execv(%s) failed: %m", conf_argv[0]);
 }
 
+static int
+master_service_read_config(struct master_service *service, bool preserve_home,
+			   const char **error_r)
+{
+	const char *path, *str;
+	int fd;
+
+	path = master_service_get_config_path(service);
+	fd = net_connect_unix(path);
+	if (fd < 0) {
+		struct stat st;
+
+		*error_r = t_strdup_printf("net_connect_unix(%s) failed: %m",
+					   path);
+
+		if (stat(path, &st) == 0 && !S_ISFIFO(st.st_mode)) {
+			/* it's a file, not a socket */
+			master_service_exec_config(service, preserve_home);
+		}
+		return -1;
+	}
+	net_set_nonblock(fd, FALSE);
+
+	str = t_strdup_printf(CONFIG_HANDSHAKE CONFIG_REQUEST_SERVICE,
+			      service->name);
+	if (write_full(fd, str, strlen(str)) < 0) {
+		*error_r = t_strdup_printf("write_full(%s) failed: %m", path);
+		return -1;
+	}
+	return fd;
+}
+
 int master_service_settings_read(struct master_service *service,
 				 const struct setting_parser_info *roots[],
 				 const struct dynamic_settings_parser *dyn_parsers,
@@ -76,12 +114,18 @@
 	ARRAY_DEFINE(all_roots, const struct setting_parser_info *);
 	const struct setting_parser_info *tmp_root;
 	struct setting_parser_context *parser;
+	struct istream *input;
 	const char *error;
 	void **sets;
 	unsigned int i;
+	int ret, fd = -1;
 
-	if (getenv("DOVECONF_ENV") == NULL)
-		master_service_exec_config(service, preserve_home);
+	if (getenv("DOVECONF_ENV") == NULL) {
+		fd = master_service_read_config(service, preserve_home,
+						error_r);
+		if (fd == -1)
+			return -1;
+	}
 
 	if (service->set_pool != NULL)
 		p_clear(service->set_pool);
@@ -96,16 +140,29 @@
 	p_array_init(&all_roots, service->set_pool, 8);
 	tmp_root = &master_service_setting_parser_info;
 	array_append(&all_roots, &tmp_root, 1);
-	for (i = 0; roots[i] != NULL; i++)
-		array_append(&all_roots, &roots[i], 1);
+	if (roots != NULL) {
+		for (i = 0; roots[i] != NULL; i++)
+			array_append(&all_roots, &roots[i], 1);
+	}
 
 	parser = settings_parser_init_list(service->set_pool,
 			array_idx(&all_roots, 0), array_count(&all_roots),
 			SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
 
-	if (settings_parse_environ(parser) < 0) {
-		*error_r = settings_parser_get_error(parser);
-		return -1;
+	if (fd != -1) {
+		input = i_stream_create_fd(fd, (size_t)-1, FALSE);
+		ret = settings_parse_stream_read(parser, input);
+		i_stream_unref(&input);
+		i_assert(ret <= 0);
+		if (ret < 0) {
+			*error_r = settings_parser_get_error(parser);
+			return -1;
+		}
+	} else {
+		if (settings_parse_environ(parser) < 0) {
+			*error_r = settings_parser_get_error(parser);
+			return -1;
+		}
 	}
 
 	if (settings_parser_check(parser, service->set_pool, &error) < 0) {
--- a/src/lib-master/master-service.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-master/master-service.c	Thu Apr 23 19:53:44 2009 -0400
@@ -13,6 +13,7 @@
 
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/stat.h>
 #include <syslog.h>
 
 #define DEFAULT_CONFIG_FILE_PATH SYSCONFDIR"/dovecot.conf"
@@ -23,9 +24,13 @@
 /* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
 #define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
 
+static void io_listeners_add(struct master_service *service);
+static void io_listeners_remove(struct master_service *service);
+static void master_status_update(struct master_service *service);
+
 const char *master_service_getopt_string(void)
 {
-	return "c:Lk";
+	return "c:ks:L";
 }
 
 static void sig_die(const siginfo_t *si, void *context)
@@ -69,7 +74,7 @@
 	   is properly initialized */
 	i_set_failure_prefix(t_strdup_printf("%s(init): ", name));
 
-	if (getenv("LOG_TO_MASTER") == NULL)
+	if (getenv(MASTER_UID_ENV) == NULL)
 		flags |= MASTER_SERVICE_FLAG_STANDALONE;
 
 	service = i_new(struct master_service, 1);
@@ -78,18 +83,22 @@
 	service->name = i_strdup(name);
 	service->flags = flags;
 	service->ioloop = io_loop_create();
+	service->service_count_left = (unsigned int)-1;
+
 	service->config_path = getenv(MASTER_CONFIG_FILE_ENV);
 	if (service->config_path == NULL)
 		service->config_path = DEFAULT_CONFIG_FILE_PATH;
 
-	if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0)
+	if ((flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
 		service->version_string = getenv(MASTER_DOVECOT_VERSION_ENV);
-	else
+		service->socket_count = 1;
+	} else {
 		service->version_string = PACKAGE_VERSION;
+	}
 
 	/* set up some kind of logging until we know exactly how and where
 	   we want to log */
-	if (getenv("LOG_TO_MASTER") != NULL)
+	if (getenv("LOG_SERVICE") != NULL)
 		i_set_failure_internal();
 	if (getenv("USER") != NULL) {
 		i_set_failure_prefix(t_strdup_printf("%s(%s): ",
@@ -98,18 +107,12 @@
 		i_set_failure_prefix(t_strdup_printf("%s: ", name));
 	}
 
-	/* set default signal handlers */
-	lib_signals_init();
-        lib_signals_ignore(SIGPIPE, TRUE);
-        lib_signals_ignore(SIGALRM, FALSE);
-        lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
-	lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
-
 	master_service_verify_version(service);
 	return service;
 }
 
-void master_service_init_log(struct master_service *service, const char *prefix)
+void master_service_init_log(struct master_service *service, const char *prefix,
+			     unsigned int max_lines_per_sec)
 {
 	const char *path;
 
@@ -118,13 +121,18 @@
 		return;
 	}
 
-	if (getenv("LOG_TO_MASTER") != NULL && !service->log_directly) {
-		/* logging via master process */
+	if (getenv("LOG_SERVICE") != NULL && !service->log_directly) {
+		/* logging via log service */
 		i_set_failure_internal();
 		i_set_failure_prefix(prefix);
 		return;
 	}
 
+	if (service->set == NULL) {
+		i_set_failure_file("/dev/stderr", prefix);
+		return;
+	}
+
 	if (*service->set->log_path == '\0') {
 		/* log to syslog */
 		int facility;
@@ -149,6 +157,8 @@
 bool master_service_parse_option(struct master_service *service,
 				 int opt, const char *arg)
 {
+	int i;
+
 	switch (opt) {
 	case 'c':
 		service->config_path = arg;
@@ -156,6 +166,11 @@
 	case 'k':
 		service->keep_environment = TRUE;
 		break;
+	case 's':
+		if ((i = atoi(arg)) < 0)
+			i_fatal("Invalid socket count: %s", arg);
+                service->socket_count = i;
+		break;
 	case 'L':
 		service->log_directly = TRUE;
 		break;
@@ -165,6 +180,77 @@
 	return TRUE;
 }
 
+static void master_status_error(void *context)
+{
+	struct master_service *service = context;
+
+	/* status fd is a write-only pipe, so if we're here it means the
+	   master wants us to die (or died itself). don't die until all
+	   service connections are finished. */
+	io_remove(&service->io_status_error);
+
+	/* the log fd may also be closed already, don't die when trying to
+	   log later */
+	i_set_failure_ignore_errors(TRUE);
+
+	if (service->master_status.available_count ==
+	    service->total_available_count)
+		master_service_stop(service);
+}
+
+void master_service_init_finish(struct master_service *service)
+{
+	struct stat st;
+	const char *value;
+	unsigned int count;
+
+	i_assert(service->total_available_count == 0);
+	i_assert(service->service_count_left == (unsigned int)-1);
+
+	/* set default signal handlers */
+	lib_signals_init();
+        lib_signals_ignore(SIGPIPE, TRUE);
+        lib_signals_ignore(SIGALRM, FALSE);
+        lib_signals_set_handler(SIGINT, TRUE, sig_die, service);
+	lib_signals_set_handler(SIGTERM, TRUE, sig_die, service);
+
+	if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
+		if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
+			i_fatal("Must be started by dovecot master process");
+
+		/* initialize master_status structure */
+		value = getenv(MASTER_UID_ENV);
+		if (value == NULL)
+			i_fatal(MASTER_UID_ENV" not set");
+		service->master_status.pid = getpid();
+		service->master_status.uid =
+			(unsigned int)strtoul(value, NULL, 10);
+
+		/* set the default limit */
+		value = getenv(MASTER_CLIENT_LIMIT_ENV);
+		count = value == NULL ? 0 :
+			(unsigned int)strtoul(value, NULL, 10);
+		if (count == 0)
+			i_fatal(MASTER_CLIENT_LIMIT_ENV" not set");
+		master_service_set_client_limit(service, count);
+
+		/* start listening errors for status fd, it means master died */
+		service->io_status_error = io_add(MASTER_STATUS_FD, IO_ERROR,
+						  master_status_error, service);
+	} else {
+		master_service_set_client_limit(service, 1);
+		master_service_set_service_count(service, 1);
+	}
+
+	io_listeners_add(service);
+
+	if ((service->flags & MASTER_SERVICE_FLAG_STD_CLIENT) != 0) {
+		/* we already have a connection to be served */
+		service->master_status.available_count--;
+	}
+	master_status_update(service);
+}
+
 void master_service_env_clean(bool preserve_home)
 {
 	const char *user, *tz, *home;
@@ -189,6 +275,47 @@
 	if (home != NULL) env_put(home);
 }
 
+void master_service_set_client_limit(struct master_service *service,
+				     unsigned int client_limit)
+{
+	i_assert(service->master_status.available_count ==
+		 service->total_available_count);
+
+	service->total_available_count = client_limit;
+	service->master_status.available_count = client_limit;
+}
+
+unsigned int master_service_get_client_limit(struct master_service *service)
+{
+	return service->total_available_count;
+}
+
+void master_service_set_service_count(struct master_service *service,
+				      unsigned int count)
+{
+	unsigned int used;
+
+	used = service->total_available_count -
+		service->master_status.available_count;
+	i_assert(count >= used);
+
+	if (service->total_available_count > count) {
+		service->total_available_count = count;
+		service->master_status.available_count = count - used;
+	}
+	service->service_count_left = count;
+}
+
+unsigned int master_service_get_service_count(struct master_service *service)
+{
+	return service->service_count_left;
+}
+
+unsigned int master_service_get_socket_count(struct master_service *service)
+{
+	return service->socket_count;
+}
+
 const char *master_service_get_config_path(struct master_service *service)
 {
 	return service->config_path;
@@ -199,9 +326,12 @@
 	return service->version_string;
 }
 
-void master_service_run(struct master_service *service)
+void master_service_run(struct master_service *service,
+			master_service_connection_callback_t *callback)
 {
+	service->callback = callback;
 	io_loop_run(service->ioloop);
+	service->callback = NULL;
 }
 
 void master_service_stop(struct master_service *service)
@@ -209,11 +339,54 @@
         io_loop_stop(service->ioloop);
 }
 
+void master_service_client_connection_destroyed(struct master_service *service)
+{
+	if (service->listeners == NULL) {
+		/* we can listen again */
+		io_listeners_add(service);
+	}
+
+	i_assert(service->total_available_count > 0);
+
+	if (service->service_count_left != service->total_available_count) {
+		i_assert(service->service_count_left == (unsigned int)-1);
+		service->master_status.available_count++;
+	} else {
+		/* we have only limited amount of service requests left */
+		i_assert(service->service_count_left > 0);
+                service->service_count_left--;
+		service->total_available_count--;
+
+		if (service->service_count_left == 0) {
+			i_assert(service->master_status.available_count ==
+				 service->total_available_count);
+			master_service_stop(service);
+		}
+	}
+	master_status_update(service);
+
+	if (service->io_status_error == NULL &&
+	    service->master_status.available_count ==
+	    service->total_available_count) {
+		/* master has closed the connection and we have nothing else
+		   to do anymore. */
+		master_service_stop(service);
+	}
+}
+
 void master_service_deinit(struct master_service **_service)
 {
 	struct master_service *service = *_service;
 
 	*_service = NULL;
+
+	io_listeners_remove(service);
+
+	if (service->io_status_error != NULL)
+		io_remove(&service->io_status_error);
+	if (service->io_status_write != NULL)
+		io_remove(&service->io_status_write);
+
 	lib_signals_deinit();
 	io_loop_destroy(&service->ioloop);
 
@@ -222,3 +395,115 @@
 
 	lib_deinit();
 }
+
+static void master_service_listen(struct master_service_listener *l)
+{
+	struct master_service_connection conn;
+
+	if (l->service->master_status.available_count == 0) {
+		/* we are full. stop listening for now. */
+		io_listeners_remove(l->service);
+		return;
+	}
+
+	memset(&conn, 0, sizeof(conn));
+	conn.listen_fd = l->fd;
+	conn.fd = net_accept(l->fd, &conn.remote_ip, &conn.remote_port);
+	if (conn.fd < 0) {
+		if (conn.fd == -1)
+			return;
+
+		if (errno != ENOTSOCK) {
+			i_error("net_accept() failed: %m");
+			io_listeners_remove(l->service);
+			return;
+		}
+		/* it's not a socket. probably a fifo. use the "listener"
+		   as the connection fd */
+		io_remove(&l->io);
+		conn.fd = l->fd;
+	}
+
+	l->service->master_status.available_count--;
+        master_status_update(l->service);
+
+        l->service->callback(&conn);
+}
+
+static void io_listeners_add(struct master_service *service)
+{
+	unsigned int i;
+
+	if (service->socket_count == 0)
+		return;
+
+	service->listeners =
+		i_new(struct master_service_listener, service->socket_count);
+
+	for (i = 0; i < service->socket_count; i++) {
+		struct master_service_listener *l = &service->listeners[i];
+
+		l->service = service;
+		l->fd = MASTER_LISTEN_FD_FIRST + i;
+		l->io = io_add(MASTER_LISTEN_FD_FIRST + i, IO_READ,
+			       master_service_listen, l);
+	}
+}
+
+static void io_listeners_remove(struct master_service *service)
+{
+	unsigned int i;
+
+	if (service->listeners != NULL) {
+		for (i = 0; i < service->socket_count; i++) {
+			if (service->listeners[i].io != NULL)
+				io_remove(&service->listeners[i].io);
+		}
+		i_free_and_null(service->listeners);
+	}
+}
+
+static bool master_status_update_is_important(struct master_service *service)
+{
+	if (service->master_status.available_count == 0)
+		return TRUE;
+	if (!service->initial_status_sent)
+		return TRUE;
+	return FALSE;
+}
+
+static void master_status_update(struct master_service *service)
+{
+	ssize_t ret;
+
+	if (service->master_status.pid == 0)
+		return; /* closed */
+
+	ret = write(MASTER_STATUS_FD, &service->master_status,
+		    sizeof(service->master_status));
+	if (ret > 0) {
+		/* success */
+		if (service->io_status_write != NULL) {
+			/* delayed important update sent successfully */
+			io_remove(&service->io_status_write);
+		}
+		service->initial_status_sent = TRUE;
+	} else if (ret == 0) {
+		/* shouldn't happen? */
+		i_error("write(master_status_fd) returned 0");
+		service->master_status.pid = 0;
+	} else if (errno != EAGAIN) {
+		/* failure */
+		if (errno != EPIPE)
+			i_error("write(master_status_fd) failed: %m");
+		service->master_status.pid = 0;
+	} else if (master_status_update_is_important(service)) {
+		/* reader is busy, but it's important to get this notification
+		   through. send it when possible. */
+		if (service->io_status_write == NULL) {
+			service->io_status_write =
+				io_add(MASTER_STATUS_FD, IO_WRITE,
+				       master_status_update, service);
+		}
+	}
+}
--- a/src/lib-master/master-service.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-master/master-service.h	Thu Apr 23 19:53:44 2009 -0400
@@ -12,6 +12,17 @@
 	MASTER_SERVICE_FLAG_LOG_TO_STDERR	= 0x04
 };
 
+struct master_service_connection {
+	int fd;
+	int listen_fd;
+
+	struct ip_addr remote_ip;
+	unsigned int remote_port;
+};
+
+typedef void
+master_service_connection_callback_t(const struct master_service_connection *conn);
+
 struct master_service;
 
 const char *master_service_getopt_string(void);
@@ -23,13 +34,32 @@
 /* Parser command line option. Returns TRUE if processed. */
 bool master_service_parse_option(struct master_service *service,
 				 int opt, const char *arg);
+/* Finish service initialization. The caller should drop privileges
+   before calling this. */
+void master_service_init_finish(struct master_service *service);
 
 /* Clean environment from everything except TZ, USER and optionally HOME. */
 void master_service_env_clean(bool preserve_home);
 
 /* Initialize logging. */
-void master_service_init_log(struct master_service *service,
-			     const char *prefix);
+void master_service_init_log(struct master_service *service, const char *prefix,
+			     unsigned int max_lines_per_sec);
+
+/* Set maximum number of clients we can handle. Default is given by master. */
+void master_service_set_client_limit(struct master_service *service,
+				     unsigned int client_limit);
+/* Returns the maximum number of clients we can handle. */
+unsigned int master_service_get_client_limit(struct master_service *service);
+
+/* Set maximum number of client connections we will handle before shutting
+   down. */
+void master_service_set_service_count(struct master_service *service,
+				      unsigned int count);
+/* Returns the number of client connections we will handle before shutting
+   down. The value is decreased only after connection has been closed. */
+unsigned int master_service_get_service_count(struct master_service *service);
+/* Return the number of listener sockets. */
+unsigned int master_service_get_socket_count(struct master_service *service);
 
 /* Returns configuration file path. */
 const char *master_service_get_config_path(struct master_service *service);
@@ -38,10 +68,14 @@
 const char *master_service_get_version_string(struct master_service *service);
 
 /* Start the service. Blocks until finished */
-void master_service_run(struct master_service *service);
+void master_service_run(struct master_service *service,
+			master_service_connection_callback_t *callback);
 /* Stop a running service. */
 void master_service_stop(struct master_service *service);
 
+/* Call whenever a client connection is destroyed. */
+void master_service_client_connection_destroyed(struct master_service *service);
+
 /* Deinitialize the service. */
 void master_service_deinit(struct master_service **service);
 
--- a/src/lib-settings/settings-parser.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-settings/settings-parser.c	Thu Apr 23 19:53:44 2009 -0400
@@ -635,7 +635,7 @@
 		static const char *argv[] = {
 			NULL,
 			"-c", NULL,
-			"-s", NULL,
+			"-p", NULL,
 			NULL
 		};
 		argv[0] = bin_path;
--- a/src/lib-settings/settings-parser.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-settings/settings-parser.h	Thu Apr 23 19:53:44 2009 -0400
@@ -108,7 +108,7 @@
 int settings_parse_stream(struct setting_parser_context *ctx,
 			  struct istream *input);
 /* Read data from input stream and parser it. returns -1 = error,
-   0 = eof/stream error, 1 = not finished yet (stream is non-blocking) */
+   0 = done, 1 = not finished yet (stream is non-blocking) */
 int settings_parse_stream_read(struct setting_parser_context *ctx,
          		       struct istream *input);
 /* Open file and parse it. */
--- a/src/lib-storage/mail-storage-service.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-storage/mail-storage-service.c	Thu Apr 23 19:53:44 2009 -0400
@@ -448,7 +448,8 @@
 		str = t_str_new(256);
 		var_expand(str, user_set->mail_log_prefix,
 			   get_var_expand_table(service, input));
-		master_service_init_log(service, str_c(str));
+		master_service_init_log(service, str_c(str),
+					user_set->mail_log_max_lines_per_sec);
 	} T_END;
 }
 
@@ -474,9 +475,7 @@
 	if ((flags & MAIL_STORAGE_SERVICE_FLAG_DEBUG) != 0)
 		set_keyval(service->set_parser, "mail_debug", "yes");
 
-	/* now that we've read settings, we can set up logging */
 	mail_storage_service_init_log(service, &input);
-
 	set = master_service_settings_get(service);
 	sets = master_service_settings_get_others(service);
 	user_set = sets[0];
@@ -563,16 +562,16 @@
 
 	mail_storage_service_init_settings(service, set_roots, FALSE);
 
-	/* do all the global initialization. delay initializing plugins until
-	   we drop privileges the first time. */
-	master_service_init_log(service,
-				t_strdup_printf("%s: ", service->name));
-
 	set = master_service_settings_get(service);
 	sets = master_service_settings_get_others(service);
 	user_set = sets[0];
 	mail_set = mail_user_set_get_storage_set(user_set);
 
+	/* do all the global initialization. delay initializing plugins until
+	   we drop privileges the first time. */
+	master_service_init_log(service, t_strconcat(service->name, ": ", NULL),
+				user_set->mail_log_max_lines_per_sec);
+
 	modules = *user_set->mail_plugins == '\0' ? NULL :
 		module_dir_load(user_set->mail_plugin_dir,
 				user_set->mail_plugins, TRUE,
--- a/src/lib-storage/mail-storage-settings.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-storage/mail-storage-settings.c	Thu Apr 23 19:53:44 2009 -0400
@@ -145,6 +145,7 @@
 	DEF(SET_UINT, last_valid_uid),
 	DEF(SET_UINT, first_valid_gid),
 	DEF(SET_UINT, last_valid_gid),
+	DEF(SET_UINT, mail_log_max_lines_per_sec),
 
 	DEF(SET_STR, mail_plugins),
 	DEF(SET_STR, mail_plugin_dir),
@@ -174,6 +175,8 @@
 	MEMBER(first_valid_gid) 1,
 	MEMBER(last_valid_gid) 0,
 
+	MEMBER(mail_log_max_lines_per_sec) 10,
+
 	MEMBER(mail_plugins) "",
 	MEMBER(mail_plugin_dir) MODULEDIR,
 
--- a/src/lib-storage/mail-storage-settings.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib-storage/mail-storage-settings.h	Thu Apr 23 19:53:44 2009 -0400
@@ -54,6 +54,8 @@
 	unsigned int first_valid_uid, last_valid_uid;
 	unsigned int first_valid_gid, last_valid_gid;
 
+	unsigned int mail_log_max_lines_per_sec;
+
 	const char *mail_plugins;
 	const char *mail_plugin_dir;
 
--- a/src/lib/failures.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib/failures.c	Thu Apr 23 19:53:44 2009 -0400
@@ -3,6 +3,7 @@
 #include "lib.h"
 #include "ioloop.h"
 #include "str.h"
+#include "hostpid.h"
 #include "network.h"
 #include "backtrace-string.h"
 #include "printf-format-fix.h"
@@ -15,15 +16,13 @@
 #include <syslog.h>
 #include <time.h>
 
-const char *failure_log_type_prefixes[] = {
+const char *failure_log_type_prefixes[LOG_TYPE_COUNT] = {
 	"Info: ",
 	"Warning: ",
 	"Error: ",
 	"Fatal: ",
-	"Panic: "
-};
-static char log_type_internal_chars[] = {
-	'I', 'W', 'E', 'F', 'P'
+	"Panic: ",
+	"Error: "
 };
 
 /* Initialize working defaults */
@@ -37,6 +36,9 @@
 static char *log_prefix = NULL, *log_stamp_format = NULL;
 static bool failure_ignore_errors = FALSE;
 
+static void ATTR_FORMAT(2, 0)
+i_internal_error_handler(enum log_type type, const char *fmt, va_list args);
+
 /* kludgy .. we want to trust log_stamp_format with -Wformat-nonliteral */
 static const char *get_log_stamp_format(const char *unused)
 	ATTR_FORMAT_ARG(1);
@@ -59,9 +61,6 @@
 	char buf[256];
 	time_t now;
 
-	if (log_prefix != NULL)
-		str_append(str, log_prefix);
-
 	if (log_stamp_format != NULL) {
 		now = time(NULL);
 		tm = localtime(&now);
@@ -70,6 +69,8 @@
 			     get_log_stamp_format("unused"), tm) > 0)
 			str_append(str, buf);
 	}
+	if (log_prefix != NULL)
+		str_append(str, log_prefix);
 }
 
 static void log_fd_flush_stop(struct ioloop *ioloop)
@@ -193,6 +194,23 @@
 	va_list args;
 
 	va_start(args, format);
+
+	if (type == LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL &&
+	    error_handler != i_internal_error_handler) {
+		/* this is handled specially only by internal logging.
+		   skip over the pid. */
+		T_BEGIN {
+			const char *str;
+
+			str = t_strdup_vprintf(format, args);
+			while (*str >= '0' && *str <= '9') str++;
+			if (*str == ' ') str++;
+
+			i_error("%s", str);
+		} T_END;
+		return;
+	}
+
 	if (type == LOG_TYPE_INFO)
 		info_handler(type, format, args);
 	else
@@ -328,12 +346,16 @@
 		level = LOG_WARNING;
 		break;
 	case LOG_TYPE_ERROR:
+	case LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL:
 		level = LOG_ERR;
 		break;
 	case LOG_TYPE_FATAL:
 	case LOG_TYPE_PANIC:
 		level = LOG_CRIT;
 		break;
+	case LOG_TYPE_COUNT:
+	case LOG_TYPE_OPTION:
+		i_unreached();
 	}
 
 	if (syslog_handler(level, type, fmt, args) < 0)
@@ -399,8 +421,28 @@
 	log_prefix = i_strdup(prefix);
 }
 
+static int internal_send_split(string_t *full_str, unsigned int prefix_len)
+{
+	string_t *str;
+	unsigned int max_text_len, pos = prefix_len;
+
+	str = t_str_new(PIPE_BUF);
+	str_append_n(str, str_c(full_str), prefix_len);
+	max_text_len = PIPE_BUF - prefix_len - 1;
+
+	while (pos < str_len(full_str)) {
+		str_truncate(str, prefix_len);
+		str_append_n(str, str_c(full_str) + pos, max_text_len);
+		str_append_c(str, '\n');
+		if (log_fd_write(2, str_data(str), str_len(str)) < 0)
+			return -1;
+		pos += max_text_len;
+	}
+	return 0;
+}
+
 static int ATTR_FORMAT(2, 0)
-internal_handler(char log_type, const char *format, va_list args)
+internal_handler(enum log_type log_type, const char *format, va_list args)
 {
 	static int recursed = 0;
 	int ret;
@@ -414,13 +456,18 @@
 	recursed++;
 	T_BEGIN {
 		string_t *str;
+		unsigned int prefix_len;
 
 		str = t_str_new(512);
-		str_append_c(str, 1);
-		str_append_c(str, log_type);
+		str_printfa(str, "\001%c%s ", log_type + 1, my_pid);
+		prefix_len = str_len(str);
+
 		str_vprintfa(str, format, args);
 		str_append_c(str, '\n');
-		ret = write_full(2, str_data(str), str_len(str));
+		if (str_len(str) <= PIPE_BUF)
+			ret = log_fd_write(2, str_data(str), str_len(str));
+		else
+			ret = internal_send_split(str, prefix_len);
 	} T_END;
 
 	if (ret < 0 && failure_ignore_errors)
@@ -430,21 +477,62 @@
 	return ret;
 }
 
+static bool line_is_ok(const char *line)
+{
+	if (*line != 1)
+		return FALSE;
+
+	if (line[1] == '\0') {
+		i_warning("Broken log line follows (type=NUL)");
+		return FALSE;
+	}
+
+	if (line[1]-1 > LOG_TYPE_OPTION) {
+		i_warning("Broken log line follows (type=%d)", line[1]-1);
+		return FALSE;
+	}
+	return TRUE;
+}
+
+void i_failure_parse_line(const char *line, struct failure_line *failure)
+{
+	memset(failure, 0, sizeof(*failure));
+	if (!line_is_ok(line)) {
+		failure->log_type = LOG_TYPE_ERROR;
+		failure->text = line;
+		return;
+	}
+	failure->log_type = line[1] - 1;
+
+	line += 2;
+	failure->text = line;
+	while (*line >= '0' && *line <= '9') {
+		failure->pid = failure->pid*10 + (*line - '0');
+		line++;
+	}
+	if (*line != ' ') {
+		/* some old protocol? */
+		failure->pid = 0;
+		return;
+	}
+	failure->text = line + 1;
+}
+
 static void ATTR_NORETURN ATTR_FORMAT(3, 0)
 i_internal_fatal_handler(enum log_type type, int status,
 			 const char *fmt, va_list args)
 {
-	if (internal_handler(log_type_internal_chars[type], fmt, args) < 0 &&
+	if (internal_handler(type, fmt, args) < 0 &&
 	    status == FATAL_DEFAULT)
 		status = FATAL_LOGERROR;
 
 	default_fatal_finish(type, status);
 }
 
-static void ATTR_FORMAT(2, 0)
+static void
 i_internal_error_handler(enum log_type type, const char *fmt, va_list args)
 {
-	if (internal_handler(log_type_internal_chars[type], fmt, args) < 0)
+	if (internal_handler(type, fmt, args) < 0)
 		failure_exit(FATAL_LOGERROR);
 }
 
@@ -480,7 +568,8 @@
 	const char *str;
 
 	if (error_handler == i_internal_error_handler) {
-		str = t_strdup_printf("\x01Oip=%s\n", net_ip2addr(ip));
+		str = t_strdup_printf("\001%c%s ip=%s\n",
+				      LOG_TYPE_OPTION, my_pid, net_ip2addr(ip));
 		(void)write_full(2, str, strlen(str));
 	}
 }
--- a/src/lib/failures.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib/failures.h	Thu Apr 23 19:53:44 2009 -0400
@@ -19,7 +19,21 @@
 	LOG_TYPE_WARNING,
 	LOG_TYPE_ERROR,
 	LOG_TYPE_FATAL,
-	LOG_TYPE_PANIC
+	LOG_TYPE_PANIC,
+
+	/* Special message from master to log process: Log message begins with
+	   "<pid> " and if <pid> has already logged a fatal/panic, this message
+	   shouldn't be written to the log. Otherwise log as an error. */
+	LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL,
+
+	LOG_TYPE_COUNT,
+	LOG_TYPE_OPTION
+};
+
+struct failure_line {
+	pid_t pid;
+	enum log_type log_type;
+	const char *text;
 };
 
 #define DEFAULT_FAILURE_STAMP_FORMAT "%b %d %H:%M:%S "
@@ -95,6 +109,9 @@
 /* Call the exit callback and exit() */
 void failure_exit(int status) ATTR_NORETURN;
 
+/* Parse a line logged using internal failure handler */
+void i_failure_parse_line(const char *line, struct failure_line *failure);
+
 void failures_deinit(void);
 
 #endif
--- a/src/lib/restrict-access.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib/restrict-access.c	Thu Apr 23 19:53:44 2009 -0400
@@ -20,6 +20,7 @@
 static gid_t process_primary_gid = (gid_t)-1;
 static gid_t process_privileged_gid = (gid_t)-1;
 static bool process_using_priv_gid = FALSE;
+static char *chroot_dir = NULL;
 
 void restrict_access_init(struct restrict_access_settings *set)
 {
@@ -273,6 +274,7 @@
 
 		if (chroot(set->chroot_dir) != 0)
 			i_fatal("chroot(%s) failed: %m", set->chroot_dir);
+		chroot_dir = i_strdup(set->chroot_dir);
 
 		if (home != NULL) {
 			if (chdir(home) < 0) {
@@ -407,6 +409,11 @@
 	env_remove("RESTRICT_CHROOT");
 }
 
+const char *restrict_access_get_current_chroot(void)
+{
+	return chroot_dir;
+}
+
 void restrict_access_allow_coredumps(bool allow ATTR_UNUSED)
 {
 #ifdef HAVE_PR_SET_DUMPABLE
--- a/src/lib/restrict-access.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lib/restrict-access.h	Thu Apr 23 19:53:44 2009 -0400
@@ -37,6 +37,10 @@
    environment settings. */
 void restrict_access_by_env(const char *home, bool disallow_root);
 
+/* Return the chrooted directory if restrict_access*() chrooted,
+   otherwise NULL. */
+const char *restrict_access_get_current_chroot(void);
+
 /* Try to set up the process in a way that core dumps are still allowed
    after calling restrict_access_by_env(). */
 void restrict_access_allow_coredumps(bool allow);
--- a/src/lmtp/client.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lmtp/client.c	Thu Apr 23 19:53:44 2009 -0400
@@ -7,6 +7,7 @@
 #include "istream.h"
 #include "ostream.h"
 #include "hostpid.h"
+#include "master-service.h"
 #include "master-service-settings.h"
 #include "mail-namespace.h"
 #include "mail-storage.h"
@@ -195,7 +196,7 @@
 	pool_unref(&client->state_pool);
 	i_free(client);
 
-	listener_client_destroyed();
+	master_service_client_connection_destroyed(service);
 }
 
 static const char *client_get_disconnect_reason(struct client *client)
--- a/src/lmtp/main.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/lmtp/main.c	Thu Apr 23 19:53:44 2009 -0400
@@ -20,33 +20,17 @@
 #define IS_STANDALONE() \
         (getenv("MASTER_SERVICE") == NULL)
 
-struct lmtp_listener {
-	int fd;
-	struct io *io;
-};
-
 struct master_service *service;
 struct mail_storage_service_multi_ctx *multi_service;
 
-static struct io *log_io = NULL;
-static ARRAY_DEFINE(listeners, struct lmtp_listener *);
-
-static void log_error_callback(void *context ATTR_UNUSED)
-{
-	/* the log fd is closed, don't die when trying to log later */
-	i_set_failure_ignore_errors(TRUE);
-
-	master_service_stop(service);
-}
-
-static void listen_connected(struct lmtp_listener *l)
+static void client_connected(const struct master_service_connection *conn)
 {
 	struct client *client;
 	struct ip_addr remote_ip;
 	unsigned int remote_port;
 	int fd;
 
-	fd = net_accept(l->fd, &remote_ip, &remote_port);
+	fd = net_accept(conn->fd, &remote_ip, &remote_port);
 	if (fd < 0) {
 		if (fd < -1)
 			i_error("accept() failed: %m");
@@ -59,80 +43,15 @@
 	(void)net_getsockname(fd, &client->local_ip, &client->local_port);
 }
 
-static void listen_start(void)
-{
-	struct lmtp_listener *const *l;
-	unsigned int i, count;
-
-	l = array_get(&listeners, &count);
-	for (i = 0; i < count; i++) {
-		i_assert(l[i]->io == NULL);
-		l[i]->io = io_add(l[i]->fd, IO_READ, listen_connected, l[i]);
-	}
-}
-
-static void listen_stop(void)
-{
-	struct lmtp_listener *const *l;
-	unsigned int i, count;
-
-	l = array_get(&listeners, &count);
-	for (i = 0; i < count; i++) {
-		i_assert(l[i]->io != NULL);
-		io_remove(&l[i]->io);
-	}
-}
-
-static void listen_free(void)
-{
-	struct lmtp_listener **l;
-	unsigned int i, count;
-
-	l = array_get_modifiable(&listeners, &count);
-	for (i = 0; i < count; i++) {
-		if (l[i]->io != NULL)
-			io_remove(&l[i]->io);
-		i_free(l[i]);
-	}
-	array_free(&listeners);
-}
-
-void listener_client_destroyed(void)
-{
-	if (array_count(&listeners) == 0)
-		master_service_stop(service);
-}
-
 static void main_init(void)
 {
-	struct lmtp_listener *l;
-	const char *value;
-	unsigned int i, count;
-
-	/* If master dies, the log fd gets closed and we'll quit */
-	log_io = io_add(STDERR_FILENO, IO_ERROR, log_error_callback, NULL);
-
-	value = getenv("LISTEN_FDS");
-	count = value == NULL ? 0 : atoi(value);
-	i_array_init(&listeners, count + 1);
-	for (i = 0; i < count; i++) {
-		l = i_new(struct lmtp_listener, 1);
-		l->fd = LMTP_MASTER_FIRST_LISTEN_FD + i;
-		array_append(&listeners, &l, 1);
-	}
-
-	if (count == 0)
+	if (IS_STANDALONE())
 		(void)client_create(STDIN_FILENO, STDOUT_FILENO);
-	else
-		listen_start();
 }
 
 static void main_deinit(void)
 {
-	if (log_io != NULL)
-		io_remove(&log_io);
 	clients_destroy();
-	listen_free();
 }
 
 int main(int argc, char *argv[], char *envp[])
@@ -157,13 +76,15 @@
 	}
 #endif
 
-	if (IS_STANDALONE())
-		service_flags |= MASTER_SERVICE_FLAG_STANDALONE;
+	if (IS_STANDALONE()) {
+		service_flags |= MASTER_SERVICE_FLAG_STANDALONE |
+			MASTER_SERVICE_FLAG_STD_CLIENT;
+	}
 
 	service = master_service_init("lmtp", service_flags, argc, argv);
 	while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
 		if (!master_service_parse_option(service, c, optarg))
-			i_fatal("Unknown argument: %c", c);
+			exit(FATAL_DEFAULT);
 	}
 
 	multi_service = mail_storage_service_multi_init(service, set_roots,
@@ -173,7 +94,7 @@
         process_title_init(argv, envp);
 
 	main_init();
-	master_service_run(service);
+	master_service_run(service, client_connected);
 
 	main_deinit();
 	mail_storage_service_multi_deinit(&multi_service);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/log/Makefile.am	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,19 @@
+pkglibexecdir = $(libexecdir)/dovecot
+
+pkglibexec_PROGRAMS = log
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-settings \
+	-I$(top_srcdir)/src/lib-master
+
+log_LDADD = $(LIBDOVECOT)
+log_DEPENDENCIES = $(LIBDOVECOT)
+
+log_SOURCES = \
+	log-connection.c \
+	main.c
+
+noinst_HEADERS = \
+	common.h \
+	log-connection.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/log/common.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,9 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include "lib.h"
+
+extern struct master_service *service;
+extern pid_t master_pid;
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/log/log-connection.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,230 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "ioloop.h"
+#include "llist.h"
+#include "hash.h"
+#include "master-interface.h"
+#include "master-service.h"
+#include "log-connection.h"
+
+#include <unistd.h>
+
+#define FATAL_QUEUE_TIMEOUT_MSECS 500
+
+struct log_client {
+	struct ip_addr ip;
+	unsigned int fatal_logged:1;
+};
+
+struct log_connection {
+	struct log_connection *prev, *next;
+
+	int fd;
+	struct io *io;
+
+	char *prefix;
+	struct hash_table *clients;
+
+	unsigned int handshaked:1;
+};
+
+static struct log_connection *log_connections = NULL;
+
+static struct log_client *log_client_get(struct log_connection *log, pid_t pid)
+{
+	struct log_client *client;
+
+	client = hash_table_lookup(log->clients, POINTER_CAST(pid));
+	if (client == NULL) {
+		client = i_new(struct log_client, 1);
+		hash_table_insert(log->clients, POINTER_CAST(pid), client);
+	}
+	return client;
+}
+
+static void log_parse_ip(struct log_connection *log,
+			 const struct failure_line *failure)
+{
+	struct log_client *client;
+
+	client = log_client_get(log, failure->pid);
+	(void)net_addr2ip(failure->text + 3, &client->ip);
+}
+
+static void log_remove_pid(struct log_connection *log, pid_t pid)
+{
+	struct log_client *client;
+
+	client = hash_table_lookup(log->clients, POINTER_CAST(pid));
+	if (client != NULL) {
+		hash_table_remove(log->clients, POINTER_CAST(pid));
+		i_free(client);
+	}
+}
+
+static void log_parse_option(struct log_connection *log,
+			     const struct failure_line *failure)
+{
+	if (strncmp(failure->text, "ip=", 3) == 0)
+		log_parse_ip(log, failure);
+	else if (strcmp(failure->text, "bye") == 0)
+		log_remove_pid(log, failure->pid);
+}
+
+static bool
+log_handle_seen_fatal(struct log_connection *log, const char **_text)
+{
+	const char *text = *_text;
+	struct log_client *client;
+	pid_t pid = 0;
+
+	while (*text >= '0' && *text <= '9') {
+		pid = pid*10 + (*text - '0');
+		text++;
+	}
+	if (*text != ' ' || pid == 0)
+		return FALSE;
+	*_text = text;
+
+	client = hash_table_lookup(log->clients, POINTER_CAST(pid));
+	if (client != NULL && client->fatal_logged) {
+		log_remove_pid(log, pid);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+static void log_it(struct log_connection *log, const char *line)
+{
+	struct failure_line failure;
+	struct log_client *client;
+
+	i_failure_parse_line(line, &failure);
+	switch (failure.log_type) {
+	case LOG_TYPE_FATAL:
+	case LOG_TYPE_PANIC:
+		client = log_client_get(log, failure.pid);
+		client->fatal_logged = TRUE;
+		break;
+	case LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL:
+		/* Special case for master connection. If the following PID
+		   has logged a fatal/panic, don't log this message. */
+		failure.log_type = LOG_TYPE_ERROR;
+		if (failure.pid != master_pid) {
+			i_error("Non-master process %s "
+				"sent LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL",
+				dec2str(failure.pid));
+			break;
+		}
+
+		if (log_handle_seen_fatal(log, &failure.text))
+			return;
+		break;
+	case LOG_TYPE_OPTION:
+		log_parse_option(log, &failure);
+		return;
+	default:
+		break;
+	}
+	i_assert(failure.log_type < LOG_TYPE_COUNT);
+
+	i_set_failure_prefix(log->prefix);
+	i_log_type(failure.log_type, "%s", failure.text);
+	i_set_failure_prefix("log: ");
+}
+
+static bool log_connection_handshake(struct log_connection *log,
+				     char **data, size_t size)
+{
+	struct log_service_handshake handshake;
+
+	if (size < sizeof(handshake))
+		return FALSE;
+
+	memcpy(&handshake, *data, sizeof(handshake));
+	if (handshake.log_magic != MASTER_LOG_MAGIC)
+		return FALSE;
+
+	if (handshake.prefix_len <= size - sizeof(handshake)) {
+		log->prefix = i_strndup(*data + sizeof(handshake),
+					handshake.prefix_len);
+		*data += sizeof(handshake) + handshake.prefix_len;
+	}
+	log->handshaked = TRUE;
+	return TRUE;
+}
+
+static void log_connection_input(struct log_connection *log)
+{
+	char data[PIPE_BUF+1], *p, *line;
+	ssize_t ret;
+
+	ret = read(log->fd, data, sizeof(data)-1);
+	if (ret <= 0) {
+		if (ret < 0)
+			i_error("read(log pipe) failed: %m");
+		log_connection_destroy(log);
+		return;
+	}
+	data[ret] = '\0';
+
+	line = data;
+	if (!log->handshaked)
+		log_connection_handshake(log, &line, ret);
+
+	p = line;
+	while ((p = strchr(line, '\n')) != NULL) {
+		*p = '\0';
+		log_it(log, line);
+		line = p + 1;
+	}
+	if (line - data != ret) {
+		i_error("Invalid log line follows: Missing LF");
+		log_it(log, line);
+	}
+}
+
+struct log_connection *log_connection_create(int fd)
+{
+	struct log_connection *log;
+
+	log = i_new(struct log_connection, 1);
+	log->fd = fd;
+	log->io = io_add(fd, IO_READ, log_connection_input, log);
+	log->clients = hash_table_create(default_pool, default_pool, 0,
+					 NULL, NULL);
+
+	DLLIST_PREPEND(&log_connections, log);
+	log_connection_input(log);
+	return log;
+}
+
+void log_connection_destroy(struct log_connection *log)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	DLLIST_REMOVE(&log_connections, log);
+
+	iter = hash_table_iterate_init(log->clients);
+	while (hash_table_iterate(iter, &key, &value))
+		i_free(value);
+	hash_table_iterate_deinit(&iter);
+	hash_table_destroy(&log->clients);
+
+	if (log->io != NULL)
+		io_remove(&log->io);
+	i_free(log->prefix);
+	i_free(log);
+
+        master_service_client_connection_destroyed(service);
+}
+
+void log_connections_deinit(void)
+{
+	/* normally we don't exit until all log connections are gone,
+	   but we could get here when we're being killed by a signal */
+	while (log_connections != NULL)
+		log_connection_destroy(log_connections);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/log/log-connection.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,9 @@
+#ifndef LOG_CONNECTION_H
+#define LOG_CONNECTION_H
+
+struct log_connection *log_connection_create(int fd);
+void log_connection_destroy(struct log_connection *log);
+
+void log_connections_deinit(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/log/main.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,73 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "lib-signals.h"
+#include "master-interface.h"
+#include "master-service.h"
+#include "master-service-settings.h"
+#include "log-connection.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+struct master_service *service;
+pid_t master_pid;
+
+static void
+sig_reread_config(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
+{
+	// FIXME
+}
+
+static void
+sig_reopen_logs(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
+{
+	// FIXME
+}
+
+static void main_init(void)
+{
+        lib_signals_set_handler(SIGHUP, TRUE, sig_reread_config, NULL);
+	lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL);
+
+	master_pid = getppid();
+}
+
+static void main_deinit(void)
+{
+	log_connections_deinit();
+}
+
+static void client_connected(const struct master_service_connection *conn)
+{
+	log_connection_create(conn->fd);
+}
+
+int main(int argc, char *argv[])
+{
+	const char *error;
+	int c;
+
+	service = master_service_init("log", 0, argc, argv);
+
+	/* use log prefix and log to stderr until we've configured the real
+	   logging */
+	i_set_failure_file("/dev/stderr", "log: ");
+
+	while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
+		if (!master_service_parse_option(service, c, optarg))
+			exit(FATAL_DEFAULT);
+	}
+
+	if (master_service_settings_read(service, NULL, NULL, FALSE,
+					 &error) < 0)
+		i_fatal("Error reading configuration: %s", error);
+
+	master_service_init_log(service, "log: ", 0);
+	master_service_init_finish(service);
+	main_init();
+	master_service_run(service, client_connected);
+	main_deinit();
+	master_service_deinit(&service);
+        return 0;
+}
--- a/src/login-common/Makefile.am	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/Makefile.am	Thu Apr 23 19:53:44 2009 -0400
@@ -4,6 +4,7 @@
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-auth \
+	-I$(top_srcdir)/src/lib-master \
 	-DPKG_RUNDIR=\""$(rundir)"\" \
 	-DPKG_STATEDIR=\""$(statedir)"\" \
 	-DSBINDIR=\""$(sbindir)"\" \
@@ -14,7 +15,6 @@
 	login-proxy.c \
 	login-settings.c \
 	main.c \
-	master.c \
 	sasl-server.c \
 	ssl-proxy.c \
 	ssl-proxy-gnutls.c \
@@ -28,7 +28,6 @@
 	login-proxy.h \
 	login-settings.h \
 	common.h \
-	master.h \
 	sasl-server.h \
 	ssl-proxy.h
 
--- a/src/login-common/client-common.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/client-common.h	Thu Apr 23 19:53:44 2009 -0400
@@ -2,7 +2,6 @@
 #define CLIENT_COMMON_H
 
 #include "network.h"
-#include "master.h"
 #include "sasl-server.h"
 
 /* max. size of input buffer. this means:
@@ -29,7 +28,6 @@
 	struct auth_request *auth_request;
 
 	unsigned int master_tag;
-	master_callback_t *master_callback;
 	sasl_server_callback_t *sasl_callback;
 
 	unsigned int auth_attempts;
@@ -39,6 +37,7 @@
 	unsigned int tls:1;
 	unsigned int secured:1;
 	unsigned int trusted:1;
+	unsigned int proxying:1;
 	unsigned int authenticating:1;
 	unsigned int auth_tried_disabled_plaintext:1;
 	/* ... */
--- a/src/login-common/common.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/common.h	Thu Apr 23 19:53:44 2009 -0400
@@ -12,20 +12,12 @@
 #define AUTH_PLAINTEXT_DISABLED_MSG \
 	"Plaintext authentication disallowed on non-secure (SSL/TLS) connections."
 
-extern const char *login_protocol;
+extern const char *login_protocol, *login_process_name;
 
 extern struct auth_client *auth_client;
 extern bool closing_down;
-extern unsigned int login_process_uid;
 
+extern struct master_service *service;
 extern struct login_settings *login_settings;
 
-void main_ref(void);
-void main_unref(void);
-
-void main_listen_start(void);
-void main_listen_stop(void);
-
-void connection_queue_add(unsigned int connection_count);
-
 #endif
--- a/src/login-common/login-proxy.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/login-proxy.c	Thu Apr 23 19:53:44 2009 -0400
@@ -6,6 +6,7 @@
 #include "ostream.h"
 #include "llist.h"
 #include "str-sanitize.h"
+#include "master-service.h"
 #include "client-common.h"
 #include "login-proxy.h"
 
@@ -31,7 +32,6 @@
 };
 
 static struct login_proxy *login_proxies = NULL;
-static unsigned int login_proxy_count = 0;
 
 static void server_input(struct login_proxy *proxy)
 {
@@ -191,9 +191,7 @@
 
 	if (proxy->client_fd != -1) {
 		/* detached proxy */
-		main_unref();
 		DLLIST_REMOVE(&login_proxies, proxy);
-		login_proxy_count--;
 
 		ipstr = net_ip2addr(&proxy->ip);
 		i_info("proxy(%s): disconnecting %s",
@@ -224,7 +222,7 @@
 	i_free(proxy->user);
 	i_free(proxy);
 
-	main_listen_start();
+	master_service_client_connection_destroyed(service);
 }
 
 bool login_proxy_is_ourself(const struct client *client, const char *host,
@@ -253,11 +251,6 @@
 	return proxy->port;
 }
 
-unsigned int login_proxy_get_count(void)
-{
-	return login_proxy_count;
-}
-
 void login_proxy_detach(struct login_proxy *proxy, struct istream *client_input,
 			struct ostream *client_output)
 {
@@ -291,9 +284,7 @@
 	proxy->callback = NULL;
 	proxy->context = NULL;
 
-	login_proxy_count++;
 	DLLIST_PREPEND(&login_proxies, proxy);
-	main_ref();
 }
 
 void login_proxy_deinit(void)
--- a/src/login-common/login-proxy.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/login-proxy.h	Thu Apr 23 19:53:44 2009 -0400
@@ -39,9 +39,6 @@
 const char *login_proxy_get_host(const struct login_proxy *proxy) ATTR_PURE;
 unsigned int login_proxy_get_port(const struct login_proxy *proxy) ATTR_PURE;
 
-/* Return number of active detached login proxies */
-unsigned int login_proxy_get_count(void) ATTR_PURE;
-
 void login_proxy_deinit(void);
 
 #endif
--- a/src/login-common/login-settings.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/login-settings.c	Thu Apr 23 19:53:44 2009 -0400
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "settings-parser.h"
+#include "master-service-settings.h"
 #include "login-settings.h"
 
 #include <stddef.h>
@@ -92,8 +93,6 @@
 	MEMBER(check_func) login_settings_check
 };
 
-static pool_t settings_pool = NULL;
-
 /* <settings checks> */
 static int ssl_settings_check(void *_set ATTR_UNUSED, const char **error_r)
 {
@@ -173,30 +172,19 @@
 }
 /* </settings checks> */
 
-struct login_settings *login_settings_read(void)
+struct login_settings *login_settings_read(struct master_service *service)
 {
-	struct setting_parser_context *parser;
-        struct login_settings *set;
+	static const struct setting_parser_info *set_roots[] = {
+		&login_setting_parser_info,
+		NULL
+	};
 	const char *error;
-
-	if (settings_pool == NULL)
-		settings_pool = pool_alloconly_create("settings pool", 512);
-	else
-		p_clear(settings_pool);
+	void **sets;
 
-	parser = settings_parser_init(settings_pool,
-				      &login_setting_parser_info,
-				      SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+	if (master_service_settings_read(service, set_roots, NULL, FALSE,
+					 &error) < 0)
+		i_fatal("Error reading configuration: %s", error);
 
-	if (settings_parse_environ(parser) < 0) {
-		i_fatal("Error reading configuration: %s",
-			settings_parser_get_error(parser));
-	}
-
-	if (settings_parser_check(parser, settings_pool, &error) < 0)
-		i_fatal("Invalid settings: %s", error);
-
-	set = settings_parser_get(parser);
-	settings_parser_deinit(&parser);
-	return set;
+	sets = master_service_settings_get_others(service);
+	return sets[0];
 }
--- a/src/login-common/login-settings.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/login-settings.h	Thu Apr 23 19:53:44 2009 -0400
@@ -1,6 +1,8 @@
 #ifndef LOGIN_SETTINGS_H
 #define LOGIN_SETTINGS_H
 
+struct master_service;
+
 struct login_settings {
 	const char *login_dir;
 	bool login_chroot;
@@ -35,6 +37,6 @@
 	const char *const *log_format_elements_split;
 };
 
-struct login_settings *login_settings_read(void);
+struct login_settings *login_settings_read(struct master_service *service);
 
 #endif
--- a/src/login-common/main.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/main.c	Thu Apr 23 19:53:44 2009 -0400
@@ -2,14 +2,13 @@
 
 #include "common.h"
 #include "ioloop.h"
-#include "array.h"
-#include "lib-signals.h"
 #include "randgen.h"
 #include "restrict-access.h"
 #include "restrict-process-size.h"
 #include "process-title.h"
-#include "fd-close-on-exec.h"
-#include "master.h"
+#include "master-auth.h"
+#include "master-service.h"
+#include "master-interface.h"
 #include "client-common.h"
 #include "auth-client.h"
 #include "ssl-proxy.h"
@@ -19,231 +18,45 @@
 #include <unistd.h>
 #include <syslog.h>
 
-struct login_settings *login_settings;
-unsigned int login_process_uid;
 struct auth_client *auth_client;
 bool closing_down;
 
-static const char *process_name;
-static struct ioloop *ioloop;
-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++;
-}
+struct master_service *service;
+struct login_settings *login_settings;
 
-void main_unref(void)
-{
-	if (--main_refcount == 0) {
-		/* nothing to do, quit */
-		io_loop_stop(ioloop);
-	} else if (closing_down && clients_get_count() == 0) {
-		/* last login finished, close all communications
-		   to master process */
-		master_close();
-		/* we might still be proxying. close the connection to
-		   dovecot-auth, since it's not needed anymore. */
-		if (auth_client != NULL)
-			auth_client_free(&auth_client);
-	} else if (clients_get_count() == 0) {
-		/* make sure we clear all the memory used by the
-		   authentication connections. also this makes sure that if
-		   this connection's authentication was finished but the master
-		   login wasn't, the next connection won't be able to log in
-		   as this user by finishing the master login. */
-		if (auth_client != NULL)
-			auth_client_reconnect(auth_client);
-	}
-}
+static bool ssl_connections = FALSE;
 
-static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
+static void client_connected(const struct master_service_connection *conn)
 {
-	/* warn about being killed because of some signal, except SIGINT (^C)
-	   which is too common at least while testing :) */
-	if (si->si_signo != SIGINT) {
-		i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
-			  si->si_signo, dec2str(si->si_pid),
-			  dec2str(si->si_uid),
-			  lib_signal_code_to_str(si->si_signo, si->si_code));
-	}
-	io_loop_stop(ioloop);
-}
-
-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(listen_fd, &remote_ip, &remote_port);
-	if (fd < 0) {
-		if (fd < -1)
-			i_error("accept() failed: %m");
-		return;
-	}
-	i_set_failure_ip(&remote_ip);
-
-	if (net_getsockname(fd, &local_ip, &local_port) < 0) {
-		memset(&local_ip, 0, sizeof(local_ip));
-		local_port = 0;
-	}
-
-	client = client_create(fd, FALSE, &local_ip, &remote_ip);
-	client->remote_port = remote_port;
-	client->local_port = local_port;
-
-	if (login_settings->login_process_per_connection) {
-		closing_down = TRUE;
-		main_listen_stop();
-	}
-}
-
-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;
+	struct ip_addr local_ip;
+	unsigned int local_port;
+	int fd_ssl;
 
-	fd = net_accept(listen_fd, &remote_ip, &remote_port);
-	if (fd < 0) {
-		if (fd < -1)
-			i_error("accept() failed: %m");
-		return;
-	}
-	i_set_failure_ip(&remote_ip);
-
-	if (net_getsockname(fd, &local_ip, &local_port) < 0) {
+	if (net_getsockname(conn->fd, &local_ip, &local_port) < 0) {
 		memset(&local_ip, 0, sizeof(local_ip));
 		local_port = 0;
 	}
 
-	connection_queue_add(1);
-
-	fd_ssl = ssl_proxy_new(fd, &remote_ip, &proxy);
-	if (fd_ssl == -1)
-		net_disconnect(fd);
-	else {
-		client = client_create(fd_ssl, TRUE, &local_ip, &remote_ip);
-		client->proxy = proxy;
-		client->remote_port = remote_port;
-		client->local_port = local_port;
-	}
-
-	if (login_settings->login_process_per_connection) {
-		closing_down = TRUE;
-		main_listen_stop();
-	}
-}
-
-void main_listen_start(void)
-{
-	struct io *io;
-	unsigned int i, current_count;
-	int cur_fd;
-
-	if (listening)
-		return;
-	if (closing_down) {
-		/* typically happens only with
-		   login_process_per_connection=yes after client logs in */
-		master_notify_state_change(LOGIN_STATE_FULL_LOGINS);
-		return;
-	}
-
-	current_count = ssl_proxy_get_count() + login_proxy_get_count();
-	if (current_count >= login_settings->login_max_connections) {
-		/* can't accept any more connections until existing proxies
-		   get destroyed */
-		return;
-	}
-
-	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);
-	}
-	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;
-
-	/* the initial notification tells master that we're ok. if we die
-	   before sending it, the master should shutdown itself. */
-	master_notify_state_change(LOGIN_STATE_LISTENING);
-}
+	// FIXME: a global ssl_connections isn't enough!
+	if (!ssl_connections) {
+		client = client_create(conn->fd, FALSE, &local_ip,
+				       &conn->remote_ip);
+	} else {
+		fd_ssl = ssl_proxy_new(conn->fd, &conn->remote_ip, &proxy);
+		if (fd_ssl == -1) {
+			net_disconnect(conn->fd);
+			return;
+		}
 
-void main_listen_stop(void)
-{
-	struct io **ios;
-	unsigned int i, count;
-	int cur_fd;
-
-	if (!listening)
-		return;
-
-	ios = array_get_modifiable(&listen_ios, &count);
-	for (i = 0; i < count; i++)
-		io_remove(&ios[i]);
-	array_free(&listen_ios);
-
-	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);
-			}
-		}
+		client = client_create(fd_ssl, TRUE,
+				       &local_ip, &conn->remote_ip);
+		client->proxying = TRUE;
+		client->proxy = proxy;
 	}
-
-	listening = FALSE;
-	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)
-{
-	unsigned int max_connections = login_settings->login_max_connections;
-	unsigned int current_count;
-
-	if (login_settings->login_process_per_connection)
-		return;
-
-	current_count = clients_get_count() + ssl_proxy_get_count() +
-		login_proxy_get_count();
-	if (current_count + connection_count + 2 >= max_connections) {
-		/* after this client we've reached max users count,
-		   so stop listening for more. reserve +2 extra for SSL with
-		   login proxy connections. */
-		main_listen_stop();
-
-		if (current_count >= max_connections) {
-			/* already reached max. users count, kill few of the
-			   oldest connections.
-
-			   this happens when we've maxed out the login process
-			   count and master has told us to start listening for
-			   new connections even though we're full. */
-			client_destroy_oldest();
-		}
-	}
+	client->remote_port = conn->remote_port;
+	client->local_port = local_port;
 }
 
 static void auth_connect_notify(struct auth_client *client ATTR_UNUSED,
@@ -253,211 +66,101 @@
                 clients_notify_auth_connected();
 }
 
-static void drop_privileges(unsigned int *max_fds_r)
+static void main_preinit(void)
 {
-	const char *value;
-
-        login_settings = login_settings_read();
+	unsigned int max_fds;
 
-	if (!is_inetd)
-		i_set_failure_internal();
-	else {
-		/* log to syslog */
-		value = getenv("SYSLOG_FACILITY");
-		i_set_failure_syslog(process_name, LOG_NDELAY,
-				     value == NULL ? LOG_MAIL : atoi(value));
-	}
-
-	value = getenv("DOVECOT_VERSION");
-	if (value != NULL && strcmp(value, PACKAGE_VERSION) != 0) {
-		i_fatal("Dovecot version mismatch: "
-			"Master is v%s, login is v"PACKAGE_VERSION" "
-			"(if you don't care, set version_ignore=yes)", value);
-	}
-
-	value = getenv("LOGIN_DIR");
-	if (value == NULL)
-		i_fatal("LOGIN_DIR environment missing");
-	if (chdir(value) < 0)
-		i_error("chdir(%s) failed: %m", value);
-
+	random_init();
 	/* Initialize SSL proxy so it can read certificate and private
 	   key file. */
-	random_init();
 	ssl_proxy_init();
 
-	value = getenv("LISTEN_FDS");
-	listen_count = value == NULL ? 0 : atoi(value);
-	value = getenv("SSL_LISTEN_FDS");
-	ssl_listen_count = value == NULL ? 0 : atoi(value);
-
 	/* set the number of fds we want to use. it may get increased or
 	   decreased. leave a couple of extra fds for auth sockets and such.
 	   normal connections each use one fd, but SSL connections use two */
-	*max_fds_r = LOGIN_MASTER_SOCKET_FD + 16 +
-		listen_count + ssl_listen_count +
+	max_fds = MASTER_LISTEN_FD_FIRST + 16 +
+		master_service_get_socket_count(service) +
 		login_settings->login_max_connections*2;
-	restrict_fd_limit(*max_fds_r);
+	restrict_fd_limit(max_fds);
+	io_loop_set_max_fd_count(current_ioloop, max_fds);
 
-	/* Refuse to run as root - we should never need it and it's
-	   dangerous with SSL. */
+	i_assert(strcmp(login_settings->ssl, "no") == 0 || ssl_initialized);
+
 	restrict_access_by_env(NULL, TRUE);
-
-	/* make sure we can't fork() */
-	restrict_process_size((unsigned int)-1, 1);
 }
 
 static void main_init(void)
 {
-	const char *value;
-
-	lib_signals_init();
-        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
-        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
-        lib_signals_ignore(SIGPIPE, TRUE);
+	/* make sure we can't fork() */
+	restrict_process_size((unsigned int)-1, 1);
 
-	value = getenv("PROCESS_UID");
-	if (value == NULL)
-		i_fatal("BUG: PROCESS_UID environment not given");
-        login_process_uid = strtoul(value, NULL, 10);
-	if (login_process_uid == 0)
-		i_fatal("BUG: PROCESS_UID environment is 0");
-
-        closing_down = FALSE;
-	main_refcount = 0;
-
-	auth_client = auth_client_new(login_process_uid);
-        auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
-	clients_init();
-
-	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 (restrict_access_get_current_chroot() == NULL) {
+		if (chdir("login") < 0)
+			i_fatal("chdir(login) failed: %m");
 	}
 
-	if (!is_inetd) {
-		master_init(LOGIN_MASTER_SOCKET_FD);
-		main_listen_start();
-	}
+	auth_client = auth_client_new((unsigned int)getpid());
+        auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
+
+	clients_init();
+	master_auth_init(service);
 }
 
 static void main_deinit(void)
 {
-	closing_down = TRUE;
-	main_listen_stop();
-
 	ssl_proxy_deinit();
 	login_proxy_deinit();
 
 	if (auth_client != NULL)
 		auth_client_free(&auth_client);
 	clients_deinit();
-	master_deinit();
-
-	lib_signals_deinit();
-	closelog();
+	master_auth_deinit(service);
 }
 
-int main(int argc ATTR_UNUSED, char *argv[], char *envp[])
+int main(int argc, char *argv[], char *envp[])
 {
-	const char *group_name;
-	struct ip_addr remote_ip, local_ip;
-	unsigned int remote_port, local_port, max_fds;
-	struct ssl_proxy *proxy = NULL;
-	struct client *client;
-	int i, fd = -1, master_fd = -1;
-	bool ssl = FALSE;
+	const char *getopt_str;
+	int c;
 
-	is_inetd = getenv("DOVECOT_MASTER") == NULL;
+	//FIXME:is_inetd = getenv("DOVECOT_MASTER") == NULL;
+
+	service = master_service_init(login_process_name, 0, argc, argv);
+	master_service_init_log(service, t_strconcat(login_process_name, ": ",
+						     NULL), 0);
 
-#ifdef DEBUG
-	if (!is_inetd && getenv("GDB") == NULL) {
-		const char *env;
+        getopt_str = t_strconcat("DS", master_service_getopt_string(), NULL);
+	while ((c = getopt(argc, argv, getopt_str)) > 0) {
+		switch (c) {
+		case 'D':
+			restrict_access_allow_coredumps(TRUE);
+			break;
+		case 'S':
+			ssl_connections = TRUE;
+			break;
+		default:
+			if (!master_service_parse_option(service, c, optarg))
+				exit(FATAL_DEFAULT);
+			break;
+		}
+	}
 
-		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, 1024);
-	}
-#endif
-	/* NOTE: we start rooted, so keep the code minimal until
-	   restrict_access_by_env() is called */
-	lib_init();
-
+#if 0
 	if (is_inetd) {
 		/* running from inetd. create master process before
 		   dropping privileges. */
-		process_name = strrchr(argv[0], '/');
-		process_name = process_name == NULL ? argv[0] : process_name+1;
-		group_name = t_strcut(process_name, '-');
-
-		for (i = 1; i < argc; i++) {
-			if (strncmp(argv[i], "--group=", 8) == 0) {
-				group_name = argv[1]+8;
-				break;
-			}
-		}
-
-		master_fd = master_connect(group_name);
+		master_fd = master_connect(t_strcut(login_process_name, '-'));
 	}
-
-	drop_privileges(&max_fds);
-
-	if (argv[1] != NULL && strcmp(argv[1], "-D") == 0)
-		restrict_access_allow_coredumps(TRUE);
+#endif
 
 	process_title_init(argv, envp);
-	ioloop = io_loop_create();
-	io_loop_set_max_fd_count(ioloop, max_fds);
+        login_settings = login_settings_read(service);
+
+	main_preinit();
+	master_service_init_finish(service);
 	main_init();
 
-	if (is_inetd) {
-		if (net_getpeername(1, &remote_ip, &remote_port) < 0) {
-			i_fatal("%s can be started only through dovecot "
-				"master process, inetd or equivalent", argv[0]);
-		}
-		if (net_getsockname(1, &local_ip, &local_port) < 0) {
-			memset(&local_ip, 0, sizeof(local_ip));
-			local_port = 0;
-		}
-
-		fd = 1;
-		for (i = 1; i < argc; i++) {
-			if (strcmp(argv[i], "--ssl") == 0)
-				ssl = TRUE;
-			else if (strncmp(argv[i], "--group=", 8) != 0)
-				i_fatal("Unknown parameter: %s", argv[i]);
-		}
-
-		/* hardcoded imaps and pop3s ports to be SSL by default */
-		if (local_port == 993 || local_port == 995 || ssl) {
-			ssl = TRUE;
-			fd = ssl_proxy_new(fd, &remote_ip, &proxy);
-			if (fd == -1)
-				return 1;
-		}
-
-		master_init(master_fd);
-		closing_down = TRUE;
-
-		if (fd != -1) {
-			client = client_create(fd, ssl, &local_ip, &remote_ip);
-			client->proxy = proxy;
-			client->remote_port = remote_port;
-			client->local_port = local_port;
-		}
-	}
-
-	io_loop_run(ioloop);
+	master_service_run(service, client_connected);
 	main_deinit();
-
-	io_loop_destroy(&ioloop);
-	lib_deinit();
-
+	master_service_deinit(&service);
         return 0;
 }
--- a/src/login-common/master.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,311 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "hash.h"
-#include "buffer.h"
-#include "ioloop.h"
-#include "network.h"
-#include "fdpass.h"
-#include "istream.h"
-#include "env-util.h"
-#include "write-full.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;
-static unsigned int master_tag_counter;
-
-static unsigned int master_pos;
-static char master_buf[sizeof(struct master_login_reply)];
-static struct client destroyed_client;
-
-static void client_call_master_callback(struct client *client,
-					const struct master_login_reply *reply)
-{
-	master_callback_t *master_callback;
-
-	master_callback = client->master_callback;
-	client->master_tag = 0;
-	client->master_callback = NULL;
-
-	master_callback(client, reply);
-}
-
-static void request_handle(struct master_login_reply *reply)
-{
-	struct client *client;
-
-	if (reply->tag == 0 && !login_settings->login_process_per_connection) {
-		/* this means we have to start listening again.
-		   we've reached maximum number of login processes. */
-		main_listen_start();
-		return;
-	}
-
-	client = hash_table_lookup(master_requests, POINTER_CAST(reply->tag));
-	if (client == NULL)
-		i_fatal("Master sent reply with unknown tag %u", reply->tag);
-
-	hash_table_remove(master_requests, POINTER_CAST(reply->tag));
-	if (client != &destroyed_client) {
-		client_call_master_callback(client, reply);
-		/* NOTE: client may be destroyed now */
-	}
-}
-
-void master_request_login(struct client *client, master_callback_t *callback,
-			  unsigned int auth_pid, unsigned int auth_id)
-{
-	buffer_t *buf;
-	struct master_login_request *req;
-	struct stat st;
-	const unsigned char *data;
-	size_t size;
-	ssize_t ret;
-	unsigned int cmd_tag_size;
-
-	i_assert(auth_pid != 0);
-
-	if (master_fd == -1) {
-		struct master_login_reply reply;
-
-		i_assert(closing_down);
-		memset(&reply, 0, sizeof(reply));
-		reply.status = MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-		callback(client, &reply);
-		return;
-	}
-
-	data = i_stream_get_data(client->input, &size);
-	cmd_tag_size = client->auth_command_tag == NULL ? 0 :
-		strlen(client->auth_command_tag);
-
-	buf = buffer_create_dynamic(pool_datastack_create(),
-				    sizeof(*req) + size + cmd_tag_size);
-	buffer_write(buf, sizeof(*req), client->auth_command_tag, cmd_tag_size);
-	buffer_write(buf, sizeof(*req) + cmd_tag_size, data, size);
-	req = buffer_get_space_unsafe(buf, 0, sizeof(*req));
-	req->version = MASTER_LOGIN_PROTOCOL_VERSION;
-	req->tag = ++master_tag_counter;
-	if (req->tag == 0)
-		req->tag = ++master_tag_counter;
-	req->auth_pid = auth_pid;
-	req->auth_id = auth_id;
-	req->local_ip = client->local_ip;
-	req->remote_ip = client->ip;
-	req->cmd_tag_size =  cmd_tag_size;
-	req->data_size = req->cmd_tag_size + size;
-#if (LOGIN_MAX_INBUF_SIZE*2) != MASTER_LOGIN_MAX_DATA_SIZE
-#  error buffer max sizes unsynced
-#endif
-	i_assert(req->data_size <= LOGIN_MAX_INBUF_SIZE);
-
-	if (fstat(client->fd, &st) < 0)
-		i_fatal("fstat(client) failed: %m");
-	req->ino = st.st_ino;
-
-	ret = fd_send(master_fd, client->fd, buf->data, buf->used);
-	if (ret < 0)
-		i_fatal("fd_send(%d) failed: %m", client->fd);
-	if ((size_t)ret != buf->used) {
-		i_fatal("fd_send() sent only %d of %d bytes",
-			(int)ret, (int)buf->used);
-	}
-
-	client->master_tag = req->tag;
-	client->master_callback = callback;
-
-	hash_table_insert(master_requests, POINTER_CAST(req->tag), client);
-}
-
-void master_request_abort(struct client *client)
-{
-	struct master_login_reply reply;
-
-	/* we're still going to get the reply from the master, so just
-	   remember that we want to ignore it */
-	hash_table_update(master_requests, POINTER_CAST(client->master_tag),
-			  &destroyed_client);
-
-	memset(&reply, 0, sizeof(reply));
-	reply.status = MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-	client_call_master_callback(client, &reply);
-}
-
-void master_notify_state_change(enum master_login_state state)
-{
-	struct master_login_request req;
-
-	if (io_master == NULL)
-		return;
-
-	memset(&req, 0, sizeof(req));
-	req.version = MASTER_LOGIN_PROTOCOL_VERSION;
-	req.tag = state;
-	req.ino = (ino_t)-1;
-
-	/* sending -1 as fd does the notification */
-	if (fd_send(master_fd, -1, &req, sizeof(req)) != sizeof(req))
-		i_fatal("fd_send(-1) failed: %m");
-}
-
-void master_close(void)
-{
-	if (io_master == NULL)
-		return;
-
-	io_remove(&io_master);
-	if (close(master_fd) < 0)
-		i_fatal("close(master) failed: %m");
-	master_fd = -1;
-
-	closing_down = TRUE;
-        main_listen_stop();
-	main_unref();
-
-        /* may call this function again through main_unref() */
-	clients_destroy_all();
-}
-
-static void master_exec(int fd)
-{
-	static char dovecot[] = "dovecot";
-	char *argv[] = { dovecot, 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");
-
-		env_put("DOVECOT_INETD=1");
-		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_fd(fd, 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_destroy(&input);
-}
-
-int master_connect(const char *group_name)
-{
-	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, 16);
-		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);
-
-	if (group_name[0] == '\0')
-		i_fatal("No login group name set");
-
-	if (strlen(group_name) >= 256)
-		i_fatal("Login group name too large: %s", group_name);
-
-	/* group_name length is now guaranteed to be in range of 1..255 so we
-	   can send <length byte><name> */
-	group_name = t_strdup_printf("%c%s", (unsigned char)strlen(group_name),
-				     group_name);
-	if (write_full(fd, group_name, strlen(group_name)) < 0)
-		i_fatal("write_full(master_fd) failed: %m");
-
-	master_read_env(fd);
-	return fd;
-}
-
-static void master_input(void *context ATTR_UNUSED)
-{
-	int ret;
-
-	ret = net_receive(master_fd, master_buf + master_pos,
-			  sizeof(master_buf) - master_pos);
-	if (ret < 0) {
-		/* master died, kill all clients logging in */
-		master_close();
-		return;
-	}
-
-	master_pos += ret;
-	if (master_pos < sizeof(master_buf))
-		return;
-
-	/* reply is now read */
-	request_handle((void *)master_buf);
-	master_pos = 0;
-}
-
-void master_init(int fd)
-{
-	main_ref();
-
-	master_fd = fd;
-	master_requests = hash_table_create(system_pool, system_pool,
-					    0, NULL, NULL);
-
-        master_pos = 0;
-	io_master = io_add(master_fd, IO_READ, master_input, NULL);
-}
-
-void master_deinit(void)
-{
-	hash_table_destroy(&master_requests);
-
-	if (io_master != NULL)
-		io_remove(&io_master);
-}
--- a/src/login-common/master.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-#ifndef MASTER_H
-#define MASTER_H
-
-struct client;
-
-#include "../master/master-login-interface.h"
-
-typedef void master_callback_t(struct client *client,
-			       const struct master_login_reply *reply);
-
-void master_request_login(struct client *client, master_callback_t *callback,
-			  unsigned int auth_pid, unsigned int auth_id);
-void master_request_abort(struct client *client);
-
-/* Notify master of a change in our state */
-void master_notify_state_change(enum master_login_state state);
-
-/* Close connection to master process */
-void master_close(void);
-
-/* inetd: Connect to existing master process, or create new one. */
-int master_connect(const char *group_name);
-
-void master_init(int fd);
-void master_deinit(void);
-
-#endif
--- a/src/login-common/sasl-server.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/sasl-server.c	Thu Apr 23 19:53:44 2009 -0400
@@ -3,11 +3,13 @@
 #include "common.h"
 #include "base64.h"
 #include "buffer.h"
+#include "istream.h"
 #include "str-sanitize.h"
 #include "auth-client.h"
 #include "ssl-proxy.h"
+#include "master-interface.h"
+#include "master-auth.h"
 #include "client-common.h"
-#include "master.h"
 
 static enum auth_request_flags
 client_get_auth_flags(struct client *client)
@@ -38,19 +40,21 @@
 }
 
 static void
-master_callback(struct client *client, const struct master_login_reply *reply)
+master_auth_callback(const struct master_auth_reply *reply, void *context)
 {
+	struct client *client = context;
 	enum sasl_server_reply sasl_reply = SASL_SERVER_REPLY_MASTER_FAILED;
 	const char *data = NULL;
 
+	client->master_tag = 0;
 	client->authenticating = FALSE;
 	switch (reply->status) {
-	case MASTER_LOGIN_STATUS_OK:
+	case MASTER_AUTH_STATUS_OK:
 		sasl_reply = SASL_SERVER_REPLY_SUCCESS;
 		break;
-	case MASTER_LOGIN_STATUS_INTERNAL_ERROR:
+	case MASTER_AUTH_STATUS_INTERNAL_ERROR:
 		break;
-	case MASTER_LOGIN_STATUS_MAX_CONNECTIONS:
+	case MASTER_AUTH_STATUS_MAX_CONNECTIONS:
 		data = "Maximum number of connections from user+IP exceeded "
 			"(mail_max_userip_connections)";
 		break;
@@ -59,6 +63,35 @@
 	call_client_callback(client, sasl_reply, data, NULL);
 }
 
+static void
+master_send_request(struct client *client, struct auth_request *request)
+{
+	struct master_auth_request req;
+	const unsigned char *data;
+	size_t size;
+	buffer_t *buf;
+
+	memset(&req, 0, sizeof(req));
+	req.auth_pid = auth_client_request_get_server_pid(request);
+	req.auth_id = auth_client_request_get_id(request);
+	req.local_ip = client->local_ip;
+	req.remote_ip = client->ip;
+
+	buf = buffer_create_dynamic(pool_datastack_create(), 256);
+	if (client->auth_command_tag != NULL) {
+		buffer_append(buf, client->auth_command_tag,
+			      strlen(client->auth_command_tag)+1);
+	}
+
+	data = i_stream_get_data(client->input, &size);
+	buffer_append(buf, data, size);
+	req.data_size = buf->used;
+
+	client->master_tag =
+		master_auth_request(service, client->fd, &req, buf->data,
+				    master_auth_callback, client);
+}
+
 static void authenticate_callback(struct auth_request *request, int status,
 				  const char *data_base64,
 				  const char *const *args, void *context)
@@ -101,9 +134,7 @@
 			call_client_callback(client, SASL_SERVER_REPLY_SUCCESS,
 					     NULL, args);
 		} else {
-			master_request_login(client, master_callback,
-				auth_client_request_get_server_pid(request),
-				auth_client_request_get_id(request));
+			master_send_request(client, request);
 		}
 		break;
 	case -1:
--- a/src/login-common/sasl-server.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/sasl-server.h	Thu Apr 23 19:53:44 2009 -0400
@@ -1,6 +1,8 @@
 #ifndef SASL_SERVER_H
 #define SASL_SERVER_H
 
+struct client;
+
 enum sasl_server_reply {
 	SASL_SERVER_REPLY_SUCCESS,
 	SASL_SERVER_REPLY_AUTH_FAILED,
--- a/src/login-common/ssl-proxy-openssl.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/ssl-proxy-openssl.c	Thu Apr 23 19:53:44 2009 -0400
@@ -8,6 +8,7 @@
 #include "read-full.h"
 #include "safe-memset.h"
 #include "llist.h"
+#include "master-service.h"
 #include "ssl-proxy.h"
 
 #include <fcntl.h>
@@ -473,7 +474,7 @@
 	ssl_proxy_unref(proxy);
 }
 
-int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r)
+int ssl_proxy_new(int fd, const struct ip_addr *ip, struct ssl_proxy **proxy_r)
 {
 	struct ssl_proxy *proxy;
 	SSL *ssl;
@@ -524,7 +525,6 @@
 	DLLIST_PREPEND(&ssl_proxies, proxy);
 
 	ssl_step(proxy);
-	main_ref();
 
 	*proxy_r = proxy;
 	return sfd[1];
@@ -602,8 +602,6 @@
 
 	SSL_free(proxy->ssl);
 	i_free(proxy);
-
-	main_unref();
 }
 
 static void ssl_proxy_destroy(struct ssl_proxy *proxy)
@@ -631,7 +629,7 @@
 
 	ssl_proxy_unref(proxy);
 
-	main_listen_start();
+        master_service_client_connection_destroyed(service);
 }
 
 static RSA *ssl_gen_rsa_key(SSL *ssl ATTR_UNUSED,
--- a/src/login-common/ssl-proxy.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/ssl-proxy.c	Thu Apr 23 19:53:44 2009 -0400
@@ -9,7 +9,7 @@
 
 /* no SSL support */
 
-int ssl_proxy_new(int fd ATTR_UNUSED, struct ip_addr *ip ATTR_UNUSED,
+int ssl_proxy_new(int fd ATTR_UNUSED, const struct ip_addr *ip ATTR_UNUSED,
 		  struct ssl_proxy **proxy_r ATTR_UNUSED)
 {
 	i_error("Dovecot wasn't built with SSL support");
--- a/src/login-common/ssl-proxy.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/login-common/ssl-proxy.h	Thu Apr 23 19:53:44 2009 -0400
@@ -9,7 +9,7 @@
 /* establish SSL connection with the given fd, returns a new fd which you
    must use from now on, or -1 if error occurred. Unless -1 is returned,
    the given fd must be simply forgotten. */
-int ssl_proxy_new(int fd, struct ip_addr *ip, struct ssl_proxy **proxy_r);
+int ssl_proxy_new(int fd, const struct ip_addr *ip, struct ssl_proxy **proxy_r);
 bool ssl_proxy_has_valid_client_cert(const struct ssl_proxy *proxy) ATTR_PURE;
 bool ssl_proxy_has_broken_client_cert(struct ssl_proxy *proxy);
 const char *ssl_proxy_get_peer_name(struct ssl_proxy *proxy);
--- a/src/master/Makefile.am	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/master/Makefile.am	Thu Apr 23 19:53:44 2009 -0400
@@ -1,7 +1,7 @@
 pkglibexecdir = $(libexecdir)/dovecot
 
 sbin_PROGRAMS = dovecot
-pkglibexec_PROGRAMS = ssl-build-param
+#pkglibexec_PROGRAMS = ssl-build-param
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
@@ -11,52 +11,37 @@
 	-DPKG_RUNDIR=\""$(rundir)"\" \
 	-DPKG_STATEDIR=\""$(statedir)"\" \
 	-DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
-	-DBINDIR=\""$(bindir)"\" \
-	-DSSLDIR=\""$(ssldir)\""
+	-DBINDIR=\""$(bindir)"\"
 
-dovecot_LDADD = \
+libs = \
 	$(LIBCAP) \
 	$(LIBDOVECOT)
 
+dovecot_LDADD = $(libs)
+dovecot_DEPENDENCIES = $(libs)
+
 dovecot_SOURCES = \
-	auth-process.c \
-	askpass.c \
 	capabilities-posix.c \
-	child-process.c \
-	dict-process.c \
 	dup2-array.c \
-	listener.c \
-	log.c \
-	login-process.c \
-	mail-process.c \
+	main.c \
 	master-settings.c \
-	main.c \
-	ssl-init.c \
-	sysinfo-get.c
+	service-auth-server.c \
+	service-auth-source.c \
+	service-listen.c \
+	service-log.c \
+	service-monitor.c \
+	service-process.c \
+	service.c
 
 noinst_HEADERS = \
-	auth-process.h \
-	askpass.h \
 	capabilities.h \
-	child-process.h \
-	dict-process.h \
+	common.h \
 	dup2-array.h \
-	listener.h \
-	common.h \
-	log.h \
-	login-process.h \
-	mail-process.h \
-	master-login-interface.h \
 	master-settings.h \
-	ssl-init.h \
-	sysinfo-get.h
-
-ssl_build_param_SOURCES = \
-	ssl-init-main.c \
-	ssl-init-openssl.c \
-	ssl-init-gnutls.c
-
-ssl_build_param_LDADD = \
-	$(LIBDOVECOT) \
-	$(SSL_LIBS)
-ssl_build_param_DEPENDENCIES = $(LIBDOVECOT)
+	service-auth-server.h \
+	service-auth-source.h \
+	service-listen.h \
+	service-log.h \
+	service-monitor.h \
+	service-process.h \
+	service.h
--- a/src/master/auth-process.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,760 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "hash.h"
-#include "ioloop.h"
-#include "env-util.h"
-#include "fd-close-on-exec.h"
-#include "unix-socket-create.h"
-#include "network.h"
-#include "istream.h"
-#include "ostream.h"
-#include "str.h"
-#include "restrict-access.h"
-#include "restrict-process-size.h"
-#include "auth-process.h"
-#include "child-process.h"
-#include "../auth/auth-master-interface.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <pwd.h>
-#include <syslog.h>
-
-#define MAX_INBUF_SIZE 8192
-#define MAX_OUTBUF_SIZE 65536
-
-struct auth_process_group {
-	struct auth_process_group *next;
-
-	int listen_fd;
-	const struct master_settings *master_set;
-	const struct master_auth_settings *set;
-
-	unsigned int process_count;
-	struct auth_process *processes;
-};
-
-struct auth_process {
-	struct auth_process *next;
-
-        struct auth_process_group *group;
-	pid_t pid;
-	int fd;
-	struct io *io;
-	struct istream *input;
-	struct ostream *output;
-
-	int worker_listen_fd;
-	struct io *worker_io;
-
-	struct hash_table *requests;
-
-	unsigned int external:1;
-	unsigned int version_received:1;
-	unsigned int initialized:1;
-	unsigned int in_auth_reply:1;
-};
-
-bool have_initialized_auth_processes = FALSE;
-
-static struct child_process auth_child_process =
-	{ MEMBER(type) PROCESS_TYPE_AUTH };
-static struct child_process auth_worker_child_process =
-	{ MEMBER(type) PROCESS_TYPE_AUTH_WORKER };
-
-static struct timeout *to;
-static unsigned int auth_tag;
-static struct auth_process_group *process_groups;
-static bool auth_stalled = FALSE;
-
-static void auth_process_destroy(struct auth_process *p);
-static int create_auth_worker(struct auth_process *process, int fd);
-static void auth_processes_start_missing(void *context);
-
-void auth_process_request(struct auth_process *process, unsigned int login_pid,
-			  unsigned int login_id,
-			  struct login_auth_request *request)
-{
-	string_t *str;
-	ssize_t ret;
-
-	str = t_str_new(256);
-	str_printfa(str, "REQUEST\t%u\t%u\t%u\n",
-		    ++auth_tag, login_pid, login_id);
-
-	ret = o_stream_send(process->output, str_data(str), str_len(str));
-	if (ret != (ssize_t)str_len(str)) {
-		if (ret >= 0) {
-			/* FIXME: well .. I'm not sure if it'd be better to
-			   just block here. I don't think this condition should
-			   happen often, so this could mean that the auth
-			   process is stuck. Or that the computer is just
-			   too heavily loaded. Possibility to block infinitely
-			   is annoying though, so for now don't do it. */
-			i_warning("Auth process %s transmit buffer full, "
-				  "killing..", dec2str(process->pid));
-		}
-		auth_process_destroy(process);
-	} else {
-		hash_table_insert(process->requests,
-				  POINTER_CAST(auth_tag), request);
-	}
-}
-
-static bool
-auth_process_input_user(struct auth_process *process, const char *args)
-{
-	struct login_auth_request *request;
-	const char *const *list;
-	unsigned int id;
-
-	/* <id> <userid> [..] */
-
-	list = t_strsplit(args, "\t");
-	if (list[0] == NULL || list[1] == NULL) {
-		i_error("BUG: Auth process %s sent corrupted USER line",
-			dec2str(process->pid));
-		return FALSE;
-	}
-	id = (unsigned int)strtoul(list[0], NULL, 10);
-
-	request = hash_table_lookup(process->requests, POINTER_CAST(id));
-	if (request == NULL) {
-		i_error("BUG: Auth process %s sent unrequested reply with ID "
-			"%u", dec2str(process->pid), id);
-		return FALSE;
-	}
-
-	if (!auth_success_written) {
-		int fd;
-
-		fd = creat(AUTH_SUCCESS_PATH, 0666);
-		if (fd == -1)
-			i_error("creat(%s) failed: %m", AUTH_SUCCESS_PATH);
-		else
-			(void)close(fd);
-		auth_success_written = TRUE;
-	}
-
-	auth_master_callback(list[1], list + 2, request);
-	hash_table_remove(process->requests, POINTER_CAST(id));
-	return TRUE;
-}
-
-static bool
-auth_process_input_notfound(struct auth_process *process, const char *args)
-{
-	struct login_auth_request *request;
-	unsigned int id;
-
-	id = (unsigned int)strtoul(args, NULL, 10);
-
-	request = hash_table_lookup(process->requests, POINTER_CAST(id));
-	if (request == NULL) {
-		i_error("BUG: Auth process %s sent unrequested reply with ID "
-			"%u", dec2str(process->pid), id);
-		return FALSE;
-	}
-
-	auth_master_callback(NULL, NULL, request);
-	hash_table_remove(process->requests, POINTER_CAST(id));
-	return TRUE;
-}
-
-static bool
-auth_process_input_spid(struct auth_process *process, const char *args)
-{
-	unsigned int pid;
-
-	if (process->initialized) {
-		i_error("BUG: Authentication server re-handshaking");
-		return FALSE;
-	}
-
-	pid = (unsigned int)strtoul(args, NULL, 10);
-	if (pid == 0) {
-		i_error("BUG: Authentication server said it's PID 0");
-		return FALSE;
-	}
-
-	if (process->pid != 0 && process->pid != (pid_t)pid) {
-		i_error("BUG: Authentication server sent invalid SPID "
-			"(%u != %s)", pid, dec2str(process->pid));
-		return FALSE;
-	}
-
-	process->pid = pid;
-        process->initialized = TRUE;
-
-	have_initialized_auth_processes = TRUE;
-	return TRUE;
-}
-
-static bool
-auth_process_input_fail(struct auth_process *process, const char *args)
-{
-	struct login_auth_request *request;
- 	const char *error;
-	unsigned int id;
-
-	error = strchr(args, '\t');
-	if (error != NULL)
-		error++;
-
-	id = (unsigned int)strtoul(args, NULL, 10);
-
-	request = hash_table_lookup(process->requests, POINTER_CAST(id));
-	if (request == NULL) {
-		i_error("BUG: Auth process %s sent unrequested reply with ID "
-			"%u", dec2str(process->pid), id);
-		return FALSE;
-	}
-
-	auth_master_callback(NULL, NULL, request);
-	hash_table_remove(process->requests, POINTER_CAST(id));
-	return TRUE;
-}
-
-static bool
-auth_process_input_line(struct auth_process *process, const char *line)
-{
-	if (strncmp(line, "USER\t", 5) == 0)
-		return auth_process_input_user(process, line + 5);
-	else if (strncmp(line, "NOTFOUND\t", 9) == 0)
-		return auth_process_input_notfound(process, line + 9);
-	else if (strncmp(line, "FAIL\t", 5) == 0)
-		return auth_process_input_fail(process, line + 5);
-	else if (strncmp(line, "SPID\t", 5) == 0)
-		return auth_process_input_spid(process, line + 5);
-	else
-		return TRUE;
-}
-
-static void auth_process_input(struct auth_process *process)
-{
-	const char *line;
-	bool ret;
-
-	switch (i_stream_read(process->input)) {
-	case 0:
-		return;
-	case -1:
-		/* disconnected */
-		auth_process_destroy(process);
-		return;
-	case -2:
-		/* buffer full */
-		i_error("BUG: Auth process %s sent us more than %d "
-			"bytes of data", dec2str(process->pid),
-			(int)MAX_INBUF_SIZE);
-		auth_process_destroy(process);
-		return;
-	}
-
-	if (!process->version_received) {
-		line = i_stream_next_line(process->input);
-		if (line == NULL)
-			return;
-
-		/* make sure the major version matches */
-		if (strncmp(line, "VERSION\t", 8) != 0 ||
-		    atoi(t_strcut(line + 8, '\t')) !=
-		    AUTH_MASTER_PROTOCOL_MAJOR_VERSION) {
-			i_error("Auth process %s not compatible with master "
-				"process (mixed old and new binaries?)",
-				dec2str(process->pid));
-			auth_process_destroy(process);
-			return;
-		}
-		process->version_received = TRUE;
-	}
-
-	while ((line = i_stream_next_line(process->input)) != NULL) {
-		T_BEGIN {
-			ret = auth_process_input_line(process, line);
-		} T_END;
-		if (!ret) {
-			auth_process_destroy(process);
-			break;
-		}
-	}
-}
-
-static void auth_worker_input(struct auth_process *p)
-{
-	int fd;
-
-	fd = net_accept(p->worker_listen_fd, NULL, NULL);
-	if (fd < 0) {
-		if (fd == -2)
-			i_error("accept(worker) failed: %m");
-		return;
-	}
-
-	net_set_nonblock(fd, TRUE);
-	fd_close_on_exec(fd, TRUE);
-
-	create_auth_worker(p, fd);
-}
-
-static struct auth_process *
-auth_process_new(pid_t pid, int fd, struct auth_process_group *group)
-{
-	struct auth_process *p;
-	const char *path, *handshake;
-
-	if (pid != 0)
-		child_process_add(pid, &auth_child_process);
-
-	p = i_new(struct auth_process, 1);
-	p->group = group;
-	p->pid = pid;
-	p->fd = fd;
-	p->io = io_add(fd, IO_READ, auth_process_input, p);
-	p->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
-	p->output = o_stream_create_fd(fd, MAX_OUTBUF_SIZE, FALSE);
-	p->requests = hash_table_create(default_pool, default_pool, 0,
-					NULL, NULL);
-
-	group->process_count++;
-
-	path = t_strdup_printf("%s/auth-worker.%s",
-			       *group->set->chroot != '\0' ?
-			       group->set->chroot :
-			       group->master_set->base_dir,
-			       dec2str(pid));
-	p->worker_listen_fd =
-		unix_socket_create(path, 0600, group->set->uid,
-				   group->set->gid, 128);
-	if (p->worker_listen_fd == -1)
-		i_fatal("Couldn't create auth worker listener");
-
-	net_set_nonblock(p->worker_listen_fd, TRUE);
-	fd_close_on_exec(p->worker_listen_fd, TRUE);
-	p->worker_io = io_add(p->worker_listen_fd, IO_READ,
-			      auth_worker_input, p);
-
-	handshake = t_strdup_printf("VERSION\t%u\t%u\n",
-				    AUTH_MASTER_PROTOCOL_MAJOR_VERSION,
-				    AUTH_MASTER_PROTOCOL_MINOR_VERSION);
-	(void)o_stream_send_str(p->output, handshake);
-
-	p->next = group->processes;
-	group->processes = p;
-	return p;
-}
-
-static void auth_process_destroy(struct auth_process *p)
-{
-	struct hash_iterate_context *iter;
-	void *key, *value;
-	struct auth_process **pos;
-	const char *path;
-
-	if (!p->initialized && io_loop_is_running(ioloop) && !p->external) {
-		/* log the process exit and kill ourself */
-		child_processes_flush();
-		log_deinit();
-		i_fatal("Auth process died too early - shutting down");
-	}
-
-	for (pos = &p->group->processes; *pos != NULL; pos = &(*pos)->next) {
-		if (*pos == p) {
-			*pos = p->next;
-			break;
-		}
-	}
-	p->group->process_count--;
-
-	path = t_strdup_printf("%s/auth-worker.%s",
-			       *p->group->set->chroot != '\0' ?
-			       p->group->set->chroot :
-			       p->group->master_set->base_dir,
-			       dec2str(p->pid));
-	(void)unlink(path);
-
-	io_remove(&p->worker_io);
-	if (close(p->worker_listen_fd) < 0)
-		i_error("close(worker_listen) failed: %m");
-
-	iter = hash_table_iterate_init(p->requests);
-	while (hash_table_iterate(iter, &key, &value))
-		auth_master_callback(NULL, NULL, value);
-	hash_table_iterate_deinit(&iter);
-	hash_table_destroy(&p->requests);
-
-	i_stream_destroy(&p->input);
-	o_stream_destroy(&p->output);
-	io_remove(&p->io);
-	if (close(p->fd) < 0)
-		i_error("close(auth) failed: %m");
-	i_free(p);
-}
-
-static int connect_auth_socket(struct auth_process_group *group,
-			       const char *path)
-{
-	struct auth_process *auth;
-	int fd;
-
-	fd = net_connect_unix(path);
-	if (fd == -1) {
-		i_error("net_connect_unix(%s) failed: %m", path);
-		return -1;
-	}
-
-	net_set_nonblock(fd, TRUE);
-	fd_close_on_exec(fd, TRUE);
-	auth = auth_process_new(0, fd, group);
-	auth->external = TRUE;
-	return 0;
-}
-
-static void auth_set_environment(const struct master_settings *master_set,
-				 const struct master_auth_settings *set)
-{
-	struct restrict_access_settings rset;
-
-	master_settings_export_to_env(master_set);
-
-	/* setup access environment */
-	restrict_access_init(&rset);
-	rset.system_groups_user = set->user;
-	rset.uid = set->uid;
-	rset.gid = set->gid;
-	rset.chroot_dir = set->chroot;
-	restrict_access_set_env(&rset);
-
-	/* set other environment */
-	env_put("DOVECOT_MASTER=1");
-	env_put(t_strconcat("AUTH_NAME=", set->name, NULL));
-	restrict_process_size(set->process_size, (unsigned int)-1);
-}
-
-static const struct master_auth_socket_settings *
-get_connect_socket(const struct master_auth_settings *auth_set)
-{
-	struct master_auth_socket_settings *const *as;
-	unsigned int count;
-
-	if (!array_is_created(&auth_set->sockets))
-		return NULL;
-
-	as = array_get(&auth_set->sockets, &count);
-	if (count > 0 && strcmp(as[0]->type, "connect") == 0)
-		return as[0];
-	else
-		return NULL;
-}
-
-static int create_auth_process(struct auth_process_group *group)
-{
-	const struct master_auth_socket_settings *as;
-	struct master_auth_socket_unix_settings *const *masters;
-	const char *prefix, *executable;
-	struct log_io *log;
-	pid_t pid;
-	unsigned int count;
-	int fd[2], log_fd, i;
-
-	/* see if this is a connect socket */
-	as = get_connect_socket(group->set);
-	if (as != NULL) {
-		masters = array_get(&as->masters, &count);
-		if (count > 0)
-			return connect_auth_socket(group, masters[0]->path);
-	}
-
-	/* create communication to process with a socket pair */
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
-		i_error("socketpair() failed: %m");
-		return -1;
-	}
-
-	log_fd = log_create_pipe(&log, 0);
-	if (log_fd < 0)
-		pid = -1;
-	else {
-		pid = fork();
-		if (pid < 0)
-			i_error("fork() failed: %m");
-	}
-
-	if (pid < 0) {
-		(void)close(fd[0]);
-		(void)close(fd[1]);
-		(void)close(log_fd);
-		return -1;
-	}
-
-	if (pid != 0) {
-		/* master */
-		prefix = t_strdup_printf("auth(%s): ", group->set->name);
-		log_set_prefix(log, prefix);
-		log_set_pid(log, pid);
-
-		net_set_nonblock(fd[0], TRUE);
-		fd_close_on_exec(fd[0], TRUE);
-		auth_process_new(pid, fd[0], group);
-		(void)close(fd[1]);
-		(void)close(log_fd);
-		return 0;
-	}
-
-	prefix = t_strdup_printf("master-auth(%s): ", group->set->name);
-	log_set_prefix(log, prefix);
-
-	/* move master communication handle to 0 */
-	if (dup2(fd[1], 0) < 0)
-		i_fatal("dup2(stdin) failed: %m");
-
-	(void)close(fd[0]);
-	(void)close(fd[1]);
-
-	/* make sure we don't leak syslog fd. try to do it as late as possible,
-	   but also before dup2()s in case syslog fd is one of them. */
-	closelog();
-
-	/* set stdout to /dev/null, so anything written into it gets ignored. */
-	if (dup2(null_fd, 1) < 0)
-		i_fatal("dup2(stdout) failed: %m");
-
-	if (dup2(log_fd, 2) < 0)
-		i_fatal("dup2(stderr) failed: %m");
-
-	child_process_init_env(group->master_set);
-
-	if (group->listen_fd != 3) {
-		if (dup2(group->listen_fd, 3) < 0)
-			i_fatal("dup2() failed: %m");
-	}
-	fd_close_on_exec(3, FALSE);
-
-	for (i = 0; i <= 2; i++)
-		fd_close_on_exec(i, FALSE);
-
-        auth_set_environment(group->master_set, group->set);
-
-	env_put(t_strdup_printf("AUTH_WORKER_PATH=%s/auth-worker.%s",
-				*group->set->chroot != '\0' ? "" :
-				group->master_set->base_dir,
-				dec2str(getpid())));
-
-	executable = group->set->executable;
-	client_process_exec(executable, "");
-	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
-	return -1;
-}
-
-static int create_auth_worker(struct auth_process *process, int fd)
-{
-	struct log_io *log;
-	const char *prefix, *executable;
-	pid_t pid;
-	int log_fd, i;
-
-	log_fd = log_create_pipe(&log, 0);
-	if (log_fd < 0)
-		pid = -1;
-	else {
-		pid = fork();
-		if (pid < 0)
-			i_error("fork() failed: %m");
-	}
-
-	if (pid < 0) {
-		(void)close(log_fd);
-		return -1;
-	}
-
-	if (pid != 0) {
-		/* master */
-		child_process_add(pid, &auth_worker_child_process);
-		prefix = t_strdup_printf("auth-worker(%s): ",
-					 process->group->set->name);
-		log_set_prefix(log, prefix);
-		(void)close(fd);
-		(void)close(log_fd);
-		return 0;
-	}
-
-	prefix = t_strdup_printf("master-auth-worker(%s): ",
-				 process->group->set->name);
-	log_set_prefix(log, prefix);
-
-	/* make sure we don't leak syslog fd. try to do it as late as possible,
-	   but also before dup2()s in case syslog fd is one of them. */
-	closelog();
-
-	/* set stdin and stdout to /dev/null, so anything written into it
-	   gets ignored. */
-	if (dup2(null_fd, 0) < 0)
-		i_fatal("dup2(stdin) failed: %m");
-	if (dup2(null_fd, 1) < 0)
-		i_fatal("dup2(stdout) failed: %m");
-
-	if (dup2(log_fd, 2) < 0)
-		i_fatal("dup2(stderr) failed: %m");
-
-	if (dup2(fd, 4) < 0)
-		i_fatal("dup2(4) failed: %m");
-
-	for (i = 0; i <= 2; i++)
-		fd_close_on_exec(i, FALSE);
-	fd_close_on_exec(4, FALSE);
-
-	child_process_init_env(process->group->master_set);
-        auth_set_environment(process->group->master_set, process->group->set);
-
-	executable = t_strconcat(process->group->set->executable, " -w", NULL);
-	client_process_exec(executable, "");
-	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
-	return -1;
-}
-
-struct auth_process *auth_process_find(unsigned int pid)
-{
-	struct auth_process_group *group;
-	struct auth_process *p;
-
-	for (group = process_groups; group != NULL; group = group->next) {
-		for (p = group->processes; p != NULL; p = p->next) {
-			if ((unsigned int)p->pid == pid)
-				return p;
-		}
-	}
-
-	return NULL;
-}
-
-static void auth_process_group_create(struct master_settings *set,
-				      struct master_auth_settings *auth_set)
-{
-	struct auth_process_group *group;
-	const char *path;
-
-	group = i_new(struct auth_process_group, 1);
-	group->master_set = set;
-	group->set = auth_set;
-
-	group->next = process_groups;
-	process_groups = group;
-
-	if (get_connect_socket(auth_set) != NULL)
-		return;
-
-	path = t_strconcat(set->login_dir, "/", auth_set->name, NULL);
-	group->listen_fd = unix_socket_create(path, 0660, master_uid,
-					      set->server->login_gid, 128);
-	if (group->listen_fd == -1)
-		i_fatal("Couldn't create auth process listener");
-
-	net_set_nonblock(group->listen_fd, TRUE);
-	fd_close_on_exec(group->listen_fd, TRUE);
-}
-
-static void auth_process_group_destroy(struct auth_process_group *group)
-{
-	struct auth_process *next;
-	const char *path;
-
-	while (group->processes != NULL) {
-		next = group->processes->next;
-		auth_process_destroy(group->processes);
-                group->processes = next;
-	}
-
-	path = t_strconcat(group->master_set->login_dir, "/",
-			   group->set->name, NULL);
-	(void)unlink(path);
-
-	if (close(group->listen_fd) < 0)
-		i_error("close(%s) failed: %m", path);
-	i_free(group);
-}
-
-void auth_processes_destroy_all(void)
-{
-	struct auth_process_group *next;
-
-	while (process_groups != NULL) {
-		next = process_groups->next;
-		auth_process_group_destroy(process_groups);
-		process_groups = next;
-	}
-
-	have_initialized_auth_processes = FALSE;
-}
-
-static void auth_process_groups_create(struct master_settings *set)
-{
-	struct master_auth_settings *const *auth_sets;
-	unsigned int i, count;
-
-	auth_sets = array_get(&set->auths, &count);
-	for (i = 0; i < count; i++)
-		auth_process_group_create(set, auth_sets[i]);
-}
-
-static void auth_processes_stall(void)
-{
-	if (auth_stalled)
-		return;
-
-	i_error("Temporary failure in creating authentication processes, "
-		"slowing down for now");
-	auth_stalled = TRUE;
-
-	timeout_remove(&to);
-	to = timeout_add(60*1000, auth_processes_start_missing, NULL);
-}
-
-static void
-auth_processes_start_missing(void *context ATTR_UNUSED)
-{
-	struct auth_process_group *group;
-	unsigned int count;
-
-	if (process_groups == NULL) {
-		/* first time here, create the groups */
-		auth_process_groups_create(master_set->defaults);
-	}
-
-	for (group = process_groups; group != NULL; group = group->next) {
-		count = group->process_count;
-		for (; count < group->set->count; count++) {
-			if (create_auth_process(group) < 0) {
-				auth_processes_stall();
-				return;
-			}
-		}
-	}
-
-	if (auth_stalled) {
-		/* processes were created successfully */
-		i_info("Created authentication processes successfully, "
-		       "unstalling");
-
-		auth_stalled = FALSE;
-		timeout_remove(&to);
-		to = timeout_add(1000, auth_processes_start_missing, NULL);
-	}
-}
-
-void auth_processes_init(void)
-{
-	process_groups = NULL;
-	to = timeout_add(1000, auth_processes_start_missing, NULL);
-
-	auth_processes_start_missing(NULL);
-}
-
-void auth_processes_deinit(void)
-{
-	timeout_remove(&to);
-	auth_processes_destroy_all();
-}
--- a/src/master/auth-process.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-#ifndef AUTH_PROCESS_H
-#define AUTH_PROCESS_H
-
-struct login_auth_request;
-
-extern bool have_initialized_auth_processes;
-
-void auth_master_callback(const char *user, const char *const *args,
-			  struct login_auth_request *request);
-
-/* Find process for given id */
-struct auth_process *auth_process_find(unsigned int pid);
-
-/* Request information about given cookie */
-void auth_process_request(struct auth_process *process, unsigned int login_pid,
-			  unsigned int login_id,
-			  struct login_auth_request *request);
-
-/* Close any fds used by auth processes */
-void auth_processes_destroy_all(void);
-
-void auth_processes_init(void);
-void auth_processes_deinit(void);
-
-#endif
--- a/src/master/child-process.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,290 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "lib-signals.h"
-#include "array.h"
-#include "hash.h"
-#include "str.h"
-#include "env-util.h"
-#include "child-process.h"
-
-#include <unistd.h>
-#include <syslog.h>
-#include <sys/wait.h>
-
-const char *process_names[PROCESS_TYPE_MAX] = {
-	"unknown",
-	"auth",
-	"auth-worker",
-	"login",
-	"imap",
-	"pop3",
-	"ssl-build-param",
-	"dict"
-};
-
-struct hash_table *processes;
-static child_process_destroy_callback_t *destroy_callbacks[PROCESS_TYPE_MAX];
-
-struct child_process *child_process_lookup(pid_t pid)
-{
-	return hash_table_lookup(processes, POINTER_CAST(pid));
-}
-
-void child_process_add(pid_t pid, struct child_process *process)
-{
-	hash_table_insert(processes, POINTER_CAST(pid), process);
-}
-
-void child_process_remove(pid_t pid)
-{
-	hash_table_remove(processes, POINTER_CAST(pid));
-}
-
-void child_process_init_env(const struct master_settings *set)
-{
-	/* remove all environment, we don't need them */
-	env_clean();
-
-	/* we'll log through master process */
-	env_put("LOG_TO_MASTER=1");
-	env_put("DOVECONF_ENV=1");
-	if (env_tz != NULL)
-		env_put(t_strconcat("TZ=", env_tz, NULL));
-
-	if (master_set != NULL && !set->version_ignore)
-		env_put("DOVECOT_VERSION="PACKAGE_VERSION);
-#ifdef DEBUG
-	if (gdb) env_put("GDB=1");
-#endif
-}
-
-void client_process_exec(const char *cmd, const char *title)
-{
-	const char **argv;
-
-	/* very simple argument splitting. */
-	if (*title == '\0')
-		argv = t_strsplit(cmd, " ");
-	else
-		argv = t_strsplit(t_strconcat(cmd, " ", title, NULL), " ");
-
-	client_process_exec_argv(argv[0], argv);
-}
-
-void client_process_exec_argv(const char *executable, const char **argv)
-{
-	const char *p;
-
-	/* hide the path, it's ugly */
-	p = strrchr(argv[0], '/');
-	if (p != NULL) argv[0] = p+1;
-
-	execv(executable, (char **)argv);
-}
-
-static const char *get_exit_status_message(enum fatal_exit_status status,
-					   enum process_type process_type)
-{
-	switch (status) {
-	case FATAL_LOGOPEN:
-		return "Can't open log file";
-	case FATAL_LOGWRITE:
-		return "Can't write to log file";
-	case FATAL_LOGERROR:
-		return "Internal logging error";
-	case FATAL_OUTOFMEM:
-		switch (process_type) {
-		case PROCESS_TYPE_AUTH:
-		case PROCESS_TYPE_AUTH_WORKER:
-			return "Out of memory - see auth_process_size setting";
-		case PROCESS_TYPE_LOGIN:
-			return "Out of memory - see login_process_size setting";
-		case PROCESS_TYPE_IMAP:
-		case PROCESS_TYPE_POP3:
-			return "Out of memory - see mail_process_size setting";
-		case PROCESS_TYPE_UNKNOWN:
-		case PROCESS_TYPE_SSL_PARAM:
-		case PROCESS_TYPE_DICT:
-		case PROCESS_TYPE_MAX:
-			break;
-		}
-		return "Out of memory";
-	case FATAL_EXEC:
-		return "exec() failed";
-
-	case FATAL_DEFAULT:
-		return "Fatal failure";
-	}
-
-	return NULL;
-}
-
-static void
-log_coredump(string_t *str, enum process_type process_type, int status)
-{
-#ifdef WCOREDUMP
-	struct master_auth_settings *const *auth_set;
-	int signum = WTERMSIG(status);
-
-	if (WCOREDUMP(status)) {
-		str_append(str, " (core dumped)");
-		return;
-	}
-
-	if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
-		return;
-
-	/* let's try to figure out why we didn't get a core dump */
-	if (core_dumps_disabled) {
-		str_printfa(str, " (core dumps disabled)");
-		return;
-	}
-
-	switch (process_type) {
-	case PROCESS_TYPE_LOGIN:
-#ifdef HAVE_PR_SET_DUMPABLE
-		str_append(str, " (core not dumped - add -D to login_executable)");
-		return;
-#else
-		break;
-#endif
-	case PROCESS_TYPE_IMAP:
-	case PROCESS_TYPE_POP3:
-#ifndef HAVE_PR_SET_DUMPABLE
-		if (!master_set->defaults->mail_drop_priv_before_exec) {
-			str_append(str, " (core not dumped - set mail_drop_priv_before_exec=yes)");
-			return;
-		}
-		if (*master_set->defaults->mail_privileged_group != '\0') {
-			str_append(str, " (core not dumped - mail_privileged_group prevented it)");
-			return;
-		}
-#endif
-		str_append(str, " (core not dumped - is home dir set?)");
-		return;
-	case PROCESS_TYPE_AUTH:
-	case PROCESS_TYPE_AUTH_WORKER:
-		auth_set = array_idx(&master_set->defaults->auths, 0);
-		if (auth_set[0]->uid == 0)
-			break;
-#ifdef HAVE_PR_SET_DUMPABLE
-		str_printfa(str, " (core not dumped - "
-			    "no permissions for auth user %s in %s?)",
-			    auth_set[0]->user, master_set->defaults->base_dir);
-#else
-		str_append(str, " (core not dumped - auth user is not root)");
-#endif
-		return;
-	default:
-		break;
-	}
-	str_append(str, " (core not dumped)");
-#endif
-}
-
-static void sigchld_handler(const siginfo_t *si ATTR_UNUSED,
-			    void *context ATTR_UNUSED)
-{
-	struct child_process *process;
-	const char *process_type_name, *msg;
-	enum process_type process_type;
-	string_t *str;
-	pid_t pid;
-	int status;
-	bool abnormal_exit;
-
-	str = t_str_new(128);
-	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
-		/* get the type and remove from hash */
-		str_truncate(str, 0);
-		process = child_process_lookup(pid);
-		if (process == NULL)
-			process_type = PROCESS_TYPE_UNKNOWN;
-		else {
-			process_type = process->type;
-			child_process_remove(pid);
-		}
-		abnormal_exit = TRUE;
-
-		/* write errors to syslog */
-		process_type_name = process_names[process_type];
-		if (WIFEXITED(status)) {
-			status = WEXITSTATUS(status);
-			if (status == 0) {
-				abnormal_exit = FALSE;
-				if (process_type == PROCESS_TYPE_UNKNOWN) {
-					i_error("unknown child %s exited "
-						"successfully", dec2str(pid));
-				}
-			} else if (status == 1 &&
-				   process_type == PROCESS_TYPE_SSL_PARAM) {
-				/* kludgy. hide this failure. */
-			} else if (status == FATAL_DEFAULT &&
-				   process->seen_fatal) {
-				/* the error was already logged. */
-			} else {
-				msg = get_exit_status_message(status,
-							      process_type);
-				msg = msg == NULL ? "" :
-					t_strconcat(" (", msg, ")", NULL);
-				str_printfa(str,
-					    "child %s (%s) returned error %d%s",
-					    dec2str(pid), process_type_name,
-					    status, msg);
-			}
-		} else if (WIFSIGNALED(status)) {
-			str_printfa(str, "child %s (%s) killed with signal %d",
-				    dec2str(pid), process_type_name,
-				    WTERMSIG(status));
-			log_coredump(str, process_type, status);
-		}
-
-		if (str_len(str) > 0) {
-			if (process != NULL && process->ip.family != 0) {
-				if (!process->ip_changed)
-					str_append(str, " (ip=");
-				else
-					str_append(str, " (latest ip=");
-				str_printfa(str, "%s)",
-					    net_ip2addr(&process->ip));
-			}
-			i_error("%s", str_c(str));
-		}
-
-		if (destroy_callbacks[process_type] != NULL) {
-			destroy_callbacks[process_type](process, pid,
-							abnormal_exit);
-		}
-	}
-
-	if (pid == -1 && errno != EINTR && errno != ECHILD)
-		i_warning("waitpid() failed: %m");
-}
-
-void child_process_set_destroy_callback(enum process_type type,
-					child_process_destroy_callback_t *cb)
-{
-	i_assert(type < PROCESS_TYPE_MAX);
-
-	destroy_callbacks[type] = cb;
-}
-
-void child_processes_init(void)
-{
-	processes = hash_table_create(default_pool, default_pool, 128, NULL, NULL);
-	lib_signals_set_handler(SIGCHLD, TRUE, sigchld_handler, NULL);
-}
-
-void child_processes_flush(void)
-{
-	/* make sure we log if child processes died unexpectedly */
-	sigchld_handler(NULL, NULL);
-}
-
-void child_processes_deinit(void)
-{
-	child_processes_flush();
-	lib_signals_unset_handler(SIGCHLD, sigchld_handler, NULL);
-	hash_table_destroy(&processes);
-}
--- a/src/master/child-process.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-#ifndef CHILD_PROCESS_H
-#define CHILD_PROCESS_H
-
-enum process_type {
-	PROCESS_TYPE_UNKNOWN,
-	PROCESS_TYPE_AUTH,
-	PROCESS_TYPE_AUTH_WORKER,
-	PROCESS_TYPE_LOGIN,
-	PROCESS_TYPE_IMAP,
-	PROCESS_TYPE_POP3,
-	PROCESS_TYPE_SSL_PARAM,
-	PROCESS_TYPE_DICT,
-
-	PROCESS_TYPE_MAX
-};
-
-struct child_process {
-	enum process_type type;
-	struct ip_addr ip;
-	unsigned int allow_change_ip:1;
-	unsigned int seen_fatal:1;
-	unsigned int ip_changed:1;
-};
-
-typedef void child_process_destroy_callback_t(struct child_process *process,
-					      pid_t pid, bool abnormal_exit);
-
-extern const char *process_names[];
-extern struct hash_table *processes;
-
-struct child_process *child_process_lookup(pid_t pid);
-void child_process_add(pid_t pid, struct child_process *process);
-void child_process_remove(pid_t pid);
-
-void child_process_init_env(const struct master_settings *set);
-void client_process_exec(const char *cmd, const char *title);
-void client_process_exec_argv(const char *executable, const char **argv);
-
-void child_process_set_destroy_callback(enum process_type type,
-					child_process_destroy_callback_t *cb);
-
-void child_processes_init(void);
-void child_processes_flush(void);
-void child_processes_deinit(void);
-
-#endif
--- a/src/master/common.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/master/common.h	Thu Apr 23 19:53:44 2009 -0400
@@ -1,26 +1,16 @@
 #ifndef COMMON_H
 #define COMMON_H
 
-struct ip_addr;
-
 #include "lib.h"
+#include "master-interface.h"
 #include "master-settings.h"
 
-#define AUTH_SUCCESS_PATH PKG_STATEDIR"/auth-success"
-
-extern struct ioloop *ioloop;
-extern int null_fd, inetd_login_fd;
+extern struct master_service *master_service;
 extern uid_t master_uid;
-extern char program_path[];
-extern char ssl_manual_key_password[];
-extern const char *env_tz;
-extern bool auth_success_written;
+extern gid_t master_gid;
 extern bool core_dumps_disabled;
-#ifdef DEBUG
-extern bool gdb;
-#endif
+extern int null_fd;
 
-#define IS_INETD() \
-	(inetd_login_fd != -1)
+void process_exec(const char *cmd, const char *extra_args[]) ATTR_NORETURN;
 
 #endif
--- a/src/master/dict-process.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-/* Copyright (c) 2006-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "ioloop.h"
-#include "network.h"
-#include "fd-close-on-exec.h"
-#include "env-util.h"
-#include "log.h"
-#include "child-process.h"
-#include "dict-process.h"
-
-#include <syslog.h>
-#include <unistd.h>
-#include <sys/stat.h>
-
-#define DICT_SERVER_SOCKET_NAME "dict-server"
-
-struct dict_listener {
-	char *path;
-	int fd;
-	struct io *io;
-
-	struct dict_process *processes;
-	unsigned int destroyed:1;
-};
-
-struct dict_process {
-	struct child_process process;
-	struct dict_process *next;
-
-	struct dict_listener *listener;
-	struct log_io *log;
-};
-
-static struct dict_listener *dict_listener;
-
-static int dict_process_create(struct dict_listener *listener)
-{
-	struct dict_process *process;
-	struct log_io *log;
-	const char *executable;
-	unsigned int i;
-	int log_fd;
-	pid_t pid;
-
-	process = i_new(struct dict_process, 1);
-	process->process.type = PROCESS_TYPE_DICT;
-	process->listener = listener;
-
-	log_fd = log_create_pipe(&log, 0);
-	if (log_fd < 0)
-		pid = -1;
-	else {
-		pid = fork();
-		if (pid < 0)
-			i_error("fork() failed: %m");
-	}
-
-	if (pid < 0) {
-		(void)close(log_fd);
-		i_free(process);
-		return -1;
-	}
-
-	if (pid != 0) {
-		/* master */
-		process->next = process->listener->processes;
-		process->listener->processes = process;
-
-		child_process_add(pid, &process->process);
-		log_set_prefix(log, "dict: ");
-		log_set_pid(log, pid);
-		(void)close(log_fd);
-
-		process->log = log;
-		log_ref(process->log);
-		return 0;
-	}
-	log_set_prefix(log, "master-dict: ");
-
-	/* make sure we don't leak syslog fd. try to do it as late as possible,
-	   but also before dup2()s in case syslog fd is one of them. */
-	closelog();
-
-	/* set stdin and stdout to /dev/null, so anything written into it
-	   gets ignored. */
-	if (dup2(null_fd, 0) < 0)
-		i_fatal("dup2(stdin) failed: %m");
-	if (dup2(null_fd, 1) < 0)
-		i_fatal("dup2(stdout) failed: %m");
-
-	/* stderr = log, 3 = listener */
-	if (dup2(log_fd, 2) < 0)
-		i_fatal("dup2(stderr) failed: %m");
-	if (dup2(process->listener->fd, 3) < 0)
-		i_fatal("dup2(3) failed: %m");
-
-	for (i = 0; i <= 3; i++)
-		fd_close_on_exec(i, FALSE);
-
-	child_process_init_env(master_set->defaults);
-	master_settings_export_to_env(master_set->defaults);
-	env_put(t_strconcat("DICT_LISTEN_FROM_FD=",
-			    process->listener->path, NULL));
-
-	executable = PKG_LIBEXECDIR"/dict";
-	client_process_exec(executable, "");
-	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
-	return -1;
-}
-
-static void dict_listener_unref(struct dict_listener *listener)
-{
-	if (listener->processes == NULL && listener->destroyed)
-		i_free(listener);
-}
-
-static void dict_process_deinit(struct dict_process *process)
-{
-	struct dict_listener *listener = process->listener;
-	struct dict_process **p;
-
-	for (p = &listener->processes; *p != NULL; p = &(*p)->next) {
-		if (*p == process) {
-			*p = process->next;
-			break;
-		}
-	}
-
-	if (process->log != NULL)
-		log_unref(process->log);
-	i_free(process);
-
-	dict_listener_unref(listener);
-}
-
-static void dict_listener_input(struct dict_listener *listener)
-{
-	unsigned int i = 0;
-	int fd;
-
-	i_assert(listener->processes == NULL);
-
-	for (i = 0; i < master_set->defaults->dict_process_count; i++) {
-		if (dict_process_create(listener) < 0)
-			break;
-	}
-	if (i > 0)
-		io_remove(&listener->io);
-	else {
-		/* failed to create dict process, so just reject this
-		   connection and try again later */
-		fd = net_accept(listener->fd, NULL, NULL);
-		if (fd >= 0)
-			(void)close(fd);
-	}
-}
-
-static struct dict_listener *dict_listener_init(const char *path)
-{
-	struct dict_listener *listener;
-	mode_t old_umask;
-
-	listener = i_new(struct dict_listener, 1);
-	listener->path = i_strdup(path);
-	old_umask = umask(0);
-	listener->fd = net_listen_unix_unlink_stale(path, 128);
-	umask(old_umask);
-	if (listener->fd == -1) {
-		if (errno == EADDRINUSE)
-			i_fatal("Socket already exists: %s", path);
-		else
-			i_fatal("net_listen_unix(%s) failed: %m", path);
-	}
-	fd_close_on_exec(listener->fd, TRUE);
-	listener->io = io_add(listener->fd, IO_READ,
-			      dict_listener_input, listener);
-	return listener;
-}
-
-static void dict_listener_deinit(struct dict_listener *listener)
-{
-	listener->destroyed = TRUE;
-
-	if (listener->io != NULL)
-		io_remove(&listener->io);
-	if (close(listener->fd) < 0)
-		i_error("close(dict listener) failed: %m");
-	listener->fd = -1;
-
-	/* don't try to free the dict processes here,
-	   let dict_process_destroyed() do it to avoid "unknown child exited"
-	   errors. */
-	dict_listener_unref(listener);
-}
-
-static void
-dict_process_destroyed(struct child_process *_process,
-		       pid_t pid ATTR_UNUSED, bool abnormal_exit ATTR_UNUSED)
-{
-	struct dict_process *process = (struct dict_process *)_process;
-	struct dict_listener *listener = process->listener;
-
-	if (listener->processes == NULL && listener->fd != -1) {
-		/* last listener died, create new ones */
-		listener->io = io_add(listener->fd, IO_READ,
-				      dict_listener_input, listener);
-	}
-	dict_process_deinit(process);
-}
-
-void dict_processes_init(void)
-{
-	const char *path;
-
-	path = t_strconcat(master_set->defaults->base_dir,
-			   "/"DICT_SERVER_SOCKET_NAME, NULL);
-	dict_listener = dict_listener_init(path);
-
-	child_process_set_destroy_callback(PROCESS_TYPE_DICT,
-					   dict_process_destroyed);
-}
-
-void dict_processes_deinit(void)
-{
-	dict_listener_deinit(dict_listener);
-}
-
-void dict_processes_kill(void)
-{
-	struct dict_process *process;
-
-	process = dict_listener->processes;
-	for (; process != NULL; process = process->next) {
-		if (process->log != NULL) {
-			log_unref(process->log);
-			process->log = NULL;
-		}
-	}
-}
--- a/src/master/dict-process.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#ifndef DICT_PROCESS_H
-#define DICT_PROCESS_H
-
-void dict_processes_init(void);
-void dict_processes_deinit(void);
-void dict_processes_kill(void);
-
-#endif
--- a/src/master/listener.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,350 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#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 master_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)
-{
-	if (master_set->imap != NULL) {
-		check_conflicts_set(master_set->imap, ip, port,
-				    "imap", proto);
-	}
-	if (master_set->pop3 != NULL) {
-		check_conflicts_set(master_set->pop3, ip, port,
-				    "pop3", proto);
-	}
-}
-
-static void
-listener_init(const char *set_name, const char *listen_list,
-	      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;
-
-	for (tmp = t_strsplit_spaces(listen_list, ", "); *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);
-		}
-	}
-}
-
-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 master_settings *set)
-{
-	const char *const *proto;
-	unsigned int default_port;
-	bool nonssl_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)
-				nonssl_listen = TRUE;
-		} else if (strcasecmp(*proto, "imaps") == 0) {
-			if (set->protocol == MAIL_PROTOCOL_IMAP &&
-			    strcmp(set->ssl, "no") != 0)
-				ssl_listen = TRUE;
-		} else if (strcasecmp(*proto, "pop3") == 0) {
-			if (set->protocol == MAIL_PROTOCOL_POP3)
-				nonssl_listen = TRUE;
-		} else if (strcasecmp(*proto, "pop3s") == 0) {
-			if (set->protocol == MAIL_PROTOCOL_POP3 &&
-			    strcmp(set->ssl, "no") != 0)
-				ssl_listen = TRUE;
-		}
-	}
-
-	if (!nonssl_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 != '\0' ?
-			      set->ssl_listen : set->listen, default_port,
-			      &set->ssl_listens);
-	}
-}
-
-static void listen_copy_old(struct master_settings *old_set,
-			    struct master_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, 128);
-			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 master_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 master_server_settings *old_set, bool retry)
-{
-	if (old_set != NULL) {
-		listen_copy_old(old_set->imap, master_set->imap);
-		listen_copy_old(old_set->pop3, master_set->pop3);
-	}
-	listen_parse_and_close_unneeded(master_set->imap);
-	listen_parse_and_close_unneeded(master_set->pop3);
-
-	if (master_set->imap != NULL)
-		listener_listen_missing(master_set->imap, "imap", retry);
-	if (master_set->pop3 != NULL)
-		listener_listen_missing(master_set->pop3, "pop3", retry);
-}
-
-void listeners_close_fds(void)
-{
-	if (master_set->imap != NULL) {
-		listener_close_fds(&master_set->imap->listens);
-		listener_close_fds(&master_set->imap->ssl_listens);
-	}
-	if (master_set->pop3 != NULL) {
-		listener_close_fds(&master_set->pop3->listens);
-		listener_close_fds(&master_set->pop3->ssl_listens);
-	}
-}
--- a/src/master/listener.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-#ifndef LISTENER_H
-#define LISTENER_H
-
-void listeners_open_fds(struct master_server_settings *old_set, bool retry);
-void listeners_close_fds(void);
-
-#endif
--- a/src/master/log.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,338 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "ioloop.h"
-#include "istream.h"
-#include "fd-set-nonblock.h"
-#include "fd-close-on-exec.h"
-#include "child-process.h"
-#include "log.h"
-
-#include <unistd.h>
-
-struct log_io {
-	struct log_io *prev, *next;
-	int refcount;
-
-	struct io *io;
-	struct istream *stream;
-	pid_t pid;
-	struct ip_addr ip;
-
-	time_t log_stamp;
-	unsigned int log_counter;
-        unsigned int max_lines_per_sec;
-
-	char *prefix;
-	char next_log_type;
-	unsigned int throttle_msg:1;
-	unsigned int destroying:1;
-};
-
-static struct log_io *log_ios;
-static struct timeout *to;
-static unsigned int throttle_count;
-
-static int log_it(struct log_io *log_io, const char *line, bool continues);
-static int log_read(struct log_io *log_io);
-static void log_throttle_timeout(void *context);
-
-static bool log_write_pending(struct log_io *log_io)
-{
-	const char *line;
-	bool ret;
-
-	if (log_io->log_stamp != ioloop_time) {
-		log_io->log_stamp = ioloop_time;
-		log_io->log_counter = 0;
-	}
-
-	while ((line = i_stream_next_line(log_io->stream)) != NULL) {
-		T_BEGIN {
-			ret = log_it(log_io, line, FALSE);
-		} T_END;
-		if (!ret)
-			return FALSE;
-	}
-
-	return TRUE;
-}
-
-static void log_throttle(struct log_io *log_io)
-{
-	if (!log_io->throttle_msg) {
-                log_io->throttle_msg = TRUE;
-		log_it(log_io, "Sending log messages too fast, throttling..",
-		       FALSE);
-	}
-
-	if (log_io->io == NULL) {
-		i_assert(to != NULL);
-		return;
-	}
-
-	io_remove(&log_io->io);
-        throttle_count++;
-
-	if (to == NULL)
-		to = timeout_add(1000, log_throttle_timeout, NULL);
-}
-
-static void log_read_callback(struct log_io *log_io)
-{
-	(void)log_read(log_io);
-}
-
-static void log_unthrottle(struct log_io *log_io)
-{
-	if (log_io->io != NULL)
-		return;
-
-	if (--throttle_count == 0 && to != NULL)
-		timeout_remove(&to);
-	log_io->io = io_add(i_stream_get_fd(log_io->stream),
-			    IO_READ, log_read_callback, log_io);
-}
-
-static int log_it(struct log_io *log_io, const char *line, bool continues)
-{
-	struct child_process *process;
-	const char *prefix;
-	enum log_type log_type;
-
-	if (log_io->next_log_type == '\0') {
-		if (line[0] == 1 && line[1] != '\0') {
-			/* our internal protocol.
-			   \001 + log_type */
-			log_io->next_log_type = line[1];
-			line += 2;
-		} else {
-			log_io->next_log_type = 'E';
-		}
-	}
-
-	prefix = log_io->prefix != NULL ? log_io->prefix : "";
-	switch (log_io->next_log_type) {
-	case 'I':
-		log_type = LOG_TYPE_INFO;
-		break;
-	case 'W':
-		log_type = LOG_TYPE_WARNING;
-		break;
-	case 'E':
-		log_type = LOG_TYPE_ERROR;
-		break;
-	case 'F':
-	case 'P':
-		log_type = log_io->next_log_type == 'F' ?
-			LOG_TYPE_FATAL : LOG_TYPE_PANIC;
-		process = child_process_lookup(log_io->pid);
-		if (process != NULL)
-			process->seen_fatal = TRUE;
-		break;
-	case 'O':
-		/* logging option. ignore unknown ones. */
-		if (strncmp(line, "ip=", 3) == 0) {
-			process = child_process_lookup(log_io->pid);
-			if (process != NULL &&
-			    (process->allow_change_ip ||
-			     process->ip.family == 0)) {
-				if (process->ip.family != 0)
-					process->ip_changed = TRUE;
-				net_addr2ip(line + 3, &process->ip);
-			}
-		}
-		log_io->next_log_type = '\0';
-		return 1;
-	default:
-		log_type = LOG_TYPE_ERROR;
-		break;
-	}
-	i_log_type(log_type, "%s%s", prefix, line);
-
-	if (!continues)
-		log_io->next_log_type = '\0';
-
-	if (++log_io->log_counter > log_io->max_lines_per_sec &&
-	    !log_io->destroying) {
-		log_throttle(log_io);
-		return 0;
-	}
-	return 1;
-}
-
-static int log_read(struct log_io *log_io)
-{
-	const unsigned char *data;
-	const char *line;
-	size_t size;
-	int ret;
-
-	if (!log_write_pending(log_io))
-		return 0;
-
-	ret = i_stream_read(log_io->stream);
-	if (ret < 0) {
-		if (ret == -1) {
-			/* closed */
-			log_unref(log_io);
-			return -1;
-		}
-
-		/* buffer full. treat it as one line */
-		data = i_stream_get_data(log_io->stream, &size);
-		line = t_strndup(data, size);
-		i_stream_skip(log_io->stream, size);
-
-		if (!log_it(log_io, line, TRUE))
-			return 0;
-	}
-
-	if (!log_write_pending(log_io))
-		return 0;
-
-	if (log_io->log_counter < log_io->max_lines_per_sec)
-		log_unthrottle(log_io);
-	return 0;
-}
-
-int log_create_pipe(struct log_io **log_r, unsigned int max_lines_per_sec)
-{
-	struct log_io *log_io;
-	int fd[2];
-
-	if (pipe(fd) < 0) {
-		i_error("pipe() failed: %m");
-		return -1;
-	}
-
-	fd_set_nonblock(fd[0], TRUE);
-	fd_close_on_exec(fd[0], TRUE);
-	fd_close_on_exec(fd[1], TRUE);
-
-	log_io = i_new(struct log_io, 1);
-	log_io->refcount = 1;
-	log_io->pid = (pid_t)-1;
-	log_io->stream = i_stream_create_fd(fd[0], 1024, TRUE);
-	log_io->max_lines_per_sec =
-		max_lines_per_sec != 0 ? max_lines_per_sec : (unsigned int)-1;
-
-	throttle_count++;
-        log_unthrottle(log_io);
-
-	if (log_ios != NULL)
-		log_ios->prev = log_io;
-	log_io->next = log_ios;
-	log_ios = log_io;
-
-	if (log_r != NULL)
-		*log_r = log_io;
-	return fd[1];
-}
-
-void log_set_prefix(struct log_io *log, const char *prefix)
-{
-	i_free(log->prefix);
-	log->prefix = i_strdup(prefix);
-}
-
-void log_set_pid(struct log_io *log, pid_t pid)
-{
-	log->pid = pid;
-}
-
-void log_ref(struct log_io *log_io)
-{
-	log_io->refcount++;
-}
-
-static void log_close(struct log_io *log_io)
-{
-	const unsigned char *data;
-	size_t size;
-
-	if (log_io->destroying)
-		return;
-
-	/* if there was something in buffer, write it */
-	log_io->destroying = TRUE;
-	(void)log_write_pending(log_io);
-
-	/* write partial data as well */
-	data = i_stream_get_data(log_io->stream, &size);
-	if (size != 0) {
-		T_BEGIN {
-			log_it(log_io, t_strndup(data, size), TRUE);
-		} T_END;
-	}
-
-	if (log_io == log_ios)
-		log_ios = log_io->next;
-	else
-		log_io->prev->next = log_io->next;
-	if (log_io->next != NULL)
-		log_io->next->prev = log_io->prev;
-
-	if (log_io->io != NULL)
-		io_remove(&log_io->io);
-	else
-		throttle_count--;
-	i_stream_destroy(&log_io->stream);
-}
-
-void log_unref(struct log_io *log_io)
-{
-	i_assert(log_io->refcount > 0);
-
-	log_close(log_io);
-
-	if (--log_io->refcount > 0)
-		return;
-
-	i_free(log_io->prefix);
-	i_free(log_io);
-}
-
-static void log_throttle_timeout(void *context ATTR_UNUSED)
-{
-	struct log_io *log, *next;
-	unsigned int left = throttle_count;
-
-	i_assert(left > 0);
-
-	for (log = log_ios; log != NULL; log = next) {
-		next = log->next;
-
-		if (log->io == NULL) {
-			if (log_write_pending(log))
-				log_unthrottle(log);
-
-			if (--left == 0)
-				break;
-		}
-	}
-}
-
-void log_init(void)
-{
-	log_ios = NULL;
-        throttle_count = 0;
-	to = NULL;
-}
-
-void log_deinit(void)
-{
-	struct log_io *next;
-
-	while (log_ios != NULL) {
-		next = log_ios->next;
-		/* do one final log read in case there's still something
-		   waiting */
-		if (log_read(log_ios) == 0)
-			log_unref(log_ios);
-		log_ios = next;
-	}
-
-	if (to != NULL)
-		timeout_remove(&to);
-}
--- a/src/master/log.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-#ifndef LOG_H
-#define LOG_H
-
-struct log_io;
-
-int log_create_pipe(struct log_io **log_r, unsigned int max_lines_per_sec);
-void log_set_prefix(struct log_io *log, const char *prefix);
-void log_set_pid(struct log_io *log, pid_t pid);
-
-void log_ref(struct log_io *log_io);
-void log_unref(struct log_io *log_io);
-
-void log_init(void);
-void log_deinit(void);
-
-#endif
--- a/src/master/login-process.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,893 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "ioloop.h"
-#include "hash.h"
-#include "network.h"
-#include "ostream.h"
-#include "fdpass.h"
-#include "fd-close-on-exec.h"
-#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"
-#include "master-login-interface.h"
-#include "master-settings.h"
-#include "log.h"
-#include "ssl-init.h"
-
-#include <unistd.h>
-#include <syslog.h>
-#include <sys/stat.h>
-
-#define LOGIN_LIMIT_WARNING_MIN_INTERVAL (60*5)
-
-struct login_process {
-	struct child_process process;
-
-	struct login_group *group;
-	struct login_process *prev_prelogin, *next_prelogin;
-	int refcount;
-
-	pid_t pid;
-	int fd;
-	struct io *io;
-	struct ostream *output;
-	enum master_login_state state;
-
-	unsigned int initialized:1;
-	unsigned int destroyed:1;
-	unsigned int inetd_child:1;
-};
-
-struct login_auth_request {
-	struct login_process *process;
-	unsigned int tag;
-	unsigned int login_tag;
-
-	struct mail_login_request mail_request;
-	unsigned char data[FLEXIBLE_ARRAY_MEMBER];
-};
-
-static unsigned int auth_id_counter, login_pid_counter;
-static struct timeout *to;
-static struct io *io_listen;
-static bool logins_stalled = FALSE;
-
-static struct login_group *login_groups;
-
-static void login_processes_stall(void);
-static void login_process_destroy(struct login_process *p);
-static void login_process_unref(struct login_process *p);
-static bool login_process_init_group(struct login_process *p);
-static void login_processes_start_missing(void *context);
-
-static void login_group_create(struct master_settings *set)
-{
-	struct login_group *group;
-
-	group = i_new(struct login_group, 1);
-	group->refcount = 1;
-	group->set = set;
-	group->mail_process_type = set->protocol == MAIL_PROTOCOL_IMAP ?
-		PROCESS_TYPE_IMAP : PROCESS_TYPE_POP3;
-
-	group->next = login_groups;
-	login_groups = group;
-}
-
-static void login_group_unref(struct login_group *group)
-{
-	i_assert(group->refcount > 0);
-
-	if (--group->refcount > 0)
-		return;
-
-	i_free(group);
-}
-
-void auth_master_callback(const char *user, const char *const *args,
-			  struct login_auth_request *request)
-{
-	struct master_login_reply master_reply;
-	ssize_t ret;
-
-	memset(&master_reply, 0, sizeof(master_reply));
-	if (user == NULL)
-		master_reply.status = MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-	else T_BEGIN {
-		struct login_group *group = request->process->group;
-
-		master_reply.status =
-			create_mail_process(group->mail_process_type,
-					    group->set, &request->mail_request,
-					    user, args, request->data, FALSE,
-					    &master_reply.mail_pid);
-	} T_END;
-
-	/* reply to login */
-	master_reply.tag = request->login_tag;
-
-	ret = o_stream_send(request->process->output, &master_reply,
-			    sizeof(master_reply));
-	if (ret != sizeof(master_reply)) {
-		if (ret >= 0) {
-			i_warning("Login process %s transmit buffer full, "
-				  "killing..", dec2str(request->process->pid));
-		}
-		login_process_destroy(request->process);
-	}
-
-	if (close(request->mail_request.fd) < 0)
-		i_error("close(mail client) failed: %m");
-	login_process_unref(request->process);
-	i_free(request);
-}
-
-static void process_remove_from_prelogin_lists(struct login_process *p)
-{
-	if (p->state != LOGIN_STATE_FULL_PRELOGINS)
-		return;
-
-	if (p->prev_prelogin == NULL)
-		p->group->oldest_prelogin_process = p->next_prelogin;
-	else
-		p->prev_prelogin->next_prelogin = p->next_prelogin;
-
-	if (p->next_prelogin == NULL)
-		p->group->newest_prelogin_process = p->prev_prelogin;
-	else
-		p->next_prelogin->prev_prelogin = p->prev_prelogin;
-
-	p->prev_prelogin = p->next_prelogin = NULL;
-}
-
-static void process_mark_nonlistening(struct login_process *p,
-				      enum master_login_state new_state)
-{
-	if (p->group == NULL)
-		return;
-
-	if (p->state == LOGIN_STATE_LISTENING)
-		p->group->listening_processes--;
-
-	if (new_state == LOGIN_STATE_FULL_PRELOGINS) {
-		/* add to prelogin list */
-		i_assert(p->state != new_state);
-
-		p->prev_prelogin = p->group->newest_prelogin_process;
-		if (p->group->newest_prelogin_process == NULL)
-			p->group->oldest_prelogin_process = p;
-		else
-			p->group->newest_prelogin_process->next_prelogin = p;
-		p->group->newest_prelogin_process = p;
-	} else {
-		process_remove_from_prelogin_lists(p);
-	}
-}
-
-static void process_mark_listening(struct login_process *p)
-{
-	if (p->group == NULL)
-		return;
-
-	if (p->state != LOGIN_STATE_LISTENING)
-		p->group->listening_processes++;
-
-	process_remove_from_prelogin_lists(p);
-}
-
-static void login_process_set_initialized(struct login_process *p)
-{
-	p->initialized = TRUE;
-
-	if (logins_stalled) {
-		/* processes were created successfully */
-		i_info("Created login processes successfully, unstalling");
-
-		logins_stalled = FALSE;
-		timeout_remove(&to);
-		to = timeout_add(1000, login_processes_start_missing, NULL);
-	}
-}
-
-static void
-login_process_set_state(struct login_process *p, enum master_login_state state)
-{
-	if (state == p->state || state > LOGIN_STATE_COUNT ||
-	    (state < p->state && p->group->set->login_process_per_connection)) {
-		i_error("login: tried to change state %d -> %d "
-			"(if you can't login at all, see src/lib/fdpass.c)",
-			p->state, state);
-		login_process_destroy(p);
-		return;
-	}
-
-	if (state == LOGIN_STATE_LISTENING) {
-		process_mark_listening(p);
-	} else {
-		process_mark_nonlistening(p, state);
-	}
-
-	p->state = state;
-}
-
-static void login_process_groups_create(void)
-{
-	if (master_set->imap != NULL)
-		login_group_create(master_set->imap);
-	if (master_set->pop3 != NULL)
-		login_group_create(master_set->pop3);
-}
-
-static struct login_group *
-login_group_process_find(enum mail_protocol protocol)
-{
-	struct login_group *group;
-
-	if (login_groups == NULL)
-                login_process_groups_create();
-
-	for (group = login_groups; group != NULL; group = group->next) {
-		if (group->set->protocol == protocol)
-			return group;
-	}
-
-	return NULL;
-}
-
-static bool login_process_read_group(struct login_process *p)
-{
-	struct login_group *group;
-	const char *proto;
-	unsigned char buf[256];
-	enum mail_protocol protocol;
-	unsigned int len;
-	ssize_t ret;
-
-	/* read length */
-	ret = read(p->fd, buf, 1);
-	if (ret != 1)
-		len = 0;
-	else {
-		len = buf[0];
-		if (len >= sizeof(buf)) {
-			i_error("login: Server name length too large");
-			return FALSE;
-		}
-
-		ret = read(p->fd, buf, len);
-	}
-
-	if (ret < 0)
-		i_error("login: read() failed: %m");
-	else if (len == 0 || (size_t)ret != len)
-		i_error("login: Server name wasn't sent");
-	else {
-		proto = t_strndup(buf, len);
-		if (strcmp(proto, "imap") == 0)
-			protocol = MAIL_PROTOCOL_IMAP;
-		else if (strcmp(proto, "pop3") == 0)
-			protocol = MAIL_PROTOCOL_POP3;
-		else {
-			i_error("login: Unknown protocol '%s'", proto);
-			return FALSE;
-		}
-
-		group = login_group_process_find(protocol);
-		if (group == NULL) {
-			i_error("login: No group for protocol '%s'", proto);
-			return FALSE;
-		}
-
-		p->group = group;
-		return login_process_init_group(p);
-	}
-	return FALSE;
-}
-
-static int
-login_read_request(struct login_process *p, struct master_login_request *req,
-		   unsigned char data[MASTER_LOGIN_MAX_DATA_SIZE],
-		   int *client_fd_r)
-{
-	struct stat st;
-	ssize_t ret;
-
-	*client_fd_r = -1;
-
-	ret = fd_read(p->fd, req, sizeof(*req), client_fd_r);
-	if (ret >= (ssize_t)sizeof(req->version) &&
-	    req->version != MASTER_LOGIN_PROTOCOL_VERSION) {
-		i_error("login: Protocol version mismatch "
-			"(mixed old and new binaries?)");
-		return -1;
-	}
-
-	if (ret != sizeof(*req)) {
-		if (ret == 0) {
-			/* disconnected, ie. the login process died */
-		} else if (ret > 0) {
-			/* request wasn't fully read */
-			i_error("login: fd_read() returned partial %d",
-				(int)ret);
-		} else {
-			if (errno == EAGAIN)
-				return 0;
-
-			i_error("login: fd_read() failed: %m");
-		}
-		return -1;
-	}
-
-	if (req->ino == (ino_t)-1) {
-		if (*client_fd_r != -1) {
-			i_error("login: Notification request sent "
-				"a file descriptor");
-			return -1;
-		}
-		return 1;
-	}
-	if (req->data_size != 0) {
-		if (req->data_size > MASTER_LOGIN_MAX_DATA_SIZE) {
-			i_error("login: Too large data_size sent");
-			return -1;
-		}
-		/* @UNSAFE */
-		ret = read(p->fd, data, req->data_size);
-		if (ret != req->data_size) {
-			if (ret == 0) {
-				/* disconnected */
-			} else if (ret > 0) {
-				/* request wasn't fully read */
-				i_error("login: Data read partially %d/%u",
-					(int)ret, req->data_size);
-			} else {
-				i_error("login: read(data) failed: %m");
-			}
-			return -1;
-		}
-	}
-
-	if (*client_fd_r == -1) {
-		i_error("login: Login request missing a file descriptor");
-		return -1;
-	}
-
-	if (fstat(*client_fd_r, &st) < 0) {
-		i_error("login: fstat(mail client) failed: %m");
-		return -1;
-	}
-	if (st.st_ino != req->ino) {
-		i_error("login: Login request inode mismatch: %s != %s",
-			dec2str(st.st_ino), dec2str(req->ino));
-		return -1;
-	}
-	return 1;
-}
-
-static void login_process_input(struct login_process *p)
-{
-	struct auth_process *auth_process;
-	struct login_auth_request *authreq;
-	struct master_login_request req;
-	unsigned char data[MASTER_LOGIN_MAX_DATA_SIZE];
-	int client_fd;
-	ssize_t ret;
-
-	if (p->group == NULL) {
-		/* we want to read the group */
-		if (!login_process_read_group(p))
-			login_process_destroy(p);
-		return;
-	}
-
-	ret = login_read_request(p, &req, data, &client_fd);
-	if (ret == 0)
-		return;
-	if (ret < 0) {
-		if (client_fd != -1) {
-			if (close(client_fd) < 0)
-				i_error("login: close(mail client) failed: %m");
-		}
-		login_process_destroy(p);
-		return;
-	}
-
-	if (req.ino == (ino_t)-1) {
-		/* state notification */
-		enum master_login_state state = req.tag;
-
-		if (!p->initialized) {
-			/* initialization notify */
-			login_process_set_initialized(p);
-		} else {
-			/* change "listening for new connections" status */
-			login_process_set_state(p, state);
-		}
-		return;
-	}
-
-	if (!p->initialized) {
-		i_error("login: trying to log in before initialization");
-		login_process_destroy(p);
-		return;
-	}
-
-	fd_close_on_exec(client_fd, TRUE);
-
-	/* ask the cookie from the auth process */
-	authreq = i_malloc(sizeof(*authreq) + req.data_size);
-	p->refcount++;
-	authreq->process = p;
-	authreq->tag = ++auth_id_counter;
-	authreq->login_tag = req.tag;
-	authreq->mail_request.fd = client_fd;
-	authreq->mail_request.local_ip = req.local_ip;
-	authreq->mail_request.remote_ip = req.remote_ip;
-	authreq->mail_request.cmd_tag_size = req.cmd_tag_size;
-	authreq->mail_request.data_size = req.data_size;
-	memcpy(authreq->data, data, req.data_size);
-
-	auth_process = auth_process_find(req.auth_pid);
-	if (auth_process == NULL) {
-		i_error("login: Authentication process %u doesn't exist",
-			req.auth_pid);
-		auth_master_callback(NULL, NULL, authreq);
-	} else {
-		auth_process_request(auth_process, p->pid,
-				     req.auth_id, authreq);
-	}
-}
-
-static struct login_process *
-login_process_new(struct login_group *group, pid_t pid, int fd,
-		  bool inetd_child)
-{
-	struct login_process *p;
-
-	i_assert(pid != 0);
-
-	p = i_new(struct login_process, 1);
-	p->process.type = PROCESS_TYPE_LOGIN;
-	p->group = group;
-	p->refcount = 2; /* once for fd close, another for process exit */
-	p->pid = pid;
-	p->fd = fd;
-	p->inetd_child = inetd_child;
-	p->io = io_add(fd, IO_READ, login_process_input, p);
-	p->output = o_stream_create_fd(fd, sizeof(struct master_login_reply)*10,
-				       FALSE);
-	if (!inetd_child) {
-		if (!group->set->login_process_per_connection)
-			p->process.allow_change_ip = TRUE;
-		child_process_add(pid, &p->process);
-	}
-
-	p->state = LOGIN_STATE_LISTENING;
-
-	if (p->group != NULL) {
-		p->group->refcount++;
-		p->group->processes++;
-		p->group->listening_processes++;
-	}
-	return p;
-}
-
-static void login_process_exited(struct login_process *p)
-{
-	if (p->group != NULL)
-		p->group->processes--;
-
-	login_process_unref(p);
-}
-
-static void login_process_destroy(struct login_process *p)
-{
-	if (p->destroyed)
-		return;
-	p->destroyed = TRUE;
-
-	if (!p->initialized)
-		login_processes_stall();
-
-	o_stream_close(p->output);
-	io_remove(&p->io);
-	if (close(p->fd) < 0)
-		i_error("close(login) failed: %m");
-
-	process_mark_nonlistening(p, LOGIN_STATE_FULL_LOGINS);
-
-	if (p->inetd_child)
-		login_process_exited(p);
-	login_process_unref(p);
-}
-
-static void login_process_unref(struct login_process *p)
-{
-	i_assert(p->refcount > 0);
-	if (--p->refcount > 0)
-		return;
-
-	if (p->group != NULL)
-		login_group_unref(p->group);
-
-	o_stream_unref(&p->output);
-	i_free(p);
-}
-
-static void login_process_init_env(struct login_group *group, pid_t pid)
-{
-	struct master_settings *set = group->set;
-	struct restrict_access_settings rset;
-
-	child_process_init_env(group->set);
-
-	/* setup access environment - needs to be done after
-	   clean_child_process() since it clears environment. Don't set user
-	   parameter since we don't want to call initgroups() for login
-	   processes. */
-	restrict_access_init(&rset);
-	rset.uid = set->login_uid;
-	rset.gid = set->server->login_gid;
-	if (set->login_chroot)
-		rset.chroot_dir = set->login_dir;
-	restrict_access_set_env(&rset);
-
-	env_put("DOVECOT_MASTER=1");
-
-	master_settings_export_to_env(group->set);
-	if (ssl_manual_key_password != NULL) {
-		env_put(t_strconcat("SSL_MANUAL_KEY_PASSWORD=",
-				    ssl_manual_key_password, NULL));
-	}
-
-	env_put(t_strconcat("PROCESS_UID=", dec2str(pid), NULL));
-	if (group->mail_process_type == PROCESS_TYPE_IMAP &&
-	    set->imap_generated_capability != NULL) {
-		env_put(t_strconcat("CAPABILITY_STRING=",
-				    set->imap_generated_capability, NULL));
-	}
-	env_put(t_strconcat("LOGIN_DIR=", set->login_dir, NULL));
-}
-
-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;
-	ARRAY_TYPE(dup2) dups;
-	unsigned int i, listen_count = 0, ssl_listen_count = 0;
-	int fd[2], log_fd, cur_fd, tmp_fd;
-
-	if (group->set->login_uid == 0)
-		i_fatal("Login process must not run as root");
-
-	/* create communication to process with a socket pair */
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
-		i_error("socketpair() failed: %m");
-		return -1;
-	}
-
-	max_log_lines_per_sec =
-		group->set->login_process_per_connection ? 10 : 0;
-	log_fd = log_create_pipe(&log, max_log_lines_per_sec);
-	if (log_fd < 0)
-		pid = -1;
-	else {
-		pid = fork();
-		if (pid < 0)
-			i_error("fork() failed: %m");
-	}
-
-	if (pid < 0) {
-		(void)close(fd[0]);
-		(void)close(fd[1]);
-		(void)close(log_fd);
-		return -1;
-	}
-
-	if (pid != 0) {
-		/* master */
-		prefix = t_strdup_printf("%s-login: ",
-				process_names[group->mail_process_type]);
-		log_set_prefix(log, prefix);
-		log_set_pid(log, pid);
-
-		net_set_nonblock(fd[0], TRUE);
-		fd_close_on_exec(fd[0], TRUE);
-		(void)login_process_new(group, pid, fd[0], FALSE);
-		(void)close(fd[1]);
-		(void)close(log_fd);
-		return pid;
-	}
-
-	prefix = t_strdup_printf("master-%s-login: ",
-				 process_names[group->mail_process_type]);
-	log_set_prefix(log, prefix);
-
-	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. */
-	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);
-	}
-
-	/* make sure we don't leak syslog fd. try to do it as late as possible,
-	   but also before dup2()s in case syslog fd is one of them. */
-	closelog();
-
-	if (dup2_array(&dups) < 0)
-		i_fatal("Failed to dup2() fds");
-
-	/* don't close any of these */
-	for (tmp_fd = 0; tmp_fd < cur_fd; tmp_fd++)
-		fd_close_on_exec(tmp_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));
-
-	restrict_process_size(group->set->login_process_size, (unsigned int)-1);
-
-	client_process_exec(group->set->login_executable, "");
-	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
-		       group->set->login_executable);
-	return -1;
-}
-
-static void
-login_process_destroyed(struct child_process *process,
-			pid_t pid ATTR_UNUSED, bool abnormal_exit)
-{
-	struct login_process *p = (struct login_process *)process;
-
-	i_assert(!p->inetd_child);
-
-	if (abnormal_exit) {
-		/* don't start raising the process count if they're dying all
-		   the time */
-		if (p->group != NULL)
-			p->group->wanted_processes_count = 0;
-	}
-
-	login_process_exited(p);
-}
-
-void login_processes_destroy_all(void)
-{
-	struct hash_iterate_context *iter;
-	void *key, *value;
-
-	iter = hash_table_iterate_init(processes);
-	while (hash_table_iterate(iter, &key, &value)) {
-		struct login_process *p = value;
-
-		if (p->process.type == PROCESS_TYPE_LOGIN)
-			login_process_destroy(p);
-	}
-	hash_table_iterate_deinit(&iter);
-
-	while (login_groups != NULL) {
-		struct login_group *group = login_groups;
-
-		login_groups = group->next;
-		login_group_unref(group);
-	}
-}
-
-static void login_processes_notify_group(struct login_group *group)
-{
-	struct hash_iterate_context *iter;
-	struct master_login_reply reply;
-	void *key, *value;
-
-	memset(&reply, 0, sizeof(reply));
-
-	iter = hash_table_iterate_init(processes);
-	while (hash_table_iterate(iter, &key, &value)) {
-		struct login_process *p = value;
-
-		if (p->process.type == PROCESS_TYPE_LOGIN && p->group == group)
-			(void)o_stream_send(p->output, &reply, sizeof(reply));
-	}
-	hash_table_iterate_deinit(&iter);
-}
-
-static int login_group_start_missings(struct login_group *group)
-{
-	if (group->set->login_process_per_connection &&
-	    group->processes >= group->set->login_max_processes_count &&
-	    group->listening_processes == 0) {
-		/* destroy the oldest listening process. non-listening
-		   processes are logged in users who we don't want to kick out
-		   because someone's started flooding */
-		if (group->oldest_prelogin_process != NULL &&
-		    group->oldest_prelogin_process->initialized)
-			login_process_destroy(group->oldest_prelogin_process);
-		else if (ioloop_time - group->last_limit_warning >
-			 LOGIN_LIMIT_WARNING_MIN_INTERVAL) {
-			group->last_limit_warning = ioloop_time;
-			i_warning("All login processes are in use. You may "
-				  "need to increase login_max_processes_count");
-		}
-	}
-
-	/* we want to respond fast when multiple clients are connecting
-	   at once, but we also want to prevent fork-bombing. use the
-	   same method as apache: check once a second if we need new
-	   processes. if yes and we've used all the existing processes,
-	   double their amount (unless we've hit the high limit).
-	   Then for each second that didn't use all existing processes,
-	   drop the max. process count by one. */
-	if (group->wanted_processes_count < group->set->login_processes_count) {
-		group->wanted_processes_count =
-			group->set->login_processes_count;
-	} else if (group->listening_processes == 0)
-		group->wanted_processes_count *= 2;
-	else if (group->wanted_processes_count >
-		 group->set->login_processes_count)
-		group->wanted_processes_count--;
-
-	while (group->listening_processes < group->wanted_processes_count &&
-	       group->processes < group->set->login_max_processes_count) {
-		if (create_login_process(group) < 0)
-			return -1;
-	}
-
-	if (group->listening_processes == 0 &&
-	    !group->set->login_process_per_connection) {
-		/* we've reached our limit. notify the processes to start
-		   listening again which makes them kill some of their
-		   oldest clients when accepting the next connection */
-		login_processes_notify_group(group);
-	}
-	return 0;
-}
-
-static void login_processes_stall(void)
-{
-	if (logins_stalled || IS_INETD())
-		return;
-
-	i_error("Temporary failure in creating login processes, "
-		"slowing down for now");
-	logins_stalled = TRUE;
-
-	timeout_remove(&to);
-	to = timeout_add(60*1000, login_processes_start_missing, NULL);
-}
-
-static void
-login_processes_start_missing(void *context ATTR_UNUSED)
-{
-	struct login_group *group;
-
-	if (!have_initialized_auth_processes) {
-		/* don't create login processes before at least one auth
-		   process has finished initializing */
-		return;
-	}
-
-	if (login_groups == NULL)
-		login_process_groups_create();
-
-	for (group = login_groups; group != NULL; group = group->next) {
-		if (login_group_start_missings(group) < 0) {
-			login_processes_stall();
-			return;
-		}
-	}
-}
-
-static int login_process_send_env(struct login_process *p)
-{
-	extern char **environ;
-	char **env;
-	ssize_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) != 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 bool login_process_init_group(struct login_process *p)
-{
-	p->group->refcount++;
-	p->group->processes++;
-	p->group->listening_processes++;
-
-	if (login_process_send_env(p) < 0) {
-		i_error("login: Couldn't send environment");
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-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_error("accept(inetd_login_fd) failed: %m");
-	} else {
-		net_set_nonblock(fd, TRUE);
-		fd_close_on_exec(fd, TRUE);
-
-		p = login_process_new(NULL, ++login_pid_counter, fd, TRUE);
-		p->initialized = TRUE;
-	}
-}
-
-void login_processes_init(void)
-{
-	auth_id_counter = 0;
-        login_pid_counter = 0;
-	login_groups = NULL;
-
-	child_process_set_destroy_callback(PROCESS_TYPE_LOGIN,
-					   login_process_destroyed);
-
-	if (!IS_INETD()) {
-		to = timeout_add(1000, login_processes_start_missing, NULL);
-		io_listen = NULL;
-	} else {
-		to = NULL;
-		io_listen = io_add(inetd_login_fd, IO_READ,
-				   inetd_login_accept, NULL);
-	}
-}
-
-void login_processes_deinit(void)
-{
-	if (to != NULL)
-		timeout_remove(&to);
-	if (io_listen != NULL)
-		io_remove(&io_listen);
-}
--- a/src/master/login-process.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-#ifndef LOGIN_PROCESS_H
-#define LOGIN_PROCESS_H
-
-#include "child-process.h"
-
-struct login_group {
-	struct login_group *next;
-	int refcount;
-
-	enum process_type mail_process_type;
-	struct master_settings *set;
-
-	unsigned int processes;
-	unsigned int listening_processes;
-	unsigned int wanted_processes_count;
-	time_t last_limit_warning;
-
-	/* if login_process_per_connection=yes this contains the list of
-	   processes that are in LOGIN_STATE_FULL_PRELOGINS state */
-	struct login_process *oldest_prelogin_process;
-	struct login_process *newest_prelogin_process;
-};
-
-void login_processes_destroy_all(void);
-
-void login_processes_init(void);
-void login_processes_deinit(void);
-
-#endif
--- a/src/master/mail-process.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,702 +0,0 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "array.h"
-#include "hash.h"
-#include "fd-close-on-exec.h"
-#include "eacces-error.h"
-#include "env-util.h"
-#include "base64.h"
-#include "str.h"
-#include "network.h"
-#include "mountpoint.h"
-#include "restrict-access.h"
-#include "restrict-process-size.h"
-#include "home-expand.h"
-#include "var-expand.h"
-#include "settings-parser.h"
-#include "mail-process.h"
-#include "login-process.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <grp.h>
-#include <syslog.h>
-#include <sys/stat.h>
-
-#ifdef HAVE_SYS_RESOURCE_H
-#  include <sys/resource.h>
-#endif
-
-/* Timeout chdir() completely after this many seconds */
-#define CHDIR_TIMEOUT 30
-/* Give a warning about chdir() taking a while if it took longer than this
-   many seconds to finish. */
-#define CHDIR_WARN_SECS 10
-
-struct mail_process_group {
-	/* process.type + user + remote_ip identifies this process group */
-	struct child_process process;
-	char *user;
-	struct ip_addr remote_ip;
-
-	/* processes array acts also as refcount */
-	ARRAY_DEFINE(processes, pid_t);
-};
-
-/* type+user -> struct mail_process_group */
-static struct hash_table *mail_process_groups;
-static unsigned int mail_process_count = 0;
-
-static unsigned int mail_process_group_hash(const void *p)
-{
-	const struct mail_process_group *group = p;
-
-	return str_hash(group->user) ^ group->process.type ^
-		net_ip_hash(&group->remote_ip);
-}
-
-static int mail_process_group_cmp(const void *p1, const void *p2)
-{
-	const struct mail_process_group *group1 = p1, *group2 = p2;
-	int ret;
-
-	ret = strcmp(group1->user, group2->user);
-	if (ret == 0)
-		ret = group1->process.type - group2->process.type;
-	if (ret == 0 && !net_ip_compare(&group1->remote_ip, &group2->remote_ip))
-		ret = -1;
-	return ret;
-}
-
-static struct mail_process_group *
-mail_process_group_lookup(enum process_type type, const char *user,
-			  const struct ip_addr *ip)
-{
-	struct mail_process_group lookup_group;
-
-	lookup_group.process.type = type;
-	lookup_group.user = t_strdup_noconst(user);
-	lookup_group.remote_ip = *ip;
-
-	return hash_table_lookup(mail_process_groups, &lookup_group);
-}
-
-static struct mail_process_group *
-mail_process_group_create(enum process_type type, const char *user,
-			  const struct ip_addr *ip)
-{
-	struct mail_process_group *group;
-
-	group = i_new(struct mail_process_group, 1);
-	group->process.type = type;
-	group->user = i_strdup(user);
-	group->remote_ip = *ip;
-
-	i_array_init(&group->processes, 10);
-	hash_table_insert(mail_process_groups, group, group);
-	return group;
-}
-
-static void
-mail_process_group_add(struct mail_process_group *group, pid_t pid)
-{
-	mail_process_count++;
-	array_append(&group->processes, &pid, 1);
-	child_process_add(pid, &group->process);
-}
-
-static void mail_process_group_free(struct mail_process_group *group)
-{
-	array_free(&group->processes);
-	i_free(group->user);
-	i_free(group);
-}
-
-static bool validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid,
-			     const char *user)
-{
-	if (uid == 0) {
-		i_error("user %s: Logins with UID 0 not permitted", user);
-		return FALSE;
-	}
-
-	if (set->login_uid == uid && master_uid != uid) {
-		i_error("user %s: Logins with login_user's UID %s "
-			"not permitted (see http://wiki.dovecot.org/UserIds).",
-			user, dec2str(uid));
-		return FALSE;
-	}
-
-	if (uid < (uid_t)set->first_valid_uid ||
-	    (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
-		i_error("user %s: Logins with UID %s not permitted "
-			"(see first_valid_uid in config file).",
-			user, dec2str(uid));
-		return FALSE;
-	}
-
-	if (gid < (gid_t)set->first_valid_gid ||
-	    (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
-		i_error("user %s: Logins for users with primary group ID %s "
-			"not permitted (see first_valid_gid in config file).",
-			user, dec2str(gid));
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static bool validate_chroot(struct master_settings *set, const char *dir)
-{
-	const char *const *chroot_dirs;
-
-	if (*dir == '\0')
-		return FALSE;
-
-	if (*set->valid_chroot_dirs == '\0')
-		return FALSE;
-
-	chroot_dirs = t_strsplit(set->valid_chroot_dirs, ":");
-	while (*chroot_dirs != NULL) {
-		if (**chroot_dirs != '\0' &&
-		    strncmp(dir, *chroot_dirs, strlen(*chroot_dirs)) == 0)
-			return TRUE;
-		chroot_dirs++;
-	}
-
-	return FALSE;
-}
-
-static const struct var_expand_table *
-get_var_expand_table(const char *protocol,
-		     const char *user, const char *home,
-		     const char *local_ip, const char *remote_ip,
-		     pid_t pid, uid_t uid)
-{
-#define VAR_EXPAND_HOME_IDX 4
-	static struct var_expand_table static_tab[] = {
-		{ 'u', NULL, "user" },
-		{ 'n', NULL, "username" },
-		{ 'd', NULL, "domain" },
-		{ 's', NULL, "service" },
-		{ 'h', NULL, "home" },
-		{ 'l', NULL, "lip" },
-		{ 'r', NULL, "rip" },
-		{ 'p', NULL, "pid" },
-		{ 'i', NULL, "uid" },
-		{ '\0', NULL, NULL }
-	};
-	struct var_expand_table *tab;
-
-	tab = t_malloc(sizeof(static_tab));
-	memcpy(tab, static_tab, sizeof(static_tab));
-
-	tab[0].value = user;
-	tab[1].value = user == NULL ? NULL : t_strcut(user, '@');
-	tab[2].value = user == NULL ? NULL : strchr(user, '@');
-	if (tab[2].value != NULL) tab[2].value++;
-	tab[3].value = t_str_ucase(protocol);
-	tab[VAR_EXPAND_HOME_IDX].value = home;
-	tab[5].value = local_ip;
-	tab[6].value = remote_ip;
-	tab[7].value = dec2str(pid);
-	tab[8].value = dec2str(uid);
-
-	return tab;
-}
-
-static void mail_process_set_environment(struct master_settings *set,
-					 bool dump_capability)
-{
-	if (dump_capability) {
-		/* the only settings we need to have are what plugins to load
-		   and from where. the rest will only make the dump-capability
-		   more likely to fail. */
-		const char *const *sets;
-		unsigned int i, count;
-
-		env_put("MAIL=raw:/tmp");
-		sets = array_get(&set->all_settings, &count);
-		for (i = 0; i < count; i++) {
-			if (strncmp(sets[i], "mail_plugin", 11) == 0)
-				env_put(sets[i]);
-		}
-	} else {
-		/* we don't know all the settings, so since we can't expand all
-		   of them just let the mail process expand all of them
-		   internally. */
-		master_settings_export_to_env(set);
-	}
-}
-
-void mail_process_exec(const char *protocol, const char **args)
-{
-	const struct var_expand_table *var_expand_table;
-	struct master_settings *set;
-	const char *executable;
-
-	if (strcmp(protocol, "ext") == 0) {
-		/* external binary. section contains path for it. */
-		if (*args == NULL)
-			i_fatal("External binary parameter not given");
-		set = master_set->defaults;
-		executable = *args;
-	} else {
-		if (strcmp(protocol, "imap") == 0)
-			set = master_set->imap;
-		else if (strcmp(protocol, "pop3") == 0)
-			set = master_set->pop3;
-		else
-			i_fatal("Unknown protocol: '%s'", protocol);
-		executable = set->mail_executable;
-		args = NULL;
-	}
-
-	var_expand_table =
-		get_var_expand_table(protocol, getenv("USER"), getenv("HOME"),
-				     getenv("TCPLOCALIP"),
-				     getenv("TCPREMOTEIP"),
-				     getpid(), geteuid());
-
-	/* set up logging */
-	env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
-	if (*set->log_path == '\0')
-		env_put("USE_SYSLOG=1");
-	else
-		env_put(t_strconcat("LOGFILE=", set->log_path, NULL));
-	if (*set->info_log_path != '\0')
-		env_put(t_strconcat("INFOLOGFILE=", set->info_log_path, NULL));
-	if (*set->mail_log_prefix != '\0') {
-		string_t *str = t_str_new(256);
-
-		str_append(str, "LOG_PREFIX=");
-		var_expand(str, set->mail_log_prefix, var_expand_table);
-		env_put(str_c(str));
-	}
-
-	mail_process_set_environment(set, FALSE);
-	if (args == NULL)
-		client_process_exec(executable, "");
-	else
-		client_process_exec_argv(executable, args);
-
-	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
-}
-
-enum master_login_status
-create_mail_process(enum process_type process_type, struct master_settings *set,
-		    const struct mail_login_request *request,
-		    const char *user, const char *const *args,
-		    const unsigned char *data, bool dump_capability,
-		    pid_t *pid_r)
-{
-	const struct var_expand_table *var_expand_table;
-	const char *p, *addr, *chroot_dir, *home_dir, *full_home_dir;
-	const char *system_groups_user, *master_user, *key;
-	struct mail_process_group *process_group;
-	struct restrict_access_settings rset;
-	char title[1024];
-	struct log_io *log;
-	string_t *str, *expanded_vars;
-	pid_t pid;
-	uid_t uid;
-	gid_t gid;
-	ARRAY_DEFINE(extra_args, const char *);
-	unsigned int i, len, count, left, process_count, throttle;
-	int ret, log_fd, nice_value, chdir_errno;
-	bool home_given;
-
-	i_assert(process_type == PROCESS_TYPE_IMAP ||
-		 process_type == PROCESS_TYPE_POP3);
-
-	if (mail_process_count == set->max_mail_processes) {
-		i_error("Maximum number of mail processes exceeded "
-			"(see max_mail_processes setting)");
-		return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-	}
-
-	t_array_init(&extra_args, 16);
-	home_dir = chroot_dir = system_groups_user = "";
-	master_user = NULL;
-	uid = (uid_t)-1; gid = (gid_t)-1; nice_value = 0;
-	home_given = FALSE;
-	for (; *args != NULL; args++) {
-		if (strncmp(*args, "home=", 5) == 0) {
-			home_dir = *args + 5;
-			home_given = TRUE;
-		} else if (strncmp(*args, "mail=", 5) == 0) {
-			const char *arg;
-
-			arg = t_strconcat("mail_location=", *args + 5, NULL);
-			array_append(&extra_args, &arg, 1);
-		} else if (strncmp(*args, "chroot=", 7) == 0)
-			chroot_dir = *args + 7;
-		else if (strncmp(*args, "nice=", 5) == 0)
-			nice_value = atoi(*args + 5);
-		else if (strncmp(*args, "system_groups_user=", 19) == 0)
-			system_groups_user = *args + 19;
-		else if (strncmp(*args, "uid=", 4) == 0) {
-			if (uid != (uid_t)-1) {
-				i_error("uid specified multiple times for %s",
-					user);
-				return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-			}
-			uid = (uid_t)strtoul(*args + 4, NULL, 10);
-		} else if (strncmp(*args, "gid=", 4) == 0) {
-			gid = (gid_t)strtoul(*args + 4, NULL, 10);
-		} else if (strncmp(*args, "master_user=", 12) == 0) {
-			const char *arg = *args;
-
-			master_user = arg + 12;
-			array_append(&extra_args, &arg, 1);
-		} else {
-			const char *arg = *args;
-			array_append(&extra_args, &arg, 1);
-		}
-	}
-
-	/* check process limit for this user, but not if this is a master
-	   user login. */
-	process_group = dump_capability ? NULL :
-		mail_process_group_lookup(process_type, user,
-					  &request->remote_ip);
-	process_count = process_group == NULL ? 0 :
-		array_count(&process_group->processes);
-	if (process_count >= set->mail_max_userip_connections &&
-	    set->mail_max_userip_connections != 0 &&
-	    master_user == NULL)
-		return MASTER_LOGIN_STATUS_MAX_CONNECTIONS;
-
-	/* if uid/gid wasn't returned, use the defaults */
-	if (uid == (uid_t)-1) {
-		uid = set->mail_uid_t;
-		if (uid == (uid_t)-1) {
-			i_error("User %s is missing UID (see mail_uid setting)",
-				user);
-			return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-		}
-	}
-	if (gid == (gid_t)-1) {
-		gid = set->mail_gid_t;
-		if (gid == (gid_t)-1) {
-			i_error("User %s is missing GID (see mail_gid setting)",
-				user);
-			return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-		}
-	}
-
-	if (*chroot_dir == '\0' && *set->valid_chroot_dirs != '\0' &&
-	    (p = strstr(home_dir, "/./")) != NULL) {
-		/* wu-ftpd like <chroot>/./<home> - check only if there's even
-		   a possibility of using them (non-empty valid_chroot_dirs)*/
-		chroot_dir = t_strdup_until(home_dir, p);
-		home_dir = p + 2;
-	} else if (*chroot_dir != '\0' && *home_dir != '/') {
-		/* home directories should never be relative, but force this
-		   with chroots. */
-		home_dir = t_strconcat("/", home_dir, NULL);
-	}
-
-	if (!dump_capability) {
-		if (!validate_uid_gid(set, uid, gid, user))
-			return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-	}
-
-	if (*chroot_dir != '\0') {
-		if (!validate_chroot(set, chroot_dir)) {
-			i_error("Invalid chroot directory '%s' (user %s) "
-				"(see valid_chroot_dirs setting)",
-				chroot_dir, user);
-			return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-		}
-	} else if (*set->mail_chroot != '\0') {
-		/* mail_chroot setting's value doesn't need to be in
-		   valid_chroot_dirs. */
-		chroot_dir = set->mail_chroot;
-	}
-	if (*chroot_dir != '\0' && set->mail_drop_priv_before_exec) {
-		i_error("Can't chroot to directory '%s' (user %s) "
-			"with mail_drop_priv_before_exec=yes",
-			chroot_dir, user);
-		return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-	}
-	len = strlen(chroot_dir);
-	if (len > 2 && strcmp(chroot_dir + len - 2, "/.") == 0 &&
-	    strncmp(home_dir, chroot_dir, len - 2) == 0) {
-		/* strip chroot dir from home dir */
-		home_dir += len - 2;
-	}
-
-	if (!dump_capability) {
-		throttle = set->mail_debug ? 0 :
-			set->mail_log_max_lines_per_sec;
-		log_fd = log_create_pipe(&log, throttle);
-		if (log_fd == -1)
-			return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-	} else {
-		log = NULL;
-		log_fd = dup(STDERR_FILENO);
-		if (log_fd == -1) {
-			i_error("dup() failed: %m");
-			return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-		}
-		fd_close_on_exec(log_fd, TRUE);
-	}
-
-	pid = fork();
-	if (pid < 0) {
-		i_error("fork() failed: %m");
-		(void)close(log_fd);
-		return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-	}
-
-	var_expand_table =
-		get_var_expand_table(process_names[process_type],
-				     user, home_given ? home_dir : NULL,
-				     net_ip2addr(&request->local_ip),
-				     net_ip2addr(&request->remote_ip),
-				     pid != 0 ? pid : getpid(), uid);
-	str = t_str_new(128);
-
-	if (pid != 0) {
-		/* master */
-		var_expand(str, set->mail_log_prefix, var_expand_table);
-
-		if (!dump_capability) {
-			log_set_prefix(log, str_c(str));
-			log_set_pid(log, pid);
-			if (process_group == NULL) {
-				process_group = mail_process_group_create(
-							process_type, user,
-							&request->remote_ip);
-			}
-			mail_process_group_add(process_group, pid);
-		}
-		(void)close(log_fd);
-		*pid_r = pid;
-		return MASTER_LOGIN_STATUS_OK;
-	}
-
-#ifdef HAVE_SETPRIORITY
-	if (nice_value != 0) {
-		if (setpriority(PRIO_PROCESS, 0, nice_value) < 0)
-			i_error("setpriority(%d) failed: %m", nice_value);
-	}
-#endif
-
-	if (!dump_capability) {
-		str_append(str, "master-");
-		var_expand(str, set->mail_log_prefix, var_expand_table);
-		log_set_prefix(log, str_c(str));
-	}
-
-	child_process_init_env(set);
-
-	/* setup environment - set the most important environment first
-	   (paranoia about filling up environment without noticing) */
-	restrict_access_init(&rset);
-	rset.uid = uid;
-	rset.gid = gid;
-	rset.privileged_gid = set->mail_priv_gid_t;
-	rset.extra_groups = set->mail_access_groups;
-	rset.system_groups_user = system_groups_user;
-	rset.first_valid_gid = set->first_valid_gid;
-	rset.last_valid_gid = set->last_valid_gid;
-	rset.chroot_dir = dump_capability ? NULL : chroot_dir;
-	restrict_access_set_env(&rset);
-
-	restrict_process_size(set->mail_process_size, (unsigned int)-1);
-
-	if (dump_capability)
-		env_put("DUMP_CAPABILITY=1");
-
-	if ((*home_dir == '\0' && *chroot_dir == '\0') || dump_capability) {
-		full_home_dir = "";
-		ret = -1;
-	} else {
-		full_home_dir = *chroot_dir == '\0' ? home_dir :
-			t_strconcat(chroot_dir, home_dir, NULL);
-		/* NOTE: if home directory is NFS-mounted, we might not
-		   have access to it as root. Change the effective UID and GID
-		   temporarily to make it work. */
-		if (uid != master_uid) {
-			if (setegid(gid) < 0)
-				i_fatal("setegid(%s) failed: %m", dec2str(gid));
-			if (seteuid(uid) < 0)
-				i_fatal("seteuid(%s) failed: %m", dec2str(uid));
-		}
-
-		alarm(CHDIR_TIMEOUT);
-		ret = chdir(full_home_dir);
-		chdir_errno = errno;
-		if ((left = alarm(0)) < CHDIR_TIMEOUT - CHDIR_WARN_SECS) {
-			i_warning("chdir(%s) blocked for %u secs",
-				  full_home_dir, CHDIR_TIMEOUT - left);
-		}
-
-		/* Change UID back. No need to change GID back, it doesn't
-		   really matter. */
-		if (uid != master_uid && seteuid(master_uid) < 0)
-			i_fatal("seteuid(%s) failed: %m", dec2str(master_uid));
-
-		/* If user's home directory doesn't exist and we're not
-		   trying to chroot anywhere, fallback to /tmp as the mails
-		   could be stored elsewhere. The ENOTDIR check is mostly for
-		   /dev/null home directory. */
-		if (ret < 0 && (*chroot_dir != '\0' ||
-				!(ENOTFOUND(chdir_errno) ||
-				  chdir_errno == EINTR))) {
-			errno = chdir_errno;
-			if (errno != EACCES) {
-				i_fatal("chdir(%s) failed with uid %s: %m",
-					full_home_dir, dec2str(uid));
-			} else {
-				i_fatal("%s", eacces_error_get("chdir",
-							       full_home_dir));
-			}
-		}
-	}
-	if (ret < 0) {
-		/* We still have to change to some directory where we have
-		   rx-access. /tmp should exist everywhere. */
-		if (chdir("/tmp") < 0)
-			i_fatal("chdir(/tmp) failed: %m");
-	}
-
-	mail_process_set_environment(set, dump_capability);
-
-	/* extra args. uppercase key value. */
-	expanded_vars = t_str_new(128);
-	str_append(expanded_vars, "VARS_EXPANDED=");
-	args = array_get(&extra_args, &count);
-	for (i = 0; i < count; i++) {
-		if (*args[i] == '=') {
-			/* Should be caught by dovecot-auth already */
-			i_fatal("Userdb returned data with empty key (%s)",
-				args[i]);
-		}
-		p = strchr(args[i], '=');
-		if (p == NULL) {
-			/* boolean */
-			key = args[i];
-			env_put(t_strconcat(t_str_ucase(key), "=1", NULL));
-
-		} else {
-			/* key=value */
-			key = t_strdup_until(args[i], p);
-			env_put(t_strconcat(t_str_ucase(key), p, NULL));
-		}
-		str_append(expanded_vars, key);
-		str_append_c(expanded_vars, ' ');
-	}
-	env_put(str_c(expanded_vars));
-
-	env_put("LOGGED_IN=1");
-	if (*home_dir != '\0')
-		env_put(t_strconcat("HOME=", home_dir, NULL));
-	env_put(t_strconcat("USER=", user, NULL));
-
-	addr = net_ip2addr(&request->remote_ip);
-	env_put(t_strconcat("IP=", addr, NULL));
-	env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&request->local_ip), NULL));
-
-	i_assert(request->cmd_tag_size <= request->data_size);
-	if (request->cmd_tag_size > 0) {
-		env_put(t_strconcat("IMAPLOGINTAG=",
-			t_strndup(data, request->cmd_tag_size), NULL));
-	}
-
-	if (request->data_size > request->cmd_tag_size) {
-		str_truncate(str, 0);
-		str_append(str, "CLIENT_INPUT=");
-		base64_encode(data + request->cmd_tag_size,
-			      request->data_size - request->cmd_tag_size, str);
-		env_put(str_c(str));
-	}
-
-	if (!set->verbose_proctitle)
-		title[0] = '\0';
-	else {
-		if (addr == NULL)
-			addr = "??";
-
-		i_snprintf(title, sizeof(title), "[%s %s]", user, addr);
-	}
-
-	/* make sure we don't leak syslog fd. try to do it as late as possible,
-	   but also before dup2()s in case syslog fd is one of them. */
-	closelog();
-
-	/* move the client socket into stdin and stdout fds, log to stderr */
-	if (dup2(request->fd, 0) < 0)
-		i_fatal("dup2(stdin) failed: %m");
-	if (dup2(request->fd, 1) < 0)
-		i_fatal("dup2(stdout) failed: %m");
-	if (dup2(log_fd, 2) < 0)
-		i_fatal("dup2(stderr) failed: %m");
-
-	for (i = 0; i < 3; i++)
-		fd_close_on_exec(i, FALSE);
-
-	if (set->mail_drop_priv_before_exec) {
-		restrict_access_by_env(home_dir, TRUE);
-		/* privileged GID is now only in saved-GID. if we want to
-		   preserve it across exec, it needs to be temporarily
-		   in effective gid. unfortunately this also causes kernel
-		   to think we're a setgid-program. */
-		restrict_access_use_priv_gid();
-	}
-
-	client_process_exec(set->mail_executable, title);
-	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
-		       set->mail_executable);
-
-	/* not reached */
-	return MASTER_LOGIN_STATUS_INTERNAL_ERROR;
-}
-
-static void
-mail_process_destroyed(struct child_process *process,
-		       pid_t pid, bool abnormal_exit ATTR_UNUSED)
-{
-	struct mail_process_group *group = (struct mail_process_group *)process;
-	const pid_t *pids;
-	unsigned int i, count;
-
-	pids = array_get(&group->processes, &count);
-	if (count == 1) {
-		/* last process in this group */
-		i_assert(pids[0] == pid);
-		hash_table_remove(mail_process_groups, group);
-		mail_process_group_free(group);
-	} else {
-		for (i = 0; i < count; i++) {
-			if (pids[i] == pid)
-				break;
-		}
-		i_assert(i != count);
-		array_delete(&group->processes, i, 1);
-	}
-
-	mail_process_count--;
-}
-
-void mail_processes_init(void)
-{
-	mail_process_groups = hash_table_create(default_pool, default_pool, 0,
-						mail_process_group_hash,
-						mail_process_group_cmp);
-
-	child_process_set_destroy_callback(PROCESS_TYPE_IMAP,
-					   mail_process_destroyed);
-	child_process_set_destroy_callback(PROCESS_TYPE_POP3,
-					   mail_process_destroyed);
-}
-
-void mail_processes_deinit(void)
-{
-	/* we may still end up in mail_process_destroyed(), so don't free
-	   anything. This deinit code needs a redesign.. */
-}
--- a/src/master/mail-process.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-#ifndef MAIL_PROCESS_H
-#define MAIL_PROCESS_H
-
-#include "master-login-interface.h"
-#include "child-process.h"
-
-struct mail_login_request {
-	int fd;
-	unsigned int cmd_tag_size;
-	unsigned int data_size;
-	struct ip_addr local_ip, remote_ip;
-};
-
-struct login_group;
-struct auth_master_reply;
-
-void mail_process_exec(const char *protocol, const char **args) ATTR_NORETURN;
-
-enum master_login_status
-create_mail_process(enum process_type process_type, struct master_settings *set,
-		    const struct mail_login_request *request,
-		    const char *user, const char *const *args,
-		    const unsigned char *data, bool dump_capability,
-		    pid_t *pid_r);
-
-void mail_processes_init(void);
-void mail_processes_deinit(void);
-
-#endif
--- a/src/master/main.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/master/main.c	Thu Apr 23 19:53:44 2009 -0400
@@ -1,249 +1,67 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
-#include "array.h"
-#include "ioloop.h"
 #include "lib-signals.h"
-#include "network.h"
-#include "env-util.h"
 #include "fd-close-on-exec.h"
 #include "write-full.h"
+#include "env-util.h"
+#include "hostpid.h"
 #include "restrict-process-size.h"
-
-#include "askpass.h"
-#include "auth-process.h"
+#include "master-service.h"
+#include "master-service-settings.h"
 #include "capabilities.h"
-#include "dict-process.h"
-#include "login-process.h"
-#include "mail-process.h"
-#include "syslog-util.h"
-#include "listener.h"
-#include "ssl-init.h"
-#include "log.h"
-#include "sysinfo-get.h"
-#include "hostpid.h"
+#include "service.h"
+#include "service-listen.h"
+#include "service-monitor.h"
+#include "service-log.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <syslog.h>
 #include <sys/stat.h>
 
-#define FATAL_FILENAME "master-fatal.lastlog"
-
-static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf";
-static pid_t master_original_pid;
-
-struct ioloop *ioloop;
-int null_fd = -1, inetd_login_fd;
+struct master_service *master_service;
 uid_t master_uid;
-char program_path[PATH_MAX];
-char ssl_manual_key_password[100];
-const char *env_tz;
-bool auth_success_written;
+gid_t master_gid;
 bool core_dumps_disabled;
-#ifdef DEBUG
-bool gdb;
-#endif
+int null_fd;
+
+static char *pidfile_path;
+static struct service_list *services;
 
-static void ATTR_NORETURN ATTR_FORMAT(3, 0)
-master_fatal_callback(enum log_type type, int status,
-		      const char *format, va_list args)
+static const char *child_process_env[3]; /* @UNSAFE */
+
+void process_exec(const char *cmd, const char *extra_args[])
 {
-	const struct master_settings *set = master_set->defaults;
-	const char *path, *str;
-	va_list args2;
-	int fd;
+	const char *executable, *p, **argv;
 
-	/* if we already forked a child process, this isn't fatal for the
-	   main process and there's no need to write the fatal file. */
-	if (getpid() == master_original_pid) {
-		/* write the error message to a file */
-		path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL);
-		fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
-		if (fd != -1) {
-			VA_COPY(args2, args);
-			str = t_strdup_vprintf(format, args2);
-			write_full(fd, str, strlen(str));
-			(void)close(fd);
-		}
-	}
+	argv = t_strsplit(cmd, " ");
+	executable = argv[0];
 
-	/* write it to log as well */
-	if (*set->log_path == '\0')
-		i_syslog_fatal_handler(type, status, format, args);
-	else
-		default_fatal_handler(type, status, format, args);
-}
+	if (extra_args != NULL) {
+		unsigned int count1, count2;
+		const char **new_argv;
 
-static void fatal_log_check(void)
-{
-	const struct master_settings *set = master_set->defaults;
-	const char *path;
-	char buf[1024];
-	ssize_t ret;
-	int fd;
-
-	path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL);
-	fd = open(path, O_RDONLY);
-	if (fd == -1)
-		return;
-
-	ret = read(fd, buf, sizeof(buf));
-	if (ret < 0)
-		i_error("read(%s) failed: %m", path);
-	else {
-		buf[ret] = '\0';
-		i_warning("Last died with error (see error log for more "
-			  "information): %s", buf);
+		/* @UNSAFE */
+		count1 = str_array_length(argv);
+		count2 = str_array_length(extra_args);
+		new_argv = t_new(const char *, count1 + count2 + 1);
+		memcpy(new_argv, argv, sizeof(const char *) * count1);
+		memcpy(new_argv + count1, extra_args,
+		       sizeof(const char *) * count2);
+		argv = new_argv;
 	}
 
-	close(fd);
-	if (unlink(path) < 0)
-		i_error("unlink(%s) failed: %m", path);
-}
-
-static void auth_warning_print(const struct master_server_settings *set)
-{
-	struct master_auth_settings *const *auths;
-	unsigned int count;
-	struct stat st;
-
-	auth_success_written = stat(AUTH_SUCCESS_PATH, &st) == 0;
-	auths = array_get(&set->defaults->auths, &count);
-	if (!auth_success_written && count > 0 && !auths[0]->debug &&
-	    strcmp(set->defaults->protocols, "none") != 0) {
-		i_info("If you have trouble with authentication failures,\n"
-		       "enable auth_debug setting. "
-		       "See http://wiki.dovecot.org/WhyDoesItNotWork");
-
-	}
-}
-
-static void set_logfile(struct master_settings *set)
-{
-	int facility;
-
-	if (*set->log_path == '\0') {
-		if (!syslog_facility_find(set->syslog_facility, &facility))
-			facility = LOG_MAIL;
-
-		i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
-	} else {
-		/* log to file or stderr */
-		i_set_failure_file(set->log_path, "dovecot: ");
-	}
-	i_set_fatal_handler(master_fatal_callback);
-
-	if (*set->info_log_path != '\0')
-		i_set_info_file(set->info_log_path);
-
-	i_set_failure_timestamp_format(set->log_timestamp);
-}
-
-static void settings_reload(void)
-{
-	/* this old_set wrapping works because master settings are
-	   alternatingly read using two different pools */
-	struct master_server_settings *set, *old_set = master_set;
-
-	i_warning("SIGHUP received - reloading configuration");
-
-	/* restart auth and login processes */
-        login_processes_destroy_all();
-        auth_processes_destroy_all();
-        dict_processes_kill();
-
-	/* see if hostname changed */
-	hostpid_init();
-
-	if (master_settings_read(configfile, &set) < 0 ||
-	    !master_settings_check(set, FALSE, FALSE))
-		i_warning("Invalid configuration, keeping old one");
-	else {
-		if (!IS_INETD())
-			listeners_open_fds(old_set, TRUE);
-                set_logfile(set->defaults);
-	}
-}
+	/* hide the path, it's ugly */
+	p = strrchr(argv[0], '/');
+	if (p != NULL) argv[0] = p+1;
 
-static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
-{
-	/* warn about being killed because of some signal, except SIGINT (^C)
-	   which is too common at least while testing :) */
-	if (si->si_signo != SIGINT) {
-		i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
-			  si->si_signo, dec2str(si->si_pid),
-			  dec2str(si->si_uid),
-			  lib_signal_code_to_str(si->si_signo, si->si_code));
-	}
-	io_loop_stop(ioloop);
-}
-
-static void sig_reload_settings(const siginfo_t *si ATTR_UNUSED,
-				void *context ATTR_UNUSED)
-{
-	settings_reload();
-}
-
-static void sig_reopen_logs(const siginfo_t *si ATTR_UNUSED,
-			    void *context ATTR_UNUSED)
-{
-	set_logfile(master_set->defaults);
-}
-
-static bool have_stderr_set(struct master_settings *set)
-{
-	if (*set->log_path != '\0' &&
-	    strcmp(set->log_path, "/dev/stderr") == 0)
-		return TRUE;
-
-	if (*set->info_log_path != '\0' &&
-	    strcmp(set->info_log_path, "/dev/stderr") == 0)
-		return TRUE;
-
-	return FALSE;
-}
+	/* prefix with dovecot/ */
+	argv[0] = t_strconcat(PACKAGE"/", argv[0], NULL);
 
-static bool have_stderr(struct master_server_settings *server)
-{
-	if (server->imap != NULL && have_stderr_set(server->imap))
-		return TRUE;
-	if (server->pop3 != NULL && have_stderr_set(server->pop3))
-		return TRUE;
-	return FALSE;
-}
-
-static void open_null_fd(void)
-{
-	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);
-}
-
-static void open_fds(void)
-{
-	/* make sure all fds between 0..3 are used. */
-	while (null_fd < 4) {
-		null_fd = dup(null_fd);
-		if (null_fd == -1)
-			i_fatal("dup(null_fd) failed: %m");
-		fd_close_on_exec(null_fd, TRUE);
-	}
-
-	if (!IS_INETD()) {
-		T_BEGIN {
-			listeners_open_fds(NULL, FALSE);
-		} T_END;
-	}
-
-	/* close stdin and stdout. */
-	if (dup2(null_fd, 0) < 0)
-		i_fatal("dup2(0) failed: %m");
-	if (dup2(null_fd, 1) < 0)
-		i_fatal("dup2(1) failed: %m");
+	execv(executable, (char **)argv);
+	i_fatal_status(FATAL_EXEC, "execvp(%s) failed: %m", executable);
 }
 
 static void create_pid_file(const char *path)
@@ -261,42 +79,58 @@
 	(void)close(fd);
 }
 
-static void pid_file_check_running(const struct master_settings *set)
+static void
+sig_settings_reload(const siginfo_t *si ATTR_UNUSED,
+		    void *context ATTR_UNUSED)
 {
-	const char *path;
-	char buf[32];
-	int fd;
-	ssize_t ret;
+	struct master_settings *new_set;
+	struct service_list *new_services;
+	const char *error;
+	pool_t pool;
+
+	/* see if hostname changed */
+	hostpid_init();
 
-	path = t_strconcat(set->base_dir, "/master.pid", NULL);
-	fd = open(path, O_RDONLY);
-	if (fd == -1) {
-		if (errno == ENOENT)
-			return;
-		i_fatal("open(%s) failed: %m", path);
+#if 0 // FIXME
+	/* FIXME: this loses process structures for existing processes.
+	   figure out something. */
+	new_set = master_settings_read(pool, config_binary, config_path);
+	new_services = new_set == NULL ? NULL :
+		services_create(new_set, child_process_env, &error);
+#endif
+	if (new_services == NULL) {
+		/* new configuration is invalid, keep the old */
+		i_error("Config reload failed: %s", error);
+		return;
 	}
 
-	ret = read(fd, buf, sizeof(buf));
-	if (ret <= 0) {
-		if (ret == 0)
-			i_error("Empty PID file in %s, overriding", path);
-		else
-			i_fatal("read(%s) failed: %m", path);
-	} else {
-		pid_t pid;
+	/* switch to new configuration. */
+	(void)services_listen_using(new_services, services);
+	services_destroy(services);
+	services = new_services;
+
+        services_monitor_start(services);
+}
+
+static void
+sig_log_reopen(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
+{
+        service_signal(services->log, SIGUSR1);
+}
 
-		if (buf[ret-1] == '\n')
-			ret--;
-		buf[ret] = '\0';
-		pid = atoi(buf);
-		if (pid == getpid() || (kill(pid, 0) < 0 && errno == ESRCH)) {
-			/* doesn't exist */
-		} else {
-			i_fatal("Dovecot is already running with PID %s "
-				"(read from %s)", buf, path);
-		}
-	}
-	(void)close(fd);
+static void
+sig_reap_children(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
+{
+	services_monitor_reap_children(services);
+}
+
+static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
+{
+	i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
+		  si->si_signo, dec2str(si->si_pid),
+		  dec2str(si->si_uid),
+		  lib_signal_code_to_str(si->si_signo, si->si_code));
+	master_service_stop(master_service);
 }
 
 static void main_log_startup(void)
@@ -312,20 +146,13 @@
 		i_info(STARTUP_STRING);
 }
 
-static void main_init(bool log_error)
+static void main_init(const struct master_settings *set, bool log_error)
 {
 	drop_capabilities();
 
 	/* deny file access from everyone else except owner */
         (void)umask(0077);
 
-	set_logfile(master_set->defaults);
-	/* close stderr unless we're logging into /dev/stderr. */
-	if (!have_stderr(master_set)) {
-		if (dup2(null_fd, 2) < 0)
-			i_fatal("dup2(2) failed: %m");
-	}
-
 	if (log_error) {
 		printf("Writing to error logs and killing myself..\n");
 		i_info("This is Dovecot's info log");
@@ -336,51 +163,44 @@
 	main_log_startup();
 
 	lib_signals_init();
-        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
-        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
         lib_signals_ignore(SIGPIPE, TRUE);
         lib_signals_ignore(SIGALRM, FALSE);
-        lib_signals_set_handler(SIGHUP, TRUE, sig_reload_settings, NULL);
-        lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL);
+        lib_signals_set_handler(SIGHUP, TRUE, sig_settings_reload, NULL);
+        lib_signals_set_handler(SIGUSR1, TRUE, sig_log_reopen, NULL);
+        lib_signals_set_handler(SIGCHLD, TRUE, sig_reap_children, NULL);
+        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
+        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
 
-	child_processes_init();
-	log_init();
-	ssl_init();
-	dict_processes_init();
-	auth_processes_init();
-	login_processes_init();
-	mail_processes_init();
+        pidfile_path = i_strconcat(set->base_dir, "/master.pid", NULL);
+	create_pid_file(pidfile_path);
 
-	create_pid_file(t_strconcat(master_set->defaults->base_dir,
-				    "/master.pid", NULL));
+	services_monitor_start(services);
 }
 
 static void main_deinit(void)
 {
-	(void)unlink(t_strconcat(master_set->defaults->base_dir,
-				 "/master.pid", NULL));
-
-	login_processes_destroy_all();
-
-	mail_processes_deinit();
-	login_processes_deinit();
-	auth_processes_deinit();
-	dict_processes_deinit();
-	ssl_deinit();
+	if (unlink(pidfile_path) < 0)
+		i_error("unlink(%s) failed: %m", pidfile_path);
+	i_free(pidfile_path);
 
-	listeners_close_fds();
-
-	if (close(null_fd) < 0)
-		i_error("close(null_fd) failed: %m");
-
-	log_deinit();
-	/* log_deinit() may still want to look up child processes */
-	child_processes_deinit();
-	lib_signals_deinit();
-	closelog();
+	services_destroy(services);
 }
 
-static void daemonize(struct master_settings *set)
+static const char *get_full_config_path(struct service_list *list)
+{
+	const char *path;
+	char cwd[PATH_MAX];
+
+	path = master_service_get_config_path(master_service);
+	if (*path == '/')
+		return path;
+
+	if (getcwd(cwd, sizeof(cwd)) == NULL)
+		i_fatal("getcwd() failed: %m");
+	return p_strconcat(list->pool, cwd, "/", path, NULL);
+}
+
+static void daemonize(void)
 {
 	pid_t pid;
 
@@ -394,21 +214,20 @@
 	if (setsid() < 0)
 		i_fatal("setsid() failed: %m");
 
-	if (chdir(set->base_dir) < 0)
-		i_fatal("chdir(%s) failed: %m", set->base_dir);
+	/* update my_pid */
+	hostpid_init();
 }
 
 static void print_help(void)
 {
 	printf(
 "Usage: dovecot [-F] [-c <config file>] [-p] [-n] [-a]\n"
-"       [--version] [--build-options] [--exec-mail <protocol> [<args>]]\n");
+"       [-cb <config binary path>] [--version] [--build-options]\n");
 }
 
 static void print_build_options(void)
 {
-	static const char *build_options =
-		"Build options:"
+	printf("Build options:"
 #ifdef IOLOOP_EPOLL
 		" ioloop=epoll"
 #endif
@@ -439,8 +258,7 @@
 #ifdef HAVE_OPENSSL
 		" openssl"
 #endif
-	"\nMail storages: "MAIL_STORAGES"\n"
-	"SQL drivers:"
+	"\nSQL drivers:"
 #ifdef BUILD_MYSQL
 		" mysql"
 #endif
@@ -472,9 +290,6 @@
 #ifdef PASSDB_SHADOW 
 		" shadow"
 #endif
-#ifdef PASSDB_SIA
-		" sia"
-#endif
 #ifdef PASSDB_SQL 
 		" sql"
 #endif
@@ -482,8 +297,8 @@
 		" vpopmail"
 #endif
 	"\nUserdb:"
-#ifdef USERDB_NSS
-		" nss"
+#ifdef USERDB_CHECKPASSWORD
+		" checkpassword"
 #endif
 #ifdef USERDB_LDAP
 		" ldap"
@@ -491,12 +306,12 @@
 #ifdef USERDB_PASSWD
 		" passwd"
 #endif
+#ifdef USERDB_PREFETCH
+		" prefetch"
+#endif
 #ifdef USERDB_PASSWD_FILE
 		" passwd-file"
 #endif
-#ifdef USERDB_PREFETCH
-		" prefetch"
-#endif
 #ifdef USERDB_SQL 
 		" sql"
 #endif
@@ -506,160 +321,139 @@
 #ifdef USERDB_VPOPMAIL
 		" vpopmail"
 #endif
-	"\n";
-	puts(build_options);
+	"\n");
 }
 
 int main(int argc, char *argv[])
 {
-	/* parse arguments */
-	struct master_server_settings *set;
-	const char *exec_protocol = NULL, **exec_args = NULL, *user, *home;
+	static const struct setting_parser_info *set_roots[] = {
+		&master_setting_parser_info,
+		NULL
+	};
+	struct master_settings *set;
+	unsigned int child_process_env_idx = 0;
+	const char *getopt_str, *error, *env_tz;
+	void **sets;
 	bool foreground = FALSE, ask_key_pass = FALSE, log_error = FALSE;
-	bool dump_config = FALSE, dump_config_nondefaults = FALSE;
-	int i;
+	int c;
 
 #ifdef DEBUG
-	gdb = getenv("GDB") != NULL;
+	if (getenv("GDB") == NULL)
+		fd_debug_verify_leaks(3, 1024);
+	else
+		child_process_env[child_process_env_idx++] = "GDB=1";
 #endif
-	lib_init();
+	master_service = master_service_init("master", 0, argc, argv);
 
 	master_uid = geteuid();
-        inetd_login_fd = -1;
-	for (i = 1; i < argc; i++) {
-		if (strcmp(argv[i], "-F") == 0) {
-			/* foreground */
+	master_gid = getegid();
+
+	getopt_str = t_strconcat("Fp", master_service_getopt_string(), NULL);
+	while ((c = getopt(argc, argv, getopt_str)) > 0) {
+		switch (c) {
+		case 'F':
 			foreground = TRUE;
-		} else if (strcmp(argv[i], "-a") == 0) {
-			dump_config = TRUE;
-		} else if (strcmp(argv[i], "-c") == 0) {
-			/* config file */
-			i++;
-			if (i == argc) i_fatal("Missing config file argument");
-			configfile = argv[i];
-		} else if (strcmp(argv[i], "-n") == 0) {
-			dump_config_nondefaults = dump_config = TRUE;
-		} else if (strcmp(argv[i], "-p") == 0) {
+			break;
+		case 'p':
 			/* Ask SSL private key password */
 			ask_key_pass = TRUE;
-		} else if (strcmp(argv[i], "--exec-mail") == 0) {
-			/* <protocol> [<args>]
-			   read configuration and execute mail process */
-			i++;
-			if (i == argc) i_fatal("Missing protocol argument");
-			exec_protocol = argv[i];
-			exec_args = (const char **)&argv[i+1];
 			break;
-		} else if (strcmp(argv[i], "--version") == 0) {
+		default:
+			if (!master_service_parse_option(master_service,
+							 c, optarg)) {
+				print_help();
+				exit(FATAL_DEFAULT);
+			}
+			break;
+		}
+	}
+
+	if (optind < argc) {
+		if (strcmp(argv[optind], "--version") == 0) {
 			printf("%s\n", VERSION);
 			return 0;
-		} else if (strcmp(argv[i], "--build-options") == 0) {
+		} else if (strcmp(argv[optind], "--build-options") == 0) {
 			print_build_options();
 			return 0;
-		} else if (strcmp(argv[i], "--log-error") == 0) {
+		} else if (strcmp(argv[optind], "--log-error") == 0) {
 			log_error = TRUE;
 			foreground = TRUE;
 		} else {
 			print_help();
-			i_fatal("Unknown argument: %s", argv[1]);
+			i_fatal("Unknown argument: %s", argv[optind]);
 		}
 	}
 
-	/* need to have this open before reading settings */
-	open_null_fd();
+	do {
+		null_fd = open("/dev/null", O_WRONLY);
+		if (null_fd == -1)
+			i_fatal("Can't open /dev/null: %m");
+		fd_close_on_exec(null_fd, TRUE);
+	} while (null_fd <= STDERR_FILENO);
 
-	if (getenv("DOVECOT_INETD") != NULL) {
-		/* 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;
-	}
-
-	if (dump_config) {
-
-		/* print the config file path before parsing it, so in case
-		   of errors it's still shown */
-		printf("# "VERSION": %s\n", configfile);
-	}
+	if (dup2(null_fd, STDIN_FILENO) < 0 ||
+	    dup2(null_fd, STDOUT_FILENO) < 0)
+		i_fatal("dup2(null_fd) failed: %m");
 
-	/* read and verify settings before forking */
-	T_BEGIN {
-		master_settings_init();
-		if (master_settings_read(configfile, &set) < 0)
-			i_fatal("Invalid configuration in %s", configfile);
-	} T_END;
-
-	if (!log_error && !dump_config && exec_protocol == NULL)
-		pid_file_check_running(set->defaults);
-
-	if (!master_settings_check(set, exec_protocol != NULL,
-				   dump_config || log_error))
-		i_fatal("Invalid configuration in %s", configfile);
+	if (master_service_settings_read(master_service, set_roots, NULL, FALSE,
+					 &error) < 0)
+		i_fatal("Error reading configuration: %s", error);
+	sets = master_service_settings_get_others(master_service);
+	set = sets[0];
 
-	if (dump_config) {
-		const char *info;
-
-		info = sysinfo_get(master_set->defaults->mail_location);
-		if (*info != '\0')
-			printf("# %s\n", info);
+	master_service_init_log(master_service, "dovecot: ", 0);
+	if (!log_error)
+		master_settings_do_fixes(set);
 
-		if (dump_config_nondefaults)
-			client_process_exec(DOVECOT_CONFIG_BIN_PATH" -a", "");
-		else
-			client_process_exec(DOVECOT_CONFIG_BIN_PATH" -n", "");
-		i_fatal("exec(%s) failed: %m", DOVECOT_CONFIG_BIN_PATH);
-	}
-
-	if (ask_key_pass && !log_error) T_BEGIN {
+#if 0 // FIXME
+	if (ask_key_pass) {
 		const char *prompt;
 
 		prompt = t_strdup_printf("Give the password for SSL key file "
-					 "%s: ",
-					 master_set->defaults->ssl_key_file);
+					 "%s: ", set->ssl_key_file);
 		askpass(prompt, ssl_manual_key_password,
 			sizeof(ssl_manual_key_password));
-	} T_END;
+	}
+#endif
 
 	/* save TZ environment. AIX depends on it to get the timezone
 	   correctly. */
 	env_tz = getenv("TZ");
-	user = getenv("USER");
-	home = getenv("HOME");
 
 	/* clean up the environment of everything */
 	env_clean();
 
 	/* put back the TZ */
-	if (env_tz != NULL)
-		env_put(t_strconcat("TZ=", env_tz, NULL));
+	if (env_tz != NULL) {
+		const char *env = t_strconcat("TZ=", env_tz, NULL);
 
-	if (exec_protocol != NULL) {
-		/* Put back user and home */
-		env_put(t_strconcat("USER=", user, NULL));
-		env_put(t_strconcat("HOME=", home, NULL));
-		mail_process_exec(exec_protocol, exec_args);
+		env_put(env);
+		child_process_env[child_process_env_idx++] = env;
 	}
+	i_assert(child_process_env_idx <
+		 sizeof(child_process_env) / sizeof(child_process_env[0]));
+	child_process_env[child_process_env_idx++] = NULL;
 
-	if (!log_error)
-		open_fds();
+	/* create service structures from settings. if there are any errors in
+	   service configuration we'll catch it here. */
+	services = services_create(set, child_process_env, &error);
+	if (services == NULL)
+		i_fatal("%s", error);
 
-	fatal_log_check();
-	auth_warning_print(master_set);
-	if (!foreground)
-		daemonize(master_set->defaults);
-	master_original_pid = getpid();
+	services->config->config_file_path = get_full_config_path(services);
 
-	ioloop = io_loop_create();
+	/* if any listening fails, fail completely */
+	if (services_listen(services) <= 0)
+		return FATAL_DEFAULT;
 
-	main_init(log_error);
-        io_loop_run(ioloop);
-	main_deinit();
+	if (!foreground)
+		daemonize();
+	if (chdir(set->base_dir) < 0)
+		i_fatal("chdir(%s) failed: %m", set->base_dir);
 
-	master_settings_deinit();
-	io_loop_destroy(&ioloop);
-	lib_deinit();
-
+	main_init(set, log_error);
+	master_service_run(master_service, NULL);
+	main_deinit();
+	master_service_deinit(&master_service);
         return 0;
 }
--- a/src/master/master-login-interface.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#ifndef MASTER_LOGIN_INTERFACE_H
-#define MASTER_LOGIN_INTERFACE_H
-
-#include "network.h"
-
-#define LOGIN_MASTER_SOCKET_FD 3
-
-/* Increase the version number every time master_login_request
-   (or something else) is changed. */
-#define MASTER_LOGIN_PROTOCOL_VERSION 3
-
-/* This should be kept in sync with LOGIN_MAX_INBUF_SIZE. Multiply it by two
-   to make sure there's space to transfer the command tag  */
-#define MASTER_LOGIN_MAX_DATA_SIZE (4096*2)
-
-enum master_login_state {
-	/* process is accepting new connections */
-	LOGIN_STATE_LISTENING = 0,
-	/* process isn't accepting new connections, but it'd be able to kill
-	   some connections which haven't logged in yet */
-	LOGIN_STATE_FULL_PRELOGINS,
-	/* process is handling only logged in users */
-	LOGIN_STATE_FULL_LOGINS,
-
-	LOGIN_STATE_COUNT
-};
-
-struct master_login_request {
-	uint32_t version;
-	/* if fd == -1, tag is used as master_login_state */
-	uint32_t tag;
-
-	uint32_t auth_pid;
-	uint32_t auth_id;
-	/* request follows this many bytes of client input */
-	uint16_t data_size;
-	uint16_t cmd_tag_size;
-
-	ino_t ino;
-
-	struct ip_addr local_ip, remote_ip;
-};
-
-enum master_login_status {
-	MASTER_LOGIN_STATUS_OK,
-	MASTER_LOGIN_STATUS_INTERNAL_ERROR,
-	/* user reached max. simultaneous connections */
-	MASTER_LOGIN_STATUS_MAX_CONNECTIONS
-};
-
-struct master_login_reply {
-	unsigned int tag;
-	enum master_login_status status;
-	/* PID of the post-login mail process handling this connection */
-	pid_t mail_pid;
-};
-
-#endif
--- a/src/master/master-settings.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/master/master-settings.c	Thu Apr 23 19:53:44 2009 -0400
@@ -1,134 +1,145 @@
-/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
 #include "array.h"
-#include "str.h"
+#include "env-util.h"
 #include "istream.h"
-#include "env-util.h"
-#include "fd-close-on-exec.h"
-#include "safe-mkdir.h"
 #include "mkdir-parents.h"
-#include "unlink-directory.h"
-#include "syslog-util.h"
-#include "mail-process.h"
-#include "master-login-interface.h"
 #include "settings-parser.h"
+#include "master-settings.h"
 
-#include <stdio.h>
 #include <stddef.h>
-#include <stdlib.h>
 #include <dirent.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <signal.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
-#include <pwd.h>
-#include <grp.h>
-#ifdef HAVE_SYS_RESOURCE_H
-#  include <sys/resource.h>
-#endif
 
-extern struct setting_parser_info master_auth_setting_parser_info;
-extern struct setting_parser_info master_setting_parser_info;
-extern struct setting_parser_info master_auth_socket_setting_parser_info;
+static bool master_settings_verify(void *_set, pool_t pool,
+				   const char **error_r);
+
+extern struct setting_parser_info service_setting_parser_info;
 
 #undef DEF
 #define DEF(type, name) \
-	{ type, #name, offsetof(struct master_auth_socket_unix_settings, name), NULL }
+	{ type, #name, offsetof(struct file_listener_settings, name), NULL }
 
-static struct setting_define master_auth_socket_master_setting_defines[] = {
+static struct setting_define file_listener_setting_defines[] = {
 	DEF(SET_STR, path),
+	DEF(SET_UINT, mode),
+	DEF(SET_STR, user),
+	DEF(SET_STR, group),
 
 	SETTING_DEFINE_LIST_END
 };
 
-static struct master_auth_socket_unix_settings master_auth_socket_master_default_settings = {
-	MEMBER(path) "auth-master"
+static struct file_listener_settings file_listener_default_settings = {
+	MEMBER(path) "",
+	MEMBER(mode) 0600,
+	MEMBER(user) "",
+	MEMBER(group) "",
+};
+
+static struct setting_parser_info file_listener_setting_parser_info = {
+	MEMBER(defines) file_listener_setting_defines,
+	MEMBER(defaults) &file_listener_default_settings,
+
+	MEMBER(parent) &service_setting_parser_info,
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct file_listener_settings)
 };
 
-struct setting_parser_info master_auth_socket_master_setting_parser_info = {
-	MEMBER(defines) master_auth_socket_master_setting_defines,
-	MEMBER(defaults) &master_auth_socket_master_default_settings,
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct inet_listener_settings, name), NULL }
+
+static struct setting_define inet_listener_setting_defines[] = {
+	DEF(SET_STR, address),
+	DEF(SET_UINT, port),
+
+	SETTING_DEFINE_LIST_END
+};
 
-	MEMBER(parent) &master_auth_socket_setting_parser_info,
-	MEMBER(dynamic_parsers) NULL,
+static struct inet_listener_settings inet_listener_default_settings = {
+	MEMBER(address) "*",
+	MEMBER(port) 0
+};
 
+static struct setting_parser_info inet_listener_setting_parser_info = {
+	MEMBER(defines) inet_listener_setting_defines,
+	MEMBER(defaults) &inet_listener_default_settings,
+
+	MEMBER(parent) &service_setting_parser_info,
 	MEMBER(parent_offset) (size_t)-1,
-	MEMBER(type_offset) (size_t)-1,
-	MEMBER(struct_size) sizeof(struct master_auth_socket_unix_settings)
+	MEMBER(struct_size) sizeof(struct inet_listener_settings)
 };
 
 #undef DEF
 #undef DEFLIST
 #define DEF(type, name) \
-	{ type, #name, offsetof(struct master_auth_socket_settings, name), NULL }
+	{ type, #name, offsetof(struct service_settings, name), NULL }
 #define DEFLIST(field, name, defines) \
-	{ SET_DEFLIST, name, offsetof(struct master_auth_socket_settings, field), defines }
-static struct setting_define master_auth_socket_setting_defines[] = {
+	{ SET_DEFLIST, name, offsetof(struct service_settings, field), defines }
+
+static struct setting_define service_setting_defines[] = {
+	DEF(SET_INTERNAL, master_set),
 	DEF(SET_STR, type),
-	DEFLIST(masters, "master", &master_auth_socket_master_setting_parser_info),
+	DEF(SET_STR, executable),
+	DEF(SET_STR, user),
+	DEF(SET_STR, group),
+	DEF(SET_STR, privileged_group),
+	DEF(SET_STR, extra_groups),
+	DEF(SET_STR, chroot),
+	DEF(SET_STR, auth_dest_service),
+
+	DEF(SET_BOOL, drop_priv_before_exec),
+
+	DEF(SET_UINT, process_limit),
+	DEF(SET_UINT, client_limit),
+	DEF(SET_UINT, vsz_limit),
+
+	DEFLIST(unix_listeners, "unix_listener",
+		&file_listener_setting_parser_info),
+	DEFLIST(fifo_listeners, "fifo_listener",
+		&file_listener_setting_parser_info),
+	DEFLIST(inet_listeners, "inet_listener",
+		&inet_listener_setting_parser_info),
 
 	SETTING_DEFINE_LIST_END
 };
 
-struct setting_parser_info master_auth_socket_setting_parser_info = {
-	MEMBER(defines) master_auth_socket_setting_defines,
-	MEMBER(defaults) NULL,
+static struct service_settings service_default_settings = {
+	MEMBER(master_set) NULL,
 
-	MEMBER(parent) &master_auth_setting_parser_info,
-	MEMBER(dynamic_parsers) NULL,
+	MEMBER(type) "",
+	MEMBER(executable) "",
+	MEMBER(user) "",
+	MEMBER(group) "",
+	MEMBER(privileged_group) "",
+	MEMBER(extra_groups) "",
+	MEMBER(chroot) "",
+	MEMBER(auth_dest_service) "",
 
-	MEMBER(parent_offset) (size_t)-1,
-	MEMBER(type_offset) offsetof(struct master_auth_socket_settings, type),
-	MEMBER(struct_size) sizeof(struct master_auth_socket_settings)
+	MEMBER(drop_priv_before_exec) FALSE,
+
+	MEMBER(process_limit) (unsigned int)-1,
+	MEMBER(client_limit) 0,
+	MEMBER(vsz_limit) 256,
+
+	MEMBER(unix_listeners) ARRAY_INIT,
+	MEMBER(fifo_listeners) ARRAY_INIT,
+	MEMBER(inet_listeners) ARRAY_INIT
 };
 
-#undef DEF
-#undef DEFLIST
-#define DEF(type, name) \
-	{ type, #name, offsetof(struct master_auth_settings, name), NULL }
-#define DEFLIST(field, name, defines) \
-	{ SET_DEFLIST, name, offsetof(struct master_auth_settings, field), defines }
-
-static struct setting_define master_auth_setting_defines[] = {
-	DEF(SET_STR, name),
-	DEF(SET_STR, executable),
-	DEF(SET_STR, user),
-	DEF(SET_STR, chroot),
-	DEF(SET_UINT, count),
-	DEF(SET_UINT, process_size),
-	DEF(SET_STR, mechanisms),
-	DEF(SET_BOOL, debug),
-	DEFLIST(sockets, "socket", &master_auth_socket_setting_parser_info),
-
-	SETTING_DEFINE_LIST_END
-};
-
-static struct master_auth_settings master_auth_default_settings = {
-	MEMBER(name) "default",
-	MEMBER(executable) PKG_LIBEXECDIR"/dovecot-auth",
-	MEMBER(user) "root",
-	MEMBER(chroot) "",
-	MEMBER(count) 1,
-	MEMBER(process_size) 256,
-	MEMBER(mechanisms) "plain",
-	MEMBER(debug) FALSE
-
-	/* .. */
-};
-
-struct setting_parser_info master_auth_setting_parser_info = {
-	MEMBER(defines) master_auth_setting_defines,
-	MEMBER(defaults) &master_auth_default_settings,
+struct setting_parser_info service_setting_parser_info = {
+	MEMBER(defines) service_setting_defines,
+	MEMBER(defaults) &service_default_settings,
 
 	MEMBER(parent) &master_setting_parser_info,
 	MEMBER(dynamic_parsers) NULL,
 
-	MEMBER(parent_offset) (size_t)-1,
-	MEMBER(type_offset) offsetof(struct master_auth_settings, name),
-	MEMBER(struct_size) sizeof(struct master_auth_settings)
+	MEMBER(parent_offset) offsetof(struct service_settings, master_set),
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct service_settings)
 };
 
 #undef DEF
@@ -139,135 +150,37 @@
 	{ SET_DEFLIST, name, offsetof(struct master_settings, field), defines }
 
 static struct setting_define master_setting_defines[] = {
-	/* common */
 	DEF(SET_STR, base_dir),
-	DEF(SET_STR, log_path),
-	DEF(SET_STR, info_log_path),
-	DEF(SET_STR, log_timestamp),
-	DEF(SET_STR, syslog_facility),
-
-	/* general */
-	DEF(SET_STR, protocols),
-	DEF(SET_STR, listen),
-	DEF(SET_STR, ssl_listen),
-
-	DEF(SET_STR, ssl),
-	DEF(SET_STR, ssl_key_file),
-	DEF(SET_UINT, ssl_parameters_regenerate),
-	DEF(SET_BOOL, version_ignore),
+	DEF(SET_STR, libexec_dir),
+	DEF(SET_UINT, default_process_limit),
+	DEF(SET_UINT, default_client_limit),
 
-	/* login */
-	DEF(SET_STR, login_dir),
-	DEF(SET_STR, login_executable),
-	DEF(SET_STR, login_user),
-
-	DEF(SET_BOOL, login_process_per_connection),
-	DEF(SET_BOOL, login_chroot),
-	DEF(SET_BOOL, disable_plaintext_auth),
-
-	DEF(SET_UINT, login_process_size),
-	DEF(SET_UINT, login_processes_count),
-	DEF(SET_UINT, login_max_processes_count),
-
-	/* mail */
-	DEF(SET_STR, valid_chroot_dirs),
-	DEF(SET_STR, mail_chroot),
-	DEF(SET_UINT, max_mail_processes),
-	DEF(SET_UINT, mail_max_userip_connections),
-	DEF(SET_BOOL, verbose_proctitle),
+	DEF(SET_BOOL, version_ignore),
 
 	DEF(SET_UINT, first_valid_uid),
 	DEF(SET_UINT, last_valid_uid),
 	DEF(SET_UINT, first_valid_gid),
 	DEF(SET_UINT, last_valid_gid),
-	DEF(SET_STR, mail_access_groups),
-	DEF(SET_STR, mail_privileged_group),
-	DEF(SET_STR, mail_uid),
-	DEF(SET_STR, mail_gid),
 
-	DEF(SET_STR, mail_plugins),
-	DEF(SET_STR, imap_capability),
-
-	DEF(SET_STR_VARS, mail_location),
-	DEF(SET_BOOL, mail_debug),
-	DEF(SET_BOOL, mail_drop_priv_before_exec),
-
-	DEF(SET_STR, mail_executable),
-	DEF(SET_UINT, mail_process_size),
-	DEF(SET_STR, mail_log_prefix),
-	DEF(SET_UINT, mail_log_max_lines_per_sec),
-
-	/* dict */
-	DEF(SET_UINT, dict_process_count),
-	DEFLIST(auths, "auth", &master_auth_setting_parser_info),
+	DEFLIST(services, "service", &service_setting_parser_info),
 
 	SETTING_DEFINE_LIST_END
 };
 
-struct master_settings master_default_settings = {
-	/* common */
+static struct master_settings master_default_settings = {
 	MEMBER(base_dir) PKG_RUNDIR,
-	MEMBER(log_path) "",
-	MEMBER(info_log_path) "",
-	MEMBER(log_timestamp) DEFAULT_FAILURE_STAMP_FORMAT,
-	MEMBER(syslog_facility) "mail",
-
-	/* general */
-	MEMBER(protocols) "imap imaps",
-	MEMBER(listen) "*",
-	MEMBER(ssl_listen) "",
-
-	MEMBER(ssl) "yes",
-	MEMBER(ssl_key_file) SSLDIR"/private/dovecot.pem",
-	MEMBER(ssl_parameters_regenerate) 168,
-	MEMBER(version_ignore) FALSE,
+	MEMBER(libexec_dir) PKG_LIBEXECDIR,
+	MEMBER(default_process_limit) 100,
+	MEMBER(default_client_limit) 1000,
 
-	/* login */
-	MEMBER(login_dir) "login",
-	MEMBER(login_executable) NULL,
-	MEMBER(login_user) "dovecot",
-
-	MEMBER(login_process_per_connection) TRUE,
-	MEMBER(login_chroot) TRUE,
-	MEMBER(disable_plaintext_auth) TRUE,
-
-	MEMBER(login_process_size) 64,
-	MEMBER(login_processes_count) 3,
-	MEMBER(login_max_processes_count) 128,
-
-	/* mail */
-	MEMBER(valid_chroot_dirs) "",
-	MEMBER(mail_chroot) "",
-	MEMBER(max_mail_processes) 512,
-	MEMBER(mail_max_userip_connections) 10,
-	MEMBER(verbose_proctitle) FALSE,
+	MEMBER(version_ignore) FALSE,
 
 	MEMBER(first_valid_uid) 500,
 	MEMBER(last_valid_uid) 0,
 	MEMBER(first_valid_gid) 1,
 	MEMBER(last_valid_gid) 0,
-	MEMBER(mail_access_groups) "",
-	MEMBER(mail_privileged_group) "",
-	MEMBER(mail_uid) "",
-	MEMBER(mail_gid) "",
-	MEMBER(mail_plugins) "",
-	MEMBER(imap_capability) "",
 
-	MEMBER(mail_location) "",
-	MEMBER(mail_debug) FALSE,
-	MEMBER(maildir_very_dirty_syncs) FALSE,
-	MEMBER(dbox_purge_min_percentage) 0,
-	MEMBER(mail_drop_priv_before_exec) FALSE,
-
-	MEMBER(mail_executable) NULL,
-	MEMBER(mail_process_size) 256,
-	MEMBER(mail_log_prefix) "%Us(%u): ",
-	MEMBER(mail_log_max_lines_per_sec) 10,
-
-	/* dict */
-	MEMBER(dict_process_count) 1
-
-	/* .. */
+	MEMBER(services) ARRAY_INIT
 };
 
 struct setting_parser_info master_setting_parser_info = {
@@ -275,513 +188,73 @@
 	MEMBER(defaults) &master_default_settings,
 
 	MEMBER(parent) NULL,
-	MEMBER(dynamic_parsers) NULL,
-
 	MEMBER(parent_offset) (size_t)-1,
-	MEMBER(type_offset) (size_t)-1,
-	MEMBER(struct_size) sizeof(struct master_settings)
+	MEMBER(struct_size) sizeof(struct master_settings),
+	MEMBER(check_func) master_settings_verify
 };
 
-static pool_t settings_pool, settings2_pool;
-struct master_server_settings *master_set = NULL;
-
-#ifdef HAVE_MODULES
-static const char *
-get_process_capability(enum process_type ptype, struct master_settings *set)
+/* <settings checks> */
+static void fix_file_listener_paths(ARRAY_TYPE(file_listener_settings) *l,
+				    pool_t pool, const char *base_dir)
 {
-	/* FIXME: pretty ugly code just for getting the capability
-	   automatically */
-	static const char *args[] = {
-		"uid=65534",
-		"gid=65534",
-		"home=/tmp",
-		NULL
-	};
-	const char *pname = process_names[ptype];
-	enum master_login_status login_status;
-	struct mail_login_request request;
-	char buf[4096];
-	int fd[2], status;
-	ssize_t ret;
-	unsigned int pos;
-	uid_t uid;
-	pid_t pid;
-
-	uid = geteuid();
-	if (uid != 0) {
-		/* use the current user */
-		args[0] = t_strdup_printf("uid=%s", dec2str(uid));
-		args[1] = t_strdup_printf("gid=%s", dec2str(getegid()));
-	}
-
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
-		i_error("socketpair() failed: %m");
-		return NULL;
-	}
-	fd_close_on_exec(fd[0], TRUE);
-	fd_close_on_exec(fd[1], TRUE);
-
-	memset(&request, 0, sizeof(request));
-	request.fd = fd[1];
-	login_status = create_mail_process(ptype, set, &request,
-					   "dump-capability", args, NULL, TRUE,
-					   &pid);
-	if (login_status != MASTER_LOGIN_STATUS_OK) {
-		(void)close(fd[0]);
-		(void)close(fd[1]);
-		return NULL;
-	}
-	(void)close(fd[1]);
-
-	alarm(5);
-	if (wait(&status) == -1) {
-		i_fatal("%s dump-capability process %d got stuck",
-			pname, (int)pid);
-	}
-	alarm(0);
+	struct file_listener_settings *const *sets;
+	unsigned int i, count;
 
-	if (status != 0) {
-		(void)close(fd[0]);
-		if (WIFSIGNALED(status)) {
-			i_error("%s dump-capability process "
-				"killed with signal %d",
-				pname, WTERMSIG(status));
-		} else {
-			i_error("%s dump-capability process returned %d",
-				pname, WIFEXITED(status) ? WEXITSTATUS(status) :
-				status);
-		}
-		return NULL;
-	}
-
-	pos = 0;
-	while ((ret = read(fd[0], buf + pos, sizeof(buf) - pos)) > 0)
-		pos += ret;
-
-	if (ret < 0) {
-		i_error("read(%s dump-capability process) failed: %m", pname);
-		(void)close(fd[0]);
-		return NULL;
-	}
-	(void)close(fd[0]);
-
-	if (pos == 0 || buf[pos-1] != '\n') {
-		i_error("%s dump-capability: Couldn't read capability "
-			"(got %u bytes)", pname, pos);
-		return NULL;
-	}
-	buf[pos-1] = '\0';
+	if (!array_is_created(l))
+		return;
 
-	return i_strdup(buf);
-}
-
-static bool get_imap_capability(struct master_settings *set)
-{
-	static const char *generated_capability = NULL;
-
-	if (generated_capability != NULL) {
-		/* Reloading configuration. Don't try to execute the imap
-		   process again. Too risky and the wait() call below will
-		   break it anyway. Just use the previous capability list we
-		   already had generated. */
-		set->imap_generated_capability =
-			p_strdup(settings_pool, generated_capability);
-		return TRUE;
-	}
-
-	generated_capability = get_process_capability(PROCESS_TYPE_IMAP, set);
-	if (generated_capability == NULL)
-		return FALSE;
-
-	set->imap_generated_capability =
-		p_strdup(settings_pool, generated_capability);
-	return TRUE;
-}
-#endif
-
-static void fix_base_path(struct master_settings *set, const char **str)
-{
-	if (*str != NULL && **str != '\0' && **str != '/') {
-		*str = p_strconcat(settings_pool,
-				   set->base_dir, "/", *str, NULL);
+	sets = array_get(l, &count);
+	for (i = 0; i < count; i++) {
+		if (*sets[i]->path != '/') {
+			sets[i]->path = p_strconcat(pool, base_dir, "/",
+						    sets[i]->path, NULL);
+		}
 	}
 }
 
-static bool parse_uid(const char *str, uid_t *uid_r)
-{
-	struct passwd *pw;
-	char *p;
-
-	if (*str >= '0' && *str <= '9') {
-		*uid_r = (uid_t)strtoul(str, &p, 10);
-		if (*p == '\0')
-			return TRUE;
-	}
-
-	pw = getpwnam(str);
-	if (pw == NULL)
-		return FALSE;
-
-	*uid_r = pw->pw_uid;
-	return TRUE;
-}
-
-static bool parse_gid(const char *str, gid_t *gid_r)
-{
-	struct group *gr;
-	char *p;
-
-	if (*str >= '0' && *str <= '9') {
-		*gid_r = (gid_t)strtoul(str, &p, 10);
-		if (*p == '\0')
-			return TRUE;
-	}
-
-	gr = getgrnam(str);
-	if (gr == NULL)
-		return FALSE;
-
-	*gid_r = gr->gr_gid;
-	return TRUE;
-}
-
-static bool get_login_uid(struct master_settings *set)
-{
-	struct passwd *pw;
-
-	if ((pw = getpwnam(set->login_user)) == NULL) {
-		i_error("Login user doesn't exist: %s", set->login_user);
-		return FALSE;
-	}
-
-	if (set->server->login_gid == 0)
-		set->server->login_gid = pw->pw_gid;
-	else if (set->server->login_gid != pw->pw_gid) {
-		i_error("All login process users must belong to same group "
-			"(%s vs %s)", dec2str(set->server->login_gid),
-			dec2str(pw->pw_gid));
-		return FALSE;
-	}
-	set->login_uid = pw->pw_uid;
-	return TRUE;
-}
-
-static bool auth_settings_verify(struct master_settings *set,
-				 struct master_auth_settings *auth)
-{
-	struct passwd *pw;
-	struct master_auth_socket_settings *const *sockets;
-	unsigned int i, count;
-
-	if ((pw = getpwnam(auth->user)) == NULL) {
-		i_error("Auth user doesn't exist: %s", auth->user);
-		return FALSE;
-	}
-
-	if (set->login_uid == pw->pw_uid && master_uid != pw->pw_uid) {
-		i_error("login_user %s (uid %s) must not be same as auth_user",
-			auth->user, dec2str(pw->pw_uid));
-		return FALSE;
-	}
-	auth->uid = pw->pw_uid;
-	auth->gid = pw->pw_gid;
-
-	if (access(t_strcut(auth->executable, ' '), X_OK) < 0) {
-		i_error("auth_executable: Can't use %s: %m",
-			t_strcut(auth->executable, ' '));
-		return FALSE;
-	}
-
-	fix_base_path(set, &auth->chroot);
-	if (*auth->chroot != '\0' && access(auth->chroot, X_OK) < 0) {
-		i_error("Can't access auth chroot directory %s: %m",
-			auth->chroot);
-		return FALSE;
-	}
-
-	if (array_is_created(&auth->sockets))
-		sockets = array_get(&auth->sockets, &count);
-	else {
-		sockets = NULL;
-		count = 0;
-	}
-	for (i = 0; i < count; i++) {
-		if (auth->count > 1 &&
-		    strcmp(sockets[i]->type, "listen") == 0) {
-			i_error("Currently auth process count must be 1 if "
-				"you're using auth socket listeners.");
-			return FALSE;
-		}
-	}
-	return TRUE;
-}
-
-static const char *get_directory(const char *path)
-{
-	char *str, *p;
-
-	str = t_strdup_noconst(path);
-	p = strrchr(str, '/');
-	if (p == NULL)
-		return ".";
-	else {
-		*p = '\0';
-		return str;
-	}
-}
-
-static bool settings_is_active(struct master_settings *set)
-{
-	if (*set->protocols == '\0') {
-		/* we're probably using this with --exec-mail */
-		return TRUE;
-	}
-
-	if (set->protocol == MAIL_PROTOCOL_IMAP) {
-		if (strstr(set->protocols, "imap") == NULL)
-			return FALSE;
-	} else {
-		if (strstr(set->protocols, "pop3") == NULL)
-			return FALSE;
-	}
-
-	return TRUE;
-}
-
-static bool settings_have_connect_sockets(struct master_settings *set)
+static bool
+master_settings_verify(void *_set, pool_t pool, const char **error_r)
 {
-	struct master_auth_settings *const *auths;
-	struct master_auth_socket_settings *const *sockets;
-	unsigned int i, count, count2;
-
-	auths = array_get(&set->auths, &count);
-	for (i = 0; i < count; i++) {
-		if (!array_is_created(&auths[i]->sockets))
-			continue;
-		sockets = array_get(&auths[i]->sockets, &count2);
-		if (count2 > 0 && strcmp(sockets[0]->type, "connect") == 0)
-			return TRUE;
-	}
-
-	return FALSE;
-}
-
-static bool settings_have_nonplaintext_auths(struct master_settings *set)
-{
-	struct master_auth_settings *const *auths;
-	const char *const *tmp;
+	const struct master_settings *set = _set;
+	struct service_settings *const *services;
 	unsigned int i, count;
 
-	auths = array_get(&set->auths, &count);
-	for (i = 0; i < count; i++) {
-		tmp = t_strsplit_spaces(auths[i]->mechanisms, " ");
-		for (; *tmp != NULL; tmp++) {
-			if (strcasecmp(*tmp, "PLAIN") != 0 &&
-			    strcasecmp(*tmp, "LOGIN") != 0)
-				return TRUE;
-		}
-	}
-
-	return FALSE;
-}
-
-static void unlink_auth_sockets(const char *path, const char *prefix)
-{
-	DIR *dirp;
-	struct dirent *dp;
-	struct stat st;
-	string_t *str;
-	unsigned int prefix_len;
-
-	dirp = opendir(path);
-	if (dirp == NULL) {
-		i_error("opendir(%s) failed: %m", path);
-		return;
-	}
-
-	prefix_len = strlen(prefix);
-	str = t_str_new(256);
-	while ((dp = readdir(dirp)) != NULL) {
-		if (dp->d_name[0] == '.')
-			continue;
-
-		if (strncmp(dp->d_name, prefix, prefix_len) != 0)
-			continue;
-
-		str_truncate(str, 0);
-		str_printfa(str, "%s/%s", path, dp->d_name);
-		if (lstat(str_c(str), &st) < 0) {
-			if (errno != ENOENT)
-				i_error("lstat(%s) failed: %m", str_c(str));
-			continue;
-		}
-		if (!S_ISSOCK(st.st_mode))
-			continue;
-
-		/* try to avoid unlinking sockets if someone's already
-		   listening in them. do this only at startup, because
-		   when SIGHUPing a child process might catch the new
-		   connection before it notices that it's supposed
-		   to die. null_fd == -1 check is a bit kludgy, but works.. */
-		if (null_fd == -1) {
-			int fd = net_connect_unix(str_c(str));
-			if (fd != -1 || errno != ECONNREFUSED) {
-				i_fatal("Dovecot is already running? "
-					"Socket already exists: %s",
-					str_c(str));
-			}
-		}
-
-		if (unlink(str_c(str)) < 0 && errno != ENOENT)
-			i_error("unlink(%s) failed: %m", str_c(str));
-	}
-	(void)closedir(dirp);
-}
-
-static bool settings_verify(struct master_settings *set)
-{
-	const char *dir;
-	int facility;
-
-	if (!get_login_uid(set))
-		return FALSE;
-
-	set->mail_uid_t = (uid_t)-1;
-	set->mail_gid_t = (gid_t)-1;
-	set->mail_priv_gid_t = (gid_t)-1;
-
-	if (*set->mail_uid != '\0') {
-		if (!parse_uid(set->mail_uid, &set->mail_uid_t)) {
-			i_error("Non-existing mail_uid: %s", set->mail_uid);
-			return FALSE;
-		}
-	}
-	if (*set->mail_gid != '\0') {
-		if (!parse_gid(set->mail_gid, &set->mail_gid_t)) {
-			i_error("Non-existing mail_gid: %s", set->mail_uid);
-			return FALSE;
-		}
-	}
-	if (*set->mail_privileged_group != '\0') {
-		if (!parse_gid(set->mail_privileged_group,
-			       &set->mail_priv_gid_t)) {
-			i_error("Non-existing mail_privileged_group: %s",
-				set->mail_privileged_group);
-			return FALSE;
-		}
-	}
-
-	if (set->protocol != MAIL_PROTOCOL_ANY &&
-	    access(t_strcut(set->mail_executable, ' '), X_OK) < 0) {
-		i_error("mail_executable: Can't use %s: %m",
-			t_strcut(set->mail_executable, ' '));
-		return FALSE;
-	}
-
-	if (*set->log_path != '\0' && access(set->log_path, W_OK) < 0) {
-		dir = get_directory(set->log_path);
-		if (access(dir, W_OK) < 0) {
-			i_error("log_path: Can't write to directory %s: %m",
-				dir);
-			return FALSE;
-		}
-	}
-
-	if (*set->info_log_path != '\0' &&
-	    access(set->info_log_path, W_OK) < 0) {
-		dir = get_directory(set->info_log_path);
-		if (access(dir, W_OK) < 0) {
-			i_error("info_log_path: Can't write to directory %s: %m",
-				dir);
-			return FALSE;
-		}
-	}
-
-	if (!syslog_facility_find(set->syslog_facility, &facility)) {
-		i_error("syslog_facility: Unknown value: %s",
-			set->syslog_facility);
-		return FALSE;
-	}
-
-#ifndef HAVE_SSL
-	if (strcmp(set->ssl, "no") != 0) {
-		i_error("SSL support not compiled in but ssl=%s", set->ssl);
-		return FALSE;
-	}
-#endif
-	if (strcmp(set->ssl, "no") == 0 && set->disable_plaintext_auth &&
-	    strncmp(set->listen, "127.", 4) != 0 &&
-	    strcmp(set->protocols, "none") != 0 &&
-	    !settings_have_nonplaintext_auths(set)) {
-		i_warning("There is no way to login to this server: "
-			  "disable_plaintext_auth=yes, ssl=no, "
-			  "no non-plaintext auth mechanisms.");
-	}
-
-	if (set->max_mail_processes < 1) {
-		i_error("max_mail_processes must be at least 1");
-		return FALSE;
-	}
-	if (strcmp(set->login_dir, set->base_dir) == 0) {
-		i_error("login_dir can't be the same as base_dir");
-		return FALSE;
-	}
-
 	if (set->last_valid_uid != 0 &&
 	    set->first_valid_uid > set->last_valid_uid) {
-		i_error("first_valid_uid can't be larger than last_valid_uid");
+		*error_r = "first_valid_uid can't be larger than last_valid_uid";
 		return FALSE;
 	}
 	if (set->last_valid_gid != 0 &&
 	    set->first_valid_gid > set->last_valid_gid) {
-		i_error("first_valid_gid can't be larger than last_valid_gid");
-		return FALSE;
-	}
-	if (set->mail_drop_priv_before_exec && *set->mail_chroot != '\0') {
-		i_error("mail_drop_priv_before_exec=yes and mail_chroot "
-			"don't work together");
-		return FALSE;
-	}
-
-	if (set->protocol != MAIL_PROTOCOL_ANY &&
-	    access(t_strcut(set->login_executable, ' '), X_OK) < 0) {
-		i_error("login_executable: Can't use %s: %m",
-			t_strcut(set->login_executable, ' '));
-		return FALSE;
-	}
-
-	if (set->login_processes_count < 1) {
-		i_error("login_processes_count must be at least 1");
+		*error_r = "first_valid_gid can't be larger than last_valid_gid";
 		return FALSE;
 	}
 
-#ifndef HAVE_MODULES
-	if (*set->mail_plugins != '\0') {
-		i_error("mail_plugins: Plugin support wasn't built into Dovecot, "
-			"can't load plugins: %s", set->mail_plugins);
+	/* check that we have at least one service. the actual service
+	   structure validity is checked later while creating them. */
+	services = array_get(&set->services, &count);
+	if (count == 0) {
+		*error_r = "No services defined";
 		return FALSE;
 	}
-#endif
+	for (i = 0; i < count; i++) {
+		if (*services[i]->executable != '/') {
+			services[i]->executable =
+				p_strconcat(pool, set->libexec_dir, "/",
+					    services[i]->executable, NULL);
+		}
+		fix_file_listener_paths(&services[i]->unix_listeners,
+					pool, set->base_dir);
+		fix_file_listener_paths(&services[i]->fifo_listeners,
+					pool, set->base_dir);
+	}
 	return TRUE;
 }
-
-static bool login_want_core_dumps(struct master_server_settings *set)
-{
-	const char *p;
+/* </settings checks> */
 
-	p = set->pop3 == NULL ? NULL :
-		strstr(set->pop3->login_executable, " -D");
-	if (p != NULL && p[3] == '\0')
-		return TRUE;
-	p = set->imap == NULL ? NULL :
-		strstr(set->imap->login_executable, " -D");
-	if (p != NULL && p[3] == '\0')
-		return TRUE;
-	return FALSE;
-}
-
-static bool settings_do_fixes(struct master_settings *set)
+bool master_settings_do_fixes(const struct master_settings *set)
 {
+	const char *login_dir;
 	struct stat st;
 
 	/* since base dir is under /var/run by default, it may have been
@@ -795,246 +268,117 @@
 		i_error("stat(%s) failed: %m", set->base_dir);
 		return FALSE;
 	}
+	if (!S_ISDIR(st.st_mode)) {
+		i_error("%s is not a directory", set->base_dir);
+		return FALSE;
+	}
 
-	/* remove auth worker sockets left by unclean exits */
-	unlink_auth_sockets(set->base_dir, "auth-worker.");
+	/* create login directory under base dir */
+	login_dir = t_strconcat(set->base_dir, "/login", NULL);
+	if (mkdir(login_dir, 0755) < 0 && errno != EEXIST) {
+		i_error("mkdir(%s) failed: %m", login_dir);
+		return FALSE;
+	}
 
 	/* Make sure our permanent state directory exists */
 	if (mkdir_parents(PKG_STATEDIR, 0750) < 0 && errno != EEXIST) {
 		i_error("mkdir(%s) failed: %m", PKG_STATEDIR);
 		return FALSE;
 	}
-
-#ifdef HAVE_MODULES
-	if (*set->mail_plugins != '\0' && set->protocol == MAIL_PROTOCOL_IMAP &&
-	    *set->imap_capability == '\0') {
-		if (!get_imap_capability(set))
-			return FALSE;
-	}
-#endif
 	return TRUE;
 }
 
-static bool
-settings_fix(struct master_settings *set, bool nochecks, bool nofixes)
-{
-	/* fix relative paths */
-	fix_base_path(set, &set->login_dir);
-
-	if (nochecks)
-		return TRUE;
-	if (!settings_verify(set))
-		return FALSE;
-	return nofixes ? TRUE : settings_do_fixes(set);
-}
-
-static void
-settings_warn_needed_fds(struct master_server_settings *server ATTR_UNUSED)
-{
-#ifdef HAVE_SETRLIMIT
-	struct rlimit rlim;
-	unsigned int fd_count = 0;
-
-	if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
-		return;
-
-	/* count only log pipes needed for login and mail processes. we need
-	   more, but they're the ones that can use up most of the fds */
-	if (server->imap != NULL)
-		fd_count += server->imap->login_max_processes_count;
-	if (server->pop3 != NULL)
-		fd_count += server->pop3->login_max_processes_count;
-	fd_count += server->defaults->max_mail_processes;
-
-	if (rlim.rlim_cur < fd_count) {
-		i_warning("fd limit %d is lower than what Dovecot can use under "
-			  "full load (more than %u). Either grow the limit or "
-			  "change login_max_processes_count and "
-			  "max_mail_processes master_settings",
-			  (int)rlim.rlim_cur, fd_count);
-	}
-#endif
-}
-
-static void
-config_split_all_settings(struct master_settings *set, const char *input)
-{
-	const char *p, *line;
-	string_t *str;
-
-	str = t_str_new(256);
-	p_array_init(&set->all_settings, settings_pool, 256);
-	for (p = input; *p != '\n'; p++) {
-		str_truncate(str, 0);
-		for (; *p != '='; p++) {
-			i_assert(*p != '\n' && *p != '\0');
-			str_append_c(str, i_toupper(*p));
-		}
-		for (; *p != '\n'; p++) {
-			i_assert(*p != '\0');
-			str_append_c(str, *p);
-		}
-		line = p_strdup(settings_pool, str_c(str));
-		array_append(&set->all_settings, &line, 1);
-	}
-}
-
-static int config_exec(const char *path, const char *service,
-		       struct master_settings **set_r)
+#if 0 //FIXME
+static struct master_settings *
+master_settings_read_fd(pool_t pool, int fd, const char **error_r)
 {
 	struct setting_parser_context *parser;
-	string_t *all_settings;
+        struct master_settings *set;
+	struct istream *input;
 	int ret;
 
-	env_put("LOG_TO_MASTER=1");
-
-	all_settings = str_new(default_pool, 10240);
-	parser = settings_parser_init(settings_pool,
-				      &master_setting_parser_info,
+	parser = settings_parser_init(pool, &master_setting_parser_info,
 				      SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
-	settings_parse_save_input(parser, all_settings);
-	if ((ret = settings_parse_exec(parser, DOVECOT_CONFIG_BIN_PATH,
-				       path, service)) == 0) {
-		*set_r = settings_parser_get(parser);
-		config_split_all_settings(*set_r, str_c(all_settings));
+	input = i_stream_create_fd(fd, 1024, FALSE);
+	ret = settings_parse_stream_read(parser, input);
+	i_stream_unref(&input);
+
+	if (ret == 0) {
+		*error_r = NULL;
+		set = settings_parser_get(parser);
+	} else {
+		*error_r = t_strdup_printf(
+			"master: Error reading configuration: %s",
+			settings_parser_get_error(parser));
+		set = NULL;
 	}
 	settings_parser_deinit(&parser);
-	str_free(&all_settings);
-	return ret;
+
+	return set;
 }
 
-int master_settings_read(const char *path,
-			 struct master_server_settings **set_r)
-{
-	struct master_server_settings *set;
-
-	p_clear(settings_pool);
-	set = p_new(settings_pool, struct master_server_settings, 1);
-
-	master_default_settings.mail_executable = NULL;
-	master_default_settings.login_executable = NULL;
-	if (config_exec(path, "", &set->defaults) < 0)
-		return -1;
-	set->defaults->protocol = MAIL_PROTOCOL_ANY;
-	set->defaults->server = set;
-
-	master_default_settings.mail_executable = PKG_LIBEXECDIR"/imap";
-	master_default_settings.login_executable = PKG_LIBEXECDIR"/imap-login";
-	if (config_exec(path, "imap", &set->imap) < 0)
-		return -1;
-	set->imap->protocol = MAIL_PROTOCOL_IMAP;
-	set->imap->server = set;
-
-	master_default_settings.mail_executable = PKG_LIBEXECDIR"/pop3";
-	master_default_settings.login_executable = PKG_LIBEXECDIR"/pop3-login";
-	if (config_exec(path, "pop3", &set->pop3) < 0)
-		return -1;
-	set->pop3->protocol = MAIL_PROTOCOL_POP3;
-	set->pop3->server = set;
-
-	*set_r = set;
-	return 0;
-}
-
-static void settings_verify_master(struct master_server_settings *set)
+struct master_settings *
+master_settings_read(pool_t pool, const char *config_binary,
+		     const char *config_path)
 {
-	if (!settings_have_connect_sockets(set->defaults)) {
-		/* we are not using external authentication, so make sure the
-		   login directory exists with correct permissions and it's
-		   empty. with external auth we wouldn't want to delete
-		   existing sockets or break the permissions required by the
-		   auth server. */
-		mode_t mode = login_want_core_dumps(set) ? 0770 : 0750;
-		if (safe_mkdir(set->defaults->login_dir, mode,
-			       master_uid, set->login_gid) == 0) {
-			i_warning("Corrected permissions for login directory "
-				  "%s", set->defaults->login_dir);
-		}
-
-		unlink_auth_sockets(set->defaults->login_dir, "");
-	}
-}
+	struct master_settings *set;
+	const char *error;
+	pid_t pid;
+	int fd[2], status;
 
-bool master_settings_check(struct master_server_settings *set,
-			   bool nochecks, bool nofixes)
-{
-	struct master_auth_settings *const *auths;
-	unsigned int i, count;
-	pool_t temp;
-
-	if ((*set->imap->protocols == '\0' ||
-	     *set->pop3->protocols == '\0') && !nochecks) {
-		i_error("protocols: No protocols given in configuration file");
-		return FALSE;
+	if (pipe(fd) < 0) {
+		i_error("pipe() failed: %m");
+		return NULL;
 	}
-	/* --exec-mail is used if nochecks=TRUE. Allow it regardless
-	   of what's in protocols setting. */
-	if (!settings_is_active(set->imap) && !nochecks) {
-		if (strcmp(set->imap->protocols, "none") == 0) {
-			set->imap->protocol = MAIL_PROTOCOL_ANY;
-			if (!settings_fix(set->imap, nochecks, nofixes))
-				return FALSE;
-		}
-		set->imap = NULL;
-	} else {
-		if (!settings_fix(set->imap, nochecks, nofixes))
-			return FALSE;
+
+	pid = fork();
+	if (pid < 0) {
+		i_error("fork() failed: %m");
+		(void)close(fd[0]);
+		(void)close(fd[1]);
+		return NULL;
 	}
 
-	if (!settings_is_active(set->pop3) && !nochecks)
-		set->pop3 = NULL;
-	else {
-		if (!settings_fix(set->pop3, nochecks, nofixes))
-			return FALSE;
-	}
-
-	if (!settings_fix(set->defaults, nochecks, nofixes))
-		return FALSE;
+	if (pid == 0) {
+		/* child */
+		const char *argv[3] = { "-c", config_path, NULL };
 
-	if (!nofixes) {
-		settings_verify_master(set);
-		auths = array_get(&set->defaults->auths, &count);
-		if (count == 0) {
-			i_error("Missing auth section");
-			return FALSE;
-		}
+		if (dup2(fd[1], STDOUT_FILENO) < 0)
+			i_fatal("dup2(stdout) failed: %m");
+		(void)close(fd[0]);
+		(void)close(fd[1]);
 
-		for (i = 0; i < count; i++) {
-			if (!auth_settings_verify(set->defaults, auths[i]))
-				return FALSE;
-		}
+		env_put("MASTER_INIT=1");
+		process_exec(config_binary, argv);
 	}
 
-	if (!nochecks)
-		settings_warn_needed_fds(set);
-
-	/* settings ok, swap them */
-	temp = settings_pool;
-	settings_pool = settings2_pool;
-	settings2_pool = temp;
+	/* parent. */
+	(void)close(fd[1]);
 
-	master_set = set;
-	return TRUE;
-}
-
-void master_settings_export_to_env(const struct master_settings *set)
-{
-	const char *const *sets;
-	unsigned int i, count;
+	set = master_settings_read_fd(pool, fd[0], &error);
+	if (close(fd[0]) < 0)
+		i_error("close(setpipe) failed: %m");
 
-	sets = array_get(&set->all_settings, &count);
-	for (i = 0; i < count; i++)
-		env_put(sets[i]);
-}
+	/* if config process doesn't return successfully, assume the returned
+	   configuration is invalid */
+	if (waitpid(pid, &status, 0) < 0) {
+		i_error("waitpid(config) failed: %m");
+		set = NULL;
+	} else if (status != 0) {
+		if (!WIFEXITED(status) || WEXITSTATUS(status) != FATAL_DEFAULT)
+			process_log_status_error(pid, "config", status);
+		set = NULL;
+	} else if (set == NULL) {
+		i_error("%s", error);
+	}
 
-void master_settings_init(void)
-{
-	settings_pool = pool_alloconly_create("settings", 4096);
-	settings2_pool = pool_alloconly_create("settings2", 4096);
-}
+	if (set != NULL) {
+		if (!master_settings_verify(set))
+			set = NULL;
+		else if (!settings_do_fixes(set))
+			set = NULL;
+	}
 
-void master_settings_deinit(void)
-{
-	pool_unref(&settings_pool);
-	pool_unref(&settings2_pool);
+	return set;
 }
+#endif
--- a/src/master/master-settings.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/master/master-settings.h	Thu Apr 23 19:53:44 2009 -0400
@@ -1,152 +1,61 @@
 #ifndef MASTER_SETTINGS_H
 #define MASTER_SETTINGS_H
 
-#include "network.h"
-
-#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
-
-enum mail_protocol {
-        MAIL_PROTOCOL_ANY,
-        MAIL_PROTOCOL_IMAP,
-	MAIL_PROTOCOL_POP3,
-	MAIL_PROTOCOL_LDA
+struct file_listener_settings {
+	const char *path;
+	unsigned int mode;
+	const char *user;
+	const char *group;
 };
+ARRAY_DEFINE_TYPE(file_listener_settings, struct file_listener_settings *);
 
-struct listener {
-	struct ip_addr ip;
+struct inet_listener_settings {
+	const char *address;
 	unsigned int port;
-	int fd;
-	bool wanted;
-};
-ARRAY_DEFINE_TYPE(listener, struct listener);
-
-struct master_auth_socket_unix_settings {
-	const char *path;
 };
 
-struct master_auth_socket_settings {
-	const char *type;
+struct service_settings {
+	struct master_settings *master_set;
 
-	ARRAY_DEFINE(masters, struct master_auth_socket_unix_settings *);
-};
-
-struct master_auth_settings {
-	const char *name;
+	const char *type;
 	const char *executable;
 	const char *user;
+	const char *group;
+	const char *privileged_group;
+	const char *extra_groups;
 	const char *chroot;
+	const char *auth_dest_service;
 
-	unsigned int count;
-	unsigned int process_size;
+	bool drop_priv_before_exec;
 
-	const char *mechanisms;
-	bool debug;
+	unsigned int process_limit;
+	unsigned int client_limit;
+	unsigned int vsz_limit;
 
-	ARRAY_DEFINE(sockets, struct master_auth_socket_settings *);
-
-	/* .. */
-	uid_t uid;
-	gid_t gid;
+	ARRAY_TYPE(file_listener_settings) unix_listeners;
+	ARRAY_TYPE(file_listener_settings) fifo_listeners;
+	ARRAY_DEFINE(inet_listeners, struct inet_listener_settings *);
 };
 
 struct master_settings {
-	/* common */
 	const char *base_dir;
-	const char *log_path;
-	const char *info_log_path;
-	const char *log_timestamp;
-	const char *syslog_facility;
-
-	/* general */
-	const char *protocols;
-	const char *listen;
-	const char *ssl_listen;
-
-	const char *ssl;
-	const char *ssl_key_file;
-	unsigned int ssl_parameters_regenerate;
-	bool version_ignore;
+	const char *libexec_dir;
+	unsigned int default_process_limit;
+	unsigned int default_client_limit;
 
-	/* login */
-	const char *login_dir;
-	const char *login_executable;
-	const char *login_user;
-
-	bool login_process_per_connection;
-	bool login_chroot;
-	bool disable_plaintext_auth;
-
-	unsigned int login_process_size;
-	unsigned int login_processes_count;
-	unsigned int login_max_processes_count;
-
-	/* mail */
-	const char *valid_chroot_dirs;
-	const char *mail_chroot;
-	unsigned int max_mail_processes;
-	unsigned int mail_max_userip_connections;
-	bool verbose_proctitle;
+	bool version_ignore;
 
 	unsigned int first_valid_uid, last_valid_uid;
 	unsigned int first_valid_gid, last_valid_gid;
-	const char *mail_access_groups;
-	const char *mail_privileged_group;
-	const char *mail_uid;
-	const char *mail_gid;
 
-	const char *mail_plugins;
-	const char *imap_capability;
-
-	const char *mail_location;
-	bool mail_debug;
-	bool maildir_very_dirty_syncs;
-	unsigned int dbox_purge_min_percentage;
-	bool mail_drop_priv_before_exec;
-
-	const char *mail_executable;
-	unsigned int mail_process_size;
-	const char *mail_log_prefix;
-	unsigned int mail_log_max_lines_per_sec;
-
-	/* dict */
-	unsigned int dict_process_count;
-
-	ARRAY_DEFINE(auths, struct master_auth_settings *);
-
-#ifndef CONFIG_BINARY
-	/* .. */
-	struct master_server_settings *server;
-	enum mail_protocol protocol;
-
-	ARRAY_TYPE(listener) listens;
-	ARRAY_TYPE(listener) ssl_listens;
-
-	uid_t login_uid, mail_uid_t;
-	gid_t mail_gid_t, mail_priv_gid_t;
-
-	const char *imap_generated_capability;
-	ARRAY_TYPE(const_string) all_settings;
-#endif
+	ARRAY_DEFINE(services, struct service_settings *);
 };
 
-struct master_server_settings {
-	struct master_settings *defaults;
-	struct master_settings *imap;
-	struct master_settings *pop3;
-
-	gid_t login_gid;
-};
-
-extern struct master_server_settings *master_set;
 extern struct setting_parser_info master_setting_parser_info;
 
-int master_settings_read(const char *path,
-			 struct master_server_settings **set_r);
-bool master_settings_check(struct master_server_settings *set,
-			   bool nochecks, bool nofixes);
-void master_settings_export_to_env(const struct master_settings *set);
-
-void master_settings_init(void);
-void master_settings_deinit(void);
+struct master_settings *
+master_settings_read(pool_t pool, const char *config_binary,
+		     const char *config_path);
+bool master_settings_do_fixes(const struct master_settings *set);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-auth-server.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,255 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "hash.h"
+#include "service.h"
+#include "service-process.h"
+#include "service-auth-server.h"
+#include "service-auth-source.h"
+#include "../auth/auth-master-interface.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#define AUTH_MAX_INBUF_SIZE 8192
+
+static void
+service_process_auth_request_free(struct service_process_auth_request *request)
+{
+	if (request->fd != -1) {
+		if (close(request->fd) < 0)
+			i_error("close(auth request fd) failed: %m");
+	}
+	i_free(request);
+}
+
+static void
+service_process_auth_server_close(struct service_process_auth_server *process)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	if (process->auth_requests != NULL) {
+		iter = hash_table_iterate_init(process->auth_requests);
+		while (hash_table_iterate(iter, &key, &value)) {
+			struct service_process_auth_request *request = value;
+
+			service_process_unref(&request->process->process);
+			service_process_auth_request_free(request);
+		}
+		hash_table_iterate_deinit(&iter);
+		hash_table_destroy(&process->auth_requests);
+	}
+
+	if (process->auth_input != NULL)
+		i_stream_close(process->auth_input);
+	if (process->auth_output != NULL)
+		o_stream_close(process->auth_output);
+
+	if (process->io_auth != NULL)
+		io_remove(&process->io_auth);
+	if (process->auth_fd != -1) {
+		if (close(process->auth_fd) < 0)
+			i_error("close(auth_fd) failed: %m");
+		process->auth_fd = -1;
+	}
+}
+
+static struct service_process_auth_request *
+auth_process_lookup_request(struct service_process_auth_server *process,
+			    unsigned int id)
+{
+        struct service_process_auth_request *request;
+
+	request = hash_table_lookup(process->auth_requests, POINTER_CAST(id));
+	if (request == NULL) {
+		i_error("service(%s): authentication service %s "
+			"sent reply with unknown ID %u",
+			process->process.service->name,
+			dec2str(process->process.pid), id);
+		return NULL;
+	}
+
+	hash_table_remove(process->auth_requests, POINTER_CAST(id));
+	if (!service_process_unref(&request->process->process)) {
+		/* process already died. */
+		service_process_auth_request_free(request);
+		return NULL;
+	}
+
+	return request;
+}
+
+static int
+auth_process_input_user(struct service_process_auth_server *process, const char *args)
+{
+        struct service_process_auth_request *request;
+	const char *const *list;
+	enum master_auth_status status;
+	unsigned int id;
+
+	/* <id> <userid> [..] */
+
+	list = t_strsplit(args, "\t");
+	if (list[0] == NULL || list[1] == NULL) {
+		i_error("BUG: Auth process %s sent corrupted USER line",
+			dec2str(process->process.pid));
+		return FALSE;
+	}
+	id = (unsigned int)strtoul(list[0], NULL, 10);
+
+        request = auth_process_lookup_request(process, id);
+	if (request != NULL) {
+		struct service *dest_service =
+			request->process->process.service->auth_dest_service;
+		struct service_process *dest_process;
+
+		/* FIXME: handle MASTER_AUTH_STATUS_MAX_CONNECTIONS */
+		dest_process = service_process_create(dest_service, list + 1,
+						      request->fd,
+						      request->data,
+						      request->data_size);
+		status = dest_process != NULL ?
+			MASTER_AUTH_STATUS_OK :
+			MASTER_AUTH_STATUS_INTERNAL_ERROR;
+		service_process_auth_source_send_reply(request->process,
+						       request->process_tag,
+						       status);
+		service_process_auth_request_free(request);
+	}
+	return TRUE;
+}
+
+static int
+auth_process_input_notfound(struct service_process_auth_server *process,
+			    const char *args)
+{
+        struct service_process_auth_request *request;
+	unsigned int id;
+
+	id = (unsigned int)strtoul(args, NULL, 10);
+
+        request = auth_process_lookup_request(process, id);
+	if (request != NULL) {
+		service_process_auth_source_send_reply(request->process,
+						       request->process_tag,
+						       FALSE);
+		service_process_auth_request_free(request);
+	}
+	return TRUE;
+}
+
+static int
+auth_process_input_fail(struct service_process_auth_server *process,
+			const char *args)
+{
+        struct service_process_auth_request *request;
+ 	const char *error;
+	unsigned int id;
+
+	error = strchr(args, '\t');
+	if (error != NULL)
+		error++;
+
+	id = (unsigned int)strtoul(args, NULL, 10);
+
+        request = auth_process_lookup_request(process, id);
+	if (request != NULL) {
+		service_process_auth_source_send_reply(request->process,
+						       request->process_tag,
+						       FALSE);
+		service_process_auth_request_free(request);
+	}
+	return TRUE;
+}
+
+static void
+service_process_auth_server_input(struct service_process_auth_server *process)
+{
+	const char *line;
+	int ret;
+
+	switch (i_stream_read(process->auth_input)) {
+	case 0:
+		return;
+	case -1:
+		/* disconnected */
+		service_process_auth_server_close(process);
+		return;
+	case -2:
+		/* buffer full */
+		i_error("service(%s): authentication server process %s "
+			"sent us too long line", process->process.service->name,
+			dec2str(process->process.pid));
+		service_process_auth_server_close(process);
+		return;
+	}
+
+	if (!process->auth_version_received) {
+		line = i_stream_next_line(process->auth_input);
+		if (line == NULL)
+			return;
+
+		/* make sure the major version matches */
+		if (strncmp(line, "VERSION\t", 8) != 0 ||
+		    atoi(t_strcut(line + 8, '\t')) !=
+		    AUTH_MASTER_PROTOCOL_MAJOR_VERSION) {
+			i_error("service(%s): authentication server process %s "
+				"not compatible with master process "
+				"(mixed old and new binaries?)",
+				process->process.service->name,
+				dec2str(process->process.pid));
+			service_process_auth_server_close(process);
+			return;
+		}
+		process->auth_version_received = TRUE;
+	}
+
+	while ((line = i_stream_next_line(process->auth_input)) != NULL) {
+		if (strncmp(line, "USER\t", 5) == 0)
+			ret = auth_process_input_user(process, line + 5);
+		else if (strncmp(line, "NOTFOUND\t", 9) == 0)
+			ret = auth_process_input_notfound(process, line + 9);
+		else if (strncmp(line, "FAIL\t", 5) == 0)
+			ret = auth_process_input_fail(process, line + 5);
+		else
+			ret = TRUE;
+
+		if (!ret) {
+			service_process_auth_server_close(process);
+			break;
+		}
+	}
+}
+
+void service_process_auth_server_init(struct service_process *_process, int fd)
+{
+	struct service_process_auth_server *process =
+		(struct service_process_auth_server *)_process;
+
+	i_assert(_process->service->type == SERVICE_TYPE_AUTH_SERVER);
+
+	process->auth_fd = fd;
+	process->auth_input = i_stream_create_fd(process->auth_fd,
+						 AUTH_MAX_INBUF_SIZE, FALSE);
+	process->auth_output =
+		o_stream_create_fd(fd, (size_t)-1, FALSE);
+	process->io_auth =
+		io_add(process->auth_fd, IO_READ,
+		       service_process_auth_server_input, process);
+	process->auth_requests =
+		hash_table_create(default_pool, default_pool, 0, NULL, NULL);
+}
+
+void service_process_auth_server_deinit(struct service_process *_process)
+{
+	struct service_process_auth_server *process =
+		(struct service_process_auth_server *)_process;
+
+	i_assert(_process->service->type == SERVICE_TYPE_AUTH_SERVER);
+
+	service_process_auth_server_close(process);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-auth-server.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,9 @@
+#ifndef SERVICE_AUTH_SERVER_H
+#define SERVICE_AUTH_SERVER_H
+
+struct service_process;
+
+void service_process_auth_server_init(struct service_process *process, int fd);
+void service_process_auth_server_deinit(struct service_process *process);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-auth-source.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,276 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "hash.h"
+#include "str.h"
+#include "ioloop.h"
+#include "ostream.h"
+#include "fdpass.h"
+#include "fd-close-on-exec.h"
+#include "../auth/auth-master-interface.h"
+#include "service.h"
+#include "service-process.h"
+#include "service-auth-source.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+#define AUTH_SOURCE_OUTBUF_THROTTLE_THRESHOLD (1024 - 256)
+#define AUTH_SERVER_MAX_OUTBUF_SIZE (1024*64)
+#define AUTH_BUSY_LOG_INTERVAL 30
+
+static void
+service_process_auth_source_input(struct service_process_auth_source *process);
+
+static void
+service_process_auth_source_close(struct service_process_auth_source *process)
+{
+	if (process->auth_output != NULL)
+		o_stream_close(process->auth_output);
+	if (process->io_auth != NULL)
+		io_remove(&process->io_auth);
+	if (process->auth_fd != -1) {
+		if (close(process->auth_fd) < 0)
+			i_error("close(auth_fd) failed: %m");
+		process->auth_fd = -1;
+	}
+}
+
+static int
+process_auth_source_output(struct service_process_auth_source *process)
+{
+	int ret;
+
+	if ((ret = o_stream_flush(process->auth_output)) < 0)
+		return -1;
+
+	if (process->io_auth == NULL &&
+	    o_stream_get_buffer_used_size(process->auth_output) <
+	    AUTH_SOURCE_OUTBUF_THROTTLE_THRESHOLD) {
+		/* enable parsing input again */
+		o_stream_unset_flush_callback(process->auth_output);
+		process->io_auth = io_add(process->auth_fd, IO_READ,
+					  service_process_auth_source_input,
+					  process);
+	}
+	return ret;
+}
+
+void service_process_auth_source_send_reply(struct service_process_auth_source *process,
+					    unsigned int tag,
+					    enum master_auth_status status)
+{
+	struct master_auth_reply reply;
+
+	if (o_stream_get_buffer_used_size(process->auth_output) >=
+	    AUTH_SOURCE_OUTBUF_THROTTLE_THRESHOLD) {
+		/* not reading our output. stop parsing input until it will. */
+		if (process->io_auth != NULL) {
+			io_remove(&process->io_auth);
+
+			o_stream_set_flush_callback(process->auth_output,
+						    process_auth_source_output,
+						    process);
+		}
+	}
+
+	/* Reply to login process */
+	memset(&reply, 0, sizeof(reply));
+	reply.tag = tag;
+	reply.status = status;
+
+	o_stream_send(process->auth_output, &reply, sizeof(reply));
+}
+
+static unsigned int
+auth_server_send_request(struct service_process_auth_server *server_process,
+			 struct service_process_auth_source *source_process,
+			 unsigned int auth_id)
+{
+	unsigned int tag = 0;
+	string_t *str;
+
+	while (tag == 0)
+                tag = ++server_process->auth_tag_counter;
+
+	str = t_str_new(256);
+	if (!server_process->auth_version_sent) {
+                server_process->auth_version_sent = TRUE;
+		str_printfa(str, "VERSION\t%u\t%u\n",
+			    AUTH_MASTER_PROTOCOL_MAJOR_VERSION,
+			    AUTH_MASTER_PROTOCOL_MINOR_VERSION);
+		o_stream_send(server_process->auth_output,
+			      str_data(str), str_len(str));
+		str_truncate(str, 0);
+	}
+
+	str_printfa(str, "REQUEST\t%u\t%s\t%u\n",
+		    tag, dec2str(source_process->process.pid), auth_id);
+	o_stream_send(server_process->auth_output, str_data(str), str_len(str));
+	return tag;
+}
+
+static int
+auth_read_request(struct service_process_auth_source *process,
+		  struct master_auth_request *req,
+		  unsigned char data[MASTER_AUTH_MAX_DATA_SIZE],
+		  int *client_fd_r)
+{
+	struct service *service = process->process.service;
+	struct stat st;
+	ssize_t ret;
+
+	*client_fd_r = -1;
+
+	ret = fd_read(process->auth_fd, req, sizeof(*req), client_fd_r);
+	if (ret != sizeof(*req)) {
+		if (ret == 0) {
+			/* disconnected */
+		} else if (ret > 0) {
+			/* request wasn't fully read */
+			i_error("service(%s): fd_read() partial input (%d/%d)",
+				service->name, (int)ret, (int)sizeof(*req));
+		} else {
+			if (errno == EAGAIN)
+				return 0;
+
+			i_error("service(%s): fd_read() failed: %m",
+				service->name);
+		}
+		return -1;
+	}
+
+	if (req->data_size != 0) {
+		if (req->data_size > MASTER_AUTH_MAX_DATA_SIZE) {
+			i_error("service(%s): Too large auth data_size sent",
+				service->name);
+			return -1;
+		}
+		/* @UNSAFE */
+		ret = read(process->auth_fd, data, req->data_size);
+		if (ret != (ssize_t)req->data_size) {
+			if (ret == 0) {
+				/* disconnected */
+			} else if (ret > 0) {
+				/* request wasn't fully read */
+				i_error("service(%s): Data read partially %d/%u",
+					service->name, (int)ret, req->data_size);
+			} else {
+				i_error("service(%s): read(data) failed: %m",
+					service->name);
+			}
+			return -1;
+		}
+	}
+
+	if (*client_fd_r == -1) {
+		i_error("service(%s): Auth request missing a file descriptor",
+			service->name);
+		return -1;
+	}
+
+	if (fstat(*client_fd_r, &st) < 0) {
+		i_error("service(%s): fstat(auth dest fd) failed: %m",
+			service->name);
+		return -1;
+	}
+	if (st.st_ino != req->ino) {
+		i_error("service(%s): Auth request inode mismatch: %s != %s",
+			service->name, dec2str(st.st_ino), dec2str(req->ino));
+		return -1;
+	}
+	return 1;
+}
+
+static void
+service_process_auth_source_input(struct service_process_auth_source *process)
+{
+	struct service *service = process->process.service;
+	struct service_process_auth_server *auth_process;
+	struct service_process_auth_request *auth_req;
+	struct master_auth_request req;
+	unsigned char data[MASTER_AUTH_MAX_DATA_SIZE];
+	unsigned int tag;
+	ssize_t ret;
+	int client_fd;
+
+	ret = auth_read_request(process, &req, data, &client_fd);
+	if (ret == 0)
+		return;
+	if (ret < 0) {
+		if (client_fd != -1) {
+			if (close(client_fd) < 0)
+				i_error("login: close(mail client) failed: %m");
+		}
+		service_process_auth_source_close(process);
+		return;
+	}
+	fd_close_on_exec(client_fd, TRUE);
+
+	/* we have a request. check its validity. */
+	auth_process = hash_table_lookup(service->list->pids, &req.auth_pid);
+	if (auth_process == NULL) {
+		i_error("service(%s): authentication request for unknown "
+			"auth server PID %s", service->name,
+			dec2str(req.auth_pid));
+		service_process_auth_source_send_reply(process, req.tag, FALSE);
+		(void)close(client_fd);
+		return;
+	}
+
+	if (o_stream_get_buffer_used_size(auth_process->auth_output) >=
+	    AUTH_SERVER_MAX_OUTBUF_SIZE) {
+		if (auth_process->auth_busy_stamp <=
+		    ioloop_time - AUTH_BUSY_LOG_INTERVAL) {
+			i_warning("service(%s): authentication server PID "
+				  "%s too busy",
+				  auth_process->process.service->name,
+				  dec2str(req.auth_pid));
+                        auth_process->auth_busy_stamp = ioloop_time;
+		}
+		service_process_auth_source_send_reply(process, req.tag, FALSE);
+		(void)close(client_fd);
+		return;
+	}
+
+	/* ok, we have a non-busy authentication server.
+	   send a request to it. */
+	auth_req = i_malloc(sizeof(*auth_req) + req.data_size);
+	auth_req->process = process;
+	auth_req->process_tag = req.tag;
+	auth_req->fd = client_fd;
+	auth_req->local_ip = req.local_ip;
+	auth_req->remote_ip = req.remote_ip;
+	auth_req->data_size = req.data_size;
+	memcpy(auth_req->data, data, req.data_size);
+
+	tag = auth_server_send_request(auth_process, process, req.auth_id);
+
+	service_process_ref(&process->process);
+	hash_table_insert(auth_process->auth_requests,
+			  POINTER_CAST(tag), auth_req);
+}
+
+void service_process_auth_source_init(struct service_process *_process, int fd)
+{
+	struct service_process_auth_source *process =
+		(struct service_process_auth_source *)_process;
+
+	i_assert(_process->service->type == SERVICE_TYPE_AUTH_SOURCE);
+
+	process->auth_fd = fd;
+	process->io_auth = io_add(process->auth_fd, IO_READ,
+				  service_process_auth_source_input, process);
+	process->auth_output =
+		o_stream_create_fd(fd, (size_t)-1, FALSE);
+}
+
+void service_process_auth_source_deinit(struct service_process *_process)
+{
+	struct service_process_auth_source *process =
+		(struct service_process_auth_source *)_process;
+
+	i_assert(_process->service->type == SERVICE_TYPE_AUTH_SOURCE);
+
+	service_process_auth_source_close(process);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-auth-source.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,14 @@
+#ifndef SERVICE_AUTH_SOURCE_H
+#define SERVICE_AUTH_SOURCE_H
+
+struct service_process;
+struct service_process_auth_source;
+
+void service_process_auth_source_init(struct service_process *process, int fd);
+void service_process_auth_source_deinit(struct service_process *process);
+
+void service_process_auth_source_send_reply(struct service_process_auth_source *process,
+					    unsigned int tag,
+					    enum master_auth_status status);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-listen.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,262 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "array.h"
+#include "fd-set-nonblock.h"
+#include "fd-close-on-exec.h"
+#include "network.h"
+#include "service.h"
+#include "service-listen.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+static int service_unix_listener_listen(struct service_listener *l)
+{
+        struct service *service = l->service;
+	const struct file_listener_settings *set = l->set.fileset.set;
+	mode_t old_umask;
+	int fd, i;
+
+	old_umask = umask((set->mode ^ 0777) & 0777);
+	for (i = 0;; i++) {
+		fd = net_listen_unix(set->path, service->process_limit);
+		if (fd != -1)
+			break;
+
+		if (errno == EISDIR || errno == ENOENT) {
+			/* looks like the path doesn't exist. */
+			return 0;
+		}
+
+		if (errno != EADDRINUSE) {
+			i_error("service(%s): net_listen_unix(%s) failed: %m",
+				service->name, set->path);
+			return -1;
+		}
+
+		/* already in use - see if it really exists.
+		   after 3 times just fail here. */
+		fd = net_connect_unix(set->path);
+		if (fd != -1 || errno != ECONNREFUSED || i >= 3) {
+			if (fd != -1)
+				(void)close(fd);
+			i_error("service(%s): Socket already exists: %s",
+				service->name, set->path);
+			return 0;
+		}
+
+		/* delete and try again */
+		if (unlink(set->path) < 0 && errno != ENOENT) {
+			i_error("service(%s): unlink(%s) failed: %m",
+				service->name, set->path);
+			return -1;
+		}
+	}
+	umask(old_umask);
+
+	i_assert(fd != -1);
+
+	/* see if we need to change its owner/group */
+	if ((service->uid != (uid_t)-1 && service->uid != master_uid) ||
+	    (service->gid != (gid_t)-1 && service->gid != master_gid)) {
+		if (chown(set->path, service->uid, service->gid) < 0) {
+			i_error("chown(%s, %s, %s) failed: %m", set->path,
+				dec2str(service->uid), dec2str(service->gid));
+			(void)close(fd);
+			return -1;
+		}
+	}
+
+	net_set_nonblock(fd, TRUE);
+	fd_close_on_exec(fd, TRUE);
+
+	l->fd = fd;
+	return 1;
+}
+
+static int service_fifo_listener_listen(struct service_listener *l)
+{
+        struct service *service = l->service;
+	const struct file_listener_settings *set = l->set.fileset.set;
+	mode_t old_umask;
+	int fd, ret;
+
+	old_umask = umask((set->mode ^ 0777) & 0777);
+	ret = mkfifo(set->path, set->mode);
+	umask(old_umask);
+
+	if (ret < 0 && errno != EEXIST) {
+		i_error("service(%s): mkfifo(%s) failed: %m",
+			service->name, set->path);
+		return -1;
+	}
+
+	fd = open(set->path, O_RDONLY);
+	if (fd == -1) {
+		i_error("service(%s): open(%s) failed: %m",
+			service->name, set->path);
+		return -1;
+	}
+
+	/* see if we need to change its owner/group */
+	if ((service->uid != (uid_t)-1 && service->uid != master_uid) ||
+	    (service->gid != (gid_t)-1 && service->gid != master_gid)) {
+		if (chown(set->path, service->uid, service->gid) < 0) {
+			i_error("chown(%s, %s, %s) failed: %m", set->path,
+				dec2str(service->uid), dec2str(service->gid));
+			(void)close(fd);
+			return -1;
+		}
+	}
+
+	fd_set_nonblock(fd, TRUE);
+	fd_close_on_exec(fd, TRUE);
+
+	l->fd = fd;
+	return 1;
+}
+
+static int service_inet_listener_listen(struct service_listener *l)
+{
+        struct service *service = l->service;
+	const struct inet_listener_settings *set = l->set.inetset.set;
+	unsigned int port = set->port;
+	int fd;
+
+	fd = net_listen(&l->set.inetset.ip, &port, service->process_limit);
+	if (fd < 0) {
+		i_error("service(%s): listen(%s, %u) failed: %m",
+			service->name, set->address, set->port);
+		return errno == EADDRINUSE ? 0 : -1;
+	}
+	net_set_nonblock(fd, TRUE);
+	fd_close_on_exec(fd, TRUE);
+
+	l->fd = fd;
+	return 1;
+}
+
+static int service_listen(struct service *service)
+{
+	struct service_listener *const *listeners;
+	unsigned int i, count;
+	int ret = 1, ret2 = 0;
+
+	listeners = array_get(&service->listeners, &count);
+	for (i = 0; i < count; i++) {
+		if (listeners[i]->fd != -1)
+			continue;
+
+		switch (listeners[i]->type) {
+		case SERVICE_LISTENER_UNIX:
+			ret2 = service_unix_listener_listen(listeners[i]);
+			break;
+		case SERVICE_LISTENER_FIFO:
+			ret2 = service_fifo_listener_listen(listeners[i]);
+			break;
+		case SERVICE_LISTENER_INET:
+			ret2 = service_inet_listener_listen(listeners[i]);
+			break;
+		}
+
+		if (ret2 < ret)
+			ret = ret2;
+	}
+
+	return ret;
+}
+
+int services_listen(struct service_list *service_list)
+{
+	struct service *const *services;
+	unsigned int i, count;
+	int ret = 1, ret2;
+
+	services = array_get(&service_list->services, &count);
+	for (i = 0; i < count; i++) {
+		ret2 = service_listen(services[i]);
+		if (ret2 < ret)
+			ret = ret2;
+	}
+	return ret;
+}
+
+static int listener_equals(const struct service_listener *l1,
+			   const struct service_listener *l2)
+{
+	if (l1->type != l2->type)
+		return FALSE;
+
+	switch (l1->type) {
+	case SERVICE_LISTENER_UNIX:
+	case SERVICE_LISTENER_FIFO:
+		if (strcmp(l1->set.fileset.set->path,
+			   l2->set.fileset.set->path) != 0)
+			return FALSE;
+		if (l1->set.fileset.set->mode != l2->set.fileset.set->mode)
+			return FALSE;
+		if (l1->set.fileset.uid != l2->set.fileset.uid)
+			return FALSE;
+		if (l1->set.fileset.gid != l2->set.fileset.gid)
+			return FALSE;
+		return TRUE;
+	case SERVICE_LISTENER_INET:
+		if (memcmp(&l1->set.inetset.ip, &l2->set.inetset.ip,
+			   sizeof(l1->set.inetset.ip)) != 0)
+			return FALSE;
+		if (l1->set.inetset.set->port != l2->set.inetset.set->port)
+			return FALSE;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+int services_listen_using(struct service_list *new_service_list,
+			  struct service_list *old_service_list)
+{
+	struct service *const *services;
+	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;
+
+	/* 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);
+	for (i = 0; i < count; i++)
+		array_append_array(&new_listeners_arr, &services[i]->listeners);
+
+	t_array_init(&old_listeners_arr, 64);
+	services = array_get(&old_service_list->services, &count);
+	for (i = 0; i < count; i++)
+		array_append_array(&old_listeners_arr, &services[i]->listeners);
+
+	/* then start moving fds */
+	new_listeners = array_get(&new_listeners_arr, &new_count);
+	old_listeners = array_get(&old_listeners_arr, &old_count);
+
+	for (i = 0; i < new_count; i++) {
+		for (j = 0; j < old_count; j++) {
+			if (old_listeners[j]->fd != -1 &&
+			    listener_equals(new_listeners[i],
+					    old_listeners[j])) {
+				new_listeners[i]->fd = old_listeners[j]->fd;
+                                old_listeners[j]->fd = -1;
+				break;
+			}
+		}
+	}
+
+	/* 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");
+		}
+	}
+
+	/* and let services_listen() deal with the remaining fds */
+	return services_listen(new_service_list);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-listen.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,16 @@
+#ifndef SERVICE_LISTEN_H
+#define SERVICE_LISTEN_H
+
+/* Start listening in all services. Returns -1 for fatal failures,
+   0 if some of the addresses are already being used or path for
+   unix socket was lost, 1 if all is ok. It's safe to call this function
+   multiple times. */
+int services_listen(struct service_list *service_list);
+
+/* Move common listener fds from old_services to new_services, close those
+   that aren't needed anymore and finally call services_listen() to add
+   missing listeners. */
+int services_listen_using(struct service_list *new_service_list,
+			  struct service_list *old_service_list);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-log.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,101 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "fd-close-on-exec.h"
+#include "array.h"
+#include "service.h"
+#include "service-log.h"
+
+#include <unistd.h>
+
+int services_log_init(struct service_list *service_list)
+{
+	struct log_service_handshake handshake;
+	struct service *const *services;
+	unsigned int i, count;
+	buffer_t *handshake_buf;
+	ssize_t ret = 0;
+
+	memset(&handshake, 0, sizeof(handshake));
+	handshake.log_magic = MASTER_LOG_MAGIC;
+
+	handshake_buf = buffer_create_dynamic(default_pool, 256);
+	services = array_get(&service_list->services, &count);
+	for (i = 0; i < count; i++) {
+		if (services[i]->type == SERVICE_TYPE_LOG)
+			continue;
+
+		i_assert(services[i]->log_fd[0] == -1);
+		if (pipe(services[i]->log_fd) < 0) {
+			i_error("pipe() failed: %m");
+			ret = -1;
+			break;
+		}
+		fd_close_on_exec(services[i]->log_fd[0], TRUE);
+		fd_close_on_exec(services[i]->log_fd[1], TRUE);
+
+		handshake.prefix_len = strlen(services[i]->name) + 2;
+
+		buffer_set_used_size(handshake_buf, 0);
+		buffer_append(handshake_buf, &handshake, sizeof(handshake));
+		buffer_append(handshake_buf, services[i]->name,
+			      strlen(services[i]->name));
+		buffer_append(handshake_buf, ": ", 2);
+
+		ret = write(services[i]->log_fd[1],
+			    handshake_buf->data, handshake_buf->used);
+		if (ret < 0) {
+			i_error("write(log handshake) failed: %m");
+			break;
+		}
+		if ((size_t)ret != handshake_buf->used) {
+			i_error("write(log handshake) didn't write everything");
+			ret = -1;
+			break;
+		}
+	}
+	buffer_free(&handshake_buf);
+	if (ret < 0) {
+		services_log_deinit(service_list);
+		return -1;
+	}
+	return 0;
+}
+
+void services_log_deinit(struct service_list *service_list)
+{
+	struct service *const *services;
+	unsigned int i, count;
+
+	services = array_get(&service_list->services, &count);
+	for (i = 0; i < count; i++) {
+		if (services[i]->log_fd[0] != -1) {
+			if (close(services[i]->log_fd[0]) < 0) {
+				i_error("service(%s): close(log_fd) failed: %m",
+					services[i]->name);
+			}
+			if (close(services[i]->log_fd[1]) < 0) {
+				i_error("service(%s): close(log_fd) failed: %m",
+					services[i]->name);
+			}
+			services[i]->log_fd[0] = -1;
+			services[i]->log_fd[1] = -1;
+		}
+	}
+}
+
+void services_log_dup2(ARRAY_TYPE(dup2) *dups,
+		       struct service_list *service_list,
+		       unsigned int first_fd, unsigned int *fd_count)
+{
+	struct service *const *services;
+	unsigned int i, n, count;
+
+	services = array_get(&service_list->services, &count);
+	for (i = n = 0; i < count; i++) {
+		if (services[i]->log_fd[1] != -1) {
+			dup2_append(dups, services[i]->log_fd[0], first_fd + n);
+			n++; *fd_count += 1;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-log.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,13 @@
+#ifndef SERVICE_LOG_H
+#define SERVICE_LOG_H
+
+#include "dup2-array.h"
+
+int services_log_init(struct service_list *service_list);
+void services_log_deinit(struct service_list *service_list);
+
+void services_log_dup2(ARRAY_TYPE(dup2) *dups,
+		       struct service_list *service_list,
+		       unsigned int first_fd, unsigned int *fd_count);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-monitor.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,274 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "array.h"
+#include "ioloop.h"
+#include "fd-close-on-exec.h"
+#include "hash.h"
+#include "service.h"
+#include "service-process.h"
+#include "service-log.h"
+#include "service-monitor.h"
+
+#include <unistd.h>
+#include <sys/wait.h>
+#include <syslog.h>
+
+#define THROTTLE_TIMEOUT (1000*60)
+
+static void service_monitor_stop(struct service *service);
+static void service_monitor_listen_start(struct service *service);
+static void service_monitor_listen_stop(struct service *service);
+
+static void service_status_input(struct service *service)
+{
+        struct master_status status;
+        struct service_process *process;
+	ssize_t ret;
+
+	ret = read(service->status_fd[0], &status, sizeof(status));
+	switch (ret) {
+	case 0:
+		i_error("service(%s): read(status) failed: EOF", service->name);
+		service_monitor_stop(service);
+		return;
+	case -1:
+		i_error("service(%s): read(status) failed: %m", service->name);
+		service_monitor_stop(service);
+		return;
+	default:
+		i_error("service(%s): child %s sent partial status update "
+			"(%d bytes)", service->name,
+			dec2str(status.pid), (int)ret);
+		return;
+
+	case sizeof(status):
+		break;
+	}
+
+	process = hash_table_lookup(service->list->pids, &status.pid);
+	if (process == NULL) {
+		/* we've probably wait()ed it away already. ignore */
+		return;
+	}
+
+	if (process->uid != status.uid || process->service != service) {
+		/* a) Process was closed and another process was created with
+		   the same PID, but we're still receiving status update from
+		   the old process.
+
+		   b) Some process is trying to corrupt our internal state by
+		   trying to pretend to be someone else. We could use stronger
+		   randomness here, but the worst they can do is DoS and there
+		   are already more serious problems if someone is able to do
+		   this.. */
+		i_error("service(%s): Ignoring invalid update from child %s "
+			"(UID=%u)", service->name, dec2str(status.pid),
+			status.uid);
+		return;
+	}
+
+	if (process->to_status != NULL) {
+		/* first status notification */
+		timeout_remove(&process->to_status);
+	}
+
+	if (process->available_count == status.available_count)
+		return;
+
+	if (process->available_count > status.available_count) {
+		/* process started servicing requests */
+		process->total_count +=
+			process->available_count - status.available_count;
+		if (status.available_count == 0) {
+			i_assert(service->process_avail > 0);
+			if (--service->process_avail == 0)
+                                service_monitor_listen_start(service);
+		}
+		process->idle_start = 0;
+	} else {
+		/* process finished servicing requests */
+		if (process->available_count == 0) {
+			if (service->process_avail++ == 0)
+                                service_monitor_listen_stop(service);
+			i_assert(service->process_avail <=
+				 service->process_count);
+		}
+		if (status.available_count == service->set->client_limit)
+			process->idle_start = ioloop_time;
+	}
+	process->available_count = status.available_count;
+}
+
+static void service_throttle_timeout(struct service *service)
+{
+	timeout_remove(&service->to_throttle);
+	service_monitor_listen_start(service);
+}
+
+static void service_monitor_throttle(struct service *service)
+{
+	if (service->to_throttle != NULL)
+		return;
+
+	i_error("service(%s): command startup failed, throttling",
+		service->name);
+	service_monitor_listen_stop(service);
+
+	service->to_throttle = timeout_add(THROTTLE_TIMEOUT,
+					   service_throttle_timeout, service);
+}
+
+static void service_accept(struct service *service)
+{
+	i_assert(service->process_avail == 0);
+
+	if (service->process_count == service->process_limit) {
+		/* we've reached our limits, new connections will have to
+		   wait until there are more processes available */
+		service->listen_pending = TRUE;
+                service_monitor_listen_stop(service);
+		return;
+	}
+
+	/* create a child process and let it accept() this connection */
+	if (service_process_create(service, NULL, -1, NULL, 0) == NULL)
+		service_monitor_throttle(service);
+	else
+		service_monitor_listen_stop(service);
+}
+
+static void service_monitor_listen_start(struct service *service)
+{
+	struct service_listener *const *listeners;
+	unsigned int i, count;
+
+	service->listen_pending = FALSE;
+
+	listeners = array_get(&service->listeners, &count);
+	for (i = 0; i < count; i++) {
+		if (listeners[i]->io == NULL && listeners[i]->fd != -1) {
+			listeners[i]->io = io_add(listeners[i]->fd, IO_READ,
+						  service_accept, service);
+		}
+	}
+}
+
+static void service_monitor_listen_stop(struct service *service)
+{
+	struct service_listener *const *listeners;
+	unsigned int i, count;
+
+	listeners = array_get(&service->listeners, &count);
+	for (i = 0; i < count; i++) {
+		struct service_listener *l = listeners[i];
+
+		if (l->io != NULL)
+			io_remove(&l->io);
+	}
+}
+
+void services_monitor_start(struct service_list *service_list)
+{
+	struct service *const *services;
+	unsigned int i, count;
+
+	services_log_init(service_list);
+
+	services = array_get(&service_list->services, &count);
+	for (i = 0; i < count; i++) {
+		if (services[i]->status_fd[0] == -1) {
+			/* we haven't yet created status pipe */
+			if (pipe(services[i]->status_fd) < 0) {
+				i_error("service(%s): pipe() failed: %m",
+					services[i]->name);
+				continue;
+			}
+
+			net_set_nonblock(services[i]->status_fd[0], TRUE);
+			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);
+
+			services[i]->io_status =
+				io_add(services[i]->status_fd[0], IO_READ,
+				       service_status_input, services[i]);
+		}
+
+		if (services[i]->status_fd[0] != -1)
+			service_monitor_listen_start(services[i]);
+	}
+
+	if (service_process_create(service_list->log, NULL, -1, NULL, 0) != NULL)
+		service_monitor_listen_stop(service_list->log);
+	if (service_process_create(service_list->config, NULL, -1, NULL, 0) != NULL)
+		service_monitor_listen_stop(service_list->config);
+}
+
+static void service_monitor_stop(struct service *service)
+{
+	int i;
+
+	if (service->io_status != NULL)
+		io_remove(&service->io_status);
+
+	if (service->status_fd[0] != -1) {
+		for (i = 0; i < 2; i++) {
+			if (close(service->status_fd[i]) < 0) {
+				i_error("service(%s): close(%d) failed: %m",
+					service->name, i);
+			}
+                        service->status_fd[i] = -1;
+		}
+	}
+	service_monitor_listen_stop(service);
+
+	if (service->to_throttle != NULL)
+		timeout_remove(&service->to_throttle);
+}
+
+void services_monitor_stop(struct service_list *service_list)
+{
+	struct service *const *services;
+	unsigned int i, count;
+
+	services = array_get(&service_list->services, &count);
+	for (i = 0; i < count; i++)
+		service_monitor_stop(services[i]);
+
+	services_log_deinit(service_list);
+}
+
+void services_monitor_reap_children(struct service_list *service_list)
+{
+	struct service_process *process;
+	struct service *service;
+	pid_t pid;
+	int status;
+
+	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+		process = hash_table_lookup(service_list->pids, &pid);
+		if (process == NULL) {
+			i_error("waitpid() returned unknown PID %s",
+				dec2str(pid));
+			continue;
+		}
+
+		service = process->service;
+		if (status == 0) {
+			/* success */
+			if (service->listen_pending)
+				service_monitor_listen_start(service);
+		} else {
+			/* failure */
+			service_process_log_status_error(process, status);
+			if (process->total_count == 0)
+				service_monitor_throttle(service);
+		}
+
+		service_process_destroy(process);
+
+                if (service->process_avail == 0 && service->to_throttle == NULL)
+			service_monitor_listen_start(service);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-monitor.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,13 @@
+#ifndef SERVICE_MONITOR_H
+#define SERVICE_MONITOR_H
+
+/* Start listening and monitoring services. */
+void services_monitor_start(struct service_list *service_list);
+
+/* Stop services. */
+void services_monitor_stop(struct service_list *service_list);
+
+/* Call after SIGCHLD has been detected */
+void services_monitor_reap_children(struct service_list *service_list);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-process.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,567 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "array.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "write-full.h"
+#include "base64.h"
+#include "hash.h"
+#include "str.h"
+#include "hostpid.h"
+#include "env-util.h"
+#include "fd-close-on-exec.h"
+#include "restrict-access.h"
+#include "master-service-settings.h"
+#include "dup2-array.h"
+#include "service.h"
+#include "service-log.h"
+#include "service-auth-server.h"
+#include "service-auth-source.h"
+#include "service-process.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+static const char **
+service_dup_fds(struct service *service, int auth_fd, int std_fd)
+{
+	struct service_listener *const *listeners;
+	ARRAY_TYPE(dup2) dups;
+	unsigned int i, count, n, socket_listener_count;
+
+	/* stdin/stdout is already redirected to /dev/null. Other master fds
+	   should have been opened with fd_close_on_exec() so we don't have to
+	   worry about them.
+
+	   because the destination fd might be another one's source fd we have
+	   to be careful not to overwrite anything. dup() the fd when needed */
+
+        socket_listener_count = 0;
+	listeners = array_get(&service->listeners, &count);
+	t_array_init(&dups, count + 4);
+	for (i = n = 0; i < count; i++) {
+		if (listeners[i]->fd == -1)
+			continue;
+
+		dup2_append(&dups, listeners[i]->fd,
+			    MASTER_LISTEN_FD_FIRST + n);
+		n++; socket_listener_count++;
+	}
+
+	dup2_append(&dups, null_fd, MASTER_RESERVED_FD);
+	dup2_append(&dups, service->status_fd[1], MASTER_STATUS_FD);
+
+	switch (service->type) {
+	case SERVICE_TYPE_AUTH_SOURCE:
+	case SERVICE_TYPE_AUTH_SERVER:
+		i_assert(auth_fd != -1);
+		dup2_append(&dups, auth_fd, MASTER_AUTH_FD);
+		env_put(t_strdup_printf("MASTER_AUTH_FD=%d", MASTER_AUTH_FD));
+		break;
+	case SERVICE_TYPE_LOG:
+		services_log_dup2(&dups, service->list,
+				  MASTER_LISTEN_FD_FIRST + n,
+				  &socket_listener_count);
+		/* fall through */
+	default:
+		i_assert(auth_fd == -1);
+		dup2_append(&dups, null_fd, MASTER_AUTH_FD);
+		break;
+	}
+
+	if (std_fd != -1) {
+		dup2_append(&dups, std_fd, STDIN_FILENO);
+		dup2_append(&dups, std_fd, STDOUT_FILENO);
+		env_put("LOGGED_IN=1");
+	}
+
+	if (service->type != SERVICE_TYPE_LOG) {
+		/* set log file to stderr. dup2() here immediately so that
+		   we can set up logging to it without causing any log messages
+		   to be lost. */
+		i_assert(service->log_fd[1] != -1);
+
+		env_put("LOG_SERVICE=1");
+		if (dup2(service->log_fd[1], STDERR_FILENO) < 0)
+			i_fatal("dup2(log fd) failed: %m");
+		i_set_failure_internal();
+	} else {
+		dup2_append(&dups, null_fd, STDERR_FILENO);
+	}
+
+	/* make sure we don't leak syslog fd. try to do it as late as possible,
+	   but also before dup2()s in case syslog fd is one of them. */
+	closelog();
+
+	if (dup2_array(&dups) < 0)
+		i_fatal("service(%s): dup2s failed", service->name);
+
+#ifdef DEBUG
+	env_put(t_strdup_printf("SOCKET_COUNT=%d", socket_listener_count));
+#endif
+
+	if (socket_listener_count == 1)
+		return NULL;
+	else {
+		const char **args = t_new(const char *, 3);
+		args[0] = "-s";
+		args[1] = dec2str(socket_listener_count);
+		return args;
+	}
+}
+
+static int validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid,
+			    const char *user)
+{
+	if (uid == 0) {
+		i_error("Logins with UID 0 not permitted (user %s)", user);
+		return FALSE;
+	}
+
+	if (uid < (uid_t)set->first_valid_uid ||
+	    (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
+		i_error("Logins with UID %s (user %s) not permitted "
+			"(see first_valid_uid in config file)",
+			dec2str(uid), user);
+		return FALSE;
+	}
+
+	if (gid < (gid_t)set->first_valid_gid ||
+	    (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
+		i_error("Logins for users with primary group ID %s (user %s) "
+			"not permitted (see first_valid_gid in config file).",
+			dec2str(gid), user);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void auth_args_apply(const char *const *args,
+			    struct restrict_access_settings *rset,
+			    const char **home)
+{
+	for (; *args != NULL; args++) {
+		if (strncmp(*args, "uid=", 4) == 0)
+			rset->uid = (uid_t)strtoul(*args + 4, NULL, 10);
+		else if (strncmp(*args, "gid=", 4) == 0)
+			rset->gid = (gid_t)strtoul(*args + 4, NULL, 10);
+		else if (strncmp(*args, "home=", 5) == 0) {
+			*home = *args + 5;
+			env_put(t_strconcat("HOME=", *args + 5, NULL));
+		} else if (strncmp(*args, "chroot=", 7) == 0)
+			rset->chroot_dir = *args + 7;
+		else if (strncmp(*args, "system_groups_user=", 19) == 0)
+			rset->system_groups_user = *args + 19;
+		else if (strncmp(*args, "mail_access_groups=", 19) == 0) {
+			rset->extra_groups =
+				rset->extra_groups == NULL ? *args + 19 :
+				t_strconcat(*args + 19, ",",
+					    rset->extra_groups, NULL);
+		} else {
+			/* unknown, set as environment */
+			//FIXME
+			env_put(t_strconcat("set_", *args, NULL));
+		}
+	}
+}
+
+static void drop_privileges(struct service *service,
+			    const char *const *auth_args)
+{
+	struct master_settings *master_set = service->set->master_set;
+	struct restrict_access_settings rset;
+	const char *user, *home = NULL;
+
+	restrict_access_init(&rset);
+	rset.uid = service->uid;
+	rset.gid = service->gid;
+	rset.privileged_gid = service->privileged_gid;
+	rset.chroot_dir = *service->set->chroot == '\0' ? NULL :
+		service->set->chroot;
+	rset.extra_groups = service->extra_gids;
+
+	if (auth_args == NULL) {
+		/* non-authenticating service. don't use *_valid_gid checks */
+	} else {
+		i_assert(auth_args[0] != NULL);
+
+		rset.first_valid_gid = master_set->first_valid_gid;
+		rset.last_valid_gid = master_set->last_valid_gid;
+
+		user = auth_args[0];
+		env_put(t_strconcat("USER=", user, NULL));
+
+		auth_args_apply(auth_args + 1, &rset, &home);
+
+		if (!validate_uid_gid(master_set, rset.uid, rset.gid, user))
+			exit(FATAL_DEFAULT);
+	}
+
+	if (home != NULL) {
+		// FIXME: warn ENOENT if mail_debug=yes
+		if (chdir(home) < 0 && errno != ENOENT)
+			i_error("chdir(%s) failed: %m", home);
+	}
+
+	if (service->set->drop_priv_before_exec)
+		restrict_access(&rset, home, FALSE); //FIXME: disallow_root?
+	else
+		restrict_access_set_env(&rset);
+}
+
+static void
+service_process_setup_environment(struct service *service, unsigned int uid)
+{
+	const struct master_service_settings *set;
+        struct service_listener *const *listeners;
+	const char *const *p;
+	unsigned int limit, count;
+
+	/* remove all environment, and put back what we need */
+	env_clean();
+	for (p = service->list->child_process_env; *p != NULL; p++)
+		env_put(*p);
+
+	switch (service->type) {
+	case SERVICE_TYPE_CONFIG:
+		env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
+				    service->config_file_path, NULL));
+		break;
+	case SERVICE_TYPE_LOG:
+		/* give the log's configuration directly, so it won't depend
+		   on config process */
+		set = master_service_settings_get(master_service);
+		env_put("DOVECONF_ENV=1");
+		env_put(t_strconcat("LOG_PATH=", set->log_path, NULL));
+		env_put(t_strconcat("INFO_LOG_PATH=", set->info_log_path, NULL));
+		env_put(t_strconcat("LOG_TIMESTAMP=", set->log_timestamp, NULL));
+		env_put(t_strconcat("SYSLOG_FACILITY=", set->syslog_facility, NULL));
+		break;
+	default:
+		listeners = array_get(&service->list->config->listeners,
+				      &count);
+		i_assert(count > 0);
+		env_put(t_strconcat(MASTER_CONFIG_FILE_ENV"=",
+				    listeners[0]->set.fileset.set->path, NULL));
+		break;
+	}
+
+	limit = service->set->client_limit;
+	if (limit == 0) {
+		/* fallback to default limit */
+		limit = service->set->master_set->default_client_limit;
+	}
+
+	env_put(t_strdup_printf(MASTER_CLIENT_LIMIT_ENV"=%u", limit));
+	env_put(t_strdup_printf(MASTER_UID_ENV"=%u", uid));
+
+	if (!service->set->master_set->version_ignore)
+		env_put(MASTER_DOVECOT_VERSION_ENV"="PACKAGE_VERSION);
+}
+
+static void service_process_status_timeout(struct service_process *process)
+{
+	i_error("service(%s): Initial status notification not received in %d "
+		"seconds, killing the process", process->service->name,
+		SERVICE_FIRST_STATUS_TIMEOUT_SECS);
+	if (kill(process->pid, SIGKILL) < 0 && errno != ESRCH) {
+		i_error("service(%s): kill(%s, SIGKILL) failed: %m",
+			process->service->name, dec2str(process->pid));
+	}
+	timeout_remove(&process->to_status);
+}
+
+struct service_process *
+service_process_create(struct service *service, const char *const *auth_args,
+		       int std_fd, const unsigned char *data, size_t data_size)
+{
+	static unsigned int uid_counter = 0;
+	struct service_process *process;
+	unsigned int uid = ++uid_counter;
+	string_t *str;
+	int fd[2];
+	pid_t pid;
+
+	switch (service->type) {
+	case SERVICE_TYPE_AUTH_SOURCE:
+	case SERVICE_TYPE_AUTH_SERVER:
+		if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
+			i_error("service(%s): socketpair() failed: %m",
+				service->name);
+			return NULL;
+		}
+		fd_close_on_exec(fd[0], TRUE);
+		fd_close_on_exec(fd[1], TRUE);
+		break;
+	default:
+		fd[0] = fd[1] = -1;
+		break;
+	}
+
+	pid = fork();
+	if (pid < 0) {
+		i_error("service(%s): fork() failed: %m", service->name);
+		if (fd[0] != -1) {
+			(void)close(fd[0]);
+			(void)close(fd[1]);
+		}
+		return NULL;
+	}
+	if (pid == 0) {
+		/* child */
+		const char **args;
+
+		if (fd[0] != -1)
+			(void)close(fd[0]);
+		service_process_setup_environment(service, uid);
+		if (data_size > 0) {
+			str = t_str_new(data_size*3);
+			str_append(str, "CLIENT_INPUT=");
+			base64_encode(data, data_size, str);
+			env_put(str_c(str));
+		}
+		args = service_dup_fds(service, fd[1], std_fd);
+		drop_privileges(service, auth_args);
+		process_exec(service->executable, args);
+	}
+
+	switch (service->type) {
+	case SERVICE_TYPE_AUTH_SERVER:
+		process = i_malloc(sizeof(struct service_process_auth_server));
+		process->service = service;
+		service_process_auth_server_init(process, fd[0]);
+		(void)close(fd[1]);
+		break;
+	case SERVICE_TYPE_AUTH_SOURCE:
+		process = i_malloc(sizeof(struct service_process_auth_source));
+		process->service = service;
+		service_process_auth_source_init(process, fd[0]);
+		(void)close(fd[1]);
+		break;
+	default:
+		process = i_new(struct service_process, 1);
+		process->service = service;
+		i_assert(fd[0] == -1);
+		break;
+	}
+		
+	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);
+
+	process->available_count = service->set->client_limit;
+	if (process->available_count == 0) {
+		/* fallback to default limit */
+		process->available_count =
+			service->set->master_set->default_client_limit;
+	}
+
+	service->process_count++;
+	service->process_avail++;
+
+	hash_table_insert(service->list->pids, &process->pid, process);
+	return process;
+}
+
+void service_process_destroy(struct service_process *process)
+{
+	struct service *service = process->service;
+	const char *data;
+
+	hash_table_remove(service->list->pids, &process->pid);
+
+	if (process->available_count > 0)
+		service->process_avail--;
+	service->process_count--;
+	i_assert(service->process_avail <= service->process_count);
+
+	if (process->to_status != NULL)
+		timeout_remove(&process->to_status);
+
+	switch (process->service->type) {
+	case SERVICE_TYPE_AUTH_SERVER:
+		service_process_auth_server_deinit(process);
+		break;
+	case SERVICE_TYPE_AUTH_SOURCE:
+		service_process_auth_source_deinit(process);
+		break;
+	default:
+		break;
+	}
+
+	data = t_strdup_printf("\001%c%s bye\n",
+			       LOG_TYPE_OPTION+1, dec2str(process->pid));
+	if (write(process->service->log_fd[1], data, strlen(data)) < 0) {
+		if (errno != EAGAIN)
+			i_error("write(log process) failed: %m");
+		else {
+			//FIXME:process->io_log_write = io_add();
+			//return;
+		}
+	}
+
+	process->destroyed = TRUE;
+	service_process_unref(process);
+}
+
+void service_process_ref(struct service_process *process)
+{
+	i_assert(process->refcount > 0);
+
+	process->refcount++;
+}
+
+int service_process_unref(struct service_process *process)
+{
+	i_assert(process->refcount > 0);
+
+	if (--process->refcount > 0)
+		return TRUE;
+
+	i_assert(process->destroyed);
+
+	i_free(process);
+	return FALSE;
+}
+
+static const char *
+get_exit_status_message(struct service *service, enum fatal_exit_status status)
+{
+	switch (status) {
+	case FATAL_LOGOPEN:
+		return "Can't open log file";
+	case FATAL_LOGWRITE:
+		return "Can't write to log file";
+	case FATAL_LOGERROR:
+		return "Internal logging error";
+	case FATAL_OUTOFMEM:
+		if (service->set->vsz_limit == 0)
+			return "Out of memory";
+		return t_strdup_printf("Out of memory (vsz_limit=%u MB)",
+				       service->set->vsz_limit);
+	case FATAL_EXEC:
+		return "exec() failed";
+
+	case FATAL_DEFAULT:
+		return "Fatal failure";
+	}
+
+	return NULL;
+}
+
+static void log_coredump(struct service *service, string_t *str, int status)
+{
+#ifdef WCOREDUMP
+	int signum = WTERMSIG(status);
+
+	if (WCOREDUMP(status)) {
+		str_append(str, " (core dumped)");
+		return;
+	}
+
+	if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
+		return;
+
+	/* let's try to figure out why we didn't get a core dump */
+	if (core_dumps_disabled) {
+		str_printfa(str, " (core dumps disabled)");
+		return;
+	}
+
+#ifdef HAVE_PR_SET_DUMPABLE
+	if (!service->set->drop_priv_before_exec) {
+		str_append(str, " (core not dumped - set drop_priv_before_exec=yes)");
+		return;
+	}
+	if (*service->set->privileged_group != '\0') {
+		str_append(str, " (core not dumped - privileged_group prevented it)");
+		return;
+	}
+#endif
+
+	str_append(str, " (core not dumped)");
+#endif
+}
+
+static void
+service_process_get_status_error(string_t *str, struct service_process *process,
+				 int status, enum log_type *type_r)
+{
+	struct service *service = process->service;
+	const char *msg;
+
+	*type_r = LOG_TYPE_ERROR;
+
+	str_printfa(str, "service(%s): child %s ", service->name,
+		    dec2str(process->pid));
+	if (WIFSIGNALED(status)) {
+		str_printfa(str, "killed with signal %d", WTERMSIG(status));
+		log_coredump(service, str, status);
+		return;
+	}
+	if (!WIFEXITED(status)) {
+		str_printfa(str, "died with status %d", status);
+		return;
+	}
+
+	status = WEXITSTATUS(status);
+	if (status == 0) {
+		str_truncate(str, 0);
+		return;
+	}
+	str_printfa(str, "returned error %d", status);
+
+	msg = get_exit_status_message(service, status);
+	if (msg != NULL)
+		str_printfa(str, " (%s)", msg);
+
+	if (status == FATAL_DEFAULT)
+		*type_r = LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL;
+}
+
+static void service_process_log(struct service_process *process,
+				enum log_type type, const char *str)
+{
+	const char *data;
+
+	if (type != LOG_TYPE_ERROR_IGNORE_IF_SEEN_FATAL ||
+	    process->service->log_fd[1] == -1) {
+		i_log_type(type, "%s", str);
+		return;
+	}
+
+	/* log it via the log process in charge of handling
+	   this process's logging */
+	data = t_strdup_printf("\001%c%s %s %s\n",
+			       type+1, my_pid, dec2str(process->pid), str);
+	if (write_full(process->service->log_fd[1], data, strlen(data)) < 0) {
+		i_error("write(log process) failed: %m");
+		i_log_type(type, "%s", str);
+	}
+}
+
+void service_process_log_status_error(struct service_process *process,
+				      int status)
+{
+	if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+		/* fast path */
+		return;
+	}
+	T_BEGIN {
+		string_t *str = t_str_new(256);
+		enum log_type type;
+
+		service_process_get_status_error(str, process, status, &type);
+		if (str_len(str) > 0)
+			service_process_log(process, type, str_c(str));
+	} T_END;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service-process.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,76 @@
+#ifndef SERVICE_PROCESS_H
+#define SERVICE_PROCESS_H
+
+struct service_process {
+	struct service *service;
+	int refcount;
+
+	pid_t pid;
+        /* uid is used to check for old/invalid status messages */
+	unsigned int uid;
+
+	/* number of new connections process is currently accepting */
+	unsigned int available_count;
+	/* number of connections process has ever accepted */
+	unsigned int total_count;
+
+	/* time when process started idling, or 0 if we're not idling */
+	time_t idle_start;
+
+	/* kill the process if it doesn't send initial status notification */
+	struct timeout *to_status;
+
+	unsigned int destroyed:1;
+};
+
+struct service_process_auth_server {
+	struct service_process process;
+
+	int auth_fd;
+	struct io *io_auth;
+	struct ostream *auth_output;
+	struct istream *auth_input;
+
+	/* pending authentication requests that are being verified from
+	   auth server. */
+	struct hash_table *auth_requests;
+	/* Last time we wrote "authentication server is too busy" to log */
+	time_t auth_busy_stamp;
+	/* Tag counter for outgoing requests */
+	unsigned int auth_tag_counter;
+
+	unsigned int auth_version_sent:1;
+	unsigned int auth_version_received:1;
+};
+
+struct service_process_auth_source {
+	struct service_process process;
+
+	int auth_fd;
+	struct io *io_auth;
+	struct ostream *auth_output;
+};
+
+struct service_process_auth_request {
+	struct service_process_auth_source *process;
+
+	unsigned int process_tag;
+	int fd;
+
+	struct ip_addr local_ip, remote_ip;
+	unsigned int data_size;
+	unsigned char data[FLEXIBLE_ARRAY_MEMBER];
+};
+
+struct service_process *
+service_process_create(struct service *service, const char *const *auth_args,
+		       int std_fd, const unsigned char *data, size_t data_size);
+void service_process_destroy(struct service_process *process);
+
+void service_process_ref(struct service_process *process);
+int service_process_unref(struct service_process *process);
+
+void service_process_log_status_error(struct service_process *process,
+				      int status);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service.c	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,449 @@
+/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "common.h"
+#include "array.h"
+#include "hash.h"
+#include "str.h"
+#include "service.h"
+#include "service-process.h"
+#include "service-monitor.h"
+
+#include <unistd.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+
+static int get_uid(const char *user, uid_t *uid_r, const char **error_r)
+{
+	struct passwd *pw;
+
+	if (*user == '\0') {
+		*uid_r = (uid_t)-1;
+		return 0;
+	}
+
+	if ((pw = getpwnam(user)) == NULL) {
+		*error_r = t_strdup_printf("User doesn't exist: %s", user);
+		return -1;
+	}
+
+	*uid_r = pw->pw_uid;
+	return 0;
+}
+
+static int get_gid(const char *group, gid_t *gid_r, const char **error_r)
+{
+	struct group *gr;
+
+	if (*group == '\0') {
+		*gid_r = (uid_t)-1;
+		return 0;
+	}
+
+	if ((gr = getgrnam(group)) == NULL) {
+		*error_r = t_strdup_printf("Group doesn't exist: %s", group);
+		return -1;
+	}
+
+	*gid_r = gr->gr_gid;
+	return 0;
+}
+
+static struct service_listener *
+service_create_file_listener(struct service *service,
+			     enum service_listener_type type,
+			     const struct file_listener_settings *set,
+			     const char **error_r)
+{
+	struct service_listener *l;
+
+	l = p_new(service->list->pool, struct service_listener, 1);
+	l->service = service;
+	l->type = type;
+	l->fd = -1;
+	l->set.fileset.set = set;
+
+	if (get_uid(set->user, &l->set.fileset.uid, error_r) < 0)
+		return NULL;
+	if (get_gid(set->group, &l->set.fileset.gid, error_r) < 0)
+		return NULL;
+	return l;
+}
+
+static int
+resolve_ip(const char *address, struct ip_addr *ip_r, const char **error_r)
+{
+	struct ip_addr *ip_list;
+	unsigned int ips_count;
+	int ret;
+
+	if (address == NULL || strcmp(address, "*") == 0) {
+		/* IPv4 any */
+		net_get_ip_any4(ip_r);
+		return 0;
+	}
+
+	if (strcmp(address, "::") == 0) {
+		/* IPv6 any */
+		net_get_ip_any6(ip_r);
+		return 0;
+	}
+
+	/* Return the first IP if there happens to be multiple. */
+	ret = net_gethostbyname(address, &ip_list, &ips_count);
+	if (ret != 0) {
+		*error_r = t_strdup_printf("Can't resolve address %s: %s",
+					   address, net_gethosterror(ret));
+		return -1;
+	}
+
+	if (ips_count < 1) {
+		*error_r = t_strdup_printf("No IPs for address: %s", address);
+		return -1;
+	}
+	if (ips_count > 1) {
+		*error_r = t_strdup_printf("Multiple IPs for address: %s",
+					   address);
+		return -1;
+	}
+
+	*ip_r = ip_list[0];
+	return 0;
+}
+
+static struct service_listener *
+service_create_inet_listener(struct service *service,
+			     const struct inet_listener_settings *set,
+			     const char **error_r)
+{
+	struct service_listener *l;
+
+	l = p_new(service->list->pool, struct service_listener, 1);
+	l->service = service;
+	l->type = SERVICE_LISTENER_INET;
+	l->fd = -1;
+	l->set.inetset.set = set;
+
+	if (resolve_ip(set->address, &l->set.inetset.ip, error_r) < 0)
+		return NULL;
+
+	if (set->port == 0) {
+		*error_r = "Port not given";
+		return NULL;
+	}
+	if (set->port > 65535) {
+		*error_r = t_strdup_printf("Invalid port: %u", set->port);
+		return NULL;
+	}
+
+	return l;
+}
+
+static struct service *
+service_create(pool_t pool, const struct service_settings *set,
+	       struct service_list *service_list, const char **error_r)
+{
+	struct file_listener_settings *const *unix_listeners;
+	struct file_listener_settings *const *fifo_listeners;
+	struct inet_listener_settings *const *inet_listeners;
+	struct service *service;
+        struct service_listener *l;
+	const char *p, *const *tmp;
+	string_t *str;
+	unsigned int i, unix_count, fifo_count, inet_count;
+
+	service = p_new(pool, struct service, 1);
+	service->list = service_list;
+	service->set = set;
+
+	service->type = SERVICE_TYPE_UNKNOWN;
+	if (*set->type != '\0') {
+		service->name = set->type;
+		if (strcmp(set->type, "log") == 0)
+			service->type = SERVICE_TYPE_LOG;
+		else if (strcmp(set->type, "config") == 0)
+			service->type = SERVICE_TYPE_CONFIG;
+		else if (strcmp(set->type, "auth") == 0)
+			service->type = SERVICE_TYPE_AUTH_SERVER;
+		else
+			service->name = NULL;
+	}
+
+	if (*set->auth_dest_service != '\0')
+		service->type = SERVICE_TYPE_AUTH_SOURCE;
+
+	if (set->process_limit == 0) {
+		/* unlimited */
+		service->process_limit = INT_MAX;
+	} else if (set->process_limit == (unsigned int)-1) {
+		/* use default */
+		service->process_limit =
+			set->master_set->default_process_limit;
+	} else {
+		service->process_limit = set->process_limit;
+	}
+
+	if (set->executable == NULL) {
+		*error_r = "executable not given";
+		return NULL;
+	}
+
+	/* get service name from some of the unique types, fallback to
+	   executable name without path and parameters */
+	if (service->name == NULL) {
+		p = strrchr(set->executable, '/');
+		if (p == NULL)
+			service->name = t_strcut(set->executable, ' ');
+		else
+			service->name = t_strcut(p + 1, ' ');
+		service->name = p_strdup(pool, service->name);
+	}
+
+	if (get_uid(set->user, &service->uid, error_r) < 0)
+		return NULL;
+	if (get_gid(set->group, &service->gid, error_r) < 0)
+		return NULL;
+	if (get_gid(set->privileged_group, &service->privileged_gid,
+		    error_r) < 0)
+		return NULL;
+
+	if (*set->extra_groups != '\0') {
+		str = t_str_new(64);
+		tmp = t_strsplit(set->extra_groups, ",");
+		for (; *tmp != NULL; tmp++) {
+			gid_t gid;
+
+			if (get_gid(*tmp, &gid, error_r) < 0)
+				return NULL;
+
+			if (str_len(str) > 0)
+				str_append_c(str, ',');
+			str_append(str, dec2str(gid));
+		}
+		service->extra_gids = p_strdup(pool, str_c(str));
+	}
+
+	if (*set->executable == '/')
+		service->executable = set->executable;
+	else {
+		service->executable =
+			p_strconcat(pool, set->master_set->libexec_dir, "/",
+				    set->executable, NULL);
+	}
+	if (access(t_strcut(service->executable, ' '), X_OK) < 0) {
+		*error_r = t_strdup_printf("access(%s) failed: %m",
+					   t_strcut(service->executable, ' '));
+		return NULL;
+	}
+
+	/* set these later, so if something fails we don't have to worry about
+	   closing them */
+	service->log_fd[0] = -1;
+	service->log_fd[1] = -1;
+	service->status_fd[0] = -1;
+	service->status_fd[1] = -1;
+
+	if (array_is_created(&set->unix_listeners))
+		unix_listeners = array_get(&set->unix_listeners, &unix_count);
+	else {
+		unix_listeners = NULL;
+		unix_count = 0;
+	}
+	if (array_is_created(&set->fifo_listeners))
+		fifo_listeners = array_get(&set->unix_listeners, &fifo_count);
+	else {
+		fifo_listeners = NULL;
+		fifo_count = 0;
+	}
+	if (array_is_created(&set->inet_listeners))
+		inet_listeners = array_get(&set->inet_listeners, &inet_count);
+	else {
+		inet_listeners = NULL;
+		inet_count = 0;
+	}
+
+	if (unix_count == 0 && service->type == SERVICE_TYPE_CONFIG) {
+		*error_r = "Service must have unix listeners";
+		return NULL;
+	}
+
+	p_array_init(&service->listeners, pool,
+		     unix_count + fifo_count + inet_count);
+		     
+	for (i = 0; i < unix_count; i++) {
+		l = service_create_file_listener(service, SERVICE_LISTENER_UNIX,
+						 unix_listeners[i], error_r);
+		if (l == NULL)
+			return NULL;
+		array_append(&service->listeners, &l, 1);
+	}
+	for (i = 0; i < fifo_count; i++) {
+		l = service_create_file_listener(service, SERVICE_LISTENER_UNIX,
+						 fifo_listeners[i], error_r);
+		if (l == NULL)
+			return NULL;
+		array_append(&service->listeners, &l, 1);
+	}
+	for (i = 0; i < inet_count; i++) {
+		l = service_create_inet_listener(service, inet_listeners[i],
+						 error_r);
+		if (l == NULL)
+			return NULL;
+		array_append(&service->listeners, &l, 1);
+	}
+
+	return service;
+}
+
+static unsigned int pid_hash(const void *p)
+{
+	const pid_t *pid = p;
+
+	return (unsigned int)*pid;
+}
+
+static int pid_hash_cmp(const void *p1, const void *p2)
+{
+	const pid_t *pid1 = p1, *pid2 = p2;
+
+	return *pid1 < *pid2 ? -1 :
+		*pid1 > *pid2 ? 1 : 0;
+}
+
+static struct service *
+service_lookup(struct service_list *service_list, const char *name)
+{
+	struct service *const *services;
+	unsigned int i, count;
+
+	services = array_get(&service_list->services, &count);
+	for (i = 0; i < count; i++) {
+		if (strcmp(services[i]->name, name) == 0)
+			return services[i];
+	}
+	return NULL;
+}
+
+struct service_list *
+services_create(const struct master_settings *set,
+		const char *const *child_process_env, const char **error_r)
+{
+	struct service_list *service_list;
+	struct service *service, *const *services;
+	struct service_settings *const *service_settings;
+	pool_t pool;
+	const char *error;
+	unsigned int i, count;
+
+	pool = pool_alloconly_create("services pool", 4096);
+
+	service_list = p_new(pool, struct service_list, 1);
+	service_list->pool = pool;
+	service_list->child_process_env = child_process_env;
+
+	service_settings = array_get(&set->services, &count);
+	p_array_init(&service_list->services, pool, count);
+
+	for (i = 0; i < count; i++) {
+		service = service_create(pool, service_settings[i],
+					 service_list, &error);
+		if (service == NULL) {
+			*error_r = t_strdup_printf("service(%s) %s",
+				service_settings[i]->type == NULL ? "unknown" :
+				service_settings[i]->type, error);
+			return NULL;
+		}
+
+		switch (service->type) {
+		case SERVICE_TYPE_LOG:
+			if (service_list->log != NULL) {
+				*error_r = "Multiple log services specified";
+				return NULL;
+			}
+			service_list->log = service;
+			break;
+		case SERVICE_TYPE_CONFIG:
+			if (service_list->config != NULL) {
+				*error_r = "Multiple config services specified";
+				return NULL;
+			}
+			service_list->config = service;
+			break;
+		default:
+			break;
+		}
+
+		array_append(&service_list->services, &service, 1);
+	}
+
+	/* resolve service dependencies */
+	services = array_get(&service_list->services, &count);
+	for (i = 0; i < count; i++) {
+		if (services[i]->type == SERVICE_TYPE_AUTH_SOURCE) {
+			const char *dest_service =
+				services[i]->set->auth_dest_service;
+			services[i]->auth_dest_service =
+				service_lookup(service_list, dest_service);
+			if (services[i]->auth_dest_service == NULL) {
+				*error_r = t_strdup_printf(
+					"auth_dest_service doesn't exist: %s",
+					dest_service);
+				return NULL;
+			}
+		}
+	}
+
+	if (service_list->log == NULL) {
+		*error_r = "log service not specified";
+		return NULL;
+	}
+
+	if (service_list->config == NULL) {
+		*error_r = "config process not specified";
+		return NULL;
+	}
+
+	service_list->pids = hash_table_create(default_pool, pool, 0,
+					       pid_hash, pid_hash_cmp);
+	return service_list;
+}
+
+void service_signal(struct service *service, int signo)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_table_iterate_init(service->list->pids);
+	while (hash_table_iterate(iter, &key, &value)) {
+		struct service_process *process = value;
+
+		if (process->service != service)
+			continue;
+
+		if (kill(process->pid, signo) < 0 && errno != ESRCH) {
+			i_error("service(%s): kill(%s, %d) failed: %m",
+				service->name, dec2str(process->pid), signo);
+		}
+	}
+	hash_table_iterate_deinit(&iter);
+}
+
+void services_destroy(struct service_list *service_list)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	/* make sure we log if child processes died unexpectedly */
+        services_monitor_reap_children(service_list);
+
+	services_monitor_stop(service_list);
+
+	/* free all child process information */
+	iter = hash_table_iterate_init(service_list->pids);
+	while (hash_table_iterate(iter, &key, &value))
+		service_process_destroy(value);
+	hash_table_iterate_deinit(&iter);
+
+	hash_table_destroy(&service_list->pids);
+	pool_unref(&service_list->pool);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/master/service.h	Thu Apr 23 19:53:44 2009 -0400
@@ -0,0 +1,110 @@
+#ifndef SERVICE_H
+#define SERVICE_H
+
+#include "network.h"
+
+/* If a service process doesn't send its first status notification in
+   this many seconds, kill the process */
+#define SERVICE_FIRST_STATUS_TIMEOUT_SECS 30
+
+enum service_type {
+	SERVICE_TYPE_UNKNOWN,
+	SERVICE_TYPE_LOG,
+	SERVICE_TYPE_CONFIG,
+	SERVICE_TYPE_AUTH_SERVER,
+	SERVICE_TYPE_AUTH_SOURCE
+};
+
+enum service_listener_type {
+	SERVICE_LISTENER_UNIX,
+	SERVICE_LISTENER_FIFO,
+	SERVICE_LISTENER_INET
+};
+
+struct service_listener {
+	struct service *service;
+
+	enum service_listener_type type;
+	int fd; /* may be -1 */
+	struct io *io;
+
+	union {
+		struct {
+			const struct file_listener_settings *set;
+			uid_t uid;
+			gid_t gid;
+		} fileset;
+		struct {
+			const struct inet_listener_settings *set;
+			struct ip_addr ip;
+		} inetset;
+	} set;
+};
+
+struct service {
+	struct service_list *list;
+
+	enum service_type type;
+	const char *name;
+
+	const struct service_settings *set;
+	const char *config_file_path;
+
+	const char *executable;
+	uid_t uid;
+	gid_t gid;
+	gid_t privileged_gid;
+	const char *extra_gids; /* comma-separated list */
+
+	/* all listeners, even those that aren't currently listening */
+	ARRAY_DEFINE(listeners, struct service_listener *);
+
+	/* number of processes currently created for this service */
+	unsigned int process_count;
+	/* number of processes currently accepting new connections */
+	unsigned int process_avail;
+	/* max number of processes allowed */
+	unsigned int process_limit;
+
+	/* log process pipe file descriptors */
+	int log_fd[2];
+
+	/* status report pipe file descriptors */
+	int status_fd[2];
+	struct io *io_status;
+
+	/* if a process fails before servicing its first request, assume it's
+	   broken and start throtting new process creations */
+	struct timeout *to_throttle;
+
+	/* SERVICE_TYPE_AUTH_SOURCE: Destination service to run after
+	   successful authentication. */
+	struct service *auth_dest_service;
+
+	/* all processes are in use and new connections are coming */
+	unsigned int listen_pending:1;
+};
+
+struct service_list {
+	pool_t pool;
+
+	struct service *config;
+	struct service *log;
+	struct hash_table *pids;
+	const char *const *child_process_env;
+
+	ARRAY_DEFINE(services, struct service *);
+};
+
+/* Create all services from settings */
+struct service_list *
+services_create(const struct master_settings *set,
+		const char *const *child_process_env, const char **error_r);
+
+/* Destroy services */
+void services_destroy(struct service_list *service_list);
+
+/* Send a signal to all processes in a given service */
+void service_signal(struct service *service, int signo);
+
+#endif
--- a/src/master/sysinfo-get.c	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "mountpoint.h"
-#include "strescape.h"
-#include "sysinfo-get.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#ifdef HAVE_SYS_UTSNAME_H
-#  include <sys/utsname.h>
-#endif
-
-static bool readfile(const char *path, const char **data_r)
-{
-	char buf[1024];
-	int fd, ret;
-
-	fd = open(path, O_RDONLY);
-	if (fd == -1)
-		return FALSE;
-	ret = read(fd, buf, sizeof(buf));
-	(void)close(fd);
-	if (ret <= 0)
-		return FALSE;
-
-	*data_r = t_strndup(buf, ret);
-	return TRUE;
-}
-
-static bool lsb_distro_get(const char *path, const char **name_r)
-{
-	const char *data, *const *p, *str, *end;
-
-	if (!readfile(path, &data))
-		return FALSE;
-
-	for (p = t_strsplit(data, "\n"); *p != '\0'; p++) {
-		if (strncmp(*p, "DISTRIB_DESCRIPTION=", 20) == 0)
-			break;
-	}
-	if (*p == '\0')
-		return FALSE;
-
-	str = t_strcut(*p + 20, '\n');
-	if (*str != '"')
-		*name_r = str;
-	else {
-		end = strrchr(++str, '"');
-		*name_r = str_unescape(p_strdup_until(unsafe_data_stack_pool,
-						      str, end));
-	}
-	return TRUE;
-}
-
-static const char *distro_get(void)
-{
-	static const char *files[] = {
-		"", "/etc/redhat-release",
-		"", "/etc/SuSE-release",
-		"", "/etc/mandriva-release",
-		"", "/etc/fedora-release",
-		"", "/etc/sourcemage-release",
-		"", "/etc/slackware-version",
-		"", "/etc/gentoo-release",
-		"Debian ", "/etc/debian_version",
-		NULL
-	};
-	const char *name;
-	unsigned int i;
-
-	if (lsb_distro_get("/etc/lsb-release", &name))
-		return name;
-	for (i = 0; files[i] != NULL; i += 2) {
-		if (readfile(files[i+1], &name)) {
-			return t_strconcat(files[i], t_strcut(name, '\n'),
-					   NULL);
-		}
-	}
-	return "";
-}
-
-static const char *filesystem_get(const char *mail_location)
-{
-	struct mountpoint mp;
-	const char *path;
-
-	path = strchr(mail_location, ':');
-	if (path == NULL)
-		path = mail_location;
-	else
-		path = t_strcut(path + 1, ':');
-	if (*path == '~') {
-		/* we don't know where users' home dirs are */
-		return "";
-	}
-	path = t_strcut(path, '%');
-	if (strlen(path) <= 1)
-		return "";
-
-	/* all in all it seems we can support only /<path>/%u style location */
-	if (mountpoint_get(path, pool_datastack_create(), &mp) < 0)
-		return "";
-	return mp.type == NULL ? "" : mp.type;
-}
-
-const char *sysinfo_get(const char *mail_location)
-{
-	const char *distro = "", *fs, *uname_info = "";
-#ifdef HAVE_SYS_UTSNAME_H
-	struct utsname u;
-
-	if (uname(&u) < 0)
-		i_error("uname() failed: %m");
-	else {
-		uname_info = t_strdup_printf("%s %s %s",
-					     u.sysname, u.release, u.machine);
-	}
-	if (strcmp(u.sysname, "Linux") == 0)
-		distro = distro_get();
-#endif
-	fs = filesystem_get(mail_location);
-	if (*uname_info == '\0' && *distro == '\0' && *fs == '\0')
-		return "";
-	return t_strdup_printf("OS: %s %s %s %s %s", u.sysname, u.release, u.machine, distro, fs);
-}
--- a/src/master/sysinfo-get.h	Thu Apr 23 14:07:45 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-#ifndef SYSINFO_GET_H
-#define SYSINFO_GET_H
-
-const char *sysinfo_get(const char *mail_location);
-
-#endif
--- a/src/plugins/convert/convert-tool.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/plugins/convert/convert-tool.c	Thu Apr 23 19:53:44 2009 -0400
@@ -55,7 +55,7 @@
 	input.username = argv[optind];
 
 	master_service_init_log(service,
-		t_strdup_printf("convert-tool(%s): ", input.username));
+		t_strdup_printf("convert-tool(%s): ", input.username), 0);
 	user = mail_storage_service_init_user(service, &input, NULL, 0);
 
 	memset(&ns_set, 0, sizeof(ns_set));
--- a/src/pop3-login/Makefile.am	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/pop3-login/Makefile.am	Thu Apr 23 19:53:44 2009 -0400
@@ -5,6 +5,7 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-auth \
+	-I$(top_srcdir)/src/lib-master \
 	-I$(top_srcdir)/src/login-common
 
 pop3_login_LDADD = \
--- a/src/pop3-login/client.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/pop3-login/client.c	Thu Apr 23 19:53:44 2009 -0400
@@ -10,6 +10,8 @@
 #include "process-title.h"
 #include "safe-memset.h"
 #include "strescape.h"
+#include "master-service.h"
+#include "master-auth.h"
 #include "client.h"
 #include "client-authenticate.h"
 #include "auth-client.h"
@@ -34,6 +36,7 @@
 #endif
 
 const char *login_protocol = "POP3";
+const char *login_process_name = "pop3-login";
 
 static void client_set_title(struct pop3_client *client)
 {
@@ -63,7 +66,6 @@
 	int fd_ssl;
 
 	client_ref(client);
-	connection_queue_add(1);
 	if (!client_unref(client) || client->destroyed)
 		return;
 
@@ -76,6 +78,7 @@
 		return;
 	}
 
+	client->common.proxying = TRUE;
 	client->common.tls = TRUE;
 	client->common.secured = TRUE;
 	client_set_title(client);
@@ -307,7 +310,11 @@
 
 	i_assert(fd != -1);
 
-	connection_queue_add(1);
+	if (clients_get_count() >= login_settings->login_max_connections) {
+		/* reached max. users count, kill few of the
+		   oldest connections */
+		client_destroy_oldest();
+	}
 
 	/* always use nonblocking I/O */
 	net_set_nonblock(fd, TRUE);
@@ -327,8 +334,6 @@
 	client_open_streams(client, fd);
 	client_link(&client->common);
 
-	main_ref();
-
 	client->auth_connected = auth_client_is_connected(auth_client);
 	if (client->auth_connected)
 		client_auth_ready(client);
@@ -367,10 +372,11 @@
 	if (client->output != NULL)
 		o_stream_close(client->output);
 
-	if (client->common.master_tag != 0)
-		master_request_abort(&client->common);
-
-	if (client->common.auth_request != NULL) {
+	if (client->common.master_tag != 0) {
+		i_assert(client->common.auth_request == NULL);
+		i_assert(client->common.authenticating);
+		master_auth_request_abort(service, client->common.master_tag);
+	} else if (client->common.auth_request != NULL) {
 		i_assert(client->common.authenticating);
 		sasl_server_auth_client_error(&client->common, NULL);
 	} else {
@@ -407,9 +413,6 @@
 		client->common.proxy = NULL;
 	}
 	client_unref(client);
-
-	main_listen_start();
-	main_unref();
 }
 
 void client_destroy_internal_failure(struct pop3_client *client)
@@ -437,12 +440,16 @@
 	if (client->output != NULL)
 		o_stream_unref(&client->output);
 
+	if (!client->common.proxying) {
+		i_assert(client->common.proxy == NULL);
+		master_service_client_connection_destroyed(service);
+	}
+
 	i_free(client->last_user);
 	i_free(client->apop_challenge);
 	i_free(client->common.virtual_user);
 	i_free(client->common.auth_mech_name);
 	i_free(client);
-
 	return FALSE;
 }
 
--- a/src/pop3-login/client.h	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/pop3-login/client.h	Thu Apr 23 19:53:44 2009 -0400
@@ -2,7 +2,6 @@
 #define CLIENT_H
 
 #include "network.h"
-#include "master.h"
 #include "client-common.h"
 #include "auth-client.h"
 
--- a/src/pop3-login/pop3-proxy.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/pop3-login/pop3-proxy.c	Thu Apr 23 19:53:44 2009 -0400
@@ -133,6 +133,7 @@
 		client->common.input = NULL;
 		client->output = NULL;
 		client->common.fd = -1;
+		client->common.proxying = TRUE;
 		client_destroy_success(client, str_c(str));
 		return 1;
 	}
@@ -240,7 +241,6 @@
 	}
 
 	i_assert(client->refcount > 1);
-	connection_queue_add(1);
 
 	if (client->destroyed) {
 		/* connection_queue_add() decided that we were the oldest
--- a/src/pop3/main.c	Thu Apr 23 14:07:45 2009 -0400
+++ b/src/pop3/main.c	Thu Apr 23 19:53:44 2009 -0400
@@ -137,6 +137,12 @@
 	clients_deinit();
 }
 
+static void client_connected(const struct master_service_connection *conn)
+{
+	/* we can't handle this yet */
+	(void)close(conn->fd);
+}
+
 int main(int argc, char *argv[], char *envp[])
 {
 	const struct setting_parser_info *set_roots[] = {
@@ -171,7 +177,7 @@
 	service = master_service_init("pop3", service_flags, argc, argv);
 	while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
 		if (!master_service_parse_option(service, c, optarg))
-			i_fatal("Unknown argument: %c", c);
+			exit(FATAL_DEFAULT);
 	}
 
 	memset(&input, 0, sizeof(input));
@@ -199,7 +205,7 @@
 	io_loop_set_running(current_ioloop);
 
 	if (main_init(set, mail_user))
-		master_service_run(service);
+		master_service_run(service, client_connected);
 
 	main_deinit();
 	mail_storage_service_deinit_user();