main.c revision c9ddb09a1b4d9991be94415387a729080d077943
0N/A/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
0N/A
0N/A#include "common.h"
0N/A#include "ioloop.h"
0N/A#include "lib-signals.h"
0N/A#include "fd-close-on-exec.h"
115N/A#include "array.h"
0N/A#include "write-full.h"
0N/A#include "env-util.h"
0N/A#include "hostpid.h"
0N/A#include "restrict-process-size.h"
0N/A#include "master-service.h"
975N/A#include "master-service-settings.h"
116N/A#include "askpass.h"
0N/A#include "capabilities.h"
116N/A#include "service.h"
0N/A#include "service-listen.h"
0N/A#include "service-monitor.h"
0N/A#include "service-log.h"
115N/A
115N/A#include <stdio.h>
115N/A#include <stdlib.h>
13N/A#include <unistd.h>
0N/A#include <fcntl.h>
0N/A#include <sys/stat.h>
0N/A#include <pwd.h>
0N/A#include <grp.h>
0N/A
0N/A#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
0N/A
0N/A#define MASTER_SERVICE_NAME "master"
975N/A#define FATAL_FILENAME "master-fatal.lastlog"
975N/A#define MASTER_PID_FILE_NAME "master.pid"
975N/A#define SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS (60*3)
975N/A
975N/Astruct master_service *master_service;
975N/Auid_t master_uid;
975N/Agid_t master_gid;
975N/Abool auth_success_written;
975N/Abool core_dumps_disabled;
975N/Achar ssl_manual_key_password[100];
975N/Aint null_fd;
0N/Astruct service_list *services;
0N/A
0N/Astatic char *pidfile_path;
0N/Astatic fatal_failure_callback_t *orig_fatal_callback;
115N/Astatic failure_callback_t *orig_error_callback;
115N/Astatic const char *child_process_env[3]; /* @UNSAFE */
975N/A
975N/Astatic const struct setting_parser_info *set_roots[] = {
975N/A &master_setting_parser_info,
0N/A NULL
0N/A};
0N/A
975N/Avoid process_exec(const char *cmd, const char *extra_args[])
975N/A{
975N/A const char *executable, *p, **argv;
975N/A
975N/A argv = t_strsplit(cmd, " ");
975N/A executable = argv[0];
975N/A
975N/A if (extra_args != NULL) {
975N/A unsigned int count1, count2;
979N/A const char **new_argv;
979N/A
975N/A /* @UNSAFE */
979N/A count1 = str_array_length(argv);
975N/A count2 = str_array_length(extra_args);
975N/A new_argv = t_new(const char *, count1 + count2 + 1);
975N/A memcpy(new_argv, argv, sizeof(const char *) * count1);
979N/A memcpy(new_argv + count1, extra_args,
975N/A sizeof(const char *) * count2);
975N/A argv = new_argv;
975N/A }
975N/A
975N/A /* hide the path, it's ugly */
975N/A p = strrchr(argv[0], '/');
975N/A if (p != NULL) argv[0] = p+1;
975N/A
975N/A /* prefix with dovecot/ */
975N/A argv[0] = t_strconcat(PACKAGE"/", argv[0], NULL);
975N/A
975N/A (void)execv(executable, (char **)argv);
975N/A i_fatal_status(errno == ENOMEM ? FATAL_OUTOFMEM : FATAL_EXEC,
975N/A "execv(%s) failed: %m", executable);
975N/A}
975N/A
975N/Aint get_uidgid(const char *user, uid_t *uid_r, gid_t *gid_r,
975N/A const char **error_r)
975N/A{
975N/A struct passwd *pw;
975N/A
975N/A if (*user == '\0') {
975N/A *uid_r = (uid_t)-1;
975N/A *gid_r = (gid_t)-1;
975N/A return 0;
979N/A }
975N/A
975N/A if ((pw = getpwnam(user)) == NULL) {
975N/A *error_r = t_strdup_printf("User doesn't exist: %s", user);
979N/A return -1;
975N/A }
975N/A
975N/A *uid_r = pw->pw_uid;
979N/A *gid_r = pw->pw_gid;
975N/A return 0;
975N/A}
975N/A
975N/Aint get_gid(const char *group, gid_t *gid_r, const char **error_r)
975N/A{
975N/A struct group *gr;
975N/A
975N/A if (*group == '\0') {
975N/A *gid_r = (gid_t)-1;
975N/A return 0;
975N/A }
975N/A
975N/A if ((gr = getgrnam(group)) == NULL) {
975N/A *error_r = t_strdup_printf("Group doesn't exist: %s", group);
975N/A return -1;
975N/A }
975N/A
975N/A *gid_r = gr->gr_gid;
975N/A return 0;
975N/A}
975N/A
975N/Astatic void ATTR_NORETURN ATTR_FORMAT(3, 0)
979N/Amaster_fatal_callback(enum log_type type, int status,
979N/A const char *format, va_list args)
975N/A{
975N/A const char *path, *str;
975N/A va_list args2;
975N/A int fd;
975N/A
979N/A /* if we already forked a child process, this isn't fatal for the
979N/A main process and there's no need to write the fatal file. */
975N/A if (getpid() == strtol(my_pid, NULL, 10)) {
975N/A /* write the error message to a file (we're chdired to
975N/A base dir) */
975N/A path = t_strconcat(FATAL_FILENAME, NULL);
975N/A fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
975N/A if (fd != -1) {
975N/A VA_COPY(args2, args);
975N/A str = t_strdup_vprintf(format, args2);
975N/A write_full(fd, str, strlen(str));
984N/A (void)close(fd);
975N/A }
975N/A }
975N/A
975N/A orig_fatal_callback(type, status, format, args);
975N/A abort(); /* just to silence the noreturn attribute warnings */
979N/A}
979N/A
979N/Astatic void ATTR_NORETURN
979N/Astartup_fatal_handler(enum log_type type, int status,
975N/A const char *fmt, va_list args)
975N/A{
975N/A va_list args2;
975N/A
984N/A VA_COPY(args2, args);
984N/A fprintf(stderr, "Fatal: %s\n", t_strdup_vprintf(fmt, args2));
975N/A orig_fatal_callback(type, status, fmt, args);
984N/A abort();
984N/A}
984N/A
984N/Astatic void
984N/Astartup_error_handler(enum log_type type, const char *fmt, va_list args)
984N/A{
975N/A va_list args2;
975N/A
984N/A VA_COPY(args2, args);
984N/A fprintf(stderr, "Error: %s\n", t_strdup_vprintf(fmt, args2));
984N/A orig_error_callback(type, fmt, args);
975N/A}
975N/A
975N/Astatic void fatal_log_check(const struct master_settings *set)
0N/A{
0N/A const char *path;
0N/A char buf[1024];
0N/A ssize_t ret;
299N/A int fd;
911N/A
299N/A path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL);
911N/A fd = open(path, O_RDONLY);
0N/A if (fd == -1)
0N/A return;
0N/A
0N/A ret = read(fd, buf, sizeof(buf));
0N/A if (ret < 0)
205N/A i_error("read(%s) failed: %m", path);
0N/A else {
0N/A buf[ret] = '\0';
0N/A fprintf(stderr, "Last died with error (see error log for more "
0N/A "information): %s\n", buf);
0N/A }
0N/A
0N/A close(fd);
0N/A if (unlink(path) < 0)
0N/A i_error("unlink(%s) failed: %m", path);
0N/A}
0N/A
0N/Astatic bool
0N/Aservices_has_name(const struct master_settings *set, const char *name)
0N/A{
0N/A struct service_settings *const *services;
0N/A unsigned int i, count;
0N/A
0N/A services = array_get(&set->services, &count);
0N/A for (i = 0; i < count; i++) {
0N/A if (strcmp(services[i]->name, name) == 0)
0N/A return TRUE;
0N/A }
115N/A return FALSE;
0N/A}
116N/A
0N/Astatic bool services_have_auth_destinations(const struct master_settings *set)
0N/A{
979N/A struct service_settings *const *services;
979N/A unsigned int i, count;
0N/A
0N/A services = array_get(&set->services, &count);
0N/A for (i = 0; i < count; i++) {
979N/A if (strcmp(services[i]->type, "auth-source") == 0) {
979N/A if (services_has_name(set, services[i]->auth_dest_service))
979N/A return TRUE;
0N/A }
979N/A }
0N/A return FALSE;
979N/A}
0N/A
116N/Astatic void auth_warning_print(const struct master_settings *set)
116N/A{
979N/A struct stat st;
116N/A
979N/A auth_success_written = stat(AUTH_SUCCESS_PATH, &st) == 0;
979N/A if (!auth_success_written && !set->auth_debug &&
116N/A services_have_auth_destinations(set)) {
979N/A fprintf(stderr,
979N/A"If you have trouble with authentication failures,\n"
979N/A"enable auth_debug setting. See http://wiki.dovecot.org/WhyDoesItNotWork\n"
116N/A"This message goes away after the first successful login.\n");
979N/A }
979N/A}
979N/A
979N/Astatic bool pid_file_read(const char *path, pid_t *pid_r)
979N/A{
116N/A char buf[32];
979N/A int fd;
116N/A ssize_t ret;
979N/A bool found;
116N/A
979N/A fd = open(path, O_RDONLY);
979N/A if (fd == -1) {
979N/A if (errno == ENOENT)
979N/A return FALSE;
979N/A i_fatal("open(%s) failed: %m", path);
116N/A }
979N/A
975N/A ret = read(fd, buf, sizeof(buf));
0N/A if (ret <= 0) {
863N/A if (ret == 0)
863N/A i_error("Empty PID file in %s, overriding", path);
863N/A else
863N/A i_fatal("read(%s) failed: %m", path);
901N/A found = FALSE;
901N/A } else {
863N/A if (buf[ret-1] == '\n')
863N/A ret--;
863N/A buf[ret] = '\0';
863N/A *pid_r = atoi(buf);
863N/A
863N/A found = !(*pid_r == getpid() ||
863N/A (kill(*pid_r, 0) < 0 && errno == ESRCH));
863N/A }
863N/A (void)close(fd);
863N/A return found;
863N/A}
863N/A
863N/Astatic void pid_file_check_running(const char *path)
863N/A{
863N/A pid_t pid;
863N/A
863N/A if (!pid_file_read(path, &pid))
863N/A return;
863N/A
863N/A i_fatal("Dovecot is already running with PID %s "
863N/A "(read from %s)", dec2str(pid), path);
863N/A}
863N/A
863N/Astatic void send_master_signal(int signo)
863N/A{
863N/A pid_t pid;
863N/A
863N/A if (!pid_file_read(pidfile_path, &pid)) {
863N/A i_fatal("Dovecot is not running (read from %s)", pidfile_path);
863N/A return;
863N/A }
863N/A
863N/A if (kill(pid, signo) < 0)
863N/A i_fatal("kill(%s, %d) failed: %m", dec2str(pid), signo);
863N/A exit(0);
975N/A}
975N/A
975N/Astatic void create_pid_file(const char *path)
975N/A{
975N/A const char *pid;
975N/A int fd;
975N/A
979N/A pid = t_strconcat(dec2str(getpid()), "\n", NULL);
975N/A
975N/A fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
975N/A if (fd == -1)
975N/A i_fatal("open(%s) failed: %m", path);
975N/A if (write_full(fd, pid, strlen(pid)) < 0)
975N/A i_fatal("write() failed in %s: %m", path);
975N/A (void)close(fd);
975N/A}
975N/A
975N/Astatic void
975N/Asig_settings_reload(const siginfo_t *si ATTR_UNUSED,
975N/A void *context ATTR_UNUSED)
975N/A{
975N/A struct master_service_settings_input input;
975N/A const struct master_settings *set;
975N/A void **sets;
975N/A struct service_list *new_services;
975N/A const char *error;
975N/A
975N/A i_warning("SIGHUP received - reloading configuration");
975N/A
975N/A /* see if hostname changed */
975N/A hostpid_init();
975N/A
975N/A memset(&input, 0, sizeof(input));
975N/A input.roots = set_roots;
975N/A input.module = MASTER_SERVICE_NAME;
975N/A input.config_path = services_get_config_socket_path(services);
975N/A if (master_service_settings_read(master_service, &input, &error) < 0) {
975N/A i_error("Error reading configuration: %s", error);
975N/A return;
975N/A }
975N/A sets = master_service_settings_get_others(master_service);
975N/A set = sets[0];
975N/A
975N/A if (services_create(set, child_process_env,
975N/A &new_services, &error) < 0) {
975N/A /* new configuration is invalid, keep the old */
975N/A i_error("Config reload failed: %s", error);
975N/A return;
975N/A }
975N/A new_services->config->config_file_path =
975N/A p_strdup(new_services->pool,
975N/A services->config->config_file_path);
975N/A
975N/A /* switch to new configuration. */
975N/A (void)services_listen_using(new_services, services);
975N/A services_destroy(services);
975N/A
975N/A services = new_services;
975N/A services_monitor_start(services);
975N/A}
975N/A
975N/Astatic void
975N/Asig_log_reopen(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
975N/A{
979N/A service_signal(services->log, SIGUSR1);
979N/A
979N/A master_service_init_log(master_service, "master: ");
979N/A i_set_fatal_handler(master_fatal_callback);
979N/A}
979N/A
979N/Astatic void
979N/Asig_reap_children(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
979N/A{
979N/A services_monitor_reap_children();
979N/A}
979N/A
979N/Astatic void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
979N/A{
979N/A i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
979N/A si->si_signo, dec2str(si->si_pid),
979N/A dec2str(si->si_uid),
979N/A lib_signal_code_to_str(si->si_signo, si->si_code));
979N/A master_service_stop(master_service);
979N/A}
979N/A
295N/Astatic void main_log_startup(void)
295N/A{
295N/A#define STARTUP_STRING PACKAGE_NAME" v"VERSION" starting up"
295N/A rlim_t core_limit;
295N/A
295N/A core_dumps_disabled = restrict_get_core_limit(&core_limit) == 0 &&
295N/A core_limit == 0;
341N/A if (core_dumps_disabled)
295N/A i_info(STARTUP_STRING" (core dumps disabled)");
295N/A else
295N/A i_info(STARTUP_STRING);
295N/A}
295N/A
295N/Astatic void main_init(bool log_error)
295N/A{
295N/A drop_capabilities();
295N/A
295N/A /* deny file access from everyone else except owner */
295N/A (void)umask(0077);
295N/A
295N/A if (log_error) {
295N/A fprintf(stderr, "Writing to error logs and killing myself..\n");
295N/A i_debug("This is Dovecot's debug log");
295N/A i_info("This is Dovecot's info log");
341N/A i_warning("This is Dovecot's warning log");
341N/A i_error("This is Dovecot's error log");
341N/A i_fatal("This is Dovecot's fatal log");
341N/A }
341N/A main_log_startup();
341N/A
341N/A lib_signals_init();
341N/A lib_signals_ignore(SIGPIPE, TRUE);
341N/A lib_signals_ignore(SIGALRM, FALSE);
301N/A lib_signals_set_handler(SIGHUP, TRUE, sig_settings_reload, NULL);
301N/A lib_signals_set_handler(SIGUSR1, TRUE, sig_log_reopen, NULL);
301N/A lib_signals_set_handler(SIGCHLD, TRUE, sig_reap_children, NULL);
301N/A lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
301N/A lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
336N/A
301N/A create_pid_file(pidfile_path);
301N/A
301N/A services_monitor_start(services);
301N/A}
301N/A
301N/Astatic void main_deinit(void)
301N/A{
301N/A if (unlink(pidfile_path) < 0)
301N/A i_error("unlink(%s) failed: %m", pidfile_path);
301N/A i_free(pidfile_path);
301N/A
301N/A services_destroy(services);
301N/A service_pids_deinit();
301N/A}
301N/A
301N/Astatic const char *get_full_config_path(struct service_list *list)
301N/A{
301N/A const char *path;
301N/A char cwd[PATH_MAX];
301N/A
301N/A path = master_service_get_config_path(master_service);
301N/A if (*path == '/')
301N/A return path;
301N/A
301N/A if (getcwd(cwd, sizeof(cwd)) == NULL)
301N/A i_fatal("getcwd() failed: %m");
301N/A return p_strconcat(list->pool, cwd, "/", path, NULL);
301N/A}
301N/A
301N/Astatic void master_time_moved(time_t old_time, time_t new_time)
301N/A{
301N/A unsigned long secs;
301N/A
301N/A if (new_time >= old_time)
301N/A return;
301N/A
301N/A /* time moved backwards. disable launching new service processes
301N/A until */
301N/A secs = old_time - new_time + 1;
301N/A if (secs > SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS)
301N/A secs = SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS;
301N/A services_throttle_time_sensitives(services, secs);
301N/A i_warning("Time moved backwards by %lu seconds, "
576N/A "waiting for %lu secs until new services are launched again.",
576N/A (unsigned long)(old_time - new_time), secs);
576N/A}
576N/A
576N/Astatic void daemonize(void)
576N/A{
576N/A pid_t pid;
576N/A
379N/A pid = fork();
379N/A if (pid < 0)
379N/A i_fatal("fork() failed: %m");
379N/A
379N/A if (pid != 0)
379N/A _exit(0);
379N/A
379N/A if (setsid() < 0)
975N/A i_fatal("setsid() failed: %m");
398N/A
379N/A /* update my_pid */
379N/A hostpid_init();
379N/A}
975N/A
975N/Astatic void print_help(void)
379N/A{
379N/A fprintf(stderr,
379N/A"Usage: dovecot [-F] [-c <config file>] [-p] [-n] [-a]\n"
379N/A" [-cb <config binary path>] [--help] [--version]\n"
975N/A" [--build-options] [--log-error] [reload] [stop]\n");
975N/A}
379N/A
379N/Astatic void print_build_options(void)
379N/A{
379N/A printf("Build options:"
379N/A#ifdef IOLOOP_EPOLL
411N/A " ioloop=epoll"
379N/A#endif
398N/A#ifdef IOLOOP_KQUEUE
398N/A " ioloop=kqueue"
398N/A#endif
398N/A#ifdef IOLOOP_POLL
379N/A " ioloop=poll"
379N/A#endif
379N/A#ifdef IOLOOP_SELECT
411N/A " ioloop=select"
997N/A#endif
411N/A#ifdef IOLOOP_NOTIFY_DNOTIFY
411N/A " notify=dnotify"
411N/A#endif
411N/A#ifdef IOLOOP_NOTIFY_INOTIFY
411N/A " notify=inotify"
411N/A#endif
411N/A#ifdef IOLOOP_NOTIFY_KQUEUE
411N/A " notify=kqueue"
975N/A#endif
975N/A#ifdef HAVE_IPV6
411N/A " ipv6"
411N/A#endif
411N/A#ifdef HAVE_GNUTLS
411N/A " gnutls"
975N/A#endif
411N/A#ifdef HAVE_OPENSSL
411N/A " openssl"
411N/A#endif
411N/A "\nMail storages: "MAIL_STORAGES"\n"
411N/A#ifdef SQL_DRIVER_PLUGINS
411N/A "SQL driver plugins:"
411N/A#else
411N/A "SQL drivers:"
411N/A#endif
411N/A#ifdef BUILD_MYSQL
411N/A " mysql"
411N/A#endif
411N/A#ifdef BUILD_PGSQL
411N/A " postgresql"
411N/A#endif
411N/A#ifdef BUILD_SQLITE
975N/A " sqlite"
411N/A#endif
997N/A "\nPassdb:"
997N/A#ifdef PASSDB_BSDAUTH
997N/A " bsdauth"
997N/A#endif
997N/A#ifdef PASSDB_CHECKPASSWORD
997N/A " checkpassword"
997N/A#endif
997N/A#ifdef PASSDB_LDAP
997N/A " ldap"
624N/A#endif
624N/A#ifdef PASSDB_PAM
624N/A " pam"
624N/A#endif
624N/A#ifdef PASSDB_PASSWD
624N/A " passwd"
624N/A#endif
624N/A#ifdef PASSDB_PASSWD_FILE
624N/A " passwd-file"
624N/A#endif
624N/A#ifdef PASSDB_SHADOW
624N/A " shadow"
624N/A#endif
624N/A#ifdef PASSDB_SQL
624N/A " sql"
624N/A#endif
624N/A#ifdef PASSDB_VPOPMAIL
624N/A " vpopmail"
624N/A#endif
624N/A "\nUserdb:"
624N/A#ifdef USERDB_CHECKPASSWORD
624N/A " checkpassword"
624N/A#endif
0N/A#ifdef USERDB_LDAP
0N/A " ldap"
0N/A#ifndef BUILTIN_LDAP
0N/A "(plugin)"
0N/A#endif
13N/A#endif
136N/A#ifdef USERDB_NSS
975N/A " nss"
#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[])
{
struct master_settings *set;
unsigned int child_process_env_idx = 0;
const char *getopt_str, *error, *env_tz, *doveconf_arg = NULL;
failure_callback_t *orig_info_callback, *orig_debug_callback;
void **sets;
bool foreground = FALSE, ask_key_pass = FALSE, log_error = FALSE;
int c, send_signal = 0;
#ifdef DEBUG
if (getenv("GDB") == NULL)
fd_debug_verify_leaks(3, 1024);
else
child_process_env[child_process_env_idx++] = "GDB=1";
#endif
master_service = master_service_init(MASTER_SERVICE_NAME,
MASTER_SERVICE_FLAG_STANDALONE |
MASTER_SERVICE_FLAG_DONT_LOG_TO_STDERR,
argc, argv);
i_set_failure_prefix("");
io_loop_set_time_moved_callback(current_ioloop, master_time_moved);
master_uid = geteuid();
master_gid = getegid();
getopt_str = t_strconcat("Fanp-", master_service_getopt_string(), NULL);
while ((c = getopt(argc, argv, getopt_str)) > 0) {
if (c == '-')
break;
switch (c) {
case 'F':
foreground = TRUE;
break;
case 'a':
doveconf_arg = "-a";
break;
case 'n':
doveconf_arg = "-n";
break;
case 'p':
/* Ask SSL private key password */
ask_key_pass = TRUE;
break;
default:
if (!master_service_parse_option(master_service,
c, optarg)) {
print_help();
exit(FATAL_DEFAULT);
}
break;
}
}
if (doveconf_arg != NULL) {
const char **args;
args = t_new(const char *, 5);
args[0] = DOVECOT_CONFIG_BIN_PATH;
args[1] = doveconf_arg;
args[2] = "-c";
args[3] = master_service_get_config_path(master_service);
args[4] = NULL;
execv(args[0], (char **)args);
i_fatal("execv(%s) failed: %m", args[0]);
}
while (optind < argc) {
if (strcmp(argv[optind], "--version") == 0) {
printf("%s\n", VERSION);
return 0;
} else if (strcmp(argv[optind], "--build-options") == 0) {
print_build_options();
return 0;
} else if (strcmp(argv[optind], "--log-error") == 0) {
log_error = TRUE;
foreground = TRUE;
} else if (strcmp(argv[optind], "--help") == 0) {
print_help();
return 0;
} else if (strcmp(argv[optind], "reload") == 0) {
send_signal = SIGHUP;
} else if (strcmp(argv[optind], "stop") == 0) {
send_signal = SIGTERM;
} else {
print_help();
i_fatal("Unknown argument: %s", argv[optind]);
}
optind++;
}
do {
null_fd = open("/dev/null", O_WRONLY);
if (null_fd == -1)
i_fatal("Can't open /dev/null: %m");
fd_close_on_exec(null_fd, TRUE);
} while (null_fd <= STDERR_FILENO);
if (master_service_settings_read_simple(master_service, set_roots,
&error) < 0)
i_fatal("Error reading configuration: %s", error);
sets = master_service_settings_get_others(master_service);
set = sets[0];
if (ask_key_pass) {
askpass("Give the password for SSL keys: ",
ssl_manual_key_password,
sizeof(ssl_manual_key_password));
}
if (dup2(null_fd, STDIN_FILENO) < 0 ||
dup2(null_fd, STDOUT_FILENO) < 0)
i_fatal("dup2(null_fd) failed: %m");
pidfile_path =
i_strconcat(set->base_dir, "/"MASTER_PID_FILE_NAME, NULL);
if (send_signal != 0)
send_master_signal(send_signal);
master_service_init_log(master_service, "master: ");
i_get_failure_handlers(&orig_fatal_callback, &orig_error_callback,
&orig_info_callback, &orig_debug_callback);
i_set_fatal_handler(startup_fatal_handler);
i_set_error_handler(startup_error_handler);
if (!log_error) {
pid_file_check_running(pidfile_path);
master_settings_do_fixes(set);
fatal_log_check(set);
auth_warning_print(set);
}
/* save TZ environment. AIX depends on it to get the timezone
correctly. */
env_tz = getenv("TZ");
/* clean up the environment of everything */
env_clean();
/* put back the TZ */
if (env_tz != NULL) {
const char *env = t_strconcat("TZ=", env_tz, NULL);
env_put(env);
child_process_env[child_process_env_idx++] = env;
}
i_assert(child_process_env_idx <
sizeof(child_process_env) / sizeof(child_process_env[0]));
child_process_env[child_process_env_idx++] = NULL;
/* create service structures from settings. if there are any errors in
service configuration we'll catch it here. */
service_pids_init();
if (services_create(set, child_process_env, &services, &error) < 0)
i_fatal("%s", error);
services->config->config_file_path = get_full_config_path(services);
if (!log_error) {
/* if any listening fails, fail completely */
if (services_listen(services) <= 0)
i_fatal("Failed to start listeners");
if (!foreground)
daemonize();
if (chdir(set->base_dir) < 0)
i_fatal("chdir(%s) failed: %m", set->base_dir);
}
i_set_fatal_handler(master_fatal_callback);
i_set_error_handler(orig_error_callback);
main_init(log_error);
master_service_run(master_service, NULL);
main_deinit();
master_service_deinit(&master_service);
return 0;
}