/* Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* $Id: imapd.c 1712 2005-03-26 20:23:18Z aaron $ * * imapd.c * * main prg for imap daemon */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "dm_getopt.h" #include "imap4.h" #include "server.h" #include "debug.h" #include "misc.h" #include "pidfile.h" #include "dbmail.h" #include "pool.h" #ifdef PROC_TITLES #include "proctitleutils.h" #endif #define PNAME "dbmail/imap4d" char *pidFile = DEFAULT_PID_DIR "dbmail-imapd" DEFAULT_PID_EXT; char *configFile = DEFAULT_CONFIG_FILE; /* set up database login data */ extern db_param_t _db_params; static void SetConfigItems(serverConfig_t * config); static void Daemonize(void); static int SetMainSigHandler(void); static void MainSigHandler(int sig, siginfo_t * info, void *data); int imap_before_smtp = 0; int mainRestart = 0; int mainStop = 0; int do_showhelp(void) { printf("*** dbmail-imapd ***\n"); printf("This daemon provides Internet Message Access Protocol v4.1 services.\n"); printf("See the man page for more info.\n"); printf("\nCommon options for all DBMail daemons:\n"); printf(" -f file specify an alternative config file\n"); printf(" -p file specify an alternative runtime pidfile\n"); printf(" -n do not daemonize (no children are forked)\n"); printf(" -v log to the console (only useful with -n)\n"); printf(" -V show the version\n"); printf(" -h show this help message\n"); return 0; } #ifdef PROC_TITLES int main(int argc, char *argv[], char **envp) #else int main(int argc, char *argv[]) #endif { serverConfig_t config; int result, status, no_daemonize = 0; pid_t pid; int opt; openlog(PNAME, LOG_PID, LOG_MAIL); /* get command-line options */ dm_opterr = 0; /* suppress error message from getopt() */ while ((opt = dm_getopt(argc, argv, "vVhqnf:p:")) != -1) { switch (opt) { case 'v': /* TODO: Perhaps verbose should log to the console with -n? */ break; case 'V': printf("DBMail: dbmail-imapd\n" "Version: %s\n" "$Revision: 1712 $\n" "Copyright: %s\n", VERSION, COPYRIGHT); return 0; case 'n': /* TODO: We should also prevent children from forking, * but for now we'll just set a flag and skip Daemonize. */ no_daemonize = 1; break; case 'h': do_showhelp(); return 0; case 'p': if (dm_optarg && strlen(dm_optarg) > 0) pidFile = dm_optarg; else { fprintf(stderr, "dbmail-imapd: -p requires a filename " "argument\n\n"); return 1; } break; case 'f': if (dm_optarg && strlen(dm_optarg) > 0) configFile = dm_optarg; else { fprintf(stderr, "dbmail-imapd: -f requires a filename " "argument\n\n"); return 1; } break; default: break; } } SetMainSigHandler(); /* TODO: don't spawn children, either. this is at least a good start. */ if (!no_daemonize) Daemonize(); /* We write the pidFile after Daemonize because * we may actually be a child of the original process. */ pidfile_create(pidFile, getpid()); result = 0; do { mainStop = 0; mainRestart = 0; trace(TRACE_DEBUG, "main(): reading config"); #ifdef PROC_TITLES init_set_proc_title(argc, argv, envp, PNAME); set_proc_title("%s", "Idle"); #endif ReadConfig("IMAP", configFile); ReadConfig("DBMAIL", configFile); SetConfigItems(&config); SetTraceLevel("IMAP"); GetDBParams(&_db_params); config.ClientHandler = IMAPClientHandler; config.timeoutMsg = IMAP_TIMEOUT_MSG; CreateSocket(&config); switch ((pid = fork())) { case -1: close(config.listenSocket); trace(TRACE_FATAL, "main(): fork failed [%s]", strerror(errno)); case 0: /* child process */ drop_privileges(config.serverUser, config.serverGroup); result = StartServer(&config); trace(TRACE_INFO, "main(): server done, exit."); exit(result); default: /* parent process, wait for child to exit */ while (waitpid(pid, &status, WNOHANG | WUNTRACED) == 0) { if (mainStop) kill(pid, SIGTERM); if (mainRestart) kill(pid, SIGHUP); sleep(2); } if (WIFEXITED(status)) { /* child process terminated neatly */ result = WEXITSTATUS(status); trace(TRACE_DEBUG, "main(): server has exited, exit status [%d]", result); } else { /* child stopped or signaled, don't like */ /* make sure it is dead */ trace(TRACE_DEBUG, "main(): server has not exited normally. Killing.."); kill(pid, SIGKILL); result = 0; } } close(config.listenSocket); config_free(); } while (result == 1 && !mainStop); /* 1 means reread-config and restart */ trace(TRACE_INFO, "main(): exit"); return 0; } void MainSigHandler(int sig, siginfo_t * info UNUSED, void *data UNUSED) { trace(TRACE_DEBUG, "MainSigHandler(): got signal [%d]", sig); if (sig == SIGHUP) mainRestart = 1; else mainStop = 1; } void Daemonize() { if (fork()) exit(0); setsid(); if (fork()) exit(0); } int SetMainSigHandler() { struct sigaction act; /* init & install signal handlers */ memset(&act, 0, sizeof(act)); act.sa_sigaction = MainSigHandler; sigemptyset(&act.sa_mask); act.sa_flags = SA_SIGINFO; sigaction(SIGINT, &act, 0); sigaction(SIGQUIT, &act, 0); sigaction(SIGTERM, &act, 0); sigaction(SIGHUP, &act, 0); return 0; } void SetConfigItems(serverConfig_t * config) { field_t val; /* read items: NCHILDREN */ GetConfigValue("NCHILDREN", "IMAP", val); if (strlen(val) == 0) trace(TRACE_FATAL, "SetConfigItems(): no value for NCHILDREN in config file"); if ((config->startChildren = atoi(val)) <= 0) trace(TRACE_FATAL, "SetConfigItems(): value for NCHILDREN is invalid: [%d]", config->startChildren); trace(TRACE_DEBUG, "SetConfigItems(): server will create [%d] children", config->startChildren); /* read items: MAXCONNECTS */ GetConfigValue("MAXCONNECTS", "IMAP", val); if (strlen(val) == 0) trace(TRACE_FATAL, "SetConfigItems(): no value for MAXCONNECTS in config file"); if ((config->childMaxConnect = atoi(val)) <= 0) trace(TRACE_FATAL, "SetConfigItems(): value for MAXCONNECTS is invalid: [%d]", config->childMaxConnect); trace(TRACE_DEBUG, "SetConfigItems(): children will make max. [%d] connections", config->childMaxConnect); /* read items: MINSPARECHILDREN */ GetConfigValue("MINSPARECHILDREN", "IMAP", val); if (strlen(val) == 0) { trace(TRACE_WARNING, "SetConfigItems(): no value for MINSPARECHILDREN in config file. Using default value [2]"); config->minSpareChildren = 2; } else if ( (config->minSpareChildren = atoi(val)) <= 0) { trace(TRACE_WARNING, "SetConfigItems(): value for MINSPARECHILDREN is invalid: [%d]. Using default value [2].", config->minSpareChildren); config->minSpareChildren = 2; } trace(TRACE_DEBUG, "SetConfigItems(): will maintain minimum of [%d] spare children in reserve", config->minSpareChildren); /* read items: MAXSPARECHILDREN */ GetConfigValue("MAXSPARECHILDREN", "IMAP", val); if (strlen(val) == 0) { trace(TRACE_WARNING, "SetConfigItems(): no value for MAXSPARECHILDREN in config file. Using default value [4]."); config->maxSpareChildren = 4; } else if ( (config->maxSpareChildren = atoi(val)) <= 0) { trace(TRACE_WARNING, "SetConfigItems(): value for MAXSPARECHILDREN is invalid: [%d]. Using default value [4].", config->maxSpareChildren); config->maxSpareChildren = 4; } trace(TRACE_DEBUG, "SetConfigItems(): will maintain maximum of [%d] spare children in reserve", config->maxSpareChildren); /* read items: MAXCHILDREN */ GetConfigValue("MAXCHILDREN", "IMAP", val); if (strlen(val) == 0) { trace(TRACE_WARNING, "SetConfigItems(): no value for MAXCHILDREN in config file. Using default value."); config->maxChildren = HARD_MAX_CHILDREN; } else if ( (config->maxChildren = atoi(val)) <= 0) { trace(TRACE_WARNING, "SetConfigItems(): value for MAXCHILDREN is invalid: [%d]. Using default value.", config->maxSpareChildren); config->maxChildren = HARD_MAX_CHILDREN; } trace(TRACE_DEBUG, "SetConfigItems(): will allow maximum of [%d] children", config->maxChildren); /* read items: TIMEOUT */ GetConfigValue("TIMEOUT", "IMAP", val); if (strlen(val) == 0) { trace(TRACE_DEBUG, "SetConfigItems(): no value for TIMEOUT in config file"); config->timeout = 0; } else if ((config->timeout = atoi(val)) <= 30) trace(TRACE_FATAL, "SetConfigItems(): value for TIMEOUT is invalid: [%d]", config->timeout); trace(TRACE_DEBUG, "SetConfigItems(): timeout [%d] seconds", config->timeout); /* read items: PORT */ GetConfigValue("PORT", "IMAP", val); if (strlen(val) == 0) trace(TRACE_FATAL, "SetConfigItems(): no value for PORT in config file"); if ((config->port = atoi(val)) <= 0) trace(TRACE_FATAL, "SetConfigItems(): value for PORT is invalid: [%d]", config->port); trace(TRACE_DEBUG, "SetConfigItems(): binding to PORT [%d]", config->port); /* read items: BINDIP */ GetConfigValue("BINDIP", "IMAP", val); if (strlen(val) == 0) trace(TRACE_FATAL, "SetConfigItems(): no value for BINDIP in config file"); strncpy(config->ip, val, IPLEN); config->ip[IPLEN - 1] = '\0'; trace(TRACE_DEBUG, "SetConfigItems(): binding to IP [%s]", config->ip); /* read items: RESOLVE_IP */ GetConfigValue("RESOLVE_IP", "IMAP", val); if (strlen(val) == 0) trace(TRACE_DEBUG, "SetConfigItems(): no value for RESOLVE_IP in config file"); config->resolveIP = (strcasecmp(val, "yes") == 0); trace(TRACE_DEBUG, "SetConfigItems(): %sresolving client IP", config->resolveIP ? "" : "not "); /* read items: IMAP-BEFORE-SMTP */ GetConfigValue("IMAP_BEFORE_SMTP", "IMAP", val); if (strlen(val) == 0) trace(TRACE_DEBUG, "SetConfigItems(): no value for IMAP_BEFORE_SMTP in config file"); imap_before_smtp = (strcasecmp(val, "yes") == 0); trace(TRACE_DEBUG, "SetConfigItems(): %s IMAP-before-SMTP", imap_before_smtp ? "Enabling" : "Disabling"); /* read items: EFFECTIVE-USER */ GetConfigValue("EFFECTIVE_USER", "IMAP", val); if (strlen(val) == 0) trace(TRACE_FATAL, "SetConfigItems(): no value for EFFECTIVE_USER in config file"); strncpy(config->serverUser, val, FIELDSIZE); config->serverUser[FIELDSIZE - 1] = '\0'; trace(TRACE_DEBUG, "SetConfigItems(): effective user shall be [%s]", config->serverUser); /* read items: EFFECTIVE-GROUP */ GetConfigValue("EFFECTIVE_GROUP", "IMAP", val); if (strlen(val) == 0) trace(TRACE_FATAL, "SetConfigItems(): no value for EFFECTIVE_GROUP in config file"); strncpy(config->serverGroup, val, FIELDSIZE); config->serverGroup[FIELDSIZE - 1] = '\0'; trace(TRACE_DEBUG, "SetConfigItems(): effective group shall be [%s]", config->serverGroup); }