Mercurial > dovecot > original-hg > dovecot-1.2
view src/master/main.c @ 9658:8ba4253adc9b HEAD tip
*-login: SSL connections didn't get closed when the client got destroyed.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 08 May 2014 16:41:29 +0300 |
parents | 00cd9aacd03c |
children |
line wrap: on
line source
/* Copyright (c) 2002-2010 Dovecot authors, see the included COPYING file */ #include "common.h" #include "array.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 "restrict-process-size.h" #include "askpass.h" #include "auth-process.h" #include "capabilities.h" #include "dict-process.h" #include "login-process.h" #include "mail-process.h" #include "syslog-util.h" #include "listener.h" #include "ssl-init.h" #include "log.h" #include "sysinfo-get.h" #include "hostpid.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <syslog.h> #include <sys/stat.h> #define FATAL_FILENAME "master-fatal.lastlog" static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf"; static pid_t master_original_pid; struct ioloop *ioloop; int null_fd = -1, inetd_login_fd; uid_t master_uid; char program_path[PATH_MAX]; char ssl_manual_key_password[100]; const char *env_tz; bool auth_success_written; bool core_dumps_disabled; #ifdef DEBUG bool gdb; #endif static void ATTR_NORETURN ATTR_FORMAT(3, 0) master_fatal_callback(enum log_type type, int status, const char *format, va_list args) { const struct settings *set = settings_root->defaults; const char *path, *str; va_list args2; int fd; /* if we already forked a child process, this isn't fatal for the main process and there's no need to write the fatal file. */ if (getpid() == master_original_pid) { /* write the error message to a file */ path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL); fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600); if (fd != -1) { VA_COPY(args2, args); str = t_strdup_vprintf(format, args2); write_full(fd, str, strlen(str)); (void)close(fd); } } /* write it to log as well */ if (*set->log_path == '\0') i_syslog_fatal_handler(type, status, format, args); else default_fatal_handler(type, status, format, args); } static void fatal_log_check(void) { const struct settings *set = settings_root->defaults; const char *path; char buf[1024]; ssize_t ret; int fd; path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL); fd = open(path, O_RDONLY); if (fd == -1) return; ret = read(fd, buf, sizeof(buf)); if (ret < 0) i_error("read(%s) failed: %m", path); else { buf[ret] = '\0'; fprintf(stderr, "Last died with error (see error log for more " "information): %s\n", buf); } close(fd); if (unlink(path) < 0) i_error("unlink(%s) failed: %m", path); } static void auth_warning_print(const struct server_settings *set) { struct stat st; auth_success_written = stat(AUTH_SUCCESS_PATH, &st) == 0; if (!auth_success_written && !set->auths->debug && strcmp(set->defaults->protocols, "none") != 0) { fprintf(stderr, "If you have trouble with authentication failures,\n" "enable auth_debug setting. See http://wiki.dovecot.org/WhyDoesItNotWork\n" "This message goes away after the first successful login.\n"); } } 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: "); } i_set_fatal_handler(master_fatal_callback); if (*set->info_log_path != '\0') i_set_info_file(set->info_log_path); i_set_failure_timestamp_format(set->log_timestamp); } static void ATTR_NORETURN tee_fatal_handler(enum log_type type, int status, const char *fmt, va_list args) { const struct settings *set = settings_root->defaults; va_list args2; VA_COPY(args2, args); fprintf(stderr, "Fatal: %s\n", t_strdup_vprintf(fmt, args2)); if (*set->log_path == '\0') { i_syslog_fatal_handler(type, status, fmt, args); } else { default_fatal_handler(type, status, fmt, args); } } static void tee_error_handler(enum log_type type, const char *fmt, va_list args) { const struct settings *set = settings_root->defaults; va_list args2; VA_COPY(args2, args); fprintf(stderr, "Error: %s\n", t_strdup_vprintf(fmt, args2)); if (*set->log_path == '\0') { i_syslog_error_handler(type, fmt, args); } else { default_error_handler(type, fmt, args); } } static void set_tee_logfile(struct settings *set) { set_logfile(set); i_set_fatal_handler(tee_fatal_handler); i_set_error_handler(tee_error_handler); } 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_processes_kill(); /* see if hostname changed */ hostpid_init(); if (!master_settings_read(configfile, FALSE, FALSE, FALSE)) i_warning("Invalid configuration, keeping old one"); else { if (!IS_INETD()) listeners_open_fds(old_set, TRUE); set_logfile(settings_root->defaults); } } static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED) { /* warn about being killed because of some signal, except SIGINT (^C) which is too common at least while testing :) */ if (si->si_signo != SIGINT) { i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)", si->si_signo, dec2str(si->si_pid), dec2str(si->si_uid), lib_signal_code_to_str(si->si_signo, si->si_code)); } io_loop_stop(ioloop); } static void sig_reload_settings(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { settings_reload(); } static void sig_reopen_logs(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED) { set_logfile(settings_root->defaults); } 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_std_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); } /* 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_log_startup(void) { #define STARTUP_STRING PACKAGE_NAME" v"VERSION" starting up" rlim_t core_limit; core_dumps_disabled = restrict_get_core_limit(&core_limit) == 0 && core_limit == 0; if (core_dumps_disabled) i_info(STARTUP_STRING" (core dumps disabled)"); else i_info(STARTUP_STRING); } static void main_init(bool log_error) { const char *base_config_path; drop_capabilities(); /* deny file access from everyone else except owner */ (void)umask(0077); set_logfile(settings_root->defaults); /* close stderr unless we're logging into /dev/stderr. */ if (!have_stderr(settings_root)) { if (dup2(null_fd, 2) < 0) i_fatal("dup2(2) failed: %m"); } if (log_error) { printf("Writing to error logs and killing myself..\n"); i_info("This is Dovecot's info log"); i_warning("This is Dovecot's warning log"); i_error("This is Dovecot's error log"); i_fatal("This is Dovecot's fatal log"); } main_log_startup(); 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); child_processes_init(); log_init(); ssl_init(); dict_processes_init(); auth_processes_init(); login_processes_init(); mail_processes_init(); create_pid_file(t_strconcat(settings_root->defaults->base_dir, "/master.pid", NULL)); base_config_path = t_strconcat(settings_root->defaults->base_dir, "/"PACKAGE".conf", NULL); (void)unlink(base_config_path); if (symlink(configfile, base_config_path) < 0) { i_error("symlink(%s, %s) failed: %m", configfile, base_config_path); } } static void main_deinit(void) { (void)unlink(t_strconcat(settings_root->defaults->base_dir, "/master.pid", NULL)); login_processes_destroy_all(); mail_processes_deinit(); login_processes_deinit(); auth_processes_deinit(); dict_processes_deinit(); ssl_deinit(); listeners_close_fds(); if (close(null_fd) < 0) i_error("close(null_fd) failed: %m"); log_deinit(); /* log_deinit() may still want to look up child processes */ child_processes_deinit(); lib_signals_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" " [--version] [--build-options] [--exec-mail <protocol> [<args>]]\n"); } static void print_build_options(void) { static const char *build_options = "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 "\nMail storages: "MAIL_STORAGES"\n" #ifdef SQL_DRIVER_PLUGINS "SQL driver plugins:" #else "SQL drivers:" #endif #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_SIA " sia" #endif #ifdef PASSDB_SQL " sql" #endif #ifdef PASSDB_VPOPMAIL " vpopmail" #endif "\nUserdb:" #ifdef USERDB_NSS " nss" #endif #ifdef USERDB_LDAP " ldap" #ifndef BUILTIN_LDAP "(plugin)" #endif #endif #ifdef USERDB_PASSWD " passwd" #endif #ifdef USERDB_PASSWD_FILE " passwd-file" #endif #ifdef USERDB_PREFETCH " prefetch" #endif #ifdef USERDB_SQL " sql" #endif #ifdef USERDB_STATIC " static" #endif #ifdef USERDB_VPOPMAIL " vpopmail" #endif "\n"; puts(build_options); } int main(int argc, char *argv[]) { /* parse arguments */ const char *exec_protocol = NULL, **exec_args = NULL, *user, *home; bool foreground = FALSE, ask_key_pass = FALSE, log_error = FALSE; bool dump_config = FALSE, dump_config_nondefaults = FALSE; bool config_path_given = 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]; config_path_given = TRUE; } 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> [<args>] read configuration and execute mail process */ i++; if (i == argc) i_fatal("Missing protocol argument"); exec_protocol = argv[i]; exec_args = (const char **)&argv[i+1]; break; } 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[i]); } } /* 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_BEGIN { master_settings_init(); if (!master_settings_read(configfile, exec_protocol != NULL, dump_config || log_error, !config_path_given && dump_config)) i_fatal("Invalid configuration in %s", configfile); } T_END; if (dump_config) { const char *info; info = sysinfo_get(settings_root->defaults->mail_location); if (*info != '\0') printf("# %s\n", info); master_settings_dump(settings_root, dump_config_nondefaults); return 0; } if (ask_key_pass) T_BEGIN { const char *prompt; 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_END; /* 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_args); } /* closes stdin/stdout, must be after --exec-mail handling */ open_std_fds(); /* log all errors to both stderr and log file until we've finished startup. */ set_tee_logfile(settings_root->defaults); fatal_log_check(); auth_warning_print(settings_root); if (!log_error && !IS_INETD()) T_BEGIN { listeners_open_fds(NULL, FALSE); } T_END; if (!foreground) daemonize(settings_root->defaults); master_original_pid = getpid(); 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; }