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

  1. /*
  2.  * top - a top users display for Unix
  3.  *
  4.  * SYNOPSIS:  a Mac running A/UX version 3.1
  5.  *
  6.  * DESCRIPTION:
  7.  * This is the machine-dependent module for A/UX 3.1.
  8.  * It might work on A/UX 3.0.
  9.  * ==
  10.  * Although AUX does not generally have a renice systemcall, it can be
  11.  * implemented by tweeking kernel memory.  While such a simple hack should
  12.  * not be difficult to get right, USE THIS FEATURE AT YOUR OWN RISK!
  13.  * To turn on setpriority emulation, add "-DIMPLEMENT_SETPRIORITY" to
  14.  * the CFLAGS when prompted in the configure script.
  15.  *
  16.  * CFLAGS: -Dclear=clear_scr -DPRIO_PROCESS=0
  17.  *
  18.  * LIBS:
  19.  *
  20.  * AUTHOR:  Richard Henderson <richard@atheist.tamu.edu>
  21.  */
  22.  
  23.  
  24. #include <stdio.h>
  25. #include <errno.h>
  26. #include <fcntl.h>
  27. #include <a.out.h>
  28. #include <sys/types.h>
  29. #include <sys/signal.h>
  30. #include <sys/param.h>
  31. #include <sys/proc.h>
  32. #include <sys/user.h>
  33. #include <sys/sysinfo.h>
  34. #include <sys/var.h>
  35.  
  36. #define FSCALE    65536.0
  37.  
  38. #include "top.h"
  39. #include "machine.h"
  40. #include "loadavg.h"
  41.  
  42. /*=NLIST INFO===========================================================*/
  43.  
  44. #define X_V        0
  45. #define X_SYSINFO    1
  46. #define X_AVENRUN    2
  47. #define X_MAXMEM    3
  48. #define X_FREEMEM    4
  49. #define X_AVAILRMEM    5
  50. #define X_AVAILSMEM    6
  51.  
  52. static struct nlist nlst[] = {
  53.     {"v"},
  54.     {"sysinfo"},
  55.     {"avenrun"},
  56.     {"maxmem"},
  57.     {"freemem"},
  58.     {0},        /* "availrmem" */
  59.     {0},        /* "availsmem" */
  60.     {0}
  61. };
  62.  
  63. static int kmem;
  64. static int mem;
  65.  
  66. static struct var v;
  67. static int maxmem;
  68.  
  69. #define V_OFS        (nlst[X_V].n_value)
  70. #define SYSINFO_OFS    (nlst[X_SYSINFO].n_value)
  71. #define AVENRUN_OFS    (nlst[X_AVENRUN].n_value)
  72. #define MAXMEM_OFS    (nlst[X_MAXMEM].n_value)
  73. #define FREEMEM_OFS    (nlst[X_FREEMEM].n_value)
  74. #define AVAILRMEM_OFS    (nlst[X_AVAILRMEM].n_value)
  75. #define AVAILSMEM_OFS    (nlst[X_AVAILSMEM].n_value)
  76.  
  77. /*=SYSTEM STATE INFO====================================================*/
  78.  
  79. /* these are for calculating cpu state percentages */
  80.  
  81. static long cp_time[NCPUSTATES];
  82. static long cp_old[NCPUSTATES];
  83. static long cp_diff[NCPUSTATES];
  84.  
  85. /* these are for keeping track of the proc array */
  86.  
  87. static struct proc *pbase;    /* the current proc structures */
  88. static struct proc *obase;    /* the old proc structures */
  89.  
  90. static struct proc **pref;    /* list of active structures */
  91. static struct proc **nextactive; /* for iterating through the processes */
  92.  
  93. /* these are for passing data back to the mach. ind. portion */
  94.  
  95. static int cpu_states[NCPUSTATES];
  96. static int process_states[8];
  97. static int memory_stats[5];
  98.  
  99. /* a few useful macros... */
  100.  
  101. #define pagetok(pg)        ((pg) << (v.v_pageshift - LOG1024))
  102. #define PROCSIZE(pp)        ((pp)->p_size)
  103.  
  104. #define proc_name(pp)        ((char *)&(pp)->p_compatflags)
  105. #define percent_cpu(pp)        (*(double *)&(pp)->p_spare[0])
  106. #define weighted_cpu(pp)    (*(double *)&(pp)->p_spare[2])
  107.  
  108. /*=STATE IDENT STRINGS==================================================*/
  109.  
  110. static char *state_abbrev[] =
  111. {
  112.     "", "sleep", "run", "zomb", "stop", "start", "cpu", "swap",
  113.     NULL
  114. };
  115.  
  116. static char *procstatenames[] =
  117. {
  118.     "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
  119.     " starting, ", " on cpu, ", " swapping, ",
  120.     NULL
  121. };
  122.  
  123. static char *cpustatenames[] =
  124. {
  125.     "idle", "user", "kernel", "wait", "nice",
  126.     NULL
  127. };
  128.  
  129. static char *memorynames[] = 
  130. {
  131.     "K real, ", "K free, ", "K free swap, ", "K locked",
  132.     NULL
  133. };
  134.  
  135. /*======================================================================*/
  136.  
  137. machine_init(statics)
  138.     struct statics *statics;
  139. {
  140.     /* access kernel memory */
  141. #ifndef IMPLEMENT_SETPRIORITY
  142.     if ((kmem = open("/dev/kmem", O_RDONLY)) < 0)
  143. #else
  144.     if ((kmem = open("/dev/kmem", O_RDWR)) < 0 &&
  145.     (kmem = open("/dev/kmem", O_RDONLY)) < 0)
  146. #endif
  147.     {
  148.     perror("/dev/kmem");
  149.     return -1;
  150.     }
  151.     if ((mem = open("/dev/mem", O_RDONLY)) < 0)
  152.     {
  153.     perror("/dev/mem");
  154.     return -1;
  155.     }
  156.  
  157.     /* get the list of symbols we want to access in the kernel */
  158.     nlst[X_AVAILRMEM].n_nptr = "availrmem";
  159.     nlst[X_AVAILSMEM].n_nptr = "availsmem";
  160.  
  161.     if (nlist("/unix", nlst) < 0)
  162.     {
  163.     fprintf(stderr, "top: nlist failed\n");
  164.     return -1;
  165.     }
  166.  
  167.     /* make sure they were all found */
  168.     if (check_nlist(nlst) > 0)
  169.     {
  170.     return (-1);
  171.     }
  172.  
  173.     /* grab the kernel configuration information */
  174.     (void)getkval(V_OFS, (char *)&v, sizeof(v), "v");
  175.     (void)getkval(MAXMEM_OFS, (char *)&maxmem, sizeof(maxmem), "maxmem");
  176.  
  177.     /* allocate space for process related info */
  178.     pbase = (struct proc *)calloc(v.v_proc, sizeof(struct proc));
  179.     obase = (struct proc *)calloc(v.v_proc, sizeof(struct proc));
  180.     pref = (struct proc **)calloc(v.v_proc, sizeof(struct proc *));
  181.  
  182.     /* Just in case ... */
  183.     if (!pbase || !obase || !pref)
  184.     {
  185.     fprintf(stderr, "top: can't allocate sufficient memory\n");
  186.     return -1;
  187.     }
  188.  
  189.     /* fill in the statics information */
  190.     statics->procstate_names = procstatenames;
  191.     statics->cpustate_names = cpustatenames;
  192.     statics->memory_names = memorynames;
  193.  
  194.     /* all done! */
  195.     return 0;
  196. }
  197.  
  198. get_system_info(info)
  199.     struct system_info *info;
  200. {
  201.     /* convert load averages */
  202.     {
  203.     load_avg ar[3];
  204.  
  205.     (void)getkval(AVENRUN_OFS, (char *)&ar, sizeof(ar), "avenrun");
  206.  
  207.     /* convert load averages to doubles */
  208.     info->load_avg[0] = loaddouble(ar[0]);
  209.     info->load_avg[1] = loaddouble(ar[1]);
  210.     info->load_avg[2] = loaddouble(ar[2]);
  211.     }
  212.  
  213.     /* get cpu time counts */
  214.     {
  215.     struct sysinfo si;
  216.  
  217.     (void)getkval(SYSINFO_OFS, (char *)&si, sizeof(si), "sysinfo");
  218.  
  219.     memcpy(cp_time, si.cpu, sizeof(cp_time));
  220.     percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
  221.     }
  222.  
  223.     /* get memory usage information */
  224.     {
  225.     int freemem, availrmem, availsmem;
  226.  
  227.     (void)getkval(FREEMEM_OFS, (char *)&freemem, sizeof(freemem),
  228.               "freemem");
  229.     (void)getkval(AVAILRMEM_OFS, (char *)&availrmem, sizeof(availrmem),
  230.               "availrmem");
  231.     (void)getkval(AVAILSMEM_OFS, (char *)&availsmem, sizeof(availsmem),
  232.               "availsmem");
  233.  
  234.     memory_stats[0] = pagetok(availrmem - freemem);
  235.     memory_stats[1] = pagetok(freemem);
  236.     memory_stats[2] = pagetok(availsmem - availrmem);
  237.     memory_stats[3] = pagetok(maxmem - availrmem);
  238.     }
  239.  
  240.     info->last_pid = -1;
  241.  
  242.     /* set arrays and strings */
  243.     info->cpustates = cpu_states;
  244.     info->memory = memory_stats;
  245. }
  246.  
  247. get_process_info(si, sel, compare)
  248.      struct system_info *si;
  249.      struct process_select *sel;
  250.      int (*compare)();
  251. {
  252.     int i, total_procs, active_procs;
  253.     struct proc *pp1, *pp2, **a;
  254.     static struct timeval lasttime;
  255.     struct timeval thistime;
  256.     double timediff, alpha, beta;
  257.  
  258.     /* these are copied out of sel for speed */
  259.     int show_idle, show_system, show_uid, show_command;
  260.  
  261.     /* calculate the time difference since our last check */
  262.     gettimeofday(&thistime);
  263.     if (lasttime.tv_sec)
  264.     timediff = ((thistime.tv_sec - lasttime.tv_sec) +
  265.             (thistime.tv_usec - lasttime.tv_usec) * 1e-6);
  266.     else
  267.     timediff = 1e9;
  268.     lasttime = thistime;
  269.  
  270.     /* calculate constants for the exponental average */
  271.     if (timediff < 30.0)
  272.     {
  273.     alpha = 0.5 * (timediff / 30.0);
  274.     beta = 1.0 - alpha;
  275.     }
  276.     else
  277.     alpha = beta = 0.5;
  278.  
  279.     /* read all the proc structures in one fell swoop */
  280.     {
  281.     struct proc *tmp = obase;
  282.     obase = pbase;
  283.     pbase = tmp;
  284.     (void)getkval((long)v.ve_proctab, (char *)tmp,
  285.               sizeof(struct proc)*v.v_proc, "proc array");
  286.     }
  287.  
  288.     /* get a pointer to the states summary array */
  289.     si->procstates = process_states;
  290.  
  291.     /* set up flags which define what we are going to select */
  292.     show_idle = sel->idle;
  293.     show_system = sel->system;
  294.     show_uid = sel->uid != -1;
  295.     show_command = sel->command != NULL;
  296.  
  297.     /* count up process states and get pointers to interesting procs */
  298.     total_procs = active_procs = 0;
  299.     memset(process_states, 0, sizeof(process_states));
  300.  
  301.     pp1 = pbase; pp2 = obase; a = pref;
  302.  
  303.     for (i = 0; i < v.v_proc; i++, pp1++, pp2++)
  304.     {
  305.     /*
  306.      *  Place pointers to each valid proc structure in pref[].
  307.      *  Process slots that are actually in use have a non-zero
  308.      *  status field.  Processes with SSYS set are system
  309.      *  processes---these get ignored unless show_sysprocs is set.
  310.      */
  311.     int state = pp1->p_stat;
  312.     int flag = pp1->p_flag;
  313.     if (state != 0 && (show_system || (flag & SSYS) == 0))
  314.     {
  315.         struct user u;
  316.  
  317.         /* load user struct -- the utime slot in proc is invalid.
  318.            stow away the two bits we need in parts of the proc
  319.            struct that we don't need */
  320.         if (lseek(mem, pp1->p_addr, 0) < 0 ||
  321.         read(mem, &u, sizeof(u)) != sizeof(u))
  322.         {
  323.         /* no user struct?? */
  324.         pp1->p_utime = pp1->p_stime = 0;
  325.         strncpy(proc_name(pp1), "<???>", COMMSIZ);
  326.         
  327.         /* calculate relevant metrics */
  328.         percent_cpu(pp1) = 0.0;
  329.         if (pp1->p_pid == pp2->p_pid)
  330.             weighted_cpu(pp1) = percent_cpu(pp2) * beta;
  331.         else
  332.             weighted_cpu(pp1) = 0.0;
  333.         }
  334.         else
  335.         {
  336.         /* update the invalid info */
  337.         pp1->p_utime = u.u_utime;
  338.         pp1->p_stime = u.u_stime;
  339.  
  340.         /* tag swapped processes with brackets */
  341.         u.u_comm[COMMSIZ] = 0;
  342.         if (pp1->p_flag & SLOAD)
  343.             strcpy(proc_name(pp1), printable(u.u_comm));
  344.         else
  345.             sprintf(proc_name(pp1), "<%s>", printable(u.u_comm));
  346.             
  347.         /* calculate relevant metrics */
  348.         if (pp1->p_pid == pp2->p_pid)
  349.         {
  350.             percent_cpu(pp1) =
  351.             (pp1->p_utime - pp2->p_utime +
  352.              pp1->p_stime - pp2->p_stime) / (v.v_hz * timediff);
  353.             weighted_cpu(pp1) = 
  354.             percent_cpu(pp2) * beta + percent_cpu(pp1) * alpha;
  355.         }
  356.         else
  357.         {
  358.             weighted_cpu(pp1) = percent_cpu(pp1) =
  359.             (pp1->p_utime + pp1->p_stime) / (v.v_hz * timediff);
  360.         }
  361.         }
  362.  
  363.         total_procs++;
  364.         process_states[state]++;
  365.  
  366.         if (state != SZOMB &&
  367.          /* use the same formula for determining active processes
  368.             as the one used by the A/UX load average computation */
  369.         (show_idle || state == SRUN || state == SIDL ||
  370.          state == SONPROC || ((state == SSLEEP || state == SSTOP) &&
  371.                       (flag & (SINTR | SSYS)) == 0)) &&
  372.         (!show_uid || pp1->p_uid == (uid_t)sel->uid))
  373.         {
  374.         /* add it to our active list */
  375.         *a++ = pp1;
  376.         active_procs++;
  377.         }
  378.     }
  379.     }
  380.  
  381.     /* if requested, sort the "interesting" processes */
  382.     if (compare != NULL)
  383.     qsort((char *)pref, active_procs, sizeof(struct proc *), compare);
  384.  
  385.     /* remember active and total counts */
  386.     si->p_total = total_procs;
  387.     si->p_active = active_procs;
  388.  
  389.     /* set up to iterate though processes */
  390.     nextactive = pref;
  391. }
  392.  
  393.  
  394. char *
  395. format_header(uname_field)
  396.     char *uname_field;
  397. {
  398.     static char header[132];
  399.     sprintf(header,
  400.     "  PID  PGRP %-8.8s PRI NICE  SIZE STATE   TIME    WCPU     CPU COMMAND",
  401.         uname_field);
  402.     return header;
  403. }
  404.  
  405. char *
  406. format_next_process(handle, get_userid)
  407.      caddr_t handle;
  408.      char *(*get_userid)();
  409. {
  410.     static char fmt[128];    /* static area where result is built */
  411.     struct proc *pp = *nextactive++;
  412.  
  413.     sprintf(fmt,
  414.         "%5d %5d %-8.8s %3d %4d %5s %-5s %6s %6.2f%% %6.2f%% %.14s",
  415.         pp->p_pid,
  416.         pp->p_pgrp,
  417.         (*get_userid)(pp->p_uid),
  418.         pp->p_pri - PZERO,
  419.         pp->p_nice - NZERO,
  420.         format_k(pagetok(PROCSIZE(pp))),
  421.         state_abbrev[pp->p_stat],
  422.         format_time((pp->p_utime + pp->p_stime) / v.v_hz),
  423.         weighted_cpu(pp) * 100.0,
  424.         percent_cpu(pp) * 100.0,
  425.         proc_name(pp));
  426.  
  427.     /* return the result */
  428.     return (fmt);
  429. }
  430.  
  431.  
  432. /*
  433.  * check_nlist(nlst) - checks the nlist to see if any symbols were not
  434.  *              found.  For every symbol that was not found, a one-line
  435.  *              message is printed to stderr.  The routine returns the
  436.  *              number of symbols NOT found.
  437.  */
  438.  
  439. int
  440. check_nlist(nlst)
  441.      register struct nlist *nlst;
  442. {
  443.     register int i;
  444.  
  445.     /* check to see if we got ALL the symbols we requested */
  446.     /* this will write one line to stderr for every symbol not found */
  447.  
  448.     i = 0;
  449.     while (nlst->n_name[0])
  450.     {
  451.     if (nlst->n_value == 0)
  452.     {
  453.         /* this one wasn't found */
  454.         fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
  455.         i = 1;
  456.     }
  457.     nlst++;
  458.     }
  459.  
  460.     return (i);
  461. }
  462.  
  463.  
  464. /*
  465.  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
  466.  *      "offset" is the byte offset into the kernel for the desired value,
  467.  *      "ptr" points to a buffer into which the value is retrieved,
  468.  *      "size" is the size of the buffer (and the object to retrieve),
  469.  *      "refstr" is a reference string used when printing error meessages,
  470.  *          if "refstr" starts with a '!', then a failure on read will not
  471.  *          be fatal (this may seem like a silly way to do things, but I
  472.  *          really didn't want the overhead of another argument).
  473.  *      
  474.  */
  475.  
  476. getkval(offset, ptr, size, refstr)
  477.      unsigned long offset;
  478.      int *ptr;
  479.      int size;
  480.      char *refstr;
  481. {
  482.     extern int errno;
  483.     extern char *sys_errlist[];
  484.  
  485.     if (lseek(kmem, offset, 0) < 0 || read(kmem, ptr, size) != size)
  486.     {
  487.     if (*refstr == '!')
  488.     {
  489.         return (0);
  490.     }
  491.     else
  492.     {
  493.         fprintf(stderr, "top: getkval for %s: %s\n",
  494.             refstr, sys_errlist[errno]);
  495.         quit(23);
  496.         /*NOTREACHED */
  497.     }
  498.     }
  499.     return (1);
  500. }
  501.  
  502. /* comparison routine for qsort */
  503.  
  504. /*
  505.  *  proc_compare - comparison function for "qsort"
  506.  *      Compares the resource consumption of two processes using five
  507.  *      distinct keys.  The keys (in descending order of importance) are:
  508.  *      percent cpu, cpu ticks, state, resident set size, total virtual
  509.  *      memory usage.  The process states are ordered as follows (from least
  510.  *      to most important):  WAIT, zombie, sleep, stop, start, run.  The
  511.  *      array declaration below maps a process state index into a number
  512.  *      that reflects this ordering.
  513.  */
  514.  
  515. static unsigned char sorted_state[] =
  516. {
  517.     0,    /* not used             */
  518.     3,    /* sleep                */
  519.     6,    /* runable              */
  520.     1,    /* zombie               */
  521.     4,    /* stop                 */
  522.     5,    /* start                */
  523.     7,    /* running              */
  524.     2,    /* swapping             */
  525. };
  526.  
  527. proc_compare(pp1, pp2)
  528.     struct proc **pp1, **pp2;
  529. {
  530.     struct proc *p1, *p2;
  531.     int result;
  532.     double dresult;
  533.  
  534.     /* remove one level of indirection */
  535.     p1 = *pp1;
  536.     p2 = *pp2;
  537.  
  538.     /* compare percent cpu */
  539.     dresult = percent_cpu(p2) - percent_cpu(p1);
  540.     if (dresult != 0.0)
  541.     return (dresult > 0.0 ? 1 : -1);
  542.  
  543.     /* compare cpu scheduling ticks */
  544.     if ((result = p2->p_cpu - p1->p_cpu) == 0)
  545.     {
  546.     /* use resident time to break the tie */
  547.     if ((result = p1->p_time - p2->p_time) == 0)
  548.     {
  549.         /* use process state to break the tie */
  550.         if ((result = (sorted_state[p2->p_stat] -
  551.                sorted_state[p1->p_stat])) == 0)
  552.         {
  553.         /* use priority to break the tie */
  554.         if ((result = p2->p_pri - p1->p_pri) == 0)
  555.         {
  556.             /* use total memory to break the tie */
  557.             result = PROCSIZE(p2) - PROCSIZE(p1);
  558.         }
  559.         }
  560.     }
  561.     }
  562.  
  563.     return (result);
  564. }
  565.  
  566. /*
  567.  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
  568.  *              the process does not exist.
  569.  *              It is EXTREMLY IMPORTANT that this function work correctly.
  570.  *              If top runs setuid root (as in SVR4), then this function
  571.  *              is the only thing that stands in the way of a serious
  572.  *              security problem.  It validates requests for the "kill"
  573.  *              and "renice" commands.
  574.  */
  575.  
  576. int
  577. proc_owner(pid)
  578.     int pid;
  579. {
  580.     struct proc *pp;
  581.     int i;
  582.  
  583.     for (pp = pbase, i = 0; i < v.v_proc; pp++, i++)
  584.     if (pp->p_stat != 0 && pp->p_pid == pid)
  585.         return pp->p_uid;
  586.  
  587.     return -1;
  588. }
  589.  
  590. /* 
  591.  * setpriority(int which, pid_t pid, int val)
  592.  * This system does not have this system call -- fake it
  593.  */
  594.  
  595. int
  596. setpriority(which, pid, val)
  597.     int which, pid, val;
  598. {
  599. #ifndef IMPLEMENT_SETPRIORITY
  600.     errno = ENOSYS;
  601.     return -1;
  602. #else
  603.     int ofs, uid;
  604.     struct proc *pp;
  605.  
  606.     /* sanity check arguments */
  607.     val += NZERO;
  608.     if (val < 0)
  609.     val = 0;
  610.     else if (val > 39)
  611.     val = 39;
  612.  
  613.     /* locate the process */
  614.     for (ofs = 0, pp = pbase; ofs < v.v_proc; ofs++, pp++)
  615.     if (pp->p_stat != 0 && pp->p_pid == pid)
  616.         break;
  617.     if (ofs == v.v_proc)
  618.     {
  619.     errno = ESRCH;
  620.     return -1;
  621.     }
  622.  
  623.     /* make sure we don't allow nasty people to do nasty things */
  624.     uid = getuid();
  625.     if (uid != 0)
  626.     {
  627.     if (uid != pp->p_uid || val < pp->p_nice)
  628.     {
  629.         errno = EACCES;
  630.         return -1;
  631.     }
  632.     }
  633.  
  634.     /* renice */
  635.     pp->p_nice = val;
  636.     if (lseek(kmem, v.ve_proctab+((char*)&pp->p_nice-(char*)pbase), 0) < 0 ||
  637.     write(kmem, &pp->p_nice, sizeof(pp->p_nice)) != sizeof(pp->p_nice))
  638.     {
  639.     return -1;
  640.     }
  641.  
  642.     return 0;
  643. #endif
  644. }
  645.