home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.muug.mb.ca
/
2014.06.ftp.muug.mb.ca.tar
/
ftp.muug.mb.ca
/
pub
/
src
/
top
/
top.c.orig
< prev
next >
Wrap
Text File
|
1992-05-26
|
30KB
|
1,306 lines
char *copyright =
"Top, version 2.5, copyright (c) 1984, 1988, William LeFebvre";
/*
* Top users display for Berkeley Unix
* Version 2.5
*
* This program may be freely redistributed to other Unix sites, but this
* entire comment MUST remain intact.
*
* Copyright (c) 1984, 1987, William LeFebvre, Rice University
*
* This program is designed to run on either Berkeley 4.1 or 4.2 Unix.
* Compile with the preprocessor constant "FOUR_ONE" set to get an
* executable that will run on Berkeley 4.1 Unix.
*
* The Sun kernel uses scaled integers instead of floating point.
* Compilation with the preprocessor variable "sun" gets an executable
* that will run on Sun Unix version 1.1 or later ("sun" is automatically
* set by the Sun C compiler).
*
* Version 4.0 of Sun Unix made some very major changes, including how a
* program reads the kernel's memory. In lieu of a preset preprocessor
* variable that would distinguish 4.0 from previous versions of Sun Unix,
* one must set the preprocessor variable "sunos4" to produce a working
* version for Sun Unix 4.0 and (hopefully) higher.
*
* The Pyramid splits the stack size (p_ssize) into control stack and user
* stack sizes. It also maintains one cp_time struct for each CPU, up to a
* maximum of NCPU. Compilation with the preprocessor variable "pyr" gets an
* executable that will run on Pyramids ("pyr" is automatically set by the
* Pyramid C compiler).
*
* The Symmetric 375 needs various goodies as well. "scs" is defined in
* cc. Thanks to Paul Vixie for providing Symmetrics specifics.
*
* See the file "Changes" for more information on version-to-version changes.
*/
#include <stdio.h>
#ifdef sunos4
#include <kvm.h>
#endif
#include <pwd.h>
#include <nlist.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <strings.h>
#include <sys/param.h>
#include <sys/dir.h>
#include <sys/user.h>
#ifdef scs
# define FLOAT /* for pctcpu in proc.h */
#endif
#include <sys/proc.h>
#include <sys/dk.h>
#include <sys/vm.h>
#ifdef pyr /* just for v4??? */
#include <sys/systm.h>
#endif
/* includes specific to top */
#include "layout.h"
#include "screen.h" /* interface to screen package */
#include "top.h"
#include "top.local.h"
#include "boolean.h"
/* Size of the stdio buffer given to stdout */
#define Buffersize 2048
#ifdef sunos4
extern kvm_t *kd; /* from kernel.c */
#endif
/* The buffer the stdio will use */
char stdoutbuf[Buffersize];
/*
* nlst: wish list for kernel symbols.
*
* I originally kept this in sorted order to make nlst faster. But a close
* look at the nlst code reveals that the order of symbols in the nlist
* array is immaterial. Architecture dependent symbols are placed at the
* end. Pyramid 4.0 needs "percpu" instead of "cp_time".
*/
struct nlist nlst[] = {
{ "_avenrun" }, /* all machines need these */
#define X_AVENRUN 0
{ "_ccpu" },
#define X_CCPU 1
{ "_hz" },
#define X_HZ 2
{ "_mpid" },
#define X_MPID 3
{ "_nproc" },
#define X_NPROC 4
{ "_proc" },
#define X_PROC 5
{ "_total" },
#define X_TOTAL 6
#if defined(pyr) && defined(CPUFOUND)
{ "_percpu" }, /* only for Pyramid 4.0 */
#define X_PERCPU 7
{ "_maxcpu" }, /* only for Pyramid 4.0 */
#define X_MAXCPU 8
#else
{ "_cp_time" }, /* all except Pyramid 4.0 */
#define X_CP_TIME 7
#endif
#ifdef scs
{ "_spt" }, /* only for SCS 375 */
#define X_SPT 8
#endif scs
{ 0 },
};
/* build Signal masks */
#define Smask(s) (1 << ((s) - 1))
/* for system errors */
extern int errno;
/* for getopt: */
extern int optind;
extern char *optarg;
/* imported from screen.c */
extern int overstrike;
/* signal handling routines */
int leave();
int onalrm();
int tstop();
int nproc;
int mpid;
/* kernel "hz" variable -- clock rate */
long hz;
/* All this is to calculate the cpu state percentages */
long cp_time[CPUSTATES];
long cp_old[CPUSTATES];
long cp_change[CPUSTATES];
long total_change;
unsigned long mpid_offset;
unsigned long avenrun_offset;
unsigned long total_offset;
#if defined(pyr) && defined(CPUFOUND)
unsigned long percpu_offset;
struct percpu percpu[NCPU];
int maxcpu = 0;
#else
unsigned long cp_time_offset;
#endif
#ifdef sun
long ccpu;
long avenrun[3];
#else
double ccpu;
double avenrun[3];
#endif
double logcpu;
struct vmtotal total;
#ifdef scs
struct spt *spt;
#endif scs
unsigned long proc;
struct proc *pbase;
int bytes;
char *myname = "top";
jmp_buf jmp_int;
/* system routines that don't return int */
caddr_t sbrk();
/* routines that don't return int */
char *username();
char *itoa();
char *ctime();
char *rindex();
char *kill_procs();
char *renice_procs();
int proc_compar();
long time();
/* different routines for displaying the user's identification */
/* (values assigned to get_userid) */
char *username();
char *itoa7();
/* display routines that need to be predeclared */
int i_loadave();
int u_loadave();
int i_procstates();
int u_procstates();
int i_cpustates();
int u_cpustates();
int i_memory();
int u_memory();
int i_message();
int u_message();
int i_header();
int u_header();
int i_process();
int u_process();
/* pointers to display routines */
int (*d_loadave)() = i_loadave;
int (*d_procstates)() = i_procstates;
int (*d_cpustates)() = i_cpustates;
int (*d_memory)() = i_memory;
int (*d_message)() = i_message;
int (*d_header)() = i_header;
int (*d_process)() = i_process;
/* buffer of proc information lines for display updating */
/* unfortunate that this must be declared globally */
char (* screenbuf)[Display_width];
main(argc, argv)
int argc;
char *argv[];
{
register struct proc *pp;
register struct proc **prefp;
register int i;
register int active_procs;
register int change;
register struct nlist *nlstp;
static struct proc **pref;
static char tempbuf1[50];
static char tempbuf2[50];
int total_procs;
int old_sigmask;
int proc_brkdn[7];
int topn = Default_TOPN;
int delay = Default_DELAY;
int displays = 0; /* indicates unspecified */
long curr_time;
char *(*get_userid)() = username;
char *uname_field = "USERNAME";
char dostates = No;
char do_unames = Yes;
char interactive = Maybe;
char show_sysprocs = No;
char warnings = 0;
#if Default_TOPN == Infinity
char topn_specified = No;
#endif
#if defined(pyr) && defined(CPUFOUND)
int ncpu;
#endif
#ifndef FOUR_ONE /* this is all used in processing commands */
char ch;
char *iptr;
char no_command = 1;
int readfds;
struct timeval timeout;
static char command_chars[] = "\f qh?en#sdkr";
/* these defines enumerate the indexes of the commands in command_chars */
#define CMD_redraw 0
#define CMD_update 1
#define CMD_quit 2
#define CMD_help1 3
#define CMD_help2 4
#define CMD_OSLIMIT 4 /* terminals with OS can only handle commands */
#define CMD_errors 5 /* less tha nor equal to CMD_OSLIMIT */
#define CMD_number1 6
#define CMD_number2 7
#define CMD_delay 8
#define CMD_displays 9
#define CMD_kill 10
#define CMD_renice 11
#endif
/* set the buffer for stdout */
setbuffer(stdout, stdoutbuf, Buffersize);
/* get our name */
if (argc > 0)
{
if ((myname = rindex(argv[0], '/')) == 0)
{
myname = argv[0];
}
else
{
myname++;
}
}
/* process options */
while ((i = getopt(argc, argv, "Sbinqus:d:")) != EOF)
{
switch(i)
{
case 'u': /* display uid instead of name */
do_unames = No;
uname_field = " UID ";
get_userid = itoa7;
break;
case 'S': /* show system processes */
show_sysprocs = Yes;
break;
case 'i': /* go interactive regardless */
interactive = Yes;
break;
case 'n': /* batch, or non-interactive */
case 'b':
interactive = No;
break;
case 'd': /* number of displays to show */
if ((i = atoiwi(optarg)) == Invalid || i == 0)
{
fprintf(stderr,
"%s: warning: display count should be positive -- option ignored\n",
myname);
warnings++;
}
else
{
displays = i;
}
break;
case 's':
if ((delay = atoi(optarg)) < 0)
{
fprintf(stderr,
"%s: warning: seconds delay should be non-negative -- using default\n",
myname);
delay = Default_DELAY;
warnings++;
}
break;
case 'q': /* be quick about it */
/* only allow this if user is really root */
if (getuid() == 0)
{
/* be very un-nice! */
(void) nice(-20);
}
else
{
fprintf(stderr,
"%s: warning: `-q' option can only be used by root\n",
myname);
warnings++;
}
break;
default:
fprintf(stderr,
"Usage: %s [-Sbinqu] [-d x] [-s x] [number]\n",
myname);
exit(1);
}
}
/* get count of top processes to display (if any) */
if (optind < argc)
{
if ((topn = atoiwi(argv[optind])) == Invalid)
{
fprintf(stderr,
"%s: warning: process display count should be non-negative -- using default\n",
myname);
topn = Default_TOPN;
warnings++;
}
#if Default_TOPN == Infinity
else
{
topn_specified = Yes;
}
#endif
}
/* initialize the kernel memory interface */
init_kernel();
/* get the list of symbols we want to access in the kernel */
/* errno = 0; ??? */
#ifdef sunos4
if (i = kvm_nlist(kd, nlst))
{
if (i < 0)
{
fprintf(stderr, "top: can't nlist image\n");
exit(2);
}
else
{
fprintf(stderr, "top: can't nlist %d symbols\n", i);
exit(2);
}
}
#else
(void) nlist(VMUNIX, nlst);
if (nlst[0].n_type == 0)
{
fprintf(stderr, "%s: can't nlist image\n", VMUNIX);
exit(2);
}
#endif
/* did we get ALL of them? */
i = 0;
for (nlstp = nlst; nlstp->n_name != NULL; nlstp++)
{
if (nlstp->n_type == 0)
{
/* this one wasn't found */
fprintf(stderr, "%s: no symbol named `%s'\n", VMUNIX,
nlstp->n_name);
i = 1;
}
}
if (i)
{
/* at least one was not found -- abort */
exit(5);
}
/* get the symbol values out of kmem */
(void) getkval(nlst[X_PROC].n_value, (int *)(&proc), sizeof(proc),
nlst[X_PROC].n_name);
(void) getkval(nlst[X_NPROC].n_value, &nproc, sizeof(nproc),
nlst[X_NPROC].n_name);
(void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
nlst[X_HZ].n_name);
(void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu), sizeof(ccpu),
nlst[X_CCPU].n_name);
#if defined(pyr) && defined(CPUFOUND)
(void) getkval(nlst[X_MAXCPU].n_value, &maxcpu, sizeof(maxcpu),
nlst[X_MAXCPU].n_name);
#endif
#ifdef scs
(void) getkval(nlst[X_SPT].n_value, (int *)(&spt), sizeof(struct spt *),
nlst[X_SPT].n_name);
#endif scs
/* some calculations we use later */
mpid_offset = nlst[X_MPID].n_value;
avenrun_offset = nlst[X_AVENRUN].n_value;
total_offset = nlst[X_TOTAL].n_value;
#if defined(pyr) && defined(CPUFOUND)
percpu_offset = nlst[X_PERCPU].n_value;
#else
cp_time_offset = nlst[X_CP_TIME].n_value;
#endif
/* this is used in calculating WCPU -- calculate it ahead of time */
#ifdef sun
logcpu = log((double)ccpu / FSCALE);
#else
logcpu = log(ccpu);
#endif
/* allocate space for proc structure array and array of pointers */
bytes = nproc * sizeof(struct proc);
pbase = (struct proc *)sbrk(bytes);
pref = (struct proc **)sbrk(nproc * sizeof(struct proc *));
/* Just in case ... */
if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
{
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
exit(3);
}
/* initialize the hashing stuff */
if (do_unames)
{
init_hash();
}
/* initialize termcap */
init_termcap();
/*
* Smart terminals can only display so many processes, precisely
* "screen_length - Header_lines". When run on dumb terminals, nothing
* fancy is done anyway, so we can display as many processes as the
* system can make. But since we never need to remember what is on the
* screen, we only allocate a buffer for one screen line.
*/
if (smart_terminal)
{
/* can only display (screen_length - Header_lines) processes */
i = screen_length - Header_lines;
if (topn > i) /* false even when topn == Infinity */
{
fprintf(stderr,
"%s: warning: this terminal can only display %d processes.\n",
myname, screen_length - Header_lines);
topn = i;
warnings++;
}
}
else
{
i = 1;
screen_length = nproc + Header_lines;
}
/* allocate space for the screen buffer */
screenbuf = (char (*)[Display_width])sbrk(i * Display_width);
if (screenbuf == (char (*)[Display_width])NULL)
{
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
exit(4);
}
/* adjust for topn == Infinity */
if (topn == Infinity)
{
/*
* For smart terminals, infinity really means everything that can
* be displayed (which just happens to be "i" at this point).
* On dumb terminals, infinity means every process in the system!
* We only really want to do that if it was explicitly specified.
* This is always the case when "Default_TOPN != Infinity". But if
* topn wasn't explicitly specified and we are on a dumb terminal
* and the default is Infinity, then (and only then) we use
* "Nominal_TOPN" instead.
*/
#if Default_TOPN == Infinity
topn = smart_terminal ? i :
(topn_specified ? nproc : Nominal_TOPN);
#else
topn = smart_terminal ? i : nproc;
#endif
}
/* determine interactive state */
if (interactive == Maybe)
{
interactive = smart_terminal;
}
/* if # of displays not specified, fill it in */
if (displays == 0)
{
displays = smart_terminal ? Infinity : 1;
}
/* hold interrupt signals while setting up the screen and the handlers */
#ifndef FOUR_ONE
old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
#endif
init_screen();
(void) signal(SIGINT, leave);
(void) signal(SIGQUIT, leave);
(void) signal(SIGTSTP, tstop);
#ifndef FOUR_ONE
(void) sigsetmask(old_sigmask);
#endif
if (warnings)
{
fprintf(stderr, "....");
fflush(stderr); /* why must I do this? */
sleep((unsigned)(3 * warnings));
}
/* setup the jump buffer for stops */
if (setjmp(jmp_int) != 0)
{
/* control ends up here after an interrupt */
reset_display();
}
/*
* main loop -- repeat while display count is positive or while it
* indicates infinity (by being -1)
*/
while ((displays == -1) || (displays-- > 0))
{
/* read all the proc structures in one fell swoop */
#ifdef sunos4
/* should use kvm_getproc() */
#endif
(void) getkval(proc, (int *)pbase, bytes, "proc array");
/* get the cp_time array */
#if defined(pyr) && defined(CPUFOUND)
(void) getkval(percpu_offset, percpu, sizeof(percpu), "_percpu");
for (i = 0; i < CPUSTATES; ++i)
cp_time[i] = 0;
for (ncpu = 0; ncpu <= maxcpu; ++ncpu)
{
if (CPUFOUND(ncpu))
{
for (i = 0; i < CPUSTATES; ++i)
cp_time[i] += CP_TIME(ncpu)[i];
}
}
#else
(void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
"_cp_time");
#endif
/* get load average array */
(void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
"_avenrun");
/* get mpid -- process id of last process */
(void) getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid");
/* get total -- systemwide main memory usage structure */
(void) getkval(total_offset, (int *)(&total), sizeof(total),
"_total");
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
bzero((char *)proc_brkdn, sizeof(proc_brkdn));
prefp = pref;
for (pp = pbase, i = 0; i < nproc; pp++, i++)
{
/*
* Place pointers to each valid proc structure in pref[].
* Process slots that are actually in use have a non-zero
* status field. Processes with SSYS set are system
* processes---these get ignored unless show_sysprocs is set.
*/
if (pp->p_stat != 0 &&
(show_sysprocs || ((pp->p_flag & SSYS) == 0)))
{
total_procs++;
proc_brkdn[pp->p_stat]++;
if (pp->p_stat != SZOMB)
{
*prefp++ = pp;
active_procs++;
}
}
}
/* display the load averages */
(*d_loadave)(mpid, avenrun);
/*
* Display the current time.
* "ctime" always returns a string that looks like this:
*
* Sun Sep 16 01:03:52 1973
* 012345678901234567890123
* 1 2
*
* We want indices 11 thru 18 (length 8).
*/
curr_time = time((long *)NULL);
if (smart_terminal)
{
Move_to(screen_width - 8, 0);
}
else
{
fputs(" ", stdout);
}
printf("%-8.8s\n", &(ctime(&curr_time)[11]));
/* display process state breakdown */
(*d_procstates)(total_procs, proc_brkdn);
/* calculate percentage time in each cpu state */
if (dostates) /* but not the first time */
{
total_change = 0;
for (i = 0; i < CPUSTATES; i++)
{
/* calculate changes for each state and overall change */
if (cp_time[i] < cp_old[i])
{
/* this only happens when the counter wraps */
change = (int)
((unsigned long)cp_time[i]-(unsigned long)cp_old[i]);
}
else
{
change = cp_time[i] - cp_old[i];
}
total_change += (cp_change[i] = change);
cp_old[i] = cp_time[i];
}
(*d_cpustates)(cp_change, total_change);
}
else
{
/* we'll do it next time */
if (smart_terminal)
{
z_cpustates();
}
else
{
putchar('\n');
}
dostates = Yes;
/* remember the current values as "old" values */
bcopy((char *)cp_time, (char *)cp_old, sizeof(cp_time));
}
/* display main memory statistics */
#ifdef sunos4
/*
* Note that total.t_vm and total.t_avm are unused in sunOS 4.0. As such,
* they get reported as zero.
*/
#endif
(*d_memory)(
pagetok(total.t_rm), pagetok(total.t_arm),
pagetok(total.t_vm), pagetok(total.t_avm),
pagetok(total.t_free));
/* handle message area */
(*d_message)();
i = 0;
if (topn > 0)
{
/* update the header area */
(*d_header)(uname_field);
/* sort by cpu percentage (pctcpu) */
qsort((char *)pref, active_procs,
sizeof(struct proc *), proc_compar);
/* adjust for a lack of processes */
if (active_procs > topn)
{
active_procs = topn;
}
/*
* Now, show the top "n" processes. The method is slightly
* different for dumb terminals, so we will just use two very
* similar loops; this increases speed but also code size.
*/
if (smart_terminal)
{
for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
{
pp = *prefp;
(*d_process)(i, pp, get_userid);
}
}
else for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
{
pp = *prefp;
/* (only one buffer lien with dumb terminals) */
(*d_process)(0, pp, get_userid);
}
}
/* do end-screen processing */
u_endscreen(i);
/* now, flush the output buffer */
fflush(stdout);
/* only do the rest if we have more displays to show */
if (displays)
{
/* switch out for new display on smart terminals */
if (smart_terminal)
{
if (overstrike)
{
reset_display();
}
else
{
d_loadave = u_loadave;
d_procstates = u_procstates;
d_cpustates = u_cpustates;
d_memory = u_memory;
d_message = u_message;
d_header = u_header;
d_process = u_process;
}
}
#ifndef FOUR_ONE
no_command = Yes;
if (!interactive)
#endif
{
/* set up alarm */
(void) signal(SIGALRM, onalrm);
(void) alarm((unsigned)delay);
/* wait for the rest of it .... */
pause();
}
#ifndef FOUR_ONE
else while (no_command)
{
/* assume valid command unless told otherwise */
no_command = No;
/* set up arguments for select with timeout */
readfds = 1; /* for standard input */
timeout.tv_sec = delay;
timeout.tv_usec = 0;
/* wait for either input or the end of the delay period */
if (select(32, &readfds, (int *)NULL, (int *)NULL, &timeout) > 0)
{
int newval;
char *errmsg;
/* something to read -- clear the message area first */
clear_message();
/* now read it and convert to command index */
/* (use "change" as a temporary to hold index) */
(void) read(0, &ch, 1);
if ((iptr = index(command_chars, ch)) == NULL)
{
/* illegal command */
new_message(1, " Command not understood");
putchar('\r');
no_command = Yes;
}
else
{
change = iptr - command_chars;
if (overstrike && change > CMD_OSLIMIT)
{
/* error */
new_message(1,
" Command cannot be handled by this terminal");
putchar('\r');
no_command = Yes;
}
else switch(change)
{
case CMD_redraw: /* redraw screen */
reset_display();
break;
case CMD_update: /* merely update display */
/* is the load average high? */
#ifdef sun
if (avenrun[0] > (int)(LoadMax * FSCALE))
#else
if (avenrun[0] > LoadMax)
#endif
{
/* yes, go home for visual feedback */
go_home();
fflush(stdout);
}
break;
case CMD_quit: /* quit */
quit(0);
/*NOTREACHED*/
break;
case CMD_help1: /* help */
case CMD_help2:
reset_display();
clear();
show_help();
standout("Hit any key to continue: ");
fflush(stdout);
(void) read(0, &ch, 1);
break;
case CMD_errors: /* show errors */
if (error_count() == 0)
{
new_message(1,
" Currently no errors to report.");
putchar('\r');
no_command = Yes;
}
else
{
reset_display();
clear();
show_errors();
standout("Hit any key to continue: ");
fflush(stdout);
(void) read(0, &ch, 1);
}
break;
case CMD_number1: /* new number */
case CMD_number2:
new_message(1,
"Number of processes to show: ");
newval = readline(tempbuf1, 8, Yes);
if (newval > -1)
{
if (newval > (i = screen_length - Header_lines))
{
new_message(1,
" This terminal can only display %d processes.",
i);
newval = i;
no_command = Yes;
putchar('\r');
break;
}
if (newval > topn)
{
/* zero fill appropriate part of screenbuf */
bzero(screenbuf[topn],
(newval - topn) * Display_width);
/* redraw header if need be */
if (topn == 0)
{
d_header = i_header;
}
}
topn = newval;
}
break;
case CMD_delay: /* new seconds delay */
new_message(1, "Seconds to delay: ");
if ((i = readline(tempbuf1, 8, Yes)) > -1)
{
delay = i;
}
clear_message();
break;
case CMD_displays: /* change display count */
new_message(1,
"Displays to show (currently %s): ",
displays == -1 ? "infinite" :
itoa(displays));
if ((i = readline(tempbuf1, 10, Yes)) > 0)
{
displays = i;
}
else if (i == 0)
{
quit(0);
}
clear_message();
break;
case CMD_kill: /* kill program */
new_message(0, "kill ");
if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
{
if ((errmsg = kill_procs(tempbuf2)) != NULL)
{
new_message(1, errmsg);
putchar('\r');
no_command = Yes;
}
}
else
{
clear_message();
}
break;
case CMD_renice: /* renice program */
new_message(0, "renice ");
if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
{
if ((errmsg = renice_procs(tempbuf2)) != NULL)
{
new_message(1, errmsg);
putchar('\r');
no_command = Yes;
}
}
else
{
clear_message();
}
break;
default:
new_message(1, " BAD CASE IN SWITCH!");
putchar('\r');
}
}
/* flush out stuff that may have been written */
fflush(stdout);
}
}
#endif
}
}
quit(0);
}
/*
* reset_display() - reset all the display routine pointers so that entire
* screen will get redrawn.
*/
reset_display()
{
d_loadave = i_loadave;
d_procstates = i_procstates;
d_cpustates = i_cpustates;
d_memory = i_memory;
d_message = i_message;
d_header = i_header;
d_process = i_process;
}
/*
* signal handlers
*/
leave() /* exit under normal conditions -- INT handler */
{
end_screen();
exit(0);
}
tstop()
{
/* move to the lower left */
end_screen();
fflush(stdout);
#ifdef FOUR_ONE /* a 4.1 system */
/* send a STOP (uncatchable) to everyone in the process group */
(void) kill(0, SIGSTOP);
/* reset the signal handler */
(void) signal(SIGTSTP, tstop);
#else /* assume it is a 4.2 system */
/* default the signal handler action */
(void) signal(SIGTSTP, SIG_DFL);
/* unblock the signal and send ourselves one */
(void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
(void) kill(0, SIGTSTP);
/* reset the signal handler */
(void) signal(SIGTSTP, tstop);
#endif
/* reinit screen */
reinit_screen();
/* jump to appropriate place */
longjmp(jmp_int, 1);
/*NOTREACHED*/
}
quit(status) /* exit under duress */
int status;
{
end_screen();
exit(status);
/*NOTREACHED*/
}
onalrm()
{
return(0);
}
/*
* proc_compar - comparison function for "qsort"
* Compares the resource consumption of two processes using five
* distinct keys. The keys (in descending order of importance) are:
* percent cpu, cpu ticks, state, resident set size, total virtual
* memory usage. The process states are ordered as follows (from least
* to most important): WAIT, zombie, sleep, stop, start, run. The
* array declaration below maps a process state index into a number
* that reflects this ordering.
*/
unsigned char sorted_state[] =
{
0, /* not used */
3, /* sleep */
1, /* ABANDONED (WAIT) */
6, /* run */
5, /* start */
2, /* zombie */
4 /* stop */
};
proc_compar(pp1, pp2)
struct proc **pp1;
struct proc **pp2;
{
register struct proc *p1;
register struct proc *p2;
register int result;
#ifndef sun
register double dresult;
#endif
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/* compare percent cpu (pctcpu) */
#ifdef sun
if ((result = p2->p_pctcpu - p1->p_pctcpu) == 0)
#else
if ((dresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
#endif
{
/* use cpticks to break the tie */
if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
{
/* use process state to break the tie */
if ((result = sorted_state[p2->p_stat] -
sorted_state[p1->p_stat]) == 0)
{
/* use priority to break the tie */
if ((result = p2->p_pri - p1->p_pri) == 0)
{
/* use resident set size (rssize) to break the tie */
#ifdef scs
if ((result = p2->p_maxrss - p1->p_maxrss) == 0)
#else scs
if ((result = p2->p_rssize - p1->p_rssize) == 0)
#endif scs
{
/* use total memory to break the tie */
#ifdef pyr
result = (p2->p_tsize + p2->p_dsize + p2->p_ussize) -
(p1->p_tsize + p1->p_dsize + p1->p_ussize);
#else !pyr
#ifdef scs
result = (p2->p_tdsize + p2->p_ssize) -
(p1->p_tdsize + p1->p_ssize);
#else !scs
result = (p2->p_tsize + p2->p_dsize + p2->p_ssize) -
(p1->p_tsize + p1->p_dsize + p1->p_ssize);
#endif scs
#endif pyr
}
}
}
}
}
#ifndef sun
else
{
result = dresult < 0.0 ? -1 : 1;
}
#endif
return(result);
}
/*
* These routines handle uid to username mapping.
* They use a hashing table scheme to reduce reading overhead.
*/
struct hash_el {
int uid;
char name[8];
};
#define H_empty -1
/* simple minded hashing function */
#define hashit(i) ((i) % Table_size)
struct hash_el hash_table[Table_size];
init_hash()
{
register int i;
register struct hash_el *h;
for (h = hash_table, i = 0; i < Table_size; h++, i++)
{
h->uid = H_empty;
}
}
char *username(uid)
register int uid;
{
register int hashindex;
register int found;
/* This is incredibly naive, but it'll probably get changed anyway */
hashindex = hashit(uid);
while ((found = hash_table[hashindex].uid) != uid)
{
if (found == H_empty)
{
/* not here -- get it out of passwd */
hashindex = get_user(uid);
break; /* out of while */
}
hashindex = (hashindex + 1) % Table_size;
}
return(hash_table[hashindex].name);
}
enter_user(uid, name)
register int uid;
register char *name;
{
register int hashindex;
register int try;
static int uid_count = 0;
/* avoid table overflow -- insure at least one empty slot */
if (++uid_count >= Table_size)
{
fprintf(stderr, "table overflow: too many users\n");
quit(10);
}
hashindex = hashit(uid);
while ((try = hash_table[hashindex].uid) != H_empty)
{
if (try == uid)
{
return(hashindex);
}
hashindex = (hashindex + 1) % Table_size;
}
hash_table[hashindex].uid = uid;
(void) strncpy(hash_table[hashindex].name, name, 8);
return(hashindex);
}
get_user(uid)
register int uid;
{
struct passwd *pwd;
register int last_index;
#ifdef RANDOM_PW
if ((pwd = getpwuid(uid)) != NULL)
#else
while ((pwd = getpwent()) != NULL)
#endif
{
last_index = enter_user(pwd->pw_uid, pwd->pw_name);
#ifndef RANDOM_PW
if (pwd->pw_uid == uid)
#endif
{
return(last_index);
}
}
return(enter_user(uid, itoa7(uid)));
}