changeset 10101:4fe8c4382712 HEAD

Redesigned how login process passes connections to mail processes and changed related APIs. Master process is no longer in the middle.
author Timo Sirainen <tss@iki.fi>
date Mon, 19 Oct 2009 21:42:09 -0400
parents 33c448ce13f9
children 57134fd23d4c
files doc/example-config/conf.d/master.conf 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-interface.h src/auth/main.c src/doveadm/doveadm.c src/dsync/dsync.c src/imap/imap-client.c src/imap/imap-client.h 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-login-auth.c src/lib-master/master-login-auth.h src/lib-master/master-login.c src/lib-master/master-login.h src/lib-master/master-service-private.h src/lib-master/master-service.c src/lib-master/master-service.h src/lib-storage/mail-storage-service.c src/lib-storage/mail-storage-service.h src/login-common/client-common.c src/login-common/common.h src/login-common/main.c src/login-common/sasl-server.c src/master/Makefile.am src/master/main.c 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-monitor.c src/master/service-process.c src/master/service-process.h src/master/service.c src/master/service.h src/pop3/main.c src/pop3/pop3-client.c src/pop3/pop3-client.h
diffstat 46 files changed, 1419 insertions(+), 1543 deletions(-) [+]
line wrap: on
line diff
--- a/doc/example-config/conf.d/master.conf	Mon Oct 19 18:34:00 2009 -0400
+++ b/doc/example-config/conf.d/master.conf	Mon Oct 19 21:42:09 2009 -0400
@@ -32,7 +32,6 @@
 }
 
 service auth {
-  type = auth
   executable = dovecot-auth
 
   # default
@@ -52,6 +51,11 @@
     path = auth-userdb
     mode = 0600
   }
+
+  unix_listener {
+    path = auth-master
+    mode = 0600
+  }
 }
 
 service auth-worker {
@@ -65,9 +69,8 @@
 
 service imap-login {
   protocol = imap
-  type = auth-source
+  type = login
   executable = imap-login
-  auth_dest_service = imap
 
   inet_listener {
     port = 143
@@ -104,13 +107,18 @@
   # Most of the memory goes to mmap()ing files. You may need to increase this
   # limit if you have huge mailboxes.
   #vsz_limit = 256
+
+  service_count = 1
+  unix_listener {
+    path = login/imap
+    mode = 0666
+  }
 }
 
 service pop3-login {
   protocol = pop3
-  type = auth-source
+  type = login
   executable = pop3-login
-  auth_dest_service = pop3
 
   inet_listener {
     port = 110
@@ -130,6 +138,12 @@
 service pop3 {
   protocol = pop3
   executable = pop3
+
+  service_count = 1
+  unix_listener {
+    path = login/pop3
+    mode = 0666
+  }
 }
 
 service lmtp {
--- a/src/auth/Makefile.am	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/auth/Makefile.am	Mon Oct 19 21:42:09 2009 -0400
@@ -109,7 +109,6 @@
 	auth-cache.h \
 	auth-client-connection.h \
 	auth-common.h \
-	auth-master-interface.h \
 	auth-master-connection.h \
 	mech-otp-skey-common.h \
 	mech-plain-common.h \
--- a/src/auth/auth-client-connection.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/auth/auth-client-connection.c	Mon Oct 19 21:42:09 2009 -0400
@@ -116,9 +116,7 @@
         conn->refcount++;
 	conn->request_handler =
 		auth_request_handler_create(conn->auth,
-			auth_callback, conn,
-			array_count(&auth_master_connections) != 0 ?
-			auth_master_request_callback : NULL);
+			auth_callback, conn, auth_master_request_callback);
 	auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
 
 	conn->pid = pid;
--- a/src/auth/auth-client-connection.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/auth/auth-client-connection.h	Mon Oct 19 21:42:09 2009 -0400
@@ -1,7 +1,7 @@
 #ifndef AUTH_CLIENT_CONNECTION_H
 #define AUTH_CLIENT_CONNECTION_H
 
-#include "master-interface.h"
+#include "master-auth.h"
 
 struct auth_client_connection {
 	struct auth *auth;
--- a/src/auth/auth-master-connection.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/auth/auth-master-connection.c	Mon Oct 19 21:42:09 2009 -0400
@@ -15,8 +15,8 @@
 #include "master-service.h"
 #include "userdb.h"
 #include "userdb-blocking.h"
+#include "master-interface.h"
 #include "auth-request-handler.h"
-#include "auth-master-interface.h"
 #include "auth-client-connection.h"
 #include "auth-master-connection.h"
 
@@ -502,7 +502,6 @@
         struct auth_master_connection *conn = *_conn;
         struct auth_master_connection *const *masters;
 	unsigned int i, count;
-	bool service_connection = conn->fd != MASTER_AUTH_FD;
 
 	*_conn = NULL;
 	if (conn->destroyed)
@@ -529,8 +528,7 @@
 		conn->fd = -1;
 	}
 
-	if (service_connection)
-		master_service_client_connection_destroyed(master_service);
+	master_service_client_connection_destroyed(master_service);
 	auth_master_connection_unref(&conn);
 }
 
--- a/src/auth/auth-master-interface.h	Mon Oct 19 18:34:00 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-#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
-#define AUTH_MASTER_PROTOCOL_MINOR_VERSION 1
-
-#endif
--- a/src/auth/main.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/auth/main.c	Mon Oct 19 21:42:09 2009 -0400
@@ -11,13 +11,13 @@
 #include "module-dir.h"
 #include "randgen.h"
 #include "master-service.h"
+#include "master-interface.h"
 #include "password-scheme.h"
 #include "mech.h"
 #include "auth.h"
 #include "auth-request-handler.h"
 #include "auth-worker-server.h"
 #include "auth-worker-client.h"
-#include "auth-master-interface.h"
 #include "auth-master-connection.h"
 #include "auth-client-connection.h"
 
@@ -86,9 +86,6 @@
 		/* workers have only a single connection from the master
 		   auth process */
 		master_service_set_client_limit(master_service, 1);
-	} else if (getenv("MASTER_AUTH_FD") != NULL) {
-		(void)auth_master_connection_create(auth, MASTER_AUTH_FD,
-						    FALSE);
 	}
 }
 
--- a/src/doveadm/doveadm.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/doveadm/doveadm.c	Mon Oct 19 21:42:09 2009 -0400
@@ -98,6 +98,7 @@
 	argc -= optind;
 	argv += optind;
 
+	master_service_init_finish(master_service);
 	if (!doveadm_try_run(cmd_name, argc, argv) &&
 	    !doveadm_mail_try_run(cmd_name, argc, argv))
 		usage();
--- a/src/dsync/dsync.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/dsync/dsync.c	Mon Oct 19 21:42:09 2009 -0400
@@ -112,6 +112,7 @@
 	}
 	if (optind != argc)
 		usage();
+	master_service_init_finish(master_service);
 
 	memset(&input, 0, sizeof(input));
 	input.username = username;
--- a/src/imap/imap-client.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/imap/imap-client.c	Mon Oct 19 21:42:09 2009 -0400
@@ -21,7 +21,7 @@
 extern struct mail_storage_callbacks mail_storage_callbacks;
 struct imap_module_register imap_module_register = { 0 };
 
-static struct client *imap_clients = NULL;
+struct client *imap_clients = NULL;
 
 static void client_idle_timeout(struct client *client)
 {
--- a/src/imap/imap-client.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/imap/imap-client.h	Mon Oct 19 21:42:09 2009 -0400
@@ -146,6 +146,8 @@
 	unsigned int modseqs_sent_since_sync:1;
 };
 
+extern struct client *imap_clients;
+
 /* Create new client with specified input/output handles. socket specifies
    if the handle is a socket. */
 struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
--- a/src/imap/main.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/imap/main.c	Mon Oct 19 21:42:09 2009 -0400
@@ -9,8 +9,9 @@
 #include "restrict-access.h"
 #include "fd-close-on-exec.h"
 #include "process-title.h"
+#include "master-interface.h"
 #include "master-service.h"
-#include "master-interface.h"
+#include "master-login.h"
 #include "mail-user.h"
 #include "mail-storage-service.h"
 #include "imap-commands.h"
@@ -21,18 +22,25 @@
 #include <unistd.h>
 
 #define IS_STANDALONE() \
-        (getenv("CLIENT_INPUT") == NULL)
+        (getenv(MASTER_UID_ENV) == NULL)
+
+static const struct setting_parser_info *set_roots[] = {
+	&imap_setting_parser_info,
+	NULL
+};
+static struct master_login *master_login = NULL;
+static enum mail_storage_service_flags storage_service_flags = 0;
+static bool user_initialized = FALSE;
 
 void (*hook_client_created)(struct client **client) = NULL;
 
-static void client_add_input(struct client *client, const char *input)
+static void client_add_input(struct client *client, const buffer_t *buf)
 {
-	buffer_t *buf;
+	struct ostream *output;
 	const char *tag;
 	unsigned int data_pos;
 	bool send_untagged_capability = FALSE;
 
-	buf = input == NULL ? NULL : t_base64_decode_str(input);
 	if (buf != NULL && buf->used > 0) {
 		tag = t_strndup(buf->data, buf->used);
 		switch (*tag) {
@@ -55,6 +63,9 @@
 		tag = getenv("IMAPLOGINTAG");
 	}
 
+	output = client->output;
+	o_stream_ref(output);
+	o_stream_cork(output);
 	if (tag == NULL) {
 		client_send_line(client, t_strconcat(
 			"* PREAUTH [CAPABILITY ",
@@ -63,68 +74,141 @@
 	} else if (send_untagged_capability) {
 		/* client doesn't seem to understand tagged capabilities. send
 		   untagged instead and hope that it works. */
-		o_stream_cork(client->output);
 		client_send_line(client, t_strconcat("* CAPABILITY ",
 			str_c(client->capability_string), NULL));
 		client_send_line(client, t_strconcat(tag, " OK Logged in", NULL));
-		o_stream_uncork(client->output);
 	} else {
 		client_send_line(client, t_strconcat(
 			tag, " OK [CAPABILITY ",
 			str_c(client->capability_string), "] Logged in", NULL));
 	}
 	(void)client_handle_input(client);
+	o_stream_uncork(output);
+	o_stream_unref(&output);
 }
 
-static void main_init(const struct imap_settings *set, struct mail_user *user,
-		      bool dump_capability)
+static void
+main_stdio_init_user(const struct imap_settings *set, struct mail_user *user)
 {
 	struct client *client;
-	struct ostream *output;
+	buffer_t *input_buf;
+	const char *input_base64;
+
+	input_base64 = getenv("CLIENT_INPUT");
+	input_buf = input_base64 == NULL ? NULL :
+		t_base64_decode_str(input_base64);
+
+	client = client_create(STDIN_FILENO, STDOUT_FILENO, user, set);
+	client_add_input(client, input_buf);
+}
+
+static void
+main_stdio_run(bool dump_capability)
+{
+	struct mail_storage_service_input input;
+	struct mail_user *mail_user;
+	const struct imap_settings *set;
+	const char *value;
 
-	if (set->shutdown_clients && !dump_capability)
+	memset(&input, 0, sizeof(input));
+	input.module = input.service = "imap";
+	input.username = getenv("USER");
+	if (input.username == NULL && IS_STANDALONE())
+		input.username = getlogin();
+	if (input.username == NULL)
+		i_fatal("USER environment missing");
+	if ((value = getenv("IP")) != NULL)
+		net_addr2ip(value, &input.remote_ip);
+	if ((value = getenv("LOCAL_IP")) != NULL)
+		net_addr2ip(value, &input.local_ip);
+
+	user_initialized = TRUE;
+	mail_user = mail_storage_service_init_user(master_service,
+						   &input, set_roots,
+						   storage_service_flags);
+	set = mail_storage_service_get_settings(master_service);
+	restrict_access_allow_coredumps(TRUE);
+	if (set->shutdown_clients)
 		master_service_set_die_with_master(master_service, TRUE);
 
-	client = client_create(0, 1, user, set);
-
 	if (dump_capability) {
+		struct client *client = client_create(0, 1, mail_user, set);
 		printf("%s\n", str_c(client->capability_string));
 		exit(0);
 	}
 
-	output = client->output;
-	o_stream_ref(output);
-	o_stream_cork(output);
-	client_add_input(client, getenv("CLIENT_INPUT"));
-        o_stream_uncork(output);
-	o_stream_unref(&output);
+	/* fake that we're running, so we know if client was destroyed
+	   while handling its initial input */
+	io_loop_set_running(current_ioloop);
+	main_stdio_init_user(set, mail_user);
 }
 
-static void main_deinit(void)
+static void
+login_client_connected(const struct master_login_client *client,
+		       const char *username, const char *const *extra_fields)
 {
-	clients_destroy_all();
+	struct mail_storage_service_input input;
+	struct mail_user *mail_user;
+	struct client *imap_client;
+	const struct imap_settings *set;
+	buffer_t input_buf;
+
+	if (imap_clients != NULL) {
+		i_error("Can't handle more than one connection currently");
+		(void)close(client->fd);
+		return;
+	}
+	i_assert(!user_initialized);
+
+	memset(&input, 0, sizeof(input));
+	input.module = input.service = "imap";
+	input.local_ip = client->auth_req.local_ip;
+	input.remote_ip = client->auth_req.remote_ip;
+	input.username = username;
+	input.userdb_fields = extra_fields;
+
+	if (input.username == NULL) {
+		i_error("login client: Username missing from auth reply");
+		(void)close(client->fd);
+		return;
+	}
+	user_initialized = TRUE;
+	master_login_deinit(&master_login);
+
+	mail_user = mail_storage_service_init_user(master_service,
+						   &input, set_roots,
+						   storage_service_flags);
+	set = mail_storage_service_get_settings(master_service);
+	restrict_access_allow_coredumps(TRUE);
+	if (set->shutdown_clients)
+		master_service_set_die_with_master(master_service, TRUE);
+
+	/* fake that we're running, so we know if client was destroyed
+	   while handling its initial input */
+	io_loop_set_running(current_ioloop);
+
+	buffer_create_const_data(&input_buf, client->data,
+				 client->auth_req.data_size);
+	imap_client = client_create(client->fd, client->fd, mail_user, set);
+	T_BEGIN {
+		client_add_input(imap_client, &input_buf);
+	} T_END;
 }
 
 static void client_connected(const struct master_service_connection *conn)
 {
-	/* FIXME: we can't handle this yet */
-	(void)close(conn->fd);
+	if (master_login == NULL) {
+		/* running standalone, we shouldn't even get here */
+		(void)close(conn->fd);
+	} else {
+		master_login_add(master_login, conn->fd);
+	}
 }
 
 int main(int argc, char *argv[], char *envp[])
 {
-	const struct setting_parser_info *set_roots[] = {
-		&imap_setting_parser_info,
-		NULL
-	};
-	enum master_service_flags service_flags =
-		MASTER_SERVICE_FLAG_STD_CLIENT;
-	enum mail_storage_service_flags storage_service_flags = 0;
-	struct mail_storage_service_input input;
-	struct mail_user *mail_user;
-	const struct imap_settings *set;
+	enum master_service_flags service_flags = 0;
 	bool dump_capability;
-	const char *value;
 	int c;
 
 	if (IS_STANDALONE() && getuid() == 0 &&
@@ -134,12 +218,12 @@
 		return 1;
 	}
 
-	if (IS_STANDALONE())
-		service_flags |= MASTER_SERVICE_FLAG_STANDALONE;
-	else {
+	if (IS_STANDALONE()) {
+		service_flags |= MASTER_SERVICE_FLAG_STANDALONE |
+			MASTER_SERVICE_FLAG_STD_CLIENT;
+	} else {
 		storage_service_flags |=
-			MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT |
-			MAIL_STORAGE_SERVICE_FLAG_RESTRICT_BY_ENV;
+			MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT;
 	}
 
 	dump_capability = getenv("DUMP_CAPABILITY") != NULL;
@@ -153,50 +237,31 @@
 		if (!master_service_parse_option(master_service, c, optarg))
 			exit(FATAL_DEFAULT);
 	}
-
-	memset(&input, 0, sizeof(input));
-	input.module = "imap";
-	input.service = "imap";
-	input.username = getenv("USER");
-	if (input.username == NULL && IS_STANDALONE())
-		input.username = getlogin();
-	if (input.username == NULL) {
-		if (getenv(MASTER_UID_ENV) == NULL)
-			i_fatal("USER environment missing");
-		else {
-			i_fatal("login_executable setting must be imap-login, "
-				"not imap");
-		}
-	}
-	if ((value = getenv("IP")) != NULL)
-		net_addr2ip(value, &input.remote_ip);
-	if ((value = getenv("LOCAL_IP")) != NULL)
-		net_addr2ip(value, &input.local_ip);
+        process_title_init(argv, envp);
+	master_service_init_finish(master_service);
 
 	/* plugins may want to add commands, so this needs to be called early */
 	commands_init();
 	imap_fetch_handlers_init();
 
-	mail_user = mail_storage_service_init_user(master_service,
-						   &input, set_roots,
-						   storage_service_flags);
-	set = mail_storage_service_get_settings(master_service);
-	restrict_access_allow_coredumps(TRUE);
-
-        process_title_init(argv, envp);
+	if (IS_STANDALONE() || dump_capability) {
+		T_BEGIN {
+			main_stdio_run(dump_capability);
+		} T_END;
+	} else {
+		master_login = master_login_init("auth-master",
+						 login_client_connected);
+		io_loop_set_running(current_ioloop);
+	}
 
-	/* fake that we're running, so we know if client was destroyed
-	   while initializing */
-	io_loop_set_running(current_ioloop);
-
-	T_BEGIN {
-		main_init(set, mail_user, dump_capability);
-	} T_END;
 	if (io_loop_is_running(current_ioloop))
 		master_service_run(master_service, client_connected);
+	clients_destroy_all();
 
-	main_deinit();
-	mail_storage_service_deinit_user();
+	if (master_login != NULL)
+		master_login_deinit(&master_login);
+	if (user_initialized)
+		mail_storage_service_deinit_user();
 	imap_fetch_handlers_deinit();
 	commands_deinit();
 
--- a/src/lda/main.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lda/main.c	Mon Oct 19 21:42:09 2009 -0400
@@ -378,6 +378,7 @@
 		i_fatal_status(EX_USAGE,
 			"destination user parameter (-d user) not given");
 	}
+	master_service_init_finish(master_service);
 
 	memset(&service_input, 0, sizeof(service_input));
 	service_input.module = "lda";
--- a/src/lib-master/Makefile.am	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lib-master/Makefile.am	Mon Oct 19 21:42:09 2009 -0400
@@ -10,6 +10,8 @@
 
 libmaster_la_SOURCES = \
 	master-auth.c \
+	master-login.c \
+	master-login-auth.c \
 	master-service.c \
 	master-service-settings.c \
 	syslog-util.c
@@ -17,6 +19,8 @@
 noinst_HEADERS = \
 	master-auth.h \
 	master-interface.h \
+	master-login.h \
+	master-login-auth.h \
 	master-service.h \
 	master-service-private.h \
 	master-service-settings.h \
--- a/src/lib-master/master-auth.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lib-master/master-auth.c	Mon Oct 19 21:42:09 2009 -0400
@@ -12,45 +12,152 @@
 #include <unistd.h>
 #include <sys/stat.h>
 
-struct master_auth {
-	struct master_service *service;
-	pool_t pool;
+struct master_auth_connection {
+	struct master_auth *auth;
+	unsigned int tag;
 
 	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;
+struct master_auth {
+	struct master_service *service;
+	pool_t pool;
+
+	const char *path;
+
+	unsigned int tag_counter;
+	struct hash_table *connections;
+};
+
+struct master_auth *
+master_auth_init(struct master_service *service, const char *path)
+{
+	struct master_auth *auth;
+	pool_t pool;
+
+	pool = pool_alloconly_create("master auth", 1024);
+	auth = p_new(pool, struct master_auth, 1);
+	auth->pool = pool;
+	auth->service = service;
+	auth->path = p_strdup(pool, path);
+	auth->connections = hash_table_create(default_pool, pool,
+					      0, NULL, NULL);
+	return auth;
+}
+
+static void
+master_auth_connection_deinit(struct master_auth_connection **_conn)
+{
+	struct master_auth_connection *conn = *_conn;
+	struct master_auth_reply reply;
+
+	*_conn = NULL;
+
+	if (conn->tag != 0) {
+		hash_table_remove(conn->auth->connections,
+				  POINTER_CAST(conn->tag));
+	}
+
+	if (conn->callback != NULL) {
+		memset(&reply, 0, sizeof(reply));
+		reply.status = MASTER_AUTH_STATUS_INTERNAL_ERROR;
+		conn->callback(&reply, conn->context);
+	}
+
+	if (conn->io != NULL)
+		io_remove(&conn->io);
+	if (conn->fd != -1) {
+		if (close(conn->fd) < 0)
+			i_fatal("close(master auth conn) failed: %m");
+		conn->fd = -1;
+	}
+	i_free(conn);
+}
 
-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_deinit(struct master_auth **_auth)
+{
+	struct master_auth *auth = *_auth;
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	*_auth = NULL;
+
+	iter = hash_table_iterate_init(auth->connections);
+	while (hash_table_iterate(iter, &key, &value)) {
+		struct master_auth_connection *conn = value;
+
+		conn->tag = 0;
+		master_auth_connection_deinit(&conn);
+	}
+	hash_table_iterate_deinit(&iter);
+	hash_table_destroy(&auth->connections);
+	pool_unref(&auth->pool);
+}
+
+static void master_auth_connection_input(struct master_auth_connection *conn)
 {
-	struct master_auth *auth = service->auth;
-        struct master_auth_request_node *node;
+	const struct master_auth_reply *reply;
+	int ret;
+
+	ret = read(conn->fd, conn->buf + conn->buf_pos,
+		   sizeof(conn->buf) - conn->buf_pos);
+	if (ret <= 0) {
+		if (ret < 0) {
+			if (errno == EAGAIN)
+				return;
+			i_error("read(master auth conn) failed: %m");
+		} else {
+			i_error("read(master auth conn) failed: "
+				"Remote closed connection");
+		}
+		master_auth_connection_deinit(&conn);
+		return;
+	}
+
+	conn->buf_pos += ret;
+	if (conn->buf_pos < sizeof(conn->buf))
+		return;
+
+	/* reply is now read */
+	reply = (struct master_auth_reply *)conn->buf;
+	conn->buf_pos = 0;
+
+	if (conn->tag != reply->tag)
+		i_error("Master sent reply with unknown tag %u", reply->tag);
+	else {
+		conn->callback(reply, conn->context);
+		conn->callback = NULL;
+	}
+	master_auth_connection_deinit(&conn);
+}
+
+void master_auth_request(struct master_auth *auth, int fd,
+			 const struct master_auth_request *request,
+			 const unsigned char *data,
+			 master_auth_callback_t *callback,
+			 void *context, unsigned int *tag_r)
+{
+        struct master_auth_connection *conn;
 	struct master_auth_request req;
 	buffer_t *buf;
 	struct stat st;
 	ssize_t ret;
 
+	i_assert(request->client_pid != 0);
 	i_assert(request->auth_pid != 0);
 
+	conn = i_new(struct master_auth_connection, 1);
+	conn->auth = auth;
+	conn->callback = callback;
+	conn->context = context;
+
 	req = *request;
 	req.tag = ++auth->tag_counter;
 	if (req.tag == 0)
@@ -65,145 +172,38 @@
 	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;
+	conn->fd = net_connect_unix(auth->path);
+	if (conn->fd == -1)
+		i_error("net_connect_unix(%s) failed: %m", auth->path);
 
-	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
-master_notify_have_more_avail_processes(struct master_service *service,
-					bool have_more)
-{
-	if (!have_more) {
-		/* make sure we're listening for more connections */
-		master_service_io_listeners_add(service);
+	ret = conn->fd == -1 ? -1 :
+		fd_send(conn->fd, fd, buf->data, buf->used);
+	if (ret < 0)
+		i_error("fd_send(%d) failed: %m", fd);
+	else if ((size_t)ret != buf->used) {
+		i_error("fd_send() sent only %d of %d bytes",
+			(int)ret, (int)buf->used);
+		ret = -1;
 	}
-	service->call_avail_overflow = !have_more;
-}
-
-static void request_handle(struct master_auth *auth,
-			   struct master_auth_reply *reply)
-{
-        struct master_auth_request_node *node;
-
-	if (reply->tag == 0) {
-		/* notification from master */
-		master_notify_have_more_avail_processes(auth->service,
-							reply->status == 0);
+	if (ret < 0) {
+		master_auth_connection_deinit(&conn);
 		return;
 	}
 
-	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;
+	conn->tag = req.tag;
+	conn->io = io_add(conn->fd, IO_READ,
+			  master_auth_connection_input, conn);
+	hash_table_insert(auth->connections, POINTER_CAST(req.tag), conn);
+	*tag_r = req.tag;
 }
 
-void master_auth_init(struct master_service *service)
+void master_auth_request_abort(struct master_auth *auth, unsigned int tag)
 {
-	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");
+        struct master_auth_connection *conn;
 
-	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;
-}
+	conn = hash_table_lookup(auth->connections, POINTER_CAST(tag));
+	if (conn == NULL)
+		i_panic("master_auth_request_abort(): tag %u not found", tag);
 
-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;
+	conn->callback = NULL;
 }
--- a/src/lib-master/master-auth.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lib-master/master-auth.h	Mon Oct 19 21:42:09 2009 -0400
@@ -1,26 +1,74 @@
 #ifndef MASTER_AUTH_H
 #define MASTER_AUTH_H
 
+#include "network.h"
+
 struct master_service;
-struct master_auth_request;
-struct master_auth_reply;
+
+/* Major version changes are not backwards compatible,
+   minor version numbers can be ignored. */
+#define AUTH_MASTER_PROTOCOL_MAJOR_VERSION 1
+#define AUTH_MASTER_PROTOCOL_MINOR_VERSION 1
+
+/* Authentication client process's cookie size */
+#define MASTER_AUTH_COOKIE_SIZE (128/8)
+
+/* 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 (1024*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, authentication ID and auth cookie. */
+	pid_t auth_pid;
+	unsigned int auth_id;
+	unsigned int client_pid;
+	uint8_t cookie[MASTER_AUTH_COOKIE_SIZE];
+
+	/* 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
+};
+
+struct master_auth_reply {
+	/* tag=0 are notifications from master */
+	unsigned int tag;
+	enum master_auth_status status;
+	/* PID of the post-login mail process handling this connection */
+	pid_t mail_pid;
+};
 
 typedef void master_auth_callback_t(const struct master_auth_reply *reply,
 				    void *context);
 
+struct master_auth *
+master_auth_init(struct master_service *service, const char *path);
+void master_auth_deinit(struct master_auth **auth);
+
 /* 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);
+void master_auth_request(struct master_auth *auth, int fd,
+			 const struct master_auth_request *request,
+			 const unsigned char *data,
+			 master_auth_callback_t *callback,
+			 void *context, unsigned int *tag_r);
+void master_auth_request_abort(struct master_auth *auth, unsigned int tag);
 
 #endif
--- a/src/lib-master/master-interface.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lib-master/master-interface.h	Mon Oct 19 21:42:09 2009 -0400
@@ -1,8 +1,6 @@
 #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. */
 
@@ -30,46 +28,9 @@
 	/* 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 (1024*2)
-
-/* Authentication client process's cookie size */
-#define MASTER_AUTH_COOKIE_SIZE (128/8)
-
-/* 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, authentication ID and auth cookie. */
-	pid_t auth_pid;
-	unsigned int auth_id;
-	uint8_t cookie[MASTER_AUTH_COOKIE_SIZE];
-
-	/* 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
-};
-
-struct master_auth_reply {
-	/* tag=0 are notifications from master */
-	unsigned int tag;
-	enum master_auth_status status;
-	/* PID of the post-login mail process handling this connection */
-	pid_t mail_pid;
+enum master_login_state {
+	MASTER_LOGIN_STATE_NONFULL = 0,
+	MASTER_LOGIN_STATE_FULL
 };
 
 /* getenv(MASTER_UID_ENV) provides master_status.uid value */
@@ -93,13 +54,10 @@
    if dovecot was started with -p parameter. */
 #define MASTER_SSL_KEY_PASSWORD_ENV "SSL_KEY_PASSWORD"
 
-/* Write pipe to anvil. Currently available only for auth destination
-   services, for others it's /dev/null. */
+/* Write pipe to anvil. */
 #define MASTER_ANVIL_FD 3
-
-/* Socket for sending master_auth_requests. Also used by auth server process
-   as a master socket. */
-#define MASTER_AUTH_FD 4
+/* Master's "all processes full" notification fd for login processes */
+#define MASTER_LOGIN_NOTIFY_FD 4
 
 /* Shared pipe to master, used to send master_status reports */
 #define MASTER_STATUS_FD 5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-master/master-login-auth.c	Mon Oct 19 21:42:09 2009 -0400
@@ -0,0 +1,290 @@
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "network.h"
+#include "ioloop.h"
+#include "istream.h"
+#include "ostream.h"
+#include "hex-binary.h"
+#include "hash.h"
+#include "str.h"
+#include "master-auth.h"
+#include "master-login-auth.h"
+
+#include <stdlib.h>
+
+#define AUTH_MAX_INBUF_SIZE 8192
+
+struct master_login_auth_request {
+	master_login_auth_request_callback_t *callback;
+	void *context;
+};
+
+struct master_login_auth {
+	pool_t pool;
+	const char *auth_socket_path;
+	int refcount;
+
+	int fd;
+	struct io *io;
+	struct istream *input;
+	struct ostream *output;
+
+	unsigned int id_counter;
+	struct hash_table *requests;
+
+	unsigned int version_received:1;
+};
+
+struct master_login_auth *master_login_auth_init(const char *auth_socket_path)
+{
+	struct master_login_auth *auth;
+	pool_t pool;
+
+	pool = pool_alloconly_create("master login auth", 1024);
+	auth = p_new(pool, struct master_login_auth, 1);
+	auth->pool = pool;
+	auth->auth_socket_path = p_strdup(pool, auth_socket_path);
+	auth->refcount = 1;
+	auth->fd = -1;
+	auth->requests = hash_table_create(default_pool, pool, 0, NULL, NULL);
+	return auth;
+}
+
+static void master_login_auth_disconnect(struct master_login_auth *auth)
+{
+	struct hash_iterate_context *iter;
+	void *key, *value;
+
+	iter = hash_table_iterate_init(auth->requests);
+	while (hash_table_iterate(iter, &key, &value)) {
+		struct master_login_auth_request *request = value;
+		request->callback(NULL, request->context);
+		i_free(request);
+	}
+	hash_table_iterate_deinit(&iter);
+	hash_table_clear(auth->requests, FALSE);
+
+	if (auth->io != NULL)
+		io_remove(&auth->io);
+	if (auth->fd != -1) {
+		i_stream_destroy(&auth->input);
+		o_stream_destroy(&auth->output);
+
+		net_disconnect(auth->fd);
+		auth->fd = -1;
+	}
+	auth->version_received = FALSE;
+}
+
+static void master_login_auth_unref(struct master_login_auth **_auth)
+{
+	struct master_login_auth *auth = *_auth;
+
+	*_auth = NULL;
+
+	i_assert(auth->refcount > 0);
+	if (--auth->refcount > 0)
+		return;
+
+	hash_table_destroy(&auth->requests);
+	pool_unref(&auth->pool);
+}
+
+void master_login_auth_deinit(struct master_login_auth **_auth)
+{
+	struct master_login_auth *auth = *_auth;
+
+	*_auth = NULL;
+
+	master_login_auth_disconnect(auth);
+	master_login_auth_unref(&auth);
+}
+
+static struct master_login_auth_request *
+master_login_auth_lookup_request(struct master_login_auth *auth,
+				 unsigned int id)
+{
+	struct master_login_auth_request *request;
+
+	request = hash_table_lookup(auth->requests, POINTER_CAST(id));
+	if (request == NULL) {
+		i_error("Auth server sent reply with unknown ID %u", id);
+		return NULL;
+	}
+
+	hash_table_remove(auth->requests, POINTER_CAST(id));
+	return request;
+}
+
+static bool
+master_login_auth_input_user(struct master_login_auth *auth, const char *args)
+{
+	struct master_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("Auth server sent corrupted USER line");
+		return FALSE;
+	}
+	id = (unsigned int)strtoul(list[0], NULL, 10);
+
+	request = master_login_auth_lookup_request(auth, id);
+	if (request != NULL) {
+		request->callback(list + 1, request->context);
+		i_free(request);
+	}
+	return TRUE;
+}
+
+static bool
+master_login_auth_input_notfound(struct master_login_auth *auth,
+				 const char *args)
+{
+	struct master_login_auth_request *request;
+	unsigned int id;
+
+	id = (unsigned int)strtoul(args, NULL, 10);
+	request = master_login_auth_lookup_request(auth, id);
+	if (request != NULL) {
+		i_error("Auth request not found (timed out?): %u", id);
+		request->callback(NULL, request->context);
+		i_free(request);
+	}
+	return TRUE;
+}
+
+static bool
+master_login_auth_input_fail(struct master_login_auth *auth, const char *args)
+{
+	struct master_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 = master_login_auth_lookup_request(auth, id);
+	if (request != NULL) {
+		request->callback(NULL, request->context);
+		i_free(request);
+	}
+	return TRUE;
+}
+
+static void master_login_auth_input(struct master_login_auth *auth)
+{
+	const char *line;
+	bool ret;
+
+	switch (i_stream_read(auth->input)) {
+	case 0:
+		return;
+	case -1:
+		/* disconnected */
+		master_login_auth_disconnect(auth);
+		return;
+	case -2:
+		/* buffer full */
+		i_error("Auth server sent us too long line");
+		master_login_auth_disconnect(auth);
+		return;
+	}
+
+	if (!auth->version_received) {
+		line = i_stream_next_line(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("Authentication server not compatible with "
+				"master process (mixed old and new binaries?)");
+			master_login_auth_disconnect(auth);
+			return;
+		}
+		auth->version_received = TRUE;
+	}
+
+	auth->refcount++;
+	while ((line = i_stream_next_line(auth->input)) != NULL) {
+		if (strncmp(line, "USER\t", 5) == 0)
+			ret = master_login_auth_input_user(auth, line + 5);
+		else if (strncmp(line, "NOTFOUND\t", 9) == 0)
+			ret = master_login_auth_input_notfound(auth, line + 9);
+		else if (strncmp(line, "FAIL\t", 5) == 0)
+			ret = master_login_auth_input_fail(auth, line + 5);
+		else
+			ret = TRUE;
+
+		if (!ret || auth->input == NULL) {
+			master_login_auth_disconnect(auth);
+			break;
+		}
+	}
+	master_login_auth_unref(&auth);
+}
+
+static int
+master_login_auth_connect(struct master_login_auth *auth)
+{
+	int fd;
+
+	i_assert(auth->fd == -1);
+
+	fd = net_connect_unix(auth->auth_socket_path);
+	if (fd == -1) {
+		i_error("net_connect_unix(%s) failed: %m",
+			auth->auth_socket_path);
+		return -1;
+	}
+	auth->fd = fd;
+	auth->input = i_stream_create_fd(fd, AUTH_MAX_INBUF_SIZE, FALSE);
+	auth->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
+	auth->io = io_add(fd, IO_READ, master_login_auth_input, auth);
+	return 0;
+}
+
+void master_login_auth_request(struct master_login_auth *auth,
+			       const struct master_auth_request *req,
+			       master_login_auth_request_callback_t *callback,
+			       void *context)
+{
+	struct master_login_auth_request *login_req;
+	unsigned int id;
+	string_t *str;
+
+	str = t_str_new(128);
+	if (auth->fd == -1) {
+		if (master_login_auth_connect(auth) < 0) {
+			callback(NULL, context);
+			return;
+		}
+		str_printfa(str, "VERSION\t%u\t%u\n",
+			    AUTH_MASTER_PROTOCOL_MAJOR_VERSION,
+			    AUTH_MASTER_PROTOCOL_MINOR_VERSION);
+	}
+
+	id = ++auth->id_counter;
+	if (id == 0)
+		id++;
+
+	str_printfa(str, "REQUEST\t%u\t%u\t%u\t", id,
+		    req->client_pid, req->auth_id);
+	binary_to_hex_append(str, req->cookie, sizeof(req->cookie));
+	str_append_c(str, '\n');
+	o_stream_send(auth->output, str_data(str), str_len(str));
+
+	login_req = i_new(struct master_login_auth_request, 1);
+	login_req->callback = callback;
+	login_req->context = context;
+	hash_table_insert(auth->requests, POINTER_CAST(id), login_req);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-master/master-login-auth.h	Mon Oct 19 21:42:09 2009 -0400
@@ -0,0 +1,18 @@
+#ifndef MASTER_LOGIN_AUTH_H
+#define MASTER_LOGIN_AUTH_H
+
+struct master_auth_request;
+
+typedef void
+master_login_auth_request_callback_t(const char *const *auth_args,
+				     void *context);
+
+struct master_login_auth *master_login_auth_init(const char *auth_socket_path);
+void master_login_auth_deinit(struct master_login_auth **auth);
+
+void master_login_auth_request(struct master_login_auth *auth,
+			       const struct master_auth_request *req,
+			       master_login_auth_request_callback_t *callback,
+			       void *context);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-master/master-login.c	Mon Oct 19 21:42:09 2009 -0400
@@ -0,0 +1,205 @@
+/* Copyright (c) 2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "ioloop.h"
+#include "ostream.h"
+#include "fdpass.h"
+#include "fd-close-on-exec.h"
+#include "llist.h"
+#include "master-login.h"
+#include "master-login-auth.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+struct master_login_connection {
+	struct master_login_connection *prev, *next;
+
+	struct master_login *login;
+	int fd;
+	struct io *io;
+	struct ostream *output;
+};
+
+struct master_login {
+	master_login_callback_t *callback;
+	struct master_login_connection *conns;
+	struct master_login_auth *auth;
+};
+
+static void master_login_conn_deinit(struct master_login_connection **_conn);
+
+struct master_login *
+master_login_init(const char *auth_socket_path,
+		  master_login_callback_t *callback)
+{
+	struct master_login *login;
+
+	login = i_new(struct master_login, 1);
+	login->callback = callback;
+	login->auth = master_login_auth_init(auth_socket_path);
+	return login;
+}
+
+void master_login_deinit(struct master_login **_login)
+{
+	struct master_login *login = *_login;
+
+	*_login = NULL;
+
+	master_login_auth_deinit(&login->auth);
+	while (login->conns != NULL) {
+		struct master_login_connection *conn = login->conns;
+
+		master_login_conn_deinit(&conn);
+	}
+	i_free(login);
+}
+
+static int
+master_login_conn_read_request(struct master_login_connection *conn,
+			       struct master_auth_request *req_r,
+			       unsigned char data[MASTER_AUTH_MAX_DATA_SIZE],
+			       int *client_fd_r)
+{
+	struct stat st;
+	ssize_t ret;
+
+	*client_fd_r = -1;
+
+	ret = fd_read(conn->fd, req_r, sizeof(*req_r), client_fd_r);
+	if (ret != sizeof(*req_r)) {
+		if (ret == 0) {
+			/* disconnected */
+		} else if (ret > 0) {
+			/* request wasn't fully read */
+			i_error("fd_read() partial input (%d/%d)",
+				(int)ret, (int)sizeof(*req_r));
+		} else {
+			if (errno == EAGAIN)
+				return 0;
+
+			i_error("fd_read() failed: %m");
+		}
+		return -1;
+	}
+
+	if (req_r->data_size != 0) {
+		if (req_r->data_size > MASTER_AUTH_MAX_DATA_SIZE) {
+			i_error("Too large auth data_size sent");
+			return -1;
+		}
+		/* @UNSAFE */
+		ret = read(conn->fd, data, req_r->data_size);
+		if (ret != (ssize_t)req_r->data_size) {
+			if (ret == 0) {
+				/* disconnected */
+			} else if (ret > 0) {
+				/* request wasn't fully read */
+				i_error("Data read partially %d/%u",
+					(int)ret, req_r->data_size);
+			} else {
+				i_error("read(data) failed: %m");
+			}
+			return -1;
+		}
+	}
+
+	if (*client_fd_r == -1) {
+		i_error("Auth request missing a file descriptor");
+		return -1;
+	}
+
+	if (fstat(*client_fd_r, &st) < 0) {
+		i_error("fstat(fd_recv client) failed: %m");
+		return -1;
+	}
+	if (st.st_ino != req_r->ino) {
+		i_error("Auth request inode mismatch: %s != %s",
+			dec2str(st.st_ino), dec2str(req_r->ino));
+		return -1;
+	}
+	return 1;
+}
+
+static void
+master_login_auth_callback(const char *const *auth_args, void *context)
+{
+	struct master_login_client *client = context;
+	struct master_auth_reply reply;
+
+	memset(&reply, 0, sizeof(reply));
+	reply.tag = client->auth_req.tag;
+	reply.status = auth_args != NULL ? MASTER_AUTH_STATUS_OK :
+		MASTER_AUTH_STATUS_INTERNAL_ERROR;
+	reply.mail_pid = getpid();
+	o_stream_send(client->conn->output, &reply, sizeof(reply));
+
+	if (auth_args == NULL) {
+		if (close(client->fd) < 0)
+			i_error("close(fd_recv client) failed: %m");
+		i_free(client);
+		return;
+	}
+
+	client->conn->login->callback(client, auth_args[0], auth_args+1);
+	i_free(client);
+}
+
+static void master_login_conn_input(struct master_login_connection *conn)
+{
+	struct master_auth_request req;
+	struct master_login_client *client;
+	unsigned char data[MASTER_AUTH_MAX_DATA_SIZE];
+	int ret, client_fd;
+
+	ret = master_login_conn_read_request(conn, &req, data, &client_fd);
+	if (ret <= 0) {
+		if (ret < 0)
+			master_login_conn_deinit(&conn);
+		if (client_fd != -1) {
+			if (close(client_fd) < 0)
+				i_error("close(fd_recv client) failed: %m");
+		}
+		return;
+	}
+	fd_close_on_exec(client_fd, TRUE);
+
+	/* @UNSAFE: we have a request. do userdb lookup for it. */
+	client = i_malloc(sizeof(struct master_login_client) + req.data_size);
+	client->conn = conn;
+	client->fd = client_fd;
+	client->auth_req = req;
+	memcpy(client->data, data, req.data_size);
+
+	master_login_auth_request(conn->login->auth, &req,
+				  master_login_auth_callback, client);
+}
+
+void master_login_add(struct master_login *login, int fd)
+{
+	struct master_login_connection *conn;
+
+	conn = i_new(struct master_login_connection, 1);
+	conn->login = login;
+	conn->fd = fd;
+	conn->io = io_add(conn->fd, IO_READ, master_login_conn_input, conn);
+	conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
+
+	DLLIST_PREPEND(&login->conns, conn);
+}
+
+static void master_login_conn_deinit(struct master_login_connection **_conn)
+{
+	struct master_login_connection *conn = *_conn;
+
+	*_conn = NULL;
+
+	DLLIST_REMOVE(&conn->login->conns, conn);
+
+	io_remove(&conn->io);
+	o_stream_unref(&conn->output);
+	if (close(conn->fd) < 0)
+		i_error("close(master login) failed: %m");
+	i_free(conn);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-master/master-login.h	Mon Oct 19 21:42:09 2009 -0400
@@ -0,0 +1,25 @@
+#ifndef MASTER_LOGIN_H
+#define MASTER_LOGIN_H
+
+#include "master-auth.h"
+
+struct master_login_client {
+	struct master_login_connection *conn;
+	int fd;
+
+	struct master_auth_request auth_req;
+	unsigned char data[FLEXIBLE_ARRAY_MEMBER];
+};
+
+typedef void
+master_login_callback_t(const struct master_login_client *client,
+			const char *username, const char *const *extra_fields);
+
+struct master_login *
+master_login_init(const char *auth_socket_path,
+		  master_login_callback_t *callback);
+void master_login_deinit(struct master_login **login);
+
+void master_login_add(struct master_login *login, int fd);
+
+#endif
--- a/src/lib-master/master-service-private.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lib-master/master-service-private.h	Mon Oct 19 21:42:09 2009 -0400
@@ -35,8 +35,8 @@
 	struct master_status master_status;
 
 	void (*avail_overflow_callback)(void);
+	struct timeout *to_overflow_state;
 
-        struct master_auth *auth;
 	master_service_connection_callback_t *callback;
 
 	pool_t set_pool;
@@ -48,6 +48,7 @@
 	unsigned int initial_status_sent:1;
 	unsigned int die_with_master:1;
 	unsigned int call_avail_overflow:1;
+	unsigned int delay_status_updates:1;
 };
 
 void master_service_io_listeners_add(struct master_service *service);
--- a/src/lib-master/master-service.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lib-master/master-service.c	Mon Oct 19 21:42:09 2009 -0400
@@ -26,8 +26,14 @@
 /* getenv(MASTER_DOVECOT_VERSION_ENV) provides master's version number */
 #define MASTER_DOVECOT_VERSION_ENV "DOVECOT_VERSION"
 
+/* when we're full of connections, how often to check if login state has
+   changed. we normally notice it immediately because of a signal, so this is
+   just a fallback against race conditions. */
+#define MASTER_SERVICE_STATE_CHECK_MSECS 1000
+
 struct master_service *master_service;
 
+static void master_service_refresh_login_state(struct master_service *service);
 static void io_listeners_remove(struct master_service *service);
 static void master_status_update(struct master_service *service);
 
@@ -51,6 +57,14 @@
 	io_loop_stop(service->ioloop);
 }
 
+static void
+sig_state_changed(const siginfo_t *si ATTR_UNUSED, void *context)
+{
+	struct master_service *service = context;
+
+	master_service_refresh_login_state(service);
+}
+
 static void master_service_verify_version(struct master_service *service)
 {
 	if (service->version_string != NULL &&
@@ -261,6 +275,10 @@
         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_TRACK_LOGIN_STATE) != 0) {
+		lib_signals_set_handler(SIGUSR1, TRUE,
+					sig_state_changed, service);
+	}
 
 	if ((service->flags & MASTER_SERVICE_FLAG_STANDALONE) == 0) {
 		if (fstat(MASTER_STATUS_FD, &st) < 0 || !S_ISFIFO(st.st_mode))
@@ -475,6 +493,47 @@
 	}
 }
 
+static void master_service_set_login_state(struct master_service *service,
+					   enum master_login_state state)
+{
+	if (service->to_overflow_state != NULL)
+		timeout_remove(&service->to_overflow_state);
+
+	switch (state) {
+	case MASTER_LOGIN_STATE_NONFULL:
+		service->call_avail_overflow = FALSE;
+		if (service->master_status.available_count > 0)
+			return;
+
+		/* some processes should now be able to handle new connections,
+		   although we can't. but there may be race conditions, so
+		   make sure that we'll check again soon if the state has
+		   changed to "full" without our knowledge. */
+		service->to_overflow_state =
+			timeout_add(MASTER_SERVICE_STATE_CHECK_MSECS,
+				    master_service_refresh_login_state,
+				    service);
+		return;
+	case MASTER_LOGIN_STATE_FULL:
+		/* make sure we're listening for more connections */
+		service->call_avail_overflow = TRUE;
+		master_service_io_listeners_add(service);
+		return;
+	}
+	i_error("Invalid master login state: %d", state);
+}
+
+static void master_service_refresh_login_state(struct master_service *service)
+{
+	int ret;
+
+	ret = lseek(MASTER_LOGIN_NOTIFY_FD, 0, SEEK_CUR);
+	if (ret < 0)
+		i_error("lseek(login notify fd) failed: %m");
+	else
+		master_service_set_login_state(service, ret);
+}
+
 static void master_service_close_config_fd(struct master_service *service)
 {
 	if (service->config_fd != -1) {
@@ -493,6 +552,8 @@
 	io_listeners_remove(service);
 
 	master_service_close_config_fd(service);
+	if (service->to_overflow_state != NULL)
+		timeout_remove(&service->to_overflow_state);
 	if (service->io_status_error != NULL)
 		io_remove(&service->io_status_error);
 	if (service->io_status_write != NULL)
@@ -523,8 +584,11 @@
 		/* we are full. stop listening for now, unless overflow
 		   callback destroys one of the existing connections */
 		if (service->call_avail_overflow &&
-		    service->avail_overflow_callback != NULL)
+		    service->avail_overflow_callback != NULL) {
+			service->delay_status_updates = TRUE;
 			service->avail_overflow_callback();
+			service->delay_status_updates = FALSE;
+		}
 
 		if (service->master_status.available_count == 0) {
 			io_listeners_remove(service);
@@ -631,7 +695,7 @@
 {
 	ssize_t ret;
 
-	if (service->master_status.pid == 0)
+	if (service->master_status.pid == 0 || service->delay_status_updates)
 		return; /* closed */
 
 	ret = write(MASTER_STATUS_FD, &service->master_status,
--- a/src/lib-master/master-service.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lib-master/master-service.h	Mon Oct 19 21:42:09 2009 -0400
@@ -17,7 +17,9 @@
 	/* Don't read settings by executing config binary */
 	MASTER_SERVICE_FLAG_NO_CONFIG_SETTINGS	= 0x10,
 	/* Don't read settings from environment */
-	MASTER_SERVICE_FLAG_NO_ENV_SETTINGS	= 0x20
+	MASTER_SERVICE_FLAG_NO_ENV_SETTINGS	= 0x20,
+	/* Use MASTER_LOGIN_NOTIFY_FD to track login overflow state */
+	MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE	= 0x40
 };
 
 struct master_service_connection {
--- a/src/lib-storage/mail-storage-service.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lib-storage/mail-storage-service.c	Mon Oct 19 21:42:09 2009 -0400
@@ -120,7 +120,7 @@
 	for (i = 0; i < count && ret == 0; i++) {
 		line = str[i];
 		if (strncmp(line, "system_groups_user=", 19) == 0)
-			*system_groups_user_r = line + 19;
+			*system_groups_user_r = t_strdup(line + 19);
 		else T_BEGIN {
 			if (strncmp(line, "mail=", 5) == 0) {
 				line = t_strconcat("mail_location=",
@@ -149,19 +149,15 @@
 
 static int
 service_auth_userdb_lookup(struct auth_master_connection *conn,
-			   struct setting_parser_context *set_parser,
 			   const char *service_name,
 			   const struct mail_storage_service_input *input,
 			   const struct mail_user_settings *user_set,
-			   const char **user, const char **system_groups_user_r,
+			   pool_t pool, const char **user,
+			   const char *const **fields_r,
 			   const char **error_r)
 {
 	struct auth_user_info info;
-	struct auth_user_reply reply;
-	const char *system_groups_user, *orig_user = *user;
-	const char *new_username, *const *fields;
-	unsigned int len;
-	pool_t pool;
+	const char *new_username;
 	int ret;
 
 	memset(&info, 0, sizeof(info));
@@ -169,32 +165,19 @@
 	info.local_ip = input->local_ip;
 	info.remote_ip = input->remote_ip;
 
-	pool = pool_alloconly_create("userdb lookup", 1024);
 	ret = auth_master_user_lookup(conn, *user, &info, pool,
-				      &new_username, &fields);
+				      &new_username, fields_r);
 	if (ret > 0) {
-		auth_user_fields_parse(fields, pool, &reply);
-		len = reply.chroot == NULL ? 0 : strlen(reply.chroot);
-
-		*user = t_strdup(new_username);
-		if (user_reply_handle(set_parser, user_set, &reply,
-				      &system_groups_user, error_r) < 0)
-			ret = -1;
-		*system_groups_user_r = t_strdup(system_groups_user);
-	} else {
-		if (ret == 0)
-			*error_r = "unknown user";
-		else
-			*error_r = "userdb lookup failed";
-		*system_groups_user_r = NULL;
-	}
-
-	if (ret > 0 && strcmp(*user, orig_user) != 0) {
-		if (mail_user_set_get_storage_set(user_set)->mail_debug)
-			i_debug("changed username to %s", *user);
-	}
-
-	pool_unref(&pool);
+		if (strcmp(*user, new_username) != 0) {
+			if (mail_user_set_get_storage_set(user_set)->mail_debug)
+				i_debug("changed username to %s", new_username);
+			*user = t_strdup(new_username);
+		}
+		*user = new_username;
+	} else if (ret == 0)
+		*error_r = "unknown user";
+	else
+		*error_r = "userdb lookup failed";
 	return ret;
 }
 
@@ -515,13 +498,14 @@
 	struct mail_user *mail_user;
 	struct auth_master_connection *conn;
 	void **sets;
-	const char *user, *orig_user, *home, *system_groups_user, *error;
+	const char *user, *orig_user, *home, *error;
+	const char *system_groups_user = NULL, *const *userdb_fields = NULL;
 	unsigned int len;
 	bool userdb_lookup;
+	pool_t userdb_pool = NULL;
 
 	io_loop_set_time_moved_callback(current_ioloop,
 					mail_storage_service_time_moved);
-	master_service_init_finish(service);
 
 	userdb_lookup = (flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0;
 	mail_storage_service_init_settings(service, &input, set_roots,
@@ -542,25 +526,36 @@
 		orig_user = user = input.username;
 		conn = auth_master_init(user_set->auth_socket_path,
 					mail_set->mail_debug);
-		if (service_auth_userdb_lookup(conn, service->set_parser,
-					       service->name, &input,
-					       user_set, &user,
-					       &system_groups_user,
-					       &error) <= 0)
+		userdb_pool = pool_alloconly_create("userdb lookup", 1024);
+		if (service_auth_userdb_lookup(conn, service->name, &input,
+					       user_set, userdb_pool, &user,
+					       &userdb_fields, &error) <= 0)
 			i_fatal("%s", error);
 		auth_master_deinit(&conn);
 		input.username = user;
 
 		/* set up logging again in case username changed */
 		mail_storage_service_init_log(service, &input);
+	} else if (input.userdb_fields != NULL) {
+		userdb_fields = input.userdb_fields;
+		userdb_pool = pool_alloconly_create("userdb fields", 1024);
 	}
+	if (userdb_fields != NULL) {
+		struct auth_user_reply reply;
+
+		auth_user_fields_parse(userdb_fields, userdb_pool, &reply);
+		if (user_reply_handle(service->set_parser, user_set, &reply,
+				      &system_groups_user, &error) < 0)
+			i_fatal("%s", error);
+	}
+	if (userdb_pool != NULL)
+		pool_unref(&userdb_pool);
 
 	/* variable strings are expanded in mail_user_init(),
 	   but we need the home sooner so do it separately here. */
 	home = user_expand_varstr(service, &input, user_set->mail_home);
 
 	if (!userdb_lookup) {
-		system_groups_user = NULL;
 		if (*home == '\0' && getenv("HOME") != NULL) {
 			home = getenv("HOME");
 			set_keyval(service->set_parser, "mail_home", home);
@@ -638,7 +633,6 @@
 
 	io_loop_set_time_moved_callback(current_ioloop,
 					mail_storage_service_time_moved);
-	master_service_init_finish(service);
 
 	ctx = i_new(struct mail_storage_service_multi_ctx, 1);
 	ctx->service = service;
@@ -676,6 +670,33 @@
 	return ctx->conn;
 }
 
+static int multi_userdb_lookup(struct mail_storage_service_multi_ctx *ctx,
+			       struct mail_storage_service_multi_user *user,
+			       pool_t userdb_pool, const char **error_r)
+{
+	struct auth_user_reply reply;
+	const char *username, *system_groups_user, *const *userdb_fields;
+	int ret;
+
+	username = user->input.username;
+	ret = service_auth_userdb_lookup(ctx->conn, ctx->service->name,
+					 &user->input, user->user_set,
+					 userdb_pool, &username,
+					 &userdb_fields, error_r);
+	if (ret <= 0)
+		return ret;
+
+	auth_user_fields_parse(userdb_fields, userdb_pool, &reply);
+	ret = user_reply_handle(ctx->service->set_parser, user->user_set,
+				&reply, &system_groups_user, error_r);
+	if (ret <= 0)
+		return ret;
+
+	user->input.username = p_strdup(user->pool, username);
+	user->system_groups_user = p_strdup(user->pool, system_groups_user);
+	return 1;
+}
+
 int mail_storage_service_multi_lookup(struct mail_storage_service_multi_ctx *ctx,
 				      const struct mail_storage_service_input *input,
 				      pool_t pool,
@@ -683,9 +704,9 @@
 				      const char **error_r)
 {
 	struct mail_storage_service_multi_user *user;
-	const char *orig_user, *username;
 	void **sets;
-	int ret;
+	pool_t userdb_pool;
+	int ret = 1;
 
 	user = p_new(pool, struct mail_storage_service_multi_user, 1);
 	memset(user_r, 0, sizeof(user_r));
@@ -698,15 +719,9 @@
 	user->user_set = sets[1];
 
 	if ((ctx->flags & MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP) != 0) {
-		orig_user = username = user->input.username;
-		ret = service_auth_userdb_lookup(ctx->conn, user->set_parser,
-						 ctx->service->name, input,
-						 user->user_set, &username,
-						 &user->system_groups_user,
-						 error_r);
-		if (ret <= 0)
-			return ret;
-		user->input.username = p_strdup(pool, username);
+		userdb_pool = pool_alloconly_create("userdb lookup", 1024);
+		ret = multi_userdb_lookup(ctx, user, userdb_pool, error_r);
+		pool_unref(&userdb_pool);
 	}
 	*user_r = user;
 	return 1;
--- a/src/lib-storage/mail-storage-service.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/lib-storage/mail-storage-service.h	Mon Oct 19 21:42:09 2009 -0400
@@ -25,6 +25,8 @@
 	const char *service;
 	const char *username;
 	struct ip_addr local_ip, remote_ip;
+
+	const char *const *userdb_fields;
 };
 
 struct setting_parser_info;
--- a/src/login-common/client-common.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/login-common/client-common.c	Mon Oct 19 21:42:09 2009 -0400
@@ -118,7 +118,7 @@
 		i_assert(client->authenticating);
 		i_assert(client->refcount > 1);
 		client->authenticating = FALSE;
-		master_auth_request_abort(master_service, client->master_tag);
+		master_auth_request_abort(master_auth, client->master_tag);
 		client->refcount--;
 	} else if (client->auth_request != NULL) {
 		i_assert(client->authenticating);
--- a/src/login-common/common.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/login-common/common.h	Mon Oct 19 21:42:09 2009 -0400
@@ -16,6 +16,7 @@
 extern unsigned int login_default_port;
 
 extern struct auth_client *auth_client;
+extern struct master_auth *master_auth;
 extern bool closing_down;
 extern int anvil_fd;
 
--- a/src/login-common/main.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/login-common/main.c	Mon Oct 19 21:42:09 2009 -0400
@@ -19,6 +19,7 @@
 #include <syslog.h>
 
 struct auth_client *auth_client;
+struct master_auth *master_auth;
 bool closing_down;
 int anvil_fd = -1;
 
@@ -139,10 +140,10 @@
 
 	auth_client = auth_client_init("auth", (unsigned int)getpid(), FALSE);
         auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
+	master_auth = master_auth_init(master_service, login_protocol);
 
 	clients_init();
 	login_proxy_init();
-	master_auth_init(master_service);
 }
 
 static void main_deinit(void)
@@ -153,23 +154,25 @@
 	if (auth_client != NULL)
 		auth_client_deinit(&auth_client);
 	clients_deinit();
+	master_auth_deinit(&master_auth);
 
 	if (anvil_fd != -1) {
 		if (close(anvil_fd) < 0)
 			i_error("close(anvil) failed: %m");
 	}
-	master_auth_deinit(master_service);
 }
 
 int main(int argc, char *argv[], char *envp[])
 {
+	enum master_service_flags service_flags =
+		MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN |
+		MASTER_SERVICE_FLAG_TRACK_LOGIN_STATE;
 	const char *getopt_str;
 	pool_t set_pool;
 	int c;
 
-	master_service = master_service_init(login_process_name,
-					MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN,
-					argc, argv);
+	master_service = master_service_init(login_process_name, service_flags,
+					     argc, argv);
 	master_service_init_log(master_service, t_strconcat(
 		login_process_name, ": ", NULL));
 
--- a/src/login-common/sasl-server.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/login-common/sasl-server.c	Mon Oct 19 21:42:09 2009 -0400
@@ -114,6 +114,7 @@
 	req.auth_id = auth_client_request_get_id(request);
 	req.local_ip = client->local_ip;
 	req.remote_ip = client->ip;
+	req.client_pid = getpid();
 
 	cookie = auth_client_get_cookie(auth_client);
 	if (hex_to_binary(cookie, buf) == 0 && buf->used == sizeof(req.cookie))
@@ -127,9 +128,8 @@
 	buffer_append(buf, data, size);
 	req.data_size = buf->used;
 
-	client->master_tag =
-		master_auth_request(master_service, client->fd, &req, buf->data,
-				    master_auth_callback, client);
+	master_auth_request(master_auth, client->fd, &req, buf->data,
+			    master_auth_callback, client, &client->master_tag);
 }
 
 static bool anvil_has_too_many_connections(struct client *client)
--- a/src/master/Makefile.am	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/master/Makefile.am	Mon Oct 19 21:42:09 2009 -0400
@@ -22,8 +22,6 @@
 	main.c \
 	master-settings.c \
 	service-anvil.c \
-	service-auth-server.c \
-	service-auth-source.c \
 	service-listen.c \
 	service-log.c \
 	service-monitor.c \
@@ -37,8 +35,6 @@
 	dup2-array.h \
 	master-settings.h \
 	service-anvil.h \
-	service-auth-server.h \
-	service-auth-source.h \
 	service-listen.h \
 	service-log.h \
 	service-monitor.h \
--- a/src/master/main.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/master/main.c	Mon Oct 19 21:42:09 2009 -0400
@@ -200,42 +200,12 @@
 		i_error("unlink(%s) failed: %m", path);
 }
 
-static bool
-services_has_name(const struct master_settings *set, const char *name)
-{
-	struct service_settings *const *services;
-	unsigned int i, count;
-
-	services = array_get(&set->services, &count);
-	for (i = 0; i < count; i++) {
-		if (strcmp(services[i]->name, name) == 0)
-			return TRUE;
-	}
-	return FALSE;
-}
-
-static bool services_have_auth_destinations(const struct master_settings *set)
-{
-	struct service_settings *const *services;
-	unsigned int i, count;
-
-	services = array_get(&set->services, &count);
-	for (i = 0; i < count; i++) {
-		if (strcmp(services[i]->type, "auth-source") == 0) {
-			if (services_has_name(set, services[i]->auth_dest_service))
-				return TRUE;
-		}
-	}
-	return FALSE;
-}
-
 static void auth_warning_print(const struct master_settings *set)
 {
 	struct stat st;
 
 	auth_success_written = stat(AUTH_SUCCESS_PATH, &st) == 0;
-	if (!auth_success_written && !set->auth_debug &&
-	    services_have_auth_destinations(set)) {
+	if (!auth_success_written && !set->auth_debug) {
 		fprintf(stderr,
 "If you have trouble with authentication failures,\n"
 "enable auth_debug setting. See http://wiki.dovecot.org/WhyDoesItNotWork\n"
@@ -334,8 +304,7 @@
 
 	if (services->config->process_avail == 0) {
 		/* we can't reload config if there's no config process. */
-		if (service_process_create(services->config,
-					   NULL, NULL) == NULL) {
+		if (service_process_create(services->config) == NULL) {
 			i_error("Can't reload configuration because "
 				"we couldn't create a config process");
 			return;
--- a/src/master/master-settings.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/master/master-settings.c	Mon Oct 19 21:42:09 2009 -0400
@@ -4,6 +4,7 @@
 #include "array.h"
 #include "env-util.h"
 #include "istream.h"
+#include "network.h"
 #include "str.h"
 #include "mkdir-parents.h"
 #include "safe-mkdir.h"
@@ -96,7 +97,6 @@
 	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),
 
@@ -128,7 +128,6 @@
 	MEMBER(privileged_group) "",
 	MEMBER(extra_groups) "",
 	MEMBER(chroot) "",
-	MEMBER(auth_dest_service) "",
 
 	MEMBER(drop_priv_before_exec) FALSE,
 
@@ -240,6 +239,27 @@
 	}
 }
 
+static bool master_settings_parse_type(struct service_settings *set,
+				       const char **error_r)
+{
+	if (*set->type == '\0')
+		set->parsed_type = SERVICE_TYPE_UNKNOWN;
+	else if (strcmp(set->type, "log") == 0)
+		set->parsed_type = SERVICE_TYPE_LOG;
+	else if (strcmp(set->type, "config") == 0)
+		set->parsed_type = SERVICE_TYPE_CONFIG;
+	else if (strcmp(set->type, "anvil") == 0)
+		set->parsed_type = SERVICE_TYPE_ANVIL;
+	else if (strcmp(set->type, "login") == 0)
+		set->parsed_type = SERVICE_TYPE_LOGIN;
+	else {
+		*error_r = t_strconcat("Unknown service type: ",
+				       set->type, NULL);
+		return FALSE;
+	}
+	return TRUE;
+}
+
 static bool
 master_settings_verify(void *_set, pool_t pool, const char **error_r)
 {
@@ -274,16 +294,8 @@
 				"Service #%d is missing name", i);
 			return FALSE;
 		}
-		if (*service->type != '\0' &&
-		    strcmp(service->type, "log") != 0 &&
-		    strcmp(service->type, "config") != 0 &&
-		    strcmp(service->type, "anvil") != 0 &&
-		    strcmp(service->type, "auth") != 0 &&
-		    strcmp(service->type, "auth-source") != 0) {
-			*error_r = t_strconcat("Unknown service type: ",
-					       service->type, NULL);
+		if (!master_settings_parse_type(service, error_r))
 			return FALSE;
-		}
 		for (j = 0; j < i; j++) {
 			if (strcmp(service->name, services[j]->name) == 0) {
 				*error_r = t_strdup_printf(
@@ -342,8 +354,7 @@
 
 	services = array_get(&set->services, &count);
 	for (i = 0; i < count; i++) {
-		if (strcmp(services[i]->type, "auth-source") == 0 &&
-		    strstr(services[i]->name, "-login") != NULL) {
+		if (strcmp(services[i]->type, "login") == 0) {
 			if (strstr(services[i]->executable, " -D") != NULL)
 				cores = TRUE;
 			(void)get_uidgid(services[i]->user, &uid, gid_r, &error);
@@ -364,8 +375,7 @@
 
 	services = array_get(&set->services, &count);
 	for (i = 0; i < count; i++) {
-		if (strcmp(services[i]->type, "auth") == 0 &&
-		    array_is_created(&services[i]->unix_listeners)) {
+		if (array_is_created(&services[i]->unix_listeners)) {
 			u = array_get(&services[i]->unix_listeners, &count2);
 			for (j = 0; j < count2; j++) {
 				if (strncmp(u[j]->path, dir, strlen(dir)) == 0)
--- a/src/master/master-settings.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/master/master-settings.h	Mon Oct 19 21:42:09 2009 -0400
@@ -1,6 +1,16 @@
 #ifndef MASTER_SETTINGS_H
 #define MASTER_SETTINGS_H
 
+/* <settings checks> */
+enum service_type {
+	SERVICE_TYPE_UNKNOWN,
+	SERVICE_TYPE_LOG,
+	SERVICE_TYPE_ANVIL,
+	SERVICE_TYPE_CONFIG,
+	SERVICE_TYPE_LOGIN
+};
+/* </settings checks> */
+
 struct file_listener_settings {
 	const char *path;
 	unsigned int mode;
@@ -27,7 +37,6 @@
 	const char *privileged_group;
 	const char *extra_groups;
 	const char *chroot;
-	const char *auth_dest_service;
 
 	bool drop_priv_before_exec;
 
@@ -40,6 +49,8 @@
 	ARRAY_TYPE(file_listener_settings) unix_listeners;
 	ARRAY_TYPE(file_listener_settings) fifo_listeners;
 	ARRAY_DEFINE(inet_listeners, struct inet_listener_settings *);
+
+	enum service_type parsed_type;
 };
 
 struct master_settings {
--- a/src/master/service-auth-server.c	Mon Oct 19 18:34:00 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-/* 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) {
-		service_error(process->process.service,
-			      "authentication service %s "
-			      "sent reply with unknown ID %u",
-			      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 struct service *
-auth_process_get_dest_service(struct service_process_auth_source *process)
-{
-	struct service *service = process->process.service;
-
-	if (!service->list->destroyed)
-		return service->auth_dest_service;
-
-	service = service_lookup(services, service->set->auth_dest_service);
-	if (service == NULL) {
-		i_warning("service(%s): Lost destination service %s",
-			  service->set->name, service->set->auth_dest_service);
-	}
-	return service;
-}
-
-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;
-		struct service_process *dest_process;
-
-		dest_service = auth_process_get_dest_service(request->process);
-		dest_process = dest_service == NULL ? NULL :
-			service_process_create(dest_service, list + 1, request);
-		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,
-					MASTER_AUTH_STATUS_INTERNAL_ERROR);
-		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,
-					MASTER_AUTH_STATUS_INTERNAL_ERROR);
-		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 */
-		service_error(process->process.service,
-			      "authentication server process %s "
-			      "sent us too long line",
-			      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) {
-			service_error(process->process.service,
-				      "authentication server process %s "
-				      "not compatible with master process "
-				      "(mixed old and new binaries?)",
-				      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);
-}
--- a/src/master/service-auth-server.h	Mon Oct 19 18:34:00 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-#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
--- a/src/master/service-auth-source.c	Mon Oct 19 18:34:00 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
-
-#include "common.h"
-#include "hash.h"
-#include "str.h"
-#include "hex-binary.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,
-			 const uint8_t cookie[MASTER_AUTH_COOKIE_SIZE])
-{
-	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\t",
-		    tag, dec2str(source_process->process.pid), auth_id);
-	binary_to_hex_append(str, cookie, MASTER_AUTH_COOKIE_SIZE);
-	str_append_c(str, '\n');
-
-	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 */
-			service_error(service, "fd_read() partial input (%d/%d)",
-				      (int)ret, (int)sizeof(*req));
-		} else {
-			if (errno == EAGAIN)
-				return 0;
-
-			service_error(service, "fd_read() failed: %m");
-		}
-		return -1;
-	}
-
-	if (req->data_size != 0) {
-		if (req->data_size > MASTER_AUTH_MAX_DATA_SIZE) {
-			service_error(service, "Too large auth data_size sent");
-			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 */
-				service_error(service,
-					      "Data read partially %d/%u",
-					      (int)ret, req->data_size);
-			} else {
-				service_error(service, "read(data) failed: %m");
-			}
-			return -1;
-		}
-	}
-
-	if (*client_fd_r == -1) {
-		service_error(service, "Auth request missing a file descriptor");
-		return -1;
-	}
-
-	if (fstat(*client_fd_r, &st) < 0) {
-		service_error(service, "fstat(auth dest fd) failed: %m");
-		return -1;
-	}
-	if (st.st_ino != req->ino) {
-		service_error(service, "Auth request inode mismatch: %s != %s",
-			      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_pids, &req.auth_pid);
-	if (auth_process == NULL) {
-		service_error(service, "authentication request for unknown "
-			      "auth server PID %s", dec2str(req.auth_pid));
-		service_process_auth_source_send_reply(process, req.tag,
-			MASTER_AUTH_STATUS_INTERNAL_ERROR);
-		(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->set->name,
-				  dec2str(req.auth_pid));
-                        auth_process->auth_busy_stamp = ioloop_time;
-		}
-		service_process_auth_source_send_reply(process, req.tag,
-			MASTER_AUTH_STATUS_INTERNAL_ERROR);
-		(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,
-				       req.cookie);
-
-	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);
-}
-
-void service_processes_auth_source_notify(struct service *service,
-					  bool all_processes_created)
-{
-	struct hash_iterate_context *iter;
-	void *key, *value;
-	enum master_auth_status status;
-
-	i_assert(service->type == SERVICE_TYPE_AUTH_SOURCE);
-
-	status = all_processes_created ? 1 : 0;
-
-	iter = hash_table_iterate_init(service_pids);
-	while (hash_table_iterate(iter, &key, &value)) {
-		struct service_process *process = value;
-		struct service_process_auth_source *auth_process;
-
-		if (process->service != service)
-			continue;
-
-		auth_process = (struct service_process_auth_source *)process;
-		if (auth_process->last_notify_status != (int)status) {
-			auth_process->last_notify_status = (int)status;
-			service_process_auth_source_send_reply(auth_process,
-							       0, status);
-		}
-	}
-	hash_table_iterate_deinit(&iter);
-}
--- a/src/master/service-auth-source.h	Mon Oct 19 18:34:00 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-#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);
-
-void service_processes_auth_source_notify(struct service *service,
-					  bool all_processes_created);
-
-#endif
--- a/src/master/service-monitor.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/master/service-monitor.c	Mon Oct 19 21:42:09 2009 -0400
@@ -5,8 +5,9 @@
 #include "ioloop.h"
 #include "fd-close-on-exec.h"
 #include "hash.h"
+#include "str.h"
+#include "safe-mkstemp.h"
 #include "service.h"
-#include "service-auth-source.h"
 #include "service-process.h"
 #include "service-process-notify.h"
 #include "service-anvil.h"
@@ -59,6 +60,11 @@
 	i_assert(service->process_avail > 0);
 	service->process_avail--;
 
+	if (service->type == SERVICE_TYPE_LOGIN &&
+	    service->process_avail == 0 &&
+	    service->process_count == service->process_limit)
+		service_login_notify(service, TRUE);
+
 	/* we may need to start more  */
 	service_monitor_start_extra_avail(service);
 	service_monitor_listen_start(service);
@@ -90,6 +96,8 @@
 					    process);
 		}
 	}
+	if (service->type == SERVICE_TYPE_LOGIN)
+		service_login_notify(service, FALSE);
 }
 
 static void service_status_input(struct service *service)
@@ -98,6 +106,7 @@
         struct service_process *process;
 	ssize_t ret;
 
+	status.pid = 0;
 	ret = read(service->status_fd[0], &status, sizeof(status));
 	switch (ret) {
 	case 0:
@@ -177,11 +186,11 @@
 	service->listen_pending = TRUE;
 	service_monitor_listen_stop(service);
 
-	if (service->type == SERVICE_TYPE_AUTH_SOURCE) {
+	if (service->type == SERVICE_TYPE_LOGIN) {
 		/* reached process limit, notify processes that they
 		   need to start killing existing connections if they
 		   reach connection limit */
-		service_processes_auth_source_notify(service, TRUE);
+		service_login_notify(service, TRUE);
 	}
 }
 
@@ -197,7 +206,7 @@
 	}
 
 	/* create a child process and let it accept() this connection */
-	if (service_process_create(service, NULL, NULL) == NULL)
+	if (service_process_create(service) == NULL)
 		service_monitor_throttle(service);
 	else
 		service_monitor_listen_stop(service);
@@ -215,7 +224,7 @@
 		count = service->process_limit - service->process_count;
 
 	for (i = 0; i < count; i++) {
-		if (service_process_create(service, NULL, NULL) == NULL) {
+		if (service_process_create(service) == NULL) {
 			service_monitor_throttle(service);
 			break;
 		}
@@ -263,6 +272,38 @@
 	service->listening = FALSE;
 }
 
+static int service_login_create_notify_fd(struct service *service)
+{
+	int fd;
+
+	if (service->login_notify_fd != -1)
+		return 0;
+
+	T_BEGIN {
+		string_t *prefix = t_str_new(128);
+		const char *path;
+
+		str_append(prefix, "/tmp/dovecot-master");
+
+		fd = safe_mkstemp(prefix, 0600, (uid_t)-1, (gid_t)-1);
+		path = str_c(prefix);
+
+		if (fd == -1) {
+			service_error(service, "safe_mkstemp(%s) failed: %m",
+				      path);
+		} else if (unlink(path) < 0) {
+			service_error(service, "unlink(%s) failed: %m", path);
+		} else {
+			fd_close_on_exec(fd, TRUE);
+			service->login_notify_fd = fd;
+		}
+	} T_END;
+
+	if (fd != service->login_notify_fd)
+		(void)close(fd);
+	return fd == -1 ? -1 : 0;
+}
+
 void services_monitor_start(struct service_list *service_list)
 {
 	struct service *const *services;
@@ -273,6 +314,10 @@
 
 	services = array_get(&service_list->services, &count);
 	for (i = 0; i < count; i++) {
+		if (services[i]->type == SERVICE_TYPE_LOGIN) {
+			if (service_login_create_notify_fd(services[i]) < 0)
+				continue;
+		}
 		if (services[i]->status_fd[0] == -1) {
 			/* we haven't yet created status pipe */
 			if (pipe(services[i]->status_fd) < 0) {
@@ -289,14 +334,13 @@
 				io_add(services[i]->status_fd[0], IO_READ,
 				       service_status_input, services[i]);
 		}
-
 		if (services[i]->status_fd[0] != -1) {
 			service_monitor_start_extra_avail(services[i]);
 			service_monitor_listen_start(services[i]);
 		}
 	}
 
-	if (service_process_create(service_list->log, NULL, NULL) != NULL)
+	if (service_process_create(service_list->log) != NULL)
 		service_monitor_listen_stop(service_list->log);
 }
 
@@ -316,6 +360,15 @@
 			service->status_fd[i] = -1;
 		}
 	}
+	if (service->login_notify_fd != -1) {
+		if (close(service->login_notify_fd) < 0) {
+			service_error(service,
+				      "close(login notify fd) failed: %m");
+		}
+		service->login_notify_fd = -1;
+	}
+	if (service->to_login_notify != NULL)
+		timeout_remove(&service->to_login_notify);
 	service_monitor_listen_stop(service);
 
 	if (service->to_throttle != NULL)
--- a/src/master/service-process.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/master/service-process.c	Mon Oct 19 21:42:09 2009 -0400
@@ -23,8 +23,6 @@
 #include "service.h"
 #include "service-anvil.h"
 #include "service-log.h"
-#include "service-auth-server.h"
-#include "service-auth-source.h"
 #include "service-process-notify.h"
 #include "service-process.h"
 
@@ -44,8 +42,7 @@
 #define CHDIR_WARN_SECS 10
 
 static void
-service_dup_fds(struct service *service, int auth_fd, int std_fd,
-		bool give_anvil_fd)
+service_dup_fds(struct service *service)
 {
 	struct service_listener *const *listeners;
 	ARRAY_TYPE(dup2) dups;
@@ -105,33 +102,14 @@
 		}
 	}
 
-	if (!give_anvil_fd)
-		dup2_append(&dups, null_fd, MASTER_ANVIL_FD);
-	else {
-		dup2_append(&dups, service->list->blocking_anvil_fd[1],
-			    MASTER_ANVIL_FD);
+	if (service->login_notify_fd != -1) {
+		dup2_append(&dups, service->login_notify_fd,
+			    MASTER_LOGIN_NOTIFY_FD);
 	}
+	dup2_append(&dups, service->list->blocking_anvil_fd[1],
+		    MASTER_ANVIL_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;
-	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
@@ -158,194 +136,11 @@
 }
 
 static void
-validate_uid_gid(struct master_settings *set,
-		 uid_t uid, gid_t gid, const char *user,
-		 const struct service_process_auth_request *request)
-{
-	struct service_process *request_process =
-		request == NULL ? NULL : &request->process->process;
-
-	if (uid == 0) {
-		i_fatal("User %s not allowed to log in using UNIX UID 0 "
-			"(root logins are never allowed)", user);
-	}
-
-	if (request != NULL && request_process->service->uid == uid &&
-	    master_uid != uid) {
-		struct passwd *pw;
-
-		pw = getpwuid(uid);
-		i_fatal("User %s not allowed to log in using %s's "
-			"UNIX UID %s%s (see http://wiki.dovecot.org/UserIds)",
-			user, request_process->service->set->name,
-			dec2str(uid), pw == NULL ? "" :
-			t_strdup_printf("(%s)", pw->pw_name));
-	}
-
-	if (uid < (uid_t)set->first_valid_uid ||
-	    (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
-		struct passwd *pw;
-		bool low = uid < (uid_t)set->first_valid_uid;
-
-		pw = getpwuid(uid);
-		i_fatal("User %s not allowed to log in using too %s "
-			"UNIX UID %s%s (see %s in config file)",
-			user, low ? "low" : "high",
-			dec2str(uid), pw == NULL ? "" :
-			t_strdup_printf("(%s)", pw->pw_name),
-			low ? "first_valid_uid" : "last_valid_uid");
-	}
-
-	if (gid < (gid_t)set->first_valid_gid ||
-	    (set->last_valid_gid != 0 && gid > (gid_t)set->last_valid_gid)) {
-		struct group *gr;
-		bool low = gid < (gid_t)set->first_valid_gid;
-
-		gr = getgrgid(gid);
-		i_fatal("User %s not allowed to log in using too %s primary "
-			"UNIX group ID %s%s (see %s in config file)",
-			user, low ? "low" : "high",
-			dec2str(gid), gr == NULL ? "" :
-			t_strdup_printf("(%s)", gr->gr_name),
-			low ? "first_valid_gid" : "last_valid_gid");
-	}
-}
-
-static void auth_args_apply(const char *const *args,
-			    struct restrict_access_settings *rset,
-			    const char **home)
+drop_privileges(struct service *service)
 {
-	const char *key, *value;
-	string_t *expanded_vars;
-
-	expanded_vars = t_str_new(128);
-	str_append(expanded_vars, "VARS_EXPANDED=");
-	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 */
-			value = strchr(*args, '=');
-			if (value == NULL) {
-				/* boolean */
-				key = *args;
-				value = "=1";
-			} else {
-				key = t_strdup_until(*args, value);
-				if (strcmp(key, "mail") == 0) {
-					/* FIXME: kind of ugly to have it
-					   here.. */
-					key = "mail_location";
-				}
-			}
-			str_append(expanded_vars, key);
-			str_append_c(expanded_vars, ' ');
-			env_put(t_strconcat(t_str_ucase(key), value, NULL));
-		}
-	}
-	env_put(str_c(expanded_vars));
-}        
-
-static void auth_success_write(void)
-{
-	int fd;
-
-	if (auth_success_written)
-		return;
-
-	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;
-}
-
-static void chdir_to_home(const struct restrict_access_settings *rset,
-			  const char *user, const char *home)
-{
-	unsigned int left;
-	int ret, chdir_errno;
-
-	if (*home != '/') {
-		i_fatal("user %s: Relative home directory paths not supported: "
-			"%s", user, home);
-	}
-
-	/* 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 (rset->uid != master_uid) {
-		if (setegid(rset->gid) < 0)
-			i_fatal("setegid(%s) failed: %m", dec2str(rset->gid));
-		if (seteuid(rset->uid) < 0)
-			i_fatal("seteuid(%s) failed: %m", dec2str(rset->uid));
-	}
-
-	alarm(CHDIR_TIMEOUT);
-	ret = chdir(home);
-	chdir_errno = errno;
-	if ((left = alarm(0)) < CHDIR_TIMEOUT - CHDIR_WARN_SECS) {
-		i_warning("user %s: chdir(%s) blocked for %u secs",
-			  user, home, CHDIR_TIMEOUT - left);
-	}
-
-	errno = chdir_errno;
-	if (ret == 0) {
-		/* chdir succeeded */
-	} else if ((errno == ENOENT || errno == ENOTDIR || errno == EINTR) &&
-		   rset->chroot_dir == NULL) {
-		/* Not chrooted, fallback to using /tmp.
-
-		   ENOENT: No home directory yet, but it might be automatically
-		     created by the service process, so don't complain.
-		   ENOTDIR: This check is mainly for /dev/null home directory.
-		   EINTR: chdir() timed out. */
-	} else if (errno == EACCES) {
-		i_fatal("user %s: %s", user, eacces_error_get("chdir", home));
-	} else {
-		i_fatal("user %s: chdir(%s) failed with uid %s: %m",
-			user, home, dec2str(rset->uid));
-	}
-	/* Change UID back. No need to change GID back, it doesn't
-	   really matter. */
-	if (rset->uid != master_uid && seteuid(master_uid) < 0)
-		i_fatal("seteuid(%s) failed: %m", dec2str(master_uid));
-
-	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");
-	}
-}
-
-static void
-drop_privileges(struct service *service, const char *const *auth_args,
-		const struct service_process_auth_request *request)
-{
-	struct master_settings *master_set = service->set->master_set;
 	struct restrict_access_settings rset;
-	const char *user, *home = NULL;
 	bool disallow_root;
 
-	if (auth_args != NULL && service->set->master_set->mail_debug)
-		env_put("DEBUG=1");
-
 	if (service->vsz_limit != 0)
 		restrict_process_size(service->vsz_limit, -1U);
 
@@ -357,31 +152,9 @@
 		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_success_write();
-		auth_args_apply(auth_args + 1, &rset, &home);
-
-		validate_uid_gid(master_set, rset.uid, rset.gid, user,
-				 request);
-	}
-
-	if (home != NULL)
-		chdir_to_home(&rset, user, home);
-
 	if (service->set->drop_priv_before_exec) {
-		disallow_root = service->type == SERVICE_TYPE_AUTH_SERVER ||
-			service->type == SERVICE_TYPE_AUTH_SOURCE;
-		restrict_access(&rset, home, disallow_root);
+		disallow_root = service->type == SERVICE_TYPE_LOGIN;
+		restrict_access(&rset, NULL, disallow_root);
 	} else {
 		restrict_access_set_env(&rset);
 	}
@@ -451,120 +224,38 @@
 	timeout_remove(&process->to_status);
 }
 
-static void
-handle_request(const struct service_process_auth_request *request)
-{
-	string_t *str;
-
-	if (request == NULL)
-		return;
-
-	if (request->data_size > 0) {
-		str = t_str_new(request->data_size*3);
-		str_append(str, "CLIENT_INPUT=");
-		base64_encode(request->data, request->data_size, str);
-		env_put(str_c(str));
-	}
-
-	env_put(t_strconcat("LOCAL_IP=", net_ip2addr(&request->local_ip), NULL));
-	env_put(t_strconcat("IP=", net_ip2addr(&request->remote_ip), NULL));
-}
-
-static const char **
-get_extra_args(struct service *service,
-	       const struct service_process_auth_request *request,
-	       const char *const *auth_args)
-{
-	const char **extra;
-
-	if (!service->set->master_set->verbose_proctitle || request == NULL)
-		return NULL;
-
-	extra = t_new(const char *, 2);
-	extra[0] = t_strdup_printf("[%s %s]", auth_args[0],
-				   net_ip2addr(&request->remote_ip));
-	return extra;
-}
-
-struct service_process *
-service_process_create(struct service *service, const char *const *auth_args,
-		       const struct service_process_auth_request *request)
+struct service_process *service_process_create(struct service *service)
 {
 	static unsigned int uid_counter = 0;
 	struct service_process *process;
 	unsigned int uid = ++uid_counter;
-	int fd[2];
 	pid_t pid;
 
 	if (service->to_throttle != NULL) {
 		/* throttling service, don't create new processes */
 		return NULL;
 	}
-	if (service->process_count >= service->process_limit) {
-		/* we should get here only with auth dest services */
-		i_warning("service(%s): process_limit reached, "
-			  "dropping this client connection",
-			  service->set->name);
-		return NULL;
-	}
-
-	switch (service->type) {
-	case SERVICE_TYPE_AUTH_SOURCE:
-	case SERVICE_TYPE_AUTH_SERVER:
-		if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
-			service_error(service, "socketpair() failed: %m");
-			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) {
 		service_error(service, "fork() failed: %m");
-		if (fd[0] != -1) {
-			(void)close(fd[0]);
-			(void)close(fd[1]);
-		}
 		return NULL;
 	}
 	if (pid == 0) {
 		/* child */
-		if (fd[0] != -1)
-			(void)close(fd[0]);
 		service_process_setup_environment(service, uid);
-		handle_request(request);
-		service_dup_fds(service, fd[1], request == NULL ? -1 :
-				request->fd, auth_args != NULL);
-		drop_privileges(service, auth_args, request);
-		process_exec(service->executable,
-			     get_extra_args(service, request, auth_args));
+		service_dup_fds(service);
+		drop_privileges(service);
+		process_exec(service->executable, NULL);
 	}
 
 	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;
 	case SERVICE_TYPE_ANVIL:
 		service_anvil_process_created(service);
 		/* fall through */
 	default:
 		process = i_new(struct service_process, 1);
 		process->service = service;
-		i_assert(fd[0] == -1);
 		break;
 	}
 
@@ -604,12 +295,6 @@
 		timeout_remove(&process->to_idle);
 
 	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;
 	case SERVICE_TYPE_ANVIL:
 		service_anvil_process_destroyed(service);
 		break;
@@ -624,8 +309,8 @@
 	service_process_unref(process);
 
 	if (service->process_count < service->process_limit &&
-	    service->type == SERVICE_TYPE_AUTH_SOURCE)
-		service_processes_auth_source_notify(service, FALSE);
+	    service->type == SERVICE_TYPE_LOGIN)
+		service_login_notify(service, FALSE);
 
 	service_list_unref(service_list);
 }
--- a/src/master/service-process.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/master/service-process.h	Mon Oct 19 21:42:09 2009 -0400
@@ -26,50 +26,10 @@
 	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;
-};
+#define SERVICE_PROCESS_IS_INITIALIZED(process) \
+	((process)->to_status == NULL)
 
-struct service_process_auth_source {
-	struct service_process process;
-
-	int last_notify_status;
-
-	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,
-		       const struct service_process_auth_request *request);
+struct service_process *service_process_create(struct service *service);
 void service_process_destroy(struct service_process *process);
 
 void service_process_ref(struct service_process *process);
--- a/src/master/service.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/master/service.c	Mon Oct 19 21:42:09 2009 -0400
@@ -17,6 +17,7 @@
 #include <signal.h>
 
 #define SERVICE_DIE_TIMEOUT_MSECS (1000*10)
+#define SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS 2
 
 struct hash_table *service_pids;
 
@@ -177,23 +178,7 @@
 
 	service->vsz_limit = set->vsz_limit != 0 ? set->vsz_limit :
 		set->master_set->default_vsz_limit;
-
-	service->type = SERVICE_TYPE_UNKNOWN;
-	if (*set->type != '\0') {
-		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, "anvil") == 0)
-			service->type = SERVICE_TYPE_ANVIL;
-		else if (strcmp(set->type, "auth") == 0)
-			service->type = SERVICE_TYPE_AUTH_SERVER;
-		else if (strcmp(set->type, "auth-source") == 0)
-			service->type = SERVICE_TYPE_AUTH_SOURCE;
-	}
-
-	if (*set->auth_dest_service != '\0')
-		service->type = SERVICE_TYPE_AUTH_SOURCE;
+	service->type = service->set->parsed_type;
 
 	if (set->process_limit == 0) {
 		/* unlimited */
@@ -258,6 +243,7 @@
 	service->status_fd[0] = -1;
 	service->status_fd[1] = -1;
 	service->log_process_internal_fd = -1;
+	service->login_notify_fd = -1;
 
 	if (array_is_created(&set->unix_listeners))
 		unix_listeners = array_get(&set->unix_listeners, &unix_count);
@@ -358,7 +344,7 @@
 		    struct service_list **services_r, const char **error_r)
 {
 	struct service_list *service_list;
-	struct service *service, *const *services;
+	struct service *service;
 	struct service_settings *const *service_settings;
 	pool_t pool;
 	const char *error;
@@ -411,23 +397,6 @@
 		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 -1;
-			}
-		}
-	}
-
 	if (service_list->log == NULL) {
 		*error_r = "log service not specified";
 		return -1;
@@ -452,6 +421,12 @@
 	for (; process != NULL; process = process->next) {
 		i_assert(process->service == service);
 
+		if (!SERVICE_PROCESS_IS_INITIALIZED(process) &&
+		    signo != SIGKILL) {
+			/* too early to signal it */
+			continue;
+		}
+		    
 		if (kill(process->pid, signo) < 0 && errno != ESRCH) {
 			service_error(service, "kill(%s, %d) failed: %m",
 				      dec2str(process->pid), signo);
@@ -459,6 +434,51 @@
 	}
 }
 
+static void service_login_notify_send(struct service *service)
+{
+	service->last_login_notify_time = ioloop_time;
+	if (service->to_login_notify != NULL)
+		timeout_remove(&service->to_login_notify);
+
+	service_signal(service, SIGUSR1);
+}
+
+static void service_login_notify_timeout(struct service *service)
+{
+	service_login_notify_send(service);
+}
+
+void service_login_notify(struct service *service, bool all_processes_full)
+{
+	enum master_login_state state;
+	int diff;
+
+	if (service->last_login_full_notify == all_processes_full ||
+	    service->login_notify_fd == -1)
+		return;
+
+	/* change the state always immediately. it's cheap. */
+	service->last_login_full_notify = all_processes_full;
+	state = all_processes_full ? MASTER_LOGIN_STATE_FULL :
+		MASTER_LOGIN_STATE_NONFULL;
+	if (lseek(service->login_notify_fd, state, SEEK_SET) < 0)
+		service_error(service, "lseek(notify fd) failed: %m");
+
+	/* but don't send signal to processes too often */
+	diff = ioloop_time - service->last_login_notify_time;
+	if (diff < SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS) {
+		if (service->to_login_notify != NULL)
+			return;
+
+		diff = (SERVICE_LOGIN_NOTIFY_MIN_INTERVAL_SECS - diff) * 1000;
+		service->to_login_notify =
+			timeout_add(diff, service_login_notify_timeout,
+				    service);
+	} else {
+		service_login_notify_send(service);
+	}
+}
+
 static void services_kill_timeout(struct service_list *service_list)
 {
 	struct service *const *services, *log_service;
--- a/src/master/service.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/master/service.h	Mon Oct 19 21:42:09 2009 -0400
@@ -2,22 +2,12 @@
 #define SERVICE_H
 
 #include "network.h"
-
-struct master_settings;
+#include "master-settings.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_ANVIL,
-	SERVICE_TYPE_CONFIG,
-	SERVICE_TYPE_AUTH_SERVER,
-	SERVICE_TYPE_AUTH_SOURCE
-};
-
 enum service_listener_type {
 	SERVICE_LISTENER_UNIX,
 	SERVICE_LISTENER_FIFO,
@@ -87,14 +77,16 @@
 	int status_fd[2];
 	struct io *io_status;
 
+	/* Login process's notify fd. We change its seek position to
+	   communicate state to login processes. */
+	int login_notify_fd;
+	time_t last_login_notify_time;
+	struct timeout *to_login_notify;
+
 	/* 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;
-
 	/* Last time a "dropping client connections" warning was logged */
 	time_t last_drop_warning;
 
@@ -104,6 +96,8 @@
 	unsigned int listening:1;
 	/* TRUE if service has at least one inet_listener */
 	unsigned int have_inet_listeners:1;
+	/* service_login_notify()'s last notification state */
+	unsigned int last_login_full_notify:1;
 };
 
 struct service_list {
@@ -122,7 +116,7 @@
 	int master_log_fd[2];
 	struct service_process_notify *log_byes;
 
-	/* passed to auth destination processes */
+	/* passed to child processes */
 	int blocking_anvil_fd[2];
 	/* used by master process to notify about dying processes */
 	int nonblocking_anvil_fd[2];
@@ -154,6 +148,10 @@
 
 /* Send a signal to all processes in a given service */
 void service_signal(struct service *service, int signo);
+/* Notify all processes (if necessary) that no more connections can be handled
+   by the service without killing existing connections (TRUE) or that they
+   can be (FALSE). */
+void service_login_notify(struct service *service, bool all_processes_full);
 
 /* Prevent service from launching new processes for a while. */
 void service_throttle(struct service *service, unsigned int secs);
--- a/src/pop3/main.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/pop3/main.c	Mon Oct 19 21:42:09 2009 -0400
@@ -2,12 +2,14 @@
 
 #include "pop3-common.h"
 #include "ioloop.h"
+#include "buffer.h"
 #include "istream.h"
-#include "buffer.h"
+#include "ostream.h"
 #include "base64.h"
 #include "restrict-access.h"
 #include "process-title.h"
 #include "master-service.h"
+#include "master-login.h"
 #include "master-interface.h"
 #include "var-expand.h"
 #include "mail-storage-service.h"
@@ -17,63 +19,151 @@
 #include <unistd.h>
 
 #define IS_STANDALONE() \
-        (getenv("LOGGED_IN") == NULL)
+        (getenv(MASTER_UID_ENV) == NULL)
+
+static const struct setting_parser_info *set_roots[] = {
+	&pop3_setting_parser_info,
+	NULL
+};
+static struct master_login *master_login = NULL;
+static enum mail_storage_service_flags storage_service_flags = 0;
+static bool user_initialized = FALSE;
 
 void (*hook_client_created)(struct client **client) = NULL;
 
-static bool main_init(const struct pop3_settings *set, struct mail_user *user)
+static void client_add_input(struct client *client, const buffer_t *buf)
+{
+	struct ostream *output;
+
+	if (buf != NULL && buf->used > 0) {
+		if (!i_stream_add_data(client->input, buf->data, buf->used))
+			i_panic("Couldn't add client input to stream");
+	}
+
+	output = client->output;
+	o_stream_ref(output);
+	o_stream_cork(output);
+	if (!IS_STANDALONE())
+		client_send_line(client, "+OK Logged in.");
+	(void)client_handle_input(client);
+	o_stream_uncork(output);
+	o_stream_unref(&output);
+}
+
+static void
+main_stdio_init_user(const struct pop3_settings *set, struct mail_user *user)
 {
 	struct client *client;
-	const char *str;
-	bool ret = TRUE;
+	buffer_t *input_buf;
+	const char *input_base64;
+
+	input_base64 = getenv("CLIENT_INPUT");
+	input_buf = input_base64 == NULL ? NULL :
+		t_base64_decode_str(input_base64);
+
+	client = client_create(STDIN_FILENO, STDOUT_FILENO, user, set);
+	client_add_input(client, input_buf);
+}
+
+static void main_stdio_run(void)
+{
+	struct mail_storage_service_input input;
+	struct mail_user *mail_user;
+	const struct pop3_settings *set;
+	const char *value;
 
+	memset(&input, 0, sizeof(input));
+	input.module = input.service = "pop3";
+	input.username = getenv("USER");
+	if (input.username == NULL && IS_STANDALONE())
+		input.username = getlogin();
+	if (input.username == NULL)
+		i_fatal("USER environment missing");
+	if ((value = getenv("IP")) != NULL)
+		net_addr2ip(value, &input.remote_ip);
+	if ((value = getenv("LOCAL_IP")) != NULL)
+		net_addr2ip(value, &input.local_ip);
+
+	user_initialized = TRUE;
+	mail_user = mail_storage_service_init_user(master_service,
+						   &input, set_roots,
+						   storage_service_flags);
+	set = mail_storage_service_get_settings(master_service);
+	restrict_access_allow_coredumps(TRUE);
 	if (set->shutdown_clients)
 		master_service_set_die_with_master(master_service, TRUE);
 
-	client = client_create(0, 1, user, set);
-	if (client == NULL)
-		return FALSE;
-
-	if (!IS_STANDALONE())
-		client_send_line(client, "+OK Logged in.");
-
-	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");
-			ret = client_handle_input(client);
-		}
-	} T_END;
-	return ret;
+	/* fake that we're running, so we know if client was destroyed
+	   while handling its initial input */
+	io_loop_set_running(current_ioloop);
+	main_stdio_init_user(set, mail_user);
 }
 
-static void main_deinit(void)
+static void
+login_client_connected(const struct master_login_client *client,
+		       const char *username, const char *const *extra_fields)
 {
-	clients_destroy_all();
+	struct mail_storage_service_input input;
+	struct mail_user *mail_user;
+	struct client *pop3_client;
+	const struct pop3_settings *set;
+	buffer_t input_buf;
+
+	if (pop3_clients != NULL) {
+		i_error("Can't handle more than one connection currently");
+		(void)close(client->fd);
+		return;
+	}
+	i_assert(!user_initialized);
+
+	memset(&input, 0, sizeof(input));
+	input.module = input.service = "pop3";
+	input.local_ip = client->auth_req.local_ip;
+	input.remote_ip = client->auth_req.remote_ip;
+	input.username = username;
+	input.userdb_fields = extra_fields;
+
+	if (input.username == NULL) {
+		i_error("login client: Username missing from auth reply");
+		(void)close(client->fd);
+		return;
+	}
+	user_initialized = TRUE;
+	master_login_deinit(&master_login);
+
+	mail_user = mail_storage_service_init_user(master_service,
+						   &input, set_roots,
+						   storage_service_flags);
+	set = mail_storage_service_get_settings(master_service);
+	restrict_access_allow_coredumps(TRUE);
+	if (set->shutdown_clients)
+		master_service_set_die_with_master(master_service, TRUE);
+
+	/* fake that we're running, so we know if client was destroyed
+	   while handling its initial input */
+	io_loop_set_running(current_ioloop);
+
+	buffer_create_const_data(&input_buf, client->data,
+				 client->auth_req.data_size);
+	pop3_client = client_create(client->fd, client->fd, mail_user, set);
+	T_BEGIN {
+		client_add_input(pop3_client, &input_buf);
+	} T_END;
 }
 
 static void client_connected(const struct master_service_connection *conn)
 {
-	/* we can't handle this yet */
-	(void)close(conn->fd);
+	if (master_login == NULL) {
+		/* running standalone, we shouldn't even get here */
+		(void)close(conn->fd);
+	} else {
+		master_login_add(master_login, conn->fd);
+	}
 }
 
 int main(int argc, char *argv[], char *envp[])
 {
-	const struct setting_parser_info *set_roots[] = {
-		&pop3_setting_parser_info,
-		NULL
-	};
-	enum master_service_flags service_flags =
-		MASTER_SERVICE_FLAG_STD_CLIENT;
-	enum mail_storage_service_flags storage_service_flags = 0;
-	struct mail_storage_service_input input;
-	struct mail_user *mail_user;
-	const struct pop3_settings *set;
-	const char *value;
+	enum master_service_flags service_flags = 0;
 	int c;
 
 	if (IS_STANDALONE() && getuid() == 0 &&
@@ -83,12 +173,12 @@
 		return 1;
 	}
 
-	if (IS_STANDALONE())
-		service_flags |= MASTER_SERVICE_FLAG_STANDALONE;
-	else {
+	if (IS_STANDALONE()) {
+		service_flags |= MASTER_SERVICE_FLAG_STANDALONE |
+			MASTER_SERVICE_FLAG_STD_CLIENT;
+	} else {
 		storage_service_flags |=
-			MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT |
-			MAIL_STORAGE_SERVICE_FLAG_RESTRICT_BY_ENV;
+			MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT;
 	}
 
 	master_service = master_service_init("pop3", service_flags, argc, argv);
@@ -96,43 +186,27 @@
 		if (!master_service_parse_option(master_service, c, optarg))
 			exit(FATAL_DEFAULT);
 	}
+        process_title_init(argv, envp);
+	master_service_init_finish(master_service);
 
-	memset(&input, 0, sizeof(input));
-	input.module = "pop3";
-	input.service = "pop3";
-	input.username = getenv("USER");
-	if (input.username == NULL && IS_STANDALONE())
-		input.username = getlogin();
-	if (input.username == NULL) {
-		if (getenv(MASTER_UID_ENV) == NULL)
-			i_fatal("USER environment missing");
-		else {
-			i_fatal("login_executable setting must be pop3-login, "
-				"not pop3");
-		}
+	if (IS_STANDALONE()) {
+		T_BEGIN {
+			main_stdio_run();
+		} T_END;
+	} else {
+		master_login = master_login_init("auth-master",
+						 login_client_connected);
+		io_loop_set_running(current_ioloop);
 	}
-	if ((value = getenv("IP")) != NULL)
-		net_addr2ip(value, &input.remote_ip);
-	if ((value = getenv("LOCAL_IP")) != NULL)
-		net_addr2ip(value, &input.local_ip);
 
-	mail_user = mail_storage_service_init_user(master_service,
-						   &input, set_roots,
-						   storage_service_flags);
-	set = mail_storage_service_get_settings(master_service);
-	restrict_access_allow_coredumps(TRUE);
+	if (io_loop_is_running(current_ioloop))
+		master_service_run(master_service, client_connected);
+	clients_destroy_all();
 
-        process_title_init(argv, envp);
-
-	/* fake that we're running, so we know if client was destroyed
-	   while initializing */
-	io_loop_set_running(current_ioloop);
-
-	if (main_init(set, mail_user))
-		master_service_run(master_service, client_connected);
-
-	main_deinit();
-	mail_storage_service_deinit_user();
+	if (master_login != NULL)
+		master_login_deinit(&master_login);
+	if (user_initialized)
+		mail_storage_service_deinit_user();
 	master_service_deinit(&master_service);
 	return 0;
 }
--- a/src/pop3/pop3-client.c	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/pop3/pop3-client.c	Mon Oct 19 21:42:09 2009 -0400
@@ -35,7 +35,7 @@
    transaction. This allows the mailbox to become unlocked. */
 #define CLIENT_COMMIT_TIMEOUT_MSECS (10*1000)
 
-static struct client *pop3_clients;
+struct client *pop3_clients;
 
 static void client_input(struct client *client);
 static int client_output(struct client *client);
--- a/src/pop3/pop3-client.h	Mon Oct 19 18:34:00 2009 -0400
+++ b/src/pop3/pop3-client.h	Mon Oct 19 21:42:09 2009 -0400
@@ -60,6 +60,8 @@
 	unsigned int anvil_sent:1;
 };
 
+extern struct client *pop3_clients;
+
 /* Create new client with specified input/output handles. socket specifies
    if the handle is a socket. */
 struct client *client_create(int fd_in, int fd_out, struct mail_user *user,