/* * 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); }