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@werple.apana.org.au>
48 *           Robert Boucher     <boucher@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
120static 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
132static unsigned long avenrun_offset;
133static unsigned long mpid_offset;
134static unsigned long nproc_offset;
135static unsigned long anoninfo_offset;
136static unsigned long total_offset;
137static unsigned long sysinfo_offset;
138
139/* get_process_info passes back a handle.  This is what it looks like: */
140
141struct 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
151static 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
158char *state_abbrev[] =
159{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
160
161int process_states[8];
162char *procstatenames[] =
163{
164  "", " sleeping, ", " running, ", " zombie, ", " stopped, ",
165  " starting, ", " on cpu, ", " swapped, ",
166  NULL
167};
168
169int cpu_states[CPUSTATES];
170char *cpustatenames[] =
171{"idle", "user", "kernel", "wait", "swap", NULL};
172
173/* these are for detailing the memory statistics */
174
175long memory_stats[5];
176char *memorynames[] =
177{"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL};
178
179/* forward reference for qsort comparison function */
180int proc_compare();
181
182static int kmem = -1;
183static int nproc;
184static int bytes;
185static int use_stats = 0;
186static struct prpsinfo *pbase;
187static struct prpsinfo **pref;
188static DIR *proc_dir;
189
190/* useful externals */
191extern int errno;
192extern char *sys_errlist[];
193extern char *myname;
194extern int check_nlist ();
195extern int getkval ();
196extern void perror ();
197extern void getptable ();
198extern void quit ();
199extern int nlist ();
200
201int
202machine_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
271char *
272format_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
283void
284get_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
354static struct handle handle;
355
356caddr_t
357get_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
429char fmt[MAX_COLS];			/* static area where result is built */
430
431char *
432format_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 */
475int
476check_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 */
525int
526getkval (
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
571unsigned 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
583int
584proc_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/*
623get process table
624*/
625void
626getptable (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 */
656int
657proc_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
669int
670setpriority (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 ****************************************************************/
705read_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 ****************************************************************/
731sysinfo_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