cks@white.toronto.edu (Chris Siebenmann) (07/31/89)
Some cautions: this was written on a 2.2 system. It should work on a 3.x system (as long as DEC hasn't changed the format of the error log file), and maybe even a DECStation 3100. It only prints out the contents of ASCII messages; other types just have their types printed out. Extensions to handle more types (in one-line messages) or a manual page are welcome; please send them to me and I'll make up a new release. The current "documentation" for the program is at the top of the source code. [Don't forget to zap my signature at the end!] #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # If this archive is complete, you will see the following message at the end: # "End of shell archive." # # Contents: # ereport.c Makefile # # Wrapped by cks@snow.white on Sun Jul 30 21:49:59 1989 # PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f ereport.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"ereport.c\" else echo shar: Extracting \"ereport.c\" \(10225 characters\) sed "s/^X//" >ereport.c <<'END_OF_ereport.c' X/* X * Report errors in an intelligable shorthand fashion, unlike <CENSORED> X * uerf. X * X * Options: X * -f <file> - pick a different file than X * /usr/adm/syserr/syserr.<hostname> X * -F - go forward, not in reverse X * -h - never print host name X * -H - don't pring host name if it hasn't changed since X * the last record. X * -t <secs> - times this close to each other are considered X * the same (defaults to 0). X * X * That's it. The report has date, host, and the ascii message; X * things without ascii messages have their type logged. The output X * format was chosen over several iterations to look good. If neither the X * time nor the hostname has changed, no line with date + host are X * printed, just the ascii message. X * X * By: Chris Siebenmann (cks@white.toronto.edu), modeled vague X * after what uerf -R -o terse gives you. X * X * Last edited: Fri Jul 28 00:24:30 1989 by cks (Chris Siebenmann) on sneezy.white X */ X X#include <stdio.h> X#include <time.h> X#include <sys/types.h> X#include <sys/errlog.h> X#include <string.h> X Xextern void perror(), exit(); Xextern int fprintf(), printf(), getopt(), fread(), fseek(), fclose(); Xextern int gethostname(), atoi(); X X/* X * Error records are highly peculiar. An error record is composed of four X * parts: X * X * - a struct el_rhdr X * - a struct el_sub_id X * - a per-type information structure (note that these can sometimes X * exceed the normal size limits on their structures; in X * particular, the ascii messages from startup can be larger than X * 256 bytes). X * - a four-byte unique (well, hopefully unique) end-of-record X * marker, namely "%~<^". X */ Xtypedef struct { X struct el_rhdr elHdr; /* how long, etc, this is. */ X struct el_sub_id elSubId; /* type information. */ X} errorHeader; X Xextern char *optarg; X Xint printHname = 1; /* print host name. */ Xint allwaysHname = 1; /* always print host name, X even if it doesn't change. */ Xint goForward = 0; /* Go forward instead of backwards X through logs. */ X Xint timeDelta = 0; /* If two times are this close X together, don't print a new X time note. */ X#define ABS(x) ((x) > 0 ? (x) : -(x)) X X X#define BIGNAMELEN 256 X Xvoid findBackwards(), dumpRec(); X Xvoid Xmain(argc, argv) Xint argc; Xchar **argv; X{ X char initial_fname[BIGNAMELEN], namebuf[BIGNAMELEN]; X char *filename; X FILE *fp; X long curpos; X int c, errflag = 0; X errorHeader elHead; X X /* X * We set filename initially.. X */ X if (gethostname(namebuf, BIGNAMELEN) < 0) { X perror("gethostname"); X exit(1); X } X (void) strcat(strcpy(initial_fname, "/usr/adm/syserr/syserr."), X namebuf); X filename = initial_fname; X X while ((c = getopt(argc, argv, "f:hHFt:")) != EOF) { X switch (c) { X case 't': X timeDelta = atoi(optarg); X break; X case 'f': X filename = optarg; X break; X case 'h': X printHname = 0; X break; X case 'H': X allwaysHname = 0; X break; X case 'F': X goForward = 1; X break; X default: X errflag ++; X break; X } X } X X if (errflag) { X (void) fprintf(stderr, "usage: %s [hHF] [-f filename]\n", X argv[0]); X exit(1); X } X X /* X * Open the file, and determine the initial starting position. X */ X fp = fopen(filename, "r"); X if (fp == NULL) { X perror(filename); X exit(1); X } X X if (goForward) { X /* X * Proceed forward through the file, dumping interesting X * information. X */ X curpos = 0; X while (!ferror(fp) && !feof(fp)) { X if (fread((char *) &elHead, sizeof(elHead), X 1, fp) != 0) { X curpos += elHead.elHdr.rhdr_reclen; X dumpRec(&elHead, fp); X if (fseek(fp, curpos, 0) != 0) { X perror("fseek"); X exit(1); X } X } X } X if (ferror(fp)) { X perror("reading file"); X exit(1); X } X } X else { X /* X * Going backwards is somewhat more complicated. We start X * at the end, and look backwards for the previous X * record's trailer markers, and read forward from there. X */ X if (fseek(fp, -5L, 2) != 0) { /* jump to character before the X last record's trailer. */ X perror("initial backseek"); X exit(1); X } X while (!ferror(fp) && ftell(fp) > 0) { X findBackwards(fp); X curpos = ftell(fp); X if (fread((char *) &elHead, sizeof(elHead), X 1, fp) == 0 && ferror(fp)) { X perror("reading file"); X exit(1); X } X dumpRec(&elHead, fp); X (void) fseek(fp, curpos-4, 0); X } X } X X /* X * Clean up and exit. X */ X (void) fclose(fp); X} X X/* X * Go from error file type to an ascii label for that type. X */ X/* 100-1xx error labels. */ Xchar *errors100[] = { X "Machine check", "Memory crd/rds", "Disk error", "Tape error", X "Device controller error", "Adapter error", "Bus error", X "Stray interrupt", "Async write error", "Panic exception/fault", X "8800 emm exception", "console timeout entry", "stack dump", X "ka650 error & status regs", X}; X Xchar * XerrorName(code) Xu_short code; X{ X if (code >= 100 && code <= 113) /* blech. magic numbers. */ X return errors100[code - 100]; X X switch (code) { X case ELSW_PNC: X return "panic (bug check)"; X case ELMSGT_INFO: X return "ascii message"; X case ELMSGT_SNAP8600: X return "8600 snapshot taken"; X case ELMSGT_SU: X return "Startup message"; X case ELMSGT_SD: X return "Shutdown message"; X case ELMSGT_TIM: X return "Time stamp"; X default: X return "Unknown code"; X } X /*NOTREACHED*/ X} X X/* X * Print a possibly multi-line message with each line indented. X */ Xvoid XprintMultiLine(str) Xregister char *str; X{ X register char *curChar, *lineStart; X int first; X X for (curChar = lineStart = str, first = 1; *curChar != 0; X curChar ++) { X if (*curChar == '\n' ) { X *curChar = 0; X if (strlen(lineStart) > 0 || !first) { X (void) printf("\t%s\n", lineStart); X first = 0; X } X lineStart = curChar + 1; X } X } X if (strlen(lineStart) > 0) X (void) printf("\t%s\n", lineStart); X} X X/* X * Dump out an individual error record. When this routine is called, the X * file is positioned right at the per-type record; individual bits are X * responsible for reading in anything necessary. Works hard (in a X * complicated way) to only print time and hostname when they change. X */ Xvoid XdumpRec(erpt, fp) XerrorHeader *erpt; XFILE *fp; X{ X char tstring[30]; X short msg_len; /* length */ X char msg_asc[8096]; /* a very large buffer. */ X struct el_pnc panicRec; X char *msg_start; X /* used to handle complicated conditions for printing stuff. */ X int printTime, printHostname; X static char hname[13]; X static time_t last_time; X long rtime, delta; X X /* X * Get the time it happened in ASCII. X */ X rtime = erpt->elHdr.rhdr_time; X (void) strcpy(tstring, ctime(&rtime)); X tstring[24] = 0; /* ctime()'s format is guaranteed */ X X /* X * Print the leading host & time indication X */ X delta = last_time - erpt->elHdr.rhdr_time; X if (ABS(delta) > timeDelta) { X last_time = erpt->elHdr.rhdr_time; X printTime = 1; X } X else X printTime = 0; X X if (printHname && strncmp(hname, erpt->elHdr.rhdr_hname, 12) != 0) X printHostname = 1; X else X printHostname = 0; X X /* time is printed if either time or hostname change. */ X if (printTime || printHostname) X (void) printf("%s", tstring); X /* hostname is only printed if it changes. */ X if (printHostname || (printTime && allwaysHname && printHname)) { X (void) strncpy(hname, erpt->elHdr.rhdr_hname, 12); X hname[12] = 0; X (void) printf(" (%s)", hname); X printHostname = 1; X } X /* if either time or hostname was printed, print a CR to end them. */ X if (printTime || printHostname) X (void) printf("\n"); X X /* X * Now, check the type... X */ X switch (erpt->elSubId.subid_class) { X case ELMSGT_INFO: X case ELMSGT_SU: X case ELMSGT_SD: X if (fread((char *) &msg_len, sizeof(msg_len), 1, fp) == 0) { X perror("reading length"); X exit(1); X } X if (erpt->elSubId.subid_class == ELMSGT_SD) { X (void) strcpy(msg_asc, "shutdown: "); X msg_start = strlen(msg_asc) + msg_asc; X } X else X msg_start = msg_asc; X X if (fread(msg_start, msg_len * sizeof(char), 1, fp) == 0) { X perror("reading ascii"); X exit(1); X } X printMultiLine(msg_asc); X break; X X case ELSW_PNC: X if (fread((char *) &panicRec, sizeof(panicRec), 1, fp) == 0) { X perror("reading panic"); X exit(1); X } X (void) strcpy(msg_asc, "panic: "); X (void) strcat(msg_asc, panicRec.pnc_asc); X printMultiLine(msg_asc); X break; X X default: X (void) strcpy(msg_asc, "binary record: "); X (void) strcat(msg_asc, errorName(erpt->elSubId.subid_class)); X printMultiLine(msg_asc); X break; X } X} X X/* X * Seek backwards in the file (starting just after a trailer) for a X * trailer, and if found, leave the file positioned just after it. For X * efficienty, reads in a large buffer into memory and manually searches X * backwards through it. X */ Xchar *trailerVar = trailer; X#define trailerComp(var) ((var)[0] == trailerVar[0] && \ X (var)[1] == trailerVar[1] && \ X (var)[2] == trailerVar[2] && \ X (var)[3] == trailerVar[3]) X X#define BIG_BUFFER 2048 Xvoid XfindBackwards(fp) XFILE *fp; X{ X char buffer[BIG_BUFFER]; X register char *bufferP; X long cur_pos, new_delta; X int buffer_size; X X /* Load a large buffer in. */ X cur_pos = ftell(fp); X if (cur_pos > BIG_BUFFER) { X if (fseek(fp, (long) -BIG_BUFFER, 1) != 0) { X perror("fseek"); X exit(1); X } X buffer_size = BIG_BUFFER; X } X else { X /* Not a full buffer left. */ X buffer_size = cur_pos; X if (cur_pos == 0) { X (void) fprintf(stderr, "not supposed to happen!\n"); X exit(1); X } X if (fseek(fp, 0L, 0) != 0) { X perror("fseek"); X exit(1); X } X } X if (fread(buffer, buffer_size * sizeof(char), 1, fp) == 0 && X ferror(fp)) { X perror("readin"); X exit(1); X } X X /* Scan backwards through the buffer looking for the trailer. */ X for (bufferP = buffer + buffer_size - 4 - 1; X bufferP > buffer; X bufferP --) { X if (trailerComp(bufferP)) { X /* Found a match. We must seek to the match X position plus four. */ X new_delta = buffer_size - (bufferP - buffer) - 4; X if (fseek(fp, -new_delta, 1) != 0) { X perror("fseek"); X exit(1); X } X return; X } X } X X /* Odd, definetly odd. We have a really long record here. We must X seek backwards to its start, and try again. */ X if (fseek(fp, (long) -buffer_size, 1) != 0) { X perror("fseek"); X exit(1); X } X if (ftell(fp) == 0) X return; X findBackwards(fp); X} END_OF_ereport.c if test 10225 -ne `wc -c <ereport.c`; then echo shar: \"ereport.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f Makefile -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"Makefile\" else echo shar: Extracting \"Makefile\" \(147 characters\) sed "s/^X//" >Makefile <<'END_OF_Makefile' XCC = gcc XCFLAGS = -O -Wall X Xereport: ereport.c X $(CC) $(CFLAGS) -o ereport ereport.c X Xlint: X lint -bh ereport.c X Xclean: X Xrealclean: X rm -f ereport END_OF_Makefile if test 147 -ne `wc -c <Makefile`; then echo shar: \"Makefile\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of shell archive. exit 0 -- "I shall clasp my hands together and bow to the corners of the world." Number Ten Ox, "Bridge of Birds" Chris Siebenmann ...!utgpu!{ncrcan,ontmoh!moore}!ziebmef!cks cks@white.toronto.edu or ...!utgpu!{,csri!}cks