login-process.c revision aff95d423ca04ac801d55bbb3e29cc18d630f97a
/* Copyright (C) 2002 Timo Sirainen */
#include "common.h"
#include "ioloop.h"
#include "network.h"
#include "ostream.h"
#include "fdpass.h"
#include "fd-close-on-exec.h"
#include "env-util.h"
#include "restrict-access.h"
#include "restrict-process-size.h"
#include "login-process.h"
#include "auth-process.h"
#include "master-interface.h"
#include <unistd.h>
#include <syslog.h>
typedef struct _LoginProcess LoginProcess;
struct _LoginProcess {
int refcount;
int fd;
unsigned int listening:1;
unsigned int destroyed:1;
};
typedef struct {
int login_id;
int auth_id;
int fd;
char login_tag[LOGIN_TAG_SIZE];
static int auth_id_counter;
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);
{
else {
}
/* reply to login */
}
static void login_process_mark_nonlistening(LoginProcess *p)
{
if (!p->listening) {
i_error("login: received another \"not listening\" "
"notification");
return;
}
if (newest_nonlisten_process != NULL)
if (oldest_nonlisten_process == NULL)
}
{
LoginProcess *p = context;
if (ret == 0) {
/* disconnected, ie. the login process died */
} else if (ret > 0) {
/* req wasn't fully read */
i_error("login: fd_read() couldn't read all req");
} else {
i_error("login: fd_read() failed: %m");
}
return;
}
if (client_fd == -1) {
/* just a notification that the login process isn't
listening for new connections anymore */
return;
}
/* login process isn't trusted, validate all data to make sure
it's not trying to exploit us */
i_error("login: Received corrupted data");
return;
}
/* ask the cookie from the auth process */
p->refcount++;
if (auth_process == NULL) {
i_error("login: Authentication process %u doesn't exist",
} else {
}
}
{
LoginProcess *p;
p->refcount = 1;
sizeof(MasterReply)*10,
return p;
}
static void login_process_remove_from_lists(LoginProcess *p)
{
if (p == oldest_nonlisten_process)
else
if (p == newest_nonlisten_process)
else
}
static void login_process_destroy(LoginProcess *p)
{
if (p->destroyed)
return;
if (p->listening)
o_stream_close(p->output);
if (!p->listening)
}
static void login_process_unref(LoginProcess *p)
{
if (--p->refcount > 0)
return;
o_stream_unref(p->output);
i_free(p);
}
static pid_t create_login_process(void)
{
int fd[2];
if (oldest_nonlisten_process != NULL)
}
if (set_login_uid == 0)
i_fatal("Login process must not run as root");
/* create communication to process with a socket pair */
i_error("socketpair() failed: %m");
return -1;
}
if (pid < 0) {
i_error("fork() failed: %m");
return -1;
}
if (pid != 0) {
/* master */
return pid;
}
/* move communication handle */
i_fatal("login: dup2() failed: %m");
/* move the listen handle */
i_fatal("login: dup2() failed: %m");
/* move the SSL listen handle */
i_fatal("login: dup2() failed: %m");
/* imap_fd and imaps_fd are closed by clean_child_process() */
/* setup access environment - needs to be done after
clean_child_process() since it clears environment */
if (!set_login_chroot) {
/* no chrooting, but still change to the directory */
if (chdir(set_login_dir) < 0) {
i_fatal("chdir(%s) failed: %m",
}
}
if (!set_ssl_disable) {
}
env_put("DISABLE_PLAINTEXT_AUTH=1");
env_put("VERBOSE_PROCTITLE=1");
env_put("PROCESS_PER_CONNECTION=1");
env_put("MAX_LOGGING_USERS=1");
} else {
}
/* 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 */
return -1;
}
{
/* don't start raising the process count if they're dying all
the time */
}
void *context __attr_unused__)
{
}
void login_processes_destroy_all(void)
{
/* don't double their amount when restarting */
}
{
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. */
(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. */
else if (listening_processes == 0)
wanted_processes_count *= 2;
else if (wanted_processes_count > set_login_processes_count)
while (listening_processes < wanted_processes_count)
(void)create_login_process();
}
}
void login_processes_init(void)
{
auth_id_counter = 0;
listening_processes = 0;
}
void login_processes_deinit(void)
{
}