Subject: top 3.3
To: None <lefebvre@dis.anl.gov, current-users@NetBSD.ORG>
From: Thorsten Lockert <tholo@sigmasoft.com>
List: current-users
Date: 03/28/1995 22:11:21
------- =_aaaaaaaaaa0
Content-Type: text/plain; charset="us-ascii"
Content-ID: <1341.796457481.1@gandalf.sigmasoft.com>
Content-Description: Description

The following are my machine modules for NetBSD, updated to report swap
space in place of the virtual memory sizes which are mostly unusable on
4.4BSD based systems (at least those running with the CMU VM).

This is the same version that exists in the NetBSD current-users mailing
list archives, but with swap handling added.

Note that support for showing last pid is also in this version, but that
the symbol needed to actually use this is by default stripped from the
kernel executable.

Enjoy!

------- =_aaaaaaaaaa0
Content-Type: text/plain; charset="us-ascii"
Content-ID: <1341.796457481.2@gandalf.sigmasoft.com>
Content-Description: Shar file

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	m_netbsd.c
#	m_netbsd.desc
#	m_netbsd.man
#
echo x - m_netbsd.c
sed 's/^X//' >m_netbsd.c << 'END-of-m_netbsd.c'
X/*
X * top - a top users display for Unix
X *
X * SYNOPSIS:  For a NetBSD system
X *
X * DESCRIPTION:
X * This is the machine-dependent module for NetBSD
X * Tested on:
X *	i386, sparc
X *
X * LIBS: -lkvm
X *
X * CFLAGS: -DHAVE_GETOPT
X *
X * AUTHOR:  Thorsten Lockert <tholo@sigmasoft.com>
X *          Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu>
X *          Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no>
X */
X
X#include <sys/types.h>
X#include <sys/signal.h>
X#include <sys/param.h>
X
X#define LASTPID
X#undef FIXED_LASTPID
X#define DOSWAP
X
X#include "os.h"
X#include <stdio.h>
X#include <stdlib.h>
X#include <nlist.h>
X#include <math.h>
X#include <kvm.h>
X#include <unistd.h>
X#include <sys/errno.h>
X#include <sys/sysctl.h>
X#include <sys/dir.h>
X#include <sys/dkstat.h>
X#include <sys/file.h>
X#include <sys/time.h>
X#include <sys/resource.h>
X
X#ifdef DOSWAP
X#include <err.h>
X#include <sys/map.h>
X#include <sys/conf.h>
X#endif
X
Xstatic int check_nlist __P((struct nlist *));
Xstatic int getkval __P((unsigned long, int *, int, char *));
Xstatic int swapmode __P((int *, int *));
Xextern char* printable __P((char *));
X
X#include "top.h"
X#include "machine.h"
X#include "utils.h"
X
X/* get_process_info passes back a handle.  This is what it looks like: */
X
Xstruct handle
X{
X    struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
X    int remaining;		/* number of pointers remaining */
X};
X
X/* declarations for load_avg */
X#include "loadavg.h"
X
X#define PP(pp, field) ((pp)->kp_proc . field)
X#define EP(pp, field) ((pp)->kp_eproc . field)
X#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
X
X/* what we consider to be process size: */
X#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
X
X/* definitions for indices in the nlist array */
X#define X_CP_TIME	0
X#define X_HZ		1
X
X#ifdef DOSWAP
X#define	VM_SWAPMAP	2
X#define	VM_NSWAPMAP	3
X#define	VM_SWDEVT	4
X#define	VM_NSWAP	5
X#define	VM_NSWDEV	6
X#define	VM_DMMAX	7
X#define	VM_NISWAP	8
X#define	VM_NISWDEV	9
X
X#define	X_LASTPID	10
X#elif defined(LASTPID)
X#define	X_LASTPID	2
X#endif
X
Xstatic struct nlist nlst[] = {
X    { "_cp_time" },		/* 0 */
X    { "_hz" },			/* 1 */
X#ifdef DOSWAP
X    { "_swapmap" },		/* 2 */
X    { "_nswapmap" },		/* 3 */
X    { "_swdevt" },		/* 4 */
X    { "_nswap" },		/* 5 */
X    { "_nswdev" },		/* 6 */
X    { "_dmmax" },		/* 7 */
X    { "_niswap" },		/* 8 */
X    { "_niswdev" },		/* 9 */
X#endif
X#ifdef LASTPID
X#ifdef FIXED_LASTPID
X    { "_lastpid" },		/* 2 / 10 */
X#else
X    { "_nextpid.112" },		/* 2 / 10 */
X#endif
X#endif
X    { 0 }
X};
X
X/*
X *  These definitions control the format of the per-process area
X */
X
Xstatic char header[] =
X  "  PID X        PRI NICE  SIZE   RES STATE WAIT     TIME    CPU COMMAND";
X/* 0123456   -- field to fill in starts at header+6 */
X#define UNAME_START 6
X
X#define Proc_format \
X	"%5d %-8.8s %3d %4d %5s %5s %-5s %-6.6s %6s %5.2f%% %.14s"
X
X
X/* process state names for the "STATE" column of the display */
X/* the extra nulls in the string "run" are for adding a slash and
X   the processor number when needed */
X
Xchar *state_abbrev[] =
X{
X    "", "start", "run\0\0\0", "sleep", "stop", "zomb",
X};
X
X
Xstatic kvm_t *kd;
X
X/* these are retrieved from the kernel in _init */
X
Xstatic          long hz;
X
X/* these are offsets obtained via nlist and used in the get_ functions */
X
Xstatic unsigned long cp_time_offset;
X#ifdef LASTPID
Xstatic unsigned long lastpid_offset;
Xstatic pid_t lastpid;
X#endif
X
X/* these are for calculating cpu state percentages */
Xstatic long cp_time[CPUSTATES];
Xstatic long cp_old[CPUSTATES];
Xstatic long cp_diff[CPUSTATES];
X
X/* these are for detailing the process states */
X
Xint process_states[7];
Xchar *procstatenames[] = {
X    "", " starting, ", " running, ", " idle, ", " stopped, ", " zombie, ",
X    NULL
X};
X
X/* these are for detailing the cpu states */
X
Xint cpu_states[CPUSTATES];
Xchar *cpustatenames[] = {
X    "user", "nice", "system", "interrupt", "idle", NULL
X};
X
X/* these are for detailing the memory statistics */
X
Xint memory_stats[8];
Xchar *memorynames[] = {
X    "Real: ", "K/", "K act/tot  ", "Free: ", "K  ",
X#ifdef DOSWAP
X    "Swap: ", "K/", "K used/tot",
X#endif
X    NULL
X};
X
X/* these are for keeping track of the proc array */
X
Xstatic int nproc;
Xstatic int onproc = -1;
Xstatic int pref_len;
Xstatic struct kinfo_proc *pbase;
Xstatic struct kinfo_proc **pref;
X
X/* these are for getting the memory statistics */
X
Xstatic int pageshift;		/* log base 2 of the pagesize */
X
X/* define pagetok in terms of pageshift */
X
X#define pagetok(size) ((size) << pageshift)
X
Xint
Xmachine_init(statics)
X
Xstruct statics *statics;
X
X{
X    register int i = 0;
X    register int pagesize;
X
X    if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
X	return -1;
X
X
X    /* get the list of symbols we want to access in the kernel */
X    (void) kvm_nlist(kd, nlst);
X    if (nlst[0].n_type == 0)
X    {
X	fprintf(stderr, "top: nlist failed\n");
X	return(-1);
X    }
X
X    /* make sure they were all found */
X    if (i > 0 && check_nlist(nlst) > 0)
X    {
X	return(-1);
X    }
X
X    /* get the symbol values out of kmem */
X    (void) getkval(nlst[X_HZ].n_value,     (int *)(&hz),	sizeof(hz),
X	    nlst[X_HZ].n_name);
X
X    /* stash away certain offsets for later use */
X    cp_time_offset = nlst[X_CP_TIME].n_value;
X#ifdef LASTPID
X    lastpid_offset = nlst[X_LASTPID].n_value;
X#endif
X
X    pbase = NULL;
X    pref = NULL;
X    onproc = -1;
X    nproc = 0;
X
X    /* get the page size with "getpagesize" and calculate pageshift from it */
X    pagesize = getpagesize();
X    pageshift = 0;
X    while (pagesize > 1)
X    {
X	pageshift++;
X	pagesize >>= 1;
X    }
X
X    /* we only need the amount of log(2)1024 for our conversion */
X    pageshift -= LOG1024;
X
X    /* fill in the statics information */
X    statics->procstate_names = procstatenames;
X    statics->cpustate_names = cpustatenames;
X    statics->memory_names = memorynames;
X
X    /* all done! */
X    return(0);
X}
X
Xchar *format_header(uname_field)
X
Xregister char *uname_field;
X
X{
X    register char *ptr;
X
X    ptr = header + UNAME_START;
X    while (*uname_field != '\0')
X    {
X	*ptr++ = *uname_field++;
X    }
X
X    return(header);
X}
X
Xvoid
Xget_system_info(si)
X
Xstruct system_info *si;
X
X{
X    long total;
X
X    /* get the cp_time array */
X    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
X		   "_cp_time");
X#ifdef LASTPID
X    (void) getkval(lastpid_offset, (int *)&lastpid, sizeof(lastpid),
X		   "!");
X#endif
X
X    /* convert load averages to doubles */
X    {
X	register int i;
X	register double *infoloadp;
X	struct loadavg sysload;
X	int size = sizeof(sysload);
X	static int mib[] = { CTL_VM, VM_LOADAVG };
X
X	if (sysctl(mib, 2, &sysload, &size, NULL, 0) < 0) {
X	    (void) fprintf(stderr, "top: sysctl failed: %s\n", strerror(errno));
X	    bzero(&total, sizeof(total));
X	}
X
X	infoloadp = si->load_avg;
X	for (i = 0; i < 3; i++)
X	    *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
X    }
X
X    /* convert cp_time counts to percentages */
X    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
X
X    /* sum memory statistics */
X    {
X	struct vmtotal total;
X	int size = sizeof(total);
X	static int mib[] = { CTL_VM, VM_METER };
X
X	/* get total -- systemwide main memory usage structure */
X	if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) {
X	    (void) fprintf(stderr, "top: sysctl failed: %s\n", strerror(errno));
X	    bzero(&total, sizeof(total));
X	}
X	/* convert memory stats to Kbytes */
X	memory_stats[0] = -1;
X	memory_stats[1] = pagetok(total.t_arm);
X	memory_stats[2] = pagetok(total.t_rm);
X	memory_stats[3] = -1;
X	memory_stats[4] = pagetok(total.t_free);
X	memory_stats[5] = -1;
X#ifdef DOSWAP
X	if (!swapmode(&memory_stats[6], &memory_stats[7])) {
X	    memory_stats[6] = 0;
X	    memory_stats[7] = 0;
X	}
X#endif
X    }
X
X    /* set arrays and strings */
X    si->cpustates = cpu_states;
X    si->memory = memory_stats;
X#ifdef LASTPID
X    if (lastpid > 0)
X	si->last_pid = lastpid;
X    else
X#endif
X	si->last_pid = -1;
X}
X
Xstatic struct handle handle;
X
Xcaddr_t get_process_info(si, sel, compare)
X
Xstruct system_info *si;
Xstruct process_select *sel;
Xint (*compare)();
X
X{
X    register int i;
X    register int total_procs;
X    register int active_procs;
X    register struct kinfo_proc **prefp;
X    register struct kinfo_proc *pp;
X
X    /* these are copied out of sel for speed */
X    int show_idle;
X    int show_system;
X    int show_uid;
X    int show_command;
X
X    
X    pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
X    if (nproc > onproc)
X	pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
X		* (onproc = nproc));
X    if (pref == NULL || pbase == NULL) {
X	(void) fprintf(stderr, "top: Out of memory.\n");
X	quit(23);
X    }
X    /* get a pointer to the states summary array */
X    si->procstates = process_states;
X
X    /* set up flags which define what we are going to select */
X    show_idle = sel->idle;
X    show_system = sel->system;
X    show_uid = sel->uid != -1;
X    show_command = sel->command != NULL;
X
X    /* count up process states and get pointers to interesting procs */
X    total_procs = 0;
X    active_procs = 0;
X    memset((char *)process_states, 0, sizeof(process_states));
X    prefp = pref;
X    for (pp = pbase, i = 0; i < nproc; pp++, i++)
X    {
X	/*
X	 *  Place pointers to each valid proc structure in pref[].
X	 *  Process slots that are actually in use have a non-zero
X	 *  status field.  Processes with SSYS set are system
X	 *  processes---these get ignored unless show_sysprocs is set.
X	 */
X	if (PP(pp, p_stat) != 0 &&
X	    (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0)))
X	{
X	    total_procs++;
X	    process_states[(unsigned char) PP(pp, p_stat)]++;
X	    if ((PP(pp, p_stat) != SZOMB) &&
X		(show_idle || (PP(pp, p_pctcpu) != 0) || 
X		 (PP(pp, p_stat) == SRUN)) &&
X		(!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
X	    {
X		*prefp++ = pp;
X		active_procs++;
X	    }
X	}
X    }
X
X    /* if requested, sort the "interesting" processes */
X    if (compare != NULL)
X    {
X	qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
X    }
X
X    /* remember active and total counts */
X    si->p_total = total_procs;
X    si->p_active = pref_len = active_procs;
X
X    /* pass back a handle */
X    handle.next_proc = pref;
X    handle.remaining = active_procs;
X    return((caddr_t)&handle);
X}
X
Xchar fmt[MAX_COLS];		/* static area where result is built */
X
Xchar *format_next_process(handle, get_userid)
X
Xcaddr_t handle;
Xchar *(*get_userid)();
X
X{
X    register struct kinfo_proc *pp;
X    register long cputime;
X    register double pct;
X    struct handle *hp;
X    char waddr[sizeof(void *) * 2 + 3];	/* Hexify void pointer */
X    char *p_wait;
X
X    /* find and remember the next proc structure */
X    hp = (struct handle *)handle;
X    pp = *(hp->next_proc++);
X    hp->remaining--;
X    
X
X    /* get the process's user struct and set cputime */
X    if ((PP(pp, p_flag) & P_INMEM) == 0) {
X	/*
X	 * Print swapped processes as <pname>
X	 */
X	char *comm = PP(pp, p_comm);
X#define COMSIZ sizeof(PP(pp, p_comm))
X	char buf[COMSIZ];
X	(void) strncpy(buf, comm, COMSIZ);
X	comm[0] = '<';
X	(void) strncpy(&comm[1], buf, COMSIZ - 2);
X	comm[COMSIZ - 2] = '\0';
X	(void) strncat(comm, ">", COMSIZ - 1);
X	comm[COMSIZ - 1] = '\0';
X    }
X
X    cputime = (PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks)) / hz;
X
X    /* calculate the base for cpu percentages */
X    pct = pctdouble(PP(pp, p_pctcpu));
X
X    if (PP(pp, p_wchan))
X        if (PP(pp, p_wmesg))
X	    p_wait = EP(pp, e_wmesg);
X	else {
X	    sprintf(waddr, "%x", 
X		(unsigned long)(PP(pp, p_wchan)) & ~KERNBASE);
X	    p_wait = waddr;
X        }
X    else
X	p_wait = "-";
X	    
X    /* format this entry */
X    snprintf(fmt, MAX_COLS,
X	    Proc_format,
X	    PP(pp, p_pid),
X	    (*get_userid)(EP(pp, e_pcred.p_ruid)),
X	    PP(pp, p_priority) - PZERO,
X	    PP(pp, p_nice) - NZERO,
X	    format_k(pagetok(PROCSIZE(pp))),
X	    format_k(pagetok(VP(pp, vm_rssize))),
X	    state_abbrev[(unsigned char) PP(pp, p_stat)],
X	    p_wait,
X	    format_time(cputime),
X	    100.0 * pct,
X	    printable(PP(pp, p_comm)));
X
X    /* return the result */
X    return(fmt);
X}
X
X
X/*
X * check_nlist(nlst) - checks the nlist to see if any symbols were not
X *		found.  For every symbol that was not found, a one-line
X *		message is printed to stderr.  The routine returns the
X *		number of symbols NOT found.
X */
X
Xstatic int check_nlist(nlst)
X
Xregister struct nlist *nlst;
X
X{
X    register int i;
X
X    /* check to see if we got ALL the symbols we requested */
X    /* this will write one line to stderr for every symbol not found */
X
X    i = 0;
X    while (nlst->n_name != NULL)
X    {
X	if (nlst->n_type == 0)
X	{
X	    /* this one wasn't found */
X	    (void) fprintf(stderr, "kernel: no symbol named `%s'\n",
X			   nlst->n_name);
X	    i = 1;
X	}
X	nlst++;
X    }
X
X    return(i);
X}
X
X
X/*
X *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
X *	"offset" is the byte offset into the kernel for the desired value,
X *  	"ptr" points to a buffer into which the value is retrieved,
X *  	"size" is the size of the buffer (and the object to retrieve),
X *  	"refstr" is a reference string used when printing error meessages,
X *	    if "refstr" starts with a '!', then a failure on read will not
X *  	    be fatal (this may seem like a silly way to do things, but I
X *  	    really didn't want the overhead of another argument).
X *  	
X */
X
Xstatic int getkval(offset, ptr, size, refstr)
X
Xunsigned long offset;
Xint *ptr;
Xint size;
Xchar *refstr;
X
X{
X    if (kvm_read(kd, offset, (char *) ptr, size) != size)
X    {
X	if (*refstr == '!')
X	{
X	    return(0);
X	}
X	else
X	{
X	    fprintf(stderr, "top: kvm_read for %s: %s\n",
X		refstr, strerror(errno));
X	    quit(23);
X	}
X    }
X    return(1);
X}
X    
X/* comparison routine for qsort */
X
X/*
X *  proc_compare - comparison function for "qsort"
X *	Compares the resource consumption of two processes using five
X *  	distinct keys.  The keys (in descending order of importance) are:
X *  	percent cpu, cpu ticks, state, resident set size, total virtual
X *  	memory usage.  The process states are ordered as follows (from least
X *  	to most important):  zombie, sleep, stop, start, run.  The array
X *  	declaration below maps a process state index into a number that
X *  	reflects this ordering.
X */
X
Xstatic unsigned char sorted_state[] =
X{
X    0,	/* not used		*/
X    4,	/* start		*/
X    5,	/* run			*/
X    2,	/* sleep		*/
X    3,	/* stop			*/
X    1	/* zombie		*/
X};
X 
Xint
Xproc_compare(pp1, pp2)
X
Xstruct proc **pp1;
Xstruct proc **pp2;
X
X{
X    register struct kinfo_proc *p1;
X    register struct kinfo_proc *p2;
X    register int result;
X    register pctcpu lresult;
X
X    /* remove one level of indirection */
X    p1 = *(struct kinfo_proc **) pp1;
X    p2 = *(struct kinfo_proc **) pp2;
X
X    /* compare percent cpu (pctcpu) */
X    if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0)
X    {
X	/* use cpticks to break the tie */
X	if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0)
X	{
X	    /* use process state to break the tie */
X	    if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] -
X			  sorted_state[(unsigned char) PP(p1, p_stat)])  == 0)
X	    {
X		/* use priority to break the tie */
X		if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
X		{
X		    /* use resident set size (rssize) to break the tie */
X		    if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
X		    {
X			/* use total memory to break the tie */
X			result = PROCSIZE(p2) - PROCSIZE(p1);
X		    }
X		}
X	    }
X	}
X    }
X    else
X    {
X	result = lresult < 0 ? -1 : 1;
X    }
X
X    return(result);
X}
X
X
X/*
X * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
X *		the process does not exist.
X *		It is EXTREMLY IMPORTANT that this function work correctly.
X *		If top runs setuid root (as in SVR4), then this function
X *		is the only thing that stands in the way of a serious
X *		security problem.  It validates requests for the "kill"
X *		and "renice" commands.
X */
X
Xint proc_owner(pid)
X
Xint pid;
X
X{
X    register int cnt;
X    register struct kinfo_proc **prefp;
X    register struct kinfo_proc *pp;
X
X    prefp = pref;
X    cnt = pref_len;
X    while (--cnt >= 0)
X    {
X	pp = *prefp++;	
X	if (PP(pp, p_pid) == (pid_t)pid)
X	{
X	    return((int)EP(pp, e_pcred.p_ruid));
X	}
X    }
X    return(-1);
X}
X
X#ifdef DOSWAP
X/*
X * swapmode is based on a program called swapinfo written
X * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
X */
X
X#define	SVAR(var) __STRING(var)	/* to force expansion */
X#define	KGET(idx, var)							\
X	KGET1(idx, &var, sizeof(var), SVAR(var))
X#define	KGET1(idx, p, s, msg)						\
X	KGET2(nlst[idx].n_value, p, s, msg)
X#define	KGET2(addr, p, s, msg)						\
X	if (kvm_read(kd, (u_long)(addr), p, s) != s)			\
X		warnx("cannot read %s: %s", msg, kvm_geterr(kd))
X
Xstatic int
Xswapmode(used, total)
Xint *used;
Xint *total;
X{
X	int nswap, nswdev, dmmax, nswapmap, niswap, niswdev;
X	int s, e, i, l, nfree;
X	struct swdevt *sw;
X	long *perdev;
X	struct map *swapmap, *kswapmap;
X	struct mapent *mp, *freemp;
X
X	KGET(VM_NSWAP, nswap);
X	KGET(VM_NSWDEV, nswdev);
X	KGET(VM_DMMAX, dmmax);
X	KGET(VM_NSWAPMAP, nswapmap);
X	KGET(VM_SWAPMAP, kswapmap);	/* kernel `swapmap' is a pointer */
X	if ((sw = malloc(nswdev * sizeof(*sw))) == NULL ||
X	    (perdev = malloc(nswdev * sizeof(*perdev))) == NULL ||
X	    (freemp = mp = malloc(nswapmap * sizeof(*mp))) == NULL)
X		err(1, "malloc");
X	KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt");
X	KGET2((long)kswapmap, mp, nswapmap * sizeof(*mp), "swapmap");
X
X	/* Supports sequential swap */
X	if (nlst[VM_NISWAP].n_value != 0) {
X		KGET(VM_NISWAP, niswap);
X		KGET(VM_NISWDEV, niswdev);
X	} else {
X		niswap = nswap;
X		niswdev = nswdev;
X	}
X
X	/* First entry in map is `struct map'; rest are mapent's. */
X	swapmap = (struct map *)mp;
X	if (nswapmap != swapmap->m_limit - (struct mapent *)kswapmap)
X		errx(1, "panic: nswapmap goof");
X
X	/* Count up swap space. */
X	nfree = 0;
X	memset(perdev, 0, nswdev * sizeof(*perdev));
X	for (mp++; mp->m_addr != 0; mp++) {
X		s = mp->m_addr;			/* start of swap region */
X		e = mp->m_addr + mp->m_size;	/* end of region */
X		nfree += mp->m_size;
X
X		/*
X		 * Swap space is split up among the configured disks.
X		 *
X		 * For interleaved swap devices, the first dmmax blocks
X		 * of swap space some from the first disk, the next dmmax
X		 * blocks from the next, and so on up to niswap blocks.
X		 *
X		 * Sequential swap devices follow the interleaved devices
X		 * (i.e. blocks starting at niswap) in the order in which
X		 * they appear in the swdev table.  The size of each device
X		 * will be a multiple of dmmax.
X		 *
X		 * The list of free space joins adjacent free blocks,
X		 * ignoring device boundries.  If we want to keep track
X		 * of this information per device, we'll just have to
X		 * extract it ourselves.  We know that dmmax-sized chunks
X		 * cannot span device boundaries (interleaved or sequential)
X		 * so we loop over such chunks assigning them to devices.
X		 */
X		i = -1;
X		while (s < e) {		/* XXX this is inefficient */
X			int bound = roundup(s+1, dmmax);
X
X			if (bound > e)
X				bound = e;
X			if (bound <= niswap) {
X				/* Interleaved swap chunk. */
X				if (i == -1)
X					i = (s / dmmax) % niswdev;
X				perdev[i] += bound - s;
X				if (++i >= niswdev)
X					i = 0;
X			} else {
X				/* Sequential swap chunk. */
X				if (i < niswdev) {
X					i = niswdev;
X					l = niswap + sw[i].sw_nblks;
X				}
X				while (s >= l) {
X					/* XXX don't die on bogus blocks */
X					if (i == nswdev-1)
X						break;
X					l += sw[++i].sw_nblks;
X				}
X				perdev[i] += bound - s;
X			}
X			s = bound;
X		}
X	}
X
X	*total = 0;
X	for (i = 0; i < nswdev; i++) {
X		int xsize, xfree;
X
X		xsize = sw[i].sw_nblks;
X		xfree = perdev[i];
X		*total += xsize;
X	}
X
X	/* 
X	 * If only one partition has been set up via swapon(8), we don't
X	 * need to bother with totals.
X	 */
X#if DEV_BSHIFT < 10
X	*used = (*total - nfree) >> (10 - DEV_BSHIFT);
X	*total >>= 10 - DEV_BSHIFT;
X#elif DEV_BSHIFT > 10
X	*used = (*total - nfree) >> (DEV_BSHIFT - 10);
X	*total >>= DEV_BSHIFT - 10;
X#endif
X	free (sw); free (freemp); free (perdev);
X	return 1;
X}
X#endif
END-of-m_netbsd.c
echo x - m_netbsd.desc
sed 's/^X//' >m_netbsd.desc << 'END-of-m_netbsd.desc'
X
Xtop - a top users display for Unix
X
XSYNOPSIS:  For a NetBSD system
X
XDESCRIPTION:
XThis is the machine-dependent module for NetBSD
XTested on:
X	i386, sparc
X
XLIBS: -lkvm
X
XCFLAGS: -DHAVE_GETOPT
X
XAUTHOR:  Thorsten Lockert <tholo@sigmasoft.com>
XAdapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu>
X
END-of-m_netbsd.desc
echo x - m_netbsd.man
sed 's/^X//' >m_netbsd.man << 'END-of-m_netbsd.man'
X.SH "NetBSD NOTES"
X
XTested with NetBSD/i386 and NetBSD/sparc.  Stolen from the m_bsd44.c...
END-of-m_netbsd.man
exit

------- =_aaaaaaaaaa0
Content-Type: text/plain; charset="us-ascii"
Content-ID: <1341.796457481.3@gandalf.sigmasoft.com>
Content-Description: Signature

--
Thorsten Lockert        | postmaster@sigmasoft.com | Universe, n.:
1262 Golden Gate Avenue | hostmaster@sigmasoft.com |         The problem.
San Francisco, CA 94115 | tholo@sigmasoft.com      |

------- =_aaaaaaaaaa0--