home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / UNIX / Utilities / top-0.5-MI / machine / m_sunos4.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-24  |  20.2 KB  |  875 lines

  1. /*
  2.  * top - a top users display for Unix
  3.  *
  4.  * SYNOPSIS:  any Sun running SunOS version 4.x
  5.  *
  6.  * DESCRIPTION:
  7.  * This is the machine-dependent module for SunOS 4.x.
  8.  * This makes top work on the following systems:
  9.  *    SunOS 4.0
  10.  *    SunOS 4.0.1
  11.  *    SunOS 4.0.2 (including 386i architecture)
  12.  *    SunOS 4.0.3
  13.  *    SunOS 4.1
  14.  *    SunOS 4.1.1
  15.  *    SunOS 4.1.2 (including MP architectures)
  16.  *    SunOS 4.1.3 (including MP architectures)
  17.  *      SunOS 4.1.4 (including MP architectures)
  18.  *    Solbourne OS/MP PRIOR to 4.1A
  19.  *
  20.  * LIBS:  -lkvm
  21.  *
  22.  * CFLAGS: -DHAVE_GETOPT
  23.  *
  24.  * AUTHOR:  William LeFebvre <phil@eecs.nwu.edu>
  25.  * Solbourne support by David MacKenzie <djm@eng.umd.edu>
  26.  */
  27.  
  28. /*
  29.  * #ifdef MULTIPROCESSOR means Sun MP.
  30.  * #ifdef solbourne is for Solbourne.
  31.  */
  32.  
  33. #include <sys/types.h>
  34. #include <sys/signal.h>
  35.  
  36. /* make sure param.h gets loaded with KERNEL defined to get PZERO & NZERO */
  37. #define KERNEL
  38. #include <sys/param.h>
  39. #undef KERNEL
  40.  
  41. #include <stdio.h>
  42. #include <kvm.h>
  43. #include <nlist.h>
  44. #include <math.h>
  45. #include <sys/dir.h>
  46. #include <sys/user.h>
  47. #include <sys/proc.h>
  48. #include <sys/dk.h>
  49. #include <sys/vm.h>
  50. #include <sys/file.h>
  51. #include <sys/time.h>
  52. #include <vm/page.h>
  53.  
  54. #ifdef solbourne
  55. #include <sys/syscall.h>
  56. #endif
  57.  
  58. /* Older versions of SunOS don't have a typedef for pid_t.
  59.    Hopefully this will catch all those cases without causing other problems.
  60.  */
  61. #ifndef __sys_stdtypes_h
  62. typedef int pid_t;
  63. #endif
  64.  
  65. #include "top.h"
  66. #include "machine.h"
  67. #include "utils.h"
  68.  
  69. /* declarations for load_avg */
  70. #include "loadavg.h"
  71.  
  72. /* get_process_info passes back a handle.  This is what it looks like: */
  73.  
  74. struct handle
  75. {
  76.     struct proc **next_proc;    /* points to next valid proc pointer */
  77.     int remaining;        /* number of pointers remaining */
  78. };
  79.  
  80. /* define what weighted cpu is.  */
  81. #define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
  82.              ((pct) / (1.0 - exp((pp)->p_time * logcpu))))
  83.  
  84. /* what we consider to be process size: */
  85. #define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize)
  86.  
  87. /* definitions for indices in the nlist array */
  88. #define X_AVENRUN    0
  89. #define X_CCPU        1
  90. #define X_MPID        2
  91. #define X_NPROC        3
  92. #define X_PROC        4
  93. #define X_TOTAL        5
  94. #define X_CP_TIME    6
  95. #define X_PAGES        7
  96. #define X_EPAGES    8
  97.  
  98. static struct nlist nlst[] = {
  99. #ifdef i386
  100.     { "avenrun" },        /* 0 */
  101.     { "ccpu" },            /* 1 */
  102.     { "mpid" },            /* 2 */
  103.     { "nproc" },        /* 3 */
  104.     { "proc" },            /* 4 */
  105.     { "total" },        /* 5 */
  106.     { "cp_time" },        /* 6 */
  107.     { "pages" },        /* 7 */
  108.     { "epages" },        /* 8 */
  109. #else
  110.     { "_avenrun" },        /* 0 */
  111.     { "_ccpu" },        /* 1 */
  112.     { "_mpid" },        /* 2 */
  113.     { "_nproc" },        /* 3 */
  114.     { "_proc" },        /* 4 */
  115.     { "_total" },        /* 5 */
  116.     { "_cp_time" },        /* 6 */
  117.     { "_pages" },        /* 7 */
  118.     { "_epages" },        /* 8 */
  119. #ifdef MULTIPROCESSOR
  120.     { "_ncpu" },
  121. #define X_NCPU        9
  122.     { "_xp_time" },
  123. #define X_XP_TIME    10
  124. #endif
  125. #endif
  126.     { 0 }
  127. };
  128.  
  129. /*
  130.  *  These definitions control the format of the per-process area
  131.  */
  132.  
  133. static char header[] =
  134.   "  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
  135. /* 0123456   -- field to fill in starts at header+6 */
  136. #define UNAME_START 6
  137.  
  138. #define Proc_format \
  139.     "%5d %-8.8s %3d %4d %5s %5s %-5s %-6s %5.2f%% %5.2f%% %s"
  140.  
  141.  
  142. /* process state names for the "STATE" column of the display */
  143. /* the extra nulls in the string "run" are for adding a slash and
  144.    the processor number when needed */
  145.  
  146. char *state_abbrev[] =
  147. {
  148.     "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop"
  149. };
  150.  
  151. /* values that we stash away in _init and use in later routines */
  152.  
  153. static double logcpu;
  154. kvm_t *kd;
  155.  
  156. /* these are retrieved from the kernel in _init */
  157.  
  158. static unsigned long proc;
  159. static          int  nproc;
  160. static load_avg ccpu;
  161. static unsigned long pages;
  162. static unsigned long epages;
  163. static          int  ncpu = 0;
  164.  
  165. /* these are offsets obtained via nlist and used in the get_ functions */
  166.  
  167. static unsigned long mpid_offset;
  168. static unsigned long avenrun_offset;
  169. static unsigned long total_offset;
  170. static unsigned long cp_time_offset;
  171. #ifdef MULTIPROCESSOR
  172. static unsigned long xp_time_offset;
  173. #endif
  174.  
  175. /* these are for calculating cpu state percentages */
  176.  
  177. static long cp_time[CPUSTATES];
  178. static long cp_old[CPUSTATES];
  179. static long cp_diff[CPUSTATES];
  180. #ifdef MULTIPROCESSOR
  181. static long xp_time[NCPU][XPSTATES];
  182. /* for now we only accumulate spin time, but extending this to pick up
  183.    other stuff in xp_time is trivial.  */
  184. static long xp_old[NCPU];
  185. #endif
  186.  
  187. /* these are for detailing the process states */
  188.  
  189. int process_states[7];
  190. char *procstatenames[] = {
  191.     "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ",
  192.     " zombie, ", " stopped, ",
  193.     NULL
  194. };
  195.  
  196. /* these are for detailing the cpu states */
  197.  
  198. int cpu_states[5];
  199. char *cpustatenames[] = {
  200.     "user", "nice", "system", "idle",
  201. #ifdef MULTIPROCESSOR
  202.     "spin",
  203. #define XCP_SPIN 4
  204. #endif
  205.     NULL
  206. };
  207.  
  208. /* these are for detailing the memory statistics */
  209.  
  210. int memory_stats[4];
  211. char *memorynames[] = {
  212.     "K available, ", "K in use, ", "K free, ", "K locked", NULL
  213. };
  214.  
  215. /* these are for keeping track of the proc array */
  216.  
  217. static int bytes;
  218. static int pref_len;
  219. static struct proc *pbase;
  220. static struct proc **pref;
  221.  
  222. /* these are for getting the memory statistics */
  223.  
  224. static struct page *physpage;
  225. static int bytesize;
  226. static int count;
  227. static int pageshift;        /* log base 2 of the pagesize */
  228.  
  229. /* define pagetok in terms of pageshift */
  230.  
  231. #define pagetok(size) ((size) << pageshift)
  232.  
  233. /* useful externals */
  234. extern int errno;
  235. extern char *sys_errlist[];
  236.  
  237. long lseek();
  238. long time();
  239.  
  240. machine_init(statics)
  241.  
  242. struct statics *statics;
  243.  
  244. {
  245.     register int i;
  246.     register int pagesize;
  247.  
  248.     /* initialize the kernel interface */
  249.     if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "top")) == NULL)
  250.     {
  251.     perror("kvm_open");
  252.     return(-1);
  253.     }
  254.  
  255.     /* get the list of symbols we want to access in the kernel */
  256.     if ((i = kvm_nlist(kd, nlst)) < 0)
  257.     {
  258.     fprintf(stderr, "top: nlist failed\n");
  259.     return(-1);
  260.     }
  261.  
  262. #ifdef MULTIPROCESSOR
  263.     /* were ncpu and xp_time not found in the nlist? */
  264.     if (i > 0 && nlst[X_NCPU].n_type == 0 && nlst[X_XP_TIME].n_type == 0)
  265.     {
  266.     /* we were compiled on an MP system but we are not running on one */
  267.     /* so we will pretend this didn't happen and set ncpu = 1 */
  268.     i -= 2;
  269.     ncpu = 1;
  270.     }
  271. #endif
  272.  
  273. #ifdef solbourne
  274.     {
  275.     unsigned int status, type;
  276.  
  277.     /* Get the number of CPUs on this system.  */
  278.     syscall(SYS_getcpustatus, &status, &ncpu, &type);
  279.     }
  280. #endif
  281.  
  282.     /* make sure they were all found */
  283.     if (i > 0 && check_nlist(nlst) > 0)
  284.     {
  285.     return(-1);
  286.     }
  287.  
  288.     /* get the symbol values out of kmem */
  289.     (void) getkval(nlst[X_PROC].n_value,   (int *)(&proc),    sizeof(proc),
  290.         nlst[X_PROC].n_name);
  291.     (void) getkval(nlst[X_NPROC].n_value,  &nproc,        sizeof(nproc),
  292.         nlst[X_NPROC].n_name);
  293.     (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),    sizeof(ccpu),
  294.         nlst[X_CCPU].n_name);
  295.     (void) getkval(nlst[X_PAGES].n_value,  (int *)(&pages),    sizeof(pages),
  296.         nlst[X_PAGES].n_name);
  297.     (void) getkval(nlst[X_EPAGES].n_value, (int *)(&epages),    sizeof(epages),
  298.         nlst[X_EPAGES].n_name);
  299. #ifdef MULTIPROCESSOR
  300.     if (ncpu == 0)
  301.     {
  302.     /* if ncpu > 0 then we are not really on an MP system */
  303.     (void) getkval(nlst[X_NCPU].n_value,   (int *)(&ncpu),    sizeof(ncpu),
  304.                nlst[X_NCPU].n_name);
  305.     }
  306. #endif
  307.  
  308.     /* stash away certain offsets for later use */
  309.     mpid_offset = nlst[X_MPID].n_value;
  310.     avenrun_offset = nlst[X_AVENRUN].n_value;
  311.     total_offset = nlst[X_TOTAL].n_value;
  312.     cp_time_offset = nlst[X_CP_TIME].n_value;
  313. #ifdef MULTIPROCESSOR
  314.     xp_time_offset = nlst[X_XP_TIME].n_value;
  315. #endif
  316.  
  317.     /* this is used in calculating WCPU -- calculate it ahead of time */
  318.     logcpu = log(loaddouble(ccpu));
  319.  
  320.     /* allocate space for proc structure array and array of pointers */
  321.     bytes = nproc * sizeof(struct proc);
  322.     pbase = (struct proc *)malloc(bytes);
  323.     pref  = (struct proc **)malloc(nproc * sizeof(struct proc *));
  324.  
  325.     /* Just in case ... */
  326.     if (pbase == (struct proc *)NULL || pref == (struct proc **)NULL)
  327.     {
  328.     fprintf(stderr, "top: can't allocate sufficient memory\n");
  329.     return(-1);
  330.     }
  331.  
  332.     /* allocate a table to hold all the page structs */
  333.     bytesize = epages - pages;
  334.     count = bytesize / sizeof(struct page);
  335.     physpage = (struct page *)malloc(epages - pages);
  336.     if (physpage == NULL)
  337.     {
  338.     fprintf(stderr, "top: can't allocate sufficient memory\n");
  339.     return(-1);
  340.     }
  341.    
  342.     /* get the page size with "getpagesize" and calculate pageshift from it */
  343.     pagesize = getpagesize();
  344.     pageshift = 0;
  345.     while (pagesize > 1)
  346.     {
  347.     pageshift++;
  348.     pagesize >>= 1;
  349.     }
  350.  
  351.     /* we only need the amount of log(2)1024 for our conversion */
  352.     pageshift -= LOG1024;
  353.  
  354. #if defined(MULTIPROCESSOR) || defined(solbourne)
  355.     /* add a slash to the "run" state abbreviation */
  356.     if (ncpu > 1)
  357.     {
  358.     state_abbrev[SRUN][3] = '/';
  359.     }
  360. #endif
  361.  
  362.     /* fill in the statics information */
  363.     statics->procstate_names = procstatenames;
  364.     statics->cpustate_names = cpustatenames;
  365.     statics->memory_names = memorynames;
  366.  
  367.     /* all done! */
  368.     return(0);
  369. }
  370.  
  371. char *format_header(uname_field)
  372.  
  373. register char *uname_field;
  374.  
  375. {
  376.     register char *ptr;
  377.  
  378.     ptr = header + UNAME_START;
  379.     while (*uname_field != '\0')
  380.     {
  381.     *ptr++ = *uname_field++;
  382.     }
  383.  
  384.     return(header);
  385. }
  386.  
  387. get_system_info(si)
  388.  
  389. struct system_info *si;
  390.  
  391. {
  392.     load_avg avenrun[3];
  393.     long total;
  394. #ifdef MULTIPROCESSOR
  395.     long half_total;
  396. #endif
  397.  
  398.     /* get the cp_time array */
  399.     (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
  400.            "_cp_time");
  401.  
  402. #ifdef MULTIPROCESSOR
  403.     /* get the xp_time array as well */
  404.     if (ncpu > 1)
  405.     {
  406.     (void) getkval(xp_time_offset, (int *)xp_time, sizeof(xp_time),
  407.                "_xp_time");
  408.     }
  409. #endif
  410.  
  411.     /* get load average array */
  412.     (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
  413.            "_avenrun");
  414.  
  415.     /* get mpid -- process id of last process */
  416.     (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
  417.            "_mpid");
  418.  
  419.     /* get the array of physpage descriptors */
  420.     (void) getkval(pages, (int *)physpage, bytesize, "array _page");
  421.  
  422.     /* convert load averages to doubles */
  423.     {
  424.     register int i;
  425.     register double *infoloadp;
  426.     register load_avg *sysloadp;
  427.  
  428.     infoloadp = si->load_avg;
  429.     sysloadp = avenrun;
  430.     for (i = 0; i < 3; i++)
  431.     {
  432.         *infoloadp++ = loaddouble(*sysloadp++);
  433.     }
  434.     }
  435.  
  436.     /* convert cp_time counts to percentages */
  437.     total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
  438.  
  439. #ifdef MULTIPROCESSOR
  440.     /* calculate spin time from all processors */
  441.     if (ncpu > 1)
  442.     {
  443.     register int c;
  444.     register int i;
  445.     register long sum;
  446.     register long change;
  447.  
  448.     /* collect differences for each processor and add them */
  449.     sum = 0;
  450.     for (i = 0; i < ncpu; i++)
  451.     {
  452.         c = xp_time[i][XP_SPIN];
  453.         change = c - xp_old[i];
  454.         if (change < 0)
  455.         {
  456.         /* counter wrapped */
  457.         change = (long)((unsigned long)c -
  458.                 (unsigned long)xp_old[i]);
  459.         }
  460.         sum += change;
  461.         xp_old[i] = c;
  462.     }
  463.  
  464.     /*
  465.      *  NOTE:  I am assuming that the ticks found in xp_time are
  466.      *  already included in the ticks accumulated in cp_time.  To
  467.      *  get an accurate reflection, therefore, we have to subtract
  468.      *  the spin time from the system time and recompute those two
  469.      *  percentages.
  470.      */
  471.     half_total = total / 2l;
  472.     cp_diff[CP_SYS] -= sum;
  473.     cpu_states[CP_SYS] = (int)((cp_diff[CP_SYS] * 1000 + half_total) /
  474.                    total);
  475.     cpu_states[XCP_SPIN] = (int)((sum * 1000 + half_total) / total);
  476.     }
  477. #endif
  478.  
  479.     /* sum memory statistics */
  480.     {
  481.     register struct page *pp;
  482.     register int cnt;
  483.     register int inuse;
  484.     register int free;
  485.     register int locked;
  486.  
  487.     /* bop thru the array counting page types */
  488.     pp = physpage;
  489.     inuse = free = locked = 0;
  490.     for (cnt = count; --cnt >= 0; pp++)
  491.     {
  492.         if (pp->p_free)
  493.             free++;
  494.         else if (pp->p_lock || pp->p_keepcnt > 0)
  495.             locked++;
  496.         else
  497.             inuse++;
  498.     }
  499.  
  500.     /* convert memory stats to Kbytes */
  501.     memory_stats[0] = pagetok(inuse + free);
  502.     memory_stats[1] = pagetok(inuse);
  503.     memory_stats[2] = pagetok(free);
  504.     memory_stats[3] = pagetok(locked);
  505.     }
  506.  
  507.     /* set arrays and strings */
  508.     si->cpustates = cpu_states;
  509.     si->memory = memory_stats;
  510. }
  511.  
  512. static struct handle handle;
  513.  
  514. caddr_t get_process_info(si, sel, compare)
  515.  
  516. struct system_info *si;
  517. struct process_select *sel;
  518. int (*compare)();
  519.  
  520. {
  521.     register int i;
  522.     register int total_procs;
  523.     register int active_procs;
  524.     register struct proc **prefp;
  525.     register struct proc *pp;
  526.  
  527.     /* these are copied out of sel for speed */
  528.     int show_idle;
  529.     int show_system;
  530.     int show_uid;
  531.     int show_command;
  532.  
  533.     /* read all the proc structures in one fell swoop */
  534.     (void) getkval(proc, (int *)pbase, bytes, "proc array");
  535.  
  536.     /* get a pointer to the states summary array */
  537.     si->procstates = process_states;
  538.  
  539.     /* set up flags which define what we are going to select */
  540.     show_idle = sel->idle;
  541.     show_system = sel->system;
  542.     show_uid = sel->uid != -1;
  543.     show_command = sel->command != NULL;
  544.  
  545.     /* count up process states and get pointers to interesting procs */
  546.     total_procs = 0;
  547.     active_procs = 0;
  548.     bzero((char *)process_states, sizeof(process_states));
  549.     prefp = pref;
  550.     for (pp = pbase, i = 0; i < nproc; pp++, i++)
  551.     {
  552.     /*
  553.      *  Place pointers to each valid proc structure in pref[].
  554.      *  Process slots that are actually in use have a non-zero
  555.      *  status field.  Processes with SSYS set are system
  556.      *  processes---these get ignored unless show_sysprocs is set.
  557.      */
  558.     if (pp->p_stat != 0 &&
  559.         (show_system || ((pp->p_flag & SSYS) == 0)))
  560.     {
  561.         total_procs++;
  562.         process_states[pp->p_stat]++;
  563.         if ((pp->p_stat != SZOMB) &&
  564.         (show_idle || (pp->p_pctcpu != 0) || (pp->p_stat == SRUN)) &&
  565.         (!show_uid || pp->p_uid == (uid_t)sel->uid))
  566.         {
  567.         *prefp++ = pp;
  568.         active_procs++;
  569.         }
  570.     }
  571.     }
  572.  
  573.     /* if requested, sort the "interesting" processes */
  574.     if (compare != NULL)
  575.     {
  576.     qsort((char *)pref, active_procs, sizeof(struct proc *), compare);
  577.     }
  578.  
  579.     /* remember active and total counts */
  580.     si->p_total = total_procs;
  581.     si->p_active = pref_len = active_procs;
  582.  
  583.     /* pass back a handle */
  584.     handle.next_proc = pref;
  585.     handle.remaining = active_procs;
  586.     return((caddr_t)&handle);
  587. }
  588.  
  589. char fmt[MAX_COLS];        /* static area where result is built */
  590.  
  591. char *format_next_process(handle, get_userid)
  592.  
  593. caddr_t handle;
  594. char *(*get_userid)();
  595.  
  596. {
  597.     register struct proc *pp;
  598.     register long cputime;
  599.     register double pct;
  600.     struct user u;
  601.     struct handle *hp;
  602.  
  603.     /* find and remember the next proc structure */
  604.     hp = (struct handle *)handle;
  605.     pp = *(hp->next_proc++);
  606.     hp->remaining--;
  607.     
  608.     /* get the process's user struct and set cputime */
  609.     if (getu(pp, &u) == -1)
  610.     {
  611.     (void) strcpy(u.u_comm, "<swapped>");
  612.     cputime = 0;
  613.     }
  614.     else
  615.     {
  616.     /* set u_comm for system processes */
  617.     if (u.u_comm[0] == '\0')
  618.     {
  619.         if (pp->p_pid == 0)
  620.         {
  621.         (void) strcpy(u.u_comm, "Swapper");
  622.         }
  623.         else if (pp->p_pid == 2)
  624.         {
  625.         (void) strcpy(u.u_comm, "Pager");
  626.         }
  627.     }
  628.  
  629.     cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec;
  630.     }
  631.  
  632.     /* calculate the base for cpu percentages */
  633.     pct = pctdouble(pp->p_pctcpu);
  634.  
  635. #ifdef MULTIPROCESSOR
  636.     /*
  637.      *  If there is more than one cpu then add the processor number to
  638.      *  the "run/" string.  Note that this will only show up if the
  639.      *  process is in the run state.  Also note:  when they
  640.      *  start making Suns with more than 9 processors this will break
  641.      *  since the string will then be more than 5 characters.
  642.      */
  643.     if (ncpu > 1)
  644.     {
  645.     state_abbrev[SRUN][4] = (pp->p_cpuid & 0xf) + '0';
  646.     }
  647. #endif
  648. #ifdef solbourne
  649.     if (ncpu > 1)
  650.       {
  651.     state_abbrev[SRUN][4] = (pp->p_lastcpu) + '0';
  652.       }
  653. #endif
  654.  
  655.     /* format this entry */
  656.     sprintf(fmt,
  657.         Proc_format,
  658.         pp->p_pid,
  659.         (*get_userid)(pp->p_uid),
  660.         pp->p_pri - PZERO,
  661.         pp->p_nice - NZERO,
  662.         format_k(pagetok(PROCSIZE(pp))),
  663.         format_k(pagetok(pp->p_rssize)),
  664.         state_abbrev[pp->p_stat],
  665.         format_time(cputime),
  666.         100.0 * weighted_cpu(pct, pp),
  667.         100.0 * pct,
  668.         printable(u.u_comm));
  669.  
  670.     /* return the result */
  671.     return(fmt);
  672. }
  673.  
  674. /*
  675.  *  getu(p, u) - get the user structure for the process whose proc structure
  676.  *    is pointed to by p.  The user structure is put in the buffer pointed
  677.  *    to by u.  Return 0 if successful, -1 on failure (such as the process
  678.  *    being swapped out).
  679.  */
  680.  
  681. getu(p, u)
  682.  
  683. register struct proc *p;
  684. struct user *u;
  685.  
  686. {
  687.     register struct user *lu;
  688.  
  689.     lu = kvm_getu(kd, p);
  690.     if (lu == NULL)
  691.     {
  692.     return(-1);
  693.     }
  694.     else
  695.     {
  696.     *u = *lu;
  697.     return(0);
  698.     }
  699. }
  700.  
  701. /*
  702.  * check_nlist(nlst) - checks the nlist to see if any symbols were not
  703.  *        found.  For every symbol that was not found, a one-line
  704.  *        message is printed to stderr.  The routine returns the
  705.  *        number of symbols NOT found.
  706.  */
  707.  
  708. int check_nlist(nlst)
  709.  
  710. register struct nlist *nlst;
  711.  
  712. {
  713.     register int i;
  714.  
  715.     /* check to see if we got ALL the symbols we requested */
  716.     /* this will write one line to stderr for every symbol not found */
  717.  
  718.     i = 0;
  719.     while (nlst->n_name != NULL)
  720.     {
  721. #ifdef i386
  722.     if (nlst->n_value == 0)
  723. #else
  724.     if (nlst->n_type == 0)
  725. #endif
  726.     {
  727.         /* this one wasn't found */
  728.         fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
  729.         i = 1;
  730.     }
  731.     nlst++;
  732.     }
  733.  
  734.     return(i);
  735. }
  736.  
  737.  
  738. /*
  739.  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
  740.  *    "offset" is the byte offset into the kernel for the desired value,
  741.  *      "ptr" points to a buffer into which the value is retrieved,
  742.  *      "size" is the size of the buffer (and the object to retrieve),
  743.  *      "refstr" is a reference string used when printing error meessages,
  744.  *        if "refstr" starts with a '!', then a failure on read will not
  745.  *          be fatal (this may seem like a silly way to do things, but I
  746.  *          really didn't want the overhead of another argument).
  747.  *      
  748.  */
  749.  
  750. getkval(offset, ptr, size, refstr)
  751.  
  752. unsigned long offset;
  753. int *ptr;
  754. int size;
  755. char *refstr;
  756.  
  757. {
  758.     if (kvm_read(kd, offset, ptr, size) != size)
  759.     {
  760.     if (*refstr == '!')
  761.     {
  762.         return(0);
  763.     }
  764.     else
  765.     {
  766.         fprintf(stderr, "top: kvm_read for %s: %s\n",
  767.         refstr, sys_errlist[errno]);
  768.         quit(23);
  769.         /*NOTREACHED*/
  770.     }
  771.     }
  772.     return(1);
  773. }
  774.     
  775. /* comparison routine for qsort */
  776.  
  777. /*
  778.  *  proc_compare - comparison function for "qsort"
  779.  *    Compares the resource consumption of two processes using five
  780.  *      distinct keys.  The keys (in descending order of importance) are:
  781.  *      percent cpu, cpu ticks, state, resident set size, total virtual
  782.  *      memory usage.  The process states are ordered as follows (from least
  783.  *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
  784.  *      array declaration below maps a process state index into a number
  785.  *      that reflects this ordering.
  786.  */
  787.  
  788. static unsigned char sorted_state[] =
  789. {
  790.     0,    /* not used        */
  791.     3,    /* sleep        */
  792.     1,    /* ABANDONED (WAIT)    */
  793.     6,    /* run            */
  794.     5,    /* start        */
  795.     2,    /* zombie        */
  796.     4    /* stop            */
  797. };
  798.  
  799. proc_compare(pp1, pp2)
  800.  
  801. struct proc **pp1;
  802. struct proc **pp2;
  803.  
  804. {
  805.     register struct proc *p1;
  806.     register struct proc *p2;
  807.     register int result;
  808.     register pctcpu lresult;
  809.  
  810.     /* remove one level of indirection */
  811.     p1 = *pp1;
  812.     p2 = *pp2;
  813.  
  814.     /* compare percent cpu (pctcpu) */
  815.     if ((lresult = p2->p_pctcpu - p1->p_pctcpu) == 0)
  816.     {
  817.     /* use cpticks to break the tie */
  818.     if ((result = p2->p_cpticks - p1->p_cpticks) == 0)
  819.     {
  820.         /* use process state to break the tie */
  821.         if ((result = sorted_state[p2->p_stat] -
  822.               sorted_state[p1->p_stat])  == 0)
  823.         {
  824.         /* use priority to break the tie */
  825.         if ((result = p2->p_pri - p1->p_pri) == 0)
  826.         {
  827.             /* use resident set size (rssize) to break the tie */
  828.             if ((result = p2->p_rssize - p1->p_rssize) == 0)
  829.             {
  830.             /* use total memory to break the tie */
  831.             result = PROCSIZE(p2) - PROCSIZE(p1);
  832.             }
  833.         }
  834.         }
  835.     }
  836.     }
  837.     else
  838.     {
  839.     result = lresult < 0 ? -1 : 1;
  840.     }
  841.  
  842.     return(result);
  843. }
  844.  
  845. /*
  846.  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
  847.  *        the process does not exist.
  848.  *        It is EXTREMLY IMPORTANT that this function work correctly.
  849.  *        If top runs setuid root (as in SVR4), then this function
  850.  *        is the only thing that stands in the way of a serious
  851.  *        security problem.  It validates requests for the "kill"
  852.  *        and "renice" commands.
  853.  */
  854.  
  855. int proc_owner(pid)
  856.  
  857. int pid;
  858.  
  859. {
  860.     register int cnt;
  861.     register struct proc **prefp;
  862.     register struct proc *pp;
  863.  
  864.     prefp = pref;
  865.     cnt = pref_len;
  866.     while (--cnt >= 0)
  867.     {
  868.     if ((pp = *prefp++)->p_pid == (pid_t)pid)
  869.     {
  870.         return((int)pp->p_uid);
  871.     }
  872.     }
  873.     return(-1);
  874. }
  875.