Smail3 log file parser

Jeff Beadles jeff at onion.pdx.com
Tue Dec 25 17:36:53 AEST 1990



[ Note:  Followup's are directed to alt.sources.d.  -Jeff]

{ This is the smaillog.READ file. }
Smail3 log file parser.  Copyright 1990, Jeff Beadles.  All rights reserved.
		jeff at onion.pdx.com or  uunet!onion.pdx.com!jeff

Permission is granted to freely copy this program as long as it is not sold.

Source MUST be provided upon request, and this message must be kept on
all copies of the program.


What this program will do is simple.  It will take the smail3 logfile
information, and produce a fairly human-readable report.  For example, it
will take the smail3 logfile (/usr/spool/smail/log/logfile on my system)
that looks like:

	12/24/90 00:33:55: [m0innd5-000NXgC] new msg: from root
	12/24/90 00:33:56: [m0innd5-000NXgC] jeff ... delivered
	12/24/90 01:24:26: [m0io4L3-000NUrC] new msg: from jeff at onion
	12/24/90 01:24:29: [m0io4L3-000NUrC] <foo at bar.har.edu> ... delivered

on stdin, and,  on stdout display:

	Processing 4 lines.  (Used 352 bytes.)
	00:33:56: root => jeff
	01:24:29: jeff at onion => <foo at bar.har.edu>

I use this as a part of my nightly system checker.  Anyway, it's something
that I originally wrote for sendmail.  I've changed the field definitions
to work with smail3.  Yea, this could be in AWK, but I was in a 'C' kinda
of mood at the time.

This is provided as-is, and you can use it at your own risk.  There's no man
page currently.  One of these days, I might even write one for it.
Until then, "Use the source."  I call it like:

% smaillog < /usr/spool/smail/log/OLD/logfile.0 | mail jeff

Have fun,
	-Jeff
-- 
Jeff Beadles		jeff at onion.pdx.com



#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -rw-r--r--  1 jeff         1486 Dec 24 22:35 smaillog.READ
# -r--r--r--  1 jeff         9628 Dec 24 22:23 smaillog.c
#
echo 'x - smaillog.READ'
if test -f smaillog.READ; then echo 'shar: not overwriting smaillog.READ'; else
sed 's/^X//' << '________This_Is_The_END________' > smaillog.READ
X
XSmail3 log file parser.  Copyright 1990, Jeff Beadles.  All rights reserved.
X		jeff at onion.pdx.com or  uunet!onion.pdx.com!jeff
X
XPermission is granted to freely copy this program as long as it is not sold.
X
XSource MUST be provided upon request, and this message must be kept on
Xall copies of the program.
X
X
XWhat this program will do is simple.  It will take the smail3 logfile
Xinformation, and produce a fairly human-readable report.  For example, it
Xwill take the smail3 logfile (/usr/spool/smail/log/logfile on my system)
Xthat looks like:
X
X	12/24/90 00:33:55: [m0innd5-000NXgC] new msg: from root
X	12/24/90 00:33:56: [m0innd5-000NXgC] jeff ... delivered
X	12/24/90 01:24:26: [m0io4L3-000NUrC] new msg: from jeff at onion
X	12/24/90 01:24:29: [m0io4L3-000NUrC] <foo at bar.har.edu> ... delivered
X
Xon stdin, and,  on stdout display:
X
X	Processing 4 lines.  (Used 352 bytes.)
X	00:33:56: root => jeff
X	01:24:29: jeff at onion => <foo at bar.har.edu>
X
XI use this as a part of my nightly system checker.  Anyway, it's something
Xthat I originally wrote for sendmail.  I've changed the field definitions
Xto work with smail3.  Yea, this could be in AWK, but I was in a 'C' kinda
Xof mood at the time.
X
XThis is provided as-is, and you can use it at your own risk.  There's no man
Xpage currently.  One of these days, I might even write one for it.
XUntil then, "Use the source."  I call it like:
X
X% smaillog < /usr/spool/smail/log/OLD/logfile.0 | mail jeff
X
XHave fun,
X	-Jeff
X-- 
XJeff Beadles		jeff at onion.pdx.com
________This_Is_The_END________
if test `wc -c < smaillog.READ` -ne 1486; then
	echo 'shar: smaillog.READ was damaged during transit (should have been 1486 bytes)'
fi
fi		; : end of overwriting check
echo 'x - smaillog.c'
if test -f smaillog.c; then echo 'shar: not overwriting smaillog.c'; else
sed 's/^X//' << '________This_Is_The_END________' > smaillog.c
X/*
X *
X * Parse smail3 log files.
X *
X * Copyright 1990, Jeff Beadles.  All rights reserved.
X *
X * Permission is granted to freely copy this program as long as it is
X * not sold.  Source MUST be provided upon request.
X *
X * This message must be kept on all copies of the program.
X *
X *	jeff at onion.pdx.com
X *  or  uunet!onion.pdx.com!jeff
X *
X *	$Header: smaillog.c,v 1.4 90/12/24 22:23:36 jeff Exp $
X */
X
X
X#include <stdio.h>
X#include <ctype.h>
X#include <strings.h>
X
Xvoid add_data();
Xstruct qlist *lookup();
Xchar *skipfields();
X
Xextern char *mspace();
Xlong int malloc_space = 0L;	/* Space malloc had to allocate */
X
X/*
X *	The qlist structure holds each unique "qid"'s entry.  It also
X *	points to the data structures.
X *	This is stored as a linked list with "qhead" pointing to the head
X *	of the list.
X */
Xstruct qlist {
X	char qid[32];
X	char date[24];
X	struct data *dp;
X	struct qlist *next;
X	};
X
X/*
X *	The data structure is the data information for the qlist structure.
X *	There can be (and most likely are) multiple data structs for each
X *	qlist structure.
X *	tfl == "to from list"
X */
X
Xstruct data {
X	char *tfl;
X	struct data *next;
X	};
X
X/*
X *	The structures look like this:
X *
X *
X *	(qhead)  qlist  -> data1 -> data2 -> data3
X *		  \|/
X *		 qlist  -> data1 -> data2
X *		  \|/
X *		 qlist  -> data1 -> data2 -> data3 -> data4 -> data5
X *		  \|/
X *		  NULL   (End of list )
X *
X */
X
Xstatic struct qlist *qhead;
Xextern char *fgets();
X
X/* ARGSUSED */
Xint
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X	struct data  *dtmp;
X	struct qlist *qtmp;
X
X	char linebuf[4096], buffer[4096];
X	char line_date[24];
X	char *fg, *cp;
X	int lines = 0;
X	char to[4096], from[4096];
X	char *source, *dest;
X	extern int strspn();
X	int itmp;
X	char *ctmp;
X
X	qhead =(struct qlist *)NULL;
X
X/*
X * Give a terse usage message if called with arguements.
X */
X	if ( argc != 1) {
X	   fprintf(stderr, "Error:  Usage:  %s < input > output\n",argv[0]);
X	   exit(1);
X	}
X/*
X * Read each line from stdin
X */
X	while ((fg=fgets(linebuf, sizeof(linebuf) - 1,stdin)) != (char *)NULL){
X		lines++;
X
X/*
X * Blast out the \n at the end... 
X */
X	ctmp = rindex(fg, '\n');
X	if (ctmp) {
X		*ctmp = '\0';
X	}
X
X/* 
X * Skip "01/01/90 "  This is meant to run daily.  If not, then remove this
X * section of code.
X */
X
X		if ((fg=skipfields(fg, 1)) == (char *)NULL) {
X			printf("Error:  Skip 1 on %s\n", linebuf);
X			continue;
X		}
X
X/*
X * Save the date, to be put in the qlist structure later
X */
X		(void)strncpy(line_date, fg, 8);
X/* 
X * Skip past the date field now, as we have it.
X */
X		if ((fg=skipfields(fg,1)) == (char *)NULL) {
X			printf("Error:  Skip 1 on %s\n", linebuf);
X			continue;
X		}
X/*
X * Catches "open_spool:" and "pid ##: smail daemon started" and other stuff.
X * This just copies them verbatium to the output file.
X * (At the head of the list)
X */
X		if ( *fg != '[' ) {
X			printf("Warning: %s\n", fg);
X			continue;
X		}
X/* 
X * Put qid in "buffer"  It looks like "[xxxxxx-xxxxxxx]".
X * This is the identifier that is used to index into the qlist struct.
X */
X		cp=buffer;
X		while(!isspace(*fg))
X			*cp++ = *fg++;
X		*cp = '\0';
X
X/*
X * buffer now contains the qid, so look it up in the qlist.
X * Lookup will always return success.  It will malloc a new area if this
X * is a new record.
X */
X		qtmp = lookup(buffer);
X		(void)strcpy(qtmp->date, line_date);
X
X/*
X * Now, skip past the qid, and on to the ftl (to/from list).
X * This is added to a data structure attached to the qlist.
X */
X		if ((fg=skipfields(fg,1)) == (char *)NULL) {
X			printf("Error:  Skip 1 on %s\n", linebuf);
X			continue;
X		}
X		add_data(qtmp, fg);
X	}
X/*
X * Note:  We are now out of the fgets() loop, and have all of the information
X * loaded in the struct.  Time to print some stats, and process the info.
X */
X
X	printf("Processing %d lines.  (Used %ld bytes.)\n",lines,malloc_space);
X
X/*
X * Time to start stepping thru the list.  Start at the head, and process each
X * qlist structure and it's data elements.
X */
X	qtmp = qhead;
X
X	while(qtmp) {
X		dtmp = qtmp->dp;
X		*from = *to = '\0';
X		while(dtmp) {
X
X/* This marks a "from" line.  If so, then strip off the first 14 characters,
X * and set the variable "from" to point to it.  It will automatically
X * append to the from line if needed.  It copies until it reaches the
X * end of line, or a ','.  The comma starts showing the origination qid.
X */
X			if ( !strncmp(dtmp->tfl, "new msg: from ", 14)) {
X				dest = from + strlen(from);
X				source = dtmp->tfl + 14;
X				while (*source && (*source != ',') )
X					*dest++ = *source++;
X				*dest = '\0';
X				dtmp = dtmp->next;
X				continue;
X			}
X/* This is used to see if this is a "to" line.  If so, then the LAST 14
X * characters will match the " ... delivered" string.  Do a little pointer
X * fun to find the right position in the string, and check it.  If this is
X * a "to" line, then append this to the "to" variable
X */
X			itmp = strlen(dtmp->tfl) - 14;
X			if ( itmp >= 0) {
X			   ctmp = dtmp->tfl;
X			   ctmp += itmp;
X				if (!strncmp(ctmp, " ... delivered",14)){
X					*ctmp = '\0';
X					dest = to + strlen(to);
X					source = dtmp->tfl ;
X					while (*source)
X						*dest++ = *source++;
X					*dest++ = ' ';
X					*dest   = '\0';
X					dtmp = dtmp->next;
X					continue;
X				}
X			}
X/* A general error trap, that should not be reached, but you know how that
X * goes... :-)
X */
X			printf("What is %s?\n", dtmp->tfl);
X			dtmp = dtmp->next;
X		}
X/* At this point, we have traveled thru one entire message's data.
X * Now, print the date, and the to and from information.  Try to figure
X * out if this is to, or from someone on this machine, to do the "=>" "<="
X * right.  If all else fails though, punt and guess.
X */
X		if ( *to && *from) {
X			printf("%s: ", qtmp->date);
X/*
X * kill trailing ',' and whitespace in the 'to' and 'from' fields
X */
X			cp = to + strlen(to) - 1;
X			while ((*cp) && (isspace(*cp) || (*cp == ',')))
X				*cp-- = '\0';
X
X			cp = from + strlen(from) - 1;
X			while ((*cp) && (isspace(*cp) || (*cp == ',')))
X				*cp-- = '\0';
X/*
X * All of that for this to display it
X */
X			printf("%s => %s\n", from, to);
X		}
X/*
X * Time to get the next element and process it.
X */
X		qtmp = qtmp->next;
X	}
X
X
X	exit(0);
X}
X
X/*
X * This routine will add the data (str) to the qlist member "q"
X */
X
Xvoid
Xadd_data(q, str)
Xstruct qlist *q;
Xchar *str;
X{
X	struct data *dtmp;
X	char *cp;
X
X/*
X * Save the string
X */
X	cp = mspace( (unsigned int)(strlen(str)));
X	(void)strcpy(cp, str);
X
X/*
X * If the data area is empty, then just load this into it.
X * and point the "next" entry to null.
X */
X	if (q->dp == (struct data *)NULL) {
X		q->dp = (struct data *)mspace(sizeof(struct data));
X		dtmp = q->dp;
X		dtmp->tfl = cp;
X		dtmp->next = (struct data *)NULL;
X		return;
X	}
X/*
X * otherwise, search and find the end of the data list for this qlist.
X * Then, add it to the end.
X */
X	dtmp = q->dp;
X	while(dtmp->next != (struct data *)NULL) {
X		dtmp= dtmp->next;
X	}
X/*
X * Get space for the new element, and add it to the end of the list.
X * Then, move to the new element, and add the data to it.
X * After that, set the "next" flag to null, to show that this is now
X * the end of the list.
X */
X	dtmp->next=(struct data *)mspace(sizeof (struct data));
X	dtmp = dtmp->next;
X	dtmp->tfl = cp;
X	dtmp->next = (struct data *)NULL;
X}
X
X
X/*
X * This routine will search the qlist for an element with the "key" of
X * the qid this is called with.  If not found, then it will automatically
X * allocate a new member, and initialize it properly
X */
Xstruct qlist *
Xlookup(str)
Xchar *str;
X{
X
X	struct qlist *qtmp = qhead;
X
X
X/*
X * Is this an empty list?  If so, then add to head.
X */
X	if ( qhead == (struct qlist *)NULL) {
X		qhead = (struct qlist *)mspace(sizeof (struct qlist));
X		qtmp = qhead;
X		(void)strcpy(qtmp->qid, str);
X		qtmp->dp = (struct data *)NULL;
X		qtmp->next = (struct qlist *) NULL;
X		return (qtmp);
X	}
X	while (1) {
X/*
X * Does this one match?  If so, then return it.
X */
X		if (!strcmp(qtmp->qid, str)) {
X			return(qtmp);
X		}
X/*
X * If at the end of the list, then add it.
X */
X		if (qtmp->next == (struct qlist *)NULL) {
X		    qtmp->next=(struct qlist *)mspace(sizeof(struct qlist));
X		    qtmp = qtmp->next;
X		    (void)strcpy(qtmp->qid, str);
X		    qtmp->dp = (struct data *)NULL;
X		    qtmp->next = (struct qlist *) NULL;
X		    return (qtmp);
X		}
X		qtmp = qtmp->next;
X	}
X}
X
X
X/*
X * This routine just skips past whitespace-delimited fields in a string.
X * IE:  If called as skipfields("a bb ccc dddd eeeee",2) it will return
X *  "ccc dddd eeeee"
X */
Xchar *
Xskipfields(str,count)
Xchar *str;
Xint count;
X
X{
X	if ( count < 1 )
X		return( (char *)NULL);
X
X	while (count--) {
X		while((*str) && !(isspace(*str)))
X			str++;
X		if (!*str)
X			return( (char *)NULL);
X		while((*str) && (isspace(*str)))
X			str++;
X		if (!*str)
X			return( (char *)NULL);
X	}
X	return(str);
X}
X
X/* This routine is a general "malloc" routine.  It does two additional
X * functions though.  One, is to keep track of how much space has been
X * malloc'ed, and the second is to automatically check for errors, and bail-
X * out if needed.  This prevents me from having to put the same code around
X * all of the malloc's.
X *
X * It also allocates "FUZZ" more bytes than were requested.  If you're 
X * having troubles with this, then change it to something >0.
X * (Don't make it less than 0 though, snicker :-)
X */
X
X#ifndef FUZZ
X#define FUZZ 0
X#endif /* !FUZZ */
X
Xchar *
Xmspace(len)
Xunsigned int len;
X{
X	static char *mp;
X	extern char *malloc();
X
X	len += FUZZ;
X
X	mp = malloc(len);
X
X	if ( mp) { 
X	  malloc_space += len;
X	  return(mp);
X	} else {
X	  fprintf(stderr, "Malloc:\tOut of space (Requested %d bytes)\n",len);
X	  fprintf(stderr, "\tAlready requested %ld bytes\n", malloc_space);
X	  exit(1);
X	}
X	/*NOTREACHED*/
X}
________This_Is_The_END________
if test `wc -c < smaillog.c` -ne 9628; then
	echo 'shar: smaillog.c was damaged during transit (should have been 9628 bytes)'
fi
fi		; : end of overwriting check
exit 0



More information about the Alt.sources mailing list