rs@uunet.UU.NET (Rich Salz) (07/23/87)
Submitted-by: hoptoad!vixie!paul (Paul Vixie Esq)
Posting-Number: Volume 10, Issue 64
Archive-name: top_s375/Part02
#! /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". If this archive is complete, you will
## see the following message at the end:
# "End of archive 2 (of 2)."
# Contents: display.c top.c
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f display.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"display.c\"
else
echo shar: Extracting \"display.c\" \(12060 characters\)
sed "s/^X//" >display.c <<'END_OF_display.c'
X/*
X * Top - a top users display for Berkeley Unix
X *
X * This file contains the routines that display information on the screen.
X * Each section of the screen has two routines: one for initially writing
X * all constant and dynamic text, and one for only updating the text that
X * changes. The prefix "i_" is used on all the "initial" routines and the
X * prefix "u_" is used for all the "updating" routines. NOTE: it is
X * assumed that none of the "i_" routines use any of the termcap
X * capabilities. In this way, those routines can be safely used on
X * terminals that have minimal (or nonexistant) terminal capabilities.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#if defined(scs)
X# define FLOAT /* for pctcpu in proc.h */
X# include <sys/vm.h> /* for struct spt */
X#endif
X#include <sys/proc.h>
X#include <sys/dk.h>
X#include "screen.h" /* interface to screen package */
X#include "layout.h" /* defines for screen position layout */
X#include "top.h"
X#include "boolean.h"
X
Xstatic int lmpid = 0;
Xstatic struct user u;
X
X#if defined(scs)
Xstatic struct spt pspt;
X#endif scs
X
Xchar *printable();
X
X/* Verbose process state names */
X
Xchar *state_name[] =
X{
X "", "sleeping", "ABANDONED", "running", "starting", "zombie", "stopped"
X};
X
X/* process state names for the "STATE" column of the display */
X
Xchar *state_abbrev[] =
X{
X "", "sleep", "WAIT", "run", "start", "zomb", "stop"
X};
X
X/* cpu state names for percentages */
X
Xchar *cpu_state[] =
X{
X "user", "nice", "system", "idle"
X};
X
X/* screen positions for cpustate figures */
Xchar x_cpustates[] = { 12, 24, 36, 50 };
X
Xi_loadave(mpid, avenrun)
X
Xint mpid;
X#if defined(sun)
Xlong *avenrun;
X#else
Xdouble *avenrun;
X#endif sun
X
X{
X register int i;
X
X printf("last pid: %5d; load averages", mpid);
X
X for (i = 0; i < 3; i++)
X {
X printf("%c %4.2f",
X i == 0 ? ':' : ',',
X#if defined(sun)
X (double)avenrun[i] / FSCALE);
X#else
X avenrun[i]);
X#endif
X }
X lmpid = mpid;
X}
X
Xu_loadave(mpid, avenrun)
X
Xint mpid;
X#if defined(sun)
Xlong *avenrun;
X#else
Xdouble *avenrun;
X#endif sun
X
X{
X register int i;
X
X if (mpid != lmpid);
X {
X Move_to(x_lastpid, y_lastpid);
X printf("%5d", mpid);
X lmpid = mpid;
X }
X
X Move_to(x_loadave, y_loadave);
X for (i = 0; i < 3; i++)
X {
X printf("%s%4.2f",
X i == 0 ? "" : ", ",
X#if defined(sun)
X (double)avenrun[i] / FSCALE);
X#else
X avenrun[i]);
X#endif
X }
X}
X
Xstatic int ltotal = 0;
Xstatic int lbrkdn[7];
X
Xi_procstates(total, brkdn)
X
Xint total;
Xint *brkdn;
X
X{
X register int i;
X
X printf("%2d processes", total); /* ??? */
X ltotal = total;
X for (i = 1; i < 7; i++)
X {
X if (brkdn[i] != 0)
X {
X printf("%c %d %s%s",
X i == 1 ? ':' : ',',
X brkdn[i],
X state_name[i],
X (i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
X }
X }
X bcopy(brkdn, lbrkdn, sizeof(lbrkdn));
X}
X
Xu_procstates(total, brkdn)
X
Xint total;
Xint *brkdn;
X
X{
X register int i;
X
X if (ltotal != total)
X {
X Move_to(x_procstate, y_procstate);
X printf("%d ", total);
X ltotal = total;
X }
X else if (bcmp(brkdn, lbrkdn, sizeof(lbrkdn)) == 0)
X {
X return;
X }
X
X Move_to(x_brkdn, y_brkdn);
X for (i = 1; i < 7; i++)
X {
X if (brkdn[i] != 0)
X {
X printf("%s%d %s%s",
X i == 1 ? "" : ", ",
X brkdn[i],
X state_name[i],
X (i == SZOMB) && (brkdn[i] > 1) ? "s" : "");
X }
X }
X putcap(clear_line);
X bcopy(brkdn, lbrkdn, sizeof(lbrkdn));
X}
X
Xi_cpustates(changes, total)
X
Xint *changes;
Xint total;
X
X{
X register int i;
X
X printf("\nCpu states: ");
X for (i = 0; i < CPUSTATES; i++)
X {
X printf("%s%4.1f%% %s",
X i == 0 ? "" : ", ",
X ((float)changes[i] / (float)total) * 100.0,
X cpu_state[i]);
X }
X printf("\n");
X}
X
Xu_cpustates(changes, total)
X
Xint *changes;
Xint total;
X
X{
X register int i;
X
X for (i = 0; i < CPUSTATES; i++)
X {
X Move_to(x_cpustates[i], y_cpustates);
X printf("%4.1f",
X ((float)changes[i] / (float)total) * 100.0);
X }
X}
X
Xz_cpustates()
X
X{
X register int i;
X
X printf("\nCpu states: ");
X for (i = 0; i < CPUSTATES; i++)
X {
X printf("%s %% %s", i == 0 ? "" : ", ", cpu_state[i]);
X }
X printf("\n");
X}
X
Xi_memory(i1, i2, i3, i4, i5)
X
Xint i1, i2, i3, i4, i5;
X
X{
X printf("Memory: %4dK (%4dK) real, %4dK (%4dK) virtual, %4dK free",
X i1, i2, i3, i4, i5);
X}
X
Xu_memory(i1, i2, i3, i4, i5)
X
Xint i1, i2, i3, i4, i5;
X
X{
X Move_to(x_realmem, y_mem);
X printf("%4dK (%4d", i1, i2);
X Move_to(x_virtmem, y_mem);
X printf("%4dK (%4d", i3, i4);
X Move_to(x_free, y_mem);
X printf("%4d", i5);
X}
X
Xi_header(f2)
X
Xchar *f2;
X
X{
X printf(
X "\n\n PID %s PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND",
X f2);
X}
X
Xu_header()
X
X{
X Move_to(0, y_header);
X}
X
X#if defined(sun)
X#define percent_cpu(pp) ((double)(pp)->p_pctcpu / FSCALE)
X#else
X#define percent_cpu(pp) ((pp)->p_pctcpu)
X#endif sun
X
X#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
X ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
X
X#ifdef DEBUG
XFILE *debug;
X#endif
X
Xstatic void
Xfmt_proc(thisline, pp, get_userid)
X char *thisline;
X struct proc *pp;
X char *(*get_userid)();
X{
X register long cputime;
X register double pctcpu;
X
X /* get the cpu usage and calculate the cpu percentages */
X cputime = get_ucpu(pp);
X pctcpu = percent_cpu(pp);
X
X /* format the line */
X
X#define Proc_format \
X "%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %.14s"
X
X#if !defined(pyr) && !defined(scs)
X /* vax or sun, both are the same here */
X sprintf(thisline, Proc_format,
X pp->p_pid,
X (*get_userid)(pp->p_uid),
X pp->p_pri - PZERO,
X pp->p_nice - NZERO,
X pagetok(pp->p_tsize + pp->p_dsize + pp->p_ssize),
X pagetok(pp->p_rssize),
X state_abbrev[pp->p_stat],
X cputime / 60l,
X cputime % 60l,
X 100.0 * weighted_cpu(pctcpu, pp),
X 100.0 * pctcpu,
X printable(u.u_comm));
X#else
X /* pyr xor scs */
X#if defined(pyr)
X sprintf(thisline, Proc_format,
X pp->p_pid,
X (*get_userid)(pp->p_uid),
X pp->p_pri - PZERO,
X pp->p_nice - NZERO,
X pagetok(pp->p_tsize+pp->p_dsize+pp->p_cssize+pp->p_ussize),
X pagetok(pp->p_rssize),
X state_abbrev[pp->p_stat],
X cputime / 60l,
X cputime % 60l,
X 100.0 * weighted_cpu(pctcpu, pp),
X 100.0 * pctcpu,
X printable(u.u_comm));
X#endif pyr
X#if defined(scs)
X get_spt(pp->p_spti, &pspt);
X sprintf(thisline, Proc_format,
X pp->p_pid,
X (*get_userid)(pp->p_uid),
X pp->p_pri - PZERO,
X pp->p_nice - NZERO,
X pagetok(pspt.spt_usedpages),
X pagetok(pspt.spt_mempages),
X state_abbrev[pp->p_stat],
X cputime / 60l,
X cputime % 60l,
X 100.0 * weighted_cpu(pctcpu, pp),
X 100.0 * pctcpu,
X printable(u.u_comm));
X#endif scs
X#endif /* for vax/sun / pyr/scs */
X}
X
Xi_process(line, pp, get_userid)
X int line;
X struct proc *pp;
X char *(*get_userid)();
X{
X register char *thisline;
X int len;
X
X#ifdef DEBUG
X debug = fopen("debug", "w");
X#endif
X
X /* calculate a pointer to the buffer for this line */
X thisline = screenbuf[line];
X
X /* format the line into our buffer */
X fmt_proc(thisline, pp, get_userid);
X
X /* write the line out */
X putchar('\n');
X fputs(thisline, stdout);
X
X /* zero fill the rest of it */
X len = strlen(thisline);
X bzero(thisline + len, Display_width - len);
X}
X
Xstatic int lastline = 0;
X
Xu_process(line, pp, get_userid)
X int line;
X struct proc *pp;
X char *(*get_userid)();
X{
X register char *optr;
X register char *nptr;
X register int ch;
X register int diff;
X register int newcol = 1;
X register int lastcol = 0;
X char cursor_on_line = No;
X char *thisline;
X int screen_line = line + Header_lines;
X static char newline[Display_width];
X
X /* get a pointer to the old text for this line */
X optr = thisline = screenbuf[line];
X
X /* format the line into our temporary buffer */
X fmt_proc(newline, pp, get_userid);
X
X /* compare the two strings and only rewrite what has changed */
X nptr = newline;
X#ifdef DEBUG
X fputs(optr, debug);
X fputc('\n', debug);
X fputs(nptr, debug);
X fputs("\n-\n", debug);
X#endif
X
X /* start things off on the right foot */
X /* this is to make sure the invariants get set up right */
X if ((ch = *nptr++) != *optr)
X {
X if (screen_line - lastline == 1)
X {
X putchar('\n');
X }
X else
X {
X Move_to(0, screen_line);
X }
X cursor_on_line = Yes;
X putchar(ch);
X *optr = ch;
X lastcol = 1;
X }
X optr++;
X
X /*
X * main loop -- check each character. If the old and new aren't the
X * same, then update the display. When the distance from the current
X * cursor position to the new change is small enough, the characters
X * that belong there are written to move the cursor over.
X *
X * Invariants:
X * lastcol is the column where the cursor currently is sitting
X * (always one beyond the end of the last mismatch).
X */
X do /* yes, a do...while */
X {
X if ((ch = *nptr++) != *optr)
X {
X /* new character is different from old */
X /* put the new character in the screen buffer */
X *optr = ch;
X
X /* make sure the cursor is on top of this character */
X diff = newcol - lastcol;
X if (diff > 0)
X {
X /* some motion is required--figure out which is shorter */
X if (diff < 6 && cursor_on_line)
X {
X /* overwrite old stuff--get it out of the screen buffer */
X printf("%.*s", diff, &thisline[lastcol]);
X }
X else
X {
X /* use cursor addressing */
X Move_to(newcol, screen_line);
X cursor_on_line = Yes;
X }
X /* remember where the cursor is */
X lastcol = newcol + 1;
X }
X else
X {
X /* already there, update position */
X lastcol++;
X }
X
X /* write what we need to */
X if (ch == '\0')
X {
X /* at the end--terminate with a clear-to-end-of-line */
X putcap(clear_line);
X }
X else
X {
X /* write the new character */
X putchar(ch);
X }
X }
X
X /* update working column and screen buffer pointer */
X newcol++;
X optr++;
X
X } while (ch != '\0');
X
X /* zero out the rest of the line buffer -- MUST BE DONE! */
X bzero(optr, Display_width - newcol);
X
X /* remember where the current line is */
X if (cursor_on_line)
X {
X lastline = screen_line;
X }
X}
X
Xstatic int last_hi = 0;
X
Xu_endscreen(hi)
X
Xregister int hi;
X
X{
X register int screen_line = hi + Header_lines;
X
X if (smart_terminal)
X {
X if (hi < last_hi)
X {
X if (hi == 0)
X {
X putchar('\n');
X putchar('\n');
X putcap(clear_line);
X putchar('\n');
X }
X else if (screen_line - lastline == 1)
X {
X putchar('\n');
X }
X else
X {
X Move_to(0, screen_line);
X }
X
X while (--last_hi > hi)
X {
X putcap(clear_line);
X putchar('\n');
X }
X putcap(clear_line);
X }
X else
X {
X last_hi = hi;
X }
X
X /* move the cursor to a pleasant place */
X Move_to(x_idlecursor, y_idlecursor);
X }
X else
X {
X /* separate this display from the next with some vertical room */
X fputs("\n\n", stdout);
X }
X}
X
X/*
X * get_ucpu(pp) - retrieve the user structure associated with the proc
X * structure pointed to by pp and return the cpu usage. The user
X * structure is stored in the global structure "u" for later use.
X */
X
Xget_ucpu(pp)
X
Xstruct proc *pp;
X
X{
X#if defined(scs)
X
X strcpy(u.u_comm, pp->p_infoname);
X return pp->p_infotime.tv_sec;
X
X#else
X
X if (getu(pp, &u) == -1)
X {
X strcpy(u.u_comm, "<swapped>");
X return(0);
X }
X else
X {
X /* set u_comm for system processes */
X if (u.u_comm[0] == '\0')
X {
X if (pp->p_pid == 0)
X {
X strcpy(u.u_comm, "Swapper");
X }
X else if (pp->p_pid == 2)
X {
X strcpy(u.u_comm, "Pager");
X }
X }
X
X#ifdef FOUR_ONE
X return((int)((float)(u.u_vm.vm_utime + u.u_vm.vm_stime)/hz));
X#else
X return(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec);
X#endif
X }
X#endif scs
X}
X
X/*
X * printable(str) - make the string pointed to by "str" into one that is
X * printable (i.e.: all ascii), by converting all non-printable
X * characters into '?'. Replacements are done in place and a pointer
X * to the original buffer is returned.
X */
X
Xchar *printable(str)
X
Xchar *str;
X
X{
X register char *ptr;
X register char ch;
X
X ptr = str;
X while ((ch = *ptr) != '\0')
X {
X if (!isprint(ch))
X {
X *ptr = '?';
X }
X ptr++;
X }
X return(str);
X}
END_OF_display.c
if test 12060 -ne `wc -c <display.c`; then
echo shar: \"display.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f top.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"top.c\"
else
echo shar: Extracting \"top.c\" \(26926 characters\)
sed "s/^X//" >top.c <<'END_OF_top.c'
Xchar *copyright =
X "Top, version 2.1, copyright (c) 1984, 1986, William LeFebvre";
X
X/*
X * Top users display for Berkeley Unix
X * Version 2.1
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, 1986, 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 constant "FOUR_ONE" set to get an
X * executable that will run on Berkeley 4.1 Unix.
X *
X * The Sun kernel uses scaled integers instead of floating point.
X * Compilation with the preprocessor variable "sun" gets an executable
X * that will run on Sun Unix version 1.1 or later ("sun" is automatically
X * set by the Sun C compiler).
X *
X * The Pyramid splits the stack size (p_ssize) into control stack and user
X * stack sizes. Compilation with the preprocessor variable "pyr" gets an
X * executable that will run on Pyramids ("pyr" is automatically set by the
X * Pyramid C compiler).
X *
X * The Symmetric 375 needs various goodies as well. "scs" is defined in cc.
X *
X * See the file "Changes" for more information on version-to-version changes.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <nlist.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <sys/param.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#if defined(scs)
X# define FLOAT /* for pctcpu in proc.h */
X#endif
X#include <sys/proc.h>
X#include <sys/dk.h>
X#include <sys/vm.h>
X
X/* includes specific to top */
X#include "layout.h"
X#include "screen.h" /* interface to screen package */
X#include "top.h"
X#include "top.local.h"
X#include "boolean.h"
X
X/* Special atoi routine returns either a non-negative number or one of: */
X#define Infinity -1
X#define Invalid -2
X
X/* Size of the stdio buffer given to stdout */
X#define Buffersize 2048
X
X/* The buffer the stdio will use */
Xchar stdoutbuf[Buffersize];
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 { "_cp_time" },
X#define X_CP_TIME 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 { "_total" },
X#define X_TOTAL 7
X#if defined(scs)
X { "_spt" },
X# define X_SPT 8
X#endif scs
X { 0 },
X};
X
X/* build Signal masks */
X#define Smask(s) (1 << ((s) - 1))
X
X/* for system errors */
Xextern int errno;
X
X/* for getopt: */
Xextern int optind;
Xextern char *optarg;
X
X/* signal handling routines */
Xint leave();
Xint onalrm();
Xint tstop();
X
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 mpid_offset;
Xlong avenrun_offset;
Xlong cp_time_offset;
Xlong total_offset;
X
X#if defined(sun)
Xlong ccpu;
Xlong avenrun[3];
X#else
Xdouble ccpu;
Xdouble avenrun[3];
X#endif
Xdouble logcpu;
X
Xstruct vmtotal total;
X
X#if defined(scs)
Xstruct spt *spt;
X#endif scs
X
Xstruct proc *proc;
Xstruct proc *pbase;
Xint bytes;
Xint initialized = No;
Xchar *myname = "top";
Xjmp_buf jmp_int;
Xchar input_waiting = No;
X
X/* routines that don't return int */
X
Xstruct passwd *getpwent();
Xchar *username();
Xchar *itoa();
Xchar *ctime();
Xchar *rindex();
Xchar *kill_procs();
Xchar *renice_procs();
X
Xint proc_compar();
Xlong time();
X
X/* different routines for displaying the user's identification */
X/* (values assigned to get_userid) */
Xchar *username();
Xchar *itoa7();
X
X/* display routines that need to be predeclared */
Xint i_loadave();
Xint u_loadave();
Xint i_procstates();
Xint u_procstates();
Xint i_cpustates();
Xint u_cpustates();
Xint i_memory();
Xint u_memory();
Xint i_header();
Xint u_header();
Xint i_process();
Xint u_process();
X
X/* pointers to display routines */
Xint (*d_loadave)() = i_loadave;
Xint (*d_procstates)() = i_procstates;
Xint (*d_cpustates)() = i_cpustates;
Xint (*d_memory)() = i_memory;
Xint (*d_header)() = i_header;
Xint (*d_process)() = i_process;
X
X/* buffer of proc information lines for display updating */
X/* unfortunate that this must be declared globally */
Xchar (* screenbuf)[Display_width];
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
X static struct proc **pref;
X static char tempbuf1[50];
X static char tempbuf2[50];
X int total_procs;
X int old_sigmask;
X int proc_brkdn[7];
X int topn = Default_TOPN;
X int delay = Default_DELAY;
X int displays = 0; /* indicates unspecified */
X long curr_time;
X char *(*get_userid)() = username;
X char *uname_field = "USERNAME";
X#ifndef FOUR_ONE
X char ch;
X char msg_showing = 0;
X int readfds;
X struct timeval timeout;
X#endif
X char dostates = No;
X char do_unames = Yes;
X char do_init = No;
X char interactive = Maybe;
X char show_sysprocs = No;
X char topn_specified = No;
X char warnings = 0;
X
X /* set the buffer for stdout */
X setbuffer(stdout, stdoutbuf, Buffersize);
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 /* process options */
X while ((i = getopt(argc, argv, "Sbinus:d:")) != EOF)
X {
X switch(i)
X {
X case 'u': /* display uid instead of name */
X do_unames = No;
X uname_field = " UID ";
X get_userid = itoa7;
X break;
X
X case 'S': /* show system processes */
X show_sysprocs = Yes;
X break;
X
X case 'i': /* go interactive regardless */
X interactive = Yes;
X break;
X
X case 'n': /* batch, or non-interactive */
X case 'b':
X interactive = No;
X break;
X
X case 'd': /* number of displays to show */
X if ((i = atoiwi(optarg)) == Invalid || i == 0)
X {
X fprintf(stderr,
X "%s: warning: display count should be positive -- option ignored\n",
X myname);
X warnings++;
X }
X else
X {
X displays = i;
X }
X break;
X
X case 's':
X if ((delay = atoi(optarg)) < 0)
X {
X fprintf(stderr,
X "%s: warning: seconds delay should be non-negative -- using default\n",
X myname);
X delay = Default_DELAY;
X warnings++;
X }
X break;
X
X default:
X fprintf(stderr,
X "Usage: %s [-Sbinu] [-d x] [-s x] [number]\n",
X myname);
X exit(1);
X }
X }
X
X /* get count of top processes to display (if any) */
X if (optind < argc)
X {
X if ((topn = atoiwi(argv[optind])) == Invalid)
X {
X fprintf(stderr,
X "%s: warning: process display count should be non-negative -- using default\n",
X myname);
X topn = Default_TOPN;
X warnings++;
X }
X else
X {
X topn_specified = Yes;
X }
X }
X
X /* initialize the kernel memory interface */
X init_kernel();
X
X if (initialized != 1)
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(2);
X }
X
X /* get the symbol values out of kmem */
X getkval(nlst[X_PROC].n_value, &proc, sizeof(int),
X nlst[X_PROC].n_name);
X getkval(nlst[X_NPROC].n_value, &nproc, sizeof(int),
X nlst[X_NPROC].n_name);
X getkval(nlst[X_HZ].n_value, &hz, sizeof(int),
X nlst[X_HZ].n_name);
X getkval(nlst[X_CCPU].n_value, &ccpu, sizeof ccpu,
X nlst[X_CCPU].n_name);
X#if defined(scs)
X getkval(nlst[X_SPT].n_value, &spt, sizeof(struct spt *),
X nlst[X_SPT].n_name);
X#endif scs
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 cp_time_offset = nlst[X_CP_TIME].n_value;
X total_offset = nlst[X_TOTAL].n_value;
X
X /* this is used in calculating WCPU -- calculate it ahead of time */
X#if defined(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(3);
X }
X
X /* initialize the hashing stuff */
X if (do_unames)
X {
X init_hash();
X }
X
X if (do_init)
X {
X initialized = 1;
X kill(0, SIGQUIT);
X exit(99);
X }
X }
X
X /* initialize termcap */
X init_termcap();
X
X /*
X * Smart terminals can only display so many processes, precisely
X * "screen_length - Header_lines". When run on dumb terminals, nothing
X * fancy is done anyway, so we can display as many processes as the
X * system can make. But since we never need to remember what is on the
X * screen, we only allocate a buffer for one screen line.
X */
X if (smart_terminal)
X {
X /* can only display (screen_length - Header_lines) processes */
X i = screen_length - Header_lines;
X if (topn > i) /* false even when topn == Infinity */
X {
X fprintf(stderr,
X "%s: warning: this terminal can only display %d processes.\n",
X myname, screen_length - Header_lines);
X topn = i;
X warnings++;
X }
X }
X else
X {
X i = 1;
X screen_length = nproc + Header_lines;
X }
X
X /* allocate space for the screen buffer */
X screenbuf = (char (*)[])sbrk(i * Display_width);
X if (screenbuf == (char (*)[])NULL)
X {
X fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
X exit(4);
X }
X
X /* adjust for topn == Infinity */
X if (topn == Infinity)
X {
X /*
X * For smart terminals, infinity really means everything that can
X * be displayed (which just happens to be "i" at this point).
X * On dumb terminals, infinity means every process in the system!
X * We only really want to do that if it was explicitly specified.
X * This is always the case when "Default_TOPN != Infinity". But if
X * topn wasn't explicitly specified and we are on a dumb terminal
X * and the default is Infinity, then (and only then) we use
X * "Nominal_TOPN" instead.
X */
X#if Default_TOPN == Infinity
X topn = smart_terminal ? i :
X (topn_specified ? nproc : Nominal_TOPN);
X#else
X topn = smart_terminal ? i : nproc;
X#endif
X }
X
X /* determine interactive state */
X if (interactive == Maybe)
X {
X interactive = smart_terminal;
X }
X
X /* if # of displays not specified, fill it in */
X if (displays == 0)
X {
X displays = smart_terminal ? Infinity : 1;
X }
X
X /* hold interrupt signals while setting up the screen and the handlers */
X#ifndef FOUR_ONE
X old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
X#endif
X init_screen();
X signal(SIGINT, leave);
X signal(SIGQUIT, leave);
X signal(SIGTSTP, tstop);
X#ifndef FOUR_ONE
X sigsetmask(old_sigmask);
X#endif
X if (warnings)
X {
X fprintf(stderr, "....");
X fflush(stderr); /* why must I do this? */
X sleep(3 * warnings);
X }
X clear();
X
X /* setup the jump buffer for stops */
X if (setjmp(jmp_int) != 0)
X {
X /* control ends up here after an interrupt */
X clear();
X reset_display();
X }
X
X /*
X * main loop -- repeat while display count is positive or while it
X * indicates infinity (by being -1)
X */
X
X while ((displays == -1) || (displays-- > 0))
X {
X /* read all the proc structures in one fell swoop */
X getkval(proc, pbase, bytes, "proc array");
X
X /* get the cp_time array */
X getkval(cp_time_offset, cp_time, sizeof(cp_time), "_cp_time");
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 /* get total -- systemwide main memory usage structure */
X getkval(total_offset, &total, sizeof(total), "_total");
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 /*
X * Place pointers to each valid proc structure in pref[].
X * Process slots that are actually in use have a non-zero
X * status field. Processes with SSYS set are system
X * processes---these get ignored unless show_sysprocs is set.
X */
X if (pp->p_stat != 0 &&
X (show_sysprocs || ((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 (*d_loadave)(mpid, avenrun);
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 if (smart_terminal)
X {
X Move_to(screen_width - 8, 0);
X }
X else
X {
X fputs(" ", stdout);
X }
X printf("%-8.8s\n", &(ctime(&curr_time)[11]));
X
X /* display process state breakdown */
X (*d_procstates)(total_procs, proc_brkdn);
X
X /* calculate percentage time in each cpu state */
X if (dostates) /* 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 cp_old[i] = cp_time[i];
X }
X (*d_cpustates)(cp_change, total_change);
X }
X else
X {
X /* we'll do it next time */
X if (smart_terminal)
X {
X z_cpustates();
X }
X else
X {
X putchar('\n');
X }
X dostates = Yes;
X
X /* remember the current values as "old" values */
X bcopy(cp_time, cp_old, sizeof(cp_time));
X }
X
X /* display main memory statistics */
X (*d_memory)(
X pagetok(total.t_rm), pagetok(total.t_arm),
X pagetok(total.t_vm), pagetok(total.t_avm),
X pagetok(total.t_free));
X
X i = 0;
X if (topn > 0)
X {
X /* update the header area */
X (*d_header)(uname_field);
X
X /* sort by cpu percentage (pctcpu) */
X qsort(pref, active_procs, sizeof(struct proc *), proc_compar);
X
X /* adjust for a lack of processes */
X if (active_procs > topn)
X {
X active_procs = topn;
X }
X
X /*
X * Now, show the top "n" processes. The method is slightly
X * different for dumb terminals, so we will just use two very
X * similar loops; this increases speed but also code size.
X */
X if (smart_terminal)
X {
X for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
X {
X pp = *prefp;
X (*d_process)(i, pp, get_userid);
X }
X }
X else for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
X {
X pp = *prefp;
X /* (only one buffer lien with dumb terminals) */
X (*d_process)(0, pp, get_userid);
X }
X }
X
X /* do end-screen processing */
X u_endscreen(i);
X
X /* now, flush the output buffer */
X fflush(stdout);
X
X /* only do the rest if we have more displays to show */
X if (displays)
X {
X /* switch out for new display on smart terminals */
X if (smart_terminal)
X {
X d_loadave = u_loadave;
X d_procstates = u_procstates;
X d_cpustates = u_cpustates;
X d_memory = u_memory;
X d_header = u_header;
X d_process = u_process;
X }
X
X#ifndef FOUR_ONE
X if (!interactive)
X#endif
X {
X /* set up alarm */
X signal(SIGALRM, onalrm);
X alarm(delay);
X
X /* wait for the rest of it .... */
X pause();
X }
X#ifndef FOUR_ONE
X else
X {
X /* wait for either input or the end of the delay period */
X readfds = 1; /* for standard input */
X timeout.tv_sec = delay;
X timeout.tv_usec = 0;
X if (select(32, &readfds, 0, 0, &timeout) > 0)
X {
X int newval;
X char *errmsg;
X
X /* something to read -- clear the message area first */
X if (msg_showing)
X {
X if (smart_terminal)
X {
X putcap(clear_line);
X }
X msg_showing = No;
X }
X
X /* now read it and act on it */
X read(0, &ch, 1);
X switch(ch)
X {
X case '\f': /* redraw screen */
X reset_display();
X clear();
X break;
X
X case ' ': /* merely update display */
X break;
X
X case 'q': /* quit */
X quit(0);
X break;
X
X case 'h': /* help */
X case '?':
X reset_display();
X clear();
X show_help();
X standout("Hit any key to continue: ");
X fflush(stdout);
X read(0, &ch, 1);
X clear();
X break;
X
X case 'e': /* show errors */
X if (error_count() == 0)
X {
X standout(" Currently no errors to report.");
X msg_showing = Yes;
X }
X else
X {
X reset_display();
X clear();
X show_errors();
X standout("Hit any key to continue: ");
X fflush(stdout);
X read(0, &ch, 1);
X clear();
X }
X break;
X
X case 'n': /* new number */
X case '#':
X standout("Number of processes to show: ");
X newval = readline(tempbuf1, 8, Yes);
X putchar('\r');
X if (newval > -1)
X {
X if (newval > (i = screen_length - Header_lines))
X {
X standout(
X " This terminal can only display %d processes.",
X i);
X newval = i;
X msg_showing = Yes;
X break;
X }
X
X if (newval > topn)
X {
X /* zero fill appropriate part of screenbuf */
X bzero(screenbuf[topn],
X (newval - topn) * Display_width);
X
X /* redraw header if need be */
X if (topn == 0)
X {
X d_header = i_header;
X }
X }
X topn = newval;
X }
X putcap(clear_line);
X break;
X
X case 's': /* new seconds delay */
X standout("Seconds to delay: ");
X if ((i = readline(tempbuf1, 8, Yes)) > -1)
X {
X delay = i;
X }
X putchar('\r');
X putcap(clear_line);
X break;
X
X case 'd': /* change display count */
X standout("Displays to show (currently %s): ",
X displays == -1 ? "infinite" :
X itoa(displays));
X if ((i = readline(tempbuf1, 10, Yes)) > 0)
X {
X displays = i;
X }
X else if (i == 0)
X {
X quit(0);
X }
X putchar('\r');
X putcap(clear_line);
X break;
X
X case 'k': /* kill program */
X fputs("kill ", stdout);
X if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
X {
X if ((errmsg = kill_procs(tempbuf2)) != NULL)
X {
X putchar('\r');
X standout(errmsg);
X }
X msg_showing = Yes;
X }
X else
X {
X putchar('\r');
X }
X putcap(clear_line);
X break;
X
X case 'r': /* renice program */
X fputs("renice ", stdout);
X if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
X {
X if ((errmsg = renice_procs(tempbuf2)) != NULL)
X {
X putchar('\r');
X standout(errmsg);
X msg_showing = Yes;
X }
X }
X else
X {
X putchar('\r');
X }
X putcap(clear_line);
X break;
X
X default:
X standout(" Command not understood");
X msg_showing = Yes;
X }
X }
X else if (msg_showing)
X {
X if (smart_terminal)
X {
X putcap(clear_line);
X }
X msg_showing = No;
X }
X }
X#endif
X }
X }
X
X quit(0);
X}
X
X/*
X * reset_display() - reset all the display routine pointers so that entire
X * screen will get redrawn.
X */
X
Xreset_display()
X
X{
X d_loadave = i_loadave;
X d_procstates = i_procstates;
X d_cpustates = i_cpustates;
X d_memory = i_memory;
X d_header = i_header;
X d_process = i_process;
X}
X
Xreadline(buffer, size, numeric)
X
Xchar *buffer;
Xint size;
Xint numeric;
X
X{
X register char *ptr = buffer;
X register char ch;
X register char cnt = 0;
X
X size -= 1;
X while ((fflush(stdout), read(0, ptr, 1) > 0))
X {
X if ((ch = *ptr) == '\n')
X {
X break;
X }
X
X if (ch == ch_kill)
X {
X *buffer = '\0';
X return(-1);
X }
X else if (ch == ch_erase)
X {
X if (cnt <= 0)
X {
X putchar('\7');
X }
X else
X {
X fputs("\b \b", stdout);
X ptr--;
X cnt--;
X }
X }
X else if (cnt == size || (numeric && (ch < '0' || ch > '9')))
X {
X putchar('\7');
X }
X else
X {
X putchar(ch);
X ptr++;
X cnt++;
X }
X }
X *ptr = '\0';
X return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
X}
X
X/*
X * signal handlers
X */
X
Xleave() /* exit under normal conditions -- INT handler */
X
X{
X end_screen();
X exit(0);
X}
X
Xtstop()
X
X{
X /* move to the lower left */
X end_screen();
X fflush(stdout);
X
X#ifdef FOUR_ONE /* a 4.1 system */
X
X /* send a STOP (uncatchable) to everyone in the process group */
X kill(0, SIGSTOP);
X
X /* reset the signal handler */
X signal(SIGTSTP, tstop);
X
X#else /* assume it is a 4.2 system */
X
X /* default the signal handler action */
X signal(SIGTSTP, SIG_DFL);
X
X /* unblock the signal and send ourselves one */
X sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
X kill(0, SIGTSTP);
X
X /* reset the signal handler */
X signal(SIGTSTP, tstop);
X
X#endif
X /* reinit screen */
X reinit_screen();
X
X /* jump to appropriate place */
X longjmp(jmp_int, 1);
X
X /*NOTREACHED*/
X}
X
Xquit(status) /* exit under duress */
X
Xint status;
X
X{
X end_screen();
X exit(status);
X}
X
Xonalrm()
X
X{
X return(0);
X}
X
X/*
X * proc_compar - comparison function for "qsort"
X * Compares the resource consumption of two processes using five
X * distinct keys. The keys (in descending order of importance) are:
X * percent cpu, cpu ticks, state, resident set size, total virtual
X * memory usage. The process states are ordered as follows (from least
X * to most important): WAIT, zombie, sleep, stop, start, run. The
X * array declaration below maps a process state index into a number
X * that reflects this ordering.
X */
X
Xunsigned char sorted_state[] =
X{
X 0, /* not used */
X 3, /* sleep */
X 1, /* ABANDONED (WAIT) */
X 6, /* run */
X 5, /* start */
X 2, /* zombie */
X 4 /* stop */
X};
X
Xproc_compar(pp1, pp2)
X
Xstruct proc **pp1;
Xstruct proc **pp2;
X
X{
X register struct proc *p1;
X register struct proc *p2;
X register int result;
X#if !defined(sun)
X register double dresult;
X#endif
X
X /* remove one level of indirection */
X p1 = *pp1;
X p2 = *pp2;
X
X /* compare percent cpu (pctcpu) */
X#if defined(sun)
X if ((result = p2->p_pctcpu - p1->p_pctcpu) == 0)
X#else
X if ((dresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
X#endif
X {
X /* use cpticks to break the tie */
X if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
X {
X /* use process state to break the tie */
X if ((result = sorted_state[p2->p_stat] -
X sorted_state[p1->p_stat]) == 0)
X {
X /* use priority to break the tie */
X if ((result = p2->p_pri - p1->p_pri) == 0)
X {
X /* use resident set size (rssize) to break the tie */
X#ifdef scs
X if ((result = p2->p_maxrss - p1->p_maxrss) == 0)
X#else scs
X if ((result = p2->p_rssize - p1->p_rssize) == 0)
X#endif scs
X
X {
X /* use total memory to break the tie */
X#ifdef pyr
X result = (p2->p_tsize + p2->p_dsize + p2->p_ussize) -
X (p1->p_tsize + p1->p_dsize + p1->p_ussize);
X#else pyr
X#ifdef scs
X result = (p2->p_tdsize + p2->p_ssize) -
X (p1->p_tdsize + p1->p_ssize);
X#else scs
X result = (p2->p_tsize + p2->p_dsize + p2->p_ssize) -
X (p1->p_tsize + p1->p_dsize + p1->p_ssize);
X#endif scs
X#endif pyr
X
X }
X }
X }
X }
X }
X#if !defined(sun)
X else
X {
X result = dresult < 0.0 ? -1 : 1;
X }
X#endif
X
X return(result);
X}
X
X/* routines to translate uids into a string */
X
Xchar *user_name(euid, ruid)
X
Xint euid, ruid;
X
X{
X return(username(euid));
X}
X
Xchar *user_uid(euid, ruid)
X
Xint euid, ruid;
X
X{
X return(itoa7(euid));
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
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 + 1) % 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(10);
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 + 1) % 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 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 return(enter_user(uid, itoa7(uid)));
X}
X
Xatoiwi(str)
X
Xchar *str;
X
X{
X register int len;
X
X len = strlen(str);
X if (len != 0)
X {
X if (strncmp(str, "infinity", len) == 0 ||
X strncmp(str, "all", len) == 0 ||
X strncmp(str, "maximum", len) == 0)
X {
X return(Infinity);
X }
X else if (str[0] == '-')
X {
X return(Invalid);
X }
X else
X {
X return(atoi(str));
X }
X }
X return(0);
X}
X
X/*
X * itoa - convert integer (decimal) to ascii string for positive numbers
X * only (we don't bother with negative numbers since we know we
X * don't use them).
X */
X
Xstatic char buffer[16]; /* shared by the next two routines */
X
Xchar *itoa(val)
X
Xregister int val;
X
X{
X register char *ptr;
X
X ptr = buffer + sizeof(buffer);
X *--ptr = '\0';
X if (val == 0)
X {
X *--ptr = '0';
X }
X else while (val != 0)
X {
X *--ptr = (val % 10) + '0';
X val /= 10;
X }
X return(ptr);
X}
X
X/*
X * itoa7(val) - like itoa, except the number is right justified in a 7
X * character field. This code is a duplication of itoa instead of
X * a front end to a more general routine for efficiency.
X */
X
Xchar *itoa7(val)
X
Xregister int val;
X
X{
X register char *ptr;
X
X ptr = buffer + sizeof(buffer);
X *--ptr = '\0';
X if (val == 0)
X {
X *--ptr = '0';
X }
X else while (val != 0)
X {
X *--ptr = (val % 10) + '0';
X val /= 10;
X }
X while (ptr > buffer + sizeof(buffer) - 7)
X {
X *--ptr = ' ';
X }
X return(ptr);
X}
X
END_OF_top.c
if test 26926 -ne `wc -c <top.c`; then
echo shar: \"top.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked both archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
Rich $alz "Anger is an energy"
Cronus Project, BBN Labs rsalz@bbn.com
Moderator, comp.sources.unix sources@uunet.uu.net