v06i093: pathrpt -- a reporting tool companion for pathalias
Brandon S. Allbery - comp.sources.misc
allbery at uunet.UU.NET
Mon Apr 24 05:31:48 AEST 1989
Posting-number: Volume 6, Issue 93
Submitted-by: david at dhw68k.cts.com (David H. Wolfskill)
Archive-name: pathrpt
I asked the folks in comp.mail.uucp if they would find a program such as
this useful or helpful; the overwhelming response was "Yes!" (along with
requests to mail copies). Anyway, I think comp.sources.misc would be a
better place for it than comp.sources.unix, mostly because it's not
terribly sophisticated -- it's really just a little filter to accompany
pathalias.
I've been using it for several months (in one form or another), and
I've sent copies off to some folks. The only reported problems I've
heard with its use came from a VMS site, and I didn't understand what
the problems were....
What this thing is: pathrpt makes a report based on the output of
pathalias. The report indicates how many of the paths (from the
pathalias output) start at each of your uucp neighbors. It also gives
some clue as to the lengths of those paths -- and it checks to see if
the uucp maps indicate you have a neighbor you don't think you have.
The above report(s) come out on stderr, so make it easier to use the
program for its other purpose in life -- taking the place of the "sed"
step in the pathproc distributed with smail. (The smail-format
pathalias output comes out on stdout.)
Folks who don't run pathalias probably have no use for this program.
Thanks,
david (Wed Apr 19 07:02:55 PDT 1989)
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
# pathrpt.c
# This archive created: Wed Apr 19 06:47:00 1989
export PATH; PATH=/bin:$PATH
echo shar: extracting "'pathrpt.c'" '(20748 characters)'
if test -f 'pathrpt.c'
then
echo shar: will not over-write existing file "'pathrpt.c'"
else
sed 's/^ X//' << \SHAR_EOF > 'pathrpt.c'
X/*
X *
X * This program is Copyright 1989, by David H. Wolfskill.
X *
X * Anyone may freely copy it and use it for any purpose, as long as:
X * 1) it is not sold, and
X * 2) no one else attempts to claim authorship of the program.
X * (In all humility and honesty, I can't imagine why anyone would want to do
X * either of the above, but human nature is a truly marvelous thing....)
X *
X * [First part of above is freely adapted from "rn," by Larry Wall.... dhw]
X *
X * Disclaimer: Although the author finds the program useful, there is no
X * guarantee that anyone else will find it useful for any given
X * purpose. Caveat Emptor, and all that.
X *
X * I cannot, of course, promise to fix things that some folks may think are
X * broken; nor can I promise to implement enhancements. However, I have a
X * strong desire to do what I can to help improve email, especially over dialup
X * 'phone lines; accordingly, it is my present intent to support this program
X * as a tool that can be helpful in some circumstances... as long as what it
X * reports isn't trusted too much.... :-)
X *
X * In all seriousness, comments & suggestions may be sent to me via
X *
X * InterNet: david at dhw68k.cts.com
X * uucp: ...{spsd,zardoz,felix}!dhw68k!david
X *
X * and -- unless you have been explicitly informed otherwise by someone
X * in a position to know (such as me) -- I welcome suggestions for improvement.
X *
X * David H. Wolfskill 16 April, 1989
X *
X */
X
X/*
X * Program to list statistics, some of which may be of interest,
X * regarding the output of "pathalias," as well as do a basic
X * "reality check" on that output.
X
X * By its nature, this program is intended to be used in conjunction
X * with pathalias. I really don't think you should spend any time
X * or other resources on this program if you neither run nor have
X * any intent to run pathalias.
X
X * This version can read the pathalias output either in the
X * original order, or either of the 2 rotations:
X
X * "pathalias": <cost> <destination> <path>
X * "smail": <destination> <path> <cost>
X * unknown: <path> <cost> <destination>
X
X * It does *not* (presently) handle "<destination> <cost> <path>"
X * (or any of its rotations); if you really think it's something
X * worth doing, I'm willing to discuss it further, but really...!
X
X * In order to make the determination of the stdin format, the
X * first line is read, and its 3 fields are scanned to find one
X * that ends in "%s". If no such field is found, the program
X * claims that the input is not pathalias output, and exits. If
X * one *is* found, however (the normal case, we hope!), that field
X * is assumed to be the <path> field; the next field (to the right,
X * wrapping around if necessary) is the <cost>, and the next is
X * <destination>.
X
X * smail likes the second order, so the stdout of this program
X * is the same information in "smail" order (regardless of the
X * order in which stdin is). This program may thus be used in
X * place of the "sed" segment of the "pathproc" pipeline that is
X * distributed with smail.
X
X * To facilitate its use in that application, the report the
X * program generates comes out on stderr.
X
X * The overall method is straightforward, brute-force: uuname (or
X * whatever -- see macro NBRLIST) is used to generate a list of
X * known immediate uucp neighbors; that is run through sort. (I
X * do it this way because uuname is set[gu]id; I think it
X * appropriate to minimize the need for programs to run as
X * set[gu]id, so I use the facilities provided by a common tool,
X * rather than require special privileges for this program.) Then
X * a "neighbor" (spelled "nbr" for typing constraint relief)
X * struct is allocated for each neighbor thus found -- after one
X * of these structs is allocated for the totals. The nbr structs
X * are allocated as a linked list. (A temporary file is created
X * to hold the output of sort; after the nbr structs are all
X * allocated, it is unlinked and closed.)
X
X * Once this housekeeping is finished, a single pass through stdin
X * (which is expected to be in the above-documented -- possibly
X * rotated -- pathalias output format) is made, updating the
X * appropriate counters in the various nbr structs: the "tots"
X * nbr struct is always updated; the "first hop" in the path is
X * determined, and the nbr struct that corresponds to it is also
X * updated.
X
X * A <path> field that consists entirely of "%s" is assumed to
X * have a "first hop" of the local host. The program will
X * determine this by using the -l flag (ala' pathalias); if -l was
X * not specified, routine hostname() is invoked, which either uses
X * uname() (if UNAME is defined during compilation) or
X * gethostname() (if it wasn't). If hostname() is invoked and
X * fails, the program complains bitterly and exits -- in such a
X * case, you may wish to change the logic in hostname() or specify
X * the -l flag, depending on how ambitious you are.
X
X * For a <path> field that is more than "%s", everything from the
X * beginning of the field up to (but not including) the first
X * occurrence of '!' is assumed to be the name of the "first hop"
X * -- which ought to be the name of one of the specified host's
X * neighbors.
X
X * (Note the pathological case of the pathalias output claiming
X * the existence of a neighbor that NBRLIST doesn't know about.
X * That actually happened in the early testing of the program, and
X * as a result of that experience, I put in code to create a "nbr"
X * struct on demand (as it were) in such a case, then in the
X * output phase, spit out a warning message.)
X
X * Once EOF is reached on stdin, a pair of traversals through the
X * linked list is made; each prints a report based on the
X * accumulated totals, and the second one also frees the elements
X * of the list. The second report shows a distribution of how
X * long the paths are; its width -- how far out to count distinct
X * pathlengths -- is controlled by the MAXDIST macro and the -d
X * flag.
X
X * Then we're done -- what could be simpler...? :-)
X
X
X
X * Notes about the flags:
X
X * Since the program was first written, the number of flags
X * ("command line arguments" for picky folk) has become large;
X * herewith is a list of them:
X
X * -B <limit> <limit> specifies an OUTPUT limit: if the
X * cost to reach a destination is below the
X * specified limit, the stdout will not contain
X * that line of input data. To make this flag
X * ineffective, use a value of 0. Default is
X * BOTOUT (0).
X
X * -T <limit> <limit> specifies an OUTPUT limit: if the
X * cost to reach a destination is above the
X * specified limit, the stdout will not contain
X * that line of input data. To make this flag
X * ineffective, use a value of 0. Default is
X * TOPOUT (0).
X
X * -b <limit> <limit> specifies a REPORTING limit: if the
X * cost to reach a destination is below the
X * specified limit, the reports (on stderr) will
X * not reflect that line of input data. To make
X * this flag ineffective, use a value of 0.
X * Default is BOTCNT (0).
X
X * -t <limit> <limit> specifies a REPORTING limit: if the
X * cost to reach a destination is above the
X * specified limit, the reports (on stderr) will
X * not reflect that line of input data. To make
X * this flag ineffective, use a value of 0.
X * Default is TOPCNT (0).
X
X * -d <dist> <dist> specifies the number of "distribution
X * buckets" for the 2nd report -- the one that
X * (sort of) shows a "histogram" of the hopcounts
X * of the paths to each of the neighboring sites.
X * The default value is DDIST (20).
X
X * -l <host> <host> specifies a local hostname other than
X * the default; it is intended to be similar in
X * operation to the pathalias flag of similar name
X * and intent.
X
X * -z specifies that the reports are not to include
X * lines for neighboring sites that are not "first
X * hops" in the stdin.
X
X * Note that each of the flags is completely independent of any
X * other flag -- and that some combinations are (arguably) useful,
X * while some are misleading or dangerous.
X
X * In the normal course of the use of the program, it is expected
X * that it will be executed in a shell script, so once the flags
X * have been figured out, this should be substantially less of a
X * burden than typing all that stuff.... Of course, the defaults
X * for each of the -b, -t, -B, -T, and -d flags can be compiled
X * in, too....
X
X */
X
X
X
X#include <stdio.h>
X#include <string.h>
X
X#ifndef MAXLINE
X#define MAXLINE 256 /* Assumed maximum field length from input */
X#endif MAXLINE
X
X#ifndef ERRLEN
X#define ERRLEN 256 /* Assumed maximum line length for error message */
X#endif ERRLEN
X
X#ifndef HOSTLEN
X#define HOSTLEN 128 /* Assumed maximum length for host name */
X#endif HOSTLEN
X
X#ifndef SYSLEN
X#define SYSLEN 80 /* Assumed maximum length for "system (2)" call */
X#endif SYSLEN
X
X#ifndef NBRLIST
X#ifdef UNAME
X#define NBRLIST "(uuname -l;uuname) | sort" /* Command for list of hosts */
X#else /* !UNAME */
X#define NBRLIST "(hostname;uuname) | sort" /* Command for list of hosts */
X#endif /* UNAME */
X#endif NBRLIST
X
X#ifndef MAXDIST
X#define MAXDIST 20 /* max # "buckets" for pathlength distribution chart */
X#endif MAXDIST
X
X#ifndef DDIST
X#define DDIST MAXDIST /* default # "buckets" for pathlength distribution chart */
X#endif DDIST
X
X#ifndef STRCHR
X#define STRCHR strchr /* strchr() or index() function */
X#endif STRCHR
X
X#ifndef DEAD
X#define DEAD 30000000 /* Intended to match the pathalias value */
X#endif DEAD
X
X#ifndef TOPOUT
X#define TOPOUT 0 /* "0" == don't supress any output; !0 == threshhold */
X#endif TOPOUT
X
X#ifndef BOTOUT
X#define BOTOUT 0 /* "0" == don't supress any output; !0 == threshhold */
X#endif BOTOUT
X
X#ifndef TOPCNT
X#define TOPCNT 0 /* "0" == don't supress any output; !0 == threshhold */
X#endif TOPCNT
X
X#ifndef BOTCNT
X#define BOTCNT 0 /* "0" == don't supress any output; !0 == threshhold */
X#endif BOTCNT
X
Xstruct nbr { /* struct for "neighbor" host */
X struct nbr *next; /* pointer to next neighbor on the list */
X char *host; /* name of neighboring host */
X int firsthop; /* count of times this appears as "1st hop" */
X int hopcount; /* sum of path lengths that start here */
X int pthlng[MAXDIST]; /* buckets for pathlength dist. */
X double cost; /* sum of COSTS for paths that start here */
X};
X
Xint main(argc, argv) /* find interesting statistics about a "paths" file */
Xint argc;
Xchar *argv[];
X
X{
X struct nbr *tots; /* "pseudo-neighbor" for totals */
X struct nbr *tnbr; /* temporary pointer to a "nbr" struct */
X struct nbr *lnbr; /* pointer to last-allocated "nbr" struct */
X struct nbr *bnbr; /* pointer to first "bad" "nbr" struct */
X struct nbr *getnbr(); /* routine to create a new nbr struct */
X
X extern int errno; /* used by perror() and friends */
X extern int optind; /* for getopt() */
X extern char *optarg; /* for getopt() */
X
X char *tempnam(); /* function to generate name of work file */
X char *tmpf; /* points to pathname of temporary file */
X FILE * tf; /* FILE pointer for temporary file */
X
X char sys[SYSLEN]; /* command line used to get list of neighbors */
X
X char *hostname(); /* function to go find out where I am */
X char site[HOSTLEN]; /* char array to hold name of site */
X
X char str[3][MAXLINE];/* input strings for pathalias output */
X int field; /* index for str[][] array */
X int strl; /* length of str[][] array */
X char *dest; /* used for both "destination" and "1st hop" */
X char *path; /* string for uucp "path" to get to dest */
X char *scost; /* pointer to cost data in string form */
X double cost; /* pathalias "COST" to get to a given dest */
X
X int top_out; /* controls high end of output suppression */
X int bot_out; /* controls low end of output suppression */
X int top_cnt; /* controls high end of counting */
X int bot_cnt; /* controls low end of counting */
X int zflag; /* controls reporting neighbors w/ 0 paths */
X
X int error; /* "error" flag returned by program */
X int hops; /* number of "hops" to get to given dest */
X int dist; /* number of "buckets" for pathlength distr. */
X int ch; /* garden-variety character/EOF-holder */
X char *name; /* name of program -- as invoked, for msgs */
X char errmsg[ERRLEN]; /* string for constructing error messages */
X char *chptr; /* garden-variety pointer-to-char work var */
X
X name = argv[0];
X error = 0;
X dist = DDIST;
X bnbr = NULL;
X site[0] = 0;
X top_out = TOPOUT;
X bot_out = BOTOUT;
X top_cnt = TOPCNT;
X bot_cnt = BOTCNT;
X zflag = 1;
X
X while ((ch = getopt(argc, argv, "b:B:d:l:t:T:z")) != EOF)
X switch (ch) {
X case 'B':
X (void)sscanf(optarg, "%d", &bot_out);
X break;
X case 'b':
X (void)sscanf(optarg, "%d", &bot_cnt);
X break;
X case 'd':
X (void)sscanf(optarg, "%d", &dist);
X break;
X case 'l':
X if (strlen(optarg) < HOSTLEN)
X (void)strcpy(site, optarg);
X else {
X (void)strncpy(site, optarg, HOSTLEN - 1);
X site[HOSTLEN-1] = 0;
X }
X break;
X case 'T':
X (void)sscanf(optarg, "%d", &top_out);
X break;
X case 't':
X (void)sscanf(optarg, "%d", &top_cnt);
X break;
X case 'z':
X zflag = 0;
X break;
X default:
X (void)fprintf(stderr, "usage: %s -d dist -l host -b limit -B limit -t limit -T limit -z\n", name);
X exit(error = 1);
X }
X
X if ((site[0] == 0) && (hostname(site) == NULL)) {
X (void)sprintf(errmsg, "%s: hostname() failed", name);
X (void)perror(errmsg);
X exit(error = errno);
X }
X
X dist = dist > MAXDIST ? MAXDIST : dist;
X
X if ((tots = tnbr = getnbr("TOTALS")) == NULL) {
X (void)sprintf(errmsg, "%s: getnbr(\"TOTALS\") failed", name);
X (void)perror(errmsg);
X exit(error = errno);
X }
X
X tmpf = tempnam((char *)NULL, (char *)NULL);
X (void)sprintf(sys, "%s >%s", NBRLIST, tmpf);
X (void)system(sys);
X
X if ((tf = fopen(tmpf, "r")) == NULL) {
X (void)sprintf(errmsg, "%s: fopen(%s, \"r\") failed", name, tmpf);
X (void)perror(errmsg);
X exit(error = errno);
X }
X
X while (fscanf(tf, "%s", str[0]) != EOF) {
X if ((tnbr->next = getnbr(str[0])) == NULL) {
X (void)sprintf(errmsg, "%s: getnbr(%s) failed", name, str[0]);
X (void)perror(errmsg);
X exit(error = errno);
X }
X tnbr = tnbr->next;
X }
X
X if (unlink(tmpf) != 0) {
X (void)sprintf(errmsg, "%s: unlink(%s) failed", name, tmpf);
X (void)perror(errmsg);
X exit(error = errno);
X }
X
X if (fclose(tf) == EOF) {
X (void)sprintf(errmsg, "%s: fclose(tf) failed", name);
X (void)perror(errmsg);
X exit(error = errno);
X }
X
X if (scanf("%s%s%s", &str[0][0], &str[1][0], &str[2][0]) != EOF) {
X for (field = 0; field < 3; field++) {
X if ((strl = strlen(str[field])) > 1) {
X if (strcmp(&str[field][strl-2], "%s") == 0)
X break;
X }
X }
X if (field > 2) {
X (void)fprintf(stderr, "%s: input is not pathalias format\n", name);
X exit(error = field);
X }
X path = &str[field++][0];
X scost = &str[(field++)%3][0];
X dest = &str[(field++)%3][0];
X
X do {
X (void)sscanf(scost, "%lg", &cost);
X if ((bot_out <= cost) && ((top_out == 0) || (top_out >= cost)))
X (void)printf("%s\t%s\t%s\n", dest, path, scost);
X if ((bot_cnt <= cost) && ((top_cnt == 0) || (top_cnt >= cost))) {
X tots->firsthop++;
X tots->cost += cost;
X chptr = path;
X hops = strcspn(path, "!");
X (void)strncpy(dest, path, hops); /* save name of "first hop" */
X *(dest + hops) = 0; /* ensure proper null term. */
X
X hops = 0;
X while ((chptr = STRCHR(chptr, '!')) != NULL) {
X chptr++;
X hops++;
X }
X
X tots->hopcount += hops;
X tots->pthlng[hops<dist?hops:dist-1]++;
X if (hops == 0 && strcmp(dest, "%s") == 0)
X (void)strcpy(dest, site);
X
X if (tnbr->next == NULL)
X tnbr = tots;
X if ((strcmp(dest, tnbr->next->host)) != 0) {
X tnbr = tots;
X while ((tnbr->next != NULL) && (strcmp(dest, tnbr->next->host)) != 0)
X tnbr = tnbr->next;
X }
X
X if (tnbr->next == NULL) {
X if ((lnbr = getnbr(dest)) == NULL) {
X (void)sprintf(errmsg, "%s: getnbr(%s) failed", name, dest);
X (void)perror(errmsg);
X exit(error = errno);
X }
X tnbr->next = lnbr;
X if (bnbr == NULL)
X bnbr = lnbr;
X }
X
X tnbr->next->firsthop++;
X tnbr->next->cost += cost;
X tnbr->next->hopcount += hops;
X tnbr->next->pthlng[hops<dist?hops:dist-1]++;
X }
X
X } while (scanf("%s%s%s", &str[0][0], &str[1][0], &str[2][0]) != EOF);
X }
X
X (void)fprintf(stderr, "\n Adj Total Total #Hops Cost");
X (void)fprintf(stderr, "\nSite #Dest #Hops Cost /Dest /Dest\n");
X
X lnbr = tots;
X while (tots != NULL) {
X if (bnbr == tots)
X (void)fprintf(stderr, "\n *** WARNING! Bogus \"neighbor\" sites follow! ***\n");
X if (zflag | tots->firsthop)
X (void)fprintf(stderr, "%-9s% 7d% 7d% 14.0f% 8.2f% 12.2f\n", tots->host,
X tots->firsthop, tots->hopcount, tots->cost,
X tots->firsthop ? (double)tots->hopcount / tots->firsthop : (double)0,
X tots->firsthop ? (double)tots->cost / tots->firsthop : (double)0);
X tots = tots->next;
X }
X
X tots = lnbr;
X (void)fprintf(stderr, "\n Adj #Hops --- Pathlength Distribution ---");
X (void)fprintf(stderr, "\nSite #Dest /Dest");
X for (hops = 0; hops < dist; hops++)
X (void)fprintf(stderr, "% 5d", hops);
X (void)fprintf(stderr, "+\n");
X
X while (tots != NULL) {
X if (bnbr == tots)
X (void)fprintf(stderr, "\n *** WARNING! Bogus \"neighbor\" sites follow! ***\n");
X if (zflag | tots->firsthop) {
X (void)fprintf(stderr, "%-9s% 7d% 8.2f", tots->host,
X tots->firsthop,
X tots->firsthop ? (double)tots->hopcount / tots->firsthop : (double)0);
X for (hops = 0; hops < dist; hops++)
X (void)fprintf(stderr, "% 5d", tots->pthlng[hops]);
X (void)fprintf(stderr, "\n");
X }
X if (tots->host != NULL)
X (void)free((char *)tots->host);
X tnbr = tots->next;
X (void)free((char *)tots);
X tots = tnbr;
X }
X exit(error);
X}
X
X
X
X
X/*
X * This routine is given the name of a neighbor host ("nbrname"),
X * then uses malloc() to acquire storage for a new "nbr" struct for
X * that host. It then uses malloc() again, this time to acquire
X * enough storage for a copy of the nbrname, initializes that copy
X * from the nbrname given, & anchors the nbrname in the newly-created
X * nbr struct.
X
X * Lastly, a pointer to the (newly-created) nbr struct is returned.
X
X * If anything goes wrong during this, NULL is returned in lieu of a
X * valid pointer to a nbr struct -- and any storage acquired in the
X * process is freed. (That is, there is a concious attempt to "back
X * out" cleanly.)
X */
X
Xstruct nbr *(getnbr(nbrname)
X)
Xchar *nbrname;
X
X{
X
X char *malloc(); /* to make lint less unhappy */
X
X extern int errno; /* used by perror() and friends */
X
X struct nbr *tnbr; /* temporary anchor for new nbr struct */
X int hops; /* subscript for pathlength buckets */
X
X if ((tnbr = (struct nbr *)malloc((unsigned)sizeof(struct nbr))) == NULL) {
X return((struct nbr *)NULL);
X }
X if ((tnbr->host = (char *)malloc((unsigned)(1 + strlen(nbrname)))) == NULL) {
X (void)free((char *)tnbr);
X return((struct nbr *)NULL);
X }
X
X tnbr->next = NULL;
X (void)strcpy(tnbr->host, nbrname);
X tnbr->firsthop = 0;
X tnbr->hopcount = 0;
X tnbr->cost = 0;
X for (hops = 0; hops < MAXDIST; hops++)
X tnbr->pthlng[hops] = 0;
X return(tnbr);
X}
X
X
X
X
X/*
X * This routine is called to find the name of the host from whose
X * perspective the pathalias data was created.
X
X * Unfortunately, I do not know of a way to ensure consistency in
X * this respect; I have tried to make consistency easy to accomplish,
X * however, by providing the same mechanism pathalias uses to determine
X * the name of the current host -- including the use of the l flag.
X
X * (Thus, this routine isn't invoked if the l flag is specified when
X * the program is invoked.)
X
X * [The following source is basically plagiarized from the sources for
X * pathalias. I hope that neither Peter Honeyman nor Steve Bellovin
X * mind too much.... dhw]
X
X * Anyway, if a problem occurs, NULL is returned.
X */
X
Xchar *hostname(nmptr)
Xchar *nmptr;
X
X{
X
X extern int errno; /* used by perror() and friends */
X
X#ifdef UNAME
X#include <sys/utsname.h>
X
X int uname(); /* system call to find out where I am */
X struct utsname ustrct; /* utsname struct for uname() */
X
X if (uname(&ustrct) < 0)
X return(NULL);
X else {
X (void)strcpy(nmptr, ustrct.nodename);
X
X#else /* !UNAME */
X#include <sys/param.h>
X
X int gethostname(); /* system call to find out where I am */
X char name[MAXHOSTNAMELEN]; /* array to hold the host name */
X
X if (gethostname(name, (int)MAXHOSTNAMELEN) < 0)
X return(NULL);
X else {
X (void)strcpy(nmptr, name);
X#endif /* UNAME */
X return(nmptr);
X }
X
X}
SHAR_EOF
if test 20748 -ne "`wc -c < 'pathrpt.c'`"
then
echo shar: error transmitting "'pathrpt.c'" '(should have been 20748 characters)'
fi
fi # end of overwriting check
# End of shell archive
exit 0
More information about the Comp.sources.misc
mailing list