[comp.sys.sequent] Sequent version of top requested

nagel@paris.ics.uci.edu (Mark Nagel) (08/26/88)

Some time ago there was a version of top (a program to display information
about the system and the top n processes using curses) that worked on
the Sequent Balance.  We seem to have lost the source code for this version
and I'd really like to make the newest version (2.5) work on our Balance.
If anyone out there has the source for this, please let me know or
point me in the right direction.  Thanks in advance...

-- 
Mark Nagel
Department of Information and Computer Science, UC Irvine
nagel@ics.uci.edu             (ARPA)             When they ship styrofoam...
{sdcsvax|ucbvax}!ucivax!nagel (UUCP)             ...what do they pack it in?

tom@taux01.UUCP (Tom Gorodecki) (08/30/88)

In article <650@paris.ICS.UCI.EDU> nagel@paris.ics.uci.edu (Mark Nagel) writes:
>Some time ago there was a version of top (a program to display information
>about the system and the top n processes using curses) that worked on
>the Sequent Balance.  We seem to have lost the source code for this version
>and I'd really like to make the newest version (2.5) work on our Balance.
>If anyone out there has the source for this, please let me know or
>point me in the right direction.  Thanks in advance...
>
>-- 

Top on sequent. Please send me a copy too.
Thanks.
Tom.

-- 
Gorodecki Tom.  National Semiconductor (IC)
6 Maskit st. P.O.B. 3007, Herzlia B,  46 104, Israel
UUCP:  {hplabs,pyramid,sun,decwrl}!nsc!taux01!tom
domain:  tom%taux01@nsc.com

pwolfe@kailand.KAI.COM (08/31/88)

Here is the source to a version 1.6 of top that compiles and runs on V3.0.4
DYNIX on our Sequent Balance.  It compiles, but doesn't work correctly on
our Sequent Symmetry running DYNIX V3.0.12.

If you can use this to make any newer version work, great!  Please post it to
comp.sys.sequent.

Thanks,

Patrick Wolfe  (pwolfe@kai.com,  kailand!pwolfe)


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README Makefile top.c top.1
# Wrapped by pwolfe@kailand on Tue Aug 30 21:10:26 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(3365 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThis file contains a few comments about "top".
X
X"top" is a program that will give continual reports about the state of the
Xsystem, including a list of the top cpu using processes.  It requires read
Xaccess to the memory files "/dev/kmem" and "/dev/mem" as well as the system
Ximage "/vmunix".  Some installations have these files protected from general
Xaccess.  These sites would have to install this program in the same way that
Xprograms such as "ps" are installed.
X
XThere are a few things that need to be checked before compiling the program:
X
XThe most important item is the internal hash table size.  This size is
Xdefined in the program with the preprocessor variable "Table_size".  This
Xconstant MUST be larger than the number of lines in the file /etc/passwd.
XIt is advisable that this number be about twice the number of lines, and
Xthat it be a prime number (since it dictates the size of the hash table).
XMake sure this is checked before compilation.
X
XThe next thing is the routine called "bzero".  This function simply zeros
Xa block of memory.  The file "bzero.c" in this distribution is written for
Xa VAX and will not work on any other type of machine.  This routine is not
Xneeded under 4.2, since it is already defined in the C run time library.
XThe routine is very easy to write, but it is something that should be as
Xfast as possible.  If the local machine is not a VAX then "bzero.c" will
Xhave to be replaced with something else.  If you can't come up with a
Xslick method, there is a very simple C equivalent (although it will not be
Xvery fast) that will work:
X
X	bzero(memory, amount)
X	
X	char *memory;
X	int amount;
X	
X	{
X	    while (amount > 0)
X	    {
X		*memory++ = 0;
X		amount--;
X	    }
X	}
X
XThere are two Makefiles in this distribution.  One makes a version of top
Xfor a 4.1 system (called "Makefile4.1") and the other makes a version for a
X4.2 system (called "Makefile4.2").  Rename or link the version that is
Xappropriate for your machine to "Makefile" before running make.  There are
Xsome differences compiling and linking the different versions.  For example:
X"bzero" is already defined in the 4.2 C library, so that routine is not needed
Xunder 4.2.  There are also differences in the source, which are
Xconditionally compiled.  Both of these cases are handled correctly by the
Xappropriate Makefile.
X
XFinally, there are two preprocessor variables that are defined at compile
Xtime by the makefile.  These are "Default_TOPN" and "Default_DELAY".  Their
Xvalues are the defaults used for the top number of processes to be displayed
Xand the number of seconds to delay between displays, respectively.  They are
Xset by the Makefile variables "TOPN" and "DELAY", respecitvely.  These
Xconstants are preset as follows:  TOPN=10, DELAY=5.  These can be overriden
Xby either changing the Makefile or by specifying the change on the make
Xcommand line (with something like "make TOPN=15").
X
XIf you make a change to "top" that you feel would be beneficial to others
Xwho use this program, or if you find and fix a bug, feel free to send me the
Xchange.
X
XEnjoy!
X
X                                William LeFebvre
X				Department of Computer Science
X				Rice University
X                                ARPANet address: <phil@Rice.arpa>
X
X				U.S. Mail address:
X				    William LeFebvre
X				    P.O. Box 1892
X				    Department of Computer Science
X				    Houston, TX  77251
END_OF_FILE
if test 3365 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(664 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile for Sequent DYNIX V3.0.4 version of "top"
X
X# values for two defaults in "top"
XTOPN = 15
XDELAY = 3
X
XCFLAGS = -DFOUR_TWO -DSUN -O
XDEFS = -DDefault_TOPN=$(TOPN) -DDefault_DELAY=$(DELAY)
XLIBS = -lcurses -ltermcap -lseq -lm
XMANSECT = l
XTARFILES = Makefile README top.c top.1
X
Xtop: top.c
X	cc -o top $(CFLAGS) $(DEFS) top.c $(LIBS)
X
Xinstall: top
X	install -s -g staff -m 771 top /usr/local/bin
X
Xmanpage: top.1
X	cp top.1 /usr/man/man$(MANSECT)/top.$(MANSECT)
X	chgrp staff /usr/man/man$(MANSECT)/top.$(MANSECT)
X	chmod 664 /usr/man/man$(MANSECT)/top.$(MANSECT)
X
Xman:
X	nroff -$(MAN) -rN$(TOPN) -rD$(DELAY) top.1 | cat -s >top.cat
X
Xtar:
X	tar cvf top.tar $(TARFILES)
END_OF_FILE
if test 664 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'top.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'top.c'\"
else
echo shar: Extracting \"'top.c'\" \(16193 characters\)
sed "s/^X//" >'top.c' <<'END_OF_FILE'
Xchar *copyright = "Top, version 1.6, copyright (c) 1984, William LeFebvre";
X
X/*
X *  Top users display for Berkeley Unix
X *  Version 1.6
X *
X *  This program may be freely redistributed to other Unix sites, but this
X *  entire comment MUST remain intact.
X *
X *  Copyright (c) 1984, William LeFebvre, Rice University
X *
X *  This program is designed to run on either Berkeley 4.1 or 4.2 Unix.
X *  Compile with the preprocessor variable "FOUR_TWO" set to get an
X *  executable that will run on Berkeley 4.2 Unix.
X *
X *  The Sun kernel uses scaled integers instead of floating point so compile
X *  with the preprocessor variable "SUN" to get an executable that will run
X *  on Sun Unix version 1.1 or later.
X *
X *  Fixes and enhancements since version 1.5:
X *
X *  Jonathon Feiber at sun:
X *	added "#ifdef SUN" code to make top work on a Sun,
X *	fixed race bug in getkval for getting user structure,
X *	efficiency improvements:  added register variables and
X *		removed the function hashit
X */
X
X#include <curses.h>
X#include <stdio.h>
X#include <pwd.h>
X#include <nlist.h>
X#include <signal.h>
X#include <sys/param.h>
X#ifdef FOUR_TWO
X#include <machine/pte.h>
X#else
X#include <sys/pte.h>
X#endif
X#include <sys/dir.h>
X#include <sys/user.h>
X#include <sys/proc.h>
X#include <sys/dk.h>
X#include <sys/vm.h>
X#include <machine/hwparam.h>
X#include <machine/engine.h>
X#include <machine/plocal.h>
X
X/* obvious file names */
X
X#define VMUNIX	"/dynix"
X#define KMEM	"/dev/kmem"
X#define MEM	"/dev/mem"
X
X/*
X *  The number of users in /etc/passwd CANNOT be greater than Table_size!
X *  If you get the message "table overflow: too many users", then increase
X *  Table_size.  Since this is the size of a hash table, things will work
X *  best if it is a prime number that is about twice the number of users.
X */
X
X#define Table_size	421
X
X/* Number of lines of header information on the standard screen */
X# define Header_lines	5
X
X/* wish list for kernel symbols */
X
Xstruct nlist nlst[] = {
X    { "_avenrun" },
X#define X_AVENRUN	0
X    { "_ccpu" },
X#define X_CCPU		1
X    { "_engine" },
X#define X_ENGINE	2
X    { "_hz" },
X#define X_HZ		3
X    { "_mpid" },
X#define X_MPID		4
X    { "_nproc" },
X#define X_NPROC		5
X    { "_proc" },
X#define X_PROC		6
X    { "_Nengine" },
X#define X_CPUS		7
X    { 0 },
X};
X
X/* useful externals */
Xextern int errno;
Xextern char *sys_errlist[];
Xextern int LINES;
X
X/* signal handling routines */
Xint leave();
Xint onalrm();
X
X/* file descriptors for memory devices */
Xint kmem;
Xint mem;
X
Xint engines;
Xstruct engine *enginep, engin;
Xstruct plocal tmppl, *plp[30];
Xunsigned *p1, *p2;
Xstruct vmmeter vm;
Xint nproc;
Xint mpid;
X
X/* kernel "hz" variable -- clock rate */
Xlong hz;
X
X/* All this is to calculate the cpu state percentages */
X
Xlong cp_time[CPUSTATES];
Xlong cp_old[CPUSTATES];
Xlong cp_change[CPUSTATES];
Xlong total_change;
Xlong cp_time_offset;
Xlong mpid_offset;
Xlong avenrun_offset;
X
X#ifdef SUN
Xlong ccpu;
Xlong avenrun[3];
X#else
Xdouble ccpu;
Xdouble avenrun[3];
X#endif
Xdouble logcpu;
X
Xstruct proc *proc;
Xstruct proc *pbase;
Xint bytes;
X
Xstruct user u;
X
X/* Verbose process state names */
X
Xchar *state_name[] = {
X    "", "sleeping", "ABANDONED", "runnable", "starting", "zombie", "stopped",
X	"Running"};
X
X/* process state names for the "STATE" column of the display */
X
Xchar *state_abbrev[] = {
X    "", "sleep", "WAIT", "run", "idl", "zomb", "stop", "Run"
X};
X
X/* cpu state names for percentages */
X
Xchar *cpu_state[] = {
X    "user", "nice", "system", "idle"
X};
X
X/* routines that don't return int */
X
Xstruct passwd *getpwent();
Xchar *username();
Xchar *ctime();
Xchar *rindex();
X
Xint proc_compar();
Xdouble log();
Xdouble exp();
X
Xmain(argc, argv)
X
Xint  argc;
Xchar *argv[];
X
X{
X    register struct proc *pp;
X    register struct proc **prefp;
X    register int i;
X    register int active_procs;
X    register int change;
X    register long cputime;
X
X    int j,k;
X    struct proc **pref;
X    int total_procs;
X    int proc_brkdn[8];
X    int topn = Default_TOPN;
X    int delay = Default_DELAY;
X    long curr_time;
X    long time();
X    char do_cpu = 0;
X    char *myname = "top";
X    double pctcpu;
X
X    /* get our name */
X    if (argc > 0)
X    {
X	if ((myname = rindex(argv[0], '/')) == 0)
X	{
X	    myname = argv[0];
X	}
X	else
X	{
X	    myname++;
X	}
X    }
X
X    /* check for time delay option */
X    if (argc > 1 && argv[1][0] == '-')
X    {
X	if (argv[1][1] != 's')
X	{
X	    fprintf(stderr, "Usage: %s [-sn] [number]\n", myname);
X	    exit(1);
X	}
X	delay = atoi(&(argv[1][2]));
X	argc--;
X	argv++;
X    }
X
X    /* get count of top processes to display (if any) */
X    if (argc > 1)
X    {
X	topn = atoi(argv[1]);
X    }
X
X    /* open kmem and mem */
X    if ((kmem = open(KMEM, 0)) < 0)
X    {
X	perror(KMEM);
X	exit(1);
X    }
X    if ((mem = open(MEM, 0)) < 0)
X    {
X	perror(MEM);
X	exit(1);
X    }
X
X    /* get the list of symbols we want to access in the kernel */
X    errno = 0;
X    nlist(VMUNIX, nlst);
X    if (nlst[0].n_type == 0)
X    {
X	fprintf(stderr, "%s: can't nlist image\n", VMUNIX);
X	exit(1);
X    }
X
X    /* get the symbol values out of kmem */
X    getkval(nlst[X_PROC].n_value,  &proc,  sizeof(int), nlst[X_PROC].n_name);
X    getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int), nlst[X_NPROC].n_name);
X    getkval(nlst[X_CCPU].n_value,  &ccpu,  sizeof(int), nlst[X_CCPU].n_name);
X    getkval(nlst[X_HZ].n_value,    &hz,    sizeof(int), nlst[X_HZ].n_name);
X    getkval(nlst[X_CPUS].n_value,  &engines,sizeof(int), nlst[X_CPUS].n_name);
X    getkval(nlst[X_ENGINE].n_value,&enginep,sizeof(int),nlst[X_ENGINE].n_name);
X
X    /* get vmmeter ptrs */
X    for(i=0;i<engines;i++){
X	getkval(enginep,&engin,sizeof(engin),"engine");
X	plp[i] = (struct plocal *)engin.e_local->pp_local;
X	enginep++;
X    }
X    /* some calculations we use later */
X
X    mpid_offset = nlst[X_MPID].n_value;
X    avenrun_offset = nlst[X_AVENRUN].n_value;
X
X    /* this is used in calculating WCPU -- calculate it ahead of time */
X#ifdef SUN
X    logcpu = log((double)ccpu / FSCALE);
X#else
X    logcpu = log(ccpu);
X#endif
X
X    /* allocate space for proc structure array and array of pointers */
X    bytes = nproc * sizeof(struct proc);
X    pbase = (struct proc *)sbrk(bytes);
X    pref  = (struct proc **)sbrk(nproc * sizeof(struct proc *));
X
X    /* Just in case ... */
X    if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
X    {
X	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
X	exit(1);
X    }
X
X    /* initialize the hashing stuff */
X    init_hash();
X
X    /* initialize curses and screen (last) */
X    initscr();
X    erase();
X    clear();
X    refresh();
X
X    /* setup signal handlers */
X    signal(SIGINT, leave);
X    signal(SIGQUIT, leave);
X
X    /* can only display (LINES - Header_lines) processes */
X    if (topn > LINES - Header_lines)
X    {
X	printw("Warning: this terminal can only display %d processes...\n",
X	    LINES - Header_lines);
X	refresh();
X	sleep(2);
X	topn = LINES - Header_lines;
X	clear();
X    }
X
X    /* main loop ... */
X
X    while (1)		/* while(1)'s are forever ... */
X    {
X	/* read all the proc structures in one fell swoop */
X	getkval(proc, pbase, bytes, "proc array");
X
X	/* get load average array */
X	getkval(avenrun_offset, avenrun, sizeof(avenrun), "_avenrun");
X
X	/* get mpid -- process id of last process */
X	getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid");
X
X	/* sum up engines data vmmeter */
X#define NVM (sizeof(struct vmmeter)/sizeof(int))
X		p1 = (unsigned *)&vm;
X		for(j=0;j<NVM;j++) *p1++ = 0;
X		for(j=0;j<engines;j++){
X			getkval(plp[j],&tmppl,sizeof(struct plocal),"plocal");
X			p1 = (unsigned *)&vm;
X			p2 = (unsigned *)&tmppl.cnt;
X			for(k=0;k<NVM;k++,p1++) *p1 += *p2++;
X		}
X	bcopy(vm.v_time,cp_time,sizeof(cp_time));
X
X	/* count up process states and get pointers to interesting procs */
X	total_procs = 0;
X	active_procs = 0;
X	bzero(proc_brkdn, sizeof(proc_brkdn));
X	prefp = pref;
X	for (pp = pbase, i = 0; i < nproc; pp++, i++)
X	{
X	    /* place pointers to each valid proc structure in pref[] */
X	    /* (processes with SSYS set are system processes) */
X	    if (pp->p_pid != 0 && pp->p_stat != 0 && (pp->p_flag & SSYS) == 0)
X	    {
X		total_procs++;
X		proc_brkdn[pp->p_stat]++;
X		if (pp->p_stat != SZOMB)
X		{
X		    *prefp++ = pp;
X		    active_procs++;
X		}
X	    }
X	}
X
X	/* display the load averages */
X	printw("last pid: %d;  load averages", mpid);
X	{
X	    for (i = 0; i < 3; i++)
X	    {
X		printw("%c %4.2f",
X		    i == 0 ? ':' : ',',
X#ifdef SUN
X		    (double)avenrun[i] / FSCALE);
X#else
X		    avenrun[i]);
X#endif
X	    }
X	}
X	 /* display number of users */
X	 printw("   Users: %d  CPUs %d",user_count(),engines);
X
X	/*
X	 *  Display the current time.
X	 *  "ctime" always returns a string that looks like this:
X	 *  
X	 *	Sun Sep 16 01:03:52 1973
X	 *      012345678901234567890123
X	 *	          1         2
X	 *
X	 *  We want indices 11 thru 18 (length 8).
X	 */
X
X	curr_time = time(0);
X	move(0, 79-8);
X	printw("%-8.8s\n", &(ctime(&curr_time)[11]));
X
X	/* display process state breakdown */
X	printw("%d processes", total_procs);
X	for (i = 1; i < 8; i++)
X	{
X	    if (proc_brkdn[i] != 0)
X		printw("%c %d %s%s",
X			i == 1 ? ':' : ',',
X			proc_brkdn[i],
X			state_name[i],
X			(i == SZOMB) && (proc_brkdn[i] > 1) ? "s" : "");
X	}
X
X	/* calculate percentage time in each cpu state */
X	printw("\nCpu states: ");
X	if (do_cpu)	/* but not the first time */
X	{
X	    total_change = 0;
X	    for (i = 0; i < CPUSTATES; i++)
X	    {
X		/* calculate changes for each state and overall change */
X		if (cp_time[i] < cp_old[i])
X		{
X		    /* this only happens when the counter wraps */
X		    change = (int)
X			((unsigned long)cp_time[i]-(unsigned long)cp_old[i]);
X		}
X		else
X		{
X		    change = cp_time[i] - cp_old[i];
X		}
X		total_change += (cp_change[i] = change);
X	    }
X	if (total_change == 0) total_change=1;
X	    for (i = 0; i < CPUSTATES; i++)
X	    {
X		printw("%s%4.1f%% %s",
X			i == 0 ? "" : ", ",
X			((float)cp_change[i] / (float)total_change) * 100.0,
X			cpu_state[i]);
X		cp_old[i] = cp_time[i];
X	    }
X	}
X	else
X	{
X	    /* we'll do it next time */
X	    for (i = 0; i < CPUSTATES; i++)
X	    {
X		printw("%s      %s",
X			i == 0 ? "" : ", ",
X			cpu_state[i]);
X		cp_old[i] = 0;
X	    }
X	    do_cpu = 1;
X	}
X	printw("\n");
X
X	if (topn > 0)
X	{
X	    printw("\n  PID USERNAME PRI NICE   SIZE   RES STATE   TIME  WCPU   CPU  ENG COMMAND\n");
X    
X	    /* sort by cpu percentage (pctcpu) */
X	    qsort(pref, active_procs, sizeof(struct proc *), proc_compar);
X    
X	    /* now, show the top whatever */
X	    if (active_procs > topn)
X	    {
X		/* adjust for a lack of processes */
X		active_procs = topn;
X	    }
X	    for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
X	    {
X		pp = *prefp;
X		if (getu(pp) == -1)
X		{
X		    strcpy(u.u_comm, "<swapped>");
X		    cputime = 0;
X		}
X		else
X		{
X		    cputime = 
X#ifdef FOUR_TWO
X			(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec);
X#else
X			(int)((float)(u.u_vm.vm_utime + u.u_vm.vm_stime)/hz);
X#endif
X		}
X#ifdef SUN
X		pctcpu = (double)pp->p_pctcpu / FSCALE;
X#else
X		pctcpu = pp->p_pctcpu;
X#endif
X		printw("%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %2d %-11.11s\n",
X		    pp->p_pid,
X		    username(pp->p_uid),
X		    pp->p_pri - PZERO,
X		    pp->p_nice - NZERO,
X		    (pp->p_dsize + pp->p_ssize) >> 1,
X		    pp->p_rssize >> 1,
X		    state_abbrev[pp->p_stat],
X		    cputime / 60l,
X		    cputime % 60l,
X#ifdef SUN
X		    (100.0 * pctcpu / (1.0 - exp(pp->p_time * logcpu))),
X		    (100.0 * pctcpu),
X#else
X		    (100.0 * pctcpu / (1.0 - exp(pp->p_time * logcpu))),
X		    (100.0 * pctcpu),
X#endif
X		    pp->p_engno,
X		    u.u_comm);
X	    }
X	}
X	refresh();
X
X	/* wait ... */
X	signal(SIGALRM, onalrm);
X	alarm(delay);
X	pause();
X
X	/* clear for new display */
X	erase();
X    }
X}
X
X/*
X *  signal handlers
X */
X
Xleave()			/* exit under normal conditions -- INT handler */
X
X{
X    move(LINES - 1, 0);
X    refresh();
X    endwin();
X    exit(0);
X}
X
Xquit(status)		/* exit under duress */
X
Xint status;
X
X{
X    endwin();
X    exit(status);
X}
X
Xonalrm()
X
X{
X    return(0);
X}
X
X/*
X *  comparison function for "qsort"
X */
X
Xproc_compar(p1, p2)
X
Xstruct proc **p1;
Xstruct proc **p2;
X
X{
X    if ((*p1)->p_pctcpu < (*p2)->p_pctcpu)
X    {
X	return(1);
X    }
X    else
X    {
X	return(-1);
X    }
X}
X
X/*
X *  These routines handle uid to username mapping.
X *  They use a hashing table scheme to reduce reading overhead.
X */
X
Xstruct hash_el {
X    int  uid;
X    char name[8];
X};
X
X#define    H_empty	-1
X
X/* simple minded hashing function */
X#define    hashit(i)	((i) % Table_size)
X
Xstruct hash_el hash_table[Table_size];
X
Xinit_hash()
X
X{
X    register int i;
X    register struct hash_el *h;
X
X    for (h = hash_table, i = 0; i < Table_size; h++, i++)
X    {
X	h->uid = H_empty;
X    }
X
X    setpwent();
X}
X
Xchar *username(uid)
X
Xregister int uid;
X
X{
X    register int index;
X    register int found;
X    register char *name;
X
X    /* This is incredibly naive, but it'll probably get changed anyway */
X    index = hashit(uid);
X    while ((found = hash_table[index].uid) != uid)
X    {
X	if (found == H_empty)
X	{
X	    /* not here -- get it out of passwd */
X	    index = get_user(uid);
X	    break;		/* out of while */
X	}
X	index = index++ % Table_size;
X    }
X    return(hash_table[index].name);
X}
X
Xenter_user(uid, name)
X
Xregister int  uid;
Xregister char *name;
X
X{
X    register int length;
X    register int index;
X    register int try;
X    static int uid_count = 0;
X
X    /* avoid table overflow -- insure at least one empty slot */
X    if (++uid_count >= Table_size)
X    {
X	fprintf(stderr, "table overflow: too many users\n");
X	quit(1);
X    }
X
X    index = hashit(uid);
X    while ((try = hash_table[index].uid) != H_empty)
X    {
X	if (try == uid)
X	{
X	    return(index);
X	}
X	index = index++ % Table_size;
X    }
X    hash_table[index].uid = uid;
X    strncpy(hash_table[index].name, name, 8);
X    return(index);
X}
X
Xget_user(uid)
X
Xregister int uid;
X
X{
X    struct passwd *pwd;
X    static char buff[20];
X    register int last_index;
X
X    while ((pwd = getpwent()) != NULL)
X    {
X	last_index = enter_user(pwd->pw_uid, pwd->pw_name);
X	if (pwd->pw_uid == uid)
X	{
X	    return(last_index);
X	}
X    }
X    sprintf(buff, "%d", uid);
X    return(enter_user(uid, buff));
X}
X
X/*
X *  All of this stuff gets things out of the memory files.
X */
X
X/*
X *  Get the user structure for the process who's proc structure is in p.  The
X *  user structure is returned in u.
X */
Xgetu(p)
X
Xregister struct proc *p;
X
X{
X    struct pte uptes[UPAGES];
X    register caddr_t upage;
X    register struct pte *pte;
X    register nbytes, n;
X
X    /*
X     *  Check if the process is currently loaded or swapped out.  The way we
X     *  get the u area is totally different for the two cases.  For this
X     *  application, we just don't bother if the process is swapped out.
X     */
X    if ((p->p_flag & SLOAD) == 0)
X    {
X	return(-1);
X    }
X
X    /*
X     *  Process is currently in memory, we hope!
X     */
X    if (!getkval(p->p_upte, uptes, sizeof(uptes), "p->p_addr"))
X    {
X	/* we can't seem to get to it, so pretend it's swapped out */
X	return(-1);
X    } 
X    upage = (caddr_t)&u;
X    pte = uptes;
X    for (nbytes = sizeof(u); nbytes > 0; nbytes -= NBPG)
X    {
X    	lseek(mem, pte++->pg_pfnum * NBPG, 0);
X	n = MIN(nbytes, NBPG);
X	if (read(mem, upage, n) != n)
X	{
X	    /* we can't seem to get to it, so pretend it's swapped out */
X	    return(-1);
X	}
X	upage += n;
X    }
X    return(0);
X}
X
X/*
X *  get the value of something from /dev/kmem
X */
X
Xgetkval(offset, ptr, size, refstr)
X
Xlong offset;
Xint *ptr;
Xint size;
Xchar *refstr;
X
X{
X    if (lseek(kmem, offset, 0) == -1)
X    {
X	fprintf(stderr, "%s: lseek to %s: %s\n",
X	    KMEM, refstr, sys_errlist[errno]);
X	quit(1);
X    }
X    if (read(kmem, ptr, size) == -1)
X    {
X	if (strcmp(refstr, "p->p_addr") == 0) 
X	{
X	    /* we lost the race with the kernel, process isn't in memory */
X	    return(0);
X	} 
X	else 
X	{
X	    fprintf(stderr, "%s: reading %s: %s\n",
X		KMEM, refstr, sys_errlist[errno]);
X	    quit(1);
X	}
X    }
X    return(1);
X}
X
X/*
X *  outc - real subroutine that writes a character; for termcap
X */
X
Xoutc(ch)
X
Xregister char ch;
X
X{
X    putchar(ch);
X}
X
X#include <utmp.h>
X
Xint user_count()
X{
X	static int first = 1;
X	static FILE *strm;
X	struct utmp utmp;
X	int count = 0;
X
X	if (first)
X	{
X	    strm = fopen("/etc/utmp", "r");
X	    first = 0;
X	}
X
X	rewind(strm);
X	while(fread(&utmp, sizeof(utmp), 1, strm))
X	{
X	    if (utmp.ut_name[0] != '\0')
X		count++;
X	}
X
X	return(count);
X}
END_OF_FILE
if test 16193 -ne `wc -c <'top.c'`; then
    echo shar: \"'top.c'\" unpacked with wrong size!
fi
# end of 'top.c'
fi
if test -f 'top.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'top.1'\"
else
echo shar: Extracting \"'top.1'\" \(2930 characters\)
sed "s/^X//" >'top.1' <<'END_OF_FILE'
X.TH TOP 1 Local
X.UC 4
X.SH NAME
Xtop \- display and update information about the top cpu processes
X.SH SYNOPSIS
X.B top
X[
X.BI \-s time
X] [
X.I number
X]
X.SH DESCRIPTION
X.\" This defines appropriate quote strings for nroff and troff
X.ds lq \&"
X.ds rq \&"
X.if t .ds lq ``
X.if t .ds rq ''
X.\" Just in case the command line didn't set these number registers...
X.if !\nN .nr N 10
X.if !\nD .nr D 5
X.I Top
Xdisplays the top \nN processes on the system and periodically
Xupdates this information.  It uses raw cpu percentage
Xto determine the top processes.  The
X.IR curses (3)
Xpackage is used to do semi-optimal screen updating.  If
X.I number
Xis given, then the top
X.I number
Xprocesses will be displayed instead of the default amount of \nN.
XThe
X.B \-s
Xoption sets the delay between screen updates to
X.I time
Xseconds.  The default delay between updates is \nD seconds.
X.PP
XThe top few lines of the display show general information about the state of
Xthe system, including the last process id assigned to a process, the three
Xload averages, the current time, the number of existing processes, the number
Xof processes in each state
X(sleeping, ABANDONED, running, starting, zombies, and stopped),
Xand a percentage of time
Xspent in each of the processor states (user, nice, system, and idle).
X.PP
XThe remainder of the screen displays information about individual
Xprocesses.  This display is similar in spirit to
X.IR ps (1)
Xbut it is not exactly the same.  PID is the process id, USERNAME is the name
Xof the process's owner, PRI is the current priority of the process, NICE is
Xthe nice amount (in the range \-20 to 20), SIZE is the total size of the
Xprocess (text, data, and stack), RES is the current amount of resident
Xmemory (both SIZE and RES are given in kilobytes), STATE is the current
Xstate (one of \*(lqsleep\*(rq, \*(lqWAIT\*(rq, \*(lqrun\*(rq,
X\*(lqidl\*(rq, \*(lqzomb\*(rq, or \*(lqstop\*(rq), TIME is
Xthe number of system and user
Xcpu seconds that the process has used, WCPU is the weighted cpu percentage
X(this is the same value that
X.IR ps (1)
Xdisplays as CPU), CPU is the raw percentage and is the field that is sorted
Xto determine the order of the processes, and COMMAND is the name of the 
Xcommand that the process is currently running (if the process is swapped
Xout, this column is marked \*(lq<swapped>\*(rq).
X.SH NOTES
XThe \*(lqABANDONED\*(rq state (known in the kernel as \*(lqSWAIT\*(rq) was
Xabandoned, thus the name.  A process should never end up in this state.
X.SH AUTHOR
XWilliam LeFebvre, Rice University graduate student
X.SH FILES
X.DT
X/dev/kmem		kernel memory
X.br
X/dev/mem		physical memory
X.br
X/vmunix 		system image
X.SH BUGS
XThe command name for swapped processes should be tracked down, but this
Xwould make the program run slower.
X.PP
XAs with
X.IR ps (1),
Xthings can change while
X.I top
Xis collecting information for an update.  The picture it gives is only a
Xclose approximation to reality.
X.SH "SEE ALSO"
Xps(1), mem(4)
END_OF_FILE
if test 2930 -ne `wc -c <'top.1'`; then
    echo shar: \"'top.1'\" unpacked with wrong size!
fi
# end of 'top.1'
fi
echo shar: End of shell archive.
exit 0