/*
* DIDUMP.C Dump or trace a dhistory file
*
* (c)Copyright 1997, Matthew Dillon, All Rights Reserved. Refer to
* the COPYRIGHT file in the base directory of this distribution
* for specific rights granted.
*/
#include "defs.h"
/*
* OldHistory - versions <= 1.07
*
*/
typedef struct OldHistory {
uint32 next; /* next link */
uint32 gmt; /* gmt time in minutes */
hash_t hv; /* hash value */
uint16 iter; /* file id */
uint16 exp; /* hours relative to gmt minutes */
} OldHistory;
int TraceMode = 0;
int DNPOpt = 0;
int FOpt = 0;
int FCount = 0;
int OldOpt = 0;
int StartOff = 0;
int LineModeOpt = 0;
int VerboseOpt = 0;
int QuietOpt = 0;
int VerifyHistory = 0;
int NextOpt = 0;
int EntriesOpt = 0;
int EmptyOpt = 0;
int ShowProgress = 0;
int DumpHashOnly = 0;
char *FindHash = NULL;
time_t MaxAge = -1;
int HistoryVersion = 0;
uint32 HOffset = 0;
uint32 ExpireDropCount = 0;
uint32 ExpireKeepCount = 0;
uint32 MaxAgeCount = 0;
uint32 ZeroGmtCount = 0;
uint32 LookupCount = 0;
uint32 DumpedCount = 0;
void DumpTrace(int fd, int hsize, int rsize);
void DumpQuick(int fd, int hsize, int rsize);
void DumpChain(int fd, int hsize, int rsize, hash_t *hv);
void
Usage(void)
{
printf("Dump the history file entries to stdout.\n\n");
printf("didump [-e] [-f] [-H msgid|hash] [-h] [-l] [-n] [-o] [-p] [-r remember] [-t]\n");
printf("\t[-TN] [-v] [-x] [-C diablo.config] [-d[n]] [-V] [@offset] dhistory-file\n");
printf(" where:\n");
printf("\tdefault\t- quick dump\n");
printf("\t-e\t- also dump an ENTRIES line - useful for diload progress\n");
printf("\t-f\t- 'tail -f' the history file\n");
printf("\t-H\t- dump hash trace for specified Message-ID or hash\n");
printf("\t-h\t- display entries not found with a HistoryLookup\n");
printf("\t-l\t- line mode - flush output after every line\n");
printf("\t-m\t- dump msgid hashes only\n");
printf("\t-n\t- also dump the value of the 'next' pointer\n");
printf("\t-o\t- dump old-style (diablo V <= 1.07) history file\n");
printf("\t-p\t- show progress on stderr\n");
printf("\t-q\t- quiet - don't show stats\n");
printf("\t-rN\t- set rememberdays\n");
printf("\t-t\t- hash table trace (slow)\n");
printf("\t-TN\t- don't dump articles older than N seconds\n");
printf("\t-v\t- Include additional (synthesized) info\n");
printf("\t-x\t- do not dump records older than rememberdays old\n");
printf("\t-z\t- include entries with a gmt of zero in the dump\n");
printf("\t-Cfile\t- specify diablo.config to use\n");
printf("\t-d[n]\t- set debug [with optional level]\n");
printf("\t-V\t- print version and exit\n");
printf("\t@\t- specify the starting offset (in bytes)\n");
exit(1);
}
int
main(int ac, char **av)
{
int fd;
int i;
int hsize = 1024 * 1024;
int rsize = sizeof(History);
struct stat st;
char *fileName = NULL;
LoadDiabloConfig(ac, av);
for (i = 1; i < ac; ++i) {
char *ptr = av[i];
if (*ptr != '-') {
if (*ptr == '@') {
StartOff = strtol(ptr + 1, NULL, 0);
continue;
}
if (fileName) {
fprintf(stderr, "unexpected argument\n");
exit(1);
}
fileName = ptr;
continue;
}
ptr += 2;
switch(ptr[-1]) {
case 'e':
EntriesOpt = 1;
break;
case 'f':
FOpt = 1;
if (*ptr)
FCount = strtol(ptr, NULL, 0);
break;
case 'H':
FindHash = (*ptr ? ptr : av[++i]);
break;
case 'h':
VerifyHistory = 1;
break;
case 'l':
LineModeOpt = 1;
break;
case 'm':
DumpHashOnly = 1;
break;
case 'n':
NextOpt = 1;
break;
case 'o':
OldOpt = 1;
break;
case 'p':
ShowProgress = 1;
break;
case 'q':
QuietOpt = 1;
break;
case 'r':
if (!*ptr)
ptr = av[++i];
DOpts.RememberSecs = TimeSpec(ptr, "d");
if (DOpts.RememberSecs == -1)
Usage();
break;
case 'T':
MaxAge = btimetol(*ptr ? ptr : av[++i]);
break;
case 't':
TraceMode = 1;
break;
case 'v':
if (*ptr)
VerboseOpt = strtol(ptr, NULL, 0);
else
++VerboseOpt;
break;
case 'x':
DNPOpt = 1;
break;
case 'z':
EmptyOpt = 1;
break;
/* Common options */
case 'C': /* parsed by LoadDiabloConfig */
if (*ptr == 0)
++i;
break;
case 'd':
DebugOpt = 1;
if (*ptr)
DebugOpt = strtol(ptr, NULL, 0);
break;
case 'V':
PrintVersion();
break;
default:
if (isdigit((int)ptr[-1])) {
FCount = strtol(ptr - 1, NULL, 0);
} else {
fprintf(stderr, "illegal option: %s\n", ptr - 2);
Usage();
}
}
}
if (fileName == NULL) {
Usage();
}
if (VerifyHistory || FindHash != NULL)
HistoryOpen(fileName, HGF_READONLY);
if (VerboseOpt)
LoadSpoolCtl(0, 1);
if (OldOpt) {
char *paramName = malloc(strlen(fileName) + 32);
FILE *fi;
sprintf(paramName, "%s.param", fileName);
rsize = sizeof(OldHistory);
if ((fi = fopen(paramName, "r")) != NULL) {
char buf[256];
hsize = 0;
while (fgets(buf, sizeof(buf), fi) != NULL) {
if (buf[0] == 'H')
hsize = strtol(buf + 1, NULL, 0);
}
fclose(fi);
if (hsize == 0) {
fprintf(stderr, "dhistory parameter file error\n");
exit(1);
}
}
}
if (DNPOpt && TraceMode) {
fprintf(stderr, "-x only works for quick dumps\n");
exit(1);
}
if ((fd = open(fileName, O_RDONLY)) >= 0 && fstat(fd, &st) == 0) {
/*
* new style history file has a header
*/
if (OldOpt == 0) {
HistHead hh;
if (read(fd, &hh, sizeof(hh)) != sizeof(hh)) {
fprintf(stderr, "corrupted history file\n");
exit(1);
}
if (hh.hmagic != HMAGIC) {
fprintf(stderr, "corrupted history file\n");
exit(1);
}
if (hh.version > HVERSION) {
fprintf(stderr, "WARNING! Version mismatch file V%d, expecting V%d\n", hh.version, HVERSION);
fprintf(stderr, "dump may be invalid\n");
}
HistoryVersion = hh.version;
rsize = hh.henSize;
hsize = hh.hashSize;
HOffset = hh.headSize + hsize * sizeof(HistIndex);
lseek(fd, hh.headSize, 0);
}
if (!QuietOpt)
fprintf(stderr, "Dumping, hash table %d entries, record size %d\n",
hsize, rsize);
if (TraceMode) {
DumpTrace(fd, hsize, rsize);
} else if (FindHash != NULL) {
hash_t hv;
if (*FindHash == '<')
hv = hhash(FindHash);
else
sscanf(FindHash, "%x.%x", &hv.h1, &hv.h2);
DumpChain(fd, hsize, rsize, &hv);
} else {
DumpQuick(fd, hsize, rsize);
}
close(fd);
} else {
fprintf(stderr, "open failed\n");
exit(1);
}
return(0);
}
void
PrintTrace(int fd, HistIndex index, int rsize)
{
off_t off;
int maxChainLen = 1000;
while (index) {
History h = { 0 };
if (HistoryVersion > 1)
off = (off_t)HOffset + (off_t)index * sizeof(History);
else
off = index;
lseek(fd, off, 0);
if (read(fd, &h, rsize) != rsize) {
fprintf(stderr, "read error @ %d (%llu)", index, off);
break;
} else {
printf(" [%u,%u %08x.%08x.%04x gm=%d ex=%d boff=%d bsize=%d F=%s part=%d]",
index, (unsigned int)h.next,
h.hv.h1,
h.hv.h2,
(int)h.iter,
(int)h.gmt,
(int)h.exp,
(int)h.boffset,
(int)h.bsize,
((h.exp & EXPF_HEADONLY) ? "H" : ""),
(int)H_SPOOL(h.exp)
);
}
if (VerboseOpt) {
char buf[1024];
printf("\n");
ArticleFileName(buf, sizeof(buf), &h, ARTFILE_FILE);
printf("\tfile=\"%s\"", buf);
{
struct tm *tp;
time_t t;
char tbuf[64];
t = h.gmt * 60;
tp = localtime(&t);
strftime(tbuf, sizeof(tbuf), "%d-%b-%Y %H:%M:%S", tp);
printf("\ttime=%s\n", tbuf);
}
}
index = h.next;
if (--maxChainLen == 0) {
printf(" MAXIMUM CHAIN LENGTH EXCEEDED!");
break;
}
}
if (index)
printf(" offset error: %d", index);
printf("\n");
}
void
DumpTrace(int fd, int hsize, int rsize)
{
int i;
HistIndex *Ary = calloc(hsize, sizeof(HistIndex));
if (read(fd, Ary, hsize * sizeof(HistIndex)) != hsize * sizeof(HistIndex)) {
fprintf(stderr, "Unable to read hash table array\n");
exit(1);
}
for (i = 0; i < hsize; ++i) {
if (Ary[i] != 0) {
printf("Index %d: ", i);
PrintTrace(fd, Ary[i], rsize);
}
}
}
void
DumpChain(int fd, int hsize, int rsize, hash_t *hv)
{
uint32 *Ary = calloc(hsize, sizeof(uint32));
uint32 off;
if (read(fd, Ary, hsize * sizeof(uint32)) != hsize * sizeof(uint32)) {
fprintf(stderr, "Unable to read hash table array\n");
exit(1);
}
off = Ary[(hv->h1 ^ hv->h2) & (hsize - 1)];
PrintTrace(fd, off, rsize);
}
void
DumpQuick(int fd, int hsize, int rsize)
{
char *hbuf;
int n;
int hlen;
int rememberMins = DOpts.RememberSecs / 60;
uint32 gmt = time(NULL) / 60;
History th;
off_t seekpos = 0;
uint32 totalentries = 0;
uint32 count = 0;
int i;
History *h;
hlen = rsize * 4096;;
if ((hbuf = (char *)malloc(hlen)) == NULL) {
fprintf(stderr, "ERROR: Unable to malloc %dB for history cache (%s)\n",
hlen, strerror(errno));
return;
}
if (StartOff)
seekpos = lseek(fd, StartOff, 0);
else if (FOpt || FCount)
seekpos = lseek(fd, -FCount * rsize, 2);
else
if (HistoryVersion > 1)
seekpos = lseek(fd, (off_t)hsize * sizeof(HistIndex) + (off_t)rsize, 1);
else
seekpos = lseek(fd, (off_t)hsize * sizeof(HistIndex), 1);
{
struct stat st;
if (fstat(fd, &st) == -1) {
fprintf(stderr, "Unable to fstat history: %s\n", strerror(errno));
exit(1);
}
totalentries = (int)(((double)st.st_size - (double)seekpos) / (double)rsize);
if (EntriesOpt)
printf("ENTRIES %u\n", totalentries);
}
if (!QuietOpt)
fprintf(stderr, "@%llu %u records (%d bytes per record)\n",
lseek(fd, 0L, 1), totalentries, rsize);
top:
while ((n = read(fd, hbuf, hlen)) > 0) {
n /= rsize;
gmt = time(NULL) / 60;
for (i = 0; i < n; ++i) {
h = (History *)(hbuf + i * rsize);
if (ShowProgress && (count++ % 32768) == 0)
fprintf(stderr, "%u/%u (%d%%) \r", count, totalentries,
(int)((double)count * 100.0 / (double)totalentries));
/*
* A gmt of zero is an empty entry - don't print it out unless
* we are verifying history
*/
if (h->gmt == 0 && !VerifyHistory && !EmptyOpt) {
ZeroGmtCount++;
continue;
}
/*
* If we specified a maximum age, then deal with it
*/
if (MaxAge != -1) {
int32 dgmt = (gmt - h->gmt) * 60; /* Delta seconds */
if (dgmt > MaxAge) {
MaxAgeCount++;
continue;
}
}
if (DNPOpt && H_EXPIRED(h->exp)) {
int32 dgmt = gmt - h->gmt; /* DELTA MINUTES */
if (dgmt < -rememberMins || dgmt > rememberMins) {
ExpireDropCount++;
continue;
} else {
ExpireKeepCount++;
}
}
if (VerifyHistory && HistoryLookupByHash(h->hv, &th) == 0) {
LookupCount++;
continue;
}
if (DumpHashOnly) {
printf("%08x.%08x\n", h->hv.h1, h->hv.h2);
continue;
}
printf("DUMP %08x.%08x.%04x gm=%d ex=%-2d",
h->hv.h1,
h->hv.h2,
(int)h->iter,
(int)h->gmt,
(int)h->exp
);
if (!OldOpt) {
printf(" boff=%-7d bsize=%-6d", (int)h->boffset, (int)h->bsize);
if (VerboseOpt) {
char buf[1024];
ArticleFileName(buf, sizeof(buf), h, ARTFILE_FILE);
printf(" file=\"%s\"", buf);
}
printf(" flags=%s",
((h->exp & EXPF_HEADONLY) ? "H" : "")
);
if (NextOpt)
printf(" next=%d", h->next);
}
printf("\n");
DumpedCount++;
if (LineModeOpt)
fflush(stdout);
}
}
if (n < 0)
fprintf(stderr, "Error reading file: %s\n", strerror(errno));
if (FOpt) {
usleep(100000);
goto top;
}
if (!QuietOpt && count > 0)
fprintf(stderr, "%u/%u (%d%%) \n", count, totalentries,
(int)((double)count * 100.0 / (double)totalentries));
if (!QuietOpt)
fprintf(stderr, "%u entries dumped\n", DumpedCount);
if (VerifyHistory) {
fprintf(stderr, "%u entries found\n", LookupCount);
} else if (!QuietOpt) {
fprintf(stderr, "%u expired entries kept\n", ExpireKeepCount);
fprintf(stderr, "%u expired entries dropped\n", ExpireDropCount);
fprintf(stderr, "%u dropped as beyond max age\n", MaxAgeCount);
fprintf(stderr, "%u dropped with gmt=0\n", ZeroGmtCount);
}
if (!QuietOpt)
printf(".\n");
free(hbuf);
}
syntax highlighted by Code2HTML, v. 0.9.1