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