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