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 >
Text File  |  1992-05-26  |  30KB  |  1,306 lines

  1. char *copyright =
  2.     "Top, version 2.5, copyright (c) 1984, 1988, William LeFebvre";
  3.  
  4. /*
  5.  *  Top users display for Berkeley Unix
  6.  *  Version 2.5
  7.  *
  8.  *  This program may be freely redistributed to other Unix sites, but this
  9.  *  entire comment MUST remain intact.
  10.  *
  11.  *  Copyright (c) 1984, 1987, William LeFebvre, Rice University
  12.  *
  13.  *  This program is designed to run on either Berkeley 4.1 or 4.2 Unix.
  14.  *  Compile with the preprocessor constant "FOUR_ONE" set to get an
  15.  *  executable that will run on Berkeley 4.1 Unix.
  16.  *
  17.  *  The Sun kernel uses scaled integers instead of floating point.
  18.  *  Compilation with the preprocessor variable "sun" gets an executable
  19.  *  that will run on Sun Unix version 1.1 or later ("sun" is automatically
  20.  *  set by the Sun C compiler).
  21.  *
  22.  *  Version 4.0 of Sun Unix made some very major changes, including how a
  23.  *  program reads the kernel's memory.  In lieu of a preset preprocessor
  24.  *  variable that would distinguish 4.0 from previous versions of Sun Unix,
  25.  *  one must set the preprocessor variable "sunos4" to produce a working
  26.  *  version for Sun Unix 4.0 and (hopefully) higher.
  27.  *
  28.  *  The Pyramid splits the stack size (p_ssize) into control stack and user
  29.  *  stack sizes. It also maintains one cp_time struct for each CPU, up to a
  30.  *  maximum of NCPU. Compilation with the preprocessor variable "pyr" gets an
  31.  *  executable that will run on Pyramids ("pyr" is automatically set by the
  32.  *  Pyramid C compiler).
  33.  *
  34.  *  The Symmetric 375 needs various goodies as well.  "scs" is defined in
  35.  *  cc.  Thanks to Paul Vixie for providing Symmetrics specifics.
  36.  *
  37.  *  See the file "Changes" for more information on version-to-version changes.
  38.  */
  39.  
  40. #include <stdio.h>
  41. #ifdef sunos4
  42. #include <kvm.h>
  43. #endif
  44. #include <pwd.h>
  45. #include <nlist.h>
  46. #include <signal.h>
  47. #include <setjmp.h>
  48. #include <ctype.h>
  49. #include <strings.h>
  50. #include <sys/param.h>
  51. #include <sys/dir.h>
  52. #include <sys/user.h>
  53. #ifdef scs
  54. # define FLOAT        /* for pctcpu in proc.h */
  55. #endif
  56. #include <sys/proc.h>
  57. #include <sys/dk.h>
  58. #include <sys/vm.h>
  59. #ifdef pyr        /* just for v4??? */
  60. #include <sys/systm.h>
  61. #endif
  62.  
  63. /* includes specific to top */
  64. #include "layout.h"
  65. #include "screen.h"        /* interface to screen package */
  66. #include "top.h"
  67. #include "top.local.h"
  68. #include "boolean.h"
  69.  
  70. /* Size of the stdio buffer given to stdout */
  71. #define Buffersize    2048
  72.  
  73. #ifdef sunos4
  74. extern kvm_t *kd;        /* from kernel.c */
  75. #endif
  76.  
  77. /* The buffer the stdio will use */
  78. char stdoutbuf[Buffersize];
  79.  
  80. /*
  81.  *  nlst: wish list for kernel symbols.
  82.  *
  83.  *  I originally kept this in sorted order to make nlst faster.  But a close
  84.  *  look at the nlst code reveals that the order of symbols in the nlist
  85.  *  array is immaterial.  Architecture dependent symbols are placed at the
  86.  *  end.  Pyramid 4.0 needs "percpu" instead of "cp_time".
  87.  */
  88.  
  89. struct nlist nlst[] = {
  90.     { "_avenrun" },        /* all machines need these */
  91. #define X_AVENRUN    0
  92.     { "_ccpu" },
  93. #define X_CCPU        1
  94.     { "_hz" },
  95. #define X_HZ        2
  96.     { "_mpid" },
  97. #define X_MPID        3
  98.     { "_nproc" },
  99. #define X_NPROC        4
  100.     { "_proc" },
  101. #define X_PROC        5
  102.     { "_total" },
  103. #define X_TOTAL        6
  104. #if defined(pyr) && defined(CPUFOUND)
  105.     { "_percpu" },            /* only for Pyramid 4.0 */
  106. #define X_PERCPU    7
  107.     { "_maxcpu" },            /* only for Pyramid 4.0 */
  108. #define X_MAXCPU    8
  109. #else
  110.     { "_cp_time" },            /* all except Pyramid 4.0 */
  111. #define X_CP_TIME    7
  112. #endif
  113. #ifdef scs
  114.     { "_spt" },                /* only for SCS 375 */
  115. #define X_SPT        8
  116. #endif scs
  117.     { 0 },
  118. };
  119.  
  120. /* build Signal masks */
  121. #define Smask(s)    (1 << ((s) - 1))
  122.  
  123. /* for system errors */
  124. extern int errno;
  125.  
  126. /* for getopt: */
  127. extern int  optind;
  128. extern char *optarg;
  129.  
  130. /* imported from screen.c */
  131. extern int overstrike;
  132.  
  133. /* signal handling routines */
  134. int leave();
  135. int onalrm();
  136. int tstop();
  137.  
  138. int nproc;
  139. int mpid;
  140.  
  141. /* kernel "hz" variable -- clock rate */
  142. long hz;
  143.  
  144. /* All this is to calculate the cpu state percentages */
  145.  
  146.      long cp_time[CPUSTATES];
  147.      long cp_old[CPUSTATES];
  148.      long cp_change[CPUSTATES];
  149.      long total_change;
  150. unsigned long mpid_offset;
  151. unsigned long avenrun_offset;
  152. unsigned long total_offset;
  153.  
  154. #if defined(pyr) && defined(CPUFOUND)
  155. unsigned long percpu_offset;
  156.        struct percpu percpu[NCPU];
  157.           int maxcpu = 0;
  158. #else
  159. unsigned long cp_time_offset;
  160. #endif
  161.  
  162. #ifdef sun
  163. long ccpu;
  164. long avenrun[3];
  165. #else
  166. double ccpu;
  167. double avenrun[3];
  168. #endif
  169. double logcpu;
  170.  
  171. struct vmtotal total;
  172.  
  173. #ifdef scs
  174. struct spt *spt;
  175. #endif scs
  176.  
  177. unsigned long proc;
  178. struct proc *pbase;
  179. int bytes;
  180. char *myname = "top";
  181. jmp_buf jmp_int;
  182.  
  183. /* system routines that don't return int */
  184.  
  185. caddr_t sbrk();
  186.  
  187. /* routines that don't return int */
  188.  
  189. char *username();
  190. char *itoa();
  191. char *ctime();
  192. char *rindex();
  193. char *kill_procs();
  194. char *renice_procs();
  195.  
  196. int proc_compar();
  197. long time();
  198.  
  199. /* different routines for displaying the user's identification */
  200. /* (values assigned to get_userid) */
  201. char *username();
  202. char *itoa7();
  203.  
  204. /* display routines that need to be predeclared */
  205. int i_loadave();
  206. int u_loadave();
  207. int i_procstates();
  208. int u_procstates();
  209. int i_cpustates();
  210. int u_cpustates();
  211. int i_memory();
  212. int u_memory();
  213. int i_message();
  214. int u_message();
  215. int i_header();
  216. int u_header();
  217. int i_process();
  218. int u_process();
  219.  
  220. /* pointers to display routines */
  221. int (*d_loadave)() = i_loadave;
  222. int (*d_procstates)() = i_procstates;
  223. int (*d_cpustates)() = i_cpustates;
  224. int (*d_memory)() = i_memory;
  225. int (*d_message)() = i_message;
  226. int (*d_header)() = i_header;
  227. int (*d_process)() = i_process;
  228.  
  229. /* buffer of proc information lines for display updating */
  230. /* unfortunate that this must be declared globally */
  231. char (* screenbuf)[Display_width];
  232.  
  233. main(argc, argv)
  234.  
  235. int  argc;
  236. char *argv[];
  237.  
  238. {
  239.     register struct proc *pp;
  240.     register struct proc **prefp;
  241.     register int i;
  242.     register int active_procs;
  243.     register int change;
  244.     register struct nlist *nlstp;
  245.  
  246.     static struct proc **pref;
  247.     static char tempbuf1[50];
  248.     static char tempbuf2[50];
  249.     int total_procs;
  250.     int old_sigmask;
  251.     int proc_brkdn[7];
  252.     int topn = Default_TOPN;
  253.     int delay = Default_DELAY;
  254.     int displays = 0;        /* indicates unspecified */
  255.     long curr_time;
  256.     char *(*get_userid)() = username;
  257.     char *uname_field = "USERNAME";
  258.     char dostates = No;
  259.     char do_unames = Yes;
  260.     char interactive = Maybe;
  261.     char show_sysprocs = No;
  262.     char warnings = 0;
  263. #if Default_TOPN == Infinity
  264.     char topn_specified = No;
  265. #endif
  266. #if defined(pyr) && defined(CPUFOUND)
  267.     int ncpu;
  268. #endif
  269. #ifndef FOUR_ONE        /* this is all used in processing commands */
  270.     char ch;
  271.     char *iptr;
  272.     char no_command = 1;
  273.     int readfds;
  274.     struct timeval timeout;
  275.     static char command_chars[] = "\f qh?en#sdkr";
  276. /* these defines enumerate the indexes of the commands in command_chars */
  277. #define CMD_redraw    0
  278. #define CMD_update    1
  279. #define CMD_quit    2
  280. #define CMD_help1    3
  281. #define CMD_help2    4
  282. #define CMD_OSLIMIT    4    /* terminals with OS can only handle commands */
  283. #define CMD_errors    5    /* less tha nor equal to CMD_OSLIMIT       */
  284. #define CMD_number1    6
  285. #define CMD_number2    7
  286. #define CMD_delay    8
  287. #define CMD_displays    9
  288. #define CMD_kill    10
  289. #define CMD_renice    11
  290. #endif    
  291.  
  292.     /* set the buffer for stdout */
  293.     setbuffer(stdout, stdoutbuf, Buffersize);
  294.  
  295.     /* get our name */
  296.     if (argc > 0)
  297.     {
  298.     if ((myname = rindex(argv[0], '/')) == 0)
  299.     {
  300.         myname = argv[0];
  301.     }
  302.     else
  303.     {
  304.         myname++;
  305.     }
  306.     }
  307.  
  308.     /* process options */
  309.     while ((i = getopt(argc, argv, "Sbinqus:d:")) != EOF)
  310.     {
  311.     switch(i)
  312.     {
  313.         case 'u':            /* display uid instead of name */
  314.         do_unames = No;
  315.         uname_field = "   UID  ";
  316.         get_userid = itoa7;
  317.         break;
  318.  
  319.         case 'S':            /* show system processes */
  320.         show_sysprocs = Yes;
  321.         break;
  322.  
  323.         case 'i':            /* go interactive regardless */
  324.         interactive = Yes;
  325.         break;
  326.  
  327.         case 'n':            /* batch, or non-interactive */
  328.         case 'b':
  329.         interactive = No;
  330.         break;
  331.  
  332.         case 'd':            /* number of displays to show */
  333.         if ((i = atoiwi(optarg)) == Invalid || i == 0)
  334.         {
  335.             fprintf(stderr,
  336.             "%s: warning: display count should be positive -- option ignored\n",
  337.             myname);
  338.             warnings++;
  339.         }
  340.         else
  341.         {
  342.             displays = i;
  343.         }
  344.         break;
  345.  
  346.         case 's':
  347.         if ((delay = atoi(optarg)) < 0)
  348.         {
  349.             fprintf(stderr,
  350.             "%s: warning: seconds delay should be non-negative -- using default\n",
  351.             myname);
  352.             delay = Default_DELAY;
  353.             warnings++;
  354.         }
  355.         break;
  356.  
  357.         case 'q':        /* be quick about it */
  358.         /* only allow this if user is really root */
  359.         if (getuid() == 0)
  360.         {
  361.             /* be very un-nice! */
  362.             (void) nice(-20);
  363.         }
  364.         else
  365.         {
  366.             fprintf(stderr,
  367.             "%s: warning: `-q' option can only be used by root\n",
  368.             myname);
  369.             warnings++;
  370.         }
  371.         break;
  372.  
  373.         default:
  374.         fprintf(stderr,
  375.             "Usage: %s [-Sbinqu] [-d x] [-s x] [number]\n",
  376.             myname);
  377.         exit(1);
  378.     }
  379.     }
  380.  
  381.     /* get count of top processes to display (if any) */
  382.     if (optind < argc)
  383.     {
  384.     if ((topn = atoiwi(argv[optind])) == Invalid)
  385.     {
  386.         fprintf(stderr,
  387.         "%s: warning: process display count should be non-negative -- using default\n",
  388.         myname);
  389.         topn = Default_TOPN;
  390.         warnings++;
  391.     }
  392. #if Default_TOPN == Infinity
  393.     else
  394.     {
  395.         topn_specified = Yes;
  396.     }
  397. #endif
  398.     }
  399.  
  400.     /* initialize the kernel memory interface */
  401.     init_kernel();
  402.  
  403.     /* get the list of symbols we want to access in the kernel */
  404.     /* errno = 0; ??? */
  405. #ifdef sunos4
  406.     if (i = kvm_nlist(kd, nlst))
  407.     {
  408.     if (i < 0)
  409.     {
  410.         fprintf(stderr, "top: can't nlist image\n");
  411.         exit(2);
  412.     }
  413.     else
  414.     {
  415.         fprintf(stderr, "top: can't nlist %d symbols\n", i);
  416.         exit(2);
  417.     }
  418.     }
  419. #else
  420.     (void) nlist(VMUNIX, nlst);
  421.     if (nlst[0].n_type == 0)
  422.     {
  423.     fprintf(stderr, "%s: can't nlist image\n", VMUNIX);
  424.     exit(2);
  425.     }
  426. #endif
  427.  
  428.     /* did we get ALL of them? */
  429.     i = 0;
  430.     for (nlstp = nlst; nlstp->n_name != NULL; nlstp++)
  431.     {
  432.     if (nlstp->n_type == 0)
  433.     {
  434.         /* this one wasn't found */
  435.         fprintf(stderr, "%s: no symbol named `%s'\n", VMUNIX,
  436.         nlstp->n_name);
  437.         i = 1;
  438.     }
  439.     }
  440.     if (i)
  441.     {
  442.     /* at least one was not found -- abort */
  443.     exit(5);
  444.     }
  445.  
  446.     /* get the symbol values out of kmem */
  447.     (void) getkval(nlst[X_PROC].n_value,   (int *)(&proc),    sizeof(proc),
  448.         nlst[X_PROC].n_name);
  449.     (void) getkval(nlst[X_NPROC].n_value,  &nproc,        sizeof(nproc),
  450.         nlst[X_NPROC].n_name);
  451.     (void) getkval(nlst[X_HZ].n_value,     (int *)(&hz),    sizeof(hz),
  452.         nlst[X_HZ].n_name);
  453.     (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),    sizeof(ccpu),
  454.         nlst[X_CCPU].n_name);
  455. #if defined(pyr) && defined(CPUFOUND)
  456.     (void) getkval(nlst[X_MAXCPU].n_value, &maxcpu,    sizeof(maxcpu),
  457.         nlst[X_MAXCPU].n_name);
  458. #endif
  459. #ifdef scs
  460.     (void) getkval(nlst[X_SPT].n_value, (int *)(&spt), sizeof(struct spt *),
  461.         nlst[X_SPT].n_name);
  462. #endif scs
  463.  
  464.     /* some calculations we use later */
  465.  
  466.     mpid_offset = nlst[X_MPID].n_value;
  467.     avenrun_offset = nlst[X_AVENRUN].n_value;
  468.     total_offset = nlst[X_TOTAL].n_value;
  469. #if defined(pyr) && defined(CPUFOUND)
  470.     percpu_offset = nlst[X_PERCPU].n_value;
  471. #else
  472.     cp_time_offset = nlst[X_CP_TIME].n_value;
  473. #endif
  474.  
  475.     /* this is used in calculating WCPU -- calculate it ahead of time */
  476. #ifdef sun
  477.     logcpu = log((double)ccpu / FSCALE);
  478. #else
  479.     logcpu = log(ccpu);
  480. #endif
  481.  
  482.     /* allocate space for proc structure array and array of pointers */
  483.     bytes  = nproc * sizeof(struct proc);
  484.     pbase  = (struct proc *)sbrk(bytes);
  485.     pref   = (struct proc **)sbrk(nproc * sizeof(struct proc *));
  486.  
  487.     /* Just in case ... */
  488.     if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
  489.     {
  490.     fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
  491.     exit(3);
  492.     }
  493.  
  494.     /* initialize the hashing stuff */
  495.     if (do_unames)
  496.     {
  497.     init_hash();
  498.     }
  499.  
  500.     /* initialize termcap */
  501.     init_termcap();
  502.  
  503.     /*
  504.      *  Smart terminals can only display so many processes, precisely
  505.      *    "screen_length - Header_lines".  When run on dumb terminals, nothing
  506.      *    fancy is done anyway, so we can display as many processes as the
  507.      *    system can make.  But since we never need to remember what is on the
  508.      *    screen, we only allocate a buffer for one screen line.
  509.      */
  510.     if (smart_terminal)
  511.     {
  512.     /* can only display (screen_length - Header_lines) processes */
  513.     i = screen_length - Header_lines;
  514.     if (topn > i)        /* false even when topn == Infinity */
  515.     {
  516.         fprintf(stderr,
  517.         "%s: warning: this terminal can only display %d processes.\n",
  518.         myname, screen_length - Header_lines);
  519.         topn = i;
  520.         warnings++;
  521.     }
  522.     }
  523.     else
  524.     {
  525.     i = 1;
  526.     screen_length = nproc + Header_lines;
  527.     }
  528.  
  529.     /* allocate space for the screen buffer */
  530.     screenbuf = (char (*)[Display_width])sbrk(i * Display_width);
  531.     if (screenbuf == (char (*)[Display_width])NULL)
  532.     {
  533.     fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
  534.     exit(4);
  535.     }
  536.  
  537.     /* adjust for topn == Infinity */
  538.     if (topn == Infinity)
  539.     {
  540.     /*
  541.      *  For smart terminals, infinity really means everything that can
  542.      *  be displayed (which just happens to be "i" at this point).
  543.      *  On dumb terminals, infinity means every process in the system!
  544.      *  We only really want to do that if it was explicitly specified.
  545.      *  This is always the case when "Default_TOPN != Infinity".  But if
  546.      *  topn wasn't explicitly specified and we are on a dumb terminal
  547.      *  and the default is Infinity, then (and only then) we use
  548.      *  "Nominal_TOPN" instead.
  549.      */
  550. #if Default_TOPN == Infinity
  551.     topn = smart_terminal ? i :
  552.             (topn_specified ? nproc : Nominal_TOPN);
  553. #else
  554.     topn = smart_terminal ? i : nproc;
  555. #endif
  556.     }
  557.  
  558.     /* determine interactive state */
  559.     if (interactive == Maybe)
  560.     {
  561.     interactive = smart_terminal;
  562.     }
  563.  
  564.     /* if # of displays not specified, fill it in */
  565.     if (displays == 0)
  566.     {
  567.     displays = smart_terminal ? Infinity : 1;
  568.     }
  569.  
  570.     /* hold interrupt signals while setting up the screen and the handlers */
  571. #ifndef FOUR_ONE
  572.     old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
  573. #endif
  574.     init_screen();
  575.     (void) signal(SIGINT, leave);
  576.     (void) signal(SIGQUIT, leave);
  577.     (void) signal(SIGTSTP, tstop);
  578. #ifndef FOUR_ONE
  579.     (void) sigsetmask(old_sigmask);
  580. #endif
  581.     if (warnings)
  582.     {
  583.     fprintf(stderr, "....");
  584.     fflush(stderr);            /* why must I do this? */
  585.     sleep((unsigned)(3 * warnings));
  586.     }
  587.  
  588.     /* setup the jump buffer for stops */
  589.     if (setjmp(jmp_int) != 0)
  590.     {
  591.     /* control ends up here after an interrupt */
  592.     reset_display();
  593.     }
  594.  
  595.     /*
  596.      *  main loop -- repeat while display count is positive or while it
  597.      *        indicates infinity (by being -1)
  598.      */
  599.  
  600.     while ((displays == -1) || (displays-- > 0))
  601.     {
  602.     /* read all the proc structures in one fell swoop */
  603. #ifdef sunos4
  604.     /* should use kvm_getproc() */
  605. #endif
  606.     (void) getkval(proc, (int *)pbase, bytes, "proc array");
  607.  
  608.     /* get the cp_time array */
  609. #if defined(pyr) && defined(CPUFOUND)
  610.       (void) getkval(percpu_offset, percpu, sizeof(percpu), "_percpu");
  611.     for (i = 0; i < CPUSTATES; ++i)
  612.         cp_time[i] = 0;
  613.     for (ncpu = 0; ncpu <= maxcpu; ++ncpu)
  614.     {
  615.         if (CPUFOUND(ncpu))
  616.         {
  617.         for (i = 0; i < CPUSTATES; ++i)
  618.             cp_time[i] += CP_TIME(ncpu)[i];
  619.         }
  620.     }
  621. #else
  622.     (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
  623.              "_cp_time");
  624. #endif
  625.  
  626.     /* get load average array */
  627.     (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
  628.              "_avenrun");
  629.  
  630.     /* get mpid -- process id of last process */
  631.     (void) getkval(mpid_offset, &mpid, sizeof(mpid), "_mpid");
  632.  
  633.     /* get total -- systemwide main memory usage structure */
  634.     (void) getkval(total_offset, (int *)(&total), sizeof(total),
  635.              "_total");
  636.  
  637.     /* count up process states and get pointers to interesting procs */
  638.     total_procs = 0;
  639.     active_procs = 0;
  640.     bzero((char *)proc_brkdn, sizeof(proc_brkdn));
  641.     prefp = pref;
  642.     for (pp = pbase, i = 0; i < nproc; pp++, i++)
  643.     {
  644.         /*
  645.          *  Place pointers to each valid proc structure in pref[].
  646.          *  Process slots that are actually in use have a non-zero
  647.          *  status field.  Processes with SSYS set are system
  648.          *  processes---these get ignored unless show_sysprocs is set.
  649.          */
  650.         if (pp->p_stat != 0 &&
  651.         (show_sysprocs || ((pp->p_flag & SSYS) == 0)))
  652.         {
  653.         total_procs++;
  654.         proc_brkdn[pp->p_stat]++;
  655.         if (pp->p_stat != SZOMB)
  656.         {
  657.             *prefp++ = pp;
  658.             active_procs++;
  659.         }
  660.         }
  661.     }
  662.  
  663.     /* display the load averages */
  664.     (*d_loadave)(mpid, avenrun);
  665.  
  666.     /*
  667.      *  Display the current time.
  668.      *  "ctime" always returns a string that looks like this:
  669.      *  
  670.      *    Sun Sep 16 01:03:52 1973
  671.      *      012345678901234567890123
  672.      *              1         2
  673.      *
  674.      *  We want indices 11 thru 18 (length 8).
  675.      */
  676.  
  677.     curr_time = time((long *)NULL);
  678.     if (smart_terminal)
  679.     {
  680.         Move_to(screen_width - 8, 0);
  681.     }
  682.     else
  683.     {
  684.         fputs("    ", stdout);
  685.     }
  686.     printf("%-8.8s\n", &(ctime(&curr_time)[11]));
  687.  
  688.     /* display process state breakdown */
  689.     (*d_procstates)(total_procs, proc_brkdn);
  690.  
  691.     /* calculate percentage time in each cpu state */
  692.     if (dostates)    /* but not the first time */
  693.     {
  694.         total_change = 0;
  695.         for (i = 0; i < CPUSTATES; i++)
  696.         {
  697.         /* calculate changes for each state and overall change */
  698.         if (cp_time[i] < cp_old[i])
  699.         {
  700.             /* this only happens when the counter wraps */
  701.             change = (int)
  702.             ((unsigned long)cp_time[i]-(unsigned long)cp_old[i]);
  703.         }
  704.         else
  705.         {
  706.             change = cp_time[i] - cp_old[i];
  707.         }
  708.         total_change += (cp_change[i] = change);
  709.         cp_old[i] = cp_time[i];
  710.         }
  711.         (*d_cpustates)(cp_change, total_change);
  712.     }
  713.     else
  714.     {
  715.         /* we'll do it next time */
  716.         if (smart_terminal)
  717.         {
  718.         z_cpustates();
  719.         }
  720.         else
  721.         {
  722.         putchar('\n');
  723.         }
  724.         dostates = Yes;
  725.  
  726.         /* remember the current values as "old" values */
  727.         bcopy((char *)cp_time, (char *)cp_old, sizeof(cp_time));
  728.     }
  729.  
  730.     /* display main memory statistics */
  731. #ifdef sunos4
  732. /*
  733.  * Note that total.t_vm and total.t_avm are unused in sunOS 4.0.  As such,
  734.  * they get reported as zero.
  735.  */
  736. #endif
  737.     (*d_memory)(
  738.         pagetok(total.t_rm), pagetok(total.t_arm),
  739.         pagetok(total.t_vm), pagetok(total.t_avm),
  740.         pagetok(total.t_free));
  741.  
  742.     /* handle message area */
  743.     (*d_message)();
  744.  
  745.     i = 0;
  746.     if (topn > 0)
  747.     {
  748.         /* update the header area */
  749.         (*d_header)(uname_field);
  750.     
  751.         /* sort by cpu percentage (pctcpu) */
  752.         qsort((char *)pref, active_procs,
  753.           sizeof(struct proc *), proc_compar);
  754.     
  755.         /* adjust for a lack of processes */
  756.         if (active_procs > topn)
  757.         {
  758.         active_procs = topn;
  759.         }
  760.  
  761.         /*
  762.          *  Now, show the top "n" processes.  The method is slightly
  763.          *    different for dumb terminals, so we will just use two very
  764.          *    similar loops; this increases speed but also code size.
  765.          */
  766.         if (smart_terminal)
  767.         {
  768.         for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
  769.         {
  770.             pp = *prefp;
  771.             (*d_process)(i, pp, get_userid);
  772.         }
  773.         }
  774.         else for (prefp = pref, i = 0; i < active_procs; prefp++, i++)
  775.         {
  776.         pp = *prefp;
  777.         /* (only one buffer lien with dumb terminals) */
  778.         (*d_process)(0, pp, get_userid);
  779.         }
  780.     }
  781.  
  782.     /* do end-screen processing */
  783.     u_endscreen(i);
  784.  
  785.     /* now, flush the output buffer */
  786.     fflush(stdout);
  787.  
  788.     /* only do the rest if we have more displays to show */
  789.     if (displays)
  790.     {
  791.         /* switch out for new display on smart terminals */
  792.         if (smart_terminal)
  793.         {
  794.         if (overstrike)
  795.         {
  796.             reset_display();
  797.         }
  798.         else
  799.         {
  800.             d_loadave = u_loadave;
  801.             d_procstates = u_procstates;
  802.             d_cpustates = u_cpustates;
  803.             d_memory = u_memory;
  804.             d_message = u_message;
  805.             d_header = u_header;
  806.             d_process = u_process;
  807.         }
  808.         }
  809.     
  810. #ifndef FOUR_ONE
  811.         no_command = Yes;
  812.         if (!interactive)
  813. #endif
  814.         {
  815.         /* set up alarm */
  816.         (void) signal(SIGALRM, onalrm);
  817.         (void) alarm((unsigned)delay);
  818.     
  819.         /* wait for the rest of it .... */
  820.         pause();
  821.         }
  822. #ifndef FOUR_ONE
  823.         else while (no_command)
  824.         {
  825.         /* assume valid command unless told otherwise */
  826.         no_command = No;
  827.  
  828.         /* set up arguments for select with timeout */
  829.         readfds = 1;            /* for standard input */
  830.         timeout.tv_sec  = delay;
  831.         timeout.tv_usec = 0;
  832.  
  833.         /* wait for either input or the end of the delay period */
  834.         if (select(32, &readfds, (int *)NULL, (int *)NULL, &timeout) > 0)
  835.         {
  836.             int newval;
  837.             char *errmsg;
  838.     
  839.             /* something to read -- clear the message area first */
  840.             clear_message();
  841.  
  842.             /* now read it and convert to command index */
  843.             /* (use "change" as a temporary to hold index) */
  844.             (void) read(0, &ch, 1);
  845.             if ((iptr = index(command_chars, ch)) == NULL)
  846.             {
  847.             /* illegal command */
  848.             new_message(1, " Command not understood");
  849.             putchar('\r');
  850.             no_command = Yes;
  851.             }
  852.             else
  853.             {
  854.             change = iptr - command_chars;
  855.             if (overstrike && change > CMD_OSLIMIT)
  856.             {
  857.                 /* error */
  858.                 new_message(1,
  859.                 " Command cannot be handled by this terminal");
  860.                 putchar('\r');
  861.                 no_command = Yes;
  862.             }
  863.             else switch(change)
  864.             {
  865.                 case CMD_redraw:    /* redraw screen */
  866.                 reset_display();
  867.                 break;
  868.     
  869.                 case CMD_update:    /* merely update display */
  870.                 /* is the load average high? */
  871. #ifdef sun
  872.                 if (avenrun[0] > (int)(LoadMax * FSCALE))
  873. #else
  874.                 if (avenrun[0] > LoadMax)
  875. #endif
  876.                 {
  877.                     /* yes, go home for visual feedback */
  878.                     go_home();
  879.                     fflush(stdout);
  880.                 }
  881.                 break;
  882.         
  883.                 case CMD_quit:    /* quit */
  884.                 quit(0);
  885.                 /*NOTREACHED*/
  886.                 break;
  887.         
  888.                 case CMD_help1:    /* help */
  889.                 case CMD_help2:
  890.                 reset_display();
  891.                 clear();
  892.                 show_help();
  893.                 standout("Hit any key to continue: ");
  894.                 fflush(stdout);
  895.                 (void) read(0, &ch, 1);
  896.                 break;
  897.     
  898.                 case CMD_errors:    /* show errors */
  899.                 if (error_count() == 0)
  900.                 {
  901.                     new_message(1,
  902.                     " Currently no errors to report.");
  903.                     putchar('\r');
  904.                     no_command = Yes;
  905.                 }
  906.                 else
  907.                 {
  908.                     reset_display();
  909.                     clear();
  910.                     show_errors();
  911.                     standout("Hit any key to continue: ");
  912.                     fflush(stdout);
  913.                     (void) read(0, &ch, 1);
  914.                 }
  915.                 break;
  916.     
  917.                 case CMD_number1:    /* new number */
  918.                 case CMD_number2:
  919.                 new_message(1,
  920.                     "Number of processes to show: ");
  921.                 newval = readline(tempbuf1, 8, Yes);
  922.                 if (newval > -1)
  923.                 {
  924.                     if (newval > (i = screen_length - Header_lines))
  925.                     {
  926.                     new_message(1,
  927.                       " This terminal can only display %d processes.",
  928.                       i);
  929.                     newval = i;
  930.                     no_command = Yes;
  931.                     putchar('\r');
  932.                     break;
  933.                     }
  934.         
  935.                     if (newval > topn)
  936.                     {
  937.                     /* zero fill appropriate part of screenbuf */
  938.                     bzero(screenbuf[topn],
  939.                         (newval - topn) * Display_width);
  940.         
  941.                     /* redraw header if need be */
  942.                     if (topn == 0)
  943.                     {
  944.                         d_header = i_header;
  945.                     }
  946.                     }
  947.                     topn = newval;
  948.                 }
  949.                 break;
  950.         
  951.                 case CMD_delay:    /* new seconds delay */
  952.                 new_message(1, "Seconds to delay: ");
  953.                 if ((i = readline(tempbuf1, 8, Yes)) > -1)
  954.                 {
  955.                     delay = i;
  956.                 }
  957.                 clear_message();
  958.                 break;
  959.     
  960.                 case CMD_displays:    /* change display count */
  961.                 new_message(1,
  962.                     "Displays to show (currently %s): ",
  963.                     displays == -1 ? "infinite" :
  964.                              itoa(displays));
  965.                 if ((i = readline(tempbuf1, 10, Yes)) > 0)
  966.                 {
  967.                     displays = i;
  968.                 }
  969.                 else if (i == 0)
  970.                 {
  971.                     quit(0);
  972.                 }
  973.                 clear_message();
  974.                 break;
  975.     
  976.                 case CMD_kill:    /* kill program */
  977.                 new_message(0, "kill ");
  978.                 if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
  979.                 {
  980.                     if ((errmsg = kill_procs(tempbuf2)) != NULL)
  981.                     {
  982.                     new_message(1, errmsg);
  983.                     putchar('\r');
  984.                     no_command = Yes;
  985.                     }
  986.                 }
  987.                 else
  988.                 {
  989.                     clear_message();
  990.                 }
  991.                 break;
  992.         
  993.                 case CMD_renice:    /* renice program */
  994.                 new_message(0, "renice ");
  995.                 if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
  996.                 {
  997.                     if ((errmsg = renice_procs(tempbuf2)) != NULL)
  998.                     {
  999.                     new_message(1, errmsg);
  1000.                     putchar('\r');
  1001.                     no_command = Yes;
  1002.                     }
  1003.                 }
  1004.                 else
  1005.                 {
  1006.                     clear_message();
  1007.                 }
  1008.                 break;
  1009.         
  1010.                 default:
  1011.                 new_message(1, " BAD CASE IN SWITCH!");
  1012.                 putchar('\r');
  1013.             }
  1014.             }
  1015.  
  1016.             /* flush out stuff that may have been written */
  1017.             fflush(stdout);
  1018.         }
  1019.         }
  1020. #endif
  1021.     }
  1022.     }
  1023.  
  1024.     quit(0);
  1025. }
  1026.  
  1027. /*
  1028.  *  reset_display() - reset all the display routine pointers so that entire
  1029.  *    screen will get redrawn.
  1030.  */
  1031.  
  1032. reset_display()
  1033.  
  1034. {
  1035.     d_loadave    = i_loadave;
  1036.     d_procstates = i_procstates;
  1037.     d_cpustates  = i_cpustates;
  1038.     d_memory     = i_memory;
  1039.     d_message     = i_message;
  1040.     d_header     = i_header;
  1041.     d_process     = i_process;
  1042. }
  1043.  
  1044. /*
  1045.  *  signal handlers
  1046.  */
  1047.  
  1048. leave()            /* exit under normal conditions -- INT handler */
  1049.  
  1050. {
  1051.     end_screen();
  1052.     exit(0);
  1053. }
  1054.  
  1055. tstop()
  1056.  
  1057. {
  1058.     /* move to the lower left */
  1059.     end_screen();
  1060.     fflush(stdout);
  1061.  
  1062. #ifdef FOUR_ONE        /* a 4.1 system */
  1063.  
  1064.     /* send a STOP (uncatchable) to everyone in the process group */
  1065.     (void) kill(0, SIGSTOP);
  1066.  
  1067.     /* reset the signal handler */
  1068.     (void) signal(SIGTSTP, tstop);
  1069.  
  1070. #else            /* assume it is a 4.2 system */
  1071.  
  1072.     /* default the signal handler action */
  1073.     (void) signal(SIGTSTP, SIG_DFL);
  1074.  
  1075.     /* unblock the signal and send ourselves one */
  1076.     (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
  1077.     (void) kill(0, SIGTSTP);
  1078.  
  1079.     /* reset the signal handler */
  1080.     (void) signal(SIGTSTP, tstop);
  1081.  
  1082. #endif
  1083.     /* reinit screen */
  1084.     reinit_screen();
  1085.  
  1086.     /* jump to appropriate place */
  1087.     longjmp(jmp_int, 1);
  1088.  
  1089.     /*NOTREACHED*/
  1090. }
  1091.  
  1092. quit(status)        /* exit under duress */
  1093.  
  1094. int status;
  1095.  
  1096. {
  1097.     end_screen();
  1098.     exit(status);
  1099.     /*NOTREACHED*/
  1100. }
  1101.  
  1102. onalrm()
  1103.  
  1104. {
  1105.     return(0);
  1106. }
  1107.  
  1108. /*
  1109.  *  proc_compar - comparison function for "qsort"
  1110.  *    Compares the resource consumption of two processes using five
  1111.  *      distinct keys.  The keys (in descending order of importance) are:
  1112.  *      percent cpu, cpu ticks, state, resident set size, total virtual
  1113.  *      memory usage.  The process states are ordered as follows (from least
  1114.  *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
  1115.  *      array declaration below maps a process state index into a number
  1116.  *      that reflects this ordering.
  1117.  */
  1118.  
  1119. unsigned char sorted_state[] =
  1120. {
  1121.     0,    /* not used        */
  1122.     3,    /* sleep        */
  1123.     1,    /* ABANDONED (WAIT)    */
  1124.     6,    /* run            */
  1125.     5,    /* start        */
  1126.     2,    /* zombie        */
  1127.     4    /* stop            */
  1128. };
  1129.  
  1130. proc_compar(pp1, pp2)
  1131.  
  1132. struct proc **pp1;
  1133. struct proc **pp2;
  1134.  
  1135. {
  1136.     register struct proc *p1;
  1137.     register struct proc *p2;
  1138.     register int result;
  1139. #ifndef sun
  1140.     register double dresult;
  1141. #endif
  1142.  
  1143.     /* remove one level of indirection */
  1144.     p1 = *pp1;
  1145.     p2 = *pp2;
  1146.  
  1147.     /* compare percent cpu (pctcpu) */
  1148. #ifdef sun
  1149.     if ((result = p2->p_pctcpu - p1->p_pctcpu) == 0)
  1150. #else
  1151.     if ((dresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
  1152. #endif
  1153.     {
  1154.     /* use cpticks to break the tie */
  1155.     if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
  1156.     {
  1157.         /* use process state to break the tie */
  1158.         if ((result = sorted_state[p2->p_stat] -
  1159.               sorted_state[p1->p_stat])  == 0)
  1160.         {
  1161.         /* use priority to break the tie */
  1162.         if ((result = p2->p_pri - p1->p_pri) == 0)
  1163.         {
  1164.             /* use resident set size (rssize) to break the tie */
  1165. #ifdef scs
  1166.             if ((result = p2->p_maxrss - p1->p_maxrss) == 0)
  1167. #else scs
  1168.             if ((result = p2->p_rssize - p1->p_rssize) == 0)
  1169. #endif scs
  1170.             {
  1171.             /* use total memory to break the tie */
  1172. #ifdef pyr
  1173.             result = (p2->p_tsize + p2->p_dsize + p2->p_ussize) -
  1174.                  (p1->p_tsize + p1->p_dsize + p1->p_ussize);
  1175. #else !pyr
  1176. #ifdef scs
  1177.             result = (p2->p_tdsize + p2->p_ssize) -
  1178.                  (p1->p_tdsize + p1->p_ssize);
  1179. #else !scs
  1180.             result = (p2->p_tsize + p2->p_dsize + p2->p_ssize) -
  1181.                  (p1->p_tsize + p1->p_dsize + p1->p_ssize);
  1182. #endif scs
  1183. #endif pyr
  1184.             }
  1185.         }
  1186.         }
  1187.     }
  1188.     }
  1189. #ifndef sun
  1190.     else
  1191.     {
  1192.     result = dresult < 0.0 ? -1 : 1;
  1193.     }
  1194. #endif
  1195.  
  1196.     return(result);
  1197. }
  1198.  
  1199. /*
  1200.  *  These routines handle uid to username mapping.
  1201.  *  They use a hashing table scheme to reduce reading overhead.
  1202.  */
  1203.  
  1204. struct hash_el {
  1205.     int  uid;
  1206.     char name[8];
  1207. };
  1208.  
  1209. #define    H_empty    -1
  1210.  
  1211. /* simple minded hashing function */
  1212. #define    hashit(i)    ((i) % Table_size)
  1213.  
  1214. struct hash_el hash_table[Table_size];
  1215.  
  1216. init_hash()
  1217.  
  1218. {
  1219.     register int i;
  1220.     register struct hash_el *h;
  1221.  
  1222.     for (h = hash_table, i = 0; i < Table_size; h++, i++)
  1223.     {
  1224.     h->uid = H_empty;
  1225.     }
  1226. }
  1227.  
  1228. char *username(uid)
  1229.  
  1230. register int uid;
  1231.  
  1232. {
  1233.     register int hashindex;
  1234.     register int found;
  1235.  
  1236.     /* This is incredibly naive, but it'll probably get changed anyway */
  1237.     hashindex = hashit(uid);
  1238.     while ((found = hash_table[hashindex].uid) != uid)
  1239.     {
  1240.     if (found == H_empty)
  1241.     {
  1242.         /* not here -- get it out of passwd */
  1243.         hashindex = get_user(uid);
  1244.         break;        /* out of while */
  1245.     }
  1246.     hashindex = (hashindex + 1) % Table_size;
  1247.     }
  1248.     return(hash_table[hashindex].name);
  1249. }
  1250.  
  1251. enter_user(uid, name)
  1252.  
  1253. register int  uid;
  1254. register char *name;
  1255.  
  1256. {
  1257.     register int hashindex;
  1258.     register int try;
  1259.     static int uid_count = 0;
  1260.  
  1261.     /* avoid table overflow -- insure at least one empty slot */
  1262.     if (++uid_count >= Table_size)
  1263.     {
  1264.     fprintf(stderr, "table overflow: too many users\n");
  1265.     quit(10);
  1266.     }
  1267.  
  1268.     hashindex = hashit(uid);
  1269.     while ((try = hash_table[hashindex].uid) != H_empty)
  1270.     {
  1271.     if (try == uid)
  1272.     {
  1273.         return(hashindex);
  1274.     }
  1275.     hashindex = (hashindex + 1) % Table_size;
  1276.     }
  1277.     hash_table[hashindex].uid = uid;
  1278.     (void) strncpy(hash_table[hashindex].name, name, 8);
  1279.     return(hashindex);
  1280. }
  1281.  
  1282. get_user(uid)
  1283.  
  1284. register int uid;
  1285.  
  1286. {
  1287.     struct passwd *pwd;
  1288.     register int last_index;
  1289.  
  1290. #ifdef RANDOM_PW
  1291.     if ((pwd = getpwuid(uid)) != NULL)
  1292. #else
  1293.     while ((pwd = getpwent()) != NULL)
  1294. #endif
  1295.     {
  1296.     last_index = enter_user(pwd->pw_uid, pwd->pw_name);
  1297. #ifndef RANDOM_PW
  1298.     if (pwd->pw_uid == uid)
  1299. #endif
  1300.     {
  1301.         return(last_index);
  1302.     }
  1303.     }
  1304.     return(enter_user(uid, itoa7(uid)));
  1305. }
  1306.