[comp.sources.misc] v02i004: 3B1 status bar redux

scott@zorch.UUCP (Scott Hazen Mueller) (01/15/88)

Comp.Sources.Misc: Volume 2, Issue 4

Submitted-By: Scott Hazen Mueller <scott@zorch.uucp>

Archive-Name: 3b1-status-line


Comp.Sources.Misc: Volume 2, Issue 4
Submitted-By: Scott Hazen Mueller <scott@zorch.uucp>
Archive-Name: 3b1-status-line

This is a considerable enhanced (enhacked?) version of the 3B1 status bar
program that I posted to comp.sources.misc around last October.  This version
is not quite so minimal.  In addition to displaying a status line across the
top of the screen, it uses the Suspend and Resume keys to switch to the next
and previous windows; combined with multiple console gettys it gives behavior
rather like that of 80x86-based Unices with virtual consoles.  Also, it maps
a bunch of 'extended-status' functions to the shifted Function keys on the
console keyboard (and a help function to shift-Print).

#! /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:
#	ReadMe
#	Makefile
#	newmgr.c
#	startmgr.c
#	ndir.c
#	ndir.h
# This archive created: Sat Jan  9 21:15:14 1988
export PATH; PATH=/bin:$PATH
if test -f 'ReadMe'
then
       echo shar: will not over-write existing file "'ReadMe'"
else
cat << \SHAR_EOF > 'ReadMe'

ReadMe for 9 Jan 1988 version of startmgr and newmgr

These two programs implement a replacement for smgr, wmgr and ph.  They don't
do the cron stuff; use cron for that.  They don't do mail, and I don't care,
because I don't use /bin/mail anyway.  Likewise, they know nothing about
calendars or memos.  Additionally, the wmgr function doesn't bring up a menu;
it just switches you between windows.

What startmgr does is start newmgr.  It starts it in a window across the
top of the screen, in the places where the Phone Manager, the Status Manager
and the Window Manager live (I don't use the PM either...).  It then returns,
so all you need to do is startmgr.  It probably can be turned into a fairly
general process starter, but I don't have the time, what with all of the
stuff that I've got piled up to look at.

Newmgr is just a forever loop that displays some status information to
stdout and peeks at the Suspend, Resume Shift-Print and Shift-Function Keys.
The Shift-Function keys display all sorts of additional status information;
since I use UUCP a lot, four of the keys show UUCP-related information (LCK,
C. and D.  files and all of /usr/spool/uucp) and the other four show process
status, utmp information, the mail directory, and root disk stats.  Feel free
to hack it; I just put up what I could think of that's interesting to me.  Load
averages would be nice, but I couldn't find any obvious hooks in /unix for that.

Compiling:  just type make.  Use make install to move the executables to the
default locations.  Modify the Makefile and the #define BARPROG for your site.
I use the ndir stuff (I got it from News, I don't know who did it originally)
to do directory scanning; it should be in the distribution.

How to use:  startmgr goes in /etc/daemons and newmgr goes into your
local executable directory (change a #define in startmgr if you don't
use /usr/local).  You've then either got to change /etc/rc (so it doesn't
start smgr,wmgr and ph) or put a script in /etc/daemons to kill them off.
If you don't know about /etc/rc and /etc/daemons, maybe you shouldn't
be doing this... :-)

Use the Shift-Print key to display the list of key mappings, once the program
is started.

Optionally, you *can* run startmgr by hand; I did for testing.  The only
thing to note here is that your current window winds up being the one
that startmgr creates for the status line; I couldn't find a way to say,
"make the last window the current one."; or at least, one that I could get
to work in 5 minutes...  To get back to your normal window, just use
Suspend/Resume.

Known bugs:  Every once in a while, like if you log out *just* as the display
is updating, it gets confused as to what window you're really in.  It has even
been known to display "w-1" (who me, check for errors on a system call?).
Also, the process status feature probably could find a better way to determine
a process's controlling terminal; it has a tendency to come up with things
like /dev/null.

Potential bugs:  Pretty much all of the extended-status routines are
implemented without any error-checking...

Last note:  this stuff is copyrighted with free redistribution permitted.
I don't really think that this is the sort of thing that needs or warrants
scads of support, so I'm not promising to do so; however, I would like to
see any changes that anyone makes.

Comments welcome, as long as they are other than, "What kind of idiot
runs without smgr, wmgr and ph?"

     \scott
     9 Jan 1988

SHAR_EOF
fi # end of overwriting check
if test -f 'Makefile'
then
       echo shar: will not over-write existing file "'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
LOCALDIR = /usr/local

all: newmgr startmgr

startmgr: startmgr.c
	cc -O -s startmgr.c -o startmgr

newmgr: newmgr.o ndir.o
	cc -O -s newmgr.o ndir.o -o newmgr

install: newmgr startmgr
	mv newmgr LOCALDIR
	mv startmgr /etc/daemons

clean:
	rm -f *.o startmgr newmgr
SHAR_EOF
fi # end of overwriting check
if test -f 'newmgr.c'
then
       echo shar: will not over-write existing file "'newmgr.c'"
else
cat << \SHAR_EOF > 'newmgr.c'
/*	newmgr:  Loop forever, printing a status display and checking the
	Suspend, Resume and shifted Function Keys.  Meant to be used on the
	top line of a Unix-PC (7300/3B1) screen that has had the Phone Manager,
	Status Manager and Window Manager disabled.  What it shows:

	- Phone line states
		o Idle - completely free line
		o Ansr - computer answering line (getty job)
		o Mach - incoming machine
		o User - incoming user
		o Call - any outbound
	- Boot date and time
	- Current run level
	- Current date and time
	- Number of users

	When Suspend is hit, it makes the next window current; Resume
	makes the last window current.

	The following functions are mapped to the function keys:

	Shift-F1:  process display, similar to 'ps -ef'
	Shift-F2:  utmp display, like 'who'
	Shift-F3:  lists UUCP LCK.. files, if any
	Shift-F4:  directory of /usr/mail
	Shift-F5:  Root filesystem freespace and free inodes
	Shift-F6:  lists UUCP C. files, if any
	Shift-F7:  lists UUCP X. files, if any
	Shift-F8:  directory of /usr/spool/uucp

	This is started by startmgr (which should be included in any
	distribution) so that the fork/exec/open code doesn't have to be
	carried around with this program.

	The process table stuff is heavily inspired by the 'fuser.c' program
	posted to the Usenet by Michael 'Ford' Ditto.

	This software is Copyright (c) 1987 by Scott Hazen Mueller.
 
	Permission is hereby granted to copy, reproduce, redistribute or
	otherwise use this software as long as: there is no monetary
	profit gained specifically from the use or reproduction or this
	software, it is not sold, rented, traded or otherwise marketed, and
	this copyright notice is included prominently in any copy
	made.

	The author make no claims as to the fitness or correctness of
	this software for any use whatsoever, and it is provided as is. 
	Any use of this software is at the user's own risk.

	(Copyright notice courtesy of News 2.11 :-)

	Additionally:  you break it, you bought it.  I've listed the problems
	that I know of in comments in the code; if you come up with fixes, I'd
	like to see them, but I'm not planning on supporting anything.  It's
	"good enough"; that's all that I'm looking for.	*/

#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/phone.h>
#include	<sys/window.h>
#include	<sys/signal.h>
#include	<sys/filsys.h>
#include	<sys/proc.h>
#include	<sys/user.h>
#include	<sys/syslocal.h>
#include	<sys/tune.h>
#include	<sys/stat.h>
#include	<fcntl.h>
#include	<time.h>
#include	<utmp.h>
#include	<nlist.h>
#include	<pwd.h>
#include	"ndir.h"

#define		PHONE1		"ph0"
#define		PHONE2		"ph1"
#define		IDLE		0
#define		ANSR		1
#define		MACH		2
#define		USER		3
#define		CALL		4
#define		BARFORM		"%s1 %s2 | Up Since %s | %s | %s | %d Users | w%d \r"
#define		MACHLEN		15
#define		MAXPATHLEN	255
#define		LOCKDIR		"/usr/spool/uucp/"
#define		MAILDIR		"/usr/mail/"
#define		DEVDIR		"/dev/"
#define		LOCKPREFIX	"LCK.."
#define		COMPREFIX	"C."
#define		UUXPREFIX	"X."
#define		TRUE		(1)
#define		FALSE		(0)
#define		TIMEFORM	"%.2d/%.2d %.2d:%.2d"
#define		TICK		15
#define		WHOSLEEP	15
#define		NOWHERE		0
#define		FORWARD		1
#define		BACKWARD	2
#define		HERE		3
#define		MINWIN		1
#define		MAXWIN		12

char	*phstat[5] = { "Idle", "Ansr", "Mach", "User", "Call" };

main()
{
int		nusers;
char	curtime[26], boottime[26], rl[12], line[80], *fmttime();
struct	utmp *ut, *getutent();
long	temp;

/*	Ignore keyboard interrupt signals.	*/

signal( SIGINT, SIG_IGN );
signal( SIGQUIT, SIG_IGN );

/*	Open up the utmp file and find the boot time; save for display.	*/

while ( ( ut = getutent() ) != NULL )
	if ( (int) ut->ut_type == BOOT_TIME ) {
		strcpy( boottime, fmttime( localtime( &( ut->ut_time ) ) ) );
		break;
	}
endutent();

for (;;) {

/*	Scan the utmp file, noting the run level and totting up users.	*/

	nusers = 0;
	while ( ( ut = getutent() ) != NULL ) {
		switch ( (int) ut->ut_type ) {
			case USER_PROCESS : nusers++; break;
			case RUN_LVL : strcpy( rl, ut->ut_line ); break;
			default : break;
			}
		}
	endutent();

/*	Figure out the current time.  Temp is needed 'cause localtime wants
	an address.	*/

	temp = time( (long *) 0 );
	strcpy( curtime, fmttime( localtime( &temp ) ) );

/*	Format and print.  Flush stdout to make it really get printed.	*/

	sprintf( line, BARFORM, phstat[ phone_stat( PHONE1 ) ],
		phstat[ phone_stat( PHONE2 ) ], boottime, rl, curtime, nusers,
		curwinno() );
	printf( "%s", line );
	fflush( stdout );

/*  Take care of the window management stuff; also do some sleeping down
	there.	*/

	wcheckz();
	}
}

char *fmttime( tm )

/*	Format a time structure into a string the way *I* want it.	*/

struct tm *tm;
{
char  abuf[26];

sprintf( abuf, TIMEFORM, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min );
return( abuf );
}

phone_stat( phone )
char	*phone;

/*	Figure out the phone line status.  There are five possible states:
		Idle - completely free line
		Ansr - getty
		Mach - incoming machine
		User - incoming user
		Call - outbound

	These five states are defined roughly as follows:

		Ansr - utmp -> LOGIN_PROCESS.
		Mach - utmp -> USER_PROCESS; E LCK.
		User - utmp -> USER_PROCESS; ~E LCK.
		Call - utmp -> nothing; E LCK.
		Idle - utmp -> nothing; ~E LCK.

	Basically, we decide what we have by a process of elimination.

	Known bugs:  This may have problems with HDB UUCP, since I've got no
	way to test against it.  One thing that I am sure of is that since I
	believe that uugetty creates a LCK..device file, this routine will
	consistently mistake users logged in through uugetty for machines
	logged into uucico.	*/

{
struct utmp	ut_try, *ut, *getutline();
short	utmptype;
char	machine[MACHLEN];

/*	Look for the phone line in the utmp file.  getutline() should return
	only LOGIN_PROCESS, USER_PROCESS, or NULL (for failure).  We need to
	save some values before the endutent() call because it clears out the
	static structure pointed to by the return value of getutline().  We
	save the ut_user field because uucico creates a machine LCK file using
	the first six characters of this name.	*/

strcpy( ut_try.ut_line, phone );
ut = getutline( &ut_try );
utmptype = ut->ut_type;
strcpy( machine, ut->ut_user );

/*	AT&T UUCP specifies a maximum length of six characters.  Remove this
	line for less brain-damaged implementations.	*/

machine[6] = '\0';
endutent();

/*	First condition:  was there a utmp entry?	*/

if ( ut != NULL )

/*	There was.  If the type field is LOGIN_PROCESS, we have a getty and can
	return the appropriate value and quit.	*/

	if ( utmptype == LOGIN_PROCESS )
		return( ANSR );
	else

/*	A USER_PROCESS is incoming and can be either a user or a uucico.  An
	incoming uucico has a LCK..machine file, so we check for that.	*/

		return( locked( machine ) ? MACH : USER );
else

/*	There is no utmp entry.  There are only two possibilities here; if there
	is an outgoing call of some sort, there will be a LCK..device file; if
	not, the line is really idle.	*/

		return( locked( phone ) ? CALL : IDLE );

/*	Shouldn't reach here.	*/

}

locked( device )
char	*device;

/*	See if a lock file exists.	*/

{
char	temp[MAXPATHLEN];
int		fd;

/*	Make up the full path name.	*/

strcpy( temp, LOCKDIR );
strcat( temp, LOCKPREFIX );
strcat( temp, device );

/*	Attempt to open the lock file.  Assume that if the open fails the lock
	file does not exist.	*/

fd = open( temp, O_RDONLY );
if ( fd == -1 )
	return( FALSE );
close( fd );
return( TRUE );
}

curwinno()

/*	Figure out the current window; we need a starting point, after all.
	Known bug:  this sometimes returns the wrong value if it gets into a
	race condition with a terminating/restarting (eg, getty) job.  It also
	doesn't check to see if the ioctl fails.	*/

{
int		foowin, dummy, temp;

foowin = open( "/dev/window", O_RDONLY );
temp = ioctl( foowin, WIOCGCURR, dummy );
close( foowin );
return( temp );
}

wcheckz()

/*	This is where the code to check for change-window keys lives.  Also,
	sleep for the main program here.  It should return about every TICK
	seconds.	*/

{
int		tock, newwin, dir;

tock = 0;

do {
/*	Check for a command key.	*/

	if ( ( dir = checkey() ) != NOWHERE ) {

/*	Determine which window is next and select it.	*/

		if ( dir != HERE ) {
			newwin = nexwin( curwinno(), dir );
			ioctl( newwin, WIOCSELECT );
			close( newwin );
			}
		tock = TICK;		/*	Kludge to force return and redisplay.	*/
		}
	}

/*	Return after TICK tocks.	*/

while ( tock++ < TICK );
}

justgoon()

/*	Stub routine to "handle" the alarm signal.  Doesn't do a lot.	*/

{
return;
}

checkey()

/*	Check and see if one of our keys has been poked.  The only keys this window
	is supposed to respond to all generate three character escape sequences.
	Set and catch an alarm signal to keep from getting hung in the fread();
	this probably could be done more neatly some other way, but...	*/

{
int		direction, i;
FILE	*fw;
char	key[3];

/*	Set the alarm signal.	*/

signal( SIGALRM, justgoon );
alarm( 1 );
if ( fread( key, 1, 3, stdin ) == 3 ) {

/*	Reset the alarm signal to be ignored.	*/

	signal( SIGALRM, SIG_IGN );
	alarm( 0 );

/*	Pick a function.	*/

	switch ( key[2] ) {
		case 'C' : showprocs(); direction=HERE; break;
		case 'D' : showutmp(); direction=HERE; break;
		case 'E' : showdir( LOCKDIR, LOCKPREFIX ); direction=HERE; break;
		case 'F' : showdir( MAILDIR, "" ); direction=HERE; break;
		case 'G' : showfsys(); direction=HERE; break;
		case 'H' : showdir( LOCKDIR, COMPREFIX ); direction=HERE; break;
		case 'I' : showdir( LOCKDIR, UUXPREFIX ); direction=HERE; break;
		case 'J' : showdir( LOCKDIR, "" ); direction=HERE; break;
		case 'p' : 
		case 'P' : direction = FORWARD; break;
		case 'q' :
		case 'Q' : direction = BACKWARD; break;
		case 'Z' : help(); direction = HERE; break;
		default : direction = NOWHERE; break;
		}
	}
	else
		direction = NOWHERE;

/*	Reset the alarm signal to be ignored.	*/

signal( SIGALRM, SIG_IGN );
alarm( 0 );
return( direction );
}

nexwin( winno, dir )
int winno, dir;

/*	Decide what should be the next window.  This relies on the fact that
	when you open a window device directly the open call will fail if it has
	not already been opened by someone else; this is how we find the open
	windows.  Invisible windows should have their user-text set to "Invisible"
	if they wish to be ignored.	*/

{
int		wd;
char	windex[12];
struct	utdata	wintext;

/*	Trivial loop; at worst, we'll wind up back where we started.  I suppose
	it's possible to have a system with no windows except those marked as
	"Invisible"; it doesn't seem too useful, though.	*/

while ( 1 ) {

/*	Forward/backward sort-of modulo arithmetic.  Real modulo arithmetic
	starts at zero.	*/

	winno += ( dir == FORWARD ? 1 : -1 );
	if ( winno > MAXWIN )
		winno = MINWIN;
	else if ( winno < MINWIN )
		winno = MAXWIN;

/*	Generate a window name and test for existence.	*/
	sprintf( windex, "/dev/w%d", winno );
	if ( ( wd = open( windex, O_RDONLY ) ) != -1 ) {

/*	It exists, now look at its user text info.  This is where "Invisible"
	gets skipped.	*/

		wintext.ut_num = WTXTUSER;
		ioctl( wd, WIOCGETTEXT, &wintext );
		if ( strcmp( wintext.ut_text, "Invisible" ) )
			return( wd );
		else
			close( wd );
		}
	}
}

showprocs()

/*	Find the process table; run through it, looking up the user areas and
	printing out some data on each one on the fly.  Works like 'ps -ef'
	command.	*/

{
struct proc	*ppt;
static struct nlist	sym[3] = { { "tuhi", }, { "proc", }, { (char *)0, }, };
struct tunable	tune;
int	mem, kmem;
FILE	*wp;

signal( SIGALRM, justgoon );

/*	Open up a window; also open the memory devices for reading.	*/

wp = fopen( "/dev/window", "w" );
mem = open( "/dev/mem", O_RDONLY );
kmem = open( "/dev/kmem", O_RDONLY );

/*	Find the tunable parameters, to get the maximum number of processes; and
	the process table.	*/

nlist( "/unix", sym );
dmemcopy( kmem, (char *)&tune, (long)(sym[0].n_value), (long)sizeof( tune ) );
dmemcopy( kmem, (char *)&ppt, (long)(sym[1].n_value), (long)sizeof( ppt ) );

/*	Run through the process table.	*/

fprintf( wp, "REAL UID EFECT ID  PID   PPID START TIME    TTY  COMMAND\n" );
while ( tune.nproc-- )
	printproc( wp, kmem, mem, ppt++ );

/*	Empty the output buffer, sleep to allow it to be read, and close up the
	extra window and return.	*/

fflush( wp );
sleep( WHOSLEEP );
fclose( wp );
signal( SIGALRM, SIG_IGN );
}

showutmp()

/*	Run through the utmp file.  Used to give an idea of system activity.	*/

{
struct	utmp *ut, *getutent();
FILE	*wp;

signal( SIGALRM, justgoon );
wp = fopen( "/dev/window", "w" );

/*	Go through utmp.  Show login and user processes.	*/

while ( ( ut = getutent() ) != NULL ) {
	switch ( (int) ut->ut_type ) {
		case USER_PROCESS : 
		case LOGIN_PROCESS : showutent( wp, ut );
		default : break;
		}
	}
endutent();
fprintf( wp, "\n" );
fflush( wp );
sleep( WHOSLEEP );
fclose( wp );
signal( SIGALRM, SIG_IGN );
}

showdir( dirname, mask )
char	*dirname, *mask;

/*	Display the contents of a directory.  Use 'mask' to specify a file prefix
	string to look for; useful for finding 'LCK..' files.	*/

{
DIR	*dirp;
struct	directy *dir;
FILE	*wp;
int	mlen;

signal( SIGALRM, justgoon );
wp = fopen( "/dev/window", "w" );
dirp = opendir( dirname );
mlen = strlen( mask );
while ( ( dir = readdir( dirp ) ) != NULL ) {
	if ( ( dir->d_name[0] != '.' ) && ( !strncmp( dir->d_name, mask, mlen ) ) ){
		fprintf( wp, "\n%s%s", dirname, dir->d_name );
		}
	}
closedir( dirp );
fprintf( wp, "\n" );
fflush( wp );
sleep( WHOSLEEP );
fclose( wp );
signal( SIGALRM, SIG_IGN );
}

showfsys()

/*	Show the root file system statistics.  Read the superblock in and get
	free blocks, total blocks, and free inodes from it.  Also display the
	freespace as a percentage.	*/

{
int rp;
struct filsys fs;

signal( SIGALRM, justgoon );
rp = open( "/dev/rfp002", O_RDONLY );
lseek( rp, 512, 0 );
read( rp, &fs, sizeof( fs ) );
close( rp );
printf( "\nRoot: %ldK free %ldK total %d%% free %d inodes", fs.s_tfree,
	fs.s_fsize, (int)(100.0*(float)(fs.s_tfree)/(float)(fs.s_fsize)),
	fs.s_tinode );
fflush( stdout );
sleep( 5 );
printf( "\n" );
signal( SIGALRM, SIG_IGN );
}

help()

/*	Print a help message on key usage.	*/

{
FILE	*wp;

signal( SIGALRM, justgoon );
wp = fopen( "/dev/window", "w" );
fprintf( wp, "\t- Phone line states\n\t\to Idle - completely free line\n" );
fprintf( wp, "\t\to Ansr - computer answering line (getty job)\n" );
fprintf( wp, "\t\to Mach - incoming machine\n\t\to User - incoming user\n" );
fprintf( wp, "\t\to Call - any outbound\n\t- Boot date and time\n" );
fprintf( wp, "\t- Current run level\n\t- Current date and time\n\t- Number of users\n" );
fprintf( wp, "\tWhen Suspend is hit, it makes the next window current; Resume\n" );
fprintf( wp, "\tmakes the last window current.\n\n" );
fprintf( wp, "\tThe following functions are mapped to the function keys:\n" );
fprintf( wp, "\tShift-F1:  process display, similar to 'ps -ef'\n" );
fprintf( wp, "\tShift-F2:  utmp display, like 'who'\n" );
fprintf( wp, "\tShift-F3:  lists UUCP LCK.. files, if any\n" );
fprintf( wp, "\tShift-F4:  directory of /usr/mail\n" );
fprintf( wp, "\tShift-F5:  Root filesystem freespace and free inodes\n" );
fprintf( wp, "\tShift-F6:  lists UUCP C. files, if any\n" );
fprintf( wp, "\tShift-F7:  lists UUCP X. files, if any\n" );
fprintf( wp, "\tShift-F8:  directory of /usr/spool/uucp\n" );
fprintf( wp, "\tShift-Print:  display this list" );
fflush( wp );
sleep( WHOSLEEP );
fclose( wp );
signal( SIGALRM, SIG_IGN );
}

dmemcopy( devmem, buf, memloc, nbytes )
int	devmem;
char	*buf;
long	memloc, nbytes;

/*	Read from a memory device into a local buffer.	*/

{
lseek( devmem, memloc, 0 );
read( devmem, buf, (unsigned)nbytes ); 
}

printproc( wp, kmem, mem, ppt )
FILE	*wp;
int	kmem, mem;
struct proc	*ppt;

/*	Get a process table entry and print some of the information from it.	*/

{
struct proc p;
struct user	u;
struct passwd	*pwd, *getpwuid();
char	curtime[26], ptty[7], *getptty();

dmemcopy( kmem, (char *)&p, (long)ppt, (long)sizeof( struct proc ) );
if ( p.p_flag & SLOAD ) {
	dmemcopy( mem, (char *)&u, (long)( ctob( p.p_addr[0] )+U_OFFSET ),
		(long)sizeof( struct user ) );
	pwd = getpwuid( p.p_uid );
	fprintf( wp, "%8s ", pwd->pw_name );
	pwd = getpwuid( p.p_suid );
	fprintf( wp, "%8s %5d %5d ", pwd->pw_name, p.p_pid, p.p_ppid );
	strcpy( curtime, fmttime( localtime( &u.u_start ) ) );
	strcpy( ptty, getptty( u.u_ttyd ) );
	fprintf( wp, "%11s %6s %s\n", curtime, ptty, u.u_comm );
	fflush( wp );
	}
}

char	*getptty( devno )
dev_t	devno;

/*	Go from a device number to a device name.	*/

{
DIR	*dirp;
struct directy	*dir;
struct stat	sbuf;
char	name[14];
static char	tname[7];

strcpy( tname, "none" );
if ( !devno )
	return( tname );
dirp = opendir( DEVDIR );
while ( ( dir = readdir( dirp ) ) != NULL ) {
	if ( dir->d_name[0] != '.' ) {
		strcpy( name, DEVDIR );
		strcat( name, dir->d_name );
		stat( name, &sbuf );
		if ( devno == sbuf.st_rdev ) {
			closedir( dirp );
			strcpy( tname, dir->d_name );
			return( tname );
			}
		}
	}
closedir( dirp );
return( tname );
}

showutent( fp, u )
FILE	*fp;
struct	utmp *u;

/*	Print a formatted utmp entry.	*/

{
char	curtime[26], *fmttime();

strcpy( curtime, fmttime( localtime( &( u->ut_time ) ) ) );
fprintf( fp, "%8s %14s %11s %5d\n", u->ut_user, u->ut_line, curtime, u->ut_pid );
}

SHAR_EOF
fi # end of overwriting check
if test -f 'startmgr.c'
then
       echo shar: will not over-write existing file "'startmgr.c'"
else
cat << \SHAR_EOF > 'startmgr.c'
/*	startmgr:  Create a one-line window on the top line of the Unix-PC
	(7300/3B1) screen and start a program running in that window.
	Declare this window to be the Window Manager, which gives it control
	over the Suspend, Resume and Print keys.  This is meant to be used to
	start newmgr, but can probably be adapted to be a fairly generic program
	starter.  It was designed to mimic the observed operation of things like
	cron, smgr, etc.

	This software is Copyright (c) 1987 by Scott Hazen Mueller.
 
	Permission is hereby granted to copy, reproduce, redistribute or
	otherwise use this software as long as: there is no monetary
	profit gained specifically from the use or reproduction or this
	software, it is not sold, rented, traded or otherwise marketed, and
	this copyright notice is included prominently in any copy
	made.

	The author make no claims as to the fitness or correctness of
	this software for any use whatsoever, and it is provided as is. 
	Any use of this software is at the user's own risk.

	(Copyright notice courtesy of News 2.11 :-)

	Additionally:  you break it, you bought it.  I've listed the problems
	that I know of in comments in the code; if you come up with fixes, I'd
	like to see them, but I'm not planning on supporting anything.  It's
	"good enough"; that's all that I'm looking for.	*/

#include	<stdio.h>
#include	<sys/types.h>
#include	<wind.h>
#include	<sys/window.h>
#include	<fcntl.h>
#include	<termio.h>

/*	Parameters defining the window and the program for that window.  You'll
	almost certainly have to change barprog to correspond with whatever
	directory you use for your local stuff.

	Note:  WFLAGS was determined empirically, by looking at the flags for
	the AT&T-supplied smgr window.  No guarantees here.	*/

#define		BARPROG		"/usr/local/newmgr"
#define		WXSTART		0
#define		WYSTART		0
#define		WWIDTH		720
#define		WHEIGHT		12
#define		WFLAGS		257

main()
{
int	wd, dummy;
struct uwdata winbar;
struct utdata winname;
struct termio bartty;

winbar.uw_x = WXSTART;
winbar.uw_y = WYSTART;
winbar.uw_width = WWIDTH;
winbar.uw_height = WHEIGHT;
winbar.uw_uflags = WFLAGS;
winname.ut_num = WTXTUSER;

strcpy( winname.ut_text, "Invisible" );
if ( fork() )
	exit( 0 );
else {

/*	Setpgrp() cannot be called from processes associated with windows.
	From the manual.  Since it's a cleaner model for the parent to redirect
	the child's stdin, stdout and stderr, we do that.  We have to setpgrp(),
	or else the child would be killed when the process group leader exited.	*/

	fclose( stdin );
	fclose( stdout );
	fclose( stderr );
	setpgrp();

/*	Get a window, and set it up according to our parameters.  Since stdin
	was closed above, wd gets to be stdin (roughly).	*/

	wd = open( "/dev/window", O_RDWR|O_EXCL, 0 );
	ioctl( wd, WIOCSETD, &winbar );
	ioctl( wd, WIOCSETTEXT, &winname );
	ioctl( wd, WIOCSYS, SYSWMGR );
	ioctl( wd, WIOCSYS, SYSPMGR );

/*	Set up the child's stdout and stderr to point at the window.	*/

	dup( wd );
	dup( wd );

/*	Set terminal parameters; after all, we'll want to read escape codes and
	other neat stuff.	*/

	ioctl( wd, TCGETA, &bartty );
	bartty.c_iflag &= ~IGNBRK;
	bartty.c_lflag &= ~( ICANON | ECHO );

/*	Read three characters at a time; 1 second timeout interval.  Whether
	this really does anything, I don't know.	*/

	bartty.c_cc[4] = 3;
	bartty.c_cc[5] = 10;
	ioctl( wd, TCSETA, &bartty );
	
/*	Execute with no command line arguments.	*/

	execl( BARPROG, BARPROG, 0 );
	}
}

SHAR_EOF
fi # end of overwriting check
if test -f 'ndir.c'
then
       echo shar: will not over-write existing file "'ndir.c'"
else
cat << \SHAR_EOF > 'ndir.c'
#if !defined(BSD4_2) && !defined(BSD4_1C) && !defined(HP9K5)
#ifdef M_XENIX
#include <sys/types.h>
#endif /* M_XENIX */
#include <sys/param.h>
#include "ndir.h"

#ifdef SCCSID
static char	*SccsId = "@(#)ndir.c	1.12	10/15/87";
#endif /* SCCSID */

/*
 * support for Berkeley directory reading routine on a V7 file system
 */

extern char *malloc();

/*
 * open a directory.
 */
DIR *
opendir(name)
char *name;
{
	register DIR *dirp;
	register int fd;

	if ((fd = open(name, 0)) == -1)
		return NULL;
	if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
		close (fd);
		return NULL;
	}
	dirp->dd_fd = fd;
	dirp->dd_loc = 0;
	return dirp;
}

/*
 * read an old style directory entry and present it as a new one
 */
#ifdef pyr
/* Pyramid in the AT&T universe */
#define ODIRSIZ 248
struct olddirect {
	long	od_ino;
	short	od_fill1, od_fill2;
	char od_name[ODIRSIZ];
};
#else /* V7 file system */
#define	ODIRSIZ	14

struct	olddirect {
	short	od_ino;
	char	od_name[ODIRSIZ];
};
#endif /* !pyr */

/*
 * get next entry in a directory.
 */
struct directy *
readdir(dirp)
register DIR *dirp;
{
	register struct olddirect *dp;
	static struct directy dir;

	for (;;) {
		if (dirp->dd_loc == 0) {
			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf, 
			    DIRBLKSIZ);
			if (dirp->dd_size <= 0)
				return NULL;
		}
		if (dirp->dd_loc >= dirp->dd_size) {
			dirp->dd_loc = 0;
			continue;
		}
		dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
		dirp->dd_loc += sizeof(struct olddirect);
		if (dp->od_ino == 0)
			continue;
		dir.d_ino = dp->od_ino;
		strncpy(dir.d_name, dp->od_name, ODIRSIZ);
		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
		dir.d_namlen = strlen(dir.d_name);
		dir.d_reclen = DIRSIZ(&dir);
		return (&dir);
	}
}

/*
 * close a directory.
 */
void
closedir(dirp)
register DIR *dirp;
{
	close(dirp->dd_fd);
	dirp->dd_fd = -1;
	dirp->dd_loc = 0;
	free((char *)dirp);
}

/*
 * seek to an entry in a directory.
 * Only values returned by "telldir" should be passed to seekdir.
 */
void
seekdir(dirp, loc)
register DIR *dirp;
long loc;
{
	long curloc, base, offset;
	struct directy *dp;
	long lseek(), telldir();

	curloc = telldir(dirp);
	if (loc == curloc)
		return;
	base = loc & ~(DIRBLKSIZ - 1);
	offset = loc & (DIRBLKSIZ - 1);
	(void) lseek(dirp->dd_fd, base, 0);
	dirp->dd_loc = 0;
	while (dirp->dd_loc < offset) {
		dp = readdir(dirp);
		if (dp == NULL)
			return;
	}
}

/*
 * return a pointer into a directory
 */
long
telldir(dirp)
DIR *dirp;
{
	return lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc;
}
#endif /* !BSD4_2 && !BSD4_1C && !HP9K5 */
SHAR_EOF
fi # end of overwriting check
if test -f 'ndir.h'
then
       echo shar: will not over-write existing file "'ndir.h'"
else
cat << \SHAR_EOF > 'ndir.h'
/*	@(#)ndir.h	1.7	10/7/87	*/
#if defined(HP9K5)
/* He should have included it instead of this, but prevent confusion */
#include <ndir.h>
#else /* other */
#ifndef DEV_BSIZE
#define	DEV_BSIZE	512
#endif
#define DIRBLKSIZ	DEV_BSIZE
#define	MAXNAMLEN	255

struct	directy {
	long	d_ino;			/* inode number of entry */
	short	d_reclen;		/* length of this record */
	short	d_namlen;		/* length of string in d_name */
	char	d_name[MAXNAMLEN + 1];	/* name must be no longer than this */
};

/*
 * The DIRSIZ macro gives the minimum record length which will hold
 * the directory entry.  This requires the amount of space in struct directy
 * without the d_name field, plus enough space for the name with a terminating
 * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
 */

#ifdef DIRSIZ
#undef DIRSIZ
#endif /* DIRSIZ */
#define DIRSIZ(dp) \
    ((sizeof (struct directy) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))

/*
 * Definitions for library routines operating on directories.
 */
typedef struct _dirdesc {
	int	dd_fd;
	long	dd_loc;
	long	dd_size;
	char	dd_buf[DIRBLKSIZ];
} DIR;
#ifndef NULL
#define NULL 0
#endif
extern	DIR *opendir();
extern	struct directy *readdir();
extern	void closedir();

#define rewinddir(dirp)	seekdir((dirp), (long)0)
#endif /* other */
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Scott Hazen Mueller   uunet!zorch!scott   scott@zorch.UU.NET
(408) 245-9461