/* $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 <sys/cdefs.h>
#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 <sys/queue.h>
#endif

#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <pthread.h>
#include <sysexits.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#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;
}


syntax highlighted by Code2HTML, v. 0.9.1