[comp.sources.misc] v05i061: "w" for Xenix/386

jfh@rpp386.UUCP (The Beach Bum) (11/27/88)

Posting-number: Volume 5, Issue 61
Submitted-by: "The Beach Bum" <jfh@rpp386.UUCP>
Archive-name: xenix.w

[This has been waiting for me to fold it into the main "w" code; I've finally
admitted that it isn't going to happen anytime soon, so here it is.  Sigh.
++bsa]

i have hacked the hell out of your "w" command to produce one which
works on sco xenix for a '386.  i don't know what order you will
receive the submissions in, but this is one of two.  the other is
a fuser command.  oh yes, both of them need to be re-wrapped because
my shar doesn't know about the perils of being mailed about.

- john.	(jfh@rpp386)
--- beginning for w.shar ---
#! /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:
#	README
#	w.1l
#	w.c
# This archive created: Fri Jul  8 09:15:25 1988
# By:	The Beach Bum (Big "D" Home for Wayward Hackers)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
[ This is the original README which was included with this package.  Note
  that only "w" works.  "avenrun" depends on system statistics which are
  not present in Xenix, and "csl" depends, in part, on "avenrun". -jfh ]

The following are some programs which give you some familiar BSD utilities
in a System V environment.  The programs are:

       w       - Yes, "w", as big as life!  Requires...
       avenrun - A program to be invoked in /etc/rc which computes
                 a "load average" from information in the "sysinfo"
                 structure of the kernel.  Based on a program by
                 Phil Budne which forges "rwhod" packets for mixed
                 networks; #define RWHOD for the original program.
       csl     - I have no idea what the BSD "sysline" does, but this
                 is my idea of a status line.  The name stands for
                 "cyclic status line"; it doesn't even attempt to
                 fit everything into one line, instead it cycles
                 through a set of "panels".  See the man page.
                 This also wants "avenrun" to be running.

Note that "w" (and ONLY "w") can be compiled for System III or Xenix 3.0;
this is in fact the default, use -DSYS5 to get the full version.

No Makefile; the compile commands are trivial:

       cc -O -o w w.c -DSYS5           # omit the -DSYS5 for System III
       cc -O -o avenrun avenrun.c      # add -DRWHOD for rwhod forgery
       cc -O -o csl csl.c

"w" requires read permission on /dev/kmem, /dev/mem, and /dev/swap.  "avenrun"
requires read permission on /dev/kmem (to read the sysinfo structure).  "csl"
can run as a normal user program.  On tdi2 we keep /dev/*mem and /dev/swap
-r--r----- root/sys and have "avenrun" and "w" setgid sys.

If someone wants to tell me what "sysline" under BSD does, I'd be interested
in writing a real one.  But "csl" is everything in one small package, so I
will probably continue to use it.

Enjoy!

++Brando
SHAR_EOF
fi
if test -f 'w.1l'
then
	echo shar: "will not over-write existing file 'w.1l'"
else
cat << \SHAR_EOF > 'w.1l'
.TH W 1 local
.SH NAME
w \- display users and processes
.SH SYNOPSIS
.B w
.SH DESCRIPTION
.B W
is a program which displays an "intelligent" listing of the current users,
what they're doing, and how active they are.  There are two basic kinds
of information displayed:  system information and per-user information.
An example is shown below.
.nf

 10:55pm  up 2 wks 2 days,  4 users
User     tty       login@  idle   JCPU   PCPU what
rhg      tty6     10:36pm        12:20  12:20 csh
robertd  tty7     10:21pm         9:21   9:21 csh
bobw     tty13    10:26pm       160:35 149:43 rn
allbery  tty15     9:47pm        50:22   0:18 sh

.fi
The first line displays the current time, how long the system has been up,
and the number of users.
The other lines display for each user, the user's login name, the
terminal the user is on, the time the user logged in, how long the user has
been idle, the CPU time used by the current program and total CPU for the
login session, and the current program.
.SH FILES
.ta \w'/dev/kmem    'u
/dev/kmem      System memory (the process table)
.br
/dev/mem       In-core program images
.br
/dev/swap      Swapped program images
.br
/dev   Searches for terminals
.br
/etc/utmp      Current system users and boot time
.br
/xenix         System namelist
.DT
.SH NOTES
.B W
displays the process name as shown by
.B ps(1)
without the -f argument.
.SH SEE ALSO
ps(1), who(1).
.SH BUGS
JCPU and current process are both kludges.  The former is really only the
CPU of running programs in the terminal session, as Xenix does not retain
user and system times for all programs in a session; the latter attempts to
disregard background processes, but it is nearly impossible to successfully
determine if a program is in the background or not.  This is exacerbated by
the fact that VAR csh(1)'s, when available, look suspiciously like
background processes because they close their standard input.
.PP
If the user block is demand paged, 
.B w
won't find it; I don't have access to a demand-paged system.
.PP
.B who -u
and
.B w
have different ideas on what constitutes idle time; one uses time of last
input, the other the time of last output.
.PP
It is possible that reading the summarized child's system and user times
would produce a better approximation of JCPU.  This is only likely, however,
if the times are updated recursively.
.PP
Things can change while 
.B w
is running; this occasionally causes the current program to be printed as
"[can't stat]" or as "[interstice]".
.PP
If you want "w" to work correctly, get 4.2BSD.
.PP
It should really take options for the system namelist, memory, and
swap files.
.PP
Because Xenix lacks the appropriate kernel variables, load averages are
not available.
.SH CREDIT
Based on a utility written at the University of California at Berkeley.
Original written by Brandon S. Allbery.
Modified for SCO Xenix 386 by John F. Haugh II (jfh@rpp386).
SHAR_EOF
fi
if test -f 'w.c'
then
	echo shar: "will not over-write existing file 'w.c'"
else
cat << \SHAR_EOF > 'w.c'
/*
 * %W% %E% %U% ncoast!bsa %Z%
 * %Z% Copyright (C) 1985 by Brandon S. Allbery, All Rights Reserved %Z%
 *
 *	8-Jul-88 John F. Haugh II (jfh@rpp386)
 *	Major hacks to force to work on SCO Xenix 386
 */

#ifndef lint
static char _SccsId[] = "%W% %E% %U% ncoast!bsa %Z%";
static char _CopyRt[] = "%Z% Copyright (C) 1985 by Brandon S. Allbery %Z%";
#endif  lint

#include <stdio.h>
#include <time.h>
#include <signal.h>
#include <a.out.h>
#include <sys/types.h>
#include <sys/page.h>
#include <sys/seg.h>
#include <sys/param.h>
#include <sys/var.h>
#include <sys/proc.h>
#include <sys/dir.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sysmacros.h>
#include <utmp.h>

#define UTMP           "/etc/utmp"
#ifndef KERNEL
#define KERNEL         "/xenix"
#endif
#define KMEM           "/dev/kmem"
#define PMEM           "/dev/mem"
#define SMEM           "/dev/swap"

#define MIN            (60)
#define HOUR           (MIN * 60)
#define DAY            (HOUR * 24)
#define WEEK           (DAY * 7)
#define MONTH          (DAY * 30)

FILE *kfd;
FILE *mfd;
FILE *sfd;
FILE *utmp;
long nproc;
char _SObuf[BUFSIZ];
daddr_t swplo;
short mypid;

struct xlist kernel[] = {
	{0, 0, 0, "_v"},
	{0, 0, 0, "_proc"},
	{0, 0, 0, "_swplo"},
	{0, 0, 0, "_lbolt"},
	{0, 0, 0, (char *) 0},
};

char *months[] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
};

struct tm *localtime();
char *vtime();
char *uptime();
char *itoa();

main() {
	struct utmp user;
	short ucnt;
	long now;
	struct var vars;
	
	setbuf(stdout, _SObuf);
	mypid = getpid();
	time(&now);
	if (xlist(KERNEL, kernel) == -1) {
		perror(KERNEL);
		exit(2);
	}
	if ((utmp = fopen(UTMP, "r")) == NULL) {
		perror(UTMP);
		exit(1);
	}
	ucnt = 0;
	while (fread(&user, sizeof user, 1, utmp) > 0) {
		if (user.ut_type != USER_PROCESS)
			continue;
		ucnt++;
	}
	if (kfd == NULL) {
		if ((kfd = fopen(KMEM, "r")) == NULL) {
			perror(KMEM);
			exit(3);
		}
	}
	fseek(kfd, kernel[0].xl_value, 0);
	fread(&vars, sizeof vars, 1, kfd);
	nproc = vars.v_proc;
	fseek(kfd, kernel[2].xl_value, 0);
	fread(&swplo, sizeof swplo, 1, kfd);
	if (mfd == NULL) {
		if ((mfd = fopen(PMEM, "r")) == NULL) {
			perror(PMEM);
			exit(4);
		}
	}
	if (sfd == NULL) {
		if ((sfd = fopen(SMEM, "r")) == NULL) {
			perror(SMEM);
			exit(5);
		}
	}
	printf(" %s  up%s,  %d users\nUser     tty       login@  idle   JCPU   PCPU what\n", vtime(&now), uptime(), ucnt);
	rewind(utmp);
	while (fread(&user, sizeof user, 1, utmp) > 0) {
		if (user.ut_type != USER_PROCESS)
			continue;
		show(&user);
	}
	fclose(utmp);
	exit(0);
}

char *uptime() {
	static char timebuf[128];
	struct proc swapper;
	struct user bootproc;
	long oldpos, now;
	short cnt, ocnt;
	long boottime = 0L;
	
	oldpos = fseek(kfd, 0L, 1);
	fseek(kfd, kernel[3].xl_value, 0);
	fread(&boottime, sizeof boottime, 1, kfd);
	now = boottime / HZ;

	if (now < 0L)
		return " with strange clock time";
	timebuf[0] = '\0';
	ocnt = 0;
	cnt = 0;
	while (now >= MONTH) {
		cnt++;
		now -= MONTH;
	}
	if (cnt > 0) {
		strcat(timebuf, itoa(cnt));
		strcat(timebuf, " mon");
		if (cnt > 1)
			strcat(timebuf, "s");
		if (++ocnt == 2)
			return timebuf;
	}
	cnt = 0;
	while (now >= WEEK) {
		cnt++;
		now -= WEEK;
	}
	if (cnt > 0) {
		strcat(timebuf, itoa(cnt));
		strcat(timebuf, " wk");
		if (cnt > 1)
			strcat(timebuf, "s");
		if (++ocnt == 2)
			return timebuf;
	}
	cnt = 0;
	while (now >= DAY) {
		cnt++;
		now -= DAY;
	}
	if (cnt > 0) {
		strcat(timebuf, itoa(cnt));
		strcat(timebuf, " day");
		if (cnt > 1)
			strcat(timebuf, "s");
		if (++ocnt == 2)
			return timebuf;
	}
	cnt = 0;
	while (now >= HOUR) {
		cnt++;
		now -= HOUR;
	}
	if (cnt > 0) {
		strcat(timebuf, itoa(cnt));
		strcat(timebuf, " hr");
		if (cnt > 1)
			strcat(timebuf, "s");
	}
	return timebuf;
}

char *itoa(n)
int n; {
	static char buf[20];
	
	sprintf(buf, " %d", n);
	return buf;
}

findu (proc, slot, user)
struct	proc	*proc;
int	slot;
struct	user	*user;
{
	struct	proc	*procs = (struct proc *) kernel[1].xl_value;
	long	swapaddr;
	int	i;

	if ((proc->p_flag & (SSWAP|SSPART)) || ! (proc->p_flag & SLOAD)) {
		swapaddr = proc->p_addr[0].te_frameno * NBPC;
		if (fseek (sfd, swapaddr, 0) == -1L)
			fprintf (stderr, "error in lseek\n");
		fread (user, sizeof *user, 1, sfd);
	} else {
		if (fseek (mfd, proc->p_addr[0].te_frameno * NBPC, 0) == -1L)
			fprintf (stderr, "error in lseek\n");
		fread (user, sizeof *user, 1, mfd);
	}
	if (user->u_procp - procs == slot)
		return (1);
	else
		return (0);
}

show(uinfo)
struct utmp *uinfo;
{
	struct stat sbuf;
	struct proc proc;
	struct user prog;
	char ttydev[16];
	long now, cnt, offset, jcpu;
	short mpid, isswap;
	FILE *ufd;
	
	strcpy(ttydev, "/dev/");
	strncpy(&ttydev[5], uinfo->ut_line, 8);
	ttydev[13] = '\0';
	if (stat(ttydev, &sbuf) != 0) {
		perror(ttydev);
		return;
	}
	time(&now);
	now -= sbuf.st_atime;
	printf("%-8.8s %-8.8s %7s ", uinfo->ut_name, uinfo->ut_line, vtime(&uinfo->ut_time));
	if (now > DAY)
		printf("%4dd", now / DAY);
	else if (now > HOUR)
		printf("%2d:%02d", now / HOUR, (now % HOUR) / 60);
	else if (now > MIN)
		printf("%5d", now / MIN);
	else
		printf("     ");
	putchar(' ');
	mpid = -1;
	jcpu = 0;
	for (cnt = 0; cnt < nproc; cnt++) {
		fseek (kfd, kernel[1].xl_value + (cnt * sizeof proc), 0);
		fread(&proc, sizeof proc, 1, kfd);
		if (proc.p_stat == 0 || proc.p_stat == SZOMB || proc.p_stat == SWAIT)
			continue;

		if (findu (&proc, cnt, &prog) == 0)
			continue;

		if (prog.u_ttyp == NULL)
			continue;
		if (sbuf.st_rdev != prog.u_ttyd)
			continue;
		jcpu += prog.u_utime + prog.u_stime +
			prog.u_cutime + prog.u_cstime;
		if (proc.p_pid == mypid)
			continue;
		if (proc.p_pid > mpid)
			mpid = proc.p_pid;
	}
	if (mpid == -1)
		printf("[can't stat]");
	else {
		fseek(kfd, kernel[1].xl_value, 0);
		for (cnt = 0; cnt < nproc; cnt++) {
			fread(&proc, sizeof proc, 1, kfd);
			if (proc.p_pid == mpid)
				break;
		}
		if (proc.p_pid != mpid)
			printf("[interstice]");
		else {
			if (findu (&proc, cnt, &prog) == 0)
				fprintf (stderr, "can't find parent\n");

			printf("%3d:%02d %3d:%02d ",
				(jcpu / HZ) / MIN, (jcpu / HZ) % MIN,
				((prog.u_utime + prog.u_stime) / HZ) / MIN,
				((prog.u_utime + prog.u_stime) / HZ) % MIN);
			prog.u_procp = &proc;
			pcmd(&prog);
		}
	}
	putchar('\n');
}

char *vtime(when)
long *when; {
	struct tm then, now;
	static char buf[20];
	short hour, min, ampm;
	long clock;

	time(&clock);
	now = *localtime(&clock);
	then = *localtime(when);
	if (then.tm_mon != now.tm_mon || then.tm_mday != now.tm_mday) {
		sprintf(buf, "%s %2d", months[then.tm_mon], then.tm_mday);
		return buf;
	}
	min = then.tm_min;
	if (then.tm_hour == 0) {
		ampm = 'a';
		hour = 12;
	}
	else if (then.tm_hour > 0 && then.tm_hour < 12) {
		ampm = 'a';
		hour = then.tm_hour;
	}
	else if (then.tm_hour == 12) {
		ampm = 'p';
		hour = 12;
	}
	else {
		ampm = 'p';
		hour = then.tm_hour - 12;
	}
	sprintf(buf, "%d:%02d%cm", hour, min, ampm);
	return buf;
}

pcmd(uinfo)
struct user *uinfo;
{
	/* someday look up the user's command line */
	printf("%-.14s", uinfo->u_comm);
}
SHAR_EOF
fi
exit 0
#	End of shell archive