main.c revision 52bc5d390f066c7cced1e20311ffe2f7d19638dc
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher/* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher#define FATAL_FILENAME "master-fatal.lastlog"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan#define SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS (60*3)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic fatal_failure_callback_t *orig_fatal_callback;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic failure_callback_t *orig_error_callback;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic const char *child_process_env[3]; /* @UNSAFE */
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagherstatic const struct setting_parser_info *set_roots[] = {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallaghervoid process_exec(const char *cmd, const char *extra_args[])
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan new_argv = t_new(const char *, count1 + count2 + 1);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher memcpy(new_argv, argv, sizeof(const char *) * count1);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan sizeof(const char *) * count2);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan /* hide the path, it's ugly */
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher /* prefix with dovecot/ */
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher argv[0] = t_strconcat(PACKAGE"/", argv[0], NULL);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_fatal_status(errno == ENOMEM ? FATAL_OUTOFMEM : FATAL_EXEC,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanint get_uidgid(const char *user, uid_t *uid_r, gid_t *gid_r,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher *error_r = t_strdup_printf("User doesn't exist: %s", user);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanint get_gid(const char *group, gid_t *gid_r, const char **error_r)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher *error_r = t_strdup_printf("Group doesn't exist: %s", group);
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagherstatic void ATTR_NORETURN ATTR_FORMAT(3, 0)
292cbb3fbe41bb7ee09b67c3ec59ab7c7ba5220eStephen Gallaghermaster_fatal_callback(enum log_type type, int status,
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* if we already forked a child process, this isn't fatal for the
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan main process and there's no need to write the fatal file. */
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher /* write the error message to a file (we're chdired to
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan orig_fatal_callback(type, status, format, args);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan abort(); /* just to silence the noreturn attribute warnings */
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivanstartup_fatal_handler(enum log_type type, int status,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan fprintf(stderr, "Fatal: %s\n", t_strdup_vprintf(fmt, args2));
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan orig_fatal_callback(type, status, fmt, args);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstartup_error_handler(enum log_type type, const char *fmt, va_list args)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan fprintf(stderr, "Error: %s\n", t_strdup_vprintf(fmt, args2));
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagherstatic void fatal_log_check(const struct master_settings *set)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan path = t_strconcat(set->base_dir, "/"FATAL_FILENAME, NULL);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan fprintf(stderr, "Last died with error (see error log for more "
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanservices_has_name(const struct master_settings *set, const char *name)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher unsigned int i, count;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan services = array_get(&set->services, &count);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan for (i = 0; i < count; i++) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic bool services_have_auth_destinations(const struct master_settings *set)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan unsigned int i, count;
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan services = array_get(&set->services, &count);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan for (i = 0; i < count; i++) {
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (strcmp(services[i]->type, "auth-source") == 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (services_has_name(set, services[i]->auth_dest_service))
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic void auth_warning_print(const struct master_settings *set)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan auth_success_written = stat(AUTH_SUCCESS_PATH, &st) == 0;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (!auth_success_written && !set->auth_debug &&
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher"If you have trouble with authentication failures,\n"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan"enable auth_debug setting. See http://wiki.dovecot.org/WhyDoesItNotWork\n"
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher"This message goes away after the first successful login.\n");
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic bool pid_file_read(const char *path, pid_t *pid_r)
056302a92862fda16351d7192600746746f38e5dStephen Gallagher i_error("Empty PID file in %s, overriding", path);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic void pid_file_check_running(const char *path)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_fatal("Dovecot is already running with PID %s "
056302a92862fda16351d7192600746746f38e5dStephen Gallagher i_fatal("Dovecot is not running (read from %s)", pidfile_path);
056302a92862fda16351d7192600746746f38e5dStephen Gallagher i_fatal("kill(%s, %d) failed: %m", dec2str(pid), signo);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagherstatic void create_pid_file(const char *path)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan pid = t_strconcat(dec2str(getpid()), "\n", NULL);
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644);
056302a92862fda16351d7192600746746f38e5dStephen Gallagher i_fatal("write() failed in %s: %m", path);
056302a92862fda16351d7192600746746f38e5dStephen Gallaghersig_settings_reload(const siginfo_t *si ATTR_UNUSED,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_warning("SIGHUP received - reloading configuration");
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* see if hostname changed */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan input.config_path = services_get_config_socket_path(services);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (master_service_settings_read(master_service, &input, &error) < 0) {
056302a92862fda16351d7192600746746f38e5dStephen Gallagher i_error("Error reading configuration: %s", error);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan sets = master_service_settings_get_others(master_service);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* new configuration is invalid, keep the old */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* switch to new configuration. */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan (void)services_listen_using(new_services, services);
056302a92862fda16351d7192600746746f38e5dStephen Gallaghersig_log_reopen(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan master_service_init_log(master_service, "master: ");
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivansig_reap_children(const siginfo_t *si ATTR_UNUSED, void *context ATTR_UNUSED)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagherstatic void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_warning("Killed with signal %d (by pid=%s uid=%s code=%s)",
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher lib_signal_code_to_str(si->si_signo, si->si_code));
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic void main_log_startup(void)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan#define STARTUP_STRING PACKAGE_NAME" v"VERSION" starting up"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan core_dumps_disabled = restrict_get_core_limit(&core_limit) == 0 &&
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher i_info(STARTUP_STRING" (core dumps disabled)");
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan /* deny file access from everyone else except owner */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan fprintf(stderr, "Writing to error logs and killing myself..\n");
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher i_warning("This is Dovecot's warning log");
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan lib_signals_set_handler(SIGHUP, TRUE, sig_settings_reload, NULL);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan lib_signals_set_handler(SIGUSR1, TRUE, sig_log_reopen, NULL);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan lib_signals_set_handler(SIGCHLD, TRUE, sig_reap_children, NULL);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivanstatic void main_deinit(void)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher i_error("unlink(%s) failed: %m", pidfile_path);
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivanstatic const char *get_full_config_path(struct service_list *list)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan path = master_service_get_config_path(master_service);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan return p_strconcat(list->pool, cwd, "/", path, NULL);
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagherstatic void master_time_moved(time_t old_time, time_t new_time)
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher unsigned long secs;
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher /* time moved backwards. disable launching new service processes
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher if (secs > SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS)
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher secs = SERVICE_TIME_MOVED_BACKWARDS_MAX_THROTTLE_SECS;
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher services_throttle_time_sensitives(services, secs);
056302a92862fda16351d7192600746746f38e5dStephen Gallagher i_warning("Time moved backwards by %lu seconds, "
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher "waiting for %lu secs until new services are launched again.",
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher (unsigned long)(old_time - new_time), secs);
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagherstatic void daemonize(void)
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* update my_pid */
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivanstatic void print_help(void)
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher"Usage: dovecot [-F] [-c <config file>] [-p] [-n] [-a]\n"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan" [-cb <config binary path>] [--help] [--version]\n"
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan" [--build-options] [--log-error] [reload] [stop]\n");
056302a92862fda16351d7192600746746f38e5dStephen Gallagher " ioloop=epoll"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan " ioloop=kqueue"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan " ioloop=poll"
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher " ioloop=select"
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan " notify=dnotify"
c938f4ba417328fe62eded0806b2d9ca053f34a5Stephen Gallagher " notify=inotify"
056302a92862fda16351d7192600746746f38e5dStephen Gallagher " notify=kqueue"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan "\nSQL driver plugins:"
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher "\nSQL drivers:"
52261fe16203dec6e6f69177c6d0a810b47d073fStephen Gallagher " checkpassword"
056302a92862fda16351d7192600746746f38e5dStephen Gallagher " passwd-file"
056302a92862fda16351d7192600746746f38e5dStephen Gallagher " checkpassword"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan " passwd-file"
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan unsigned int child_process_env_idx = 0;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan const char *getopt_str, *error, *env_tz, *doveconf_arg = NULL;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher failure_callback_t *orig_info_callback, *orig_debug_callback;
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan bool foreground = FALSE, ask_key_pass = FALSE, log_error = FALSE;
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher child_process_env[child_process_env_idx++] = "GDB=1";
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan master_service = master_service_init(MASTER_SERVICE_NAME,
f9fdc87c80f2744780c6a0f2bf5b1b57bcbb095aYuri Chornoivan io_loop_set_time_moved_callback(current_ioloop, master_time_moved);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan getopt_str = t_strconcat("Fanp-", master_service_getopt_string(), NULL);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan while ((c = getopt(argc, argv, getopt_str)) > 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan if (c == '-')
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan /* Ask SSL private key password */
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher if (!master_service_parse_option(master_service,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan const char **args;
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher args[3] = master_service_get_config_path(master_service);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (strcmp(argv[optind], "--version") == 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan } else if (strcmp(argv[optind], "--build-options") == 0) {
056302a92862fda16351d7192600746746f38e5dStephen Gallagher } else if (strcmp(argv[optind], "--log-error") == 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan } else if (strcmp(argv[optind], "--help") == 0) {
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher } else if (strcmp(argv[optind], "reload") == 0) {
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher } else if (strcmp(argv[optind], "stop") == 0) {
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_fatal("Unknown argument: %s", argv[optind]);
cbe7c54c2caf718bdea7ca6660ba8193d759d2d5Stephen Gallagher if (master_service_settings_read_simple(master_service, set_roots,
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_fatal("Error reading configuration: %s", error);
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan sets = master_service_settings_get_others(master_service);
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher askpass("Give the password for SSL keys: ",
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_strconcat(set->base_dir, "/"MASTER_PID_FILE_NAME, NULL);
056302a92862fda16351d7192600746746f38e5dStephen Gallagher master_service_init_log(master_service, "master: ");
bde69429374859acff41273c0771d2b5f5c199b1Yuri Chornoivan i_get_failure_handlers(&orig_fatal_callback, &orig_error_callback,
056302a92862fda16351d7192600746746f38e5dStephen Gallagher /* save TZ environment. AIX depends on it to get the timezone
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher /* clean up the environment of everything */
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher /* put back the TZ */
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher const char *env = t_strconcat("TZ=", env_tz, NULL);
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher child_process_env[child_process_env_idx++] = env;
056302a92862fda16351d7192600746746f38e5dStephen Gallagher sizeof(child_process_env) / sizeof(child_process_env[0]));
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher child_process_env[child_process_env_idx++] = NULL;
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher /* create service structures from settings. if there are any errors in
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher service configuration we'll catch it here. */
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher if (services_create(set, child_process_env, &services, &error) < 0)
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher services->config->config_file_path = get_full_config_path(services);
b355dcb54194f498921743ca33304eac35d89718Stephen Gallagher /* if any listening fails, fail completely */
9643e7da1a54a9edb2360ab8f855664a8b4397caStephen Gallagher i_fatal("chdir(%s) failed: %m", set->base_dir);