/* $Id: conf.c,v 1.42.2.2 2006/09/20 07:38:24 manu Exp $ */ /* * Copyright (c) 2004 Emmanuel Dreyfus * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Emmanuel Dreyfus * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #ifdef HAVE_SYS_CDEFS_H #include #ifdef __RCSID __RCSID("$Id: conf.c,v 1.42.2.2 2006/09/20 07:38:24 manu Exp $"); #endif #endif #ifdef HAVE_OLD_QUEUE_H #include "queue.h" #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "acl.h" #ifdef USE_DNSRBL #include "dnsrbl.h" #endif #include "autowhite.h" #include "conf.h" #include "sync.h" #include "pending.h" #include "dump.h" #include "milter-greylist.h" /* Default configuration */ struct conf defconf; struct conf conf; int conf_cold = 1; int conf_nodetach = 0; char c_pidfile[QSTRLEN + 1]; char c_dumpfile[QSTRLEN + 1]; char c_socket[QSTRLEN + 1]; char c_user[QSTRLEN + 1]; char c_syncaddr[IPADDRSTRLEN + 1]; char c_syncport[NUMLEN + 1]; char c_syncsrcaddr[IPADDRSTRLEN + 1]; char c_syncsrcport[NUMLEN + 1]; char c_dracdb[QSTRLEN + 1]; char *conffile = CONFFILE; struct timeval conffile_modified; int numb_of_conf_update_threads; #define MAX_NUMB_OF_CONF_UPDATE_THREADS 1 /* * this lock does not protect conf_update any more, * only conffile_modified and numb_of_conf_update_threads. * there are lot of non-auto variables above to protect as well, * so it is safer to limit the maximum number of configuration loading * processes to one for the time being. */ pthread_rwlock_t conf_lock; void conf_init(void) { int error; if ((error = pthread_rwlock_init(&conf_lock, NULL)) != 0) { mg_log(LOG_ERR, "pthread_rwlock_init failed: %s", strerror(error)); exit(EX_OSERR); } return; } void conf_load(void) { FILE *stream; struct timeval tv1, tv2, tv3; /* * Reset the configuration to its default * (This includes command line flags) */ memcpy(&conf, &defconf, sizeof(conf)); (void)gettimeofday(&tv1, NULL); if (!conf_cold || conf.c_debug) mg_log(LOG_INFO, "%sloading config file \"%s\"", conf_cold ? "" : "re", conffile); if ((stream = fopen(conffile, "r")) == NULL) { mg_log(LOG_ERR, "cannot open config file %s: %s", conffile, strerror(errno)); mg_log(LOG_ERR, "continuing with no exception list"); } else { peer_clear(); ACL_WRLOCK; #ifdef USE_DNSRBL dnsrbl_clear(); #endif acl_clear(); conf_in = stream; conf_line = 1; conf_acl_end = 0; conf_parse(); ACL_UNLOCK; fclose(stream); if (!conf_cold || conf.c_debug) { (void)gettimeofday(&tv2, NULL); timersub(&tv2, &tv1, &tv3); mg_log(LOG_INFO, "%sloaded config file \"%s\" in %ld.%06lds", conf_cold ? "" : "re", conffile, tv3.tv_sec, tv3.tv_usec); } } if (conf_cold) { (void)gettimeofday(&conffile_modified, NULL); } else { CONF_WRLOCK; --numb_of_conf_update_threads; CONF_UNLOCK; } if (conf.c_debug || conf.c_acldebug) acl_dump(); return; } void conf_update(void) { struct stat st; pthread_t tid; pthread_attr_t attr; int error; if (stat(conffile, &st) != 0) { mg_log(LOG_ERR, "config file \"%s\" unavailable", conffile); return; } CONF_WRLOCK; numb_of_conf_update_threads++; if (st.st_mtime <= conffile_modified.tv_sec || numb_of_conf_update_threads > MAX_NUMB_OF_CONF_UPDATE_THREADS) { --numb_of_conf_update_threads; CONF_UNLOCK; return; } conffile_modified.tv_sec = st.st_mtime; CONF_UNLOCK; /* * On some platforms, the thread stack limit is too low and * conf_parse will get a SIGSEGV because it overflows the * stack. * * In order to fix this, we spawn a new thread just for * parsing the config file, and we request a stack big * enough to hold the parser data. 2 MB seems okay. * * We do not do that during the initial config load because * it is useless and it will trigger a bug on some systems * (launching a thread before a fork seems to be a problem) */ if ((error = pthread_attr_init(&attr)) != 0) { mg_log(LOG_ERR, "pthread_attr_init failed: %s", strerror(error)); exit(EX_OSERR); } if ((error = pthread_attr_setstacksize(&attr, 2 * 1024 * 1024)) != 0) { mg_log(LOG_ERR, "pthread_attr_setstacksize failed: %s", strerror(error)); exit(EX_OSERR); } if ((error = pthread_create(&tid, &attr, (void *(*)(void *))conf_load, NULL)) != 0) { mg_log(LOG_ERR, "pthread_create failed: %s", strerror(error)); exit(EX_OSERR); } if ((error = pthread_detach(tid)) != 0) { mg_log(LOG_ERR, "pthread_detach failed: %s", strerror(error)); exit(EX_OSERR); } if ((error = pthread_attr_destroy(&attr)) != 0) { mg_log(LOG_ERR, "pthread_attr_destroy failed: %s", strerror(error)); exit(EX_OSERR); } return; } /* * Write path into dst, stripping leading and trailing quotes */ char * quotepath(dst, path, len) char *dst; char *path; size_t len; { path++; /* strip first quote */ strncpy(dst, path, len); dst[len] = '\0'; /* Strip trailing quote */ if ((len = strlen(dst)) > 0) dst[len - 1] = '\0'; return dst; } void conf_defaults(c) struct conf *c; { c->c_forced = C_GLNONE; c->c_debug = 0; c->c_acldebug = 0; c->c_quiet = 0; c->c_noauth = 0; c->c_noaccessdb = 0; c->c_nospf = 0; c->c_delayedreject = 0; c->c_testmode = 0; c->c_delay = GLDELAY; c->c_autowhite_validity = AUTOWHITE_VALIDITY; c->c_pidfile = NULL; c->c_dumpfile = DUMPFILE; prefix2mask4(32, &c->c_match_mask); #ifdef AF_INET6 prefix2mask6(128, &c->c_match_mask6); #endif c->c_syncaddr = NULL; c->c_syncport = NULL; c->c_syncsrcaddr = NULL; c->c_syncsrcport = NULL; c->c_socket = NULL; c->c_user = NULL; c->c_report = C_ALL; c->c_dumpfreq = DUMPFREQ; c->c_timeout = TIMEOUT; c->c_extendedregex = 0; c->c_dracdb = DRACDB; c->c_nodrac = 0; return; }