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)-------------------