changeset 614:e60620644af3 HEAD

login_process_per_connection = yes scales now better when multiple users are trying to log in at the same time.
author Timo Sirainen <tss@iki.fi>
date Sat, 16 Nov 2002 07:57:20 +0200
parents 1906116a62ce
children 0d852af6842e
files dovecot-example.conf src/master/login-process.c src/master/settings.c src/master/settings.h
diffstat 4 files changed, 60 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/dovecot-example.conf	Sat Nov 16 07:21:21 2002 +0200
+++ b/dovecot-example.conf	Sat Nov 16 07:57:20 2002 +0200
@@ -67,6 +67,14 @@
 # yes, this is the number of extra processes waiting for users to log in.
 #login_processes_count = 3
 
+# Maximum number of extra login processes to create. The extra process count
+# usually stays at login_processes_count, but when multiple users start logging
+# in at the same time more extra processes are created. To prevent fork-bombing
+# we check only once in a second if new processes should be created - if all
+# of them are used at the time, we double their amount until limit set by this
+# setting is reached. This setting is used only if login_process_per_use is yes.
+#login_max_processes_count = 128
+
 # Maximum number of connections allowed in login state. When this limit is
 # reached, the oldest connections are dropped. If login_process_per_user
 # is no, this is a per-process value, so the absolute maximum number of users
--- a/src/master/login-process.c	Sat Nov 16 07:21:21 2002 +0200
+++ b/src/master/login-process.c	Sat Nov 16 07:57:20 2002 +0200
@@ -44,6 +44,7 @@
 static HashTable *processes;
 static LoginProcess *oldest_nonlisten_process, *newest_nonlisten_process;
 static unsigned int listening_processes;
+static unsigned int wanted_processes_count;
 
 static void login_process_destroy(LoginProcess *p);
 static void login_process_unref(LoginProcess *p);
@@ -86,6 +87,27 @@
 	i_free(request);
 }
 
+void login_process_mark_nonlistening(LoginProcess *p)
+{
+	if (!p->listening) {
+		i_error("login: received another \"not listening\" "
+			"notification");
+		return;
+	}
+
+	p->listening = FALSE;
+	listening_processes--;
+
+	p->prev_nonlisten = newest_nonlisten_process;
+
+	if (newest_nonlisten_process != NULL)
+		newest_nonlisten_process->next_nonlisten = p;
+	newest_nonlisten_process = p;
+
+	if (oldest_nonlisten_process == NULL)
+		oldest_nonlisten_process = p;
+}
+
 static void login_process_input(void *context, int fd __attr_unused__,
 				IO io __attr_unused__)
 {
@@ -113,22 +135,7 @@
 	if (client_fd == -1) {
 		/* just a notification that the login process isn't
 		   listening for new connections anymore */
-		if (!p->listening) {
-			i_error("login: received another \"not listening\" "
-				"notification");
-		} else {
-			p->listening = FALSE;
-			listening_processes--;
-
-			p->prev_nonlisten = newest_nonlisten_process;
-
-			if (newest_nonlisten_process != NULL)
-				newest_nonlisten_process->next_nonlisten = p;
-			newest_nonlisten_process = p;
-
-			if (oldest_nonlisten_process == NULL)
-                                oldest_nonlisten_process = p;
-		}
+		login_process_mark_nonlistening(p);
 		return;
 	}
 
@@ -342,16 +349,39 @@
 static void login_processes_start_missing(void *context __attr_unused__,
 					  Timeout timeout __attr_unused__)
 {
-	/* 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();
+	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--;
+
+		if (wanted_processes_count > set_login_max_processes_count)
+			wanted_processes_count = set_login_max_processes_count;
+
+		while (listening_processes < wanted_processes_count)
+			(void)create_login_process();
+	}
 }
 
 void login_processes_init(void)
 {
         auth_id_counter = 0;
 	listening_processes = 0;
+        wanted_processes_count = 0;
 	oldest_nonlisten_process = newest_nonlisten_process = NULL;
 
 	processes = hash_create(default_pool, 128, NULL, NULL);
--- a/src/master/settings.c	Sat Nov 16 07:21:21 2002 +0200
+++ b/src/master/settings.c	Sat Nov 16 07:57:20 2002 +0200
@@ -92,6 +92,7 @@
 int set_login_chroot = TRUE;
 int set_login_process_per_connection = TRUE;
 unsigned int set_login_processes_count = 3;
+unsigned int set_login_max_processes_count = 128;
 unsigned int set_max_logging_users = 256;
 
 uid_t set_login_uid; /* generated from set_login_user */
--- a/src/master/settings.h	Sat Nov 16 07:21:21 2002 +0200
+++ b/src/master/settings.h	Sat Nov 16 07:57:20 2002 +0200
@@ -21,7 +21,7 @@
 extern char *set_login_dir;
 extern int set_login_chroot;
 extern int set_login_process_per_connection;
-extern unsigned int set_login_processes_count;
+extern unsigned int set_login_processes_count, set_login_max_processes_count;
 extern unsigned int set_max_logging_users;
 
 extern uid_t set_login_uid;