/*
** Copyright (c) 2007 Sendmail, Inc. and its suppliers.
** All rights reserved.
**
** $Id: stats.c,v 1.10 2007/06/27 19:49:23 msk Exp $
*/
#ifdef _FFR_STATS
#ifndef lint
static char stats_c_id[] = "@(#)$Id: stats.c,v 1.10 2007/06/27 19:49:23 msk Exp $";
#endif /* !lint */
/* system includes */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
#include <assert.h>
#include <syslog.h>
/* libdb includes */
#include <db.h>
/* libsm includes */
#include <sm/gen.h>
#include <sm/cdefs.h>
#include <sm/string.h>
/* libdkim includes */
#include <dkim.h>
/* dkim-filter includes */
#include "stats.h"
#include "dkim-filter.h"
/* definitions */
#ifndef DB_NOTFOUND
# define DB_NOTFOUND 1
#endif /* ! DB_NOTFOUND */
#define DB_MODE (S_IRUSR|S_IWUSR)
#ifndef DB_VERSION_MAJOR
# define DB_VERSION_MAJOR 1
#endif /* ! DB_VERSION_MAJOR */
#define DB_VERSION_CHECK(x,y,z) ((DB_VERSION_MAJOR == (x) && \
DB_VERSION_MINOR >= (y)) || \
DB_VERSION_MAJOR > (x))
/* globals */
static pthread_mutex_t stats_lock;
/*
** DKIMF_STATS_INIT -- initialize statistics
**
** Parameters:
** None.
**
** Return value:
** None.
*/
void
dkimf_stats_init(void)
{
pthread_mutex_init(&stats_lock, NULL);
}
/*
** DKIMF_STATS_RECORD -- record a DKIM result
**
** Parameters:
** path -- patth to the DB to update
** sigdomain -- signing domain
** hdrcanon -- header canonicalization used
** bodycanon -- body canonicalization used
** signalg -- signing algorithm used
** passfail -- result (TRUE == pass, FALSE == not pass)
** testing -- testing?
** lengths -- l= tag present?
**
** Return value:
** None (for now).
*/
void
dkimf_stats_record(const char *path, const char *sigdomain,
dkim_canon_t hdrcanon, dkim_canon_t bodycanon,
dkim_alg_t signalg, bool passfail,
bool testing, bool lengths)
{
int status = 0;
DB *db;
DBT key;
DBT data;
struct dkim_stats_key reckey;
struct dkim_stats_data recdata;
assert(path != NULL);
assert(sigdomain != NULL);
pthread_mutex_lock(&stats_lock);
/* open the DB */
#if DB_VERSION_CHECK(3,0,0)
status = db_create(&db, NULL, 0);
if (status == 0)
{
# if DB_VERSION_CHECK(4,0,0)
status = db->open(db, NULL, path, NULL, DB_HASH,
(DB_CREATE|DB_THREAD), DB_MODE);
# else /* DB_VERSION_CHECK(4,0,0) */
status = db->open(db, path, NULL, DB_HASH,
(DB_CREATE|DB_THREAD), DB_MODE);
# endif /* DB_VERSION_CHECK(4,0,0) */
}
#elif DB_VERSION_CHECK(2,0,0)
status = db_open(path, DB_HASH, (DB_CREATE|DB_THREAD), DB_MODE,
NULL, NULL, &db);
#else /* DB_VERSION_CHECK(2,0,0) */
db = dbopen(path, (O_CREAT|O_RDWR), DB_MODE, DB_HASH, NULL);
if (db == NULL)
status = errno;
#endif /* DB_VERSION_CHECK */
if (status != 0)
{
if (dolog)
{
char *err;
#if DB_VERSION_CHECK(3,0,0)
err = db_strerror(status);
#else /* DB_VERSION_CHECK(3,0,0) */
err = strerror(errno);
#endif /* DB_VERSION_CHECK(3,0,0) */
syslog(LOG_ERR, "%s: db->open(): %s", path, err);
}
pthread_mutex_unlock(&stats_lock);
return;
}
/* populate the records */
memset(&reckey, '\0', sizeof reckey);
memset(&recdata, '\0', sizeof recdata);
reckey.sk_hdrcanon = hdrcanon;
reckey.sk_bodycanon = bodycanon;
sm_strlcpy(reckey.sk_sigdomain, sigdomain, sizeof reckey.sk_sigdomain);
/* see if this key already exists */
memset(&key, '\0', sizeof key);
memset(&data, '\0', sizeof data);
key.data = (void *) &reckey;
key.size = sizeof reckey;
#if DB_VERSION_CHECK(3,0,0)
key.ulen = sizeof reckey;
key.flags = DB_DBT_USERMEM;
#endif /* DB_VERSION_CHECK(3,0,0) */
data.data = (void *) &recdata;
data.size = sizeof recdata;
#if DB_VERSION_CHECK(3,0,0)
data.ulen = sizeof recdata;
data.flags = DB_DBT_USERMEM;
#endif /* DB_VERSION_CHECK(3,0,0) */
#if DB_VERSION_CHECK(2,0,0)
status = db->get(db, NULL, &key, &data, 0);
#else /* DB_VERSION_CHECK(2,0,0) */
status = db->get(db, &key, &data, 0);
#endif /* DB_VERSION_CHECK(2,0,0) */
if (status != DB_NOTFOUND && status != 0)
{
if (dolog)
{
char *err;
#if DB_VERSION_CHECK(3,0,0)
err = db_strerror(status);
#else /* DB_VERSION_CHECK(3,0,0) */
err = strerror(errno);
#endif /* DB_VERSION_CHECK(3,0,0) */
syslog(LOG_ERR, "%s: db->get(): %s", path, err);
}
#if DB_VERSION_CHECK(2,0,0)
(void) db->close(db, 0);
#else /* DB_VERSION_CHECK(2,0,0) */
(void) db->close(db);
#endif /* DB_VERSION_CHECK(2,0,0) */
pthread_mutex_unlock(&stats_lock);
return;
}
#if !DB_VERSION_CHECK(2,0,0)
memcpy((void *) &recdata, data.data, MIN(sizeof recdata, data.size));
#endif /* ! DB_VERSION_CHECK(2,0,0) */
/* update totals */
recdata.sd_lengths = lengths;
(void) time(&recdata.sd_lastseen);
recdata.sd_lastalg = signalg;
if (passfail)
recdata.sd_pass++;
else
recdata.sd_fail++;
/* write/update the record */
memset(&key, '\0', sizeof key);
memset(&data, '\0', sizeof data);
key.data = (void *) &reckey;
key.size = sizeof reckey;
#if DB_VERSION_CHECK(3,0,0)
key.ulen = sizeof reckey;
key.flags = DB_DBT_USERMEM;
#endif /* DB_VERSION_CHECK(3,0,0) */
data.data = (void *) &recdata;
data.size = sizeof recdata;
#if DB_VERSION_CHECK(3,0,0)
data.ulen = sizeof recdata;
data.flags = DB_DBT_USERMEM;
#endif /* DB_VERSION_CHECK(3,0,0) */
#if DB_VERSION_CHECK(2,0,0)
status = db->put(db, NULL, &key, &data, 0);
#else /* DB_VERSION_CHECK(2,0,0) */
status = db->put(db, &key, &data, 0);
#endif /* DB_VERSION_CHECK(2,0,0) */
if (status != 0 && dolog)
{
char *err;
#if DB_VERSION_CHECK(3,0,0)
err = db_strerror(status);
#else /* DB_VERSION_CHECK(3,0,0) */
err = strerror(errno);
#endif /* DB_VERSION_CHECK(3,0,0) */
syslog(LOG_ERR, "%s: db->put(): %s", path, err);
}
/* close the DB */
#if DB_VERSION_CHECK(2,0,0)
(void) db->close(db, 0);
#else /* DB_VERSION_CHECK(2,0,0) */
(void) db->close(db);
#endif /* DB_VERSION_CHECK(2,0,0) */
pthread_mutex_unlock(&stats_lock);
}
#endif /* _FFR_STATS */
syntax highlighted by Code2HTML, v. 0.9.1