view src/master/main.c @ 5639:e1b3043c0d90 HEAD

If startup fails because of configuration problems, print "Invalid configuration in <path>"
author Timo Sirainen <tss@iki.fi>
date Tue, 22 May 2007 03:58:32 +0300
parents b02af502bcd6
children b929827ddbe9
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "common.h"
#include "ioloop.h"
#include "lib-signals.h"
#include "network.h"
#include "env-util.h"
#include "fd-close-on-exec.h"
#include "write-full.h"

#include "askpass.h"
#include "auth-process.h"
#include "dict-process.h"
#include "login-process.h"
#include "mail-process.h"
#include "syslog-util.h"
#include "ssl-init.h"
#include "log.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/wait.h>
#ifdef HAVE_LIBCAP
#include <sys/capability.h>
#endif

const char *process_names[PROCESS_TYPE_MAX] = {
	"unknown",
	"auth",
	"auth-worker",
	"login",
	"imap",
	"pop3",
	"ssl-build-param",
	"dict"
};

/* the capabilities that we *need* in order to operate */
#ifdef HAVE_LIBCAP
cap_t caps;
cap_value_t suidcaps[] = {
	CAP_CHOWN,
	CAP_SYS_CHROOT,
	CAP_SETUID,
	CAP_SETGID,
	CAP_NET_BIND_SERVICE
};
#endif

static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf";
static const char *env_tz;

struct ioloop *ioloop;
struct hash_table *pids;
int null_fd = -1, inetd_login_fd;
uid_t master_uid;
char program_path[PATH_MAX];
char ssl_manual_key_password[100];
#ifdef DEBUG
static bool gdb;
#endif

static void listen_fds_open(bool retry);
static void listen_fds_close(struct server_settings *server);

bool validate_str(const char *str, size_t max_len)
{
	size_t i;

	for (i = 0; i < max_len; i++) {
		if (str[i] == '\0')
			return TRUE;
	}

	return FALSE;
}

void child_process_init_env(void)
{
	int facility;

	/* remove all environment, we don't need them */
	env_clean();

	/* we'll log through master process */
	env_put("LOG_TO_MASTER=1");
	if (env_tz != NULL)
		env_put(t_strconcat("TZ=", env_tz, NULL));

	if (settings_root == NULL ||
	    !syslog_facility_find(settings_root->defaults->syslog_facility,
				  &facility))
		facility = LOG_MAIL;
	env_put(t_strdup_printf("SYSLOG_FACILITY=%d", facility));

	if (settings_root != NULL && !settings_root->defaults->version_ignore)
		env_put("DOVECOT_VERSION="PACKAGE_VERSION);
#ifdef DEBUG
	if (gdb) env_put("GDB=1");
#endif
}

void client_process_exec(const char *cmd, const char *title)
{
	const char *executable, *p, **argv;

	/* very simple argument splitting. */
	if (*title == '\0')
		argv = t_strsplit(cmd, " ");
	else
		argv = t_strsplit(t_strconcat(cmd, " ", title, NULL), " ");

	executable = argv[0];

	/* hide the path, it's ugly */
	p = strrchr(argv[0], '/');
	if (p != NULL) argv[0] = p+1;

	execv(executable, (char **)argv);
}

static void set_logfile(struct settings *set)
{
	int facility;

	if (*set->log_path == '\0') {
		if (!syslog_facility_find(set->syslog_facility, &facility))
			facility = LOG_MAIL;

		i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
	} else {
		/* log to file or stderr */
		i_set_failure_file(set->log_path, "dovecot: ");
	}

	if (*set->info_log_path != '\0')
		i_set_info_file(set->info_log_path);

	i_set_failure_timestamp_format(set->log_timestamp);
}

static void settings_reload(void)
{
	struct server_settings *old_set = settings_root;

	i_warning("SIGHUP received - reloading configuration");

	/* restart auth and login processes */
        login_processes_destroy_all();
        auth_processes_destroy_all();
        dict_process_kill();

	if (!master_settings_read(configfile, FALSE, FALSE))
		i_warning("Invalid configuration, keeping old one");
	else {
		if (!IS_INETD()) {
			listen_fds_close(old_set);
			listen_fds_open(TRUE);
		}
                set_logfile(settings_root->defaults);
	}
}

static void sig_die(int signo, void *context __attr_unused__)
{
	/* warn about being killed because of some signal, except SIGINT (^C)
	   which is too common at least while testing :) */
	if (signo != SIGINT)
		i_warning("Killed with signal %d", signo);
	io_loop_stop(ioloop);
}

static void sig_reload_settings(int signo __attr_unused__,
				void *context __attr_unused__)
{
	settings_reload();
}

static void sig_reopen_logs(int signo __attr_unused__,
			    void *context __attr_unused__)
{
	set_logfile(settings_root->defaults);
}

static const char *get_exit_status_message(enum fatal_exit_status status)
{
	switch (status) {
	case FATAL_LOGOPEN:
		return "Can't open log file";
	case FATAL_LOGWRITE:
		return "Can't write to log file";
	case FATAL_LOGERROR:
		return "Internal logging error";
	case FATAL_OUTOFMEM:
		return "Out of memory";
	case FATAL_EXEC:
		return "exec() failed";

	case FATAL_DEFAULT:
		return NULL;
	}

	return NULL;
}

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);
		if (process_type != PROCESS_TYPE_UNKNOWN)
			PID_REMOVE_PROCESS_TYPE(pid);

		abnormal_exit = TRUE;

		/* write errors to syslog */
		process_type_name = process_names[process_type];
		if (WIFEXITED(status)) {
			status = WEXITSTATUS(status);
			if (status == 0) {
				abnormal_exit = FALSE;
				if (process_type == PROCESS_TYPE_UNKNOWN) {
					i_error("unknown child %s exited "
						"successfully", dec2str(pid));
				}
			} else if (status == 1 &&
				   process_type == PROCESS_TYPE_SSL_PARAM) {
				/* kludgy. hide this failure. */
			} else {
				msg = get_exit_status_message(status);
				msg = msg == NULL ? "" :
					t_strconcat(" (", msg, ")", NULL);
				i_error("child %s (%s) returned error %d%s",
					dec2str(pid), process_type_name,
					status, msg);
			}
		} else if (WIFSIGNALED(status)) {
			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(abnormal_exit);
			break;
		case PROCESS_TYPE_DICT:
			dict_process_restart();
			break;
		}
	}

	if (pid == -1 && errno != EINTR && errno != ECHILD)
		i_warning("waitpid() failed: %m");
}

static void resolve_ip(const char *set_name, const char *name,
		       struct ip_addr *ip, unsigned int *port)
{
	struct ip_addr *ip_list;
	const char *p;
	unsigned int ips_count;
	int ret;

	if (*name == '\0') {
                /* defaults to "*" or "[::]" */
		ip->family = 0;
		return;
	}

	if (name[0] == '[') {
		/* IPv6 address */
		p = strchr(name, ']');
		if (p == NULL) {
			i_fatal("%s: Missing ']' in address %s",
				set_name, name);
		}
		name = t_strdup_until(name+1, p);

		p++;
		if (*p == '\0')
			p = NULL;
		else if (*p != ':') {
			i_fatal("%s: Invalid data after ']' in address %s",
				set_name, name);
		}
	} else {
		p = strrchr(name, ':');
		if (p != NULL)
			name = t_strdup_until(name, p);
	}

	if (p != NULL) {
		if (!is_numeric(p+1, '\0')) {
			i_fatal("%s: Invalid port in address %s",
				set_name, name);
		}
		*port = atoi(p+1);
	}

	if (strcmp(name, "*") == 0) {
		/* IPv4 any */
		net_get_ip_any4(ip);
		return;
	}

	if (strcmp(name, "::") == 0) {
		/* IPv6 any */
		net_get_ip_any6(ip);
		return;
	}

	/* Return the first IP if there happens to be multiple. */
	ret = net_gethostbyname(name, &ip_list, &ips_count);
	if (ret != 0) {
		i_fatal("%s: Can't resolve address %s: %s",
			set_name, name, net_gethosterror(ret));
	}

	if (ips_count < 1)
		i_fatal("%s: No IPs for address: %s", set_name, name);

	*ip = ip_list[0];
}

static void
check_conflicts_set(const struct settings *set, const struct ip_addr *ip,
		    unsigned int port, const char *name1, const char *name2)
{
	if (set->listen_port == port && net_ip_compare(ip, &set->listen_ip) &&
	    set->listen_fd > 0) {
		i_fatal("Protocols %s and %s are listening in same ip/port",
			name1, name2);
	}
	if (set->ssl_listen_port == port &&
	    net_ip_compare(ip, &set->ssl_listen_ip) && set->ssl_listen_fd > 0) {
		i_fatal("Protocols %ss and %s are listening in same ip/port",
			name1, name2);
	}
}

static void check_conflicts(const struct ip_addr *ip, unsigned int port,
			    const char *proto)
{
	struct server_settings *server;

	for (server = settings_root; server != NULL; server = server->next) {
		if (server->imap != NULL) {
			check_conflicts_set(server->imap, ip, port,
					    "imap", proto);
		}
		if (server->pop3 != NULL) {
			check_conflicts_set(server->pop3, ip, port,
					    "pop3", proto);
		}
	}
}

static void listen_protocols(struct settings *set, bool retry)
{
	struct ip_addr *ip;
	const char *const *proto;
	unsigned int port;
	int *fd, i;

	set->listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 143 : 110;
#ifdef HAVE_SSL
	set->ssl_listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 993 : 995;
#else
	set->ssl_listen_port = 0;
#endif

	/* resolve */
	resolve_ip("listen", set->listen, &set->listen_ip, &set->listen_port);
	if (!set->ssl_disable) {
		resolve_ip("ssl_listen", set->ssl_listen, &set->ssl_listen_ip,
			   &set->ssl_listen_port);
	}

	/* if ssl_listen wasn't explicitly set in the config file,
	   use the non-ssl IP settings for the ssl listener, too. */
	if (set->ssl_listen_ip.family == 0 && *set->ssl_listen == '\0')
		set->ssl_listen_ip = set->listen_ip;

	/* register wanted protocols */
        proto = t_strsplit_spaces(set->protocols, " ");
	for (; *proto != NULL; proto++) {
		fd = NULL; ip = NULL; port = 0;
		if (strcasecmp(*proto, "imap") == 0) {
			if (set->protocol == MAIL_PROTOCOL_IMAP) {
				fd = &set->listen_fd;
				port = set->listen_port;
				ip = &set->listen_ip;
			}
		} else if (strcasecmp(*proto, "imaps") == 0) {
			if (set->protocol == MAIL_PROTOCOL_IMAP &&
			    !set->ssl_disable) {
				fd = &set->ssl_listen_fd;
				port = set->ssl_listen_port;
				ip = &set->ssl_listen_ip;
			}
		} else if (strcasecmp(*proto, "pop3") == 0) {
			if (set->protocol == MAIL_PROTOCOL_POP3) {
				fd = &set->listen_fd;
				port = set->listen_port;
				ip = &set->listen_ip;
			}
		} else if (strcasecmp(*proto, "pop3s") == 0) {
			if (set->protocol == MAIL_PROTOCOL_POP3 &&
			    !set->ssl_disable) {
				fd = &set->ssl_listen_fd;
				port = set->ssl_listen_port;
				ip = &set->ssl_listen_ip;
			}
		} else {
			i_fatal("Unknown protocol %s", *proto);
		}

		if (fd == NULL)
			continue;

		if (*fd != -1)
			i_fatal("Protocol %s given more than once", *proto);

		if (port == 0)
			*fd = null_fd;
		else {
			for (i = 0; i < 10; i++) {
				*fd = net_listen(ip, &port, 8);
				if (*fd != -1)
					break;
				if (errno == EADDRINUSE) {
					/* retry */
				} else if (errno == EINTR &&
					   io_loop_is_running(ioloop)) {
					/* SIGHUPing sometimes gets us here.
					   we don't want to die. */
				} else {
					/* error */
					break;
				}

				check_conflicts(ip, port, *proto);
				if (!retry)
					break;

				/* wait a while and try again. we're SIGHUPing
				   so we most likely just closed it ourself.. */
				sleep(1);
			}

			if (*fd == -1)
				i_fatal("listen(%d) failed: %m", port);
			net_set_nonblock(*fd, TRUE);
			fd_close_on_exec(*fd, TRUE);
		}
	}

	if (set->listen_fd == -1)
		set->listen_fd = null_fd;
	if (set->ssl_listen_fd == -1)
		set->ssl_listen_fd = null_fd;
}

static void listen_fds_open(bool retry)
{
	struct server_settings *server;

	for (server = settings_root; server != NULL; server = server->next) {
		if (server->imap != NULL)
			listen_protocols(server->imap, retry);
		if (server->pop3 != NULL)
			listen_protocols(server->pop3, retry);
	}
}

static void listen_fds_close(struct server_settings *server)
{
	for (; server != NULL; server = server->next) {
		if (server->imap != NULL) {
			if (server->imap->listen_fd != null_fd &&
			    close(server->imap->listen_fd) < 0)
				i_error("close(imap.listen_fd) failed: %m");
			if (server->imap->ssl_listen_fd != null_fd &&
			    close(server->imap->ssl_listen_fd) < 0)
				i_error("close(imap.ssl_listen_fd) failed: %m");
		}
		if (server->pop3 != NULL) {
			if (server->pop3->listen_fd != null_fd &&
			    close(server->pop3->listen_fd) < 0)
				i_error("close(pop3.listen_fd) failed: %m");
			if (server->pop3->ssl_listen_fd != null_fd &&
			    close(server->pop3->ssl_listen_fd) < 0)
				i_error("close(pop3.ssl_listen_fd) failed: %m");
		}
	}
}

static bool have_stderr_set(struct settings *set)
{
	if (*set->log_path != '\0' &&
	    strcmp(set->log_path, "/dev/stderr") == 0)
		return TRUE;

	if (*set->info_log_path != '\0' &&
	    strcmp(set->info_log_path, "/dev/stderr") == 0)
		return TRUE;

	return FALSE;
}

static bool have_stderr(struct server_settings *server)
{
	while (server != NULL) {
		if (server->imap != NULL && have_stderr_set(server->imap))
			return TRUE;
		if (server->pop3 != NULL && have_stderr_set(server->pop3))
			return TRUE;

		server = server->next;
	}

	return FALSE;
}

static void open_null_fd(void)
{
	null_fd = open("/dev/null", O_RDONLY);
	if (null_fd == -1)
		i_fatal("Can't open /dev/null: %m");
	fd_close_on_exec(null_fd, TRUE);
}

static void open_fds(void)
{
	/* make sure all fds between 0..3 are used. */
	while (null_fd < 4) {
		null_fd = dup(null_fd);
		if (null_fd == -1)
			i_fatal("dup(null_fd) failed: %m");
		fd_close_on_exec(null_fd, TRUE);
	}

	if (!IS_INETD())
		listen_fds_open(FALSE);

	/* close stdin and stdout. */
	if (dup2(null_fd, 0) < 0)
		i_fatal("dup2(0) failed: %m");
	if (dup2(null_fd, 1) < 0)
		i_fatal("dup2(1) failed: %m");
}

static void create_pid_file(const char *path)
{
	const char *pid;
	int fd;

	pid = t_strconcat(dec2str(getpid()), "\n", NULL);

	fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
	if (fd == -1)
		i_fatal("open(%s) failed: %m", path);
	if (write_full(fd, pid, strlen(pid)) < 0)
		i_fatal("write() failed in %s: %m", path);
	(void)close(fd);
}

static void main_init(bool log_error)
{
	/* deny file access from everyone else except owner */
        (void)umask(0077);

	/* close stderr unless we're logging into /dev/stderr. keep as little
	   distance between closing it and opening the actual log file so that
	   we don't lose anything. */
	if (!have_stderr(settings_root)) {
		if (dup2(null_fd, 2) < 0)
			i_fatal("dup2(2) failed: %m");
	}

	set_logfile(settings_root->defaults);
	i_info("Dovecot v"VERSION" starting up");

	log_init();

	if (log_error)
		i_fatal("This is Dovecot's error log");

#ifdef HAVE_LIBCAP
	/* drop capabilities that we don't need, be very restrictive. */
	caps = cap_init();
	cap_clear(caps);
	cap_set_flag(caps, CAP_PERMITTED,
		     sizeof(suidcaps) / sizeof(cap_value_t), suidcaps, CAP_SET);
	cap_set_flag(caps, CAP_EFFECTIVE,
		     sizeof(suidcaps) / sizeof(cap_value_t), suidcaps, CAP_SET);
	cap_set_proc(caps);
	cap_free(caps);
#endif

	lib_signals_init();
        lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
        lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
        lib_signals_ignore(SIGPIPE, TRUE);
        lib_signals_ignore(SIGALRM, FALSE);
        lib_signals_set_handler(SIGHUP, TRUE, sig_reload_settings, NULL);
        lib_signals_set_handler(SIGUSR1, TRUE, sig_reopen_logs, NULL);

	pids = hash_create(default_pool, default_pool, 128, NULL, NULL);
	lib_signals_set_handler(SIGCHLD, TRUE, sigchld_handler, NULL);

	ssl_init();
	dict_process_init();
	auth_processes_init();
	login_processes_init();

	create_pid_file(t_strconcat(settings_root->defaults->base_dir,
				    "/master.pid", NULL));
}

static void main_deinit(void)
{
	(void)unlink(t_strconcat(settings_root->defaults->base_dir,
				 "/master.pid", NULL));

	/* make sure we log if child processes died unexpectedly */
	sigchld_handler(SIGCHLD, NULL);

	login_processes_deinit();
	auth_processes_deinit();
	dict_process_deinit();
	ssl_deinit();

	lib_signals_unset_handler(SIGCHLD, sigchld_handler, NULL);

	if (close(null_fd) < 0)
		i_error("close(null_fd) failed: %m");

	hash_destroy(pids);
	lib_signals_deinit();
	log_deinit();
	closelog();
}

static void daemonize(struct settings *set)
{
	pid_t pid;

	pid = fork();
	if (pid < 0)
		i_fatal("fork() failed: %m");

	if (pid != 0)
		_exit(0);

	if (setsid() < 0)
		i_fatal("setsid() failed: %m");

	if (chdir(set->base_dir) < 0)
		i_fatal("chdir(%s) failed: %m", set->base_dir);
}

static void print_help(void)
{
	printf(
"Usage: dovecot [-F] [-c <config file>] [-p] [-n] [-a]\n"
"       [--exec-mail <protocol>] [--version] [--build-options]\n");
}

static void print_build_options(void)
{
	printf("Build options:"
#ifdef IOLOOP_EPOLL
		" ioloop=epoll"
#endif
#ifdef IOLOOP_KQUEUE
		" ioloop=kqueue"
#endif
#ifdef IOLOOP_POLL
		" ioloop=poll"
#endif
#ifdef IOLOOP_SELECT
		" ioloop=select"
#endif
#ifdef IOLOOP_NOTIFY_DNOTIFY
		" notify=dnotify"
#endif
#ifdef IOLOOP_NOTIFY_INOTIFY
		" notify=inotify"
#endif
#ifdef IOLOOP_NOTIFY_KQUEUE
		" notify=kqueue"
#endif
#ifdef HAVE_IPV6
		" ipv6"
#endif
#ifdef HAVE_GNUTLS
		" gnutls"
#endif
#ifdef HAVE_OPENSSL
		" openssl"
#endif
	"\nSQL drivers:"
#ifdef BUILD_MYSQL
		" mysql"
#endif
#ifdef BUILD_PGSQL
		" postgresql"
#endif
#ifdef BUILD_SQLITE
		" sqlite"
#endif
	"\nPassdb:"
#ifdef PASSDB_BSDAUTH
		" bsdauth"
#endif
#ifdef PASSDB_CHECKPASSWORD
		" checkpassword"
#endif
#ifdef PASSDB_LDAP
		" ldap"
#endif
#ifdef PASSDB_PAM
		" pam"
#endif
#ifdef PASSDB_PASSWD
		" passwd"
#endif
#ifdef PASSDB_PASSWD_FILE
		" passwd-file"
#endif
#ifdef PASSDB_SHADOW 
		" shadow"
#endif
#ifdef PASSDB_SQL 
		" sql"
#endif
#ifdef PASSDB_VPOPMAIL
		" vpopmail"
#endif
	"\nUserdb:"
#ifdef USERDB_CHECKPASSWORD
		" checkpassword"
#endif
#ifdef USERDB_LDAP
		" ldap"
#endif
#ifdef USERDB_PASSWD
		" passwd"
#endif
#ifdef USERDB_PREFETCH
		" prefetch"
#endif
#ifdef USERDB_PASSWD_FILE
		" passwd-file"
#endif
#ifdef USERDB_SQL 
		" sql"
#endif
#ifdef USERDB_STATIC 
		" static"
#endif
#ifdef USERDB_VPOPMAIL
		" vpopmail"
#endif
	"\n");
}

int main(int argc, char *argv[])
{
	/* parse arguments */
	const char *exec_protocol = NULL, *exec_section = NULL, *user, *home;
	bool foreground = FALSE, ask_key_pass = FALSE, log_error = FALSE;
	bool dump_config = FALSE, dump_config_nondefaults = FALSE;
	int i;

#ifdef DEBUG
	gdb = getenv("GDB") != NULL;
#endif
	lib_init();

	master_uid = geteuid();
        inetd_login_fd = -1;
	for (i = 1; i < argc; i++) {
		if (strcmp(argv[i], "-F") == 0) {
			/* foreground */
			foreground = TRUE;
		} else if (strcmp(argv[i], "-a") == 0) {
			dump_config = TRUE;
		} else if (strcmp(argv[i], "-c") == 0) {
			/* config file */
			i++;
			if (i == argc) i_fatal("Missing config file argument");
			configfile = argv[i];
		} else if (strcmp(argv[i], "-n") == 0) {
			dump_config_nondefaults = dump_config = TRUE;
		} else if (strcmp(argv[i], "-p") == 0) {
			/* Ask SSL private key password */
			ask_key_pass = TRUE;
		} else if (strcmp(argv[i], "--exec-mail") == 0) {
			/* <protocol> [<server section>]
			   read configuration and execute mail process */
			i++;
			if (i == argc) i_fatal("Missing protocol argument");
			exec_protocol = argv[i];
			if (i+1 != argc) 
				exec_section = argv[++i];
		} else if (strcmp(argv[i], "--version") == 0) {
			printf("%s\n", VERSION);
			return 0;
		} else if (strcmp(argv[i], "--build-options") == 0) {
			print_build_options();
			return 0;
		} else if (strcmp(argv[i], "--log-error") == 0) {
			log_error = TRUE;
			foreground = TRUE;
		} else {
			print_help();
			i_fatal("Unknown argument: %s", argv[1]);
		}
	}

	/* need to have this open before reading settings */
	open_null_fd();

	if (getenv("DOVECOT_INETD") != NULL) {
		/* starting through inetd. */
		inetd_login_fd = dup(0);
		if (inetd_login_fd == -1)
			i_fatal("dup(0) failed: %m");
		fd_close_on_exec(inetd_login_fd, TRUE);
		foreground = TRUE;
	}

	if (dump_config) {
		/* print the config file path before parsing it, so in case
		   of errors it's still shown */
		printf("# "VERSION": %s\n", configfile);
	}

	/* read and verify settings before forking */
	t_push();
	master_settings_init();
	if (!master_settings_read(configfile, exec_protocol != NULL,
				  dump_config))
		i_fatal("Invalid configuration in %s", configfile);
	t_pop();

	if (dump_config) {
		master_settings_dump(settings_root, dump_config_nondefaults);
		return 0;
	}

	if (ask_key_pass) {
		const char *prompt;

		t_push();
		prompt = t_strdup_printf("Give the password for SSL key file "
					 "%s: ",
					 settings_root->defaults->ssl_key_file);
		askpass(prompt, ssl_manual_key_password,
			sizeof(ssl_manual_key_password));
		t_pop();
	}

	/* save TZ environment. AIX depends on it to get the timezone
	   correctly. */
	env_tz = getenv("TZ");
	user = getenv("USER");
	home = getenv("HOME");

	/* clean up the environment of everything */
	env_clean();

	/* put back the TZ */
	if (env_tz != NULL)
		env_put(t_strconcat("TZ=", env_tz, NULL));

	if (exec_protocol != NULL) {
		/* Put back user and home */
		env_put(t_strconcat("USER=", user, NULL));
		env_put(t_strconcat("HOME=", home, NULL));
		mail_process_exec(exec_protocol, exec_section);
	}

	open_fds();

	if (!foreground)
		daemonize(settings_root->defaults);

	ioloop = io_loop_create();

	main_init(log_error);
        io_loop_run(ioloop);
	main_deinit();

	master_settings_deinit();
	io_loop_destroy(&ioloop);
	lib_deinit();

        return 0;
}