/* * 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 VerifyHistory = 0; int NextOpt = 0; int EntriesOpt = 0; int ShowProgress = 0; char *FindHash = NULL; time_t MaxAge = -1; 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-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 stdout\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-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 'n': NextOpt = 1; break; case 'o': OldOpt = 1; break; case 'p': ShowProgress = 1; break; case 'r': DOpts.RememberDays = strtol(*ptr ? ptr : av[++i], NULL, 0); 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; /* 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"); } rsize = hh.henSize; hsize = hh.hashSize; lseek(fd, hh.headSize, 0); } 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, int32 off, int rsize) { int maxChainLen = 1000; while (off) { History h = { 0 }; lseek(fd, (off_t)(uint32)off, 0); if (read(fd, &h, rsize) != rsize) { printf("read error @ %d", off); break; } else { printf(" [%u,%u %08x.%08x.%04x gm=%d ex=%d boff=%d bsize=%d F=%s part=%d]", (unsigned int)off, (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); } } off = h.next; if (--maxChainLen == 0) { printf(" MAXIMUM CHAIN LENGTH EXCEEDED!"); break; } } if (off) printf(" offset error: %d", off); printf("\n"); } void DumpTrace(int fd, int hsize, int rsize) { int i; int32 *Ary = calloc(hsize, sizeof(int32)); if (read(fd, Ary, hsize * sizeof(int32)) != hsize * sizeof(int32)) { printf("Unable to read hash table array\n"); exit(1); } for (i = 0; i < hsize; ++i) { int32 off; if ((off = Ary[i]) != 0) { printf("Index %d: ", i); PrintTrace(fd, off, rsize); } } } void DumpChain(int fd, int hsize, int rsize, hash_t *hv) { int32 *Ary = calloc(hsize, sizeof(int32)); int32 off; if (read(fd, Ary, hsize * sizeof(int32)) != hsize * sizeof(int32)) { printf("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.RememberDays + 1) * 24 * 60; uint32 gmt = time(NULL) / 60; History th; off_t seekpos = 0; int totalentries = 0; int count = 0; 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 seekpos = lseek(fd, hsize * sizeof(int32), 1); if (EntriesOpt || ShowProgress) { struct stat st; if (fstat(fd, &st) == -1) { fprintf(stderr, "Unable to fstat history: %s\n", strerror(errno)); exit(1); } totalentries = (int)(st.st_size - seekpos) / rsize; if (EntriesOpt) printf("ENTRIES %d\n", totalentries); } fprintf(stderr, "@%d %d records\n", (int)lseek(fd, 0L, 1), totalentries); top: while ((n = read(fd, hbuf, hlen)) > 0) { int i; n /= rsize; for (i = 0; i < n; ++i) { History *h = (History *)(hbuf + i * rsize); if ((i & 255) == 0) gmt = time(NULL) / 60; /* * A gmt of zero is an empty entry - don't print it out unless * we are verifying history */ if (h->gmt == 0 && !VerifyHistory) 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) continue; } if (DNPOpt && H_EXPIRED(h->exp)) { int32 dgmt = gmt - h->gmt; /* DELTA MINUTES */ if (dgmt < -rememberMins || dgmt > rememberMins) continue; } if (VerifyHistory && HistoryLookupByHash(h->hv, &th) == 0) 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"); if (LineModeOpt) fflush(stdout); if (totalentries > 0 && (count++ % 8192) == 0) { fprintf(stderr, "%d/%d\r", count, totalentries); } } } if (n < 0) fprintf(stderr, "Error reading file: %s\n", strerror(errno)); if (FOpt) { sleep(1); goto top; } if (totalentries > 0) fprintf(stderr, "%d/%d\n", count, totalentries); printf(".\n"); free(hbuf); }