[net.sources] SPS on Sun Release 2.0

robert@hslrswi.UUCP (Robert Ward) (08/14/85)

The following files are in addition to the ones I posted last month.
These files constitute the changes necessary to the code of SPS to
make it work on a Sun Release 2.0. (At least it now works on our
Release 2.0 Sun-2/120).

Replace the files of the original posting with the ones below and
then recompile the entire code.

Send me mail if you would like SPS to be reposted or remailed in
its entirety.

*******************************************************************************

Send comments, bug reports and fixes to:

Robert Ward, Hasler AG, Murtenstrasse 137a, CH-3008 Bern, Switzerland.
Tel.:	(031) - 65 23 19
Uucp:	... mcvax!cernvax!hslrswi!robert

*******************************************************************************

This is a sh(1) archive.
In order to extract the contents of this shar, use an editor to place
the contents of this file from the dotted line onwards into a second,
temporary file. Then run sh on that second file.

--------------------------------- C U T --- H E R E ---------------------------
#! /bin/sh
echo Extracting Makefile.sun
cat > Makefile.sun << '---END-OF-Makefile.sun---'
# Makefile for SPS (Sun 4.2BSD UNIX Version)

PROG    =       sps
OBJS    =       filecount.o findtty.o flagdecode.o flagsetup.o \
		getcmd.o getupage.o globals1.o globals2.o hashuid.o \
		initialise.o initsymbols.o inittty.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 \
		selectproc.o selecttty.o termwidth.o ttystatus.o waitingfor.o
INCS    =       sps.h
CC      =       cc
CFLAGS  =       -DSUN -DBSD42 -I/sys
LIBS    =       -ltermlib

all:		    $(PROG)
.c.o:
		$(CC) $(CFLAGS) -c -O -R $<
	
globals1.o waitingfor.o:
		$(CC) $(CFLAGS) -c -O $<

$(OBJS):		$(INCS)

$(PROG):		$(OBJS)
		$(CC) -o $@ $(OBJS) $(LIBS)

install:		$(PROG)
		strip $(PROG)
		mv $(PROG) /bin/$(PROG)
		/etc/chown root /bin/$(PROG)

lint:
		lint -x -b $(CFLAGS) *.c
clean:
		rm -f $(OBJS) $(PROG)
---END-OF-Makefile.sun---
echo Extracting getcmd.c
cat > getcmd.c << '---END-OF-getcmd.c---'
# include       "sps.h"
# include       "flags.h"
# include       <h/vm.h>
# ifdef BSD42
# ifdef SUN
# include       <sun/pte.h>
# else
# include       <vax/pte.h>
# endif
# else
# include       <h/pte.h>
# endif

/*
** 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 ;
	unsigned                nbad ;
	struct dblock           db ;
	struct pte              ptetbl[ UPAGES + CLSIZE ] ;
	union
	{
		char            a_argc[ CLSIZE * NBPG ] ;
		int             a_argi[ CLSIZE * NBPG / sizeof( int ) ] ;
	} argbuf ;
	extern struct flags     Flg ;
	extern union userstate  User ;
	extern int              Flmem, Flswap ;
	char                    *strcat(), *strncpy(), *strsave() ;

	p->pr_csaved = 0 ;
	p->pr_upag = 0 ;
	if ( p->pr_p.p_stat == SZOMB )
		return ( "** Exit **" ) ;
	if ( !(p->pr_p.p_flag & SLOAD) && Flg.flg_o )
		return ( "** Swapped out **" ) ;
	/* Find the process' upage */
	if ( !getupage( p, ptetbl ) )           
		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.
	   The page is loaded if the process itself is loaded and the pte
	   contains is marked as valid. */
	if ( (p->pr_p.p_flag & SLOAD)
	&& !ptetbl[0].pg_fod && ptetbl[0].pg_pfnum )
	{       /* If the page is loaded, read the arguments from
		   physical memory. */
		memseek( Flmem, (long)ctob( ptetbl[0].pg_pfnum ) ) ;
		if ( read( Flmem, argbuf.a_argc, CLSIZE*NBPG ) != CLSIZE*NBPG )
			return ( "** Memory read error **" ) ;
	}
	else                            
	{       /* Otherwise the page is on the swap device */
		vstodb( 0, ctod( CLSIZE ), &User.u_us.u_smap, &db, 1 ) ;
# ifdef BSD42
		swseek( (long)dtob( db.db_base ) ) ;
# else
		swseek( (long)ctob( db.db_base ) ) ;
# endif
		if ( Flg.flg_o )
			return ( "** Swapped page **" ) ;
		if ( read( Flswap, argbuf.a_argc, CLSIZE*NBPG ) != CLSIZE*NBPG )
			return ( "** Swap device read error **" ) ;
	}
	/* Look down until the end of command arguments is found. */
	p->pr_upag = 1 ;
	p->pr_csaved = 1 ;
	ip = &argbuf.a_argi[ CLSIZE*NBPG / sizeof( int ) ] ;
	ip -= 2 ;
	while ( *--ip )
		if ( ip == &argbuf.a_argi[0] )
			goto getsysargs ;
	/* Process the command arguments, looking for nulls and unprintable
	   characters. */
	cp0 = (char*)(ip + 1) ;
	if ( !*cp0 )                    
		cp0++ ;                 
	if ( *cp0 )
	{
		nbad = 0 ;                      
		for ( cp = cp0 ; cp < &argbuf.a_argc[ CLSIZE*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 ) ) ;
	}
getsysargs :
	/* If the command arguments cannot be accessed from the user's memory
	   space, get the command name from the system's idea of what the
	   name should be. */
	argbuf.a_argc[0] = '(' ;
	(void)strncpy( &argbuf.a_argc[1], User.u_us.u_comm,
		sizeof( User.u_us.u_comm ) ) ;
	(void)strcat( &argbuf.a_argc[0], ")" ) ;
	return ( strsave( argbuf.a_argc ) ) ;
}

/*
** VSTODB - Given a base/size pair in virtual swap area,
** return a physical base/size pair which is the
** (largest) initial, physically contiguous block.
/* This code is stolen from the kernel file /sys/sys/vm_drum.c.
*/
vstodb ( vsbase, vssize, dmp, dbp, rev )

register int                    vsbase ;
register int                    vssize;
struct dmap                     *dmp ;
register struct dblock          *dbp ;
int                             rev ;

{
	register int            blk ;
	register swblk_t        *ip ;
# ifdef BSD42
	extern struct info      Info ;
# endif

# ifdef BSD42
	blk = Info.i_dmmin ;
# else
	blk = DMMIN ;
# endif
	ip = dmp->dm_map ;
	while ( vsbase >= blk )
	{
		vsbase -= blk ;
# ifdef BSD42
		if ( blk < Info.i_dmmax )
# else
		if ( blk < DMMAX )
# endif
			blk *= 2 ;
		ip++ ;
	}
	dbp->db_size = vssize < blk - vsbase ? vssize : blk - vsbase ;
	dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
}
---END-OF-getcmd.c---
echo Extracting getupage.c
cat > getupage.c << '---END-OF-getupage.c---'
# include       "sps.h"
# include       <h/vm.h>
# ifdef BSD42
# ifdef SUN
# include       <sun/pte.h>
# else
# include       <vax/pte.h>
# endif
# else
# include       <h/pte.h>
# endif
# include       <stdio.h>

/*
** GETUPAGE - Reads the upage for the specified process as well as sufficient
** page tables entries for reading the command arguments. The pte's are read
** into the argument `ptetbl'. The upage is read into the external variable
** `User'. This procedure returns 1 if the upage was successfully read.
*/

# define        usrpt           (Info.i_usrpt)

getupage ( p, ptetbl )

register struct process         *p ;
register struct pte             *ptetbl ;

{
	register int            i ;
	register int            ncl ;
	struct pte              pte ;
	extern struct info      Info ;
	extern union userstate  User ;
	extern int              Flmem, Flkmem, Flswap ;

	/* If the process is not loaded, look for the upage on the swap device*/
	if ( !(p->pr_p.p_flag & SLOAD) )
	{                               
# ifdef BSD42
		swseek( (long)dtob( p->pr_p.p_swaddr ) ) ;
# else
		swseek( (long)ctob( p->pr_p.p_swaddr ) ) ;
# endif
# ifdef SUN
		if ( read( Flswap, (char*)&User.u_us, sizeof( union userstate ))
		!= sizeof( union userstate ) )
# else
		if ( read( Flswap, (char*)&User.u_us, sizeof( struct user ) )
		!= sizeof( struct user ) )
# endif
		{
			fprintf( stderr,
				"sps - Can't read upage of process %d\n",
			    p->pr_p.p_pid ) ;
			return ( 0 ) ;
		}
		return ( 1 ) ;          
	}                               
	/* The process is loaded. Locate the process pte's by reading
	   the pte of their base address from system virtual address space. */
	memseek( Flkmem, (long)&Info.i_usrptmap[ btokmx(p->pr_p.p_p0br)
		+ p->pr_p.p_szpt-1 ] ) ;
	if ( read( Flkmem, (char*)&pte, sizeof( struct pte ) )
	!= sizeof( struct pte ) )
	{
		fprintf( stderr,
		      "sps - Can't read indir pte for upage of process %d\n",
		    p->pr_p.p_pid ) ;
		return ( 0 ) ;
	}                               
	/* Now read the process' pte's from physical memory. We need to access
	   sufficient pte's for the upage and for the command arguments. */
	memseek( Flmem, (long)ctob( pte.pg_pfnum+1 )
		- (UPAGES+CLSIZE)*sizeof( struct pte ) ) ;
	if ( read( Flmem, (char*)ptetbl, (UPAGES+CLSIZE)*sizeof( struct pte ) )
	!= (UPAGES+CLSIZE)*sizeof( struct pte ) )
	{
		fprintf( stderr, "sps - Can't read page table of process %d\n",
			p->pr_p.p_pid ) ;
		return ( 0 ) ;
	}
	/* Now we can read the pages belonging to the upage.
	   Here we read in an entire click at one go. */
	ncl = (sizeof( struct user ) + NBPG*CLSIZE - 1) / (NBPG*CLSIZE) ;
	while ( --ncl >= 0 )            
	{                               
		i = ncl * CLSIZE ;
		memseek( Flmem, (long)ctob( ptetbl[ CLSIZE+i ].pg_pfnum ) ) ;
		if ( read( Flmem, User.u_pg[i], CLSIZE*NBPG ) != CLSIZE*NBPG )
		{
			fprintf( stderr,
				"sps - Can't read page 0x%x of process %d\n",
				ptetbl[ CLSIZE+i ].pg_pfnum, p->pr_p.p_pid ) ;
			return ( 0 ) ;
		}
	}
	return ( 1 ) ;
}
---END-OF-getupage.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 within the kernel ;
**      ii.  whether an extra indirection is needed through the kernel,
**           i.e. whether the value of that symbol should be obtained
**           rather than its address.
**      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. All these addresses should be
	   located in the symbol file during initialisation. */
	{ "_proc",      1,  (caddr_t*)&Info.i_proc0,    (char*)0        },
	{ "_nproc",     1,  (caddr_t*)&Info.i_nproc,    (char*)0        },
	{ "_text",      1,  (caddr_t*)&Info.i_text0,    (char*)0        },
	{ "_ntext",     1,  (caddr_t*)&Info.i_ntext,    (char*)0        },
	{ "_inode",     1,  (caddr_t*)&Info.i_inode0,   (char*)0        },
	{ "_ninode",    1,  (caddr_t*)&Info.i_ninode,   (char*)0        },
	{ "_swbuf",     1,  (caddr_t*)&Info.i_swbuf0,   (char*)0        },
	{ "_nswbuf",    1,  (caddr_t*)&Info.i_nswbuf,   (char*)0        },
	{ "_buf",       1,  (caddr_t*)&Info.i_buf0,     (char*)0        },
	{ "_nbuf",      1,  (caddr_t*)&Info.i_nbuf,     (char*)0        },
	{ "_ecmx",      1,  (caddr_t*)&Info.i_ecmx,     (char*)0        },
	{ "_Usrptmap",  0,  (caddr_t*)&Info.i_usrptmap, (char*)0        },
	{ "_usrpt",     0,  (caddr_t*)&Info.i_usrpt,    (char*)0        },
	{ "_cdevsw",    0,  (caddr_t*)&Info.i_cdevsw,   (char*)0        },
# ifdef BSD42
	{ "_quota",     1,  (caddr_t*)&Info.i_quota0,   (char*)0        },
	{ "_nquota",    1,  (caddr_t*)&Info.i_nquota,   (char*)0        },
	{ "_dmmin",     1,  (caddr_t*)&Info.i_dmmin,    (char*)0        },
	{ "_dmmax",     1,  (caddr_t*)&Info.i_dmmax,    (char*)0        },
	{ "_mbutl",     0,  (caddr_t*)&Info.i_mbutl,    (char*)0        },
# else
	{ "_hz",        1,  (caddr_t*)&Info.i_hz,       (char*)0        },
# endif
# ifdef CHAOS
	{ "_Chconntab", 0,  &Info.i_Chconntab,          (char*)0        },
# endif
	/* Kernel addresses associated with process wait states.
	   It is not important if some of these addresses are unresolved
	   at initialisation. */
	{ "_fltab",     0,  &Info.i_waitstate[0],       "floppy"        },
	{ "_tu",        0,  &Info.i_waitstate[1],       "tu58"          },
	{ "_bfreelist", 0,  &Info.i_waitstate[2],       "buffer"        },
	{ "_lp_softc",  0,  &Info.i_waitstate[3],       "printr"        },
	{ "_lbolt",     0,  &Info.i_waitstate[4],       "lbolt"         },
	{ "_runin",     0,  &Info.i_waitstate[5],       "runin"         },
	{ "_runout",    0,  &Info.i_waitstate[6],       "runout"        },
	{ "_ipc",       0,  &Info.i_waitstate[7],       "ptrace"        },
	{ "_u",         0,  &Info.i_waitstate[8],       "pause"         },
	{ "_freemem",   0,  &Info.i_waitstate[9],       "freemm"        },
	{ "_kernelmap", 0,  &Info.i_waitstate[10],      "kermap"        },
	{ "_cwaiting",  0,  &Info.i_waitstate[11],      "cwait"         },
	{ "_rhpbuf",    0,  &Info.i_waitstate[12],      "rhpbuf"        },
	{ "_rhtbuf",    0,  &Info.i_waitstate[13],      "rhtbuf"        },
	{ "_ridcbuf",   0,  &Info.i_waitstate[14],      "ridcbf"        },
	{ "_rikbuf",    0,  &Info.i_waitstate[15],      "rikbuf"        },
	{ "_rmtbuf",    0,  &Info.i_waitstate[16],      "rmtbuf"        },
	{ "_rrkbuf",    0,  &Info.i_waitstate[17],      "rrkbuf"        },
	{ "_rrlbuf",    0,  &Info.i_waitstate[18],      "rrlbuf"        },
	{ "_rrxbuf",    0,  &Info.i_waitstate[19],      "rrxbuf"        },
	{ "_rswbuf",    0,  &Info.i_waitstate[20],      "rswbuf"        },
	{ "_rtmbuf",    0,  &Info.i_waitstate[21],      "rtmbuf"        },
	{ "_rtsbuf",    0,  &Info.i_waitstate[22],      "rtsbuf"        },
	{ "_rudbuf",    0,  &Info.i_waitstate[23],      "rudbuf"        },
	{ "_rupbuf",    0,  &Info.i_waitstate[24],      "rupbuf"        },
	{ "_rutbuf",    0,  &Info.i_waitstate[25],      "rutbuf"        },
	{ "_rvabuf",    0,  &Info.i_waitstate[26],      "rvabuf"        },
	{ "_rvpbuf",    0,  &Info.i_waitstate[27],      "rvpbuf"        },
	{ "_chtbuf",    0,  &Info.i_waitstate[28],      "chtbuf"        },
	{ "_cmtbuf",    0,  &Info.i_waitstate[29],      "cmtbuf"        },
	{ "_ctmbuf",    0,  &Info.i_waitstate[30],      "ctmbuf"        },
	{ "_ctsbuf",    0,  &Info.i_waitstate[31],      "ctsbuf"        },
	{ "_cutbuf",    0,  &Info.i_waitstate[32],      "cutbuf"        },
# ifdef BSD42
	{ "_selwait",   0,  &Info.i_waitstate[33],      "select"        },
# endif
# ifdef CHAOS
	{ "_Chrfclist", 0,  &Info.i_waitstate[34],      "chrfc"         },
# endif
# ifdef SUN
	{ "_async_bufhead", 0,  &Info.i_waitstate[35],  "async"		},
# endif
	{ (char*)0,     0,  (caddr_t*)0,                (char*)0        }
} ;
---END-OF-globals2.c---
echo Extracting percentmem.c
cat > percentmem.c << '---END-OF-percentmem.c---'
# include       "sps.h"
# include       <h/text.h>
# ifdef BSD42
# ifdef SUN
# include       <sun/pte.h>
# else
# include       <vax/pte.h>
# endif
# else
# include       <h/pte.h>
# include       <h/vmparam.h>
# endif
# include       <h/vmmac.h>

/* PERCENTMEM - Returns the percentage of real memory used by this process */
double  percentmem ( p )

register struct process         *p ;

{
	register struct text    *tp ;
	int                     szptudot ;
	double                  fracmem ;
	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 ) ;
}
---END-OF-percentmem.c---
echo Extracting prcpu.c
cat > prcpu.c << '---END-OF-prcpu.c---'
# include       "sps.h"

# ifdef BSD42

/* PRCPU - Print cpu time */
prcpu ( time, utime )

register time_t                 time ;
time_t                          utime ;

{
	time += utime / 1000000 ;
	utime %= 1000000 ;
	if ( time < 0L )
	{       /* Ignore negative times */
		printf( "     " ) ;     
		return ;
	}
	if ( time < 60L*10L )
	{       /* Print as seconds if less than 1000 seconds */
		printf( "%3d.%1d", (int)time, (int)utime/100000 ) ;
		return ;
	}
	/* Print as minutes if less than 10 hours ; print as hours if less than
	   10 days, else print as days. */
	if ( time < 60L*60L*10L )               
		printf( "%3D M", time/60L ) ;
	else if ( time < 24L*60L*60L*10L )
		printf( "%3D H", time/60L/60L ) ;
	else
		printf( "%3D D", time/60L/60L/24L ) ;
}

# else

/* PRCPU - Print cpu time */
prcpu ( time )

register time_t                 time ;

{
	extern struct info      Info ;

	if ( time < 0L )
	{       /* Ignore negative times */
		printf( "     " ) ;     
		return ;
	}
	if ( time < Info.i_hz*60L*10L )
	{       /* Less than 10 minutes */
		printf( "%3D.%1D", time/Info.i_hz,
			(time % Info.i_hz / (Info.i_hz/10L)) ) ;
		return ;
	}
	/* If less than 10 hours, print as minutes */
	time /= Info.i_hz ;
	/* Print as minutes if less than 10 hours ; print as hours if less than
	   10 days, else print as days. */
	if ( time < 60L*60L*10L )               
		printf( "%3D M", time/60L ) ;
	else if ( time < 24L*60L*60L*10L )
		printf( "%3D H", time/60L/60L ) ;
	else
		printf( "%3D D", time/60L/60L/24L ) ;
}

# endif
---END-OF-prcpu.c---
echo Extracting printproc.c
cat > printproc.c << '---END-OF-printproc.c---'
# include       "sps.h"
# include       "flags.h"
# include       <h/text.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 ;
	register struct hashtab *hp ;
	char                    chbuf[10] ;
	time_t                  time ;
	time_t                  chtime ;
# ifdef BSD42
	time_t                  utime ;
	time_t                  uchtime ;
# endif
	extern short            Lastuid, Lastpgrp ;
	extern struct flags     Flg ;
	char                    *waitingfor() ;
	struct hashtab          *hashuid() ;
	double                  percentmem() ;

	/* List tty name and foreground/background/detached information */
	printf( "%2.2s%c", p->pr_tty->l_name,
		!p->pr_p.p_pgrp ? ' ' :
# ifdef SDETACH
		p->pr_p.p_flag & SDETACH ? '_' :
# endif
		p->pr_p.p_pgrp == p->pr_tty->l_pgrp ? '.' : ' ' ) ;
	hp = hashuid( p->pr_p.p_uid ) ;
	if ( !md  )                             
	{       /* If a top-level process, list the user name */
		if ( hp )
			printf( "%-8.8s ", hp->h_uname ) ;
		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%c", md, "",
			p->pr_p.p_pgrp == Lastpgrp ? '|' : '*' ) ;      
		/* But beware of setuid processes */
		md = 8 - md ;
		if ( p->pr_p.p_uid == Lastuid )
			printf( "%-*.*s", md, md, "" ) ;
		else if ( hp )
			printf( "%-*.*s", md, md, hp->h_uname ) ;
		else
		{
			md -= 4 ;
			printf( "user%-*.*d", md, md, p->pr_p.p_uid ) ;
		}
	}
	Lastuid = p->pr_p.p_uid ;
	Lastpgrp = p->pr_p.p_pgrp ;
	if ( Flg.flg_d )                        
	{       /* List disc I/O and paging information */
		if ( !p->pr_upag || p->pr_p.p_stat == SZOMB )
		{
			prcmd( p, 49, -63 ) ;
			return ;
		}
		printf( "%2d %8d+%8d %4d %8d %8D ",
			p->pr_files,
# ifdef BSD42
			p->pr_rself.ru_majflt,
			p->pr_rself.ru_minflt,
			p->pr_rself.ru_nswap,
			p->pr_rself.ru_inblock + p->pr_rself.ru_oublock,
			KBYTES( p->pr_rself.ru_idrss + p->pr_rself.ru_isrss
				+ p->pr_rself.ru_ixrss ) ) ;
# else
			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,
			KBYTES( (p->pr_vself.vm_idsrss
				+ p->pr_vself.vm_ixrss) / Info.i_hz ) ) ;
# endif
		prcmd( p, 5, -63 ) ;
		return ;
	}
	if ( !Flg.flg_v )                       
	{       /* Not verbose so just list command arguments */
		prcmd( p, 5, -19 ) ;
		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 */
	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 & SUANOM ? 'A' : ' ' ) ;
	/* 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 )
	{
		prcmd( p, 41, -69 ) ;
		return ;
	}                                       
	/* List process and text virtual sizes */
	printf( "%4d", KBYTES( p->pr_p.p_dsize + p->pr_p.p_ssize ) ) ;
	if ( tp = p->pr_p.p_textp )
		printf( "+%3d ", KBYTES( tp->x_size ) ) ;
	else
		printf( "     " ) ;
	/* List process and text real sizes */
	printf( "%4d", KBYTES( p->pr_p.p_rssize ) ) ;
	if ( tp )
		printf( "+%3d %2.0f ", KBYTES( tp->x_rssize ),
			percentmem( p ) );
	else
		printf( "        " ) ;
	/* List information obtained from the upage. This includes the process
	   times and command arguments. */
	if ( !p->pr_upag )
	{
		prcmd( p, 20, -69 ) ;
		return ;
	}                                       
	/* List process time information */
# ifdef BSD42
	time   = Flg.flg_q ? p->pr_rself.ru_utime.tv_sec :
		 p->pr_rself.ru_utime.tv_sec + p->pr_rself.ru_stime.tv_sec ;
	utime  = Flg.flg_q ? p->pr_rself.ru_utime.tv_usec :
		 p->pr_rself.ru_utime.tv_usec + p->pr_rself.ru_stime.tv_usec ;
	chtime = Flg.flg_q ? p->pr_rchild.ru_utime.tv_sec :
		 p->pr_rchild.ru_utime.tv_sec + p->pr_rchild.ru_stime.tv_sec ;
	uchtime = Flg.flg_q ? p->pr_rchild.ru_utime.tv_usec :
		 p->pr_rchild.ru_utime.tv_usec + p->pr_rchild.ru_stime.tv_usec ;
	prcpu( time, utime ) ;
	if ( chtime != 0L )
	{
		printf( "+" ) ;
		prcpu( chtime, uchtime ) ;
	}
# else
	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 ;
	prcpu( time ) ;
	if ( chtime != 0L )
	{
		printf( "+" ) ;
		prcpu( chtime ) ;
	}
# endif
	else
		printf( "      " ) ;
# ifdef BSD42
	if ( time || utime )
# else
	if ( time )
# endif
# ifdef SUN
		printf( " %2.0f ", (double)p->pr_p.p_pctcpu ) ;
# else
		printf( " %2.0f ", p->pr_p.p_pctcpu * 100.0 ) ;
# endif
	else
		printf( "    " ) ;
	/* Finally, list the process command arguments. */
	prcmd( p, 5, -69 ) ;                    
}

/* 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++ ;
	}
}
---END-OF-printproc.c---
echo Extracting sps.h
cat > sps.h << '---END-OF-sps.h---'
# include       <h/param.h>
# include       <h/dir.h>
# include       <h/user.h>
# include       <h/proc.h>

/*
** Maximum # of users to be considered. (Should probably be
** approximately double the # of users in /etc/passwd.)
*/
# define        MAXUSERID       100
/* Maximum # ttys to be considered ... */
# define        MAXTTYS         50
/* Maximum user name length ... */
# define        UNAMELEN        8
/* Maximum process-id not to be considered busy ... */
# define        MSPID           2
/* # of wait states defined in the `struct info' ... */
# define        NWAITSTATE      36

/* Convert clicks to kbytes ... */
# ifdef SUN
# define        KBYTES( size )  ((size) << 1)
# else
# define        KBYTES( size )  ((size) >> (10 - PGSHIFT))
# 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/drum"     /* Swap/paging device */
# define        FILE_DEV        "/dev"          /* Directory of tty entries */
# define        FILE_SYMBOL     "/vmunix"       /* Symbol file for nlist() */
# define        FILE_INFO       "/etc/spsinfo"  /* Sps information file */

/* 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 # */
} ;

/* Structure holding a single hash table entry ... */
struct hashtab
{
	unsigned short          h_uid ;         /* Uid of user entry */
	char                    h_uname[ UNAMELEN ] ; /* Corresponding name */
} ;

/*
** Format of the standard information file maintained by sps.
** This structure is filled in at initialisation time and then is read back
** in whenever sps 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 ... */
	struct proc             *i_proc0 ;      /* address of process table */
	int                     i_nproc ;       /* length of process table */
	struct text             *i_text0 ;      /* address of text table */
	int                     i_ntext ;       /* length of text table */
	struct inode            *i_inode0 ;     /* address of inode table */
	int                     i_ninode ;      /* length of inode table */
	struct buf              *i_swbuf0 ;     /* address of swap buffers */
	int                     i_nswbuf ;      /* # swap buffers */
	struct buf              *i_buf0 ;       /* address of i/o buffers */
	int                     i_nbuf ;        /* # i/o buffers */
	int                     i_ecmx ;        /* max physical memory address*/
	struct pte              *i_usrptmap ;   /* page table map */
	struct pte              *i_usrpt ;      /* page table map */
	struct cdevsw           *i_cdevsw ;     /* device switch to find ttys */
# ifdef BSD42
	struct quota            *i_quota0 ;     /* disc quota structures */
	int                     i_nquota ;      /* # quota structures */
	int                     i_dmmin ;       /* The start of the disc map */
	int                     i_dmmax ;       /* The end of the disc map */
	struct mbuf             *i_mbutl ;      /* Start of mbuf area */
# else
	int                     i_hz ;          /* Clock rate */
# endif
# ifdef CHAOS
	caddr_t                 i_Chconntab ;   /* Chaos connection table */
# endif
	/* Kernel addresses are associated with process wait states ... */
	caddr_t                 i_waitstate[ NWAITSTATE ] ;
	/* User names, stored in a hash table ... */
	struct hashtab          i_hnames[ MAXUSERID ] ;
	/* Tty device info ... */
	struct ttyline          i_ttyline[ MAXTTYS ] ;
} ;

/*
** 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 */
} ;

/* The `user' structure obtained from /dev/mem or /dev/swap ... */
union userstate
{
	struct user             u_us ;
	char                    u_pg[ UPAGES ][ NBPG ] ;
} ;

/* 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 ;      /* Ptr to parent process */
# ifdef BSD42
	struct rusage           pr_rself ;      /* Read from upage for self */
	struct rusage           pr_rchild ;     /* ... and the children */
# else
	struct vtimes           pr_vself ;      /* Read from upage for self */
	struct vtimes           pr_vchild ;     /* ... and the children */
# endif
	int                     pr_files ;      /* # open files */
	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() */
} ;

/* 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 */
	long                    sm_kswapped ;   /* Size totally swapped out */
} ;
---END-OF-sps.h---
echo Extracting waitingfor.c
cat > waitingfor.c << '---END-OF-waitingfor.c---'
# include       "sps.h"
# include       <h/tty.h>
# include       <h/text.h>
# ifdef SUN
# include       <h/vnode.h>
# include       <ufs/inode.h>
# else
# include       <h/inode.h>
# endif
# include       <h/buf.h>
# ifdef BSD42
# include       <h/quota.h>
# include       <h/mbuf.h>
# include       <h/socket.h>
# include       <h/socketvar.h>
# endif

/* 1 if `w' is in the address range defined by `a1' and `a2' ... */
# define        INRANGE( w, a1, a2 ) \
			( (caddr_t)(a1) <= (w) && (w) < (caddr_t)(a2) )

/* WAITINGFOR - Determine what a process is waiting for and describe it. */
char    *waitingfor ( p )

struct process                  *p ;

{
	register caddr_t        w ;
	register struct ttyline *lp ;
	register struct symbol  *s ;
	register char           *cp ;
# ifdef BSD42
	struct socket           sc ;
# endif
	static char             wbuf[ 8 ] ;
	extern struct info      Info ;
	extern struct symbol    Symbollist[] ;
	char                    *sprintf() ;

	w = p->pr_p.p_wchan ;
	if ( !w )
		return ( "null" ) ;
	/* Waiting for a child process, alternatively in a vfork() ? */
	if ( INRANGE( w, Info.i_proc0, &Info.i_proc0[ Info.i_nproc ] ) )
		return ( p->pr_p.p_flag & SNOVM ? "vfork" : "child" ) ;
	/* Waiting for a page to be brought in ? */
	if ( INRANGE( w, Info.i_swbuf0, &Info.i_swbuf0[ Info.i_nswbuf ] ) )
		return ( "swap" ) ;
	/* Waiting for discio through a block device to complete ? */
	if ( INRANGE( w, Info.i_buf0, &Info.i_buf0[ Info.i_nbuf ] ) )
		/* SHOULD ACTUALLY READ AS "blkio" BUT "discio" IS WHAT
		   IS GENERALLY MEANT HERE. */
		return ( "discio" ) ;
	/* Waiting for a text page to be brought in ? */
	if ( INRANGE( w, Info.i_text0, &Info.i_text0[ Info.i_ntext ] ) )
		return ( "swtext" ) ;
# ifdef BSD42
	/* Waiting for an event associated with the quota system ? */
	if ( INRANGE( w, Info.i_quota0, &Info.i_quota0[ Info.i_nquota ] ) )
		return ( "quota" ) ;
# endif
	/* Waiting for tty I/O ? If so, find which tty it is */
	for ( lp = Info.i_ttyline ; lp->l_name[0] ; lp++ )
		if ( INRANGE( w, &lp->l_addr[0], &lp->l_addr[1] ) )
		{
			switch ( w - (int)lp->l_addr )
			{
				case (int)&((struct tty*)0)->t_rawq :
					/* Read from a tty or slave pty */
					cp = "rtty??" ;
					break ;
				case (int)&((struct tty*)0)->t_outq :
					/* Write to a tty or slave pty */
					cp = "wtty??" ;
					break ;
				case (int)&((struct tty*)0)->t_state :
					/* Tty not open */
					cp = "otty??" ;
					break ;
				case (int)&((struct tty*)0)->t_outq.c_cf :
					/* Read from a controller pty */
					cp = "rpty??" ;
					break ;
				case (int)&((struct tty*)0)->t_rawq.c_cf :
					/* Write to a controller pty */
					cp = "wpty??" ;
					break ;
				default :
					cp = "?tty??" ;
					break ;
			}
			cp[4] = lp->l_name[0] ;
			cp[5] = lp->l_name[1] ;
			return ( cp ) ;
		}
	/* Waiting for an inode ? */
	if ( INRANGE( w, Info.i_inode0, &Info.i_inode0[ Info.i_ninode ] ) )
		switch ( ((int)w - (int)Info.i_inode0) % sizeof( struct inode ))
		{
# ifdef BSD42
# ifndef SUN
			case (int)&((struct inode*)0)->i_exlockc :
				/* Exclusive lock on this inode */
				return ( "exlock" ) ;
			case (int)&((struct inode*)0)->i_shlockc :
				/* Shared lock on this inode */
				return ( "shlock" ) ;
# endif
# else
			case 1 :
				return ( "wpipe" ) ;
			case 2 :
				return ( "rpipe" ) ;
			case (int)&((struct inode*)0)->i_un.i_group.g_datq :
				return ( "rmux" ) ;
# endif
			default :
				/* Inode probably locked */
				return ( "inode" ) ;
		}
# ifdef BSD42
	/* Waiting for a structure inside an mbuf ? If so, try to find why */
	if ( INRANGE( w, Info.i_mbutl,
	&Info.i_mbutl[ NMBCLUSTERS * CLBYTES / sizeof( struct mbuf ) ] ) )
		switch ( ((int)w - (int)Info.i_mbutl) % sizeof( struct mbuf )
			- (int)&((struct mbuf*)0)->m_dat[0] )
		{
			case (int)&((struct socket*)0)->so_timeo :
				/* Socket timeout event */
				return ( "socket" ) ;
			case (int)&((struct socket*)0)->so_rcv.sb_cc :
				/* Read from an empty socket. Here we actually
				   attempt to determine whether the socket
				   structure in question really does refer to
				   a socket, or whether it is in fact a pipe
				   in disguise. */
				return ( getsocket( (struct socket*)(w
				    - (int)&((struct socket*)0)->so_rcv.sb_cc),
						&sc )
					&& sc.so_type == SOCK_STREAM
					&& !sc.so_rcv.sb_hiwat
					&& !sc.so_rcv.sb_mbmax
					&& (sc.so_state
					    & (SS_ISCONNECTED|SS_CANTRCVMORE))
					? "rpipe" : "rsockt" ) ;
			case (int)&((struct socket*)0)->so_snd.sb_cc :
				/* Write to a full socket. Again, we try
				   to determine whether or not this is a
				   real socket or a pipe. */
				return ( getsocket( (struct socket*)(w
				    - (int)&((struct socket*)0)->so_snd.sb_cc),
						&sc )
					&& sc.so_type == SOCK_STREAM
					&& sc.so_rcv.sb_hiwat == 2048
					&& sc.so_rcv.sb_mbmax == 4096
					&& (sc.so_state
					    & (SS_ISCONNECTED|SS_CANTSENDMORE))
					? "wpipe" : "wsockt" ) ;
			default :
				/* Other mbuf event */
				return ( "mbuf" ) ;
		}
# 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 SUN
	(void)sprintf( wbuf, "x%05x", w ) ;
# else
	(void)sprintf( wbuf, "x%05x", w - 0x80000000 ) ;
# endif
	return ( wbuf ) ;
}

# ifdef BSD42
/*
** GETSOCKET - Reads a `struct socket' from the kernel virtual memory address
** identified by `ks' into the buffer `s'.
*/
getsocket ( ks, s )

struct socket                   *ks ;
struct socket                   *s ;

{
	extern int              Flkmem ;

	memseek( Flkmem, (long)ks ) ;
	return ( read( Flkmem, (char*)s, sizeof( struct socket ) )
		== sizeof( struct socket ) ) ;
}
# endif
---END-OF-waitingfor.c---