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

  1. /*
  2.  * top - a top users display for Unix
  3.  *
  4.  * SYNOPSIS:  DEC Alpha AXP running OSF/1, versions 1.2, 1.3, 2.0, 3.0
  5.  *
  6.  * DESCRIPTION:
  7.  * This is the machine-dependent module for DEC OSF/1 
  8.  * It is known to work on OSF/1 1.2, 1.3, 2.0-T3, and 3.0
  9.  * WARNING: do not use optimization with the standard "cc" compiler that
  10.  * .        comes with V3.0.  There is some sort of bug that causes the
  11.  * .        resulting executable to core dump.  When asked for compiler
  12.  * .        options, do NOT specify -O.
  13.  *
  14.  * LIBS: -lmld -lmach
  15.  *
  16.  * CFLAGS: -DHAVE_GETOPT
  17.  *
  18.  * AUTHOR:  Anthony Baxter, <anthony@aaii.oz.au>
  19.  * Derived originally from m_ultrix, by David S. Comay <dsc@seismo.css.gov>, 
  20.  * although by now there is hardly any of the code from m_ultrix left.
  21.  * Helped a lot by having the source for syd(1), by Claus Kalle, and
  22.  * from several people at DEC who helped with providing information on
  23.  * some of the less-documented bits of the kernel interface.
  24.  *
  25.  * Modified: 31-Oct-94, Pat Welch, tpw@physics.orst.edu
  26.  *    changed _mpid to pidtab for compatibility with OSF/1 version 3.0
  27.  *
  28.  * Modified: 13-Dec-94, William LeFebvre, lefebvre@dis.anl.gov
  29.  *    removed used of pidtab (that was bogus) and changed things to
  30.  *    automatically detect the absence of _mpid in the nlist and
  31.  *    recover gracefully---this appears to be the only difference
  32.  *    with 3.0.
  33.  */
  34. /* 
  35.  * $Id: m_decosf1.c,v 1.14 1994/01/18 07:34:42 anthony Exp $
  36.  * Theres some real icky bits in this code - you have been warned :)
  37.  * Extremely icky bits are marked with FIXME: 
  38.  *
  39.  * Theory of operation: 
  40.  * 
  41.  * Use Mach calls to build up a structure that contains all the sorts
  42.  * of stuff normally found in a struct proc in a BSD system. Then
  43.  * everything else uses this structure. This has major performance wins,
  44.  * and also should work for future versions of the O/S.
  45.  */
  46.  
  47. #include <sys/types.h>
  48. #include <sys/signal.h>
  49. #include <sys/param.h>
  50.  
  51. #include <string.h>
  52. #include <sys/user.h>
  53. #include <stdio.h>
  54. #include <nlist.h>
  55. #include <math.h>
  56. #include <sys/dir.h>
  57. #include <sys/user.h>
  58. #include <sys/proc.h>
  59. #include <sys/dk.h>
  60. #include <sys/vm.h>
  61. #include <sys/file.h>
  62. #include <sys/time.h>
  63. /* #include <machine/pte.h> */
  64. #include <sys/table.h>
  65. #include <mach.h>
  66. #include <mach/mach_types.h>
  67. #include <mach/vm_statistics.h>
  68. #include <sys/syscall.h> /* for SYS_setpriority, in setpriority(), below */
  69.  
  70.  
  71. #include "top.h"
  72. #include "machine.h"
  73.  
  74. extern int errno, sys_nerr;
  75. extern char *sys_errlist[];
  76. #define strerror(e) (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error")
  77.  
  78. #define VMUNIX    "/vmunix"
  79. #define KMEM    "/dev/kmem"
  80. #define MEM    "/dev/mem"
  81.  
  82. /* get_process_info passes back a handle.  This is what it looks like: */
  83.  
  84. struct handle
  85. {
  86.     struct osf1_top_proc **next_proc;    /* points to next valid proc pointer */
  87.     int remaining;        /* number of pointers remaining */
  88. };
  89.  
  90. /* declarations for load_avg */
  91. #include "loadavg.h"
  92.  
  93. /* definitions for indices in the nlist array */
  94. #define X_MPID        0
  95.  
  96. static struct nlist nlst[] = {
  97.     { "_mpid" },        /* 0 */
  98.     { 0 }
  99. };
  100.  
  101. /* Some versions of OSF/1 don't support reporting of the last PID.
  102.    This flag indicates whether or not we are reporting the last PID. */
  103. static int do_last_pid = 1;
  104.  
  105. /*
  106.  *  These definitions control the format of the per-process area
  107.  */
  108.  
  109. static char header[] =
  110.   "  PID X        PRI NICE  SIZE   RES STATE   TIME    CPU COMMAND";
  111. /* 0123456   -- field to fill in starts at header+6 */
  112. #define UNAME_START 6
  113.  
  114. #define Proc_format \
  115.     "%5d %-8.8s %3d %4d %5s %5s %-5s %-6s %5.2f%% %.14s"
  116.  
  117.  
  118. /* process state names for the "STATE" column of the display */
  119. /* the extra nulls in the string "run" are for adding a slash and
  120.  * the processor number when needed. Although OSF/1 doesnt support
  121.  * multiple processors yet, (and this module _certainly_ doesnt
  122.  * support it, either, we may as well plan for the future. :-)
  123.  */
  124.  
  125. char *state_abbrev[] =
  126. {
  127.     "", "run\0\0\0", "WAIT", "sleep", "sleep", "stop", "halt", "???", "zomb"
  128. };
  129.  
  130.  
  131. static int kmem, mem;
  132.  
  133. /* values that we stash away in _init and use in later routines */
  134.  
  135. static double logcpu;
  136.  
  137. /* these are retrieved from the kernel in _init */
  138.  
  139. static unsigned long proc;
  140. static          int  nproc;
  141. static load_avg  ccpu;
  142.  
  143. typedef long mtime_t;
  144.  
  145. /* these are offsets obtained via nlist and used in the get_ functions */
  146.  
  147. static unsigned long mpid_offset;
  148.  
  149. /* these are for detailing the process states */
  150.  
  151. int process_states[7];
  152. char *procstatenames[] = {
  153.     "", " running, ", " waiting, ", " sleeping, ", " idle, ",
  154.     " stopped", "",
  155.     NULL
  156. };
  157.  
  158. /* these are for detailing the cpu states */
  159.  
  160. int cpu_states[4];
  161. char *cpustatenames[] = {
  162.     "user", "nice", "system", "idle", NULL
  163. };
  164.  
  165. long old_cpu_ticks[4];
  166.  
  167. /* these are for detailing the memory statistics */
  168.  
  169. int memory_stats[8];
  170. char *memorynames[] = {
  171.     "Real: ", "K/", "K act/tot  ", "Virtual: ", "M/",
  172.     "M use/tot  ", "Free: ", "K", NULL
  173. };
  174.  
  175. /* these are for getting the memory statistics */
  176.  
  177. static int pageshift;        /* log base 2 of the pagesize */
  178.  
  179. /* define pagetok in terms of pageshift */
  180.  
  181. #define pagetok(size) ((size) << pageshift)
  182.  
  183. /* take a process, make it a mach task, and grab all the info out */
  184. void do_threads_calculations();
  185.  
  186. /*
  187.  * Because I dont feel like repeatedly grunging through the kernel with
  188.  * Mach calls, and I also dont want the horrid performance hit this
  189.  * would give, I read the stuff I need out, and put in into my own
  190.  * structure, for later use.
  191.  */
  192.  
  193. struct osf1_top_proc {
  194.     size_t p_mach_virt_size;
  195.     char p_mach_state;
  196.     fixpt_t p_mach_pct_cpu; /* aka p_pctcpu */
  197.     int used_ticks;
  198.     size_t process_size;
  199.     pid_t p_pid;
  200.     uid_t p_ruid;
  201.     char p_pri;
  202.     char p_nice;
  203.     size_t p_rssize;
  204.     char u_comm[PI_COMLEN + 1];
  205. } ;
  206.  
  207. /* these are for keeping track of the proc array */
  208.  
  209. static int bytes;
  210. static int pref_len;
  211. static struct osf1_top_proc *pbase;
  212. static struct osf1_top_proc **pref;
  213.  
  214. /* useful externals */
  215. extern int errno;
  216. extern char *sys_errlist[];
  217.  
  218. long percentages();
  219.  
  220. machine_init(statics)
  221. struct statics *statics;
  222. {
  223.     register int i = 0;
  224.     register int pagesize;
  225.     struct tbl_sysinfo sibuf;
  226.  
  227.     if ((kmem = open(KMEM, O_RDONLY)) == -1) {
  228.     perror(KMEM);
  229.     return(-1);
  230.     }
  231.     if ((mem = open(MEM, O_RDONLY)) == -1) {
  232.     perror(MEM);
  233.     return(-1);
  234.     }
  235.  
  236.     /* get the list of symbols we want to access in the kernel */
  237.     if (nlist(VMUNIX, nlst) == -1)
  238.     {
  239.     perror("TOP(nlist)");
  240.     return (-1);
  241.     }
  242.  
  243.     if (nlst[X_MPID].n_type == 0)
  244.     {
  245.     /* this kernel has no _mpid, so go without */
  246.     do_last_pid = 0;
  247.     }
  248.     else
  249.     {
  250.     /* stash away mpid pointer for later use */
  251.     mpid_offset = nlst[X_MPID].n_value;
  252.     }
  253.  
  254.     /* get the symbol values out of kmem */
  255.     nproc  = table(TBL_PROCINFO, 0, (struct tbl_procinfo *)NULL, 32767, 0);
  256.  
  257.     /* allocate space for proc structure array and array of pointers */
  258.     bytes = nproc * sizeof(struct osf1_top_proc);
  259.     pbase = (struct osf1_top_proc *)malloc(bytes);
  260.     pref  = (struct osf1_top_proc **)malloc(nproc * 
  261.                                               sizeof(struct osf1_top_proc *));
  262.  
  263.     /* Just in case ... */
  264.     if (pbase == (struct osf1_top_proc *)NULL || 
  265.                                   pref == (struct osf1_top_proc **)NULL)
  266.     {
  267.     fprintf(stderr, "top: cannot allocate sufficient memory\n");
  268.     return(-1);
  269.     }
  270.  
  271.     /* get the page size with "getpagesize" and calculate pageshift from it */
  272.     pagesize = getpagesize();
  273.     pageshift = 0;
  274.     while (pagesize > 1)
  275.     {
  276.     pageshift++;
  277.     pagesize >>= 1;
  278.     }
  279.  
  280.     /* we only need the amount of log(2)1024 for our conversion */
  281.     pageshift -= LOG1024;
  282.  
  283.     /* fill in the statics information */
  284.     statics->procstate_names = procstatenames;
  285.     statics->cpustate_names = cpustatenames;
  286.     statics->memory_names = memorynames;
  287.  
  288.     /* initialise this, for calculating cpu time */
  289.     if (table(TBL_SYSINFO,0,&sibuf,1,sizeof(struct tbl_sysinfo))<0) {
  290.     perror("TBL_SYSINFO");
  291.     return(-1);
  292.     }
  293.     old_cpu_ticks[0] = sibuf.si_user;
  294.     old_cpu_ticks[1] = sibuf.si_nice;
  295.     old_cpu_ticks[2] = sibuf.si_sys;
  296.     old_cpu_ticks[3] = sibuf.si_idle;
  297.  
  298.     /* all done! */
  299.     return(0);
  300. }
  301.  
  302. char *format_header(uname_field)
  303. register char *uname_field;
  304. {
  305.     register char *ptr;
  306.  
  307.     ptr = header + UNAME_START;
  308.     while (*uname_field != '\0')
  309.     {
  310.     *ptr++ = *uname_field++;
  311.     }
  312.  
  313.     return(header);
  314. }
  315.  
  316. get_system_info(si)
  317. struct system_info *si;
  318. {
  319.     struct tbl_loadavg labuf;
  320.     struct tbl_sysinfo sibuf;
  321.     struct tbl_swapinfo swbuf;
  322.     vm_statistics_data_t vmstats;
  323.     int swap_pages=0,swap_free=0,i;
  324.     long new_ticks[4],diff_ticks[4];
  325.     long delta_ticks;
  326.  
  327.     if (do_last_pid)
  328.     {
  329.     /* last pid assigned */
  330.     (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), 
  331.                "_mpid");
  332.     }
  333.     else
  334.     {
  335.     si->last_pid = -1;
  336.     }
  337.  
  338.     /* get load averages */
  339.     if (table(TBL_LOADAVG,0,&labuf,1,sizeof(struct tbl_loadavg))<0) {
  340.     perror("TBL_LOADAVG");
  341.     return(-1);
  342.     }
  343.     if (labuf.tl_lscale)   /* scaled */
  344.     for(i=0;i<3;i++) 
  345.         si->load_avg[i] = ((double)labuf.tl_avenrun.l[i] / 
  346.                                             (double)labuf.tl_lscale );
  347.     else                   /* not scaled */
  348.     for(i=0;i<3;i++) 
  349.         si->load_avg[i] = labuf.tl_avenrun.d[i];
  350.  
  351.     /* array of cpu state counters */
  352.     if (table(TBL_SYSINFO,0,&sibuf,1,sizeof(struct tbl_sysinfo))<0) {
  353.     perror("TBL_SYSINFO");
  354.     return(-1);
  355.     }
  356.     new_ticks[0] = sibuf.si_user ; new_ticks[1] = sibuf.si_nice;
  357.     new_ticks[2] = sibuf.si_sys  ; new_ticks[3] = sibuf.si_idle;
  358.     delta_ticks=0;
  359.     for(i=0;i<4;i++) {
  360.     diff_ticks[i] = new_ticks[i] - old_cpu_ticks[i];
  361.     delta_ticks += diff_ticks[i];
  362.     old_cpu_ticks[i] = new_ticks[i];
  363.     }
  364.     si->cpustates = cpu_states;
  365.     if(delta_ticks)
  366.     for(i=0;i<4;i++) 
  367.         si->cpustates[i] = (int)( ( (double)diff_ticks[i] / 
  368.                                            (double)delta_ticks ) * 1000 );
  369.     
  370.     /* memory information */
  371.     /* this is possibly bogus - we work out total # pages by */
  372.     /* adding up the free, active, inactive, wired down, and */
  373.     /* zero filled. Anyone who knows a better way, TELL ME!  */
  374.     /* Change: dont use zero filled. */
  375.     (void) vm_statistics(task_self(),&vmstats);
  376.  
  377.     /* thanks DEC for the table() command. No thanks at all for   */
  378.     /* omitting the man page for it from OSF/1 1.2, and failing   */
  379.     /* to document SWAPINFO in the 1.3 man page. Lets hear it for */
  380.     /* include files. */
  381.     i=0;
  382.     while(table(TBL_SWAPINFO,i,&swbuf,1,sizeof(struct tbl_swapinfo))>0) {
  383.     swap_pages += swbuf.size;
  384.     swap_free  += swbuf.free;
  385.     i++;
  386.     }
  387.     memory_stats[0] = -1;
  388.     memory_stats[1] = pagetok(vmstats.active_count);
  389.     memory_stats[2] = pagetok((vmstats.free_count + vmstats.active_count +
  390.     vmstats.inactive_count + vmstats.wire_count));
  391.     memory_stats[3] = -1;
  392.     memory_stats[4] = pagetok((swap_pages - swap_free))/1024;
  393.     memory_stats[5] = pagetok(swap_pages)/1024;
  394.     memory_stats[6] = -1;
  395.     memory_stats[7] = pagetok(vmstats.free_count);
  396.     si->memory = memory_stats;
  397. }
  398.  
  399. static struct handle handle;
  400.  
  401. caddr_t get_process_info(si, sel, compare)
  402. struct system_info *si;
  403. struct process_select *sel;
  404. int (*compare)();
  405. {
  406.     register int i;
  407.     register int total_procs;
  408.     register int active_procs;
  409.     register struct osf1_top_proc **prefp;
  410.     register struct osf1_top_proc *pp;
  411.     struct tbl_procinfo p_i[8];
  412.     int j,k,r;
  413.  
  414.     /* these are copied out of sel for speed */
  415.     int show_idle;
  416.     int show_system;
  417.     int show_uid;
  418.     int show_command;
  419.  
  420.     /* get a pointer to the states summary array */
  421.     si->procstates = process_states;
  422.  
  423.     /* set up flags which define what we are going to select */
  424.     show_idle = sel->idle;
  425.     show_system = sel->system;
  426.     show_uid = sel->uid != -1;
  427.     show_command = sel->command != NULL;
  428.  
  429.     /* count up process states and get pointers to interesting procs */
  430.     total_procs = 0;
  431.     active_procs = 0;
  432.     memset((char *)process_states, 0, sizeof(process_states));
  433.     process_states[0]=0;
  434.     process_states[1]=0;
  435.     process_states[2]=0;
  436.     process_states[3]=0;
  437.     process_states[4]=0;
  438.     process_states[5]=0;
  439.     process_states[6]=0;
  440.     prefp = pref;
  441.     pp=pbase;
  442.     for (j=0; j<nproc; j += 8) 
  443.     {
  444.     r = table(TBL_PROCINFO, j, (struct tbl_procinfo *)p_i, 8, 
  445.                                                sizeof(struct tbl_procinfo));
  446.     for (k=0; k < r; k++ , pp++) 
  447.     {
  448.         if(p_i[k].pi_pid == 0) 
  449.         {
  450.         pp->p_pid = 0;
  451.         }
  452.         else
  453.         {
  454.         pp->p_pid = p_i[k].pi_pid;
  455.         pp->p_ruid = p_i[k].pi_ruid;
  456.         pp->p_nice = getpriority(PRIO_PROCESS,p_i[k].pi_pid);
  457.         /* Load useful values into the proc structure */
  458.         do_threads_calculations(pp);
  459.         /*
  460.          *  Place pointers to each valid proc structure in pref[].
  461.          *  Process slots that are actually in use have a non-zero
  462.          *  status field.  Processes with SSYS set are system
  463.          *  processes---these get ignored unless show_sysprocs is set.
  464.          */
  465.         if (pp->p_mach_state != 0)
  466.         {
  467.             total_procs++;
  468.             process_states[pp->p_mach_state]++;
  469.             if ((pp->p_mach_state != SZOMB) &&
  470.             (pp->p_mach_state != SIDL) &&
  471.             (show_idle || (pp->p_mach_pct_cpu != 0) || 
  472.                                    (pp->p_mach_state == SRUN)) &&
  473.             (!show_uid || pp->p_ruid == (uid_t)sel->uid)) {
  474.             *prefp++ = pp;
  475.             active_procs++;
  476.             }
  477.         }
  478.         }
  479.     }
  480.     }
  481.  
  482.     /* if requested, sort the "interesting" processes */
  483.     if (compare != NULL)
  484.     {
  485.     qsort((char *)pref, active_procs, sizeof(struct osf1_top_proc *), 
  486.                                                                     compare);
  487.     }
  488.  
  489.     /* remember active and total counts */
  490.     si->p_total = total_procs;
  491.     si->p_active = pref_len = active_procs;
  492.  
  493.     /* pass back a handle */
  494.     handle.next_proc = pref;
  495.     handle.remaining = active_procs;
  496.     return((caddr_t)&handle);
  497. }
  498.  
  499. char fmt[128];        /* static area where result is built */
  500.  
  501. char *format_next_process(handle, get_userid)
  502. caddr_t handle;
  503. char *(*get_userid)();
  504. {
  505.     register struct osf1_top_proc *pp;
  506.     register long cputime;
  507.     register double pct;
  508.     int where;
  509.     struct user u;
  510.     struct handle *hp;
  511.  
  512.     /* find and remember the next proc structure */
  513.     hp = (struct handle *)handle;
  514.     pp = *(hp->next_proc++);
  515.     hp->remaining--;
  516.  
  517.     /* get the process's user struct and set cputime */
  518.     
  519.     if (table(TBL_UAREA,pp->p_pid,&u,1,sizeof(struct user))<0) {
  520.     /* whoops, it must have died between the read of the proc area
  521.      * and now. Oh well, lets just dump some meaningless thing out
  522.      * to keep the rest of the program happy
  523.      */
  524.     sprintf(fmt,
  525.         Proc_format,
  526.         pp->p_pid,
  527.         (*get_userid)(pp->p_ruid),
  528.         0,
  529.         0,
  530.         "",
  531.         "",
  532.         "dead",
  533.         "",
  534.         0.0,
  535.         "<dead>");
  536.         return(fmt);
  537.     }
  538.  
  539.     /* set u_comm for system processes */
  540.     if (u.u_comm[0] == '\0')
  541.     {
  542.     if (pp->p_pid == 0)
  543.     {
  544.         (void) strcpy(u.u_comm, "[idle]");
  545.     }
  546.     else if (pp->p_pid == 2)
  547.     {
  548.         (void) strcpy(u.u_comm, "[execpt.hndlr]");
  549.     }
  550.     }
  551.     if (where == 1) {
  552.     /*
  553.      * Print swapped processes as <pname>
  554.      */
  555.     char buf[sizeof(u.u_comm)];
  556.     (void) strncpy(buf, u.u_comm, sizeof(u.u_comm));
  557.     u.u_comm[0] = '<';
  558.     (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2);
  559.     u.u_comm[sizeof(u.u_comm) - 2] = '\0';
  560.     (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1);
  561.     u.u_comm[sizeof(u.u_comm) - 1] = '\0';
  562.     }
  563.  
  564.     cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec;
  565.  
  566.     /* calculate the base for cpu percentages */
  567.     pct = pctdouble(pp->p_mach_pct_cpu);
  568.  
  569.     /* format this entry */
  570.     sprintf(fmt,
  571.         Proc_format,
  572.         pp->p_pid,
  573.         (*get_userid)(pp->p_ruid),
  574.         pp->p_pri,
  575.         pp->p_nice,
  576.             format_k(pp->p_mach_virt_size/1024),
  577.             format_k(pp->p_rssize/1000),
  578.         state_abbrev[pp->p_mach_state],
  579.         format_time(cputime),
  580.         100.0 * ((double)pp->p_mach_pct_cpu / 10000.0),
  581.         printable(u.u_comm));
  582.  
  583.     /* return the result */
  584.     return(fmt);
  585. }
  586.  
  587. /*
  588.  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
  589.  *    "offset" is the byte offset into the kernel for the desired value,
  590.  *      "ptr" points to a buffer into which the value is retrieved,
  591.  *      "size" is the size of the buffer (and the object to retrieve),
  592.  *      "refstr" is a reference string used when printing error meessages,
  593.  *        if "refstr" starts with a '!', then a failure on read will not
  594.  *          be fatal (this may seem like a silly way to do things, but I
  595.  *          really didn't want the overhead of another argument).
  596.  *      
  597.  */
  598.  
  599. getkval(offset, ptr, size, refstr)
  600.  
  601. unsigned long offset;
  602. int *ptr;
  603. int size;
  604. char *refstr;
  605.  
  606. {
  607.     if (lseek(kmem, (long)offset, L_SET) == -1) {
  608.         if (*refstr == '!')
  609.             refstr++;
  610.         (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, 
  611.                refstr, strerror(errno));
  612.         quit(23);
  613.     }
  614.     if (read(kmem, (char *) ptr, size) == -1) {
  615.         if (*refstr == '!') 
  616.             return(0);
  617.         else {
  618.             (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, 
  619.                refstr, strerror(errno));
  620.             quit(23);
  621.         }
  622.     }
  623.     return(1);
  624. }
  625.     
  626. /* comparison routine for qsort */
  627.  
  628. /*
  629.  *  proc_compare - comparison function for "qsort"
  630.  *   Compares the resource consumption of two processes using five
  631.  *   distinct keys. The keys (in descending order of importance) are:
  632.  *   percent cpu, cpu ticks, state, resident set size, total virtual
  633.  *   memory usage. The process states are ordered as follows (from least
  634.  *   to most important): WAIT, zombie, sleep, stop, start, run. The array
  635.  *   declaration below maps a process state index into a number that
  636.  *   reflects this ordering.
  637.  */
  638.  
  639. static unsigned char sorted_state[] =
  640. {
  641.    0, /*""*/
  642.    8, /*"run"*/
  643.    1, /*"WAIT"*/
  644.    6, /*"sleep"*/
  645.    5, /*"idle"*/
  646.    7, /*"stop"*/
  647.    4, /*"halt"*/
  648.    3, /*"???"*/
  649.    2, /*"zomb"*/
  650. };
  651.  
  652. proc_compare(pp1, pp2)
  653.  
  654. struct osf1_top_proc **pp1;
  655. struct osf1_top_proc **pp2;
  656.  
  657. {
  658.     register struct osf1_top_proc *p1;
  659.     register struct osf1_top_proc *p2;
  660.     register int result;
  661.     register pctcpu lresult=0.0;
  662.     struct user u1, u2;
  663.  
  664.     /* remove one level of indirection */
  665.     p1 = *pp1;
  666.     p2 = *pp2;
  667.  
  668.     /* compare percent cpu (pctcpu) */
  669.     if ((lresult = p2->p_mach_pct_cpu - p1->p_mach_pct_cpu) == 0)
  670.     {
  671.     /* use process state to break the tie */
  672.     if ((result = sorted_state[p2->p_mach_state] -
  673.           sorted_state[p1->p_mach_state])  == 0)
  674.     {
  675.         /* use elapsed time to break the tie */
  676.         if ((result = p2->used_ticks - p1->used_ticks) == 0)
  677.         {
  678.         /* use priority to break the tie */
  679.         if ((result = p2->p_pri - p1->p_pri) == 0)
  680.         {
  681.             /* use resident set size (rssize) to break the tie */
  682.             if ((result = p2->p_rssize - p1->p_rssize) == 0)
  683.             {
  684.             /* use total memory to break the tie */
  685.             result = p2->process_size - p1->process_size;
  686.             }
  687.         }
  688.         }
  689.     }
  690.     }
  691.  
  692.     if(lresult)
  693.     result = lresult < 0 ? -1 : 1;
  694.     return(result);
  695. }
  696.  
  697. /*
  698.  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
  699.  *        the process does not exist.
  700.  *        It is EXTREMLY IMPORTANT that this function work correctly.
  701.  *        If top runs setuid root (as in SVR4), then this function
  702.  *        is the only thing that stands in the way of a serious
  703.  *        security problem.  It validates requests for the "kill"
  704.  *        and "renice" commands.
  705.  */
  706.  
  707. int proc_owner(pid)
  708.  
  709. int pid;
  710.  
  711. {
  712.     register int cnt;
  713.     register struct osf1_top_proc **prefp;
  714.     register struct osf1_top_proc *pp;
  715.  
  716.     prefp = pref;
  717.     cnt = pref_len;
  718.     while (--cnt >= 0)
  719.     {
  720.     if ((pp = *prefp++)->p_pid == (pid_t)pid)
  721.     {
  722.         return((int)pp->p_ruid);
  723.     }
  724.     }
  725.     return(-1);
  726. }
  727.  
  728.  
  729. /*
  730.  * We use the Mach interface, as well as the table(UAREA,,,) call to
  731.  * get some more information, then put it into unused fields in our
  732.  * copy of the proc structure, to make it faster and easier to get at
  733.  * later.
  734.  */
  735. void do_threads_calculations(thisproc)
  736. struct osf1_top_proc *thisproc; 
  737. {
  738.   int j;
  739.   task_t  thistask;
  740.   task_basic_info_data_t   taskinfo;
  741.   unsigned int taskinfo_l;
  742.   thread_array_t    threadarr;
  743.   unsigned int threadarr_l;
  744.   thread_basic_info_t     threadinfo;
  745.   thread_basic_info_data_t threadinfodata;
  746.   unsigned int threadinfo_l;
  747.   int task_tot_cpu=0;  /* total cpu usage of threads in a task */
  748.   struct user u;
  749.  
  750.   thisproc->p_pri=0; 
  751.   thisproc->p_rssize=0; 
  752.   thisproc->p_mach_virt_size=0; 
  753.   thisproc->p_mach_state=0; 
  754.   thisproc->p_mach_pct_cpu=0;
  755.  
  756.   if(task_by_unix_pid(task_self(), thisproc->p_pid, &thistask) 
  757.                                                 != KERN_SUCCESS){
  758.       thisproc->p_mach_state=8; /* (zombie) */
  759.   } else {
  760.     taskinfo_l=TASK_BASIC_INFO_COUNT;
  761.     if(task_info(thistask, TASK_BASIC_INFO, (task_info_t) &taskinfo, 
  762.                                       &taskinfo_l)
  763.        != KERN_SUCCESS) {
  764.       thisproc->p_mach_state=8; /* (zombie) */
  765.     } else {
  766.       int minim_state=99,mcurp=1000,mbasp=1000,mslpt=999;
  767.  
  768.       thisproc->p_rssize=taskinfo.resident_size;
  769.       thisproc->p_mach_virt_size=taskinfo.virtual_size;
  770.  
  771.       (void) task_threads(thistask, &threadarr, &threadarr_l);
  772.       threadinfo= &threadinfodata;
  773.       for(j=0; j < threadarr_l; j++) {
  774.     threadinfo_l=THREAD_BASIC_INFO_COUNT;
  775.     if(thread_info(threadarr[j],THREAD_BASIC_INFO,
  776.            (thread_info_t) threadinfo, &threadinfo_l) == KERN_SUCCESS) {
  777.         
  778.       task_tot_cpu += threadinfo->cpu_usage;
  779.       if(minim_state>threadinfo->run_state) 
  780.               minim_state=threadinfo->run_state;
  781.       if(mcurp>threadinfo->cur_priority) 
  782.               mcurp=threadinfo->cur_priority;
  783.       if(mbasp>threadinfo->base_priority) 
  784.               mbasp=threadinfo->base_priority;
  785.       if(mslpt>threadinfo->sleep_time) 
  786.               mslpt=threadinfo->sleep_time;
  787.     }
  788.       }
  789.       switch (minim_state) {
  790.       case TH_STATE_RUNNING:      
  791.         thisproc->p_mach_state=1;  break;
  792.       case TH_STATE_UNINTERRUPTIBLE: 
  793.         thisproc->p_mach_state=2; break;
  794.       case TH_STATE_WAITING:      
  795.         thisproc->p_mach_state=(threadinfo->sleep_time > 20) ? 4 : 3; break;
  796.       case TH_STATE_STOPPED:      
  797.         thisproc->p_mach_state=5; break;
  798.       case TH_STATE_HALTED:       
  799.         thisproc->p_mach_state=6; break;
  800.       default:                    
  801.         thisproc->p_mach_state=7; break;
  802.       }
  803.  
  804.       thisproc->p_pri=mcurp;
  805.       thisproc->p_mach_pct_cpu=(fixpt_t)(task_tot_cpu*10);
  806.       vm_deallocate(task_self(),(vm_address_t)threadarr,threadarr_l);
  807.     }
  808.   }
  809.   if (table(TBL_UAREA,thisproc->p_pid,&u,1,sizeof(struct user))>=0) {
  810.     thisproc->used_ticks=(u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec);
  811.     thisproc->process_size=u.u_tsize + u.u_dsize + u.u_ssize;
  812.   }
  813. }
  814.  
  815. /* The reason for this function is that the system call will let
  816.  * someone lower their own processes priority (because top is setuid :-(
  817.  * Yes, using syscall() is a hack, if you can come up with something 
  818.  * better, then I'd be thrilled to hear it. I'm not holding my breath,
  819.  * though.  
  820.  *             Anthony.
  821.  */
  822. int setpriority(int dummy, int procnum, int niceval)
  823. {
  824.  
  825.     int uid, curprio;
  826.  
  827.     uid=getuid();
  828.     if ( (curprio=getpriority(PRIO_PROCESS,procnum) ) == -1) 
  829.     {
  830.     return(-1); /* errno goes back to renice_process() */
  831.     }
  832.     /* check for not-root - if so, dont allow users to decrease priority */
  833.     else if ( uid && (niceval<curprio) )
  834.     {
  835.     errno=EACCES;
  836.     return(-1);
  837.     }
  838.     return(syscall(SYS_setpriority,PRIO_PROCESS,procnum,niceval));
  839. }
  840.