home *** CD-ROM | disk | FTP | other *** search
- /* 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 );
- }
-
-