[comp.sources.misc] Load average, "w", and status line for USG systems

allbery@ncoast.UUCP (Brandon S. Allbery) (08/02/87)

Herein are some programs I use under System V.  They are:

avenrun -- a daemon which calculates an approximate load average from the
	"runque" and "runocc" parameters in the kernel "sysinfo" structure.
	Thanks to Phil Budne for the original, which forged "rwho" packets
	for USG systems on BSD networks; this one puts its load average into
	an array of 3 (double)'s in a shm segment identified by ftok("/unix",
	'a').

w -- The BSD "w" program, large as life.  It works as expected, except that
	the "JCPU" is a kludge; USG doesn't keep per-session CPU time.

csl -- A status line program.  It displays things in "panels" which "rotate"
	every 5 seconds.  The information displayed is:
	
		- sender and subject of mail in your mailbox,
		  one message per panel
		- current program (if possible), login name,
		  # of messages in mailbox, current date/time
		- full name and terminal (useful!), # non-daemons
		  running
		- list of system users, similar to "who -q"
		- lines from $HOME/.messages, one per panel

I've been using these for a few months without problems.  Enjoy!
++Brandon

#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -rw-r--r--   1 allbery  System      1579 May  7 22:09 README.w
# -rw-r--r--   1 allbery  System      8210 May  7 21:52 avenrun.c
# -rw-r--r--   1 allbery  System      8313 May  7 21:52 csl.c
# -rw-r--r--   1 allbery  System      7909 May  7 21:28 w.c
# -rw-r--r--   1 allbery  System      1023 May  7 22:51 avenrun.1m
# -rw-r--r--   1 allbery  System      3217 May  7 22:39 csl.1l
# -rw-r--r--   1 allbery  System      3056 May  7 23:12 w.1l
#
echo 'x - README.w'
if test -f README.w; then echo 'shar: not overwriting README.w'; else
sed 's/^X//' << '________This_Is_The_END________' > README.w
XThe following are some programs which give you some familiar BSD utilities
Xin a System V environment.  The programs are:
X
X	w	- Yes, "w", as big as life!  Requires...
X	avenrun	- A program to be invoked in /etc/rc which computes
X		  a "load average" from information in the "sysinfo"
X		  structure of the kernel.  Based on a program by
X		  Phil Budne which forges "rwhod" packets for mixed
X		  networks; #define RWHOD for the original program.
X	csl	- I have no idea what the BSD "sysline" does, but this
X		  is my idea of a status line.  The name stands for
X		  "cyclic status line"; it doesn't even attempt to
X		  fit everything into one line, instead it cycles
X		  through a set of "panels".  See the man page.
X		  This also wants "avenrun" to be running.
X
XNote that "w" (and ONLY "w") can be compiled for System III or Xenix 3.0;
Xthis is in fact the default, use -DSYS5 to get the full version.
X
XNo Makefile; the compile commands are trivial:
X
X	cc -O -o w w.c -DSYS5		# omit the -DSYS5 for System III
X	cc -O -o avenrun avenrun.c	# add -DRWHOD for rwhod forgery
X	cc -O -o csl csl.c
X
X"w" requires read permission on /dev/kmem, /dev/mem, and /dev/swap.  "avenrun"
Xrequires read permission on /dev/kmem (to read the sysinfo structure).  "csl"
Xcan run as a normal user program.  On tdi2 we keep /dev/*mem and /dev/swap
X-r--r----- root/sys and have "avenrun" and "w" setgid sys.
X
XIf someone wants to tell me what "sysline" under BSD does, I'd be interested
Xin writing a real one.  But "csl" is everything in one small package, so I
Xwill probably continue to use it.
X
XEnjoy!
X
X++Brando
________This_Is_The_END________
if test `wc -l < README.w` -ne 37; then
	echo 'shar: README.w was damaged during transit (should have been 37 bytes)'
fi
fi		; : end of overwriting check
echo 'x - avenrun.c'
if test -f avenrun.c; then echo 'shar: not overwriting avenrun.c'; else
sed 's/^X//' << '________This_Is_The_END________' > avenrun.c
X/*
X *	ldavg.c -- compute load averages for System V
X *	Phil Budne @ Boston U / DSG
X *
X *	Forges BSD 4.2 rwhod packets containing system load averages
X *	(#ifdef RWHOD for this, else a shm segment is used, ftok("/unix", 'a'))
X */
X
X# include <sys/types.h>			/* system types */
X# include <sys/sysinfo.h>		/* sysinfo structure */
X# include <sys/utsname.h>		/* for uname(2) */
X# include <sys/stat.h>			/* for stat(2) */
X# include <sys/param.h>			/* for HZ */
X# include <stdio.h>
X# include <nlist.h>
X# include <time.h>
X# include <math.h>
X# include <utmp.h>
X# include <fcntl.h>
X#ifdef RWHOD
X# include "rwhod.h"			/* (from BSD) */
X#else  SHM
X# include <sys/ipc.h>
X# include <sys/shm.h>
X#endif RWHOD
X
X/* # define DEBUG /**/
X#ifdef RWHOD
X# define UDP 1
X# define DSK 1
X# define PMUL 100
X
X# if UDP
X# include "netdb.h"
Xunsigned short port = 513;
Xunsigned long ipaddr;
X# endif
X#endif RWHOD
X
Xextern struct utmp *getutent();
X
X# define SYSTEM "/unix"
X# define KMEM "/dev/kmem"
X
Xstruct nlist nl[] = {
X# define NL_SYSINFO 0
X        { "_sysinfo" },			/* 0 */
X# define NL_LBOLT 1
X	{ "_lbolt" },			/* 1 */
X        { 0 }
X};
X
X#ifdef RWHOD
Xstruct whod proto;
Xstruct utsname utsn;
Xchar whopacket[100];
X#else  SHM
Xkey_t aven_key;
Xint aven_shm;
Xdouble *aven_seg;
X#endif RWHOD
Xint fd, memfd;
X
Xchar *system = SYSTEM;
Xchar *kmem = KMEM;
Xchar *argv0;
X
Xmain(argc, argv)
Xint  argc;
Xchar *argv[];
X{
X	switch (fork()) {
X	case -1:
X		perror("fork");
X		exit(1);
X	case 0:
X		break;
X	default:
X		exit(0);
X	}
X	argv0 = argv[0];
X#ifdef RWHOD
X	uname(&utsn);			/* get system names */
X#endif RWHOD
X	setpgrp();			/* create own pgrp */
X	init_nlist();			/* get name values, open kmem */
X	init_packet();			/* initialize packet prototype */
X	doit();				/* never returns */
X} /* main */
X
Xinit_nlist() {
X	nlist(system, nl);		/* get system values */
X
X        if(nl[NL_SYSINFO].n_value == 0) {
X                fprintf(stderr, "%s: can't find sysinf structure\n", argv0);
X                exit(1);
X        } /* no value */
X
X	if ((memfd = open(kmem, O_RDONLY)) < 0) {
X		fprintf(stderr, "%s: no mem\n", argv0);
X		exit(1);
X	} /* could not open kmem */
X
X} /* init_nlist */
X
X# define PERIOD 5			/* sample period (in seconds) */
X# define INTERVAL1 60			/* average interval 1 (in seconds) */
X# define INTERVAL2 (5*60)		/* average interval 2 (in seconds) */
X# define INTERVAL3 (15*60)		/* average interval 3 (in seconds) */
X# define PACKINTERVAL 30		/* interval for make_packet */
X
Xdoit() {
X	struct sysinfo sinf;
X	int packt = 0;
X	long occ, que, nocc, nque, n, c;
X	double avg1, avg2, avg3, new;
X	double exp1, exp2, exp3;
X
X	exp1 = exp( - ((double) PERIOD) / INTERVAL1 );
X	exp2 = exp( - ((double) PERIOD) / INTERVAL2 );
X	exp3 = exp( - ((double) PERIOD) / INTERVAL3 );
X
X	getsysinf(&sinf);		/* prime the pump */
X	occ = sinf.runocc;		/* number of samples */
X	que = sinf.runque;		/* run queue summation */
X
X	avg1 = avg2 = avg3 = ((double) que) / occ;
X
X	for( ; ; ) {
X		if( --packt < 0 ) {
X#ifdef RWHOD
X			make_packet((int) (avg1 * PMUL),
X				    (int) (avg2 * PMUL),
X				    (int) (avg3 * PMUL));
X#else  SHM
X			make_packet(avg1, avg2, avg3);
X#endif RWHOD
X			packt = PACKINTERVAL / PERIOD;
X		} /* packet time */
X
X/*		printf("runque: %ld  runocc: %ld\n", que, occ ); /**/
X
X		sleep(PERIOD);
X		getsysinf(&sinf);	/* get new info */
X		nocc = sinf.runocc;
X		nque = sinf.runque;
X
X		n = nocc - occ;		/* get number of times updated */
X		if( n <= 0 ) continue;
X		c = nque - que - n;	/* get number of runners w/o us */
X		if( c < 0 ) c = 0;	/* mumble! */
X
X		new = ((double) c ) / n; /* new instantaneous avg */
X
X		/************************************************/
X		/*   The following formula is used to achieve   */
X		/*   exponential decay of old measurements:	*/
X		/*	avgN = avgN * expN  +  new * (1 - expN)	*/
X		/*						*/
X		/*   However, the factorized forms below	*/
X		/*   require fewer floating point operations.	*/
X		/************************************************/
X
X		avg1 = ((avg1 - new) * exp1) + new;
X		avg2 = ((avg2 - new) * exp2) + new;
X		avg3 = ((avg3 - new) * exp3) + new;
X
X		occ = nocc;
X		que = nque;
X
X	} /* for ever */
X} /* doit */
X
Xgetsysinf(s)
Xstruct sysinfo *s;
X{
X	l_lseek(memfd, (long)nl[NL_SYSINFO].n_value, 0);
X	r_read(memfd, (char *)s, sizeof(struct sysinfo));
X}
X
X/* lseek with error checking */
Xl_lseek(fd, offset, whence)
Xint fd, whence;
Xlong	offset;
X{
X	if (lseek(fd, offset, whence) == -1) {
X		fprintf(stderr, "%s: error on lseek\n", argv0);
X		exit(1);
X	}
X}
X
X/* read with error checking */
Xr_read (fd, buf, nbytes)
Xint	fd, nbytes;
Xchar	*buf;
X{
X	if (read(fd, buf, nbytes) != nbytes) {
X		fprintf(stderr, "%s: error on read\n", argv0);
X		exit(1);
X	}
X}
X
Xinit_packet() {
X#ifdef RWHOD
X	time_t boothz;
X# if UDP
X	struct hostent *he;
X
X	he = gethostbyname( "localnet" );
X	if( he == NULL || he->h_addr == 0 ) {
X		fprintf(stderr, "no address: localnet\n");
X		exit( 1 );
X	}
X	ipaddr = he->h_addr;
X# endif
X# if DSK
X	sprintf(whopacket, "/usr/spool/rwho/whod.%s", utsn.nodename);
X# endif
X	memset(&proto, '\0', sizeof proto);	/* clear proto packet */
X
X	strncat(proto.wd_hostname, utsn.nodename, 9); /* at most 9, add null */
X	proto.wd_vers = WHODVERSION;
X	proto.wd_type = WHODTYPE_STATUS;
X
X	l_lseek(memfd, (long)nl[NL_LBOLT].n_value, 0);
X	r_read(memfd, (char *)&boothz, sizeof( boothz ) );
X	proto.wd_boottime = time(0) - (boothz / HZ);
X#else  SHM
X	if ((aven_key = ftok(SYSTEM, 'a')) == (key_t) -1) {
X		perror(SYSTEM);
X		exit(1);
X	}
X	if ((aven_shm = shmget(aven_key, 3 * sizeof (double), IPC_CREAT|IPC_EXCL|0644)) < 0) {
X		perror("shmget");
X		exit(1);
X	}
X	if ((int) (aven_seg = (double *) shmat(aven_shm, (char *) 0, 0)) == -1) {
X		perror("shmat");
X		if (shmdt((char *) aven_seg) == -1)
X			perror("shmdt");
X		if (shmctl(aven_shm, IPC_RMID, (struct shmid_ds *) 0) < 0)
X			perror("shmctl(IPC_RMID)");
X		exit(1);
X	}
X#endif RWHOD
X	
X} /* init_packet */
X
Xmake_packet(iavg1, iavg2, iavg3)
X#ifdef RWHOD
Xlong iavg1, iavg2, iavg3;
X#else  SHM
Xdouble iavg1, iavg2, iavg3;
X#endif RWHOD
X{
X#ifdef RWHOD
X	static struct whod packet;	/* local packet copy */
X	register struct whoent *wep;	/* pointer to packet whoent */
X	register struct utmp *utp;	/* return from getutent */
X	int whof, cc;			/* output file, char count */
X
X	packet = proto;			/* copy proto packet */
X	time(&packet.wd_sendtime);
X	time(&packet.wd_recvtime);	/* forge this !! */
X	packet.wd_loadav[0] = iavg1;
X	packet.wd_loadav[1] = iavg2;
X	packet.wd_loadav[2] = iavg3;
X
X	setutent();			/* open utmp file */
X	wep = &packet.wd_we[0];		/* get pointer to first user in pkt */
X
X	while( (utp = getutent()) != NULL ) {
X	    if( (utp->ut_type == USER_PROCESS) && utp->ut_user[0]) {
X		strncpy(wep->we_utmp.out_line, utp->ut_id, 4);
X		wep->we_utmp.out_line[4] = '\0';
X
X		strncpy(wep->we_utmp.out_name, utp->ut_user, 8);
X
X		wep->we_utmp.out_time = utp->ut_time;
X
X		wep->we_idle = idletime(utp);
X		wep++;			/* bump packet pointer */
X	    } /* user process */
X	} /* while */
X	endutent();
X
X# if DSK
X	whof = creat(whopacket, 0644);	/* open packt file */
X	if( whof >= 0 ) {
X	    cc = (char *)wep - (char *)&packet;
X	    if( write(whof, (char *)&packet, cc) != cc )
X	    	perror("write failed");
X	    close(whof);
X	} /* file opened */
X	else perror(whopacket);
X# endif
X# if UDP
X	cc = (char *)wep - (char *)&packet;
X	udpsend( (char *)&packet, cc, ipaddr, port, port, 1);
X# endif
X# ifdef DEBUG
X	fprintf(stderr, "wrote packet (%d)\n", cc);
X	fflush(stderr);
X# endif
X#else  SHM
X	aven_seg[0] = iavg1;
X	aven_seg[1] = iavg2;
X	aven_seg[2] = iavg3;
X#endif RWHOD
X} /* make_packet */
X
X#ifdef RWHOD
Xidletime(up)
Xstruct utmp *up;
X{
X    register int i;
X    register char *cp, *dp;
X    char ttyname[10];
X    struct stat buf;
X    time_t now;
X
X    cp = "/dev/";
X    dp = ttyname;
X    
X    while( *cp != '\0' )		/* copy "/dev/" */
X        *dp++ = *cp++;
X
X    cp = up->ut_line;			/* get line name */
X    if( *cp == 's' )			/* starts with an 's'? (sxtnnn) */
X        *dp++ = 'v';			/* insert a 'v' */
X
X    for( i = 0; i < 8; i++ )		/* copy line name */
X        if( (*dp++ = *cp++) == '\0' ) break;	/* or until null */
X
X    if( stat(ttyname, &buf) != 0 )	/* get file status */
X        return( 0 );
X
X    time(&now);				/* get current time */
X    i = now - buf.st_atime;		/* get differnce from last acces */
X    return( i );			/* return idle time */
X} /* idletime */
X#endif RWHOD
________This_Is_The_END________
if test `wc -l < avenrun.c` -ne 342; then
	echo 'shar: avenrun.c was damaged during transit (should have been 342 bytes)'
fi
fi		; : end of overwriting check
echo 'x - csl.c'
if test -f csl.c; then echo 'shar: not overwriting csl.c'; else
sed 's/^X//' << '________This_Is_The_END________' > csl.c
X#include <curses.h>
X#include <a.out.h>
X#include <term.h>
X#include <time.h>
X#include <fcntl.h>
X#include <varargs.h>
X#include <pwd.h>
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/ipc.h>
X#include <sys/shm.h>
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/proc.h>
X#include <sys/user.h>
X#include <sys/var.h>
X#include <sys/inode.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#include <sys/sysmacros.h>
X#include <utmp.h>
X
X#define DISPLAY		5	/* seconds between panels */
X
Xchar SObuf[BUFSIZ], lock[1024], messages[1024], fullname[64], logname[64];
Xdouble *avenrun;
Xint kmem, nproc, mem, swap, mail;
Xdaddr_t swplo;
X
Xextern char *getenv(), *curcmd(), *ttyname(), *strrchr(), *itoa(), *fname(), *strchr();
Xextern int cleanup();
Xextern void setpwent();
Xextern struct passwd *getpwnam();
Xextern long time();
Xextern void setutent();
Xextern struct utmp *getutent();
X
Xstruct nlist k[] = {
X	{"_proc"},
X	{"_v"},
X	{"_swplo"},	
X	{0},
X};
X
Xchar *weekday[] = {
X	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
X};
X
Xmain() {
X	int cnt, nmsg, avend_id, aprogs, panel;
X	char *cp, *cmd;
X	key_t avend_k;
X	struct tm *now;
X	long instant;
X	char msg[1024], subj[1024], from[128], mailf[128];
X	char *fp, *bp, *logtty;
X	struct utmp *user;
X	
X	setupterm((char *) 0, fileno(stdout), (int *) 0);
X	if (!has_status_line) {
X		fprintf(stderr, "csl: terminal doesn't have a status line\n");
X		exit(1);
X	}
X	switch (fork()) {
X	case 0:
X		break;
X	case -1:
X		perror("csl");
X		exit(1);
X	default:
X		exit(0);
X	}
X	signal(SIGINT, SIG_IGN);
X	signal(SIGQUIT, SIG_IGN);
X	signal(SIGHUP, cleanup);
X	freopen("/dev/null", "r", stdin);
X	if (nlist("/unix", k) != 0) {
X		perror("/unix");
X		exit(2);
X	}
X	if ((kmem = open("/dev/kmem", O_RDONLY)) == -1) {
X		perror("/dev/kmem");
X		exit(1);
X	}
X	if ((mem = open("/dev/mem", O_RDONLY)) == -1) {
X		perror("/dev/mem");
X		exit(1);
X	}
X	if ((swap = open("/dev/swap", O_RDONLY)) == -1) {
X		perror("/dev/swap");
X		exit(1);
X	}
X	if ((cp = getenv("LOGNAME")) == (char *) 0)
X		strcpy(logname, "daemon");
X	else
X		strcpy(logname, cp);
X	fname(fullname);
X	getnproc();
X	if ((cp = getenv("HOME")) == (char *) 0) {
X 		strcpy(lock, "./.syslinelock");
X		strcpy(messages, "./.messages");
X	}
X	else {
X		sprintf(lock, "%s/.syslinelock", cp);
X		sprintf(messages, "%s/.messages", cp);
X	}
X	setbuf(stdout, SObuf);
X	if ((avend_k = ftok("/unix", 'a')) == (key_t) -1) {
X		perror("/unix");
X		exit(1);
X	}
X	if ((avend_id = shmget(avend_k, 3 * sizeof (double), 0644)) < 0)
X		avenrun = (double *) 0;
X	else if ((int) (avenrun = (double *) shmat(avend_id, (char *) 0, SHM_RDONLY)) == -1) {
X		perror("shmat");
X		exit(1);
X	}
X	lseek(kmem, k[3].n_value, 0);
X	read(kmem, &swplo, sizeof swplo);
X	logtty = strrchr(ttyname(fileno(stdout)), '/') + 1;
X	sprintf(mailf, "/usr/mail/%s", logname);
X	for (;;) {
X		if (access(lock, 0) == 0) {
X			putp(dis_status_line);
X			fflush(stdout);
X			while (access(lock, 0) == 0)
X				sleep(30);
X		}
X		panel = 1;
X		from[0] = '\0';
X		subj[0] = '\0';
X		if ((mail = open(mailf, O_RDONLY)) == -1)
X			nmsg = 0;
X		else {
X			cnt = 0;
X			while (rgets(mail, msg, sizeof msg) != -1) {
X				if (strncmp(msg, "From ", 5) == 0) {
X					int mcnt;
X
X					if (from[0] != '\0')
X						showpanel(panel++, "New mail from %s: %s", from, (subj[0]? subj: "[No subject]"));
X					subj[0] = '\0';
X					cnt++;
X					for (mcnt = 5; msg[mcnt] != '\0'; mcnt++)
X						if (msg[mcnt] == ' ')
X							break;
X					msg[mcnt++] = '\0';
X					if ((fp = strrchr(msg + 5, '!')) == (char *) 0)
X						strcpy(from, msg + 5);
X					else {
X						*fp = '\0';
X						if ((bp = strrchr(msg + 5, '!')) == (char *) 0)
X						bp = msg + 5;
X						*fp = '!';
X						strcpy(from, bp);
X					}
X					subj[0] = '\0';
X				}
X				else if (strncmp(msg, "Subject: ", 9) == 0)
X					strcpy(subj, msg + 9);
X			}
X			close(mail);
X			if (from[0] != '\0')
X				showpanel(panel++, "New mail from %s: %s", from, (subj[0]? subj: "[No subject]"));
X			nmsg = cnt;
X		}
X		cmd = curcmd(&aprogs);
X		time(&instant);
X		now = localtime(&instant);
X		if (avenrun != (double *) 0)
X			showpanel(panel++, " %s // %s // %4.2f %4.2f %4.2f // %s msgs // %3s %02d/%02d %02d:%02d ", cmd, logname, avenrun[0], avenrun[1], avenrun[2], (nmsg == 0? "No": itoa(nmsg)), weekday[now->tm_wday], now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min);
X		else
X			showpanel(panel++, " %s // %s // %s msgs // %3s %02d/%02d %02d:%02d //", cmd, logname, (nmsg == 0? "No": itoa(nmsg)), weekday[now->tm_wday], now->tm_mon + 1, now->tm_mday, now->tm_hour, now->tm_min);
X		showpanel(panel++, " ``%s'' on %s // %d active programs", fullname, logtty, aprogs);
X		if ((mail = open(messages, O_RDONLY)) != -1) {
X			while (rgets(mail, msg, sizeof msg) != -1)
X				showpanel(panel++, "%s", msg);
X			close(mail);
X		}
X		setutent();
X		cnt = 0;
X		msg[0] = '\0';
X		while ((user = getutent()) != (struct utmp *) 0) {
X			if (user->ut_type != USER_PROCESS)
X				continue;
X			sprintf(subj, "%.8s", user->ut_user);
X			if (msg[0] != '\0')
X				strcat(msg, ", ");
X			strcat(msg, subj);
X			cnt++;
X		}
X		showpanel(panel++, " %d users: %s", cnt, msg);
X	}
X}
X
Xcleanup() {
X	if (avenrun != (double *) 0 && shmdt(avenrun) < 0) {
X		perror("shmdt");
X		exit(1);
X	}
X	reset_shell_mode();
X	exit(0);
X}
X
Xgetnproc() {
X	struct var v;
X
X	lseek(kmem, k[1].n_value, 0);
X	read(kmem, &v, sizeof v);
X	nproc = v.v_proc;
X}
X
Xchar *curcmd(progp)
Xint *progp; {
X	struct proc p;
X	struct user cmd;
X	struct file f;
X	struct inode i;
X	static struct stat idev;
X	static int pgrp, mypid;
X	static char cmdname[DIRSIZ + 1];
X	long stime;
X	int cnt;
X	static int didit = 0;
X	
X	if (didit == 0) {
X		stat("/dev/null", &idev);
X		pgrp = getpgrp();
X		mypid = getpid();
X		didit = 1;
X	}
X	*progp = 0;
X	stime = 0L;
X	strcpy(cmdname, "???");
X	lseek(kmem, k[0].n_value, 0);
X	for (cnt = 0; cnt < nproc; cnt++) {
X		read(kmem, &p, sizeof p);
X		if (p.p_stat != SRUN && p.p_stat != SSLEEP)
X			continue;
X		if (p.p_pid == mypid)
X			continue;
X		if (p.p_pgrp != pgrp)
X			continue;
X		if (p.p_stat == SSLEEP && (long) p.p_wchan >= k[0].n_value && (long) p.p_wchan < k[0].n_value + nproc * sizeof p)
X			continue;	/* sleeping on a child */
X		(*progp)++;
X		if (p.p_flag & SLOAD) {
X			lseek(mem, (long) (NBPC * p.p_addr), 0);
X			read(mem, &cmd, sizeof cmd);
X		}
X		else {
X			lseek(swap, (swplo + p.p_swaddr + ctod(p.p_swsize) - ctod(USIZE)) << 9, 0);
X			read(swap, &cmd, sizeof cmd);
X		}
X		if (cmd.u_ttyp == 0)
X			continue;
X/* AAUGH!  USG VAR csh'es close stdin!!!  That's absolutely insane!!!
X *		if (p.p_pid != p.p_ppid && cmd.u_ofile[0] == 0)
X *			continue;
X */
X		if (cmd.u_ofile[0] != 0) {
X			lseek(kmem, cmd.u_ofile[0], 0);
X			read(kmem, &f, sizeof f);
X			lseek(kmem, f.f_inode, 0);
X			read(kmem, &i, sizeof i);
X			lseek(kmem, k[0].n_value + cnt * sizeof p, 0);
X			if (i.i_dev == idev.st_dev && i.i_number == idev.st_ino && cmd.u_signal[SIGINT] == (long) SIG_IGN)
X				continue;	/* ignore background processes */
X		}
X		if (cmd.u_start < stime)
X			continue;
X		stime = cmd.u_start;
X		strncpy(cmdname, cmd.u_comm, DIRSIZ);
X	}
X	return cmdname;
X}
X
Xrgets(fd, buf, maxlen)
Xchar *buf;
Xunsigned maxlen; {
X	unsigned cnt;
X	
X	*buf = '\0';
X	for (cnt = 0; cnt < maxlen - 1 && read(fd, buf, 1) > 0 && *buf != '\n'; cnt++, buf++)
X		;
X	if (cnt == 0 && *buf != '\n')
X		return -1;
X	*buf = '\0';
X	return cnt;
X}
X
Xchar *itoa(n) {
X	static char ibuf[20];
X	
X	sprintf(ibuf, "%d", n);
X	return ibuf;
X}
X
X/* Does the lint declaration below actually *do* anything? */
X/*VARARGS PRINTFLIKE2*/
Xshowpanel(va_alist)
Xva_dcl {
X	va_list args;
X	int panel, cnt;
X	static char buf[5120];
X	register char *cp;
X
X	va_start(args);
X	panel = va_arg(args, int);
X	cp = va_arg(args, char *);
X	sprintf(buf, "%2.2d>", panel);
X	vsprintf(buf + 3, cp, args);
X	putp(tparm(to_status_line, 0));
X	for (cp = buf, cnt = 0; cnt < width_status_line && *cp != '\0'; cp++, cnt++)
X		putchar(*cp);
X	while (cnt++ < width_status_line)
X		putchar(' ');
X	putp(from_status_line);
X	fflush(stdout);
X	sleep(DISPLAY);
X}
X
X/*
X * If you use RJE fullnames :0000-Foo Z. Bar(0000):, change this.
X * If you aren't running RJE, why bother with it?
X */
X
Xchar *fname(buf)
Xchar *buf; {
X	static char fbuf[128];
X	struct passwd *me;
X	char *cp;
X
X	setpwent();
X	me = getpwnam(logname);
X	endpwent();
X	if (me == (struct passwd *) 0)
X		strcpy(fbuf, logname);
X	else {
X		strcpy(fbuf, me->pw_gecos);
X		if ((cp = strchr(fbuf, ',')) != (char *) 0)
X			*cp = '\0';
X	}
X	if (buf == (char *) 0)
X		return fbuf;
X	strcpy(buf, fbuf);
X	return buf;
X}
________This_Is_The_END________
if test `wc -l < csl.c` -ne 348; then
	echo 'shar: csl.c was damaged during transit (should have been 348 bytes)'
fi
fi		; : end of overwriting check
echo 'x - w.c'
if test -f w.c; then echo 'shar: not overwriting w.c'; else
sed 's/^X//' << '________This_Is_The_END________' > w.c
X/*
X * %W% %E% %U% ncoast!bsa %Z%
X * %Z% Copyright (C) 1985 by Brandon S. Allbery, All Rights Reserved %Z%
X */
X
X#ifndef lint
Xstatic char _SccsId[] = "%W% %E% %U% ncoast!bsa %Z%";
Xstatic char _CopyRt[] = "%Z% Copyright (C) 1985 by Brandon S. Allbery %Z%";
X#endif  lint
X
X#include <stdio.h>
X#include <time.h>
X#include <signal.h>
X#include <a.out.h>
X#ifdef SYS5
X#include <sys/types.h>
X#endif
X#include <sys/param.h>
X#include <sys/var.h>
X#include <sys/proc.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#include <sys/stat.h>
X#ifdef SYS5
X#include <sys/ipc.h>
X#include <sys/shm.h>
X#include <sys/sysmacros.h>
X#endif
X#include <utmp.h>
X
X#define UTMP		"/etc/utmp"
X#ifndef KERNEL
X#define KERNEL		"/unix"
X#endif
X#define KMEM		"/dev/kmem"
X#define PMEM		"/dev/mem"
X#define SMEM		"/dev/swap"
X
X#define MIN		(60)
X#define HOUR		(MIN * 60)
X#define DAY		(HOUR * 24)
X#define WEEK		(DAY * 7)
X#define MONTH		(DAY * 30)
X
XFILE *kfd;
XFILE *mfd;
XFILE *sfd;
XFILE *utmp;
Xlong nproc;
Xchar _SObuf[BUFSIZ];
Xdaddr_t swplo;
Xshort mypid;
X
Xstruct nlist kernel[] = {
X	{"_v"},
X	{"_proc"},
X	{"_swplo"},
X	{NULL},
X};
X
Xchar *months[] = {
X	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
X	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X};
X
Xstruct tm *localtime();
Xchar *vtime();
Xchar *uptime();
Xchar *itoa();
X
Xmain() {
X	struct utmp user;
X	short ucnt;
X	long now;
X	struct var vars;
X#ifdef SYS5
X	int shm_id;
X	key_t shm_k;
X	double *avenrun;
X#endif
X	
X	setbuf(stdout, _SObuf);
X	mypid = getpid();
X	time(&now);
X	if (nlist(KERNEL, kernel) == -1) {
X		perror(KERNEL);
X		exit(2);
X	}
X	if ((utmp = fopen(UTMP, "r")) == NULL) {
X		perror(UTMP);
X		exit(1);
X	}
X	ucnt = 0;
X	while (fread(&user, sizeof user, 1, utmp) > 0) {
X#ifdef SYS5
X		if (user.ut_type != USER_PROCESS)
X#else
X		if (user.ut_name[0] == '\0')
X#endif
X			continue;
X		ucnt++;
X	}
X	if (kfd == NULL) {
X		if ((kfd = fopen(KMEM, "r")) == NULL) {
X			perror(KMEM);
X			exit(3);
X		}
X	}
X	fseek(kfd, kernel[0].n_value, 0);
X	fread(&vars, sizeof vars, 1, kfd);
X	nproc = vars.v_proc;
X	fseek(kfd, kernel[2].n_value, 0);
X	fread(&swplo, sizeof swplo, 1, kfd);
X	if (mfd == NULL) {
X		if ((mfd = fopen(PMEM, "r")) == NULL) {
X			perror(PMEM);
X			exit(4);
X		}
X	}
X	if (sfd == NULL) {
X		if ((sfd = fopen(SMEM, "r")) == NULL) {
X			perror(SMEM);
X			exit(5);
X		}
X	}
X#ifdef SYS5
X	if ((shm_k = ftok(KERNEL, 'a')) == (key_t) -1) {
X		perror(KERNEL);
X		exit(2);
X	}
X	if ((shm_id = shmget(shm_k, 3 * sizeof *avenrun, 0644)) < 0) {
X		perror("shmget()");
X		exit(2);
X	}
X	if ((int) (avenrun = (double *) shmat(shm_id, (char *) 0, SHM_RDONLY)) == -1) {
X		perror("shmat()");
X		exit(2);
X	}
X	printf(" %s  up%s,  %d users,  load average: %.1f, %.1f, %.1f\nUser     tty       login@  idle   JCPU   PCPU what\n", vtime(&now), uptime(), ucnt, avenrun[0], avenrun[1], avenrun[2]);
X	if (shmdt((char *) avenrun) == -1) {
X		perror("shmdt()");
X		exit(2);
X	}
X#else
X	printf(" %s  up%s,  %d users,  load average: not available under sys3\nUser     tty       login@  idle   JCPU   PCPU what\n", vtime(&now), uptime(), ucnt);
X#endif
X	rewind(utmp);
X	while (fread(&user, sizeof user, 1, utmp) > 0) {
X#ifdef SYS5
X		if (user.ut_type != USER_PROCESS)
X#else
X		if (user.ut_name[0] == '\0')
X#endif
X			continue;
X		show(&user);
X	}
X	fclose(utmp);
X	exit(0);
X}
X
Xchar *uptime() {
X	static char timebuf[128];
X#ifdef SYS5
X	struct utmp bootdata;
X#else
X	struct proc swapper;
X	struct user bootproc;
X#endif
X	long oldpos, now;
X	short cnt, ocnt;
X	
X#ifdef SYS5
X	oldpos = fseek(utmp, 0L, 0);
X	do {
X		fread(&bootdata, sizeof bootdata, 1, utmp);
X	} while (bootdata.ut_type != BOOT_TIME);
X	fseek(utmp, oldpos, 0);
X#else
X	oldpos = fseek(kfd, 0L, 1);
X	fseek(kfd, kernel[1].n_value, 0);
X	fread(&swapper, sizeof swapper, 1, kfd);
X	fseek(kfd, oldpos, 0);
X	oldpos = fseek(mfd, 0L, 1);
X	fseek(mfd, ctob(swapper.p_addr), 0);
X	fread(&bootproc, sizeof bootproc, 1, mfd);
X	fseek(mfd, 0L, 1);
X#endif
X	time(&now);
X#ifdef SYS5
X	now -= bootdata.ut_time;
X#else
X	now -= bootproc.u_start;
X#endif
X	if (now < 0L)
X		return " with strange clock time";
X	timebuf[0] = '\0';
X	ocnt = 0;
X	cnt = 0;
X	while (now >= MONTH) {
X		cnt++;
X		now -= MONTH;
X	}
X	if (cnt > 0) {
X		strcat(timebuf, itoa(cnt));
X		strcat(timebuf, " mon");
X		if (cnt > 1)
X			strcat(timebuf, "s");
X		if (++ocnt == 2)
X			return timebuf;
X	}
X	cnt = 0;
X	while (now >= WEEK) {
X		cnt++;
X		now -= WEEK;
X	}
X	if (cnt > 0) {
X		strcat(timebuf, itoa(cnt));
X		strcat(timebuf, " wk");
X		if (cnt > 1)
X			strcat(timebuf, "s");
X		if (++ocnt == 2)
X			return timebuf;
X	}
X	cnt = 0;
X	while (now >= DAY) {
X		cnt++;
X		now -= DAY;
X	}
X	if (cnt > 0) {
X		strcat(timebuf, itoa(cnt));
X		strcat(timebuf, " day");
X		if (cnt > 1)
X			strcat(timebuf, "s");
X		if (++ocnt == 2)
X			return timebuf;
X	}
X	cnt = 0;
X	while (now >= HOUR) {
X		cnt++;
X		now -= HOUR;
X	}
X	if (cnt > 0) {
X		strcat(timebuf, itoa(cnt));
X		strcat(timebuf, " hr");
X		if (cnt > 1)
X			strcat(timebuf, "s");
X	}
X	return timebuf;
X}
X
Xchar *itoa(n)
Xint n; {
X	static char buf[20];
X	
X	sprintf(buf, " %d", n);
X	return buf;
X}
X
Xshow(uinfo)
Xstruct utmp *uinfo; {
X	struct stat sbuf;
X	struct proc proc;
X	struct user prog;
X	char ttydev[16];
X	long now, cnt, offset, jcpu;
X	short mpid, isswap;
X	FILE *ufd;
X	
X	strcpy(ttydev, "/dev/");
X	strncpy(&ttydev[5], uinfo->ut_line, 8);
X	ttydev[13] = '\0';
X	if (stat(ttydev, &sbuf) != 0) {
X		perror(ttydev);
X		return;
X	}
X	time(&now);
X	now -= sbuf.st_atime;
X	printf("%-8.8s %-8.8s %7s ", uinfo->ut_name, uinfo->ut_line, vtime(&uinfo->ut_time));
X	if (now > DAY)
X		printf("%4dd", now / DAY);
X	else if (now > HOUR)
X		printf("%2d:%02d", now / HOUR, (now % HOUR) / 60);
X	else if (now > MIN)
X		printf("%5d", now / MIN);
X	else
X		printf("     ");
X	putchar(' ');
X	fseek(kfd, kernel[1].n_value, 0);
X	mpid = -1;
X	jcpu = 0;
X	for (cnt = 0; cnt < nproc; cnt++) {
X		fread(&proc, sizeof proc, 1, kfd);
X		if (proc.p_stat == 0 || proc.p_stat == SZOMB || proc.p_stat == SWAIT)
X			continue;
X		if (proc.p_flag & SLOAD) {
X			fseek(mfd, ctob(proc.p_addr), 0);
X			fread(&prog, sizeof prog, 1, mfd);
X			isswap = 0;
X		}
X		else {
X#ifdef SYS5
X			fseek(sfd, (swplo + proc.p_swaddr + ctod(proc.p_swsize) - ctod(USIZE)) << 9, 0);
X#else
X			fseek(sfd, (long) (swplo + proc.p_addr) << 9, 0);
X#endif
X			fread(&prog, sizeof prog, 1, sfd);
X			isswap = 1;
X		}
X		if (prog.u_ttyp == NULL)
X			continue;
X		if (sbuf.st_rdev != prog.u_ttyd)
X			continue;
X		jcpu += prog.u_utime + prog.u_stime;
X		if (proc.p_pid == mypid)
X			continue;
X		if (proc.p_pid > mpid)
X			mpid = proc.p_pid;
X	}
X	if (mpid == -1)
X		printf("[can't stat]");
X	else {
X		fseek(kfd, kernel[1].n_value, 0);
X		for (cnt = 0; cnt < nproc; cnt++) {
X			fread(&proc, sizeof proc, 1, kfd);
X			if (proc.p_pid == mpid)
X				break;
X		}
X		if (proc.p_pid != mpid)
X			printf("[interstice]");
X		else {
X			if (proc.p_flag & SLOAD) {
X				fseek(mfd, ctob(proc.p_addr), 0);
X				fread(&prog, sizeof prog, 1, mfd);
X			}
X			else {
X#ifdef SYS5
X				fseek(sfd, (swplo + proc.p_swaddr + ctod(proc.p_swsize) - ctod(USIZE)) << 9, 0);
X#else
X				fseek(sfd, (long) (swplo + proc.p_addr) << 9, 0);
X#endif
X				fread(&prog, sizeof prog, 1, sfd);
X			}
X			printf("%3d:%02d %3d:%02d ", jcpu / MIN, jcpu % MIN, (prog.u_utime + prog.u_stime) / MIN, (prog.u_utime + prog.u_stime) % MIN);
X			prog.u_procp = &proc;
X			pcmd(&prog);
X		}
X	}
X	putchar('\n');
X}
X
Xchar *vtime(when)
Xlong *when; {
X	struct tm then, now;
X	static char buf[20];
X	short hour, min, ampm;
X	long clock;
X
X	time(&clock);
X	now = *localtime(&clock);
X	then = *localtime(when);
X	if (then.tm_mon != now.tm_mon || then.tm_mday != now.tm_mday) {
X		sprintf(buf, "%s %2d", months[then.tm_mon], then.tm_mday);
X		return buf;
X	}
X	min = then.tm_min;
X	if (then.tm_hour == 0) {
X		ampm = 'a';
X		hour = 12;
X	}
X	else if (then.tm_hour > 0 && then.tm_hour < 12) {
X		ampm = 'a';
X		hour = then.tm_hour;
X	}
X	else if (then.tm_hour == 12) {
X		ampm = 'p';
X		hour = 12;
X	}
X	else {
X		ampm = 'p';
X		hour = then.tm_hour - 12;
X	}
X	sprintf(buf, "%d:%02d%cm", hour, min, ampm);
X	return buf;
X}
X
Xpcmd(uinfo)
Xstruct user *uinfo; {
X	/* someday look up the user's command line */
X	printf("%-.14s", uinfo->u_comm);
X}
________This_Is_The_END________
if test `wc -l < w.c` -ne 390; then
	echo 'shar: w.c was damaged during transit (should have been 390 bytes)'
fi
fi		; : end of overwriting check
echo 'x - avenrun.1m'
if test -f avenrun.1m; then echo 'shar: not overwriting avenrun.1m'; else
sed 's/^X//' << '________This_Is_The_END________' > avenrun.1m
X.TH AVENRUN 1M
X.SH NAME
Xavenrun \- compute load averages
X.SH SYNOPSIS
X.B avenrun
X.SH DESCRIPTION
X.B
XAvenrun
Xis a program to generate load average numbers on systems running AT&T's
Xversion of UNIX(R).  (UNIX is a registered trademark of AT&T.)  It does
Xits job by creating a shared memory segment whose key is the value of
X.I ftok(\"/unix\", 'a')
Xwhich contains three (double)'s.  It then updates this structure with the
X1-minute, 5-minute, and 15-minute load averages, as calculated from the
Xsysinfo structure of the kernel (the "runque" and "runocc" entries).
X.P
XThe load average is only an approximation of the actual number, which is
Xthe average number of processes on the run queue.  It is about as close as
Xyou can get without actually computing the load average in the kernel as
Xprocesses are placed on the run queue.
X.SH FILES
X.ta \w'/dev/kmem    'u
X/unix	system namelist
X.br
X/dev/kmem	kernel memory, for the sysinfo structure
X.DT
X.SH SEE ALSO
Xstdipc(3), shmop(2), sa(4).
X.SH BUGS
XIt really should be in the kernel.
________This_Is_The_END________
if test `wc -l < avenrun.1m` -ne 30; then
	echo 'shar: avenrun.1m was damaged during transit (should have been 30 bytes)'
fi
fi		; : end of overwriting check
echo 'x - csl.1l'
if test -f csl.1l; then echo 'shar: not overwriting csl.1l'; else
sed 's/^X//' << '________This_Is_The_END________' > csl.1l
X.TH CSL local
X.SH NAME
Xcsl \- cyclic status line
X.SH SYNOPSIS
X.B csl
X.SH DESCRIPTION
X.B Csl
Xis a program for displaying interesting information on the status line
Xof a terminal which possesses one.  The information is shown in "panels"
Xwhich are displayed for five seconds before going to the next panel.
XAfter the last panel is displayed, the first panel will be redisplayed.
X.PP
XThe panels display information in the following order:
X.IP
X- mail messages, one per panel; sender and subject
X.IP
X- current program, login name, load average, current time
X.IP
X- full name, terminal, number of active programs
X.IP
X- lines from $HOME/.messages, one per panel
X.IP
X- current users on the system
X.PP
XAn example of some successive panels:
X.nf
X
X	01>From bobw: Re: x10.bas
X	02>From root: [No subject]
X	03> jove // brandon // 0.76 1.02 0.95 // Thu 05/08 16:56
X	04> "Brandon Allbery" on tty2 // 3 active programs
X	05>START THE MONTHLY REPORTS!!!
X
X.fi
X.PP
X.B Csl
Xlooks for terminfo definitions "hsl", "tsl", "fsl", "wsl", and "dsl".
XIt does not make use of the parameterization feature except to position
Xto the first column of the status line, so non-addressable status lines
X(such as on the Wyse 50 and Wyse 75) can be used.
X.P
XIf the file
X.I $HOME/.syslinelock
Xis found to exist at the beginning of a cycle,
X.B csl
Xwaits until the file disappears before performing any more displays.
XThis is useful when 
X.B csl
Xupsets the display from a particular program, or on terminals which
Xdo not accept keyboard commands while the status line is being loaded
X(e.g. the Wyse 75) when rapid keyboard input is required.
X.SH FILES
X.ta \w'$HOME/.syslinelock    'u
X$HOME/.syslinelock	Disables status line
X.br
X$HOME/.messages	Scanned for messages to display
X.br
X/usr/mail/$LOGNAME	Scanned for new mail
X.br
X/etc/passwd	The user's full name is stored here
X.br
X/etc/utmp	Current users, and the current user's terminal
X.DT
X.SH NOTES
X.B Csl
Xwill refuse to run on a terminal whose terminfo description does not
Xinclude the boolean attribute "hsl".  See terminfo(4) for information
Xabout the status line attributes of terminfo.
X.PP
XThe full name of a user is expected to be the (only) entry in the pw_gecos
Xfield of /etc/passwd for that user (see
X.B getpwent(3) .
XRJE format (of the form
X"0000-user(0000)") is not understood.
X.SH SEE ALSO
Xterminfo(4), avenrun(1m).
X.SH BUGS
X.B Csl
Xshould scan for $HOME/.syslinelock after each panel, rather than at
Xthe beginning of a cycle.
X.PP
XThings can change while
X.I csl
Xis displaying a panel; in general, changes are not seen until the next
Xcycle.
X.PP
XFull names are not always easy to figure out under System V.  Some sites
Xuse the RJE format, others use the BSD format.
X.B Csl
Xuses BSD because it's straightforward, the full name is the pw_gecos field
Xup to a comma.
X.PP
XBecause VAR versions of
X.B csh(1)
Xinsist on closing their fd 0,
X.B csl
Xmust admit such programs as being running in the foreground.  This causes
Xvarious lies about the name of the current process.
X.PP
XOn systems running MH, the mail checking routines will fail; they
Xexpect "normal" UUCP mailboxes, not MMDF maildrops.  Perhaps the
Xbest fix for this is to run
X.B ims(local)
Xinstead.
X.PP
XIt isn't
X.B statline(1) .
X(This may be a feature.)
________This_Is_The_END________
if test `wc -l < csl.1l` -ne 108; then
	echo 'shar: csl.1l was damaged during transit (should have been 108 bytes)'
fi
fi		; : end of overwriting check
echo 'x - w.1l'
if test -f w.1l; then echo 'shar: not overwriting w.1l'; else
sed 's/^X//' << '________This_Is_The_END________' > w.1l
X.TH W 1 local
X.SH NAME
Xw \- display users and processes
X.SH SYNOPSIS
X.B w
X.SH DESCRIPTION
X.B W
Xis a program which displays an "intelligent" listing of the current users,
Xwhat they're doing, and how active they are.  There are two basic kinds
Xof information displayed:  system information and per-user information.
XAn example is shown below.
X.nf
X
X 10:55pm  up 2 wks 2 days,  4 users,  load average: 1.2, 1.0, 0.8
XUser     tty       login@  idle   JCPU   PCPU what
Xrhg      tty6     10:36pm        12:20  12:20 csh
Xrobertd  tty7     10:21pm         9:21   9:21 csh
Xbobw     tty13    10:26pm       160:35 149:43 rn
Xallbery  tty15     9:47pm        50:22   0:18 sh
X
X.fi
XThe first line displays the current time, how long the system has been up,
Xthe number of users, and the load average (if available; not under System
XIII).  The other lines display for each user, the user's login name, the
Xterminal the user is on, the time the user logged in, how long the user has
Xbeen idle, the CPU time used by the current program and total CPU for the
Xlogin session, and the current program.
X.SH FILES
X.ta \w'/dev/kmem    'u
X/dev/kmem	System memory (the process table)
X.br
X/dev/mem	In-core program images
X.br
X/dev/swap	Swapped program images
X.br
X/dev	Searches for terminals
X.br
X/etc/utmp	Current system users and boot time
X.DT
X.SH NOTES
X.B W
Xdisplays the process name as shown by
X.B ps(1)
Xwithout the -f argument.
X.SH SEE ALSO
Xavenrun(1m), ps(1), who(1).
X.SH BUGS
XJCPU and current process are both kludges.  The former is really only the
XCPU of running programs in the terminal session, as System V does not retain
Xuser and system times for all programs in a sessionl; the latter attempts to
Xdisregard background processes, but it is nearly impossible to successfully
Xdetermine if a program is in the background or not.  This is exacerbated by
Xthe fact that VAR csh(1)'s, when available, look suspiciously like
Xbackground processes because they close their standard input.
X.PP
XIf the user block is demand paged, 
X.B w
Xwon't find it; I don't have access to a demand-paged system.
X.PP
X.B who -u
Xand
X.B w
Xhave different ideas on what constitutes idle time; one uses time of last
Xinput, the other the time of last output.
X.PP
XThe boot time isn't guaranteed to be correct if you don't have a real time
Xclock with battery backup.  There is also a kludge which will not affect
Xnormal systems, but which deals with a tendency for Plexus systems to
Xforward-date the system clock by one month.
X.PP
XIt is possible that reading the summarized child's system and user times
Xwould produce a better approximation of JCPU.  This is only likely, however,
Xif the times are updated recursively.
X.PP
XThings can change while 
X.B w
Xis running; this occasionally causes the current program to be printed as
X"[can't stat]" or as "[interstice]".
X.PP
XIf you want "w" to work correctly, get 4.2BSD.
X.PP
XIt should really take options for the system namelist, memory, and
Xswap files, as well as the key of the shared memory partition; it should
Xalso recover gracefully if avenrun(1m) isn't running.
________This_Is_The_END________
if test `wc -l < w.1l` -ne 84; then
	echo 'shar: w.1l was damaged during transit (should have been 84 bytes)'
fi
fi		; : end of overwriting check
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/2400 baud>>