Mercurial > dovecot > original-hg > dovecot-1.2
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);