[comp.sources.misc] Utility for AT&T Unix-PC

scott@zorch.UU.NET (Scott Hazen Mueller) (11/09/87)

This is a little toy status-bar utility for the AT&T Unix-PC.  I use it, but
then I wrote it; it's probably more useful as an example program.  It subsumes
some of the functionality of ph, smgr and wmgr, and assumes that none of them
are being used.

#! /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
#	newmgr.c
#	startmgr.c
# This archive created: Mon Nov  2 21:38:48 1987
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 startmgr and newmgr

These two programs implement a very minimal replacement for smgr and wmgr.
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 and Resume keys.  You could run it off of the
command line, but it's not very exciting and there's no way to win.  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:  all I do is type "make startmgr" and "make newmgr".  Make
has default rules for *.c files, after all.  Stripping wouldn't hurt.

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... :-)

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:  Yes, one.  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?).  Other than that, I don't know of any, but I haven't pushed it too
hard.

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
SHAR_EOF
chmod +x 'ReadMe'
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 and Resume 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 (Idle, Busy)
	- Boot date and time
	- Current run level
	- Current date and time
	- Number of users

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

	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.

	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	<fcntl.h>
#include	<time.h>
#include	<utmp.h>

#define		PHONE1		"ph0"
#define		PHONE2		"ph1"
#define		IDLE		0
#define		BARFORM		"%s1 %s2 | Up Since %s | %s | %s | %d Users | w%d \r"
#define		phstat( st ) 	( st ? "Busy" : "Idle" )
#define		MAXPATHLEN	255
#define		LOCKDIR		"/usr/spool/uucp/"
#define		LOCKPREFIX	"LCK.."
#define		TRUE		(1)
#define		FALSE		(0)
#define		TIMEFORM	"%.2d/%.2d %.2d:%.2d"
#define		TICK		15
#define		NOWHERE		0
#define		FORWARD		1
#define		BACKWARD	2
#define		MINWIN		1
#define		MAXWIN		12

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

/* 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.
	Skip the loop for dead processes - we don't want to confuse the
	phone-check code...	*/

	nusers = 0;
	ph1st = IDLE;
	ph2st = IDLE;
	while ( ( ut = getutent() ) != NULL ) {
		switch ( (int) ut->ut_type ) {
			case USER_PROCESS : nusers++; break;
			case RUN_LVL : strcpy( rl, ut->ut_line ); break;
			case DEAD_PROCESS : continue;
			default : break;
			}
/*	Peek at the line field and see if a phone line is mentioned.  The answer
	is either yes (1 or many, don't care exactly) or no, so I can get away
	with the 'or'.	*/

		ph1st = ph1st | !strcmp( PHONE1, ut->ut_line );
		ph2st = ph2st | !strcmp( PHONE2, ut->ut_line );
		}
	endutent();

/*	The phone check stuff *still* hasn't caught everything, e.g., an outgoing
	uucico started by cron, so we'll now peek and see if we can find a LCK
	file.  It's gross and it's nasty, and if anyone can do me better without
	hanging up my voice calls, I'd be happy to replace this stuff...	*/

	ph1st = ph1st | locked( PHONE1 );
	ph2st = ph2st | locked( PHONE2 );

/*	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( ph1st ), phstat( ph2st ),
		boottime, rl, curtime, nusers, curwinno() );
	printf( "%s", line );
	fflush( stdout );

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

	wcheckz();
	}
}

/*	See if a lock file exists.	*/

locked( device )
char	*device;
{
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 );
}

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

char *fmttime( tm )
struct tm *tm;
{
char  abuf[26];

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

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.	*/

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

/*	Return after TICK tocks.	*/

while ( tock++ < TICK );
}

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

justgoon()
{
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.
	The print key gets ignored; I suppose it could be aliased to something
	useful, like system reboot...  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 );
	switch ( key[2] ) {
		case 'p' : 
		case 'P' : direction = FORWARD; break;
		case 'q' :
		case 'Q' : direction = BACKWARD; break;
		default : direction = NOWHERE; break;
		}
	}
	else
		direction = NOWHERE;

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

signal( SIGALRM, SIG_IGN );
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 to 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 );
		}
	}
}

SHAR_EOF
chmod +x 'newmgr.c'
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 );

/*	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
chmod +x 'startmgr.c'
fi # end of overwriting check
#	End of shell archive
exit 0
-- 
Scott Hazen Mueller   ( near_me ? lll-crg!csustan!helium : uunet )!zorch!scott
(209) 527-1203                       scott@zorch.UU.NET