[dovecot-cvs] dovecot/src/master login-process.c, 1.75, 1.76 login-process.h, 1.7, 1.8 main.c, 1.89, 1.90 master-login-interface.h, 1.7, 1.8 master-settings.c, 1.133, 1.134 master-settings.h, 1.86, 1.87
cras at dovecot.org
cras at dovecot.org
Sun Aug 6 23:05:39 EEST 2006
Update of /var/lib/cvs/dovecot/src/master
In directory talvi:/tmp/cvs-serv16539/src/master
Modified Files:
login-process.c login-process.h main.c
master-login-interface.h master-settings.c master-settings.h
Log Message:
Fixes to login process handling, especially with
login_process_per_connection=no. Removed login_max_logging_users setting
since it was somewhat weird in how it worked. Added login_max_connections to
replace it with login_process_per_connection=no, and with =yes its
functionality is now within login_max_processes_count.
Index: login-process.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/master/login-process.c,v
retrieving revision 1.75
retrieving revision 1.76
diff -u -d -r1.75 -r1.76
--- login-process.c 16 Jun 2006 18:13:43 -0000 1.75
+++ login-process.c 6 Aug 2006 20:05:36 -0000 1.76
@@ -21,16 +21,18 @@
struct login_process {
struct login_group *group;
- struct login_process *prev_nonlisten, *next_nonlisten;
+ struct login_process *prev_prelogin, *next_prelogin;
int refcount;
pid_t pid;
int fd;
struct io *io;
struct ostream *output;
+ enum master_login_state state;
+
unsigned int initialized:1;
- unsigned int listening:1;
unsigned int destroyed:1;
+ unsigned int inetd_child:1;
};
struct login_auth_request {
@@ -114,30 +116,79 @@
i_free(request);
}
-static void login_process_mark_nonlistening(struct login_process *p)
+static void process_remove_from_prelogin_lists(struct login_process *p)
{
- if (!p->listening) {
- i_error("login: received another \"not listening\" "
- "notification (if you can't login at all, "
- "see src/lib/fdpass.c)");
+ if (p->state != LOGIN_STATE_FULL_PRELOGINS)
return;
- }
- p->listening = FALSE;
+ if (p->prev_prelogin == NULL)
+ p->group->oldest_prelogin_process = p->next_prelogin;
+ else
+ p->prev_prelogin->next_prelogin = p->next_prelogin;
- if (p->group != NULL) {
+ if (p->next_prelogin == NULL)
+ p->group->newest_prelogin_process = p->prev_prelogin;
+ else
+ p->next_prelogin->prev_prelogin = p->prev_prelogin;
+
+ p->prev_prelogin = p->next_prelogin = NULL;
+}
+
+static void process_mark_nonlistening(struct login_process *p,
+ enum master_login_state new_state)
+{
+ if (p->group == NULL)
+ return;
+
+ if (p->state == LOGIN_STATE_LISTENING)
p->group->listening_processes--;
- p->prev_nonlisten = p->group->newest_nonlisten_process;
- if (p->group->newest_nonlisten_process != NULL)
- p->group->newest_nonlisten_process->next_nonlisten = p;
- p->group->newest_nonlisten_process = p;
+ if (new_state == LOGIN_STATE_FULL_PRELOGINS) {
+ /* add to prelogin list */
+ i_assert(p->state != new_state);
- if (p->group->oldest_nonlisten_process == NULL)
- p->group->oldest_nonlisten_process = p;
+ p->prev_prelogin = p->group->newest_prelogin_process;
+ if (p->group->newest_prelogin_process == NULL)
+ p->group->oldest_prelogin_process = p;
+ else
+ p->group->newest_prelogin_process->next_prelogin = p;
+ p->group->newest_prelogin_process = p;
+ } else {
+ process_remove_from_prelogin_lists(p);
}
}
+static void process_mark_listening(struct login_process *p)
+{
+ if (p->group == NULL)
+ return;
+
+ if (p->state != LOGIN_STATE_LISTENING)
+ p->group->listening_processes++;
+
+ process_remove_from_prelogin_lists(p);
+}
+
+static void
+login_process_set_state(struct login_process *p, enum master_login_state state)
+{
+ if (state == p->state || state > LOGIN_STATE_COUNT ||
+ (state < p->state && p->group->set->login_process_per_connection)) {
+ i_error("login: tried to change state %d -> %d "
+ "(if you can't login at all, see src/lib/fdpass.c)",
+ p->state, state);
+ return;
+ }
+
+ if (state == LOGIN_STATE_LISTENING) {
+ process_mark_listening(p);
+ } else {
+ process_mark_nonlistening(p, state);
+ }
+
+ p->state = state;
+}
+
static void login_process_groups_create(void)
{
struct server_settings *server;
@@ -271,12 +322,14 @@
if (client_fd == -1) {
/* just a notification that the login process */
+ enum master_login_state state = req.tag;
+
if (!p->initialized) {
/* initialization notify */
p->initialized = TRUE;;
} else {
- /* not listening for new connections anymore */
- login_process_mark_nonlistening(p);
+ /* change "listening for new connections" status */
+ login_process_set_state(p, state);
}
return;
}
@@ -316,7 +369,6 @@
p->refcount = 1;
p->pid = pid;
p->fd = fd;
- p->listening = TRUE;
p->io = io_add(fd, IO_READ, login_process_input, p);
p->output = o_stream_create_file(fd, default_pool,
sizeof(struct master_login_reply)*10,
@@ -325,6 +377,8 @@
PID_ADD_PROCESS_TYPE(pid, PROCESS_TYPE_LOGIN);
hash_insert(processes, POINTER_CAST(pid), p);
+ p->state = LOGIN_STATE_LISTENING;
+
if (p->group != NULL) {
p->group->processes++;
p->group->listening_processes++;
@@ -332,22 +386,13 @@
return p;
}
-static void login_process_remove_from_lists(struct login_process *p)
+static void login_process_exited(struct login_process *p)
{
- if (p->group == NULL)
- return;
-
- 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 == p->group->newest_nonlisten_process)
- p->group->newest_nonlisten_process = p->prev_nonlisten;
- else
- p->next_nonlisten->prev_nonlisten = p->prev_nonlisten;
+ if (p->group != NULL)
+ p->group->processes--;
- p->next_nonlisten = p->prev_nonlisten = NULL;
+ hash_remove(processes, POINTER_CAST(p->pid));
+ login_process_unref(p);
}
static void login_process_destroy(struct login_process *p)
@@ -361,24 +406,15 @@
io_loop_stop(ioloop);
}
- if (p->listening && p->group != NULL)
- p->group->listening_processes--;
-
o_stream_close(p->output);
io_remove(&p->io);
if (close(p->fd) < 0)
i_error("close(login) failed: %m");
- if (!p->listening)
- login_process_remove_from_lists(p);
-
- if (p->group != NULL)
- p->group->processes--;
-
- if (p->pid != 0)
- hash_remove(processes, POINTER_CAST(p->pid));
+ process_mark_nonlistening(p, LOGIN_STATE_FULL_LOGINS);
- login_process_unref(p);
+ if (p->inetd_child)
+ login_process_exited(p);
}
static void login_process_unref(struct login_process *p)
@@ -445,8 +481,8 @@
env_put("PROCESS_PER_CONNECTION=1");
env_put("MAX_LOGGING_USERS=1");
} else {
- env_put(t_strdup_printf("MAX_LOGGING_USERS=%u",
- set->login_max_logging_users));
+ env_put(t_strdup_printf("MAX_CONNECTIONS=%u",
+ set->login_max_connections));
}
env_put(t_strconcat("PROCESS_UID=", dec2str(pid), NULL));
@@ -473,13 +509,6 @@
pid_t pid;
int fd[2], log_fd;
- if (group->set->login_process_per_connection &&
- group->processes - group->listening_processes >=
- group->set->login_max_logging_users) {
- if (group->oldest_nonlisten_process != NULL)
- login_process_destroy(group->oldest_nonlisten_process);
- }
-
if (group->set->login_uid == 0)
i_fatal("Login process must not run as root");
@@ -569,25 +598,36 @@
return -1;
}
-void login_process_abormal_exit(pid_t pid)
+void login_process_destroyed(pid_t pid, bool abnormal_exit)
{
struct login_process *p;
- /* don't start raising the process count if they're dying all
- the time */
p = hash_lookup(processes, POINTER_CAST(pid));
- if (p != NULL && p->group != NULL)
- p->group->wanted_processes_count = 0;
+ if (p == NULL)
+ i_panic("Lost login process PID %s", dec2str(pid));
+ i_assert(!p->inetd_child);
+
+ if (abnormal_exit) {
+ /* don't start raising the process count if they're dying all
+ the time */
+ if (p->group != NULL)
+ p->group->wanted_processes_count = 0;
+ }
+
+ login_process_destroy(p);
+ login_process_exited(p);
}
-void login_processes_destroy_all(void)
+void login_processes_destroy_all(bool unref)
{
struct hash_iterate_context *iter;
void *key, *value;
iter = hash_iterate_init(processes);
- while (hash_iterate(iter, &key, &value))
+ while (hash_iterate(iter, &key, &value)) {
login_process_destroy(value);
+ if (unref) login_process_unref(value);
+ }
hash_iterate_deinit(iter);
while (login_groups != NULL) {
@@ -598,17 +638,34 @@
}
}
+static void login_processes_notify_group(struct login_group *group)
+{
+ struct hash_iterate_context *iter;
+ struct master_login_reply reply;
+ void *key, *value;
+
+ memset(&reply, 0, sizeof(reply));
+
+ iter = hash_iterate_init(processes);
+ while (hash_iterate(iter, &key, &value)) {
+ struct login_process *p = value;
+
+ if (p->group == group)
+ (void)o_stream_send(p->output, &reply, sizeof(reply));
+ }
+ hash_iterate_deinit(iter);
+}
+
static int login_group_start_missings(struct login_group *group)
{
- if (!group->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 (group->listening_processes <
- group->set->login_processes_count) {
- if (create_login_process(group) < 0)
- return -1;
- }
- return 0;
+ if (group->set->login_process_per_connection &&
+ group->processes >= group->set->login_max_processes_count &&
+ group->listening_processes == 0) {
+ /* destroy the oldest listening process. non-listening
+ processes are logged in users who we don't want to kick out
+ because someone's started flooding */
+ if (group->oldest_prelogin_process != NULL)
+ login_process_destroy(group->oldest_prelogin_process);
}
/* we want to respond fast when multiple clients are connecting
@@ -627,16 +684,19 @@
group->set->login_processes_count)
group->wanted_processes_count--;
- if (group->wanted_processes_count >
- group->set->login_max_processes_count) {
- group->wanted_processes_count =
- group->set->login_max_processes_count;
- }
-
- while (group->listening_processes < group->wanted_processes_count) {
+ while (group->listening_processes < group->wanted_processes_count &&
+ group->processes < group->set->login_max_processes_count) {
if (create_login_process(group) < 0)
return -1;
}
+
+ if (group->listening_processes == 0 &&
+ !group->set->login_process_per_connection) {
+ /* we've reached our limit. notify the processes to start
+ listening again which makes them kill some of their
+ oldest clients when accepting the next connection */
+ login_processes_notify_group(group);
+ }
return 0;
}
@@ -744,6 +804,7 @@
p = login_process_new(NULL, ++login_pid_counter, fd);
p->initialized = TRUE;
+ p->inetd_child = TRUE;
}
}
@@ -771,6 +832,6 @@
if (io_listen != NULL)
io_remove(&io_listen);
- login_processes_destroy_all();
+ login_processes_destroy_all(TRUE);
hash_destroy(processes);
}
Index: login-process.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/master/login-process.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- login-process.h 24 May 2004 22:33:51 -0000 1.7
+++ login-process.h 6 Aug 2006 20:05:36 -0000 1.8
@@ -11,12 +11,15 @@
unsigned int listening_processes;
unsigned int wanted_processes_count;
- struct login_process *oldest_nonlisten_process;
- struct login_process *newest_nonlisten_process;
+ /* if login_process_per_connection=yes this contains the list of
+ processes that are in LOGIN_STATE_FULL_PRELOGINS state */
+ struct login_process *oldest_prelogin_process;
+ struct login_process *newest_prelogin_process;
};
-void login_process_abormal_exit(pid_t pid);
-void login_processes_destroy_all(void);
+void login_process_destroyed(pid_t pid, bool abnormal_exit);
+
+void login_processes_destroy_all(bool unref);
void login_processes_init(void);
void login_processes_deinit(void);
Index: main.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/master/main.c,v
retrieving revision 1.89
retrieving revision 1.90
diff -u -d -r1.89 -r1.90
--- main.c 5 Jul 2006 19:52:22 -0000 1.89
+++ main.c 6 Aug 2006 20:05:36 -0000 1.90
@@ -37,7 +37,6 @@
};
static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf";
-static struct timeout *to;
static const char *env_tz;
struct ioloop *ioloop;
@@ -134,7 +133,7 @@
i_warning("SIGHUP received - reloading configuration");
/* restart auth and login processes */
- login_processes_destroy_all();
+ login_processes_destroy_all(FALSE);
auth_processes_destroy_all();
dict_process_kill();
@@ -191,38 +190,28 @@
return NULL;
}
-static void timeout_handler(void *context __attr_unused__)
+static void sigchld_handler(int signo __attr_unused__,
+ void *context __attr_unused__)
{
const char *process_type_name, *msg;
pid_t pid;
int status, process_type;
+ bool abnormal_exit;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
/* get the type and remove from hash */
process_type = PID_GET_PROCESS_TYPE(pid);
PID_REMOVE_PROCESS_TYPE(pid);
- switch (process_type) {
- case PROCESS_TYPE_IMAP:
- case PROCESS_TYPE_POP3:
- mail_process_destroyed(pid);
- break;
- case PROCESS_TYPE_SSL_PARAM:
- ssl_parameter_process_destroyed(pid);
- break;
- case PROCESS_TYPE_DICT:
- dict_process_restart();
- break;
- }
+ abnormal_exit = TRUE;
/* write errors to syslog */
process_type_name = process_names[process_type];
if (WIFEXITED(status)) {
status = WEXITSTATUS(status);
- if (status != 0) {
- if (process_type == PROCESS_TYPE_LOGIN)
- login_process_abormal_exit(pid);
-
+ if (status == 0)
+ abnormal_exit = FALSE;
+ else {
msg = get_exit_status_message(status);
msg = msg == NULL ? "" :
t_strconcat(" (", msg, ")", NULL);
@@ -231,12 +220,26 @@
status, msg);
}
} else if (WIFSIGNALED(status)) {
- if (process_type == PROCESS_TYPE_LOGIN)
- login_process_abormal_exit(pid);
i_error("child %s (%s) killed with signal %d",
dec2str(pid), process_type_name,
WTERMSIG(status));
}
+
+ switch (process_type) {
+ case PROCESS_TYPE_LOGIN:
+ login_process_destroyed(pid, abnormal_exit);
+ break;
+ case PROCESS_TYPE_IMAP:
+ case PROCESS_TYPE_POP3:
+ mail_process_destroyed(pid);
+ break;
+ case PROCESS_TYPE_SSL_PARAM:
+ ssl_parameter_process_destroyed(pid);
+ break;
+ case PROCESS_TYPE_DICT:
+ dict_process_restart();
+ break;
+ }
}
if (pid == -1 && errno != EINTR && errno != ECHILD)
@@ -558,7 +561,7 @@
lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL);
pids = hash_create(default_pool, default_pool, 128, NULL, NULL);
- to = timeout_add(100, timeout_handler, NULL);
+ lib_signals_set_handler(SIGCHLD, TRUE, sigchld_handler, NULL);
ssl_init();
dict_process_init();
@@ -575,14 +578,14 @@
"/master.pid", NULL));
/* make sure we log if child processes died unexpectedly */
- timeout_handler(NULL);
+ sigchld_handler(SIGCHLD, NULL);
login_processes_deinit();
auth_processes_deinit();
dict_process_deinit();
ssl_deinit();
- timeout_remove(&to);
+ lib_signals_unset_handler(SIGCHLD, sigchld_handler, NULL);
if (close(null_fd) < 0)
i_error("close(null_fd) failed: %m");
Index: master-login-interface.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/master/master-login-interface.h,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -d -r1.7 -r1.8
--- master-login-interface.h 13 Jan 2006 20:26:40 -0000 1.7
+++ master-login-interface.h 6 Aug 2006 20:05:36 -0000 1.8
@@ -9,10 +9,23 @@
/* Increase the version number every time master_login_request
(or something else) is changed. */
-#define MASTER_LOGIN_PROTOCOL_VERSION 1
+#define MASTER_LOGIN_PROTOCOL_VERSION 2
+
+enum master_login_state {
+ /* process is accepting new connections */
+ LOGIN_STATE_LISTENING = 0,
+ /* process isn't accepting new connections, but it'd be able to kill
+ some connections which haven't logged in yet */
+ LOGIN_STATE_FULL_PRELOGINS,
+ /* process is handling only logged in users */
+ LOGIN_STATE_FULL_LOGINS,
+
+ LOGIN_STATE_COUNT
+};
struct master_login_request {
uint32_t version;
+ /* if fd == -1, tag is used as master_login_state */
uint32_t tag;
uint32_t auth_pid;
Index: master-settings.c
===================================================================
RCS file: /var/lib/cvs/dovecot/src/master/master-settings.c,v
retrieving revision 1.133
retrieving revision 1.134
diff -u -d -r1.133 -r1.134
--- master-settings.c 2 Jul 2006 19:19:35 -0000 1.133
+++ master-settings.c 6 Aug 2006 20:05:36 -0000 1.134
@@ -93,7 +93,7 @@
DEF(SET_INT, login_process_size),
DEF(SET_INT, login_processes_count),
DEF(SET_INT, login_max_processes_count),
- DEF(SET_INT, login_max_logging_users),
+ DEF(SET_INT, login_max_connections),
/* mail */
DEF(SET_STR, valid_chroot_dirs),
@@ -299,7 +299,7 @@
MEMBER(login_process_size) 32,
MEMBER(login_processes_count) 3,
MEMBER(login_max_processes_count) 128,
- MEMBER(login_max_logging_users) 256,
+ MEMBER(login_max_connections) 256,
/* mail */
MEMBER(valid_chroot_dirs) "",
@@ -846,8 +846,8 @@
i_error("login_processes_count must be at least 1");
return FALSE;
}
- if (set->login_max_logging_users < 1) {
- i_error("login_max_logging_users must be at least 1");
+ if (set->login_max_connections < 1) {
+ i_error("login_max_connections must be at least 1");
return FALSE;
}
Index: master-settings.h
===================================================================
RCS file: /var/lib/cvs/dovecot/src/master/master-settings.h,v
retrieving revision 1.86
retrieving revision 1.87
diff -u -d -r1.86 -r1.87
--- master-settings.h 28 Jun 2006 13:10:54 -0000 1.86
+++ master-settings.h 6 Aug 2006 20:05:36 -0000 1.87
@@ -54,7 +54,7 @@
unsigned int login_process_size;
unsigned int login_processes_count;
unsigned int login_max_processes_count;
- unsigned int login_max_logging_users;
+ unsigned int login_max_connections;
/* mail */
const char *valid_chroot_dirs;
More information about the dovecot-cvs
mailing list