Home | History | Annotate | Line # | Download | only in machine
      1 /*
      2  * Copyright (c) 1984 through 2008, William LeFebvre
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *
     16  *     * Neither the name of William LeFebvre nor the names of other
     17  * contributors may be used to endorse or promote products derived from
     18  * this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * top - a top users display for Unix
     35  *
     36  * SYNOPSIS:  Intel based System V Release 4
     37  *
     38  * DESCRIPTION:
     39  *	System V release 4.0.x for i486
     40  *	System V release 4     for Okidata M88100
     41  *	System V release 4     for NCR 3000 series OS Rel 1.00 to 2.02
     42  *	System V release 4     for NCR 3000 series OS Rel 02.03.00 and above
     43  *	and probably other svr4 ports
     44  *
     45  * LIBS:  -lelf
     46  *
     47  * AUTHORS:  Andrew Herbert     <andrew (at) werple.apana.org.au>
     48  *           Robert Boucher     <boucher (at) sofkin.ca>
     49  * Ported to System 3000 Release 2.03 by:
     50  * 	     Jeff Janvrin       <jeff.janvrinColumbiaSC.NCR.COM>
     51  */
     52 
     53 #include "top.h"
     54 #include "machine.h"
     55 #include "utils.h"
     56 #include <stdio.h>
     57 #include <fcntl.h>
     58 #include <unistd.h>
     59 #include <stdlib.h>
     60 #include <errno.h>
     61 #include <dirent.h>
     62 #include <nlist.h>
     63 #include <string.h>
     64 #if TIME_WITH_SYS_TIME
     65 # include <sys/time.h>
     66 # include <time.h>
     67 #else
     68 # if HAVE_SYS_TIME_H
     69 #  include <sys/time.h>
     70 # else
     71 #  include <time.h>
     72 # endif
     73 #endif
     74 #include <sys/types.h>
     75 #include <sys/stat.h>
     76 #include <sys/param.h>
     77 #include <sys/procfs.h>
     78 #include <sys/sysinfo.h>
     79 #include <sys/sysmacros.h>
     80 #include <sys/vmmeter.h>
     81 #include <vm/anon.h>
     82 #include <sys/priocntl.h>
     83 #include <sys/rtpriocntl.h>
     84 #include <sys/tspriocntl.h>
     85 #include <sys/procset.h>
     86 #include <sys/var.h>
     87 
     88 #define UNIX "/stand/unix"
     89 #define KMEM "/dev/kmem"
     90 #define PROCFS "/proc"
     91 #define CPUSTATES	5
     92 
     93 #ifndef PRIO_MAX
     94 #define PRIO_MAX	20
     95 #endif
     96 #ifndef PRIO_MIN
     97 #define PRIO_MIN	-20
     98 #endif
     99 
    100 #ifndef FSCALE
    101 #define FSHIFT  8		/* bits to right of fixed binary point */
    102 #define FSCALE  (1<<FSHIFT)
    103 #endif
    104 
    105 #define loaddouble(x) ((double)(x) / FSCALE)
    106 #define percent_cpu(x) ((double)(x)->pr_cpu / FSCALE)
    107 #define weighted_cpu(pct, pp) ( ((pp)->pr_time.tv_sec) == 0 ? 0.0 : \
    108         ((pp)->pr_cpu) / ((pp)->pr_time.tv_sec) )
    109 #define pagetok(size) ctob(size) >> LOG1024
    110 
    111 /* definitions for the index in the nlist array */
    112 #define X_AVENRUN	0
    113 #define X_MPID		1
    114 #define X_V		2
    115 #define X_NPROC		3
    116 #define X_ANONINFO	4
    117 #define X_TOTAL		5
    118 #define X_SYSINFO	6
    119 
    120 static struct nlist nlst[] =
    121 {
    122 {"avenrun"},			/* 0 */
    123 {"mpid"},			/* 1 */
    124 {"v"},			/* 2 */
    125 {"nproc"},			/* 3 */
    126 {"anoninfo"},			/* 4 */
    127 {"total"},			/* 5 */
    128 {"sysinfo"},			/* 6 */
    129 {NULL}
    130 };
    131 
    132 static unsigned long avenrun_offset;
    133 static unsigned long mpid_offset;
    134 static unsigned long nproc_offset;
    135 static unsigned long anoninfo_offset;
    136 static unsigned long total_offset;
    137 static unsigned long sysinfo_offset;
    138 
    139 /* get_process_info passes back a handle.  This is what it looks like: */
    140 
    141 struct handle
    142   {
    143     struct prpsinfo **next_proc;/* points to next valid proc pointer */
    144     int remaining;		/* number of pointers remaining */
    145   };
    146 
    147 /*
    148  *  These definitions control the format of the per-process area
    149  */
    150 
    151 static char header[] =
    152 "  PID X        PRI NICE  SIZE   RES STATE   TIME   WCPU    CPU COMMAND";
    153 /* 0123456   -- field to fill in starts at header+6 */
    154 #define UNAME_START 6
    155 #define Proc_format \
    156 	"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %3d.0%% %5.2f%% %.16s"
    157 
    158 char *state_abbrev[] =
    159 {"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
    160 
    161 int process_states[8];
    162 char *procstatenames[] =
    163 {
    164   "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
    165   " starting, ", " on cpu, ", " swapped, ",
    166   NULL
    167 };
    168 
    169 int cpu_states[CPUSTATES];
    170 char *cpustatenames[] =
    171 {"idle", "user", "kernel", "wait", "swap", NULL};
    172 
    173 /* these are for detailing the memory statistics */
    174 
    175 long memory_stats[5];
    176 char *memorynames[] =
    177 {"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL};
    178 
    179 /* forward reference for qsort comparison function */
    180 int proc_compare();
    181 
    182 static int kmem = -1;
    183 static int nproc;
    184 static int bytes;
    185 static int use_stats = 0;
    186 static struct prpsinfo *pbase;
    187 static struct prpsinfo **pref;
    188 static DIR *proc_dir;
    189 
    190 /* useful externals */
    191 extern int errno;
    192 extern char *sys_errlist[];
    193 extern char *myname;
    194 extern int check_nlist ();
    195 extern int getkval ();
    196 extern void perror ();
    197 extern void getptable ();
    198 extern void quit ();
    199 extern int nlist ();
    200 
    201 int
    202 machine_init (struct statics *statics)
    203   {
    204     static struct var v;
    205 
    206     /* fill in the statics information */
    207     statics->procstate_names = procstatenames;
    208     statics->cpustate_names = cpustatenames;
    209     statics->memory_names = memorynames;
    210 
    211     /* get the list of symbols we want to access in the kernel */
    212     if (nlist (UNIX, nlst))
    213       {
    214 	(void) fprintf (stderr, "Unable to nlist %s\n", UNIX);
    215 	return (-1);
    216       }
    217 
    218     /* make sure they were all found */
    219     if (check_nlist (nlst) > 0)
    220       return (-1);
    221 
    222     /* open kernel memory */
    223     if ((kmem = open (KMEM, O_RDONLY)) == -1)
    224       {
    225 	perror (KMEM);
    226 	return (-1);
    227       }
    228 
    229     /* get the symbol values out of kmem */
    230     /* NPROC Tuning parameter for max number of processes */
    231     (void) getkval (nlst[X_V].n_value, &v, sizeof (struct var), nlst[X_V].n_name);
    232     nproc = v.v_proc;
    233 
    234     /* stash away certain offsets for later use */
    235     mpid_offset = nlst[X_MPID].n_value;
    236     nproc_offset = nlst[X_NPROC].n_value;
    237     avenrun_offset = nlst[X_AVENRUN].n_value;
    238     anoninfo_offset = nlst[X_ANONINFO].n_value;
    239     total_offset = nlst[X_TOTAL].n_value;
    240 /* JJ this may need to be changed */
    241     sysinfo_offset = nlst[X_SYSINFO].n_value;
    242 
    243     /* allocate space for proc structure array and array of pointers */
    244     bytes = nproc * sizeof (struct prpsinfo);
    245     pbase = (struct prpsinfo *) malloc (bytes);
    246     pref = (struct prpsinfo **) malloc (nproc * sizeof (struct prpsinfo *));
    247 
    248     /* Just in case ... */
    249     if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
    250       {
    251 	(void) fprintf (stderr, "%s: can't allocate sufficient memory\n", myname);
    252 	return (-1);
    253       }
    254 
    255     if (!(proc_dir = opendir (PROCFS)))
    256       {
    257 	(void) fprintf (stderr, "Unable to open %s\n", PROCFS);
    258 	return (-1);
    259       }
    260 
    261     if (chdir (PROCFS))
    262       {				/* handy for later on when we're reading it */
    263 	(void) fprintf (stderr, "Unable to chdir to %s\n", PROCFS);
    264 	return (-1);
    265       }
    266 
    267     /* all done! */
    268     return (0);
    269   }
    270 
    271 char *
    272 format_header (char *uname_field)
    273 {
    274   register char *ptr;
    275 
    276   ptr = header + UNAME_START;
    277   while (*uname_field != '\0')
    278     *ptr++ = *uname_field++;
    279 
    280   return (header);
    281 }
    282 
    283 void
    284 get_system_info (struct system_info *si)
    285 {
    286   long avenrun[3];
    287   struct sysinfo sysinfo;
    288   static struct sysinfo *mpinfo = NULL;	/* array, per-processor sysinfo structures. */
    289   struct vmtotal total;
    290   struct anoninfo anoninfo;
    291   static long cp_old[CPUSTATES];
    292   static long cp_diff[CPUSTATES];	/* for cpu state percentages */
    293   static int num_cpus;
    294   static int fd_cpu = 0;
    295   register int i;
    296 
    297   if ( use_stats == 1) {
    298     if ( fd_cpu == 0 ) {
    299       if ((fd_cpu = open("/stats/cpuinfo", O_RDONLY)) == -1) {
    300         (void) fprintf (stderr, "%s: Open of /stats/cpuinfo failed\n", myname);
    301 	quit(2);
    302       }
    303       if (read(fd_cpu, &num_cpus, sizeof(int)) != sizeof(int)) {
    304         (void) fprintf (stderr, "%s: Read of /stats/cpuinfo failed\n", myname);
    305 	quit(2);
    306       }
    307       close(fd_cpu);
    308     }
    309     if (mpinfo == NULL) {
    310       mpinfo = (struct sysinfo *)calloc(num_cpus, sizeof(mpinfo[0]));
    311       if (mpinfo == NULL) {
    312         (void) fprintf (stderr, "%s: can't allocate space for per-processor sysinfos\n", myname);
    313         quit(12);
    314       }
    315     }
    316     /* Read the per cpu sysinfo structures into mpinfo struct. */
    317     read_sysinfos(num_cpus, mpinfo);
    318     /* Add up all of the percpu sysinfos to get global sysinfo */
    319     sysinfo_data(num_cpus, &sysinfo, mpinfo);
    320   } else {
    321     (void) getkval (sysinfo_offset, &sysinfo, sizeof (struct sysinfo), "sysinfo");
    322   }
    323 
    324   /* convert cp_time counts to percentages */
    325   (void) percentages (CPUSTATES, cpu_states, sysinfo.cpu, cp_old, cp_diff);
    326 
    327   /* get mpid -- process id of last process */
    328   (void) getkval (mpid_offset, &(si->last_pid), sizeof (si->last_pid),
    329 		  "mpid");
    330 
    331   /* get load average array */
    332   (void) getkval (avenrun_offset, (int *) avenrun, sizeof (avenrun), "avenrun");
    333 
    334   /* convert load averages to doubles */
    335   for (i = 0; i < 3; i++)
    336     si->load_avg[i] = loaddouble (avenrun[i]);
    337 
    338   /* get total -- systemwide main memory usage structure */
    339   (void) getkval (total_offset, (int *) (&total), sizeof (total), "total");
    340   /* convert memory stats to Kbytes */
    341   memory_stats[0] = pagetok (total.t_rm);
    342   memory_stats[1] = pagetok (total.t_arm);
    343   memory_stats[2] = pagetok (total.t_free);
    344   (void) getkval (anoninfo_offset, (int *) (&anoninfo), sizeof (anoninfo),
    345 		  "anoninfo");
    346   memory_stats[3] = pagetok (anoninfo.ani_max - anoninfo.ani_free);
    347   memory_stats[4] = pagetok (anoninfo.ani_max - anoninfo.ani_resv);
    348 
    349   /* set arrays and strings */
    350   si->cpustates = cpu_states;
    351   si->memory = memory_stats;
    352 }
    353 
    354 static struct handle handle;
    355 
    356 caddr_t
    357 get_process_info (
    358 		   struct system_info *si,
    359 		   struct process_select *sel,
    360 		   int x)
    361 {
    362   register int i;
    363   register int total_procs;
    364   register int active_procs;
    365   register struct prpsinfo **prefp;
    366   register struct prpsinfo *pp;
    367 
    368   /* these are copied out of sel for speed */
    369   int show_idle;
    370   int show_system;
    371   int show_uid;
    372 
    373   /* Get current number of processes */
    374   (void) getkval (nproc_offset, (int *) (&nproc), sizeof (nproc), "nproc");
    375 
    376   /* read all the proc structures */
    377   getptable (pbase);
    378 
    379   /* get a pointer to the states summary array */
    380   si->procstates = process_states;
    381 
    382   /* set up flags which define what we are going to select */
    383   show_idle = sel->idle;
    384   show_system = sel->system;
    385   show_uid = sel->uid != -1;
    386 
    387   /* count up process states and get pointers to interesting procs */
    388   total_procs = 0;
    389   active_procs = 0;
    390   (void) memset (process_states, 0, sizeof (process_states));
    391   prefp = pref;
    392 
    393   for (pp = pbase, i = 0; i < nproc; pp++, i++)
    394     {
    395       /*
    396 	 *  Place pointers to each valid proc structure in pref[].
    397 	 *  Process slots that are actually in use have a non-zero
    398 	 *  status field.  Processes with SSYS set are system
    399 	 *  processes---these get ignored unless show_sysprocs is set.
    400 	 */
    401       if (pp->pr_state != 0 &&
    402 	  (show_system || ((pp->pr_flag & SSYS) == 0)))
    403 	{
    404 	  total_procs++;
    405 	  process_states[pp->pr_state]++;
    406 	  if ((!pp->pr_zomb) &&
    407 	      (show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
    408 	      (!show_uid || pp->pr_uid == (uid_t) sel->uid))
    409 	    {
    410 	      *prefp++ = pp;
    411 	      active_procs++;
    412 	    }
    413 	}
    414     }
    415 
    416   /* if requested, sort the "interesting" processes */
    417   qsort ((char *) pref, active_procs, sizeof (struct prpsinfo *), proc_compare);
    418 
    419   /* remember active and total counts */
    420   si->p_total = total_procs;
    421   si->p_active = active_procs;
    422 
    423   /* pass back a handle */
    424   handle.next_proc = pref;
    425   handle.remaining = active_procs;
    426   return ((caddr_t) & handle);
    427 }
    428 
    429 char fmt[MAX_COLS];			/* static area where result is built */
    430 
    431 char *
    432 format_next_process (
    433 		      caddr_t handle,
    434 		      char *(*get_userid) ())
    435 {
    436   register struct prpsinfo *pp;
    437   struct handle *hp;
    438   register long cputime;
    439   register double pctcpu;
    440 
    441   /* find and remember the next proc structure */
    442   hp = (struct handle *) handle;
    443   pp = *(hp->next_proc++);
    444   hp->remaining--;
    445 
    446   /* get the cpu usage and calculate the cpu percentages */
    447   cputime = pp->pr_time.tv_sec;
    448   pctcpu = percent_cpu (pp);
    449 
    450   /* format this entry */
    451   (void) sprintf (fmt,
    452 		  Proc_format,
    453 		  pp->pr_pid,
    454 		  (*get_userid) (pp->pr_uid),
    455 		  pp->pr_pri - PZERO,
    456 		  pp->pr_nice - NZERO,
    457 		  format_k(pagetok (pp->pr_size)),
    458 		  format_k(pagetok (pp->pr_rssize)),
    459 		  state_abbrev[pp->pr_state],
    460 		  format_time(cputime),
    461 		  (pp->pr_cpu & 0377),
    462 		  100.0 * pctcpu,
    463 		  printable(pp->pr_fname));
    464 
    465   /* return the result */
    466   return (fmt);
    467 }
    468 
    469 /*
    470  * check_nlist(nlst) - checks the nlist to see if any symbols were not
    471  *		found.  For every symbol that was not found, a one-line
    472  *		message is printed to stderr.  The routine returns the
    473  *		number of symbols NOT found.
    474  */
    475 int
    476 check_nlist (register struct nlist *nlst)
    477 {
    478   register int i;
    479   struct stat stat_buf;
    480 
    481   /* check to see if we got ALL the symbols we requested */
    482   /* this will write one line to stderr for every symbol not found */
    483 
    484   i = 0;
    485   while (nlst->n_name != NULL)
    486     {
    487       if (nlst->n_type == 0)
    488 	{
    489 	  if (strcmp("sysinfo", nlst->n_name) == 0)
    490 	    {
    491 		/* check to see if /stats file system exists. If so, 	*/
    492 		/* ignore error. 					*/
    493 		if ( !((stat("/stats/sysinfo", &stat_buf) == 0) &&
    494 		  (stat_buf.st_mode & S_IFREG)) )
    495 		  {
    496 		    (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
    497 		    i = 1;
    498 		  } else {
    499 		    use_stats = 1;
    500 		  }
    501 	    } else {
    502 
    503 	      /* this one wasn't found */
    504 	      (void) fprintf (stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
    505 	      i = 1;
    506 	    }
    507 	}
    508       nlst++;
    509     }
    510   return (i);
    511 }
    512 
    513 
    514 /*
    515  *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
    516  *	"offset" is the byte offset into the kernel for the desired value,
    517  *  	"ptr" points to a buffer into which the value is retrieved,
    518  *  	"size" is the size of the buffer (and the object to retrieve),
    519  *  	"refstr" is a reference string used when printing error meessages,
    520  *	    if "refstr" starts with a '!', then a failure on read will not
    521  *  	    be fatal (this may seem like a silly way to do things, but I
    522  *  	    really didn't want the overhead of another argument).
    523  *
    524  */
    525 int
    526 getkval (
    527 	  unsigned long offset,
    528 	  int *ptr,
    529 	  int size,
    530 	  char *refstr)
    531 {
    532 #ifdef MIPS
    533   if (lseek (kmem, (long) (offset & 0x7fffffff), 0) == -1)
    534 #else
    535   if (lseek (kmem, (long) offset, 0) == -1)
    536 #endif
    537     {
    538       if (*refstr == '!')
    539 	refstr++;
    540       (void) fprintf (stderr, "%s: lseek to %s: %s\n",
    541 		      myname, refstr, sys_errlist[errno]);
    542       quit (22);
    543     }
    544   if (read (kmem, (char *) ptr, size) == -1)
    545     if (*refstr == '!')
    546       /* we lost the race with the kernel, process isn't in memory */
    547       return (0);
    548     else
    549       {
    550 	(void) fprintf (stderr, "%s: reading %s: %s\n",
    551 			myname, refstr, sys_errlist[errno]);
    552 	quit (23);
    553       }
    554   return (1);
    555 }
    556 
    557 /* comparison routine for qsort */
    558 
    559 /*
    560  *  proc_compare - comparison function for "qsort"
    561  *	Compares the resource consumption of two processes using five
    562  *  	distinct keys.  The keys (in descending order of importance) are:
    563  *  	percent cpu, cpu ticks, state, resident set size, total virtual
    564  *  	memory usage.  The process states are ordered as follows (from least
    565  *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
    566  *  	array declaration below maps a process state index into a number
    567  *  	that reflects this ordering.
    568  */
    569 
    570 
    571 unsigned char sorted_state[] =
    572 {
    573   0,				/* not used		*/
    574   3,				/* sleep		*/
    575   6,				/* run			*/
    576   2,				/* zombie		*/
    577   4,				/* stop			*/
    578   5,				/* start		*/
    579   7,				/* run on a processor   */
    580   1				/* being swapped (WAIT)	*/
    581 };
    582 
    583 int
    584 proc_compare (
    585 	       struct prpsinfo **pp1,
    586 	       struct prpsinfo **pp2)
    587 {
    588     register struct prpsinfo *p1;
    589     register struct prpsinfo *p2;
    590     register long result;
    591 
    592     /* remove one level of indirection */
    593     p1 = *pp1;
    594     p2 = *pp2;
    595 
    596     /* compare percent cpu (pctcpu) */
    597     if ((result = (long) (p2->pr_cpu - p1->pr_cpu)) == 0)
    598       {
    599 	/* use cpticks to break the tie */
    600 	if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
    601 	  {
    602 	    /* use process state to break the tie */
    603 	    if ((result = (long) (sorted_state[p2->pr_state] -
    604 				  sorted_state[p1->pr_state])) == 0)
    605 	      {
    606 		/* use priority to break the tie */
    607 		if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
    608 		  {
    609 		    /* use resident set size (rssize) to break the tie */
    610 		    if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
    611 		      {
    612 			/* use total memory to break the tie */
    613 			result = (p2->pr_size - p1->pr_size);
    614 		      }
    615 		  }
    616 	      }
    617 	  }
    618       }
    619     return (result);
    620   }
    621 
    622 /*
    623 get process table
    624 */
    625 void
    626 getptable (struct prpsinfo *baseptr)
    627 {
    628   struct prpsinfo *currproc;	/* pointer to current proc structure	*/
    629   int numprocs = 0;
    630   struct dirent *direntp;
    631 
    632   for (rewinddir (proc_dir); direntp = readdir (proc_dir);)
    633     {
    634       int fd;
    635 
    636       if ((fd = open (direntp->d_name, O_RDONLY)) < 0)
    637 	continue;
    638 
    639       currproc = &baseptr[numprocs];
    640       if (ioctl (fd, PIOCPSINFO, currproc) < 0)
    641 	{
    642 	  (void) close (fd);
    643 	  continue;
    644 	}
    645 
    646       numprocs++;
    647       (void) close (fd);
    648     }
    649 
    650   if (nproc != numprocs)
    651     nproc = numprocs;
    652 }
    653 
    654 /* return the owner of the specified process, for use in commands.c as we're
    655    running setuid root */
    656 int
    657 proc_owner (int pid)
    658 {
    659   register struct prpsinfo *p;
    660   int i;
    661   for (i = 0, p = pbase; i < nproc; i++, p++)
    662     if (p->pr_pid == (pid_t)pid)
    663       return (p->pr_uid);
    664 
    665   return (-1);
    666 }
    667 
    668 #ifndef HAVE_SETPRIORITY
    669 int
    670 setpriority (int dummy, int who, int niceval)
    671 {
    672   int scale;
    673   int prio;
    674   pcinfo_t pcinfo;
    675   pcparms_t pcparms;
    676   tsparms_t *tsparms;
    677 
    678   strcpy (pcinfo.pc_clname, "TS");
    679   if (priocntl (0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
    680     return (-1);
    681 
    682   prio = niceval;
    683   if (prio > PRIO_MAX)
    684     prio = PRIO_MAX;
    685   else if (prio < PRIO_MIN)
    686     prio = PRIO_MIN;
    687 
    688   tsparms = (tsparms_t *) pcparms.pc_clparms;
    689   scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
    690   tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
    691   pcparms.pc_cid = pcinfo.pc_cid;
    692 
    693   if (priocntl (P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
    694     return (-1);
    695 
    696   return (0);
    697 }
    698 #endif
    699 
    700 /****************************************************************
    701  * read_sysinfos() -						*
    702  *	Read all of the CPU specific sysinfo sturctures in from	*
    703  *	the /stats file system.					*
    704  ****************************************************************/
    705 read_sysinfos(num_cpus, buf)
    706 	int num_cpus;
    707 	struct sysinfo	*buf;
    708 {
    709 
    710 	static int	fd1=0;	/* file descriptor for /stats/sysinfo */
    711 	int		read_sz;
    712 
    713 	/* Open /stats/sysinfo one time only and leave it open */
    714 	if (fd1==0) {
    715 		if ((fd1 = open("/stats/sysinfo", O_RDONLY)) == -1)
    716 			(void) fprintf (stderr, "%s: Open of /stats/sysinfo failed\n", myname);
    717 	}
    718 	/* reset the read pointer to the beginning of the file */
    719 	if (lseek(fd1, 0L, SEEK_SET) == -1)
    720 		(void) fprintf (stderr, "%s: lseek to beginning of /stats/sysinfo failed\n", myname);
    721 	read_sz = num_cpus * sizeof(buf[0]);
    722 	if (read(fd1, buf, read_sz) != read_sz)
    723 		(void) fprintf (stderr, "%s: Read of /stats/sysinfo failed\n", myname);
    724 }
    725 
    726 /****************************************************************
    727  * sysinfo_data() -						*
    728  *	Add up all of the CPU specific sysinfo sturctures to	*
    729  *	make the GLOBAL sysinfo.				*
    730  ****************************************************************/
    731 sysinfo_data(num_cpus, global_si, percpu_si)
    732 	int num_cpus;
    733 	struct sysinfo	*global_si;
    734 	struct sysinfo	*percpu_si;
    735 {
    736 	struct sysinfo	*percpu_p;
    737 	int		cpu, i, *global, *src;
    738 
    739 	/* null out the global statistics from last sample */
    740 	memset(global_si, 0, sizeof(struct sysinfo));
    741 
    742 	percpu_p = (struct sysinfo *)percpu_si;
    743 	for(cpu = 0; cpu < num_cpus; cpu++) {
    744 		global = (int *)global_si;
    745 		src = (int *)percpu_p;
    746 
    747 		/* assume sysinfo ends on an int boundary */
    748 		/* Currently, all of the struct sysinfo members are the same
    749 		 * size as an int. If that changes, we may not be able to
    750 		 * do this. But this should be safe.
    751 		 */
    752 		for(i=0; i<sizeof(struct sysinfo)/sizeof(int); i++) {
    753 			*global++ += *src++;
    754 		}
    755 		percpu_p++;
    756 	}
    757 }
    758