/* $Id: dump.c,v 1.28.2.1 2006/09/04 22:05:59 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: dump.c,v 1.28.2.1 2006/09/04 22:05:59 manu Exp $");
#endif
#endif
#ifdef HAVE_OLD_QUEUE_H
#include "queue.h"
#else
#include <sys/queue.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <pthread.h>
#include <errno.h>
#include <sysexits.h>
#include <syslog.h>
#include <time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "conf.h"
#include "sync.h"
#include "dump.h"
#include "autowhite.h"
#include "milter-greylist.h"
pthread_cond_t dump_sleepflag;
int dump_parse(void);
int dump_dirty = 0;
void
dump_init(void) {
int error;
if ((error = pthread_cond_init(&dump_sleepflag, NULL)) != 0) {
mg_log(LOG_ERR,
"pthread_cond_init failed: %s", strerror(error));
exit(EX_OSERR);
}
return;
}
void
dumper_start(void) {
pthread_t tid;
int error;
if ((error = pthread_create(&tid, NULL, dumper, NULL)) != 0) {
mg_log(LOG_ERR,
"cannot start dumper thread: %s", strerror(error));
exit(EX_OSERR);
}
return;
}
/* ARGSUSED0 */
void *
dumper(dontcare)
void *dontcare;
{
int error;
pthread_mutex_t mutex;
if ((error = pthread_mutex_init(&mutex, NULL)) != 0) {
mg_log(LOG_ERR, "pthread_mutex_init failed: %s",
strerror(error));
exit(EX_OSERR);
}
if ((error = pthread_mutex_lock(&mutex)) != 0) {
mg_log(LOG_ERR, "pthread_mutex_lock failed: %s",
strerror(error));
exit(EX_OSERR);
}
for (;;) {
/* XXX Not really dynamically adjustable */
switch (conf.c_dumpfreq) {
case -1:
sleep(DUMPFREQ);
break;
case 0:
if ((error = pthread_cond_wait(&dump_sleepflag,
&mutex)) != 0)
mg_log(LOG_ERR, "pthread_cond_wait failed: %s",
strerror(error));
break;
default:
sleep(conf.c_dumpfreq);
break;
}
/*
* If there is no change to dump, go back to sleep
*/
if ((conf.c_dumpfreq == -1) || (dump_dirty == 0))
continue;
dump_perform();
}
/* NOTREACHED */
mg_log(LOG_ERR, "dumper unexpectedly exitted");
exit(EX_SOFTWARE);
return NULL;
}
void
dump_perform(void) {
FILE *dump;
int dumpfd;
struct timeval tv1, tv2, tv3;
char newdumpfile[MAXPATHLEN + 1];
int done;
int greylisted_count;
int whitelisted_count;
char *s_buffer = NULL;
if (conf.c_debug) {
(void)gettimeofday(&tv1, NULL);
mg_log(LOG_DEBUG, "dumping %d modifications",
dump_dirty);
}
/*
* dump_dirty is not protected by a lock,
* hence it could be modified between the
* display and the actual dump. This debug
* message does not give an accurate information
*/
dump_dirty = 0;
/*
* Dump the database in a temporary file and
* then replace the old one by the new one.
* On decent systems, rename(2) garantees that
* even if the machine crashes, we will not
* loose both files.
*/
snprintf(newdumpfile, MAXPATHLEN,
"%s-XXXXXXXX", conf.c_dumpfile);
if ((dumpfd = mkstemp(newdumpfile)) == -1) {
mg_log(LOG_ERR, "mkstemp(\"%s\") failed: %s",
newdumpfile, strerror(errno));
exit(EX_OSERR);
}
if ((dump = fdopen(dumpfd, "w")) == NULL) {
mg_log(LOG_ERR, "cannot write dumpfile \"%s\": %s",
newdumpfile, strerror(errno));
exit(EX_OSERR);
}
#define BIG_BUFFER (10 * 1024 * 1024)
/* XXX TODO: make this configurable */
if ((s_buffer = calloc(1, BIG_BUFFER + 1)) == NULL) {
mg_log(LOG_ERR, "Unable to allocate big buffer for \"%s\": %s "
"- continuing with sys default",
newdumpfile, strerror(errno));
} else {
setvbuf(dump, s_buffer, _IOFBF, BIG_BUFFER);
}
dump_header(dump);
greylisted_count = pending_textdump(dump);
whitelisted_count = autowhite_textdump(dump);
done = greylisted_count + whitelisted_count;
fprintf(dump, "#\n# Summary: %d records, %d greylisted, %d "
"whitelisted\n#\n", done, greylisted_count, whitelisted_count);
fclose(dump);
if (s_buffer)
free(s_buffer);
if (rename(newdumpfile, conf.c_dumpfile) != 0) {
mg_log(LOG_ERR, "cannot replace \"%s\" by \"%s\": %s\n",
conf.c_dumpfile, newdumpfile, strerror(errno));
exit(EX_OSERR);
}
if (conf.c_debug) {
(void)gettimeofday(&tv2, NULL);
timersub(&tv2, &tv1, &tv3);
mg_log(LOG_DEBUG, "dumping %d records in %ld.%06lds",
done, tv3.tv_sec, tv3.tv_usec);
}
return;
}
void
dump_reload(void) {
FILE *dump;
/*
* Re-import a saved greylist
*/
if ((dump = fopen(conf.c_dumpfile, "r")) == NULL) {
mg_log(LOG_ERR, "cannot read dumpfile \"%s\"", conf.c_dumpfile);
mg_log(LOG_ERR, "starting with an empty greylist");
} else {
dump_in = dump;
PENDING_WRLOCK;
AUTOWHITE_WRLOCK;
dump_parse();
/*
* dump_dirty has been bumped on each pending_get call,
* whereas there is nothing to flush. Fix that.
*/
dump_dirty = 0;
AUTOWHITE_UNLOCK;
PENDING_UNLOCK;
fclose(dump);
}
return;
}
void
dump_flush(void) {
int error;
if ((error = pthread_cond_signal(&dump_sleepflag)) != 0) {
mg_log(LOG_ERR, "cannot wakeup dumper: %s", strerror(error));
exit(EX_SOFTWARE);
}
return;
}
void
dump_header(stream)
FILE *stream;
{
char textdate[DATELEN + 1];
struct tm tm;
time_t t;
t = time(NULL);
localtime_r(&t, &tm);
strftime(textdate, DATELEN, "%Y-%m-%d %T", &tm);
fprintf(stream, "#\n# milter-greylist databases, "
"dumped by milter-greylist-%s on %s.\n",
PACKAGE_VERSION, textdate);
fprintf(stream, "# DO NOT EDIT while milter-greylist is running, "
"changes will be overwritten.\n#\n");
return;
}
syntax highlighted by Code2HTML, v. 0.9.1