diff src/master/login-process.c @ 1055:a72bba3f8a55 HEAD

Rewrote setting handling. Changed some existing settings also since POP3 support required changes anyway. POP3 seems to be really working now :)
author Timo Sirainen <tss@iki.fi>
date Thu, 30 Jan 2003 19:59:31 +0200
parents 793f05a7e50e
children 6cb5b50aea71
line wrap: on
line diff
--- a/src/master/login-process.c	Thu Jan 30 19:56:19 2003 +0200
+++ b/src/master/login-process.c	Thu Jan 30 19:59:31 2003 +0200
@@ -11,13 +11,31 @@
 #include "restrict-process-size.h"
 #include "login-process.h"
 #include "auth-process.h"
-#include "imap-process.h"
+#include "mail-process.h"
 #include "master-login-interface.h"
 
 #include <unistd.h>
 #include <syslog.h>
 
+struct login_group {
+	struct login_group *next;
+
+	struct login_settings *set;
+
+	unsigned int processes;
+	unsigned int listening_processes;
+	unsigned int wanted_processes_count;
+
+	struct login_process *oldest_nonlisten_process;
+	struct login_process *newest_nonlisten_process;
+
+	const char *executable;
+	unsigned int process_size;
+	int *listen_fd, *ssl_listen_fd;
+};
+
 struct login_process {
+	struct login_group *group;
 	struct login_process *prev_nonlisten, *next_nonlisten;
 	int refcount;
 
@@ -44,14 +62,40 @@
 static struct timeout *to;
 
 static struct hash_table *processes;
-static struct login_process *oldest_nonlisten_process;
-static struct login_process *newest_nonlisten_process;
-static unsigned int listening_processes;
-static unsigned int wanted_processes_count;
+static struct login_group *login_groups;
 
 static void login_process_destroy(struct login_process *p);
 static void login_process_unref(struct login_process *p);
 
+static void login_group_create(struct login_settings *login_set)
+{
+	struct login_group *group;
+
+	group = i_new(struct login_group, 1);
+	group->set = login_set;
+
+	if (strcmp(login_set->name, "imap") == 0) {
+		group->executable = set->imap_executable;
+		group->process_size = set->imap_process_size;
+		group->listen_fd = &mail_fd[FD_IMAP];
+		group->ssl_listen_fd = &mail_fd[FD_IMAPS];
+	} else if (strcmp(login_set->name, "pop3") == 0) {
+		group->executable = set->pop3_executable;
+		group->process_size = set->pop3_process_size;
+		group->listen_fd = &mail_fd[FD_POP3];
+		group->ssl_listen_fd = &mail_fd[FD_POP3S];
+	} else
+		i_panic("Unknown login group name '%s'", login_set->name);
+
+	group->next = login_groups;
+	login_groups = group;
+}
+
+static void login_group_destroy(struct login_group *group)
+{
+	i_free(group);
+}
+
 void auth_master_callback(struct auth_master_reply *reply,
 			  const unsigned char *data, void *context)
 {
@@ -61,9 +105,13 @@
 	if (reply == NULL || !reply->success)
 		master_reply.success = FALSE;
 	else {
+		struct login_group *group = request->process->group;
+
 		master_reply.success =
-			create_imap_process(request->fd, &request->ip, reply,
-					    (const char *) data);
+			create_mail_process(request->fd, &request->ip,
+					    group->executable,
+					    group->process_size,
+					    reply, (const char *) data);
 	}
 
 	/* reply to login */
@@ -74,7 +122,7 @@
 		login_process_destroy(request->process);
 
 	if (close(request->fd) < 0)
-		i_error("close(imap client) failed: %m");
+		i_error("close(mail client) failed: %m");
 	login_process_unref(request->process);
 	i_free(request);
 }
@@ -88,16 +136,16 @@
 	}
 
 	p->listening = FALSE;
-	listening_processes--;
+	p->group->listening_processes--;
 
-	p->prev_nonlisten = newest_nonlisten_process;
+	p->prev_nonlisten = p->group->newest_nonlisten_process;
 
-	if (newest_nonlisten_process != NULL)
-		newest_nonlisten_process->next_nonlisten = p;
-	newest_nonlisten_process = p;
+	if (p->group->newest_nonlisten_process != NULL)
+		p->group->newest_nonlisten_process->next_nonlisten = p;
+	p->group->newest_nonlisten_process = p;
 
-	if (oldest_nonlisten_process == NULL)
-		oldest_nonlisten_process = p;
+	if (p->group->oldest_nonlisten_process == NULL)
+		p->group->oldest_nonlisten_process = p;
 }
 
 static void login_process_input(void *context)
@@ -121,7 +169,7 @@
 
 		if (client_fd != -1) {
 			if (close(client_fd) < 0)
-				i_error("close(imap client) failed: %m");
+				i_error("close(mail client) failed: %m");
 		}
 
 		login_process_destroy(p);
@@ -162,13 +210,15 @@
 	}
 }
 
-static struct login_process *login_process_new(pid_t pid, int fd)
+static struct login_process *
+login_process_new(struct login_group *group, pid_t pid, int fd)
 {
 	struct login_process *p;
 
 	PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_LOGIN);
 
 	p = i_new(struct login_process, 1);
+	p->group = group;
 	p->refcount = 1;
 	p->pid = pid;
 	p->fd = fd;
@@ -179,19 +229,20 @@
 					 IO_PRIORITY_DEFAULT, FALSE);
 
 	hash_insert(processes, POINTER_CAST(pid), p);
-        listening_processes++;
+	p->group->processes++;
+	p->group->listening_processes++;
 	return p;
 }
 
 static void login_process_remove_from_lists(struct login_process *p)
 {
-	if (p == oldest_nonlisten_process)
-		oldest_nonlisten_process = p->next_nonlisten;
+	if (p == p->group->oldest_nonlisten_process)
+		p->group->oldest_nonlisten_process = p->next_nonlisten;
 	else
 		p->prev_nonlisten->next_nonlisten = p->next_nonlisten;
 
-	if (p == newest_nonlisten_process)
-		newest_nonlisten_process = p->prev_nonlisten;
+	if (p == p->group->newest_nonlisten_process)
+		p->group->newest_nonlisten_process = p->prev_nonlisten;
 	else
 		p->next_nonlisten->prev_nonlisten = p->prev_nonlisten;
 
@@ -209,7 +260,7 @@
 		io_loop_stop(ioloop);
 	}
 	if (p->listening)
-		listening_processes--;
+		p->group->listening_processes--;
 
 	o_stream_close(p->output);
 	io_remove(p->io);
@@ -219,6 +270,7 @@
 	if (!p->listening)
 		login_process_remove_from_lists(p);
 
+	p->group->processes--;
 	hash_remove(processes, POINTER_CAST(p->pid));
 
 	login_process_unref(p);
@@ -233,19 +285,20 @@
 	i_free(p);
 }
 
-static pid_t create_login_process(void)
+static pid_t create_login_process(struct login_group *group)
 {
-	static char *argv[] = { NULL, NULL };
+	static const char *argv[] = { NULL, NULL };
 	pid_t pid;
 	int fd[2];
 
-	if (set_login_process_per_connection &&
-	    hash_size(processes)-listening_processes >= set_max_logging_users) {
-		if (oldest_nonlisten_process != NULL)
-			login_process_destroy(oldest_nonlisten_process);
+	if (group->set->process_per_connection &&
+	    group->processes - group->listening_processes >=
+	    group->set->max_logging_users) {
+		if (group->oldest_nonlisten_process != NULL)
+			login_process_destroy(group->oldest_nonlisten_process);
 	}
 
-	if (set_login_uid == 0)
+	if (group->set->uid == 0)
 		i_fatal("Login process must not run as root");
 
 	/* create communication to process with a socket pair */
@@ -265,7 +318,7 @@
 	if (pid != 0) {
 		/* master */
 		fd_close_on_exec(fd[0], TRUE);
-		login_process_new(pid, fd[0]);
+		login_process_new(group, pid, fd[0]);
 		(void)close(fd[1]);
 		return pid;
 	}
@@ -276,21 +329,21 @@
 	fd_close_on_exec(LOGIN_MASTER_SOCKET_FD, FALSE);
 
 	/* move the listen handle */
-	if (dup2(imap_fd, LOGIN_IMAP_LISTEN_FD) < 0)
-		i_fatal("login: dup2(imap) failed: %m");
-	fd_close_on_exec(LOGIN_IMAP_LISTEN_FD, FALSE);
+	if (dup2(*group->listen_fd, LOGIN_LISTEN_FD) < 0)
+		i_fatal("login: dup2(listen_fd) failed: %m");
+	fd_close_on_exec(LOGIN_LISTEN_FD, FALSE);
 
 	/* move the SSL listen handle */
-	if (!set_ssl_disable) {
-		if (dup2(imaps_fd, LOGIN_IMAPS_LISTEN_FD) < 0)
-			i_fatal("login: dup2(imaps) failed: %m");
+	if (!set->ssl_disable) {
+		if (dup2(*group->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0)
+			i_fatal("login: dup2(ssl_listen_fd) failed: %m");
 	} else {
-		if (dup2(null_fd, LOGIN_IMAPS_LISTEN_FD) < 0)
-			i_fatal("login: dup2(imaps) failed: %m");
+		if (dup2(null_fd, LOGIN_SSL_LISTEN_FD) < 0)
+			i_fatal("login: dup2(ssl_listen_fd) failed: %m");
 	}
-	fd_close_on_exec(LOGIN_IMAPS_LISTEN_FD, FALSE);
+	fd_close_on_exec(LOGIN_SSL_LISTEN_FD, FALSE);
 
-	/* imap_fd and imaps_fd are closed by clean_child_process() */
+	/* listen_fds are closed by clean_child_process() */
 
 	(void)close(fd[0]);
 	(void)close(fd[1]);
@@ -299,58 +352,64 @@
 
 	/* setup access environment - needs to be done after
 	   clean_child_process() since it clears environment */
-	restrict_access_set_env(set_login_user, set_login_uid, set_login_gid,
-				set_login_chroot ? set_login_dir : NULL);
+	restrict_access_set_env(group->set->user,
+				group->set->uid, set->login_gid,
+				set->login_chroot ? set->login_dir : NULL);
 
-	if (!set_login_chroot) {
+	if (!set->login_chroot) {
 		/* no chrooting, but still change to the directory */
-		if (chdir(set_login_dir) < 0)
-			i_fatal("chdir(%s) failed: %m", set_login_dir);
+		if (chdir(set->login_dir) < 0)
+			i_fatal("chdir(%s) failed: %m", set->login_dir);
 	}
 
-	if (!set_ssl_disable) {
-		env_put(t_strconcat("SSL_CERT_FILE=", set_ssl_cert_file, NULL));
-		env_put(t_strconcat("SSL_KEY_FILE=", set_ssl_key_file, NULL));
+	if (!set->ssl_disable) {
+		env_put(t_strconcat("SSL_CERT_FILE=",
+				    set->ssl_cert_file, NULL));
+		env_put(t_strconcat("SSL_KEY_FILE=", set->ssl_key_file, NULL));
 		env_put(t_strconcat("SSL_PARAM_FILE=",
-				    set_ssl_parameters_file, NULL));
+				    set->ssl_parameters_file, NULL));
 	}
 
-	if (set_disable_plaintext_auth)
+	if (set->disable_plaintext_auth)
 		env_put("DISABLE_PLAINTEXT_AUTH=1");
-	if (set_verbose_proctitle)
+	if (set->verbose_proctitle)
 		env_put("VERBOSE_PROCTITLE=1");
 
-	if (set_login_process_per_connection) {
+	if (group->set->process_per_connection) {
 		env_put("PROCESS_PER_CONNECTION=1");
 		env_put("MAX_LOGGING_USERS=1");
 	} else {
 		env_put(t_strdup_printf("MAX_LOGGING_USERS=%u",
-					set_max_logging_users));
+					group->set->max_logging_users));
 	}
 
 	env_put(t_strdup_printf("PROCESS_UID=%s", dec2str(getpid())));
 
-	restrict_process_size(set_login_process_size);
+	restrict_process_size(group->set->process_size);
 
 	/* make sure we don't leak syslog fd, but do it last so that
 	   any errors above will be logged */
 	closelog();
 
 	/* hide the path, it's ugly */
-	argv[0] = strrchr(set_login_executable, '/');
-	if (argv[0] == NULL) argv[0] = set_login_executable; else argv[0]++;
+	argv[0] = strrchr(group->set->executable, '/');
+	if (argv[0] == NULL) argv[0] = group->set->executable; else argv[0]++;
 
-	execv(set_login_executable, (char **) argv);
+	execv(group->set->executable, (char **) argv);
 
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", argv[0]);
 	return -1;
 }
 
-void login_process_abormal_exit(pid_t pid __attr_unused__)
+void login_process_abormal_exit(pid_t pid)
 {
+	struct login_process *p;
+
 	/* don't start raising the process count if they're dying all
 	   the time */
-	wanted_processes_count = 0;
+	p = hash_lookup(processes, POINTER_CAST(pid));
+	if (p != NULL)
+		p->group->wanted_processes_count = 0;
 }
 
 static void login_hash_destroy(void *key __attr_unused__, void *value,
@@ -363,47 +422,64 @@
 {
 	hash_foreach(processes, login_hash_destroy, NULL);
 
-	/* don't double their amount when restarting */
-	wanted_processes_count = 0;
+	while (login_groups != NULL) {
+		struct login_group *group = login_groups;
+
+		login_groups = group->next;
+		login_group_destroy(group);
+	}
+}
+
+static void login_group_start_missings(struct login_group *group)
+{
+	if (!group->set->process_per_connection) {
+		/* create max. one process every second, that way if it keeps
+		   dying all the time we don't eat all cpu with fork()ing. */
+		if (group->listening_processes < group->set->processes_count)
+			(void)create_login_process(group);
+		return;
+	}
+
+	/* we want to respond fast when multiple clients are connecting
+	   at once, but we also want to prevent fork-bombing. use the
+	   same method as apache: check once a second if we need new
+	   processes. if yes and we've used all the existing processes,
+	   double their amount (unless we've hit the high limit).
+	   Then for each second that didn't use all existing processes,
+	   drop the max. process count by one. */
+	if (group->wanted_processes_count < group->set->processes_count)
+		group->wanted_processes_count = group->set->processes_count;
+	else if (group->listening_processes == 0)
+		group->wanted_processes_count *= 2;
+	else if (group->wanted_processes_count > group->set->processes_count)
+		group->wanted_processes_count--;
+
+	if (group->wanted_processes_count > group->set->max_processes_count)
+		group->wanted_processes_count = group->set->max_processes_count;
+
+	while (group->listening_processes < group->wanted_processes_count)
+		(void)create_login_process(group);
 }
 
 static void
 login_processes_start_missing(void *context __attr_unused__)
 {
-	if (!set_login_process_per_connection) {
-		/* create max. one process every second, that way if it keeps
-		   dying all the time we don't eat all cpu with fork()ing. */
-		if (listening_processes < set_login_processes_count)
-			(void)create_login_process();
-	} else {
-		/* we want to respond fast when multiple clients are connecting
-		   at once, but we also want to prevent fork-bombing. use the
-		   same method as apache: check once a second if we need new
-		   processes. if yes and we've used all the existing processes,
-		   double their amount (unless we've hit the high limit).
-		   Then for each second that didn't use all existing processes,
-		   drop the max. process count by one. */
-		if (wanted_processes_count < set_login_processes_count)
-			wanted_processes_count = set_login_processes_count;
-		else if (listening_processes == 0)
-			wanted_processes_count *= 2;
-		else if (wanted_processes_count > set_login_processes_count)
-			wanted_processes_count--;
+	struct login_group *group;
+	struct login_settings *login;
 
-		if (wanted_processes_count > set_login_max_processes_count)
-			wanted_processes_count = set_login_max_processes_count;
+	if (login_groups == NULL) {
+		for (login = set->logins; login != NULL; login = login->next)
+			login_group_create(login);
+	}
 
-		while (listening_processes < wanted_processes_count)
-			(void)create_login_process();
-	}
+	for (group = login_groups; group != NULL; group = group->next)
+		login_group_start_missings(group);
 }
 
 void login_processes_init(void)
 {
         auth_id_counter = 0;
-	listening_processes = 0;
-        wanted_processes_count = 0;
-	oldest_nonlisten_process = newest_nonlisten_process = NULL;
+	login_groups = NULL;
 
 	processes = hash_create(default_pool, default_pool, 128, NULL, NULL);
 	to = timeout_add(1000, login_processes_start_missing, NULL);