[comp.sources.wanted] uureport.shar

steveb@shade.ann-arbor.mi.us (Steve Barber) (08/23/90)

In article <75@dlss2.UUCP> james@dlss2.UUCP (James Cummings) writes:
> In article <32912@cup.portal.com> thad@cup.portal.com (Thad P Floryan) writes:
> >elliot@alfred.UUCP (Elliot Dierksen) in <1990Aug16.005046.6908@alfred.UUCP>
> >writes:
> >
> >	A while back, there was a program that came accros the Net called
> >	traffic.c or traffic2.c. It read /usr/spool/uucp/.Admin/xferstats and
> >	reported on how much data was moved via UUCP & some average CPS info.
> 
> 	I believe the animal you're looking for is "uutraf".  It was written
> by Greg Hackney.  You "should be able" to reach him at address 
> 	     hack@texbell.swbt.com

Well, this seems to do what was asked for.  I wrote it up about a year and a
half ago but never bothered to post it.  It's called uureport, and it's known
to run on 3B1's.  It probably won't run on BSD systems without a few changes.
The only docs are in the header comments, but it's straightforward.  You
should be able to just compile and go.  There's no Makefile; just do

	gcc -O -s uureport.c -o uureport

or whatever.  I haven't tried it with cc, but I have no reason to believe it
won't work.  [Yah, right!]

Usage:
	uureport [-l] [-f filename]

    The -l option gives a long report with per-user-per-system stats as well.
    The -f option lets you use a file other than the default
	/usr/spool/uucp/.Admin/xferstats.  I use this feature in my weekly
	uucp cleanup scripts - all the daily xferstats are combined and at the
	end of the week I run uureport on the combined file to get stats for
	the whole week.

The one known "bug" that I know of occurs when a username or systemname is
8 characters (or more) long - this just messes up the tabs a little.  Should
be an easy change to the printf format to fix it, but I never bothered since
I rarely run into it.

Here's the shar file:
------------ cut here ---------------
#! /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
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  uureport.c
# Wrapped by steveb@shade on Wed Aug 22 20:11:13 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'uureport.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'uureport.c'\"
else
echo shar: Extracting \"'uureport.c'\" \(11161 characters\)
sed "s/^X//" >'uureport.c' <<'END_OF_FILE'
X/****************************************************************************
X ****************************************************************************
X
X uureport - Report UUCP transfer statistics for HDB UUCP
X Version 1.0, 2/20/89
X
X COPYRIGHT NOTICE:
X =================
X Copyright (C) 1989 by Stephen W. Barber
X This program may be freely distributed as long as the following conditions
X are met:
X a)	This copyright and distribution notice must be included unmodified.
X b)	Source code must be available with any binary distribution.
X c)	Any and all changes or localizations you make must be documented in
X	the change log below.
X d)	Any and all changes you make to the code must be within compiler
X	conditionals (#ifdef .. #endif), thus reversible, so a user can always
X	get back to the "as-distributed" base configuration.
X e)	Bug reports and fixes, portability concerns, and additional features
X	should be mailed to the author if you want to see them included in
X	the next official release.  The author's address is
X	steveb@shade.Ann-Arbor.MI.US
X
X ****************************************************************************
X ****************************************************************************/
X
X
X/****************************************************************************
X ****************************************************************************
X
X Usage:  uureport [-l] [-f filename]
X
X Reads in the HDB 'xferstats' file and compile a list of totals and averages.
X xferstats format (no line breaks):
X
X clip!steveb M (2/19-17:13:07) (C,6211,2) [tty002] -> 153 / 3.966 secs, \
X	38 bytes/sec
X
X sysname!user [MS] (date-time) (C,PID,TN) [DEV] DIR BYTES [/] SECS, TR bytes/sec
X
X sysname=	system name transacting with
X user=		user id of the user on the originating system that requested
X		the transaction
X [MS]=		either an M if the local system called the remote, or an S
X		if the remote called us.
X (date-time)=	The date and time the transaction completed
X C=		???
X PID=		The process ID of the uucico process that made the transaction.
X		Theoretically you could figure out the number of [successful]
X		calls (master+slave) by finding out how many uniq pid's there
X		are for each system, but uureport doesn't do this.
X TN=		Transaction number - starts at 1 at the beginning of each call.
X DEV=		Device used
X BYTES=		A long integer showing the number of bytes in the file.
X DIR=		The string "->" if the file was sent to the remote system, or
X		"<-" if the remote sent it to us.
X [/]=		A slash character
X SECS=		A floating point value of the number of seconds it took to
X		complete the transaction.
X TR=		An integer reported transfer rate in bytes/sec for this file.
X bytes/sec=	The string, literally matched
X
X uureport only cares about sysname, user, DIR, BYTES, and SECS.  More
X statistics could be gathered from this file but this would cause uureport
X to lose its simplicity.  If you want more statistics generated, I recommend
X using a second pass through the input file, rather than trying to do it
X all in the same loop.  This code is not time-critical, and there's no
X point in destroying its readability.  It could also be made to check
X the log files for uucico, uux, uuxqt, etc. to report number of calls to a
X system, number of successful calls to a system, number of failed calls
X to a system, reasons for failed calls, etc.  But why bother?
X
X Typically this program will be run nightly, in the uudemon.cleanu script.
X This requires changes to uudemon.cleanu, as it is typically run weekly
X rather than daily.
X
X uureport will report totals and averages based on ALL entries in the file,
X so the xferstats file should be cleaned out every night immediately after
X uureport is run so that the daily stats are really daily.  I recommend
X keeping an xferstats.wk and rather than just deleting xferstats every night,
X copy it onto the end of the xferstats.wk file.  Then at the end of the
X week, run "uureport -l xferstats.wk", and mv xferstats.wk into .OLD.
X
X
X CHANGE LOG:
X ================================
X v1.0	 2/19/89 SWB	Version 1.0 created on an AT&T 3B1 System Vr2 (3.51)
X			Compiled with gcc 1.33
X
X
X ****************************************************************************
X ****************************************************************************/
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X#include <sys/utsname.h>
X
Xstatic char XFERSTATS[] = "/usr/spool/uucp/.Admin/xferstats";
X
Xtypedef enum { false=0, true } boolean;
X
Xtypedef struct orig
X{
X	char		user[16];
X	short		files_transfered;
X	long		bytes_transfered;
X	float		connect_secs;
X	struct orig *	next;
X} ORIGINATOR;
X
Xtypedef struct
X{
X	short		files_transfered;
X	long		bytes_transfered;
X	float		connect_secs;
X	ORIGINATOR *	users;
X} INFO;
X
Xtypedef struct syst
X{
X	char		sysname[16];
X	INFO		send;
X	INFO		receive;
X	struct syst *	next;
X} SYSTEM;
X
Xextern char *malloc();
X
XSYSTEM *SysHead;
X
X/*
X * Maintain a sorted linked list of ORIGINATORs.  If the user field matches
X * a record already in the list, the information is added to that within
X * that record, otherwise a new entry is created.
X */
Xvoid AddOriginator(head, user, bytes, secs)
XORIGINATOR **head;
Xchar *user;
Xlong bytes;
Xfloat secs;
X{
X	register ORIGINATOR *p, *q, *qp;
X	register int rv;
X
X	qp = (ORIGINATOR *)0;
X	q = *head;
X	rv = 1;		/* as long as it's not 0... */
X	while (q)
X	{
X		if ((rv = strncmp(user, q->user, sizeof(q->user))) >= 0)
X			break;
X		qp = q;
X		q = q->next;
X	}
X
X	if ((!q) || (rv))
X	{
X		if (!(p = (ORIGINATOR *)malloc(sizeof(ORIGINATOR))))
X			return;
X		(void) strncpy(p->user, user, sizeof(p->user));
X		p->files_transfered = 1;
X		p->bytes_transfered = bytes;
X		p->connect_secs = secs;
X		p->next = q;
X		if (!qp)
X			*head = p;
X		else
X			qp->next = p;
X	}
X	else	/* found a match */
X	{
X		q->files_transfered++;
X		q->bytes_transfered += bytes;
X		q->connect_secs += secs;
X	}
X}
X
X
X/*
X * Maintain a sorted linked list of SYSTEMs.  If the sysname field matches
X * a record already in the list, the information is added to that within
X * that record, otherwise a new entry is created.
X */
Xvoid AddSystem(head, sys, user, sender, bytes, secs)
XSYSTEM **head;
Xchar *sys;
Xchar *user;
Xshort sender;
Xlong bytes;
Xfloat secs;
X{
X	register SYSTEM *p, *q, *qp;
X	register INFO *ip;
X	register int rv;
X
X	qp = (SYSTEM *)0;
X	q = *head;
X	rv = 1;		/* as long as it's not 0... */
X	while (q)
X	{
X		if ((rv = strncmp(sys, q->sysname, sizeof(q->sysname))) >= 0)
X			break;
X		qp = q;
X		q = q->next;
X	}
X
X	if ((!q) || (rv))
X	{
X		if (!(p = (SYSTEM *)malloc(sizeof (SYSTEM))))
X			return;
X		(void) strncpy(p->sysname, sys, sizeof(p->sysname));
X		p->next = q;
X		p->send.users = p->receive.users = (ORIGINATOR *)0;
X		if (!qp)
X			*head = p;
X		else
X			qp->next = p;
X		if (sender)
X			ip = &(p->send);
X		else
X			ip = &(p->receive);
X		ip->files_transfered = 1;
X		ip->bytes_transfered = bytes;
X		ip->connect_secs = secs;
X		AddOriginator(&(ip->users), user, bytes, secs);
X	}
X	else	/* found a match */
X	{
X		if (sender)
X			ip = &(q->send);
X		else
X			ip = &(q->receive);
X		ip->files_transfered++;
X		ip->bytes_transfered += bytes;
X		ip->connect_secs += secs;
X		AddOriginator(&(ip->users), user, bytes, secs);
X	}
X}
X
X
X/*
X * float_to_hms - convert a floating point number of seconds into an
X * hours:minutes:seconds representation of shorts.
X */
Xvoid float_to_hms(ts, h, m, s)
Xfloat ts;
Xshort *h, *m, *s;
X{
X	register long secs;
X
X	ts += 0.5;
X	secs = (long) ts;
X	*h = secs / 3600;
X	secs %= 3600;
X	*m = secs / 60;
X	secs %= 60;
X	*s = secs;
X}
X
X
X/*
X * printusers - print a subreport of individual users
X */
Xvoid printusers(head)
XORIGINATOR *head;
X{
X	register ORIGINATOR *s;
X	short hr, mi, se;		/* hours, minutes, seconds */
X
X	for (s = head; s; s = s->next)
X	{
X		float_to_hms(s->connect_secs, &hr, &mi, &se);
X		(void) printf("\t%s\t%3d files,%8ld bytes,%4d:%02d:%02d modem time (hh:mm:ss)\n",
X			s->user,
X			s->files_transfered,
X			s->bytes_transfered,
X			hr, mi, se);
X	}
X	putchar('\n');
X}
X
X
X/*
X * printreport - print a full report of the info gathered
X */
Xvoid printreport(head, lflag)
XSYSTEM *head;
Xboolean lflag;		/* long flag */
X{
X	register SYSTEM *s;
X	short files, cum_files;
X	long bytes, cum_bytes;
X	float secs, cum_secs;
X	short hr, mi, se;		/* hours, minutes, seconds */
X	struct utsname uts;
X
X	cum_files = 0;
X	cum_bytes = 0;
X	cum_secs = 0.0;
X	(void) uname(&uts);
X	for (s = head; s; s = s->next)
X	{
X		files = s->send.files_transfered + s->receive.files_transfered;
X		cum_files += files;
X		bytes = s->send.bytes_transfered + s->receive.bytes_transfered;
X		cum_bytes += bytes;
X		secs  = s->send.connect_secs     + s->receive.connect_secs;
X		cum_secs += secs;
X		(void) printf("\nSYSTEM:\t%s\n", s->sysname);
X		float_to_hms(s->send.connect_secs, &hr, &mi, &se);
X		(void) printf("TO:\t\t%3d files,%8ld bytes,%4d:%02d:%02d modem time (hh:mm:ss)\n",
X			s->send.files_transfered,
X			s->send.bytes_transfered,
X			hr, mi, se);
X		if (lflag)
X			printusers(s->send.users);
X		float_to_hms(s->receive.connect_secs, &hr, &mi, &se);
X		(void) printf("FROM:\t\t%3d files,%8ld bytes,%4d:%02d:%02d modem time (hh:mm:ss)\n",
X			s->receive.files_transfered,
X			s->receive.bytes_transfered,
X			hr, mi, se);
X		if (lflag)
X			printusers(s->receive.users);
X		float_to_hms(secs, &hr, &mi, &se);
X		(void) printf("TOTAL:\t\t%3d files,%8ld bytes,%4d:%02d:%02d modem time (hh:mm:ss)\n",
X			files, bytes, hr, mi, se);
X		(void) printf("AVERAGE TRANSFER RATE FOR %s:\t%7.1f BYTES/SECOND\n",
X			s->sysname, (float) ((float) bytes / secs));
X	}
X
X	float_to_hms(cum_secs, &hr, &mi, &se);
X	(void) printf(
X"\n\nGRAND TOTALS FOR %s:\n\t\t%3d files,%8ld bytes,%4d:%02d:%02d modem time (hh:mm:ss)\n",
X		uts.nodename, cum_files, cum_bytes, hr, mi, se);
X}
X
X
X/*
X * usage - print usage and exit with an error code
X */
Xvoid usage(pn)
Xchar *pn;
X{
X	(void) fprintf(stderr, "Usage: %s [-l] [-f file]\n", pn);
X	exit(-1);
X}
X
X
X/*
X * MAIN PROGRAM ENTRY POINT
X */
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X	register char *ptr;
X	char *progname;
X	char *xferstats;
X	char line[160];
X	FILE *fp;
X	boolean lflag;
X	char user[16], sysuser[20], direction[4];
X	long bytes;
X	float secs;
X	int c;
X
X	/* For getopt(3) */
X	extern char *optarg;
X	extern int optind, getopt();
X
X	xferstats = XFERSTATS;
X	progname = argv[0];
X	SysHead = (SYSTEM *)0;
X	lflag = false;
X	while ((c = getopt(argc, argv, "lf:")) != EOF)
X	{
X		switch(c)
X		{
X			case 'l':	lflag = true;		break;
X			case 'f':	xferstats = optarg;	break;
X			default:	usage(progname);
X					/*NOTREACHED*/
X					break;
X		}
X	}
X	if (optind != argc)
X	{
X		usage(progname);
X		/*NOTREACHED*/
X	}
X	
X	if ((fp = fopen(xferstats, "r")) == NULL)
X	{
X		perror(xferstats);
X		exit(-2);
X		/*NOTREACHED*/
X	}
X
X	while (fgets(line, sizeof line, fp) != NULL)
X	{
X		(void) sscanf(line,
X			"%s %*1s %*s %*s %*s %s %ld %*1s %f %*s %*d %*s",
X			sysuser, direction, &bytes, &secs);
X		if (!(ptr = strchr(sysuser, '!')))
X		{
X			(void) fprintf(stderr,"%s: bad input file format\n",
X				progname);
X			exit(3);
X			/*NOTREACHED*/
X		}
X		*ptr++ = '\0';
X		(void) strcpy(user, ptr);
X		if (!strncmp(direction, "->", 2))
X			AddSystem(&SysHead, sysuser, user, 1, bytes, secs);
X		else
X			AddSystem(&SysHead, sysuser, user, 0, bytes, secs);
X	}
X	(void) fclose(fp);
X	printreport(SysHead, lflag);
X	exit(0);
X	/*NOTREACHED*/
X}
END_OF_FILE
if test 11161 -ne `wc -c <'uureport.c'`; then
    echo shar: \"'uureport.c'\" unpacked with wrong size!
fi
# end of 'uureport.c'
fi
echo shar: End of shell archive.
exit 0
-- 
--**-Steve Barber----steveb@shade.Ann-Arbor.MI.US----(cmode)-------------------