src/history.c
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- hisSetkey
- hisOpen
- hisGetDbz
- hisGet
- hisAddDbz
- hisAdd
- hisPruneDbz
- hisPrune
/* $Id: history.c,v 1.2 1999/09/16 22:17:44 proff Exp $
* $Copyright$
*/
#include "nglobal.h"
#include "dbz.h"
#include "ipc.h"
#include "lock.h"
#include "history.h"
static int his_fd = -1;
#define HEADER_LEN 5
static void hisSetkey (char *s, datum * key)
/* [<][>][^][v][top][bottom][index][help] */
{
int len;
len = strlen (s);
key->dptr = s;
key->dsize = len;
return;
}
static bool hisOpen ()
/* [<][>][^][v][top][bottom][index][help] */
{
struct stat st;
char dbdir[MAX_PATH];
char dbpag[MAX_PATH];
bool rebuild = FALSE;
if (his_fd != -1)
return TRUE;
his_fd = open (con->historyFile, O_RDWR);
if (chdir(con->cacheDir)<0)
{
loge (("couldn't chdir('%s')", con->cacheDir));
return FALSE;
}
if (his_fd == -1)
{
loge (("missing history file '%s'", con->historyFile));
rebuild = TRUE;
}
sprintf (dbdir, "%.127s.dir", con->historyFile);
if (stat (dbdir, &st) == -1)
{
loge (("missing history file '%s'", dbdir));
rebuild = TRUE;
}
sprintf (dbpag, "%.127s.pag", con->historyFile);
if (stat (dbpag, &st) == -1)
{
loge (("missing history file '%s'", dbpag));
rebuild = TRUE;
}
if (rebuild)
{
unlink (con->historyFile);
unlink (dbdir);
unlink (dbpag);
his_fd = open (con->historyFile, O_RDWR | O_CREAT | O_EXCL, 0664);
if (his_fd == -1)
{
loge (("couldn't create history file '%s'", con->historyFile));
return FALSE;
}
dbzincore (1);
if (dbzfresh (con->historyFile, 700001, '\n', '0', 0) == -1)
{
loge (("couldn't dbzfresh() '%s'", con->historyFile));
return FALSE;
}
log (("created history file '%s' (ignore earlier errors if this is a first-run)", con->historyFile));
} else
dbminit (con->historyFile);
dbzwritethrough (1);
return TRUE;
}
EXPORT char *hisGetDbz (char *msgid)
/* [<][>][^][v][top][bottom][index][help] */
{
#define HIS_BLK_SIZE 512
static char *buf;
static char *ret;
long offset;
int cc;
int len;
datum key;
datum val;
char *p, *p2;
if (!hisOpen ())
return FALSE;
hisSetkey (msgid, &key);
val = dbzfetch (key);
if (!val.dptr)
return NULL;
Stats->historyFetches++;
memcpy ((char *) &offset, val.dptr, sizeof offset);
offset -= HEADER_LEN;
if (lseek (his_fd, offset, SEEK_SET) != offset)
{
loge (("couldn't lseek to %ld in file '%s'", offset, con->historyFile));
return NULL;
}
if (!buf)
buf = (char *) Smalloc (HIS_BLK_SIZE + 1);
if ((cc = read (his_fd, buf, HIS_BLK_SIZE)) <= 0)
{
loge (("couldn't read history data key %s from %s", msgid, con->historyFile));
return NULL;
}
buf[cc] = '\0';
if (!(p = strchr (buf, ' ')) || (len = strToi (buf)) < HEADER_LEN)
{
logw (("malformed history data returned from key %s", msgid));
return NULL;
}
if (len > cc)
{
int n;
buf = Srealloc (buf, len);
if ((n = read (his_fd, buf + cc, len - cc)) <= 0)
{
loge (("couldn't read history data key %s from %s", msgid, con->historyFile));
return NULL;
}
buf[cc + n] = '\0';
}
p = strchr (buf, '\n');
if (!p || !(p2 = strchr (p + 1, '\n')))
{
logw (("malformed data '%.128s' in '%.128s'", buf, con->historyFile));
return NULL;
}
*p2 = '\0';
if (ret)
free (ret);
ret = Sstrdup (p + 1);
Stats->msgidFromCache++;
Stats->msgidFromCacheBytes+=len;
return ret;
}
EXPORT char *hisGet (char *msgid)
/* [<][>][^][v][top][bottom][index][help] */
{
return (Task->ti_state !=nc_oneshot) ? hisGetIPC (msgid) : hisGetDbz (msgid);
}
EXPORT bool hisAddDbz (char *msgid, char *path)
/* [<][>][^][v][top][bottom][index][help] */
{
datum keydat, val;
long offset;
int len;
char *buf;
if (!hisOpen ())
return FALSE;
hisSetkey (msgid, &keydat);
len = HEADER_LEN + strlen (keydat.dptr) + 1 + strlen (path) + 1;
if (len >= 10000)
{
logw (("record length for <%s> %.256s %d>=10000", msgid, path, len));
return FALSE;
}
buf = (char *) Smalloc (len + 1);
sprintf (buf, "%-*d %.*s\n%.255s\n", HEADER_LEN - 1, len, MAX_MSGID, keydat.dptr, path);
offset = lseek (his_fd, 0, SEEK_END);
Stats->historySize = offset;
if (offset == -1)
{
lockun (his_fd);
close (his_fd);
his_fd = -1;
loge (("couldn't lseek '%s' to %ld", con->historyFile, offset));
free (buf);
return FALSE;
}
if (write (his_fd, buf, len) != len)
{
lockun (his_fd);
close (his_fd);
his_fd = -1;
loge (("error during write to '%s'", con->historyFile));
free (buf);
return FALSE;
}
offset += HEADER_LEN;
val.dptr = (char *) &offset;
val.dsize = sizeof offset;
if (store (keydat, val) == -1)
{
logw (("couldn't dbzstore(\"%s\")", msgid));
free (buf);
return FALSE;
}
free (buf);
Stats->historyStores++;
Stats->historyStoresBytes+=len;
return TRUE;
}
EXPORT bool hisAdd (char *msgid, char *path)
/* [<][>][^][v][top][bottom][index][help] */
{
return (Task->ti_state !=nc_oneshot)? hisAddIPC (msgid, path) : hisAddDbz (msgid, path);
}
static char *hisPruneDbz (int newsize)
/* [<][>][^][v][top][bottom][index][help] */
{
FILE *r, *w;
char buf[10000];
char buf2[10000];
int len;
int offset;
datum val, key;
if (chdir (con->cacheDir)==-1)
{
loge (("couldn't chdir(\"%s\") for history prune", con->cacheDir));
}
if (!(r = fopen (con->historyFile, "r")))
return con->historyFile;
if (!(w = fopen (con->historyFile, "r+")))
return con->historyFile;
dbzincore (1);
if (dbzagain (con->historyFile, con->historyFile) < 0)
return "couldn't dbzagain()";
fseek (r, -newsize, SEEK_END);
if (!fgets (buf, sizeof buf, r))
return "couldn't fgets() after initial fseek()";
/* get to the start of a new line */
if (!fgets (buf, sizeof buf, r))
return "second fgets()";
/* are we on the key or the record? */
if (!isdigit (*buf) || sscanf (buf, "%d ", &len) != 1)
{
if (!fgets (buf, sizeof buf, r))
return "couldn't fgets() initial record";
if (!isdigit (*buf) || sscanf (buf, "%d ", &len) != 1)
return "malformed record in history";
}
do
{
if (!fgets (buf2, sizeof buf2, r))
return "couldn't fgets() record";
offset = ftell (w) + HEADER_LEN;
val.dsize = sizeof offset;
val.dptr = (char *) &offset;
/* we don't need to have the key in the base file for
store() -- unlike fetch() */
if (fputs (buf, w) == EOF)
return "couldn't fputs() key";
if (fputs (buf2, w) == EOF)
return "couldn't fputs() record";
key.dptr = buf + HEADER_LEN;
key.dsize = strlen (key.dptr) - 1;
if (store (key, val) < 0)
{
logw (("return couldn't store() %s", buf));
continue;
}
}
while (fgets (buf, sizeof buf, r));
fflush (w);
offset = ftell (w);
if (ferror (r) || fclose (w) != 0)
return con->historyFile;
if (ftruncate (his_fd, offset) < 0)
return "unable to ftruncate() history";
fclose (r);
dbzsync ();
return NULL;
}
EXPORT bool hisPrune ()
/* [<][>][^][v][top][bottom][index][help] */
{
struct stat st;
char *msg;
settaskinfo ("pruning history database");
log (("pruning history database"));
if (chdir(con->cacheDir)<0 || !hisOpen () || stat (con->historyFile, &st) < 0)
{
loge (("fatal error: couldn't open/stat %s", con->historyFile));
retire_vm_proc (1);
}
if (st.st_size < con->hisHighWater)
return TRUE;
dbmclose ();
if (lockex (his_fd) < 0)
msg = "couldn't lockex()";
else
{
msg = hisPruneDbz (con->hisLowWater);
lockun (his_fd);
}
if (msg)
{
loge (("couldn't prune history (%s)", msg));
retire_vm_proc (1); /* seriously suffering */
}
return TRUE; /* hisPruneDbz leaves con->historyFile open */
}