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