Release of uucp support tool uuls - (nf)
ajs at hpfcla.UUCP
ajs at hpfcla.UUCP
Wed Dec 7 04:15:07 AEST 1983
#N:hpfcla:21800002:000:20936
hpfcla!ajs Dec 5 11:51:00 1983
Subject: release of uuls(1)
Here is the uucp support program you've been waiting for. Uuls helps
enormously with the administration of /usr/spool/uucp, as long as it
doesn't get too huge (in which case, even ls has trouble). You may find
yourself using it several times a day if you have a busy system and like
to keep an eye on uucp activity.
We've been using the command here at HP for six months with no problems.
We've also run it on UCB4.1 (compile with "-DUCB"). We're putting it
into the public domain with one restriction: You can't sell it for
profit or as part of a profit-making package.
The rest of this article consists of the manual page (uuls.1) and source
code (uuls.c), separated by and followed by lines of dashes. Sorry, you
have to unpack it by hand. The manual page starts with a couple of
lines that should let you run it through nroff -man, but it's otherwise
a literal document.
Enjoy!
Alan Silverstein, Hewlett-Packard Fort Collins Systems Division, Colorado
ucbvax!hplabs!hpfcla!ajs, 303-226-3800 x3053, N 40 31'31" W 105 00'43"
-------------------------- uuls.1 --------------------------
.ta 0.3i 1.1i 1.9i 2.7i 3.5i 4.3i 5.1i 5.9i 6.7i 7.5i
.nf
UULS(1) HP EXPERIMENTAL UULS(1)
NAME
uuls -- list spooled uucp transactions grouped by transaction
SYNOPSIS
uuls [-m] [directories...]
uuls -s [-m] [directories...]
uuls -k [-m] [directories...]
DESCRIPTION
This command lists the contents of uucp spool directories
(default "/usr/spool/uucp") with the files grouped into three
categories:
Transactions:
Each line starts with a transaction filename and
includes the name of each local (same-directory) subfile
referenced by the transaction file (see below), possibly
followed by the total size in bytes (-s) or Kbytes (-k)
in the transaction (see below). The -m (meanings)
option replaces the subfile names with nodename, user,
and commandline information (see below).
Orphans:
All subfiles not referenced by any transaction file.
Others:
All other files in the directory (all files not listed
under one of the above categories).
Filenames are columnated so there may be more than one file
per line. If a transaction has more subfiles than fit on one
line, it is followed by continuation lines which are indented
further.
The -s (size in bytes) and -k (Kbytes) options cause uuls to
follow each transaction in the Transactions section with a
total size for all stat-able, sendable files in that
transaction. This includes "D.*" files only, not "C.*" or
"X.*" files, nor files outside the directory which are
indirectly referenced by "C.*" files. Sizes are either in
bytes or rounded to the nearest Kbyte (1024 bytes),
respectively.
The -m (meanings) option causes uuls to follow "C.*" files
(only) with "nodename!username commandline" line(s), one per
"D*X*" subfile, instead of subfilename(s). Nodename and
username are truncated at eight characters and commandline at
38 characters. See below for details.
Filenames are listed in alphabetical order within each
section, except that the first section is only sorted by the
transaction filename. Every file in the directory except "."
and ".." appears exactly once in the entire list, unless -m
is used.
DETAILS
Transaction files are those whose names start with "C." or
"X.". Subfilenames, which usually start with "D.*", are
gleaned from transaction file lines, at most one per line, as
follows:
C.*: "S<junk><blank><hyphen><junk><blank><filename><end>"
X.*: "F<blank><filename><end>"
where <junk> ::= <any chars except blank or null>
and <end> ::= <blank>|<tab>|<newline>|<null>
Lines that don't begin with the appropriate character ('S' or
'F'), and subfilenames of "D.0", are ignored.
Orphan files are those whose names start with "D." and which
are not referenced by any transaction files.
This algorithm extracts from transaction files the names of
all subfiles which should exist in the spool directory when
the transaction is not being actively processed. It is not
unusual to see "missing subfiles" and "orphans" if you uuls a
spool directory while uucico, uucp, uux, or uuxqt is active.
"Meanings" information is gotten by reading each "D*X*"
subfile referenced by each "C.*" file. Nodename!username is
taken from the last line in the file which is of the form:
"U<blank>[<username><blank>[<nodename><blank>[<junk...>]]]"
Fields must be missing; separators must be exactly one blank.
Likewise, commandline is taken from the last line of the
form:
"C<blank>[<commandline>]"
DIAGNOSTICS
The program writes an appropriate message to standard error
if it has any problems dealing with a specified file
(directory), including failure to get heap space. It always
returns zero as its exit value.
If a transaction file is unopenable (wrong permissions or it
disappeared while uuls was running), its name is preceded by
a "*" and the size of the transaction is zero. If a subfile
is missing (filename not found in the directory being listed)
or unstatable (if required for -s or -k), its name is
preceded by a "*" and it contributes zero bytes to the size
of the transaction.
If -m is specified and a "D*X*" file is missing or
unreadable, its name is given with a "*" prepended, as usual.
AUTHOR
Alan Silverstein, Hewlett-Packard Fort Collins Systems Div, Colorado
hplabs!hpfcla!ajs, 303-226-3800 x3053, N 40 31'31" W 105 00'43"
SEE ALSO
mail(1), uucp(1), uuto(1), uux(1), uuxqt(1), stat(2)
-------------------------- uuls.c --------------------------
static char Uni_id[] = "@(#)HP 20.1";
/* UNISRC_ID: @(#)HP uuls.c 20.1 83/12/02 */
/*
* Copyright Hewlett-Packard Company, 1983. Permission is given for the use,
* modification, and distribution of this program and manual entry with one
* exception: It may not be sold for profit individually nor as part of any
* software package, regardless of modifications.
*
* List files in a uucp spool directory grouped by transaction.
* Compile with -DDEBUG for more output.
* Compile with -DUCB if strchr(3s) or getopt(3) are missing.
*
* Possible enhancements:
* Tell name of each directory listed if argc > 2.
* Leave off empty sections or say "none" if no files.
*
* Author:
* Alan Silverstein, Hewlett-Packard Fort Collins Systems Division, Colorado
* ucbvax!hplabs!hpfcla!ajs, 303-226-3800 x3053, N 40 31'31" W 105 00'43"
*/
#ifdef UCB
#define strchr index
#endif
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#define PRINTERR(part1,part2) \
fprintf (stderr, "%s: %s %s\n", myname, part1, part2);
#define MISSING '*' /* precedes missing file */
#define INDLINE 2 /* indent filename lines */
#define INDFILE 2 /* before each filename */
#define FILESIZE (DIRSIZ + 1) /* filename plus null */
#define INDSUB (INDLINE + INDFILE + FILESIZE) /* indent subfile lines */
#define ENDFILES 71 /* past last file column */
#define NUMSIZE 8 /* digits to allow for */
#define USERSIZE 8 /* longest username */
#define NODESIZE 8 /* longest nodename */
#define NODEUSER (NODESIZE + 1 + USERSIZE) /* node!user */
#define CMDSIZE 38 /* longest command line */
#define TRANSACTION 0 /* types for printfile() */
#define ORPHAN 1
#define OTHER 2
char *myname; /* how command invoked */
int kflag = 0; /* -k (kbytes) option */
int mflag = 0; /* -m (meanings) option */
int sflag = 0; /* -s (size) option */
int stflag = 0; /* stat() required */
int nextcol; /* next column to print */
char *DEFAULT[] = { "/usr/spool/uucp", 0 }; /* default arg list */
char *strchr(); /* library routine */
/****************************************************************/
main (argc, argv)
int argc;
char **argv;
{
struct stat statbuf; /* for return from stat() */
extern int optind; /* for getopt() */
char optchar; /* for getopt() */
char *dirname; /* current directory name */
char *start, *end; /* heap start, end + 1 */
myname = *argv;
/*
* Check arguments:
*/
while ((optchar = getopt (argc, argv, "kms")) != EOF)
if (optchar == 'k') kflag = 1;
else if (optchar == 'm') mflag = 1;
else if (optchar == 's') sflag = 1;
else usage ();
if (kflag && sflag)
usage ();
stflag = (kflag || sflag); /* stats are required */
argc -= optind;
argv += optind;
if (argc < 1) /* use default arg list */
argv = DEFAULT;
/*
* Stat each argument and check if directory:
*/
for ( ; (dirname = *argv); argv++) {
if (stat (dirname, &statbuf) < 0) {
PRINTERR ("can't stat", dirname);
continue;
}
if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
PRINTERR (dirname, "is not a directory");
continue;
}
/*
* Read directory contents into heap, sort list of files, list each file, and
* return heap space:
*/
if (readdir (dirname, &start, &end))
lsfiles (dirname, start, end);
if (brk (start) == -1)
PRINTERR ("warning: failed to release heap space", "");
}
}
/****************************************************************
* Tell correct usage:
*/
usage ()
{
fprintf (stderr, "usage:\t%s [-m] [directories...]\n", myname);
fprintf (stderr, "\t%s -s [-m] [directories...]\n", myname);
fprintf (stderr, "\t%s -k [-m] [directories...]\n", myname);
exit (1);
}
/****************************************************************
* Read filenames from directory into heap space, sort them, and
* tack on a terminator:
*
* Return one if successful, else return zero.
*/
readdir (dirname, startp, endp)
char *dirname; /* name of directory */
char **startp, **endp; /* heap start, end + 1 */
{
FILE *dirp; /* open file pointer */
struct direct dirbuf; /* one file entry */
long needbytes = sizeof (dirbuf); /* bytes to read */
long gotbytes; /* actually read */
char *fname; /* filename from dir */
char *sbrk();
int strcmp();
*startp = 0; /* means "not set" */
/*
* Open directory for reading:
*/
if ((dirp = fopen (dirname, "r")) == NULL) {
PRINTERR ("can't open", dirname);
return (0);
}
/*
* Read each directory entry, increase heap size, and save filename:
*/
while ((gotbytes = fread (&dirbuf, 1, needbytes, dirp)) == needbytes)
{
if (dirbuf.d_ino && dirbuf.d_name[0]) /* valid dir entry */
{
if ((strncmp (dirbuf.d_name, ".", 2) == 0) ||
(strncmp (dirbuf.d_name, "..", 3) == 0))
continue; /* ignore those files */
if ((int) (fname = sbrk (FILESIZE)) == -1)
{
PRINTERR ("can't get heapspace to do", dirname);
fclose (dirp);
return (0);
}
if (*startp == 0) /* first time only */
*startp = fname; /* save start of heap */
strncpy (fname, dirbuf.d_name, DIRSIZ);
fname [DIRSIZ] = 0; /* final null */
#ifdef DEBUG
printf ("%x: %s\n", fname, fname);
#endif
}
}
fclose (dirp);
/*
* Check if read failed:
*/
if (gotbytes != 0) {
PRINTERR ("read failed from", dirname);
return (0);
}
/*
* Set end and sort files in heap:
*/
*endp = sbrk (0);
if (*endp > *startp)
qsort (*startp, (*endp - *startp) / FILESIZE, FILESIZE, strcmp);
#ifdef DEBUG
{ char *cp;
printf ("-- sorted:\n");
for (cp = *startp; cp < *endp; cp += FILESIZE)
printf ("%x: %s\n", cp, cp);
}
#endif
return (1);
}
/****************************************************************
* List files based on filenames sorted in heapspace, in three phases:
*
* 1: Search for transaction files ("C.*" and "X.*") and list them and
* the subfiles they reference, with optional stats and prints of
* file sizes and total;
* 2: List orphans (remaining "D.*" files);
* 3: List other files (any not already referenced).
*
* During phases 1 and 2, filenames are removed from the list as they're listed.
*/
lsfiles (dirname, start, end)
char *dirname; /* directory being done */
char *start, *end; /* heap start, end + 1 */
{
char *transp, *subp; /* transaction, subfile */
char transname[FILESIZE]; /* save trans filename */
char *subname; /* subfile within trans */
FILE *filep; /* open trans file */
struct stat statbuf; /* return from stat() */
int statval = 0; /* return from stat() */
long transize; /* bytes in transaction */
long totsize = 0; /* total bytes */
char *getsubname();
/*
* Go to directory to be listed:
*/
if (chdir (dirname) < 0) {
PRINTERR ("can't chdir to", dirname);
return;
}
/*
* List transactions (main file first, then referenced files):
*/
printf ("Transactions:\n");
for (transp = start; transp < end; transp += FILESIZE)
if ((strncmp (transp, "C.", 2) == 0) ||
(strncmp (transp, "X.", 2) == 0) )
{
strcpy (transname, transp); /* save filename */
*transp = 0;
nextcol = 1;
transize = 0;
/*
* Open transaction file, then list it:
*/
filep = fopen (transname, "r");
printfile (TRANSACTION, (filep == NULL), transname);
if (filep == NULL) {
if (stflag)
printsize (0);
printf ("\n");
nextcol = 1;
continue;
}
/*
* Get subfilenames from transaction file, search for them in the sorted
* list, stat them if needed, and either analyze them or just list them
* (whether found or missing):
*/
while (subname = getsubname (filep, transname[0]))
{
for (subp = start; subp < end; subp += FILESIZE)
if (strcmp (subname, subp) == 0) {
*subp = 0;
break;
}
if (stflag &&
((statval = stat (subname, &statbuf)) >= 0))
transize += statbuf.st_size;
if (mflag && (transname[0] == 'C'))
printmeaning (subname);
else
printfile (TRANSACTION,
((statval < 0) || (subp >= end)),
subname);
}
fclose (filep);
if (stflag)
printsize (transize);
printf ("\n");
totsize += transize;
}
/*
* Print transaction totals if needed:
*/
if (stflag) {
nextcol = 1;
printfile (TRANSACTION, 0, "total");
printsize (totsize);
printf ("\n");
}
/*
* List orphaned subfiles:
*/
printf ("Orphans:");
nextcol = ENDFILES; /* force newline */
for (transp = start; transp < end; transp += FILESIZE) {
if (strncmp (transp, "D.", 2) == 0) {
printfile (ORPHAN, 0, transp);
*transp = 0;
}
}
/*
* List other files in columns:
*/
printf ("\nOthers:");
nextcol = ENDFILES; /* force newline */
for (transp = start; transp < end; transp += FILESIZE)
if (*transp)
printfile (OTHER, 0, transp);
printf("\n");
}
/****************************************************************
* Get next subfilename from a transaction file (never more than
* DIRSIZ non-null chars):
*
* Returns *subname if found, or zero at the end of the trans file.
*
* Subfilenames come from transaction file lines, at most one per line:
*
* C.*: "S<junk><blank><hyphen><junk><blank><filename><end>"
* X.*: "F<blank><filename><end>"
*
* where <junk> ::= <any chars except blank or null>
* <end> ::= <blank>|<tab>|<newline>|<null>
*
* Subfilenames of "D.0" are ignored.
*/
char *
getsubname (filep, mode)
FILE *filep; /* open transaction file */
char mode; /* type of file: 'C' or 'X' */
{
static char line[BUFSIZ]; /* read from trans file */
char *cp; /* for scanning line */
char *subname; /* found in line */
/*
* Read lines and check first letters:
*/
while (fgets (line, BUFSIZ, filep) != NULL)
{
#ifdef DEBUG
printf ("\ngetsubname: %s\n", line);
#endif
cp = line;
if (*cp != ((mode == 'C') ? 'S' : 'F'))
continue;
/*
* For "C.*" files, skip past " -", if any:
*/
if (mode == 'C') {
while (*cp && strncmp (cp, " -", 2))
cp++;
if (*cp++ == 0)
continue;
}
/*
* Skip past next blank and save start of subname:
*/
while (*cp && (*cp != ' '))
cp++;
subname = ++cp;
/*
* Skip subname, but not more than DIRSIZ, and mark end:
*/
while ((cp < subname + DIRSIZ) &&
*cp && (*cp != ' ') && (*cp != '\t') && (*cp != '\n'))
cp++;
*cp = 0;
/*
* Return subname if valid:
*/
if ((*subname == 0) || (strcmp (subname, "D.0") == 0))
continue;
return (subname);
}
return (0);
}
/****************************************************************
* Print transaction meaning ("C" and "U" lines from "D*X*" subfiles):
*
* Non-"D*X*" files are ignored. If a "D*X*" subfile is missing, the usual
* missing-file format is used. Otherwise, the last "C" and "U" lines in
* each file are used to print one line.
*/
printmeaning (subname)
char *subname; /* name of subfile */
{
FILE *subp; /* open subfile */
char subline[BUFSIZ]; /* line from subfile */
char nodeuser[NODEUSER+1]; /* node!user + null */
char cmdline [CMDSIZE+1]; /* commandline + null */
/*
* Check if execute file; if not, do nothing:
*/
if (strchr (subname, 'X') == NULL)
return;
/*
* Open file; if fails, print as if missing:
*/
if ((subp = freopen (subname, "r", stdin)) == NULL)
printfile (TRANSACTION, 1, subname);
/*
* Get data from subfile, then print it:
*/
else {
while (gets (subline) != NULL)
getmeaning (subline, nodeuser, cmdline);
if (nextcol > 1 + INDSUB) {
printf ("\n%*s", INDSUB, "");
nextcol = 1 + INDSUB;
}
printf ("%*s%-*s%*s%s", INDFILE, "", NODEUSER, nodeuser,
INDFILE, "", cmdline);
nextcol += INDFILE + NODEUSER + INDFILE + strlen (cmdline);
fclose (subp);
}
}
/****************************************************************
* Get transaction meaning information from a subfile line:
*
* Node!user is built from "U " lines, and commandline from "C " lines.
* Other lines are ignored. Assumes "U " lines are "healthy" (no multiple
* blanks), but can handle missing or null names.
*/
getmeaning (subline, nodeuser, cmdline)
char *subline; /* line to read */
char *nodeuser; /* field to update */
char *cmdline; /* field to update */
{
char *user = &subline[2]; /* start of username */
char *node = ""; /* default null */
char *cp;
if (strncmp (subline, "U ", 2) == NULL) {
if (cp = strchr (user, ' ')) { /* nodename does follow */
*cp = 0; /* put trailing null */
node = cp + 1; /* start of nodename */
if (cp = strchr (node, ' '))
*cp = 0; /* put trailing null */
}
nodeuser[0] = 0;
strncat (nodeuser, node, NODESIZE);
strcat (nodeuser, "!");
strncat (nodeuser, user, USERSIZE);
}
else if (strncmp (subline, "C ", 2) == NULL) {
strncpy (cmdline, &subline[2], CMDSIZE);
cmdline[CMDSIZE] = 0; /* trailing null */
}
}
/****************************************************************
* Print one filename with proper formatting:
*/
printfile (type, missing, filename)
int type; /* trans, orphan, other */
int missing; /* if need to mark file */
char *filename;
{
int space; /* leading spaces used */
/*
* New line if needed:
* TRANSACTION secondary lines are pre-indented an extra amount.
*/
if (nextcol + INDFILE + FILESIZE > ENDFILES) {
space = (type == TRANSACTION) ? INDSUB: 0;
printf ("\n%*s", space, "");
nextcol = 1 + space;
}
/*
* Figure spacing and print filename, with missing flag if needed:
*/
space = ((nextcol == 1) * INDLINE) + INDFILE;
printf ("%*s%c%-*s",
space - 1, "",
(missing ? MISSING : ' '),
FILESIZE, filename);
nextcol += space + FILESIZE;
}
/****************************************************************
* Print transaction size at end of line with proper formatting:
*/
printsize (size)
long size; /* number of bytes */
{
if (kflag) /* round to kbytes */
size = (size + 512) / 1024;
if (nextcol > ENDFILES) {
printf ("\n");
nextcol = 1;
}
printf ("%*s%*d", ENDFILES - nextcol + 1, "", NUMSIZE, size);
}
#ifdef UCB
/****************************************************************
* Analyze options:
*
* This primitive version of getopt(), written from scratch, is
* provided so the program is more portable.
*/
int optind = 0; /* which option */
int optoff = 0; /* which letter */
getopt (argc, argv, options)
int argc; /* unmodified */
char **argv; /* unmodified */
char *options; /* legal list */
{
char letter;
optind += (optind == 0); /* skip first arg */
while (optind < argc) { /* there are more args */
if (optoff == 0) { /* now at start of arg */
if (argv[optind][0] != '-') /* not opt arg */
return (EOF);
else
optoff++; /* move to next char */
}
if (letter = argv[optind][optoff++]) /* not end of arg */
return (strchr (options, letter) ? letter : '?');
optind++;
optoff = 0;
}
return (EOF); /* no more arguments */
}
#endif
-------------------------- end of article --------------------------
More information about the Comp.sources.unix
mailing list