robert@hslrswi.UUCP (Robert Ward) (06/29/85)
Here is the version of SPS for Bell Labs V7 (PDP-11) Unix and Unisoft 1.3 MC68000 Unix for those who have expressed interest in it. It is almost functionally equivalent of the 4.xbsd version that was posted a few days ago to net.sources. This is a somewhat older version than the 4.[12]bsd version of SPS and hence lacks a few of the fancier features such as hashing of user-ids as well as some miscellaneous options. However, it shouldn't be too difficult to update the code to incorporate these additions. The following notes apply to anyone who is interested in trying to port SPS to a new machine or system. The majority of the code of SPS is suprisingly portable to most systems (at least it has been so far). The difficulty invariably lies in the part of the code that fetches a process' upage (getcmd11.c) and the code which attempts to locate the base of the command argument stack in the user address space. If you have the code for ps(1), you can always rip the relevant sections out of there. Otherwise it means having to use guesswork and adb(1) and a lot of time. The code enclosed in "#ifdef VMUNIX" lines below refers to an older version of SPS for a 4.1bsd Vax only. It is largely obselete and should be ignored. The code for this and the 4.xbsd versions should, of course, really be combined together but then the ifdefing becomes a severe headache and makes the code even more unreadable. Please send any comments, suggestions, bug fixes and reports to me. Robert Ward. Hasler AG (Abt. 34) Belpstrasse 23 CH-3000 Bern 14 Switzerland. ******************************************************************************* This is a sh(1) archive. To extract, cut after the following dotted line and run sh on that file. --------------------------------- C U T --- H E R E --------------------------- #! /bin/sh echo Extracting Makefile.11 cat > Makefile.11 << '---END-OF-Makefile.11---' # Makefile for SPS (IIASA PDP-11/70 Version) PROG = sps OBJS = findtty.o flagdecode.o flagsetup.o \ getcmd11.o globals1.o globals2.o \ initialise.o main.o mktree.o needed.o openfiles.o \ prcmd.o prcpu.o prheader.o printall.o \ printproc.o prsummary.o readstatus.o readsymbols.o \ select.o select_tty.o ttyinit.o ttystatus.o waitingfor.o SRCS = findtty.c flagdecode.c flagsetup.c \ getcmd11.c globals1.c globals2.c \ initialise.c main.c mktree.c needed.c openfiles.c \ prcmd.c prcpu.c prheader.c printall.c \ printproc.c prsummary.c readstatus.c readsymbols.c \ select.c select_tty.c ttyinit.c ttystatus.c waitingfor.c INCS = sps.h CC = cc11 CFLAGS = -I/usr/include/local/pdp11/sys all: $(PROG) .c.o: $(CC) $(CFLAGS) -c -O $< $(OBJS): $(INCS) $(PROG): $(OBJS) $(CC) -o $@ $(OBJS) install: $(PROG) strip $(PROG) mv $(PROG) /bin/$(PROG) /etc/chown root /bin/$(PROG) chmod 4711 /bin/$(PROG) lint: lint -x -b $(CFLAGS) $(SRCS) clean: rm -f $(OBJS) $(PROG) ---END-OF-Makefile.11--- echo Extracting Makefile.4.1 cat > Makefile.4.1 << '---END-OF-Makefile.4.1---' # Makefile for SPS (4.1BSD UNIX Version, with or without Chaos network) PROG = sps OBJS = filecount.o findtty.o flagdecode.o flagsetup.o \ getcmd.o getupage.o globals1.o globals2.o \ initialise.o main.o mktree.o needed.o openfiles.o \ percentmem.o prcmd.o prcpu.o prheader.o printall.o \ printproc.o prsummary.o readstatus.o readsymbols.o \ select.o select_tty.o ttyinit.o ttystatus.o waitingfor.o SRCS = filecount.c findtty.c flagdecode.c flagsetup.c \ getcmd.c getupage.c globals1.c globals2.c \ initialise.c main.c mktree.c needed.c openfiles.c \ percentmem.c prcmd.c prcpu.c prheader.c printall.c \ printproc.c prsummary.c readstatus.c readsymbols.c \ select.c select_tty.c ttyinit.c ttystatus.c waitingfor.c INCS = sps.h CFLAGS = -I/usr/src/sys -DVMUNIX -DCHAOS all: $(PROG) .c.o: cc $(CFLAGS) -c -O -R $< getcmd.o globals1.o waitingfor.o: cc $(CFLAGS) -c -O $< $(OBJS): $(INCS) $(PROG): $(OBJS) cc -o $@ $(OBJS) install: $(PROG) strip $(PROG) mv $(PROG) /usr/local/$(PROG) /etc/chown root /usr/local/$(PROG) chmod 4711 /usr/local/$(PROG) lint: lint -x -b $(CFLAGS) $(SRCS) clean: rm -f $(OBJS) $(PROG) ---END-OF-Makefile.4.1--- echo Extracting Makefile.68000 cat > Makefile.68000 << '---END-OF-Makefile.68000---' # Makefile for SPS (Unisoft Version 1.3 UNIX) PROG = sps OBJS = findtty.o flagdecode.o flagsetup.o \ getcmd11.o globals1.o globals2.o \ initialise.o main.o mktree.o needed.o openfiles.o \ prcmd.o prcpu.o prheader.o printall.o \ printproc.o prsummary.o readstatus.o readsymbols.o \ select.o select_tty.o ttyinit.o ttystatus.o waitingfor.o SRCS = findtty.c flagdecode.c flagsetup.c \ getcmd11.c globals1.c globals2.c \ initialise.c main.c mktree.c needed.c openfiles.c \ prcmd.c prcpu.c prheader.c printall.c \ printproc.c prsummary.c readstatus.c readsymbols.c \ select.c select_tty.c ttyinit.c ttystatus.c waitingfor.c INCS = sps.h CFLAGS = -c -DV7 -DM68000 -I/usr/include/sys CC = cc all: $(PROG) .c.o: $(CC) -O $(CFLAGS) $< openfiles.o: $(CC) $(CFLAGS) $< $(OBJS): $(INCS) $(PROG): $(OBJS) $(CC) -o $@ $(OBJS) install: $(PROG) strip $(PROG) mv $(PROG) /bin/$(PROG) chown root /bin/$(PROG) chmod 4711 /bin/$(PROG) lint: lint -x -b $(CFLAGS) $(SRCS) clean: rm -f $(OBJS) $(PROG) ---END-OF-Makefile.68000--- echo Extracting Makefile.v7 cat > Makefile.v7 << '---END-OF-Makefile.v7---' # Makefile for SPS (Bell Labs PDP-11 Version 7 Unix) PROG = sps OBJS = findtty.o flagdecode.o flagsetup.o \ getcmd11.o globals1.o globals2.o \ initialise.o main.o mktree.o needed.o openfiles.o \ prcmd.o prcpu.o prheader.o printall.o \ printproc.o prsummary.o readstatus.o readsymbols.o \ select.o select_tty.o ttyinit.o ttystatus.o waitingfor.o SRCS = findtty.c flagdecode.c flagsetup.c \ getcmd11.c globals1.c globals2.c \ initialise.c main.c mktree.c needed.c openfiles.c \ prcmd.c prcpu.c prheader.c printall.c \ printproc.c prsummary.c readstatus.c readsymbols.c \ select.c select_tty.c ttyinit.c ttystatus.c waitingfor.c INCS = sps.h CFLAGS = -DV7 -Dvoid=int -I/v7/usr/include/sys CC = /v7/bin/cc all: $(PROG) .c.o: $(CC) $(CFLAGS) -c -O $< $(OBJS): $(INCS) $(PROG): $(OBJS) $(CC) -o $@ $(OBJS) install: $(PROG) strip $(PROG) mv $(PROG) /bin/$(PROG) /etc/chown root /bin/$(PROG) chmod 4711 /bin/$(PROG) lint: lint -x -b $(CFLAGS) $(SRCS) clean: rm -f $(OBJS) $(PROG) ---END-OF-Makefile.v7--- echo Extracting README cat > README << '---END-OF-README---' SPS - Show Process Status ========================= SPS currently runs under four different Unix systems : Bells Labs PDP-11 Unix, Berkeley 4.1BSD VAX Unix (VAX-11/780 and VAX-11/750), IIASA PDP-11/70 Unix, Unisoft Version 1.3 Unix (PSI-9068). Compiler options are as follows - -DVMUNIX for 4.1bsd -DCHAOS if the Chaos network is incorporated into 4.1bsd -DV7 for V7 Unix, IIASA PDP-11/70 and Unisoft -DM68000 for Unisoft -DTRACE for testing/debugging purposes ******************************************************************************** When porting SPS to a new machine, the things to watch out for are the way in which a process' upage is accessed (see pread() in getcmd11.c) and the way in which command arguments are set up on the user stack (see getcmd()). Command arguments are generally set up differently on each machine ; the difficulty lies in trying to locate the base of the argument list. Some versions of Unix also seem to be incorrectly compiled : the `d_ttys' field of the entries in the `cdevsw' table that refer to tty devices is sometimes null. The kernel must then be patched so that these fields refer to the corresponding `struct tty' inside the kernel. ******************************************************************************** If you want to tell SPS about a new type of device, then add make a new entry in the `struct info' (see sps.h) and add a new line in the symbol table (see globals2.c). ******************************************************************************** SPS understands if the size of internal kernel tables are changed under VMUNIX, but not under any other system. It must be recompiled if major modifications are made to the kernel. ---END-OF-README--- echo Extracting filecount.c cat > filecount.c << '---END-OF-filecount.c---' # ifdef VMUNIX # include "sps.h" /* FILECOUNT - Counts the # open files for the current process */ filecount () { register int i ; register struct file **f ; register int count ; extern union userstate User ; count = 0 ; for ( i = 0, f = User.u_us.u_ofile ; i < NOFILE ; i++ ) if ( *f++ ) count++ ; return ( count ) ; } # endif ---END-OF-filecount.c--- echo Extracting findtty.c cat > findtty.c << '---END-OF-findtty.c---' # include "sps.h" /* FINDTTY - Attempts to determine to which tty a process is connected */ struct ttyline *findtty ( p ) register struct process *p ; { register struct ttyline *lp ; extern struct info Info ; extern struct ttyline Notty ; # ifdef VMUNIX extern union userstate User ; # endif if ( !p->pr_p.p_pgrp ) return ( &Notty ) ; for ( lp = Info.i_ttyline ; lp->l_name[0] ; lp++ ) # ifdef VMUNIX if ( lp->l_dev == User.u_us.u_ttyd ) # else if ( lp->l_dev == p->pr_ttyd ) # endif return ( lp ) ; return ( &Notty ) ; } ---END-OF-findtty.c--- echo Extracting flagdecode.c cat > flagdecode.c << '---END-OF-flagdecode.c---' # include "sps.h" /* FLAGDECODE - Looks at the argument list and sets various internal switches */ /* Note that the `d' and `D' switches are only implemented under VMUNIX */ flagdecode ( argc, argv ) register int argc ; register char **argv ; { register char *chp ; union flaglist *plist ; union flaglist *tlist ; union flaglist *ulist ; static char usage[] = # ifdef VMUNIX "sps - Unknown option %s\nUsage - sps [ -defgiqrvwyABDFNPSTUWZ ][ process|tty|user ] ...\n"; # else "sps - Unknown option %s\nUsage - sps [ -efgiqrvwyABFNPSTUWZ ][ process|tty|user ] ...\n"; # endif union flaglist *getflgsp() ; extern struct flags Flg ; plist = tlist = ulist = (union flaglist*)0 ; for ( argv++ ; --argc ; argv++ ) { chp = *argv ; while ( *chp ) switch ( *chp++ ) { case '-' : /* Separation character */ continue ; # ifdef VMUNIX case 'd' : /* List disc orientated information */ Flg.flg_d = 1 ; Flg.flg_v = 0 ; continue ; # endif case 'e' : case 'E' : /* List environment strings */ Flg.flg_e = 1 ; continue ; case 'f' : /* List the father's process id */ Flg.flg_f = 1 ; Flg.flg_g = 0 ; continue ; case 'g' : case 'G' : /* List the process group id */ Flg.flg_f = 0 ; Flg.flg_g = 1 ; continue ; case 'i' : case 'I' : /* Initialise (super-user only) */ Flg.flg_i = 1 ; continue ; case 'l' : case 'v' : case 'L' : case 'V' : /* Verbose output */ # ifdef VMUNIX Flg.flg_d = 0 ; # endif Flg.flg_v = 1 ; continue ; case 'q' : case 'Q' : /* Show only the user time, not the user + system times together. */ Flg.flg_q = 1 ; continue ; case 'r' : case 'R' : /* Repeat output every 5 seconds */ Flg.flg_r = 1 ; continue ; case 'w' : /* Wide output, exceeding 79 columns */ Flg.flg_w = 1 ; continue ; case 'y' : case 'Y' : /* List current tty information */ Flg.flg_y = 1 ; continue ; case 'a' : case 'A' : /* List all processes */ Flg.flg_AZ = 1 ; Flg.flg_A = 1 ; continue ; case 'b' : case 'B' : /* List only busy processes */ Flg.flg_AZ = 1 ; Flg.flg_B = 1 ; continue ; # ifdef VMUNIX case 'D' : /* List only detached processes */ Flg.flg_AZ = 1 ; Flg.flg_D = 1 ; continue ; # endif case 'F' : /* List only foreground processes */ Flg.flg_AZ = 1 ; Flg.flg_F = 1 ; continue ; case 'n' : case 'N' : /* No processes, just the summary line*/ Flg.flg_AZ = 1 ; Flg.flg_N = 1 ; continue ; case 'p' : case 'P' : /* List only the given process ids */ Flg.flg_AZ = 1 ; Flg.flg_P = 1 ; if ( !plist ) plist=Flg.flg_Plist=getflgsp( argc ); while ( argc > 1 ) { if ( **++argv == '-' ) { --argv ; break ; } --argc ; plist->f_chp = *argv ; (++plist)->f_chp = (char*)0 ; } continue ; case 's' : case 'S' : /* List only stopped processes */ Flg.flg_AZ = 1 ; Flg.flg_S = 1 ; continue ; case 't' : case 'T' : /* List only processes attached to the specified terminals */ Flg.flg_AZ = 1 ; Flg.flg_T = 1 ; if ( !tlist ) tlist=Flg.flg_Tlist=getflgsp( argc ); while ( argc > 1 ) { if ( **++argv == '-' ) { --argv ; break ; } --argc ; tlist->f_chp = *argv ; (++tlist)->f_chp = (char*)0 ; } continue ; case 'u' : case 'U' : /* List only processes belonging to the specified users */ Flg.flg_AZ = 1 ; Flg.flg_U = 1 ; if ( !ulist ) ulist=Flg.flg_Ulist=getflgsp( argc ); while ( argc > 1 ) { if ( **++argv == '-' ) { --argv ; break ; } --argc ; ulist->f_chp = *argv ; (++ulist)->f_chp = (char*)0 ; } continue ; case 'W' : /* List only waiting processes */ Flg.flg_AZ = 1 ; Flg.flg_W = 1 ; continue ; case 'z' : case 'Z' : /* List only zombie processes */ Flg.flg_AZ = 1 ; Flg.flg_Z = 1 ; continue ; default : prexit( usage, *argv ) ; /* NOTREACHED */ } } } ---END-OF-flagdecode.c--- echo Extracting flagsetup.c cat > flagsetup.c << '---END-OF-flagsetup.c---' # include "sps.h" /* ** FLAGSETUP - Replaces any users or processes specified by flagdecode() ** with numerical equivalents. The lists are terminated by negative values. ** or null pointers. Ttystatus() must have been previously called to ** initialise the Info structure with chaos tty values. */ flagsetup () { register union flaglist *fp ; register char *chp ; register int i ; register struct ttyline *lp ; int found ; extern struct flags Flg ; extern struct info Info ; /* Look for specified users */ if ( Flg.flg_U ) { if ( !Flg.flg_Ulist->f_chp ) prexit( "sps - User name was expected after -u flag\n"); for ( fp = Flg.flg_Ulist ; chp = fp->f_chp ; fp++ ) { found = 0 ; for ( i = 0 ; i < MAXUSERS ; i++ ) if ( !strncmp( chp, Info.i_unames[i], UNAMELEN)) { fp->f_uid = i, found = 1 ; break ; } if ( !found ) prexit( "sps - Unknown user: %s\n", chp ) ; } fp->f_uid = -1 ; } /* Look for specified process ids */ if ( Flg.flg_P ) { if ( !Flg.flg_Plist->f_chp ) prexit( "sps - Process id was expected after -p flag\n" ) ; for ( fp = Flg.flg_Plist ; chp = fp->f_chp ; fp++ ) { if ( chp[0] < '0' || chp[0] > '9' ) prexit( "sps - Bad process id: %s\n", chp ) ; fp->f_pid = atoi( chp ) ; } fp->f_pid = -1 ; } /* Look for specified ttys */ if ( !Flg.flg_T ) return ; if ( !Flg.flg_Tlist->f_chp ) prexit( "sps - Tty name was expected after -t flag\n" ) ; for ( fp = Flg.flg_Tlist ; chp = fp->f_chp ; fp++ ) { # ifdef VMUNIX /* Under VMUNIX, all ttys have two character names. Thus, a flag of the form `t 8' should be expanded to become `t 08'. */ if ( !chp[1] ) chp[1] = chp[0], chp[0] = '0' ; # endif found = 0 ; for ( lp = Info.i_ttyline ; lp->l_name[0] ; lp++ ) if ( !strncmp( chp, lp->l_name, 2 ) ) { fp->f_ttyline = lp ; found = 1 ; break ; } if ( !found ) prexit( "sps - Unknown tty name: %.2s\n", chp ) ; } fp->f_ttyline = (struct ttyline*)0 ; } ---END-OF-flagsetup.c--- echo Extracting getcmd.c cat > getcmd.c << '---END-OF-getcmd.c---' # ifdef VMUNIX # include "sps.h" /* ** GETCMD - Returns a character string read from a process' upage. ** This character string should represent the arguments to the current process. */ char *getcmd ( p ) register struct process *p ; { register int *ip ; register char *cp ; register char *cp0 ; struct pte pte ; char *argptr ; int nbad, i ; static char argbuf[ MAXARGPG * NBPG ] ; static struct pte ptetable[ PAGETABLESIZE ] ; extern struct flags Flg ; extern union userstate User ; extern int Flmem ; extern int Flswap ; char *strcat(), *strsave() ; long lseek() ; p->pr_csaved = 0 ; p->pr_upag = 0 ; if ( p->pr_p.p_stat == SZOMB ) return ( "** Defunct **" ) ; /* Find the process' upage */ if ( !getupage( p, ptetable ) ) return ( "** No upage **" ) ; /* Is this a system process ? */ if ( p->pr_p.p_flag & SSYS ) switch ( p->pr_p.p_pid ) { case 0 : p->pr_upag = 1 ; return ( "Unix Swapper" ) ; case 2 : p->pr_upag = 1 ; return ( "Unix Pager" ) ; default : break ; } /* Look at the top of the upage to locate the command arguments. To locate each user page, we must access the page table entries to find the physical locations. */ for ( i = 0 ; i < MAXARGPG ; i++ ) { argptr = &argbuf[( MAXARGPG - 1 - i ) * NBPG] ; pte = ptetable[MAXARGPG - 1 - i] ; if ( (p->pr_p.p_flag & SLOAD) && !pte.pg_fod && pte.pg_pfnum ) { /* If the page is loaded, read the arguments from physical memory. */ (void)lseek( Flmem, (long)ctob( pte.pg_pfnum ), 0 ) ; if ( read( Flmem, argptr, NBPG ) != NBPG ) return ( "** Mem read error **" ) ; } else { /* Otherwise the page is on the swap device */ (void)lseek( Flswap, (long)ctob( User.u_us.u_smap.dm_map[0]+DMMIN-1-i), 0 ) ; if ( read( Flswap, argptr, NBPG ) != NBPG ) return ( "** Swap read error **" ) ; } /* Arrive here with `argptr' pointing to the top of the upage. Look down until the end of command arguments is found. */ ip = (int*)&argptr[NBPG] ; if ( i == 0 ) { *--ip = 0 ; ip-- ; } while ( ip > (int*)argptr && *--ip ) ; if ( ip > (int*)argptr || !*ip ) break ; /* If no command arguments were found on this page, then access the next. */ } p->pr_upag = 1 ; p->pr_csaved = 1 ; /* If the command arguments could not be accessed from the user's memory space, get the command name from the system's idea of what the name should be. */ if ( i >= MAXARGPG ) { argbuf[0] = '(' ; (void)strncpy( &argbuf[1], User.u_us.u_comm, sizeof( User.u_us.u_comm ) ) ; (void)strcat( argbuf, ")" ) ; return ( strsave( argbuf ) ) ; } /* Process the command arguments, looking for nulls and unprintable characters. */ cp0 = (char*)(ip + 1) ; if ( !*cp0 ) cp0++ ; nbad = 0 ; for ( cp = cp0 ; cp < &argbuf[MAXARGPG*NBPG] ; cp++ ) { *cp &= 0177 ; if ( !*cp ) { /* Replace nulls with spaces */ *cp = ' ' ; continue ; } if ( *cp < ' ' || *cp == 0177 ) { /* Replace control characters with ?'s */ if ( ++nbad > 5 ) { *cp++ = ' ' ; break ; } *cp = '?' ; continue ; } if ( !Flg.flg_e && *cp == '=' ) { /* Break on an `=' if we are not interested in the environment strings. */ *cp = '\0' ; while ( cp > cp0 && *--cp != ' ' ) *cp = '\0' ; break ; } } while ( *--cp == ' ' ) *cp = '\0' ; return ( strsave( cp0 ) ) ; } # endif ---END-OF-getcmd.c--- echo Extracting getcmd11.c cat > getcmd11.c << '---END-OF-getcmd11.c---' # ifndef VMUNIX # include "sps.h" /* The effective `page' size for a PDP-11/70 ... */ # ifndef NBPG # define NBPG 512 # endif /* For the benefit of Unisoft Unix ... */ # ifdef M68000 # ifndef UBASE # define UBASE 0x1ff000 # endif # endif /* ** GETCMD - Returns a character string read from system memory. ** This character string should represent the arguments to the current process. ** This is functionally similar to the VAX version of getcmd(). Notice, however, ** that, in the interests of speed, only one page is ever accessed to ** determine the command arguments. If the combined arguments plus ** environment span across more than one page, an attempt is made to locate ** the command name from system space. */ char *getcmd ( p ) register struct process *p ; { register char *cp ; register char *cp0 ; # ifdef M68000 register int *cpi ; # endif int nbad ; static char argbuf[ NBPG ] ; extern struct flags Flg ; extern struct info Info ; char *strsave() ; p->pr_csaved = 0 ; p->pr_upag = 0 ; if ( p->pr_p.p_stat == SZOMB ) return ( "** Defunct **" ) ; if ( p->pr_p.p_flag & SSYS ) { p->pr_upag = 1 ; return ( "UNIX Swapper" ) ; } if ( !(p->pr_p.p_flag & SLOAD) && p->pr_p.p_addr > Info.i_nswap ) return ( "** Swap address error **" ) ; if ( pread( p, NBPG, argbuf, sizeof( argbuf ), 1 ) == -1 ) return ( "** Read error **" ) ; p->pr_upag = 1 ; # ifdef M68000 cpi = (int*)&argbuf[NBPG] ; while ( cpi > (int*)argbuf && !*--cpi ) ; if ( cpi <= (int*)&argbuf[0] ) goto notfound ; while ( cpi > (int*)argbuf && *--cpi ) ; if ( cpi <= (int*)&argbuf[0] ) goto notfound ; cp0 = (char*)(cpi+1) ; # else /* Locate the start and end of the command arguments in the buffer. The start is determined by locating a byte containing 0377, searching from the end of the buffer. */ cp = &argbuf[NBPG] ; while ( cp > &argbuf[0] && (int)*--cp != -1 ) ; if ( cp <= &argbuf[0] ) goto notfound ; /* If the command arguments were successfully accessed, we arrive here with `cp' pointing just before a byte containing 0377, followed (possibly) by a series of non-null bytes, followed by a series of nulls. Now increment `cp' until it points to the first byte past the nulls. This, hopefully, represents the beginning of the command arguments. */ cp += 2 ; while ( cp < &argbuf[NBPG] && *cp++ ) ; if ( cp >= &argbuf[NBPG] ) goto notfound ; cp-- ; while ( cp < &argbuf[NBPG] && !*cp++ ) ; if ( cp >= &argbuf[NBPG] ) goto notfound ; cp0 = cp-1 ; # endif p->pr_csaved = 1 ; /* Process the command arguments, looking for nulls and unprintable characters. */ nbad = 0 ; for ( cp = cp0 ; cp < &argbuf[ NBPG ] ; cp++ ) { *cp &= 0177 ; if ( !*cp ) { /* Replace nulls with spaces */ *cp = ' ' ; continue ; } if ( *cp < ' ' || *cp == 0177 ) { /* Replace control characters with ?'s */ if ( ++nbad > 5 ) { *cp++ = ' ' ; break ; } *cp = '?' ; continue ; } if ( !Flg.flg_e && *cp == '=' ) { /* Break on an '=' if we are not interested in the environment strings. */ *cp = '\0' ; while ( cp > cp0 && *--cp != ' ' ) *cp = '\0' ; break ; } } while ( *--cp == ' ' ) *cp = '\0' ; return ( strsave( cp0 ) ) ; notfound : /* We arrive here if the page of command arguments is full, in other words, if we have not found the beginning of the command. Now try to determine the name of the command by accessing the system's idea of what the name is. */ if ( pread( p, (int)((struct user*)0)->u_comm, &argbuf[1], DIRSIZ, 0 ) == -1 ) return ( "** Too many arguments **" ) ; argbuf[0] = '(' ; argbuf[DIRSIZ+1] = '\0' ; for ( cp = &argbuf[1] ; cp <= &argbuf[DIRSIZ+1] ; cp++ ) { *cp &= 0177 ; if ( !*cp ) { *cp++ = ')' ; *cp = '\0' ; break ; } if ( *cp < ' ' || *cp == 0177 ) *cp = '?' ; } p->pr_csaved = 1 ; return ( strsave( &argbuf[0] ) ) ; } /* PREAD - Read process information */ pread ( p, addr, buf, nbytes, flag ) register struct process *p ; register unsigned addr ; register char *buf ; int nbytes ; int flag ; { long seekaddr ; long saddr1, saddr2 ; char *cp ; static char localbuf[ NBPG ] ; extern int Flmem ; extern int Flswap ; extern struct info Info ; long lseek() ; /* `Flag' indicates whether the search is to be from the top of the process address space, or the upage area. */ seekaddr = flag ? (long)ctob( p->pr_p.p_size ) - addr : addr + (int)Info.i_u - UBASE ; /* If the process is loaded, look in its address space. */ if ( p->pr_p.p_flag & SLOAD ) { (void)lseek( Flmem, seekaddr + (long)ctob( p->pr_p.p_addr), 0 ); return ( read( Flmem, buf, nbytes ) ) ; } /* Otherwise the process is swapped out. Look on the swap device. The code here seems to be because /dev/swap is a block device and therefore must be accessed one block at a time. */ seekaddr += ((long)(unsigned)p->pr_p.p_addr + Info.i_swplo) << 9 ; saddr1 = seekaddr / 512L * 512L ; (void)lseek( Flswap, saddr1, 0 ) ; saddr2 = seekaddr % 512L ; if ( !saddr2 ) return ( read( Flswap, buf, nbytes ) ) ; (void)read( Flswap, localbuf, sizeof( localbuf ) ) ; cp = &localbuf[ (int)saddr2 ] ; while ( cp < &localbuf[ sizeof( localbuf ) ] && nbytes-- ) *buf++ = *cp++ ; if ( nbytes > 0 ) return ( read( Flswap, buf, nbytes ) ) ; return ( 0 ) ; } /* GETUINFO - Read information from the process' upage */ getuinfo ( p ) register struct process *p ; { /* NOTE THAT THE STRUCTURE SHOWN BELOW SHOULD MIMIC HOW THE TIME INFORMATION IS STORED IN A PROCESS' UPAGE. */ struct { time_t t_utime ; time_t t_stime ; time_t t_cutime ; time_t t_cstime ; } t ; /* Firstly, read the process cpu times from the upage */ if ( pread( p, (int)&((struct user*)0)->u_utime, (char*)&t, sizeof( t ), 0 ) == -1 ) { p->pr_utime = 0L ; p->pr_stime = 0L ; p->pr_cutime = 0L ; p->pr_cstime = 0L ; } else { p->pr_utime = t.t_utime ; p->pr_stime = t.t_stime ; p->pr_cutime = t.t_cutime ; p->pr_cstime = t.t_cstime ; } /* Now determine to which tty the process is connected */ if ( pread( p, (int)&((struct user*)0)->u_ttyd, (char*)&p->pr_ttyd, sizeof( dev_t ), 0 ) == -1 ) p->pr_ttyd = (dev_t)0 ; } ---END-OF-getcmd11.c--- echo Extracting getupage.c cat > getupage.c << '---END-OF-getupage.c---' # ifdef VMUNIX # include "sps.h" # include <stdio.h> /* GETUPAGE - Reads the upage for the specified process */ # define usrpt (Info.i_usrpt) getupage ( p, ptetable ) register struct process *p ; struct pte *ptetable ; { struct pte *pteaddr ; struct pte pte ; register int i ; int ncl ; extern struct info Info ; extern union userstate User ; extern int Flmem ; extern int Flkmem ; extern int Flswap ; long lseek() ; /* If the process is not loaded, look for the upage on the swap device*/ if ( !(p->pr_p.p_flag & SLOAD) ) { (void)lseek( Flswap, (long)ctob( p->pr_p.p_swaddr ), 0 ) ; if ( read( Flswap, (char*)&User.u_us, sizeof( struct user ) ) != sizeof( struct user ) ) { fprintf( stderr, "sps - Can't read upage of process %d\n", p->pr_p.p_pid ) ; return ( 0 ) ; } return ( 1 ) ; } /* The process is loaded. Find where its pte's are kept */ pteaddr = &Info.i_usrptmap[ btokmx(p->pr_p.p_p0br) + p->pr_p.p_szpt-1 ]; (void)lseek( Flkmem, (long)pteaddr, 0 ); if ( read( Flkmem, (char*)&pte, sizeof( struct pte ) ) != sizeof( struct pte ) ) { fprintf ( stderr, "sps - cant read indir pte to get upage for process %d\n", p->pr_p.p_pid ) ; return ( 0 ) ; } /* Now get sufficient pte's to be able to read the upage */ (void)lseek( Flmem, (long) (ctob(pte.pg_pfnum+1) - PAGETABLESIZE*sizeof( struct pte )), 0 ) ; if ( read( Flmem, (char*)ptetable, PAGETABLESIZE*sizeof( struct pte ) ) != PAGETABLESIZE * sizeof( struct pte ) ) { fprintf( stderr, "sps - cant read page table of process %d\n", p->pr_p.p_pid ) ; return ( 0 ) ; } /* Now we can read the pages belonging to the upage */ ncl = ( sizeof( struct user ) + NBPG*CLSIZE - 1 ) / ( NBPG*CLSIZE ) ; while ( --ncl >= 0 ) { i = ncl * CLSIZE ; (void)lseek(Flmem,(long)ctob(ptetable[MAXARGPG+i].pg_pfnum),0); if ( read( Flmem, User.u_pg[i], CLSIZE*NBPG ) != CLSIZE*NBPG ) { fprintf( stderr, "sps - cant read page %d of process %d\n", ptetable[MAXARGPG+i].pg_pfnum, p->pr_p.p_pid ) ; return( 0 ) ; } } return ( 1 ) ; } # endif ---END-OF-getupage.c--- echo Extracting globals1.c cat > globals1.c << '---END-OF-globals1.c---' # include "sps.h" # ifndef VMUNIX # include <stdio.h> # endif /* Read/Write Variables global to the code of sps */ struct info Info ; /* Information structure */ struct flags Flg ; /* Flag options */ struct summary Summary ; /* Summary of processes */ # ifdef VMUNIX union userstate User ; /* Upage of one process */ # endif int Flmem, Flkmem, Flswap ; /* File descriptors */ short Lastuid ; /* Last process uid printed */ # ifndef VMUNIX char Sobuf[ BUFSIZ ] ; /* Stdout buffer */ # endif ---END-OF-globals1.c--- echo Extracting globals2.c cat > globals2.c << '---END-OF-globals2.c---' # include "sps.h" /* Read Only variables, global to the code of sps ... */ /* Null ttyline device ... */ struct ttyline Notty = { 0, 0, " ", 0 } ; /* ** The symbol table. For each address read from the kernel during ** initialisation, this table shows the following: ** i. the name of that symbol inside the kernel ; ** ii. whether an extra indirection is needed through the kernel ** to obtain the value of that symbol ; ** iii. where the obtained value/address is placed in the Info structure ; ** iv. whether the obtained value is associated with a reason for ** a process wait state. */ /* The order of entries in this table is unimportant. */ extern struct info Info ; struct symbol Symbollist[] = { /* Kernel addresses required in order to access process, tty and upage information. */ # ifdef VMUNIX { "_nproc", 1, (caddr_t*)&Info.i_nproc, (char*)0 }, { "_ntext", 1, (caddr_t*)&Info.i_ntext, (char*)0 }, { "_ninode", 1, (caddr_t*)&Info.i_ninode, (char*)0 }, { "_nswbuf", 1, (caddr_t*)&Info.i_nswbuf, (char*)0 }, { "_nbuf", 1, (caddr_t*)&Info.i_nbuf, (char*)0 }, { "_hz", 1, (caddr_t*)&Info.i_hz, (char*)0 }, { "_ecmx", 1, (caddr_t*)&Info.i_ecmx, (char*)0 }, { "_pt_tty", 0, (caddr_t*)&Info.i_pt_tty, (char*)0 }, { "_Usrptmap", 0, (caddr_t*)&Info.i_usrptmap, (char*)0 }, { "_usrpt", 0, (caddr_t*)&Info.i_usrpt, (char*)0 }, { "_swbuf", 1, (caddr_t*)&Info.i_swbuf0, (char*)0 }, { "_proc", 1, (caddr_t*)&Info.i_proc0, (char*)0 }, { "_text", 1, (caddr_t*)&Info.i_text0, (char*)0 }, { "_inode", 1, (caddr_t*)&Info.i_inode0, (char*)0 }, { "_buf", 1, (caddr_t*)&Info.i_buf0, (char*)0 }, { "_cdevsw", 0, (caddr_t*)&Info.i_cdevsw, (char*)0 }, # ifdef CHAOS { "_Chconntab", 0, &Info.i_Chconntab, (char*)0 }, # endif # else { "_nswap", 1, (caddr_t*)&Info.i_nswap, (char*)0 }, { "_swplo", 1, (caddr_t*)&Info.i_swplo, (char*)0 }, { "_proc", 0, (caddr_t*)&Info.i_proc0, (char*)0 }, { "_text", 0, (caddr_t*)&Info.i_text0, (char*)0 }, { "_inode", 0, (caddr_t*)&Info.i_inode0, (char*)0 }, { "_buf", 0, (caddr_t*)&Info.i_buf0, (char*)0 }, { "_cdevsw", 0, (caddr_t*)&Info.i_cdevsw, (char*)0 }, # endif /* Kernel addresses associated with process wait states. */ # ifdef VMUNIX { "_fltab", 0, &Info.i_fltab, "floppy" }, { "_va_softc", 0, &Info.i_va_softc, "varian" }, { "_bfreelist", 0, &Info.i_bfreeli, "buffer" }, { "_lp_softc", 0, &Info.i_lpdt, "printr" }, { "_rswbuf", 0, &Info.i_rswbuf, "rswap" }, # ifdef CHAOS { "_Chrfclist", 0, &Info.i_chrfclist, "chrfc" }, # endif # else { "_vn11", 0, &Info.i_va_softc, "varian" }, { "_bfreeli", 0, &Info.i_bfreeli, "buffer" }, { "_lp_dt", 0, &Info.i_lpdt, "printr" }, { "_swbuf1", 0, &Info.i_swap1, "swap1" }, { "_swbuf2", 0, &Info.i_swap2, "swap2" }, # endif { "_u", 0, &Info.i_u, "pause" }, { "_chtbuf", 0, &Info.i_chtbuf, "tapecn" }, { "_rhtbuf", 0, &Info.i_rhtbuf, "tapeio" }, { "_lbolt", 0, &Info.i_lbolt, "lbolt" }, { "_runin", 0, &Info.i_runin, "runin" }, { "_runout", 0, &Info.i_runout, "runout" }, { "_ipc", 0, &Info.i_ipc, "ptrace" }, { (char*)0, 0, (caddr_t*)0, (char*)0 } } ; ---END-OF-globals2.c--- echo Extracting initialise.c cat > initialise.c << '---END-OF-initialise.c---' # include "sps.h" # include <pwd.h> # include <stdio.h> /* ** INITIALISE - Called to reset the `Info' structure with new kernel ** addresses and user and tty information. */ initialise () { register FILE *fd ; static char file_info[] = FILE_INFO ; extern struct info Info ; FILE *fopen() ; # ifndef TRACE if ( getuid() ) prexit( "sps - You are not the super-user\n" ) ; # endif /* Read kernel addresses */ readsymbols() ; /* Read user names */ readusers() ; (void)umask( ~0644 ) ; if ( !(fd = fopen( file_info, "w" )) ) { fprintf( stderr, "sps - Can't create info file %s", file_info ); sysperror() ; } /* Find tty addresses */ ttyinit() ; if ( fwrite( (char*)&Info, sizeof( struct info ), 1, fd ) != 1 ) { fprintf( stderr, "sps - can't write info file %s", file_info ) ; sysperror() ; exit( 1 ) ; } (void)fclose( fd ) ; printf( "Sps is initialised\n" ) ; } /* READUSERS - Read the passwd file and fill in the user name arrays */ readusers () { register struct passwd *pw ; struct passwd *getpwent() ; char *strncpy() ; extern struct info Info ; while ( pw = getpwent() ) { if ( pw->pw_uid >= MAXUSERS ) { fprintf( stderr, "sps - User %s has uid %d exceeding maximum %d\n", pw->pw_name, pw->pw_uid, MAXUSERS ) ; continue ; } if ( !Info.i_unames[ pw->pw_uid ][0] ) (void)strncpy( &Info.i_unames[ pw->pw_uid ][0], pw->pw_name, UNAMELEN ) ; } endpwent() ; } ---END-OF-initialise.c--- echo Extracting main.c cat > main.c << '---END-OF-main.c---' # include "sps.h" # include <stdio.h> /* SPS - Show Process Status */ /* J. R. Ward - IIASA - 31 March 1983 */ main ( argc,argv ) int argc ; char **argv ; { register struct process *plist ; # ifdef VMUNIX register struct process *process ; register struct text *text ; # else static struct process process[ NPROC ] ; static struct text text[ NTEXT ] ; # endif int flinfo ; extern struct flags Flg ; extern struct info Info ; extern int Flmem ; extern int Flkmem ; extern int Flswap ; # ifndef VMUNIX extern char Sobuf[] ; # endif # ifdef VMUNIX char *getcore() ; # endif struct process *needed(), *mktree() ; # ifndef TRACE (void)nice( -40 ) ; (void)setuid( getuid() ) ; # endif # ifndef VMUNIX setbuf( stdout, Sobuf ) ; # endif /* Decode the flag arguments */ flagdecode( argc, argv ) ; /* Open the cpu physical memory, kernel virtual memory and swap device*/ Flmem = openfile( FILE_MEM ) ; Flkmem = openfile( FILE_KMEM ) ; Flswap = openfile( FILE_SWAP ) ; if ( Flg.flg_i ) { /* -i flag for info file initialisation */ initialise() ; exit( 0 ) ; } /* Read the information file */ flinfo = openfile( FILE_INFO ) ; if ( read( flinfo, (char*)&Info, sizeof( struct info ) ) != sizeof( struct info ) ) { fprintf( stderr, "sps - Can't read info file %s", FILE_INFO ) ; sysperror() ; } (void)close( flinfo ) ; /* Find current tty status */ ttystatus() ; /* Now that we know the available ttys, decode the flags */ flagsetup() ; # ifdef VMUNIX process = (struct process*)getcore(Info.i_nproc*sizeof(struct process)); text = (struct text*)getcore( Info.i_ntext * sizeof( struct text ) ) ; # endif do { /* Read current process and text status */ readstatus( process, text ) ; /* Select those processes to be listed */ plist = needed( getuid(), process, text ) ; /* Form a tree of listed processes */ plist = mktree( process, plist ) ; if ( !Flg.flg_N ) { /* Print the processes */ prheader() ; printall( plist, 0 ) ; } prsummary() ; (void)fflush( stdout ) ; if ( Flg.flg_r ) { /* If repeating, again get tty status */ ttystatus() ; sleep( 5 ) ; } } while ( Flg.flg_r ) ; exit( 0 ) ; } ---END-OF-main.c--- echo Extracting mktree.c cat > mktree.c << '---END-OF-mktree.c---' # include "sps.h" /* ** MKTREE - Sort the needed processes by subtree and at the top by user. ** This procedure takes a list of processes (as returned by needed()) ** and returnes a pointer to a sorted list. */ struct process *mktree ( process, plist ) struct process *process ; struct process *plist ; { register struct process *p ; register struct process *pp ; register struct process *lp ; struct process *op ; struct process proot ; proot.pr_sibling = (struct process*)0 ; for ( p = plist ; p ; p = p->pr_plink ) { if ( p->pr_pptr > &process[1] ) { for ( pp = plist ; pp ; pp = pp->pr_plink ) { if ( pp != p->pr_pptr ) continue ; if ( lp = pp->pr_child ) { /* Does process have children ? */ op = (struct process*)0 ; while (lp && lp->pr_p.p_pid < p->pr_p.p_pid ) { op = lp ; lp=lp->pr_sibling ; } if ( op ) { p->pr_sibling = lp ; op->pr_sibling = p ; break ; } } p->pr_sibling = lp ; pp->pr_child = p ; break ; } if ( pp ) continue ; } /* We have a top level process, sort into top level list. The top level is sorted firstly by user-id and then by process-id. */ lp = &proot ; pp = lp->pr_sibling ; while ( pp ) { if ( p->pr_p.p_uid < pp->pr_p.p_uid ) break ; if ( p->pr_p.p_uid == pp->pr_p.p_uid && p->pr_p.p_pid < pp->pr_p.p_pid ) break ; lp = pp, pp = pp->pr_sibling ; } p->pr_sibling = lp->pr_sibling ; lp->pr_sibling = p ; } return ( proot.pr_sibling ) ; } ---END-OF-mktree.c--- echo Extracting needed.c cat > needed.c << '---END-OF-needed.c---' # include "sps.h" # include <stdio.h> /* ** NEEDED - Determine which processes are needed for the printout ** and add these to a list of needed processes. */ struct process *needed ( thisuid, process, text ) int thisuid ; register struct process *process ; struct text *text ; { register struct process *p ; # ifdef V7 register struct process *pp ; # endif register struct process *plist ; struct process *lastp ; extern struct flags Flg ; # ifdef VMUNIX extern union userstate User ; # endif extern struct info Info ; extern struct ttyline Notty ; struct ttyline *findtty() ; char *getcmd() ; plist = (struct process*)0 ; # ifdef VMUNIX lastp = &process[Info.i_nproc] ; # else lastp = &process[NPROC] ; # endif /* Normalise internal pointers from kernel addresses. For each kmem address in the `proc' and `text' structures, we convert that address for our own internal use. */ for ( p = process ; p < lastp ; p++ ) { if ( !p->pr_p.p_stat ) continue ; /* Normalise internal text pointers */ if ( p->pr_p.p_textp ) p->pr_p.p_textp = &text[p->pr_p.p_textp - Info.i_text0]; /* Normalise internal linked list of processes */ p->pr_plink = p->pr_p.p_link ? &process[ p->pr_p.p_link - Info.i_proc0 ] : (struct process*)0 ; # ifdef V7 /* Compensate for the lack of a parent pointer in the standard V7 proc structure ; here we go through the proc array searching for a process that has the same id as that of the current parent. */ p->pr_pptr = (struct process*)0 ; for ( pp = process ; pp < lastp ; pp++ ) if ( pp->pr_p.p_pid == p->pr_p.p_ppid ) { p->pr_pptr = pp ; break ; } # else /* Normalise internal process parent pointers */ p->pr_pptr = p->pr_p.p_pptr ? &process[ p->pr_p.p_pptr - Info.i_proc0 ] : (struct process*)0 ; # endif /* Check for valid parent pointers */ if ( !p->pr_pptr ) { p->pr_pptr = process ; continue ; } if ( p->pr_pptr < process || p->pr_pptr >= lastp ) { fprintf( stderr, "sps - process %d has bad pptr\n", p->pr_p.p_pid ) ; p->pr_pptr = process ; } } /* For each process, see if it is a candidate for selection. If so, retrieve its command arguments and upage information. */ for ( p = process ; p < lastp ; p++ ) { if ( !p->pr_p.p_stat ) continue ; /* Count processes and sizes */ summarise( p ) ; /* Select the given processes. Bear in mind that selection of processes based on the `F' and `T' flags must be postponed until the upage is accessed. */ if ( !Flg.flg_F && !Flg.flg_T && !select( p, process, thisuid )) continue ; /* Try to find the process' command arguments. Accessing the arguments also involves retrieving the upage. */ p->pr_cmd = getcmd( p ) ; /* If the upage was found successfully, use this information */ if ( p->pr_upag ) { # ifdef VMUNIX p->pr_vself = User.u_us.u_vm ; p->pr_vchild = User.u_us.u_cvm ; p->pr_tty = findtty( p ) ; p->pr_files = filecount() ; # else getuinfo( p ) ; p->pr_tty = findtty( p ) ; # ifndef V7 /* Here we simulate the missing SDETACH bit from the proc structure. A process is detached if its parent is detached, or if its parent is process 1 and it is running in the background. */ p->pr_detach = p->pr_pptr->pr_detach || (p->pr_pptr->pr_p.p_pid == 1 && (p->pr_p.p_flag & SBGRND)) ; # endif # endif } else p->pr_tty = &Notty ; /* Select on the basis of the `F' and `T' flags */ if ( Flg.flg_F && !(p->pr_p.p_pgrp && p->pr_p.p_pgrp == p->pr_tty->l_pgrp) ) continue ; if ( Flg.flg_T && !select_tty( p ) ) continue ; /* Arrive here with a selected process. Add this to the linked list of needed processes. */ p->pr_plink = plist ; plist = p ; p->pr_child = (struct process*)0 ; p->pr_sibling = (struct process*)0 ; /* Check for valid process user-id */ if ( p->pr_p.p_uid >= MAXUSERS ) { fprintf( stderr, "sps - Process %d has uid %d exceeding maximum %d\n", p->pr_p.p_pid, p->pr_p.p_uid, MAXUSERS ) ; p->pr_p.p_uid %= MAXUSERS ; } } return ( plist ) ; } /* SUMMARISE - Summarises the given process into the `Summary' structure */ /* ** SHOULD ACCOUNT HERE FOR THE SIZE OF LOADED PAGE TABLES, BUT WE DON'T REALLY ** KNOW THEIR RESIDENT SIZES. */ summarise ( p ) register struct process *p ; { register struct text *tp ; int busy ; extern struct summary Summary ; Summary.sm_ntotal++ ; if ( p->pr_p.p_stat == SZOMB ) return ; /* Firstly, account for processes */ # ifdef VMUNIX Summary.sm_ktotal += p->pr_p.p_dsize + p->pr_p.p_ssize ; Summary.sm_kloaded += p->pr_p.p_rssize ; Summary.sm_kswapped += p->pr_p.p_swrss ; # else Summary.sm_ktotal += p->pr_p.p_size ; if ( p->pr_p.p_flag & SLOAD ) Summary.sm_kloaded += p->pr_p.p_size ; # endif if ( p->pr_p.p_flag & SLOAD ) Summary.sm_nloaded++ ; else Summary.sm_nswapped++ ; busy = (p->pr_p.p_stat == SRUN) || (p->pr_p.p_stat==SSLEEP && (p->pr_p.p_pri<PZERO && p->pr_p.p_pid > MSPID) ) ; if ( busy ) { Summary.sm_nbusy++ ; # ifdef VMUNIX Summary.sm_kbusy += p->pr_p.p_dsize + p->pr_p.p_ssize ; # else Summary.sm_kbusy += p->pr_p.p_size ; # endif } /* Now account for their texts */ if ( !(tp = p->pr_p.p_textp) || !tp->x_count ) return ; Summary.sm_ktotal += tp->x_size ; # ifdef VMUNIX Summary.sm_kloaded += tp->x_rssize ; Summary.sm_kswapped += tp->x_swrss ; # else if ( p->pr_p.p_flag & SLOAD ) Summary.sm_kloaded += tp->x_size ; # endif if ( busy ) Summary.sm_kbusy += tp->x_size ; tp->x_count = 0 ; } ---END-OF-needed.c--- echo Extracting openfiles.c cat > openfiles.c << '---END-OF-openfiles.c---' # include <stdio.h> # include "sps.h" /* Miscellaneous procedures */ /* OPENFILE - Opens the named file */ openfile ( name ) char *name ; { register int fd ; if ( (fd = open( name, 0 )) >= 0 ) return ( fd ) ; fprintf( stderr, "sps - Can't open %s", name ) ; sysperror() ; /* NOTREACHED */ } # ifdef lint int errno ; int sys_nerr ; char *sys_errlist[] ; # endif /* SYSPERROR - Reports a system defined error msg and then exits gracefully */ sysperror () { extern int errno ; extern int sys_nerr ; extern char *sys_errlist[] ; if ( 0 < errno && errno < sys_nerr ) fprintf( stderr, " : %s", sys_errlist[errno] ) ; fputc( '\n', stderr ) ; exit( 1 ) ; } /* STRSAVE - Store a string in core for later use. */ char *strsave ( cp ) register char *cp ; { register char *chp ; char *getcore(), *strcpy() ; chp = getcore( strlen( cp ) + 1 ) ; (void)strcpy( chp, cp ) ; return ( chp ) ; } /* GETCORE - Allocate and return a pointer to the asked for amount of core */ char *getcore ( size ) register int size ; { register char *chp ; char *malloc() ; if ( chp = malloc( (unsigned)size ) ) return ( chp ) ; fprintf( stderr, "sps - Out of core" ) ; sysperror() ; /* NOTREACHED */ } union flaglist *getflgsp ( argc ) register int argc ; { char *getcore() ; return ( (union flaglist*)getcore( sizeof( union flaglist )*argc ) ) ; } /* PREXIT - Print an error message and exit */ /* VARARGS1 */ /* ARGSUSED */ prexit ( fmt, args ) char *fmt ; { _doprnt( fmt, &args, stderr ) ; exit( 1 ) ; } ---END-OF-openfiles.c--- echo Extracting percentmem.c cat > percentmem.c << '---END-OF-percentmem.c---' # ifdef VMUNIX # include "sps.h" /* PERCENTMEM - Returns the percentage of real memory used by this process */ double percentmem ( p ) register struct process *p ; { register struct text *tp ; double fracmem ; int szptudot ; extern struct info Info ; tp = p->pr_p.p_textp ; if ( !(p->pr_p.p_flag & SLOAD) || !tp ) return ( 0.0 ) ; szptudot = UPAGES + clrnd( ctopt( p->pr_p.p_dsize + p->pr_p.p_ssize ) ); fracmem = ( (double)p->pr_p.p_rssize+szptudot ) / CLSIZE ; if ( tp->x_ccount ) fracmem += ((double)tp->x_rssize)/CLSIZE/tp->x_ccount ; return ( 100.0 * fracmem / (double)Info.i_ecmx ) ; } # endif ---END-OF-percentmem.c--- echo Extracting prcmd.c cat > prcmd.c << '---END-OF-prcmd.c---' # include "sps.h" /* PRCMD - Prints the command arguments according to the switches */ prcmd ( p, lpad, width ) register struct process *p ; int lpad ; int width ; { extern struct flags Flg ; # ifdef VMUNIX printf( "%*d ", lpad+5, Flg.flg_f ? p->pr_p.p_ppid : # else printf( "%*d ", lpad+5, Flg.flg_f ? p->pr_pptr->pr_p.p_pid : # endif Flg.flg_g ? p->pr_p.p_pgrp : p->pr_p.p_pid ) ; if ( Flg.flg_w ) printf( "%s\n", p->pr_cmd ) ; else printf( "%-.*s\n", width, p->pr_cmd ) ; if ( p->pr_csaved ) free( p->pr_cmd ) ; } ---END-OF-prcmd.c--- echo Extracting prcpu.c cat > prcpu.c << '---END-OF-prcpu.c---' # include "sps.h" # ifdef VMUNIX # define HERTZ (Info.i_hz) # else # define HERTZ HZ # endif /* PRCPU - Print cpu time */ prcpu ( time ) register time_t time ; { extern struct info Info ; /* Assume more than 1000 hours is in error */ if ( time < 0 || time > HERTZ*1000L*60L*60L ) { printf( " " ) ; return ; } if ( time < HERTZ*60L*10L ) { /* Less than 10 minutes */ printf( "%3d.%1d", (int)(time/HERTZ), (int)(time % HERTZ / (HERTZ/10L)) ) ; return ; } time /= HERTZ ; /* If less than 10 hours, print as minutes */ if ( time < 60L*60L*10L ) printf( "%3D M", time/60L ) ; else printf( "%3D H", time/60L/60L ) ; } ---END-OF-prcpu.c--- echo Extracting prheader.c cat > prheader.c << '---END-OF-prheader.c---' # include "sps.h" /* PRHEADER - Print a header according to the switches */ prheader () { # ifdef VMUNIX static char vhdr[] = "Status Fl Nice Virtual Resident %M Time Child %C Proc# Command\n" ; static char dhdr[] = " Files PageFaults Swap BlockI/O Kbytsecs Proc# Command\n" ; # else static char vhdr[] = "Status Fl Nice Memory Time Child Proc# Command\n" ; # endif static char hdr[] = "Proc# Command\n" ; extern struct flags Flg ; printf( "Ty User " ) ; # ifdef VMUNIX printf( "%s", Flg.flg_v ? vhdr : Flg.flg_d ? dhdr : hdr ) ; # else printf( "%s", Flg.flg_v ? vhdr : hdr ) ; # endif } ---END-OF-prheader.c--- echo Extracting printall.c cat > printall.c << '---END-OF-printall.c---' # include <stdio.h> # include "sps.h" /* PRINTALL - Recursively print the process tree. */ printall ( p, md ) register struct process *p ; register int md ; { while ( p ) { /* Print this process */ printproc( p, md ) ; (void)fflush( stdout ) ; /* Print child processes */ printall( p->pr_child, md+1 ) ; /* Print brother processes */ p = p->pr_sibling ; } } ---END-OF-printall.c--- echo Extracting printproc.c cat > printproc.c << '---END-OF-printproc.c---' # include "sps.h" /* PRINTPROC - Pretty print a process according to the switches. */ printproc ( p, md ) register struct process *p ; int md ; { register char *chp ; register struct text *tp ; char chbuf[10] ; time_t time ; time_t chtime ; extern short Lastuid ; extern struct flags Flg ; extern struct info Info ; char *waitingfor() ; # ifdef VMUNIX double percentmem() ; # endif /* List tty name and foreground/background/detached information */ printf( "%2.2s%c", p->pr_tty->l_name, !p->pr_p.p_pgrp ? ' ' : # ifdef VMUNIX p->pr_p.p_flag & SDETACH ? '_' : # else # ifndef V7 p->pr_detach ? '_' : # endif # endif p->pr_p.p_pgrp == p->pr_tty->l_pgrp ? '.' : ' ' ) ; if ( !md ) { /* If a top-level process, list the user name */ if ( Info.i_unames[ p->pr_p.p_uid ][0] ) printf( "%-8.8s ", Info.i_unames[ p->pr_p.p_uid ] ) ; else printf( "user%-4.4d ", p->pr_p.p_uid ) ; } else { /* Usually list an asterisk for a child process */ md = md > 8 ? 8 : md ; printf( "%*s*", md, "" ) ; /* But beware of setuid processes */ chp = p->pr_p.p_uid == Lastuid ? "" : Info.i_unames[ p->pr_p.p_uid ] ; md = 8 - md ; printf( "%-*.*s", md, md, chp ) ; } Lastuid = p->pr_p.p_uid ; # ifdef VMUNIX if ( Flg.flg_d ) { /* List disc I/O and paging information */ if ( !p->pr_upag || p->pr_p.p_stat == SZOMB ) { prcmd( p, 44, 17 ) ; return ; } printf( "%2d %8d+%8d %4d %8d %8D ", p->pr_files, p->pr_vself.vm_majflt, p->pr_vself.vm_minflt, p->pr_vself.vm_nswap, p->pr_vself.vm_inblk + p->pr_vself.vm_oublk, MSIZE( ( p->pr_vself.vm_idsrss + p->pr_vself.vm_ixrss )/Info.i_hz ) ) ; prcmd( p, 0, 17 ) ; return ; } # endif if ( !Flg.flg_v ) { /* Not verbose so just list command arguments */ prcmd( p, 0, 61 ) ; return ; } /* Arrive here if being verbose ; list cpu information */ switch ( p->pr_p.p_stat ) { case SSLEEP : case SWAIT : case SIDL : /* Determine why a process should be in a wait state */ chp = waitingfor( p ) ; break ; case SRUN : chp = "run" ; break ; case SZOMB : chp = "exit" ; break ; case SSTOP : chp = "stop" ; break ; } /* If the process is loaded, list the status information in capitals */ printf( "%-6.6s ", p->pr_p.p_flag & SLOAD ? (capitals( chp, chbuf ), chbuf) : chp ) ; /* List process flags */ # ifdef VMUNIX printf( "%c%c%c", p->pr_p.p_flag & SSYS ? 'U' : p->pr_p.p_flag & STRC ? 'T' : ' ', p->pr_p.p_flag & SVFORK ? 'V' : p->pr_p.p_flag & SPHYSIO ? 'I' : ' ', p->pr_p.p_flag & (SANOM|SUANOM) ? 'A' : ' ' ) ; # else printf( "%c%c%c", p->pr_p.p_flag & SSYS ? 'U' : p->pr_p.p_flag & STRC ? 'T' : ' ', p->pr_p.p_flag & SSWAP ? 'S' : ' ', p->pr_p.p_flag & SULOCK ? 'L' : ' ' ) ; # endif /* List process niceness */ if ( p->pr_p.p_nice != NZERO ) printf( "%3d ", p->pr_p.p_nice - NZERO ) ; else printf( " " ) ; if ( p->pr_p.p_stat == SZOMB ) { # ifdef VMUNIX prcmd( p, 36, 11 ) ; # else prcmd( p, 23, 13 ) ; # endif return ; } /* List process and text virtual sizes */ # ifdef VMUNIX printf( "%4d", MSIZE( p->pr_p.p_dsize + p->pr_p.p_ssize ) ) ; if ( tp = p->pr_p.p_textp ) printf( "+%3d ", MSIZE( tp->x_size ) ) ; else printf( " " ) ; /* List process and text real sizes */ printf( "%4d", MSIZE( p->pr_p.p_rssize ) ) ; if ( tp ) printf( "+%3d %2.0f ", MSIZE( tp->x_rssize ), percentmem( p ) ); else printf( " " ) ; # else printf( " " ) ; prmem( p->pr_p.p_size ) ; if ( tp = p->pr_p.p_textp ) { printf( "+" ) ; prmem( tp->x_size ) ; } else printf( " " ) ; printf( " " ) ; # endif /* List information obtained from the upage. This includes the process times and command arguments. */ if ( !p->pr_upag ) { # ifdef VMUNIX prcmd( p, 15, 11 ) ; # else prcmd( p, 12, 24 ) ; # endif return ; } /* List process time information */ # ifdef VMUNIX time = Flg.flg_q ? p->pr_vself.vm_utime : p->pr_vself.vm_utime + p->pr_vself.vm_stime ; chtime = Flg.flg_q ? p->pr_vchild.vm_utime : p->pr_vchild.vm_utime + p->pr_vchild.vm_stime ; # else time = Flg.flg_q ? p->pr_utime : p->pr_utime + p->pr_stime ; chtime = Flg.flg_q ? p->pr_cutime : p->pr_cutime + p->pr_cstime ; # endif prcpu( time ) ; if ( chtime != 0L ) { printf( "+" ) ; prcpu( chtime ) ; } else printf( " " ) ; # ifdef VMUNIX if ( time ) printf( " %2.0f ", p->pr_p.p_pctcpu * 100.0 ) ; else printf( " " ) ; # endif /* Finally, list the process command arguments. */ # ifdef VMUNIX prcmd( p, 0, 11 ) ; # else prcmd( p, 1, 24 ) ; # endif } /* CAPITALS - Converts letters in `chp' to upper-case in buffer `buf'. */ capitals ( chp, buf ) register char *chp ; register char *buf ; { while ( *buf = *chp++ ) { if ( 'a' <= *buf && *buf <= 'z' ) *buf -= 'a' - 'A' ; buf++ ; } } # ifndef VMUNIX /* PRMEM - List the memory size of a given process */ prmem ( size ) register int size ; { # ifdef M68000 printf( "%4d", MSIZE( size ) ) ; # else printf( "%2d.%1d", (size / 16) & 07777, (size % 16) * 10/64 ) ; # endif } # endif ---END-OF-printproc.c--- echo Extracting prsummary.c cat > prsummary.c << '---END-OF-prsummary.c---' # include "sps.h" /* PRSUMMARY - Print the summarising information */ prsummary () { extern struct summary Summary ; printf( # ifdef VMUNIX "%D (%Dk) processes, %D (%Dk) busy, %D (%Dk) loaded, %D (%Dk) swapped\n", # else "%D (%Dk) processes, %D (%Dk) busy, %D (%Dk) loaded, %D swapped\n", # endif Summary.sm_ntotal, MSIZE( Summary.sm_ktotal ), Summary.sm_nbusy, MSIZE( Summary.sm_kbusy ), Summary.sm_nloaded, MSIZE( Summary.sm_kloaded ), # ifdef VMUNIX Summary.sm_nswapped, MSIZE( Summary.sm_kswapped ) ) ; # else Summary.sm_nswapped ) ; # endif Summary.sm_ntotal = Summary.sm_ktotal = 0L ; Summary.sm_nbusy = Summary.sm_kbusy = 0L ; Summary.sm_nloaded = Summary.sm_kloaded = 0L ; # ifdef VMUNIX Summary.sm_nswapped = Summary.sm_kswapped = 0L ; # else Summary.sm_nswapped = 0L ; # endif } ---END-OF-prsummary.c--- echo Extracting readstatus.c cat > readstatus.c << '---END-OF-readstatus.c---' # include "sps.h" /* READSTATUS - Reads the kernel memory for current processes and texts */ readstatus ( process, text ) register struct process *process ; struct text *text ; { register struct proc *p ; # ifdef VMUNIX register struct proc *p0 ; # else static struct proc p0[ NPROC ] ; # endif register struct process *pr ; extern struct info Info ; extern int Flkmem ; long lseek() ; # ifdef VMUNIX char *getcore() ; # endif /* Read current text information */ (void)lseek( Flkmem, (long)Info.i_text0, 0 ) ; # ifdef VMUNIX (void)read( Flkmem, (char*)text, Info.i_ntext * sizeof( struct text ) ); # else (void)read( Flkmem, (char*)text, NTEXT * sizeof( struct text ) ); # endif /* Read current process information */ # ifdef VMUNIX p0 = (struct proc*)getcore( sizeof( struct proc )*Info.i_nproc ) ; # endif (void)lseek( Flkmem, (long)Info.i_proc0, 0 ) ; # ifdef VMUNIX (void)read( Flkmem, (char*)p0, Info.i_nproc * sizeof( struct proc ) ) ; /* Copy process information into our own array */ for ( p = p0, pr = process ; pr < &process[Info.i_nproc] ; p++, pr++ ) pr->pr_p = *p ; free( (char*)p0 ) ; # else (void)read( Flkmem, (char*)p0, NPROC * sizeof( struct proc ) ) ; for ( p = p0, pr = process ; pr < &process[NPROC] ; p++, pr++ ) pr->pr_p = *p ; # endif } ---END-OF-readstatus.c--- echo Extracting readsymbols.c cat > readsymbols.c << '---END-OF-readsymbols.c---' # include "sps.h" # ifdef VMUNIX # include <nlist.h> # else # include <a.out.h> # endif # include <stdio.h> # ifndef NCPS # define NCPS 8 # endif /* READSYMBOLS - Reads kmem values into the Info structure */ /* ** THIS CODE COPIES KMEM VALUES INTO THE INFO STRUCTURE ASSUMING THAT ** VALUES READ FROM THE KERNEL HAVE TYPE CADDR_T. THEREFORE, WE ARE ** MAKING THE DUBIOUS ASSUMPTION THAT INTS, POINTERS AND CADDR_T's ** HAVE IDENTICAL SIZES. */ readsymbols () { register struct nlist *np ; register struct symbol *s ; register struct nlist *np0 ; static char file_symbol[] = FILE_SYMBOL ; extern struct symbol Symbollist[] ; extern int Flkmem ; char *getcore() ; long lseek() ; # ifndef VMUNIX char *strncpy() ; # endif /* Find the length of the symbol table */ for ( s = Symbollist ; s->s_kname ; s++ ) ; /* Construct an nlist structure by copying names from the symbol table*/ np0 = (struct nlist*)getcore( (s-Symbollist+1)*sizeof( struct nlist ) ); for ( s = Symbollist, np = np0 ; s->s_kname ; s++, np++ ) { # ifdef VMUNIX np->n_name = s->s_kname ; np[1].n_name = (char*)0 ; # else (void)strncpy( np->n_name, s->s_kname, NCPS ) ; np[1].n_name[0] = '\0' ; # endif np->n_value = 0 ; } /* Get kernel addresses */ nlist( file_symbol, np0 ) ; # ifdef VMUNIX if ( np0[0].n_value == -1 ) { fprintf( stderr, "sps - Can't read symbol file %s", file_symbol ) ; sysperror() ; } # endif /* Copy addresses and values into the `Info' structure */ for ( s = Symbollist, np = np0 ; s->s_kname ; s++, np++ ) { if ( !np->n_value ) { # ifdef VMUNIX fprintf( stderr, "sps - Can't find symbol %s in %s\n", np->n_name, file_symbol ) ; # else fprintf( stderr, "sps - Can't find symbol %*.*s in %s\n", NCPS, NCPS, np->n_name, file_symbol ) ; # endif *s->s_info = (caddr_t)0 ; continue ; } /* If no indirection is required, just copy the obtained value into the `Info' structure. */ if ( !s->s_indirect ) { /* DUBIOUS ASSUMPTION THAT KMEM VALUE HAS SIZE OF A CADDR_T */ *s->s_info = (caddr_t)np->n_value ; continue ; } /* Otherwise one level of indirection is required. Using the obtained address, look again in the kernel for the value */ (void)lseek( Flkmem, (long)np->n_value, 0 ) ; /* DUBIOUS ASSUMPTION THAT KMEM VALUE HAS SIZE OF A CADDR_T */ (void)read( Flkmem, (char*)s->s_info, sizeof(caddr_t) ) ; } free( (char*)np0 ) ; } ---END-OF-readsymbols.c--- echo Extracting select.c cat > select.c << '---END-OF-select.c---' # include "sps.h" /* ** SELECT - Given a process structure, this procedure decides whether ** the process is a candidate for printing. */ select ( p, process, thisuid ) register struct process *p ; register struct process *process ; int thisuid ; { register union flaglist *fp ; register struct process *pp ; extern struct flags Flg ; /* Flg.flg_AZ is an internal flag set if one of flags `A' to `Z' was specified. If this is not set, a process is listed only if it or one of its ancestors belongs to the invoking user. */ if ( !Flg.flg_AZ ) for ( pp = p ; pp > &process[1] ; pp = pp->pr_pptr ) if ( thisuid == pp->pr_p.p_uid ) return ( 1 ) ; if ( Flg.flg_A ) return ( 1 ) ; if ( Flg.flg_P ) for ( fp = Flg.flg_Plist ; fp->f_pid >= 0 ; fp++ ) if ( fp->f_pid == p->pr_p.p_pid ) return ( 1 ) ; if ( Flg.flg_U ) for ( pp = p ; pp > &process[1] ; pp = pp->pr_pptr ) for ( fp = Flg.flg_Ulist ; fp->f_uid >= 0 ; fp++ ) if ( fp->f_uid == pp->pr_p.p_uid ) return ( 1 ) ; switch ( p->pr_p.p_stat ) { case SRUN : if ( Flg.flg_B ) return ( 1 ) ; break ; case SSLEEP : if ( Flg.flg_B && p->pr_p.p_pri < PZERO && p->pr_p.p_pid > MSPID ) return ( 1 ) ; case SWAIT : case SIDL : if ( Flg.flg_W ) return ( 1 ) ; break ; case SSTOP : if ( Flg.flg_S ) return ( 1 ) ; break ; case SZOMB : if ( Flg.flg_Z ) return ( 1 ) ; break ; default : break ; } # ifdef VMUNIX if ( Flg.flg_D && p->pr_p.p_pgrp && (p->pr_p.p_flag & SDETACH) ) return ( 1 ) ; # endif return ( 0 ) ; } ---END-OF-select.c--- echo Extracting select_tty.c cat > select_tty.c << '---END-OF-select_tty.c---' # include "sps.h" /* SELECT_TTY - Decides whether this process is interesting for its tty */ select_tty ( p ) register struct process *p ; { register union flaglist *fp ; extern struct flags Flg ; for ( fp = Flg.flg_Tlist ; fp->f_ttyline ; fp++ ) if ( fp->f_ttyline == p->pr_tty ) return ( 1 ) ; return ( 0 ) ; } ---END-OF-select_tty.c--- echo Extracting sps.h cat > sps.h << '---END-OF-sps.h---' # ifdef VMUNIX # include <h/param.h> # include <h/dir.h> # include <h/user.h> # include <h/proc.h> # include <h/pte.h> # include <h/vm.h> # include <h/inode.h> # include <h/file.h> # include <h/buf.h> # include <h/text.h> # include <h/tty.h> # include <h/conf.h> # else # include <param.h> # include <dir.h> # include <user.h> # include <proc.h> # include <inode.h> # include <file.h> # include <buf.h> # include <text.h> # include <tty.h> # include <conf.h> # endif /* Max # users to be considered ... */ # define MAXUSERS 300 /* Max # ttys to be considered ... */ # define MAXTTYS 50 /* Max user name length ... */ # define UNAMELEN 8 /* Max process-id not to be considered busy ... */ # define MSPID 2 # ifdef VMUNIX /* # process' virtual pages to be read to determine argument list ... */ # define MAXARGPG 5 /* Length of each process' page table to be obtained ... */ # define PAGETABLESIZE (UPAGES + MAXARGPG) # endif /* Convert clicks to kbytes ... */ # ifdef VMUNIX # define KSHIFT 10 # define MSIZE( s ) ( (s) >> (KSHIFT - PGSHIFT) ) # else # ifdef M68000 # define MSIZE( s ) ( (s) << 1 ) # else # define MSIZE( s ) ( (s) >> 4 ) # endif # endif /* Standard files to be examined ... */ # define FILE_MEM "/dev/mem" /* System physical memory */ # define FILE_KMEM "/dev/kmem" /* Kernel virtual memory */ # define FILE_SWAP "/dev/swap" /* Swap/paging device */ # ifdef VMUNIX # define FILE_SYMBOL "/vmunix" /* Symbol file for nlist() */ # else # define FILE_SYMBOL "/unix" # endif # define FILE_DEV "/dev" /* Directory of tty entries */ # ifdef TRACE # define FILE_INFO "./spsinfo" /* Information file */ # else # define FILE_INFO "/etc/spsinfo" # endif /* Structure to hold necessary information concerning a tty ... */ struct ttyline { struct tty *l_addr ; /* Ptr to tty struct in kmem */ unsigned short l_pgrp ; /* Tty process group */ char l_name[2] ; /* Tty character name */ dev_t l_dev ; /* Tty device # */ } ; /* Format of the standard information file maintained by sps ... */ /* ** This structure is filled in at initialisation time. Otherwise it ** is read in whenever sps is invoked. Keeping known kernel addresses ** in a file is much faster than accessing them every time the program ** is invoked. */ /* ** Note that the pointer variables in this structure refer to ** kernel virtual addresses, not addresses within sps. ** These variable are typed as such so that pointer arithmetic ** on the kernel addresses will work correctly. */ struct info { /* Kernel values determining process, tty and upage info ... */ # ifdef VMUNIX int i_nproc ; /* length of process table */ int i_ntext ; /* length of text table */ int i_ninode ; /* length of inode table */ int i_nswbuf ; /* # swap buffers */ int i_nbuf ; /* # i/o buffers */ int i_hz ; /* clock rate on this machine */ int i_ecmx ; /* max physical memory address*/ struct tty *i_pt_tty ; /* pseudo-tty device base */ struct pte *i_usrptmap ; /* page table map */ struct pte *i_usrpt ; /* page table map */ struct buf *i_swbuf0 ; /* address of swap buffers */ # ifdef CHAOS caddr_t i_Chconntab ; /* Chaos connection table */ # endif # else int i_nswap ; int i_swplo ; # endif struct proc *i_proc0 ; /* address of process table */ struct text *i_text0 ; /* address of text table */ struct inode *i_inode0 ; /* address of inode table */ struct buf *i_buf0 ; /* address of i/o buffers */ struct cdevsw *i_cdevsw ; /* device switch to find ttys */ /* These remaining kernel addresses are associated with process wait states ... */ # ifdef VMUNIX caddr_t i_fltab ; caddr_t i_rswbuf ; # ifdef CHAOS caddr_t i_chrfclist ; # endif # else caddr_t i_swap1 ; caddr_t i_swap2 ; # endif caddr_t i_va_softc ; caddr_t i_bfreeli ; caddr_t i_lpdt ; caddr_t i_lbolt ; caddr_t i_runin ; caddr_t i_runout ; caddr_t i_ipc ; caddr_t i_u ; caddr_t i_chtbuf ; caddr_t i_rhtbuf ; /* User names, indexed by uid ... */ char i_unames[ MAXUSERS ][ UNAMELEN ] ; /* Tty device info ... */ struct ttyline i_ttyline[ MAXTTYS ] ; } ; # ifdef VMUNIX /* The `user' structure obtained from /dev/mem or /dev/swap ... */ union userstate { struct user u_us ; char u_pg[ UPAGES ][ NBPG ] ; } ; # endif /* Information concerning each process filled from /dev/kmem ... */ struct process { struct proc pr_p ; /* struct proc from /dev/kmem */ struct process *pr_plink ; /* Normalised ptrs from above */ struct process *pr_sibling ; /* Ptr to sibling process */ struct process *pr_child ; /* Ptr to child process */ struct process *pr_pptr ; # ifdef VMUNIX struct vtimes pr_vself ; /* Read from upage for self */ struct vtimes pr_vchild ; /* ... and the children */ int pr_files ; /* # open files */ # else time_t pr_utime ; time_t pr_stime ; time_t pr_cutime ; time_t pr_cstime ; dev_t pr_ttyd ; # endif struct ttyline *pr_tty ; /* Associated tty information */ char *pr_cmd ; /* Command args, from upage */ int pr_upag:1 ; /* Upage was obtained */ int pr_csaved:1 ; /* Cmd args saved by malloc() */ # ifndef VMUNIX # ifndef V7 int pr_detach:1 ; /* Simulate the SDETACH bit */ # endif # endif } ; /* To hold information specified in the option list ... */ union flaglist { char *f_chp ; /* Option specified as string */ int f_uid ; /* Numerical user id */ int f_pid ; /* Numerical process id */ struct ttyline *f_ttyline ; /* Specified tty */ } ; /* To hold global information specifed by options in the arg list ... */ struct flags { # ifdef VMUNIX int flg_d:1 ; /* disc orientated output */ # endif int flg_e:1 ; /* print environment string */ int flg_f:1 ; /* print process father # */ int flg_g:1 ; /* print process group # */ int flg_i:1 ; /* initialise sps */ int flg_q:1 ; /* show user time only */ int flg_r:1 ; /* repeat output */ int flg_v:1 ; /* print verbose listing */ int flg_w:1 ; /* print wide output */ int flg_y:1 ; /* print tty information */ int flg_A:1 ; /* print all processes */ int flg_B:1 ; /* print busy processes */ # ifdef VMUNIX int flg_D:1 ; /* print detached processes */ # endif int flg_F:1 ; /* print foreground processes */ int flg_N:1 ; /* print no processes */ int flg_P:1 ; /* print specified process #'s*/ int flg_S:1 ; /* print stopped processes */ int flg_T:1 ; /* print procs for given ttys */ int flg_U:1 ; /* print procs for given users*/ int flg_W:1 ; /* print waiting processes */ int flg_Z:1 ; /* print zombie processes */ int flg_AZ:1 ; /* One of A to Z was specified*/ union flaglist *flg_Plist ; /* List of specified processes*/ union flaglist *flg_Tlist ; /* List of specified ttys */ union flaglist *flg_Ulist ; /* List of specified users */ } ; /* Structure to hold summarising information ... */ struct summary { long sm_ntotal ; /* Total # processes */ long sm_ktotal ; /* Total virtual memory */ long sm_nbusy ; /* # busy processes */ long sm_kbusy ; /* Busy virtual memory */ long sm_nloaded ; /* # loaded processes */ long sm_kloaded ; /* Active resident memory */ long sm_nswapped ; /* # swapped processes */ # ifdef VMUNIX long sm_kswapped ; /* Size totally swapped out */ # endif } ; /* ** The symbol structure cross-references values read from the kernel with ** their place in the info structure, and if such a value is associated with ** a process wait state or not. */ struct symbol { char *s_kname ; /* Kernel symbol name */ char s_indirect ; /* Value requires indirection */ caddr_t *s_info ; /* Corresponding info address */ char *s_wait ; /* Reason for wait, if any */ } ; ---END-OF-sps.h--- echo Extracting ttyinit.c cat > ttyinit.c << '---END-OF-ttyinit.c---' # include "sps.h" # include <stdio.h> # include <sys/stat.h> /* TTYINIT - Initialise the tty part of the info structure */ ttyinit () { register struct ttyline *lp ; FILE *fd ; struct direct dirbuf ; struct stat statbuf ; static char file_dev[] = FILE_DEV ; extern struct info Info ; extern int Flkmem ; FILE *fdopen() ; lp = Info.i_ttyline ; fd = fdopen( openfile( file_dev ), "r" ) ; if ( chdir( file_dev ) < 0 ) prexit( "sps - Can't chdir to %s\n", file_dev ) ; /* Read all entries in the device directory, looking for ttys */ while ( fread( (char*)&dirbuf, sizeof( struct direct ), 1, fd ) == 1 ) { if ( !dirbuf.d_ino ) continue ; /* Skip entries that do not match "tty" or "console" */ if ( strncmp( "tty", dirbuf.d_name, 3 ) && strcmp( "console", dirbuf.d_name ) ) continue ; /* Skip "tty" itself */ if ( dirbuf.d_name[3] == '\0' ) continue ; # ifdef CHAOS /* Skip chaos ttys ; they are accessed during ttystatus() */ if ( dirbuf.d_name[ sizeof( "tty" ) - 1 ] == 'C' ) continue ; # endif if ( lp >= &Info.i_ttyline[ MAXTTYS ] ) prexit( "sps - Too many ttys in %s\n", file_dev ) ; /* Copy the tty name into the information entry */ if ( !strcmp( dirbuf.d_name, "console" ) ) lp->l_name[0] = 'c', lp->l_name[1] = 'o' ; else { lp->l_name[0] = dirbuf.d_name[3] ; lp->l_name[1] = dirbuf.d_name[4] ; } /* Ensure that this tty is actually a valid character device */ if ( stat( dirbuf.d_name, &statbuf ) < 0 ) continue ; if ( (statbuf.st_mode & S_IFMT) != S_IFCHR ) continue ; /* Find the device # of the tty and the address of its associated struct tty in /dev/kmem. */ lp->l_dev = statbuf.st_rdev ; (void)lseek( Flkmem, (long)&Info.i_cdevsw[ major( statbuf.st_rdev ) ].d_ttys, 0 ) ; (void)read( Flkmem, (char*)&lp->l_addr, sizeof( lp->l_addr ) ) ; lp->l_addr += (int)minor( statbuf.st_rdev ) ; lp++ ; } (void)fclose( fd ) ; } ---END-OF-ttyinit.c--- echo Extracting ttystatus.c cat > ttystatus.c << '---END-OF-ttystatus.c---' # include "sps.h" # ifdef CHAOS # include <chunix/chsys.h> # include <chaos/chaos.h> # endif /* ** TTYSTATUS - Reads the kernel memory for tty structures of active processes. ** The addresses of the associated struct ttys of /dev/kmem are kept in the ** info structure. Here we use those addresses to access the structures. ** Actually, we are mostly interested just in the process group of each tty. */ ttystatus () { register struct ttyline *lp ; struct tty tty ; extern struct flags Flg ; extern struct info Info ; extern int Flkmem ; long lseek() ; if ( Flg.flg_y ) # if VMUNIX | M68000 printf( "Ty Dev Addr Rawq Canq Outq Pgrp\n" ) ; # else printf( "Ty Dev Addr Rawq Canq Outq Pgrp\n" ) ; # endif lp = Info.i_ttyline ; # ifdef CHAOS while ( lp->l_name[0] && lp->l_name[0] != 'C' ) # else while ( lp->l_name[0] ) # endif { (void)lseek( Flkmem, (long)lp->l_addr, 0 ) ; (void)read( Flkmem, (char*)&tty, sizeof( struct tty ) ) ; lp->l_pgrp = tty.t_pgrp ; prtty( lp, &tty ) ; lp++ ; } # ifdef CHAOS chaosttys( lp ) ; # endif } /* PRTTY - Print out the tty structure */ prtty ( lp, tty ) register struct ttyline *lp ; register struct tty *tty ; { extern struct flags Flg ; if ( !Flg.flg_y ) return ; # if VMUNIX | M68000 printf( "%-2.2s %2d,%2d 0x%08x %4d %4d %4d %5d\n", # else printf( "%-2.2s %2d,%2d 0o%06o %4d %4d %4d %5d\n", # endif lp->l_name, major(lp->l_dev), minor(lp->l_dev), lp->l_addr, tty->t_rawq.c_cc, tty->t_canq.c_cc, tty->t_outq.c_cc, tty->t_pgrp ) ; } # ifdef CHAOS /* CHAOSTTYS - Finds ttys attached to the Chaos net */ chaosttys ( lp ) register struct ttyline *lp ; { register struct connection **cnp ; register int i ; struct tty tty ; struct connection *conntab[CHNCONNS] ; struct connection conn ; extern struct info Info ; extern int Flkmem ; (void)lseek( Flkmem, (long)Info.i_Chconntab, 0 ) ; (void)read( Flkmem, (char*)conntab, sizeof( conntab ) ) ; for ( i = 0, cnp = conntab ; cnp < &conntab[CHNCONNS] ; i++, cnp++ ) { if ( !*cnp ) continue ; (void)lseek( Flkmem, (long)*cnp, 0 ) ; (void)read( Flkmem, (char*)&conn, sizeof( struct connection ) ); if ( !(conn.cn_flags & CHTTY) ) continue ; (void)lseek( Flkmem, (long)conn.cn_ttyp, 0 ) ; (void)read( Flkmem, (char*)&tty, sizeof( struct tty ) ) ; if ( lp >= &Info.i_ttyline[MAXTTYS] ) prexit( "sps - Too many chaos ttys\n" ) ; lp->l_addr = conn.cn_ttyp ; lp->l_pgrp = tty.t_pgrp ; lp->l_dev = tty.t_dev ; lp->l_name[0] = 'C' ; lp->l_name[1] = i < 10 ? '0'+i : i-10 <= 'z'-'a' ? i-10+'a' : i-10-('z'-'a')+'A' ; prtty( lp, &tty ) ; lp++ ; } } # endif ---END-OF-ttystatus.c--- echo Extracting waitingfor.c cat > waitingfor.c << '---END-OF-waitingfor.c---' # include "sps.h" /* WAITINGFOR - Determine what a process is waiting for and describe it. */ # define INRANGE( w, a1, a2 ) \ ( (caddr_t)(a1) <= (w) && (w) < (caddr_t)(a2) ) # ifdef VMUNIX /* Max # active pty devices (this # is unobtainable from the system) ... */ # define NPTY 16 # endif char *waitingfor ( p ) struct process *p ; { register caddr_t w ; register struct ttyline *lp ; register struct symbol *s ; register char *cp ; static char wbuf[ 7 ] ; extern struct info Info ; extern struct symbol Symbollist[] ; char *sprintf() ; w = p->pr_p.p_wchan ; if ( !w ) return ( "null" ) ; # ifdef VMUNIX if ( INRANGE( w, Info.i_proc0, &Info.i_proc0[ Info.i_nproc ] ) ) return ( p->pr_p.p_flag & SNOVM ? "vfork" : "child" ) ; if ( INRANGE( w, Info.i_swbuf0, &Info.i_swbuf0[ Info.i_nswbuf ] ) ) return ( "swap" ) ; if ( INRANGE( w, Info.i_buf0, &Info.i_buf0[ Info.i_nbuf ] ) ) return ( "discio" ) ; if ( INRANGE( w, Info.i_text0, &Info.i_text0[ Info.i_ntext ] ) ) return ( "swtext" ) ; # else if ( INRANGE( w, Info.i_proc0, &Info.i_proc0[ NPROC ] ) ) return ( "child" ) ; if ( INRANGE( w, Info.i_buf0, &Info.i_buf0[ NBUF ] ) ) return ( "discio" ) ; # endif /* Is the process waiting for a tty ? */ for ( lp = Info.i_ttyline ; lp->l_name[0] ; lp++ ) if ( INRANGE( w, &lp->l_addr[0], &lp->l_addr[1] ) ) { w -= (int)lp->l_addr ; cp = w == (caddr_t)&((struct tty*)0)->t_rawq ? "rtty??" : w == (caddr_t)&((struct tty*)0)->t_outq ? "wtty??" : w == (caddr_t)&((struct tty*)0)->t_state ? "otty??" : "?tty??" ; cp[4] = lp->l_name[0] ; cp[5] = lp->l_name[1] ; return ( cp ) ; } /* Is the process waiting for an inode ? */ # ifdef VMUNIX if ( INRANGE( w, Info.i_inode0, &Info.i_inode0[Info.i_ninode] ) ) # else if ( INRANGE( w, Info.i_inode0, &Info.i_inode0[NINODE] ) ) # endif switch ( ((int)w - (int)Info.i_inode0) % sizeof( struct inode )) { case 1 : return ( "wpipe" ) ; case 2 : return ( "rpipe" ) ; # ifdef VMUNIX case (int)&((struct inode*)0)->i_un.i_group.g_datq : return ( "rmux" ) ; # endif default : return ( "inode" ) ; } # ifdef VMUNIX # if NPTY > 0 /* Is the process waiting for a pseudo tty ? */ if ( INRANGE( w, Info.i_pt_tty, &Info.i_pt_tty[NPTY] ) ) switch ( ((int)w - (int)Info.i_pt_tty) % sizeof( struct tty ) ) { case (int)&((struct tty*)0)->t_rawq : return ( "rspty" ) ; case (int)&((struct tty*)0)->t_rawq.c_cf : return ( "wcpty" ) ; case (int)&((struct tty*)0)->t_outq : return ( "wspty" ) ; case (int)&((struct tty*)0)->t_outq.c_cf : return ( "rcpty" ) ; default : return ( "??pty" ) ; } # endif # endif /* Look in the symbol table for known wait addresses. */ for ( s = Symbollist ; s->s_kname ; s++ ) if ( s->s_wait && w == *s->s_info ) return ( s->s_wait ) ; /* No reason for the wait state has been found. Return the wait channel as a hexadecimal address. */ # ifdef VMUNIX (void)sprintf( wbuf, "x%05x", w - 0x80000000 ) ; # else (void)sprintf( wbuf, "x%05x", w ) ; # endif return ( wbuf ) ; } ---END-OF-waitingfor.c---