/* Copyright (c) 2005-2010 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "lib-signals.h"
#include "ioloop.h"
#include "fd-close-on-exec.h"
#include "restrict-access.h"
#include "randgen.h"
#include "sql-api.h"
#include "dict.h"
#include "dict-client.h"
#include "dict-server.h"
#include "module-dir.h"

#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>

#define DICT_MASTER_LISTENER_FD 3

static struct ioloop *ioloop;
static struct io *log_io;
static struct module *modules;
static struct dict_server *dict_server;

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 log_error_callback(void *context ATTR_UNUSED)
{
	/* the log fd is closed, don't die when trying to log later */
	i_set_failure_ignore_errors(TRUE);

	io_loop_stop(ioloop);
}

static void drop_privileges(void)
{
	/* Log file or syslog opening probably requires roots */
	i_set_failure_internal();

	/* Maybe needed. Have to open /dev/urandom before possible
	   chrooting. */
	random_init();

	/* Load built-in SQL drivers (if any) */
	sql_drivers_init();
	sql_drivers_register_all();

	restrict_access_by_env(FALSE);
}

static void main_init(void)
{
	const char *version, *path;
	int fd;

	version = getenv("DOVECOT_VERSION");
	if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) {
		i_fatal("Dovecot version mismatch: "
			"Master is v%s, dict is v"PACKAGE_VERSION" "
			"(if you don't care, set version_ignore=yes)", version);
	}

	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);

	/* If master dies, the log fd gets closed and we'll quit */
	log_io = io_add(STDERR_FILENO, IO_ERROR, log_error_callback, NULL);

	modules = module_dir_load(DICT_MODULE_DIR, NULL, TRUE, version);
	module_dir_init(modules);

	/* Register only after loading modules. They may contain SQL drivers,
	   which we'll need to register. */
	dict_drivers_register_all();

	path = getenv("DICT_LISTEN_FROM_FD");
	fd = path == NULL ? -1 : DICT_MASTER_LISTENER_FD;
	if (path == NULL) {
		path = t_strconcat(getenv("BASE_DIR"),
				   DEFAULT_DICT_SERVER_SOCKET_FNAME, NULL);
	}

	dict_server = dict_server_init(path, fd);
}

static void main_deinit(void)
{
	io_remove(&log_io);
	dict_server_deinit(dict_server);

	module_dir_unload(&modules);

	dict_drivers_unregister_all();

	sql_drivers_deinit();
	random_deinit();
	lib_signals_deinit();
	closelog();
}

int main(void)
{
#ifdef DEBUG
	if (getenv("GDB") == NULL)
		fd_debug_verify_leaks(DICT_MASTER_LISTENER_FD+1, 1024);
#endif

	/* NOTE: we start rooted, so keep the code minimal until
	   restrict_access_by_env() is called */
	lib_init();
	drop_privileges();

	ioloop = io_loop_create();

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

	io_loop_destroy(&ioloop);
	lib_deinit();

	return 0;
}
