My error-log printing program
Chris Siebenmann
cks at white.toronto.edu
Mon Jul 31 16:28:58 AEST 1989
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 at 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 at 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 at white.toronto.edu or ...!utgpu!{,csri!}cks
More information about the Comp.unix.ultrix
mailing list