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