[comp.sources.misc] uul -- list uucp systems with jobs pending

allbery@ncoast.UUCP (Brandon S. Allbery) (07/27/87)

The following is a quick utility to list systems with UUCP jobs pending.  It
is (sort of) a successor to the uuque I hacked up in C from the original shell
script; it stands a chance of being more portable.  It presently doesn't take
any options.  There also isn't a makefile or man page.

Sample output:

System           Files  Send  Recv  Exec   Size Status
----------------------------------------------------------------------
cwruecm              4     4     0     0     7K Aborted (dial failed)
cbosgd               2     2     0     0     1K Last poll successful
hal                 30    30     0     0   160K Aborted (conversation)
hnsurg3             22    22     0     0   465K Last poll successful
peng                 8     8     0     0    35K Last poll successful
devon               10    10     0     0   230K Last poll successful
----------------------------------------------------------------------
TOTAL               76                     898K for 6 system(s)

Compiling/installation:

The only system dependencies should be in the UUCP files/directories themsel-
ves.  The program doesn't use strchr()/index()/foobar()/whatever-its-called-
this-week().  It uses the Berkeley directory routines, but PD versions of
these are distributed with B news for those not running Berkeley UN*X.

Most of the UUCP configuration is in #defines at the beginning of the pro-
gram.  They are:

UUCPDIR -- shouldn't need changing.  This is the name of the UUCP spool
directory.  /usr/spool/uucp is the default.

L_SYS -- the name of the systems file.  HoneyDanBer uses /usr/lib/uucp/Systems;
others use /usr/lib/uucp/L.sys.

JOBNLEN -- When a UUCP job is queued, it is stored in a file named:

	C.<sysname><jobname>

The #define JOBNLEN specifies the length of the <jobname> component; for most
of the uucp's I've used, this is 5.

MAXSYSLEN -- The number of significant characters in a system name.  Also the
maximum length of the <sysname> component of a job file name.  For V7, Xenix 3,
and BSD4.2, this is 7; for HoneyDanBer and non-HDB System V, this is 6; I
haven't the faintest what 4.3BSD uses.

UUSUBDIRS -- If this is #defined, then the UUCP spool directory is assumed to
use subdirectories C., D., D.<localsysname>, etc.  For BSD UUCP's only;
undefine for V7, System III, System V, HDB(?).

UUSTST(s) -- The name of the STST file for system s.  For most UUCP's, the
default will do.  I understand that some non-flat systems (4.3BSD?) have an
STST. subdirectory; if this is so, change the define to:

	#define UUSTST(s) _catstr(UUCPDIR, "/STST./STST.", s, (char *) 0)

UULOCK(s) -- Like UUSTST(s), but returns the name of the system lock file.
Again, this is set correctly unless you have an LCK. subdirectory.

UUCPCMD -- This is an fscanf() string which extracts the command and data
file name from a UUCP job file (C.system...).  It should return two strings:
the first is the command, the second is the data file name and is assumed to
not be a pathname, just a basename.  The default is valid for 4.2BSD UUCP.
I haven't checked what System V uses and I don't have HoneyDanBer.

LOCALDIR -- If UUSUBDIRS is defined, then LOCALDIR must be set to the basename
of the local UUCP data file directory, "D.<sysname>".  Alternatively, it
could be changed to get the local system name from somewhere else dynamically,
but my experience with grabbing local system names indicates that hardcoding
the name is probably the most portable way available.  (Sigh.)

The only other spot that might cause problems is the format of an STST. file.
I have the 4.2BSD format hardcoded; unfortunately, the code is nontrivial
compared to that for parsing UUCP job files.  I'd appreciate information
on what format other UUCPs use.

The L_SYS file is assumed to have the system name as the first non-whitespace
on a line; blank lines and lines whose first nonspace character is '#' are
skipped.  Consecutive duplicate system names (for alternate speeds or phone
numbers, etc.) are folded to a single output line, but _non_consecutive dupli-
cate lines are not caught.

This program uses varargs in one routine, _catstr(), a function which concat-
enates a series of string arguments (terminated by (char *) 0) into a static
string, then returns a pointer to the string.  This is used to construct path
names for UUCP files in various ways, depending on whether UUSUBDIRS is defined
or not.  The usage is fairly vanilla and conforms to the standard insofar as
I know it:  the function declaration is _catstr(va_alist) and the va_start
and va_end macros are matched in the code; and all arguments retrieved are the
same size, sizeof (char *), and terminated with (char *) 0.  I've also made an
attempt to avoid problems with portability to machines where sizeof (int) !=
sizeof (char *) or sizeof (long) or both; all NULLs are explicitly cast to
the correct type, and the only ints are used to collect file sizes in Kunits
(a "unit" being whatever the basic unit of (struct stat).st_size is, generally
an 8-bit byte but who knows what the DEC-20 uses).  If someone has more than
32767K queued for a system or total, they probably have worse problems than
uul not working right!

To compile:  set up the UUCP parameters described above to the correct values,
then type "cc -O -o uul uul.c".  Append -lndir if you're not running Berkeley
UN*X.  (If you didn't install ndir in your system library directory, do so;
it's a wonderful portability aid!)

The executable must be chown'ed to the owner of uucico (usually uucp or
uucputl; at least one system I know of requires setuid _root_) and made
setuid.  This is safe, as all files are opened for read and only job files
and STST. files are read; data files are only stat'ed.

On ncoast, I find uul invaluable for tracking uucp spool size.  (I discovered
some major problems recently involving some local systems queueing 1500K each
and not picking their files up!)  Hopefully, others will find it useful; and
hopefully others will be able to use it no matter what flavor UUCP they run.
(Please mail configuration information for your version of UUCP to me; include
the system type, OS type, and UUCP type.  I'll fold this into a makefile which
will attempt to autoconfigure uul and send it out with any revisions I make.)

Cut at the dotted line and save to a file.  (WARNING:  IT'S NOT A SHAR!)

===============================================================================
#include <stdio.h>
#include <ctype.h>
#include <varargs.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ndir.h>

#define UUCPDIR		"/usr/spool/uucp"
#define L_SYS		"/usr/lib/uucp/L.sys"
/* for HDB, change the above to /usr/lib/uucp/Systems */
#define JOBNLEN		5
/* this is the number of characters tacked onto a file name, the job # */
#define MAXSYSLEN	7
/* the maximum number of characters allowed in a UUCP name, 6 for sys5 */
#define UUSUBDIRS
/* do not define this for flat-directory UUCP's! */
#define UUSTST(s)	_catstr(UUCPDIR, "/STST.", s, (char *) 0)
/* for 4.3BSD UUCP this may be STST./STST.sysname */
#define UULOCK(s)	_catstr(UUCPDIR, "/LCK..", s, (char *) 0)
/* for 4.3BSD UUCP this may be LCK./LCK..sysname */
#define UUCPCMD		"%s %s %*s %*s %*s %*s %*s"
/* fscanf() format for UUCP command files (C.xxx) - arg 1 = S or R, */
/* arg 2 is filename on local system.  The above is for 4.2BSD uucp. */

#ifdef UUSUBDIRS
#define UUPATH(c,f)	_catstr(UUCPDIR, "/", c, "/", f, (char *) 0)
#define UUDIR(d)	_catstr(UUCPDIR, "/", d, (char *) 0)
#define LOCALDIR	"D.ncoast"
#else
#define UUPATH(c,f)	_catstr(UUCPDIR, "/", f, (char *) 0)
#define UUDIR(d)	UUCPDIR
#define LOCALDIR	""
#endif

static char *_catstr(va_alist)
va_dcl {
	static char buf[5120];
	char *cp, *bp;
	va_list args;
	
	va_start(args);
	bp = buf;
	while ((cp = va_arg(args, char *)) != (char *) 0)
		while (*cp != '\0')
			*bp++ = *cp++;
	*bp = '\0';
	va_end(args);
	return buf;
}

lsys(sys, nf, nk)
int *nf, *nk;
char *sys; {
	DIR *dp;
	struct direct *uuf;
	int len, n, files, blocks, nsend, nrec, nexec;
	char dname[256], cmd[16];
	FILE *fp;
	struct stat st;
	
	blocks = 0;
	files = 0;
	nsend = 0;
	nrec = 0;
	nexec = 0;
	if ((dp = opendir(UUDIR("C."))) == (DIR *) 0) {
		perror(UUDIR("C."));
		exit(1);
	}
	while ((uuf = readdir(dp)) != (struct direct *) 0) {
		if (strncmp(uuf->d_name, "C.", 2) != 0)
			continue;
		len = strlen(uuf->d_name) - JOBNLEN;
		for (n = 2; n < len; n++)
			dname[n - 2] = uuf->d_name[n];
		dname[n - 2] = '\0';
		if (strcmp(dname, sys) != 0)
			continue;
		if ((fp = fopen(UUPATH("C.", uuf->d_name), "r")) == (FILE *) 0) {
			perror(UUPATH("C.", uuf->d_name));
			continue;
		}
		while (fscanf(fp, UUCPCMD, cmd, dname) == 2) {
			files++;
			switch (*cmd) {
			case 'S':
				nsend++;
				break;
			case 'R':
				nrec++;
				continue;
			}
			if (stat(UUPATH(LOCALDIR, dname), &st) < 0)
				continue;
			blocks += (st.st_size + 1023) / 1024;
		}
		fclose(fp);
	}
	closedir(dp);
	if ((dp = opendir(UUDIR("X."))) == (DIR *) 0) {
		perror(UUDIR("X."));
		exit(1);
	}
	while ((uuf = readdir(dp)) != (struct direct *) 0) {
		if (strncmp(uuf->d_name, "X.", 2) != 0)
			continue;
		len = strlen(uuf->d_name) - JOBNLEN;
		for (n = 2; n < len; n++)
			dname[n - 2] = uuf->d_name[n];
		dname[n - 2] = '\0';
		if (strcmp(dname, sys) != 0)
			continue;
		files++;
		nexec++;
	}
	closedir(dp);
	*nf = files;
	*nk = blocks;
	if (files == 0)
		return;
	printf("%-16s %5d %5d %5d %5d %5dK ", sys, files, nsend, nrec, nexec, blocks);
	if ((fp = fopen(UUSTST(sys), "r")) == (FILE *) 0)
		printf("Last poll successful\n");
	else {
		if (stat(UULOCK(sys), &st) < 0)
			printf("Aborted");
		else
			printf("Connected");
		if (fgets(dname, sizeof dname, fp) == (char *) 0)
			printf(" (reason unknown)\n");
		else {
			for (len = strlen(dname) - 1; dname[len] != ' '; len--)
				;
			dname[len] = '\0';
			printf(" (");
			for (n = 0, len = 0; dname[len] != ' ' || ++n < 4; len++)
				;
			while (dname[++len] != '\0')
				putchar(tolower(dname[len]));
			printf(")\n");
		}
		fclose(fp);
	}
}

main() {
	FILE *fp;
	char sys[1024], sysname[132], lastsys[132];
	int nf, nk, files, blocks, nsys;
	
	if ((fp = fopen(L_SYS, "r")) == (FILE *) 0) {
		perror(L_SYS);
		exit(1);
	}
	printf("System           Files  Send  Recv  Exec   Size Status\n");
	printf("----------------------------------------------------------------------\n");
	nsys = 0;
	lastsys[0] = '\0';
	while (fgets(sys, sizeof sys, fp) != (char *) 0) {
		if (sscanf(sys, "%s", sysname) == 0)
			continue;
		sysname[MAXSYSLEN] = '\0';
		if (sysname[0] == '#')
			continue;
		if (strcmp(sysname, lastsys) == 0)
			continue;
		lsys(sysname, &nf, &nk);
		files += nf;
		blocks += nk;
		if (nf != 0)
			nsys++;
		strcpy(lastsys, sysname);
	}
	if (nsys != 0)
		printf("----------------------------------------------------------------------\n");
	printf("TOTAL            %5d                   %5dK for %d system(s)\n", files, blocks, nsys);
	exit(0);
}
-- 
 Brandon S. Allbery, moderator of comp.sources.misc and comp.binaries.ibm.pc
  {{harvard,mit-eddie}!necntc,well!hoptoad,sun!cwruecmp!hal}!ncoast!allbery
ARPA: necntc!ncoast!allbery@harvard.harvard.edu  Fido: 157/502  MCI: BALLBERY
   <<ncoast Public Access UNIX: +1 216 781 6201 24hrs. 300/1200/240I.!XW!XW!