/*
Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl
Copyright (C) 2004-2006 NFG Net Facilities Group BV support@nfg.nl
Copyright (C) 2006 Aaron Stone aaron@serendipity.cx
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.
*/
/*
*
* serverparent.c
*
* Main program for all daemons.
*/
#include "dbmail.h"
#define THIS_MODULE "serverparent"
/* set up database login data */
extern db_param_t _db_params;
static char *configFile = DEFAULT_CONFIG_FILE;
extern volatile sig_atomic_t mainRestart;
extern volatile sig_atomic_t mainStatus;
extern volatile sig_atomic_t mainStop;
extern volatile sig_atomic_t mainSig;
/* Not used, but required to link with libdbmail.so */
int verbose = 0;
int no_to_all = 0;
int yes_to_all = 0;
int reallyquiet = 0;
int quiet = 0;
static int SetMainSigHandler(void);
static void MainSigHandler(int sig, siginfo_t * info, void *data);
static void ClearConfig(serverConfig_t * conf);
static void DoConfig(serverConfig_t * conf, const char * const service);
static void LoadServerConfig(serverConfig_t * config, const char * const service);
/* Valid chars used by LMTP, POP3 and Tim's Sieve. */
const char ValidNetworkChars[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
",'\"?_.!|@#$%^&*()-+=~[]{}<>:;\\/ '";
void serverparent_showhelp(const char *name, const char *greeting) {
printf("*** %s ***\n", name);
printf("%s\n", greeting);
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(" -s file specify an alternative runtime statefile\n");
printf(" -n do not daemonize (no children are forked)\n");
printf(" -v verbose logging to syslog and stderr\n");
printf(" -V show the version\n");
printf(" -h show this help message\n");
}
/* Return values:
* -1 just quit
* 0 all is well, config is updated
* 1 help must be shown, then quit
*/
int serverparent_getopt(serverConfig_t *config, const char *service, int argc, char *argv[])
{
int opt;
configFile = g_strdup(DEFAULT_CONFIG_FILE);
ClearConfig(config);
TRACE(TRACE_DEBUG, "checking command line options");
/* get command-line options */
opterr = 0; /* suppress error message from getopt() */
while ((opt = getopt(argc, argv, "vVhqnf:p:s:")) != -1) {
switch (opt) {
case 'v':
config->log_verbose = 1;
break;
case 'V':
PRINTF_THIS_IS_DBMAIL;
return -1;
case 'n':
config->no_daemonize = 1;
break;
case 'h':
return 1;
case 'p':
if (optarg && strlen(optarg) > 0)
config->pidFile = g_strdup(optarg);
else {
fprintf(stderr, "%s: -p requires a filename argument\n\n", argv[0]);
return 1;
}
break;
case 's':
if (optarg && strlen(optarg) > 0)
config->stateFile = g_strdup(optarg);
else {
fprintf(stderr, "%s: -s requires a filename argument\n\n", argv[0]);
return 1;
}
break;
case 'f':
if (optarg && strlen(optarg) > 0) {
g_free(configFile);
configFile = g_strdup(optarg);
} else {
fprintf(stderr, "%s: -f requires a filename argument\n\n", argv[0]);
return 1;
}
break;
default:
fprintf(stderr, "%s: unrecognized option: %s\n\n", argv[0], argv[optind]);
return 1;
}
}
if (optind < argc) {
fprintf(stderr, "%s: unrecognized options: ", argv[0]);
while (optind < argc)
fprintf(stderr, "%s ", argv[optind++]);
fprintf(stderr, "\n\n");
return 1;
}
DoConfig(config, service);
return 0;
}
int serverparent_mainloop(serverConfig_t *config, const char *service, const char *servicename)
{
SetMainSigHandler();
if (config->no_daemonize) {
StartCliServer(config);
TRACE(TRACE_INFO, "exiting cli server");
return 0;
}
server_daemonize(config);
/* We write the pidFile after daemonize because
* we may actually be a child of the original process. */
if (! config->pidFile)
config->pidFile = config_get_pidfile(config, servicename);
pidfile_create(config->pidFile, getpid());
if (! config->stateFile)
config->stateFile = config_get_statefile(config, servicename);
statefile_create(config->stateFile);
/* This is the actual main loop. */
while (!mainStop && server_run(config)) {
/* Reread the config file and restart the services,
* e.g. on SIGHUP or other graceful restart condition. */
DoConfig(config, service);
sleep(2);
}
ClearConfig(config);
TRACE(TRACE_INFO, "leaving main loop");
return 0;
}
void MainSigHandler(int sig, siginfo_t * info UNUSED, void *data UNUSED)
{
mainSig = sig;
if (sig == SIGHUP)
mainRestart = 1;
else if (sig == SIGUSR1)
mainStatus = 1;
else
mainStop = 1;
}
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);
sigaction(SIGUSR1, &act, 0);
return 0;
}
void ClearConfig(serverConfig_t * config)
{
assert(config);
g_strfreev(config->iplist);
g_free(config->listenSockets);
config->listenSockets = NULL;
config->iplist = NULL;
memset(config, 0, sizeof(serverConfig_t));
}
void DoConfig(serverConfig_t * config, const char * const service)
{
TRACE(TRACE_DEBUG, "reading config [%s]", configFile);
config_free();
config_read(configFile);
SetTraceLevel(service);
/* Override SetTraceLevel. */
if (config->log_verbose) {
configure_debug(5,5);
}
LoadServerConfig(config, service);
GetDBParams(&_db_params);
}
void LoadServerConfig(serverConfig_t * config, const char * const service)
{
field_t val;
config_get_logfiles(config);
/* read items: NCHILDREN */
config_get_value("NCHILDREN", service, val);
if (strlen(val) == 0)
TRACE(TRACE_FATAL, "no value for NCHILDREN in config file");
if ((config->startChildren = atoi(val)) <= 0)
TRACE(TRACE_FATAL, "value for NCHILDREN is invalid: [%d]",
config->startChildren);
TRACE(TRACE_DEBUG, "server will create [%d] children",
config->startChildren);
/* read items: MAXCONNECTS */
config_get_value("MAXCONNECTS", service, val);
if (strlen(val) == 0)
TRACE(TRACE_FATAL, "no value for MAXCONNECTS in config file");
if ((config->childMaxConnect = atoi(val)) <= 0)
TRACE(TRACE_FATAL, "value for MAXCONNECTS is invalid: [%d]",
config->childMaxConnect);
TRACE(TRACE_DEBUG, "children will make max. [%d] connections",
config->childMaxConnect);
/* read items: TIMEOUT */
config_get_value("TIMEOUT", service, val);
if (strlen(val) == 0) {
TRACE(TRACE_DEBUG, "no value for TIMEOUT in config file");
config->timeout = 0;
} else if ((config->timeout = atoi(val)) <= 30)
TRACE(TRACE_FATAL, "value for TIMEOUT is invalid: [%d]",
config->timeout);
TRACE(TRACE_DEBUG, "timeout [%d] seconds",
config->timeout);
/* read items: LOGIN_TIMEOUT */
config_get_value("LOGIN_TIMEOUT", service, val);
if (strlen(val) == 0) {
TRACE(TRACE_DEBUG, "no value for TIMEOUT in config file");
config->login_timeout = 60;
} else if ((config->login_timeout = atoi(val)) <= 10)
TRACE(TRACE_FATAL, "value for TIMEOUT is invalid: [%d]",
config->login_timeout);
TRACE(TRACE_DEBUG, "login_timeout [%d] seconds",
config->login_timeout);
/* SOCKET */
config_get_value("SOCKET", service, val);
if (strlen(val) == 0)
TRACE(TRACE_DEBUG, "no value for SOCKET in config file");
strncpy(config->socket, val, FIELDSIZE);
TRACE(TRACE_DEBUG, "socket [%s]",
config->socket);
/* read items: PORT */
config_get_value("PORT", service, val);
if (strlen(val) == 0)
TRACE(TRACE_FATAL, "no value for PORT in config file");
if ((config->port = atoi(val)) <= 0)
TRACE(TRACE_FATAL, "value for PORT is invalid: [%d]",
config->port);
TRACE(TRACE_DEBUG, "binding to PORT [%d]",
config->port);
/* read items: BINDIP */
config_get_value("BINDIP", service, val);
if (strlen(val) == 0)
TRACE(TRACE_FATAL, "no value for BINDIP in config file");
// If there was a SIGHUP, then we're resetting an active config.
g_strfreev(config->iplist);
g_free(config->listenSockets);
// Allowed list separators are ' ' and ','.
config->iplist = g_strsplit_set(val, " ,", 0);
config->ipcount = g_strv_length(config->iplist);
if (config->ipcount < 1) {
TRACE(TRACE_FATAL, "no value for BINDIP in config file");
}
int ip;
for (ip = 0; ip < config->ipcount; ip++) {
// Remove whitespace from each list entry, then log it.
g_strstrip(config->iplist[ip]);
TRACE(TRACE_DEBUG, "binding to IP [%s]", config->iplist[ip]);
}
/* read items: BACKLOG */
config_get_value("BACKLOG", service, val);
if (strlen(val) == 0) {
TRACE(TRACE_DEBUG, "no value for BACKLOG in config file. Using default value [%d]",
BACKLOG);
config->backlog = BACKLOG;
} else if ((config->backlog = atoi(val)) <= 0)
TRACE(TRACE_FATAL, "value for BACKLOG is invalid: [%d]",
config->backlog);
/* read items: RESOLVE_IP */
config_get_value("RESOLVE_IP", service, val);
if (strlen(val) == 0)
TRACE(TRACE_DEBUG, "no value for RESOLVE_IP in config file");
config->resolveIP = (strcasecmp(val, "yes") == 0);
TRACE(TRACE_DEBUG, "%sresolving client IP",
config->resolveIP ? "" : "not ");
/* read items: service-BEFORE-SMTP */
char *service_before_smtp = g_strconcat(service, "_BEFORE_SMTP", NULL);
config_get_value(service_before_smtp, service, val);
g_free(service_before_smtp);
if (strlen(val) == 0)
TRACE(TRACE_DEBUG, "no value for %s_BEFORE_SMTP in config file",
service);
config->service_before_smtp = (strcasecmp(val, "yes") == 0);
TRACE(TRACE_DEBUG, "%s %s-before-SMTP",
config->service_before_smtp ? "Enabling" : "Disabling", service);
/* read items: EFFECTIVE-USER */
config_get_value("EFFECTIVE_USER", service, val);
if (strlen(val) == 0)
TRACE(TRACE_FATAL, "no value for EFFECTIVE_USER in config file");
strncpy(config->serverUser, val, FIELDSIZE);
config->serverUser[FIELDSIZE - 1] = '\0';
TRACE(TRACE_DEBUG, "effective user shall be [%s]",
config->serverUser);
/* read items: EFFECTIVE-GROUP */
config_get_value("EFFECTIVE_GROUP", service, val);
if (strlen(val) == 0)
TRACE(TRACE_FATAL, "no value for EFFECTIVE_GROUP in config file");
strncpy(config->serverGroup, val, FIELDSIZE);
config->serverGroup[FIELDSIZE - 1] = '\0';
TRACE(TRACE_DEBUG, "effective group shall be [%s]",
config->serverGroup);
/* read items: MINSPARECHILDREN */
config_get_value("MINSPARECHILDREN", service, val);
if (strlen(val) == 0)
TRACE(TRACE_FATAL, "no value for MINSPARECHILDREN in config file");
if ( (config->minSpareChildren = atoi(val)) < 0)
TRACE(TRACE_FATAL, "value for MINSPARECHILDREN is invalid: [%d]",
config->minSpareChildren);
TRACE(TRACE_DEBUG, "will maintain minimum of [%d] spare children in reserve",
config->minSpareChildren);
/* read items: MAXSPARECHILDREN */
config_get_value("MAXSPARECHILDREN", service, val);
if (strlen(val) == 0)
TRACE(TRACE_FATAL, "no value for MAXSPARECHILDREN in config file");
if ( (config->maxSpareChildren = atoi(val)) <= 0)
TRACE(TRACE_FATAL, "value for MAXSPARECHILDREN is invalid: [%d]",
config->maxSpareChildren);
TRACE(TRACE_DEBUG, "will maintain maximum of [%d] spare children in reserve",
config->maxSpareChildren);
/* read items: MAXCHILDREN */
config_get_value("MAXCHILDREN", service, val);
if (strlen(val) == 0)
TRACE(TRACE_FATAL, "no value for MAXCHILDREN in config file");
if ( (config->maxChildren = atoi(val)) <= 0)
TRACE(TRACE_FATAL, "value for MAXCHILDREN is invalid: [%d]",
config->maxSpareChildren);
TRACE(TRACE_DEBUG, "will allow maximum of [%d] children",
config->maxChildren);
}
syntax highlighted by Code2HTML, v. 0.9.1