machine.c revision 39075
198937Sdes/*
298937Sdes * top - a top users display for Unix
398937Sdes *
498937Sdes * SYNOPSIS:  For FreeBSD-2.x system
598937Sdes *
698937Sdes * DESCRIPTION:
798937Sdes * Originally written for BSD4.4 system by Christos Zoulas.
898937Sdes * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider
998937Sdes * Order support hacked in from top-3.5beta6/machine/m_aix41.c
1098937Sdes *   by Monte Mitzelfelt (for latest top see http://www.groupsys.com/topinfo/)
1198937Sdes *
1298937Sdes * This is the machine-dependent module for FreeBSD 2.2
1398937Sdes * Works for:
1498937Sdes *	FreeBSD 2.2, and probably FreeBSD 2.1.x
1598937Sdes *
1698937Sdes * LIBS: -lkvm
1798937Sdes *
1898937Sdes * AUTHOR:  Christos Zoulas <christos@ee.cornell.edu>
1998937Sdes *          Steven Wallace  <swallace@freebsd.org>
2098937Sdes *          Wolfram Schneider <wosch@FreeBSD.org>
2198937Sdes *
2298937Sdes * $Id: machine.c,v 1.14 1998/08/12 09:58:15 wosch Exp $
2398937Sdes */
2498937Sdes
2598937Sdes
2698937Sdes#include <sys/types.h>
2798937Sdes#include <sys/signal.h>
28147005Sdes#include <sys/param.h>
29147005Sdes
30147005Sdes#include "os.h"
31147005Sdes#include <stdio.h>
32147005Sdes#include <nlist.h>
33147005Sdes#include <math.h>
34147005Sdes#include <kvm.h>
35147005Sdes#include <pwd.h>
36147005Sdes#include <sys/errno.h>
37147005Sdes#include <sys/sysctl.h>
38147005Sdes#include <sys/dkstat.h>
39147005Sdes#include <sys/file.h>
40147005Sdes#include <sys/time.h>
41147005Sdes#include <sys/proc.h>
42147005Sdes#include <sys/user.h>
43147005Sdes#include <sys/vmmeter.h>
44147005Sdes#include <sys/resource.h>
45147005Sdes#include <sys/rtprio.h>
46147005Sdes
47147005Sdes/* Swap */
48147005Sdes#include <stdlib.h>
4998937Sdes#include <sys/rlist.h>
5098937Sdes#include <sys/conf.h>
5198937Sdes
5298937Sdes#include <osreldate.h> /* for changes in kernel structures */
5398937Sdes
54147005Sdes#include "top.h"
55147005Sdes#include "machine.h"
56147005Sdes
57147005Sdesstatic int check_nlist __P((struct nlist *));
58147005Sdesstatic int getkval __P((unsigned long, int *, int, char *));
59147005Sdesextern char* printable __P((char *));
60147005Sdesint swapmode __P((int *retavail, int *retfree));
61147005Sdesstatic int smpmode;
62147005Sdesstatic int namelength;
63147005Sdesstatic int cmdlength;
64147005Sdes
65147005Sdes
66147005Sdes/* get_process_info passes back a handle.  This is what it looks like: */
67147005Sdes
68147005Sdesstruct handle
69147005Sdes{
70147005Sdes    struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
71147005Sdes    int remaining;		/* number of pointers remaining */
72147005Sdes};
73147005Sdes
74147005Sdes/* declarations for load_avg */
75147005Sdes#include "loadavg.h"
76147005Sdes
77147005Sdes#define PP(pp, field) ((pp)->kp_proc . field)
78147005Sdes#define EP(pp, field) ((pp)->kp_eproc . field)
79147005Sdes#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
80147005Sdes
81147005Sdes/* define what weighted cpu is.  */
82147005Sdes#define weighted_cpu(pct, pp) (PP((pp), p_swtime) == 0 ? 0.0 : \
83147005Sdes			 ((pct) / (1.0 - exp(PP((pp), p_swtime) * logcpu))))
84147005Sdes
85147005Sdes/* what we consider to be process size: */
86147005Sdes#define PROCSIZE(pp) (VP((pp), vm_map.size) / 1024)
87147005Sdes
88147005Sdes/* definitions for indices in the nlist array */
89147005Sdes
90147005Sdes
91147005Sdesstatic struct nlist nlst[] = {
92147005Sdes#define X_CCPU		0
93147005Sdes    { "_ccpu" },		/* 0 */
94147005Sdes#define X_CP_TIME	1
95147005Sdes    { "_cp_time" },		/* 1 */
96147005Sdes#define X_HZ		2
97147005Sdes    { "_hz" },		        /* 2 */
98147005Sdes#define X_STATHZ	3
99147005Sdes    { "_stathz" },		/* 3 */
100147005Sdes#define X_AVENRUN	4
101147005Sdes    { "_averunnable" },		/* 4 */
102147005Sdes
103147005Sdes/* Swap */
104147005Sdes#define VM_SWAPLIST	5
105147005Sdes	{ "_swaplist" },/* list of free swap areas */
106147005Sdes#define VM_SWDEVT	6
107147005Sdes	{ "_swdevt" },	/* list of swap devices and sizes */
108147005Sdes#define VM_NSWAP	7
109147005Sdes	{ "_nswap" },	/* size of largest swap device */
110147005Sdes#define VM_NSWDEV	8
111147005Sdes	{ "_nswdev" },	/* number of swap devices */
112147005Sdes#define VM_DMMAX	9
113147005Sdes	{ "_dmmax" },	/* maximum size of a swap block */
114147005Sdes#define X_BUFSPACE	10
115147005Sdes	{ "_bufspace" },	/* K in buffer cache */
116147005Sdes#define X_CNT           11
117147005Sdes    { "_cnt" },		        /* struct vmmeter cnt */
118147005Sdes
119147005Sdes/* Last pid */
120147005Sdes#define X_LASTPID	12
121147005Sdes    { "_nextpid" },
122147005Sdes    { 0 }
123147005Sdes};
124147005Sdes
125147005Sdes/*
126147005Sdes *  These definitions control the format of the per-process area
127147005Sdes */
128147005Sdes
129147005Sdesstatic char smp_header[] =
130147005Sdes  "  PID %-*.*s PRI NICE  SIZE    RES STATE  C   TIME   WCPU    CPU COMMAND";
131147005Sdes
132147005Sdes#define smp_Proc_format \
133147005Sdes	"%5d %-*.*s %3d %3d%7s %6s %-6.6s %1x%7s %5.2f%% %5.2f%% %.*s"
134147005Sdes
135147005Sdesstatic char up_header[] =
136147005Sdes  "  PID %-*.*s PRI NICE  SIZE    RES STATE    TIME   WCPU    CPU COMMAND";
137147005Sdes
138147005Sdes#define up_Proc_format \
139147005Sdes	"%5d %-*.*s %3d %3d%7s %6s %-6.6s%.0d%7s %5.2f%% %5.2f%% %.*s"
140147005Sdes
141147005Sdes
142147005Sdes
143147005Sdes/* process state names for the "STATE" column of the display */
144147005Sdes/* the extra nulls in the string "run" are for adding a slash and
145147005Sdes   the processor number when needed */
146147005Sdes
14798937Sdeschar *state_abbrev[] =
14898937Sdes{
14998937Sdes    "", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB",
150162856Sdes};
151162856Sdes
152162856Sdes
153162856Sdesstatic kvm_t *kd;
154162856Sdes
155162856Sdes/* values that we stash away in _init and use in later routines */
156162856Sdes
157162856Sdesstatic double logcpu;
158162856Sdes
159162856Sdes/* these are retrieved from the kernel in _init */
160162856Sdes
161162856Sdesstatic          long hz;
162162856Sdesstatic load_avg  ccpu;
163162856Sdes
164181111Sdes/* these are offsets obtained via nlist and used in the get_ functions */
165162856Sdes
166162856Sdesstatic unsigned long cp_time_offset;
167162856Sdesstatic unsigned long avenrun_offset;
168162856Sdesstatic unsigned long lastpid_offset;
169162856Sdesstatic long lastpid;
17098937Sdesstatic unsigned long cnt_offset;
17198937Sdesstatic unsigned long bufspace_offset;
17298937Sdesstatic long cnt;
17398937Sdes
174147005Sdes/* these are for calculating cpu state percentages */
175147005Sdes
176147005Sdesstatic long cp_time[CPUSTATES];
177147005Sdesstatic long cp_old[CPUSTATES];
17898937Sdesstatic long cp_diff[CPUSTATES];
17998937Sdes
180147005Sdes/* these are for detailing the process states */
18198937Sdes
18298937Sdesint process_states[6];
18398937Sdeschar *procstatenames[] = {
18498937Sdes    "", " starting, ", " running, ", " sleeping, ", " stopped, ",
18598937Sdes    " zombie, ",
18698937Sdes    NULL
18798937Sdes};
18898937Sdes
18998937Sdes/* these are for detailing the cpu states */
19098937Sdes
19198937Sdesint cpu_states[CPUSTATES];
19298937Sdeschar *cpustatenames[] = {
19398937Sdes    "user", "nice", "system", "interrupt", "idle", NULL
19498937Sdes};
19598937Sdes
19698937Sdes/* these are for detailing the memory statistics */
19798937Sdes
19898937Sdesint memory_stats[7];
19998937Sdeschar *memorynames[] = {
20098937Sdes    "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free",
20198937Sdes    NULL
20298937Sdes};
20398937Sdes
20498937Sdesint swap_stats[7];
20598937Sdeschar *swapnames[] = {
206202213Sed/*   0           1            2           3            4       5 */
20798937Sdes    "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out",
20898937Sdes    NULL
20998937Sdes};
210147005Sdes
211147005Sdes
21298937Sdes/* these are for keeping track of the proc array */
213147005Sdes
21498937Sdesstatic int nproc;
21598937Sdesstatic int onproc = -1;
21698937Sdesstatic int pref_len;
21798937Sdesstatic struct kinfo_proc *pbase;
21898937Sdesstatic struct kinfo_proc **pref;
219147005Sdes
220147005Sdes/* these are for getting the memory statistics */
22198937Sdes
22298937Sdesstatic int pageshift;		/* log base 2 of the pagesize */
22398937Sdes
22498937Sdes/* define pagetok in terms of pageshift */
22598937Sdes
22698937Sdes#define pagetok(size) ((size) << pageshift)
22798937Sdes
22898937Sdes/* useful externals */
22998937Sdeslong percentages();
230147005Sdes
23198937Sdes#ifdef ORDER
23298937Sdes/* sorting orders. first is default */
233147005Sdeschar *ordernames[] = {
23498937Sdes    "cpu", "size", "res", "time", "pri", NULL
23598937Sdes};
23698937Sdes#endif
237147005Sdes
238147005Sdesint
23998937Sdesmachine_init(statics)
24098937Sdes
24198937Sdesstruct statics *statics;
24298937Sdes
24398937Sdes{
24498937Sdes    register int i = 0;
24598937Sdes    register int pagesize;
24698937Sdes    int modelen;
24798937Sdes    struct passwd *pw;
24898937Sdes
24998937Sdes    modelen = sizeof(smpmode);
250147005Sdes    if ((sysctlbyname("machdep.smp_active", &smpmode, &modelen, NULL, 0) < 0 &&
25198937Sdes         sysctlbyname("smp.smp_active", &smpmode, &modelen, NULL, 0) < 0) ||
25298937Sdes	modelen != sizeof(smpmode))
253147005Sdes	    smpmode = 0;
254147005Sdes
25598937Sdes    while ((pw = getpwent()) != NULL) {
25698937Sdes	if (strlen(pw->pw_name) > namelength)
25798937Sdes	    namelength = strlen(pw->pw_name);
25898937Sdes    }
25998937Sdes    if (namelength < 8)
26098937Sdes	namelength = 8;
26198937Sdes    if (namelength > 16)
26298937Sdes	namelength = 16;
26398937Sdes
26498937Sdes    if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
26598937Sdes	return -1;
26698937Sdes
26798937Sdes
26898937Sdes    /* get the list of symbols we want to access in the kernel */
26998937Sdes    (void) kvm_nlist(kd, nlst);
27098937Sdes    if (nlst[0].n_type == 0)
27198937Sdes    {
272221420Sdes	fprintf(stderr, "top: nlist failed\n");
27398937Sdes	return(-1);
27498937Sdes    }
27598937Sdes
27698937Sdes    /* make sure they were all found */
277147005Sdes    if (i > 0 && check_nlist(nlst) > 0)
27898937Sdes    {
279147005Sdes	return(-1);
28098937Sdes    }
28198937Sdes
282147005Sdes    /* get the symbol values out of kmem */
283147005Sdes    (void) getkval(nlst[X_STATHZ].n_value, (int *)(&hz), sizeof(hz), "!");
28498937Sdes    if (!hz) {
28598937Sdes	(void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
28698937Sdes		       nlst[X_HZ].n_name);
28798937Sdes    }
28898937Sdes
28998937Sdes    (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),	sizeof(ccpu),
29098937Sdes	    nlst[X_CCPU].n_name);
29198937Sdes
29298937Sdes    /* stash away certain offsets for later use */
29398937Sdes    cp_time_offset = nlst[X_CP_TIME].n_value;
29498937Sdes    avenrun_offset = nlst[X_AVENRUN].n_value;
29598937Sdes    lastpid_offset =  nlst[X_LASTPID].n_value;
296221420Sdes    cnt_offset = nlst[X_CNT].n_value;
29798937Sdes    bufspace_offset = nlst[X_BUFSPACE].n_value;
29898937Sdes
29998937Sdes    /* this is used in calculating WCPU -- calculate it ahead of time */
30098937Sdes    logcpu = log(loaddouble(ccpu));
30198937Sdes
30298937Sdes    pbase = NULL;
30398937Sdes    pref = NULL;
30498937Sdes    nproc = 0;
30598937Sdes    onproc = -1;
30698937Sdes    /* get the page size with "getpagesize" and calculate pageshift from it */
30798937Sdes    pagesize = getpagesize();
30898937Sdes    pageshift = 0;
30998937Sdes    while (pagesize > 1)
310221420Sdes    {
311221420Sdes	pageshift++;
31298937Sdes	pagesize >>= 1;
31398937Sdes    }
314147005Sdes
31598937Sdes    /* we only need the amount of log(2)1024 for our conversion */
31698937Sdes    pageshift -= LOG1024;
31798937Sdes
318147005Sdes    /* fill in the statics information */
31998937Sdes    statics->procstate_names = procstatenames;
320147005Sdes    statics->cpustate_names = cpustatenames;
32198937Sdes    statics->memory_names = memorynames;
32298937Sdes    statics->swap_names = swapnames;
32398937Sdes#ifdef ORDER
324147005Sdes    statics->order_names = ordernames;
325147005Sdes#endif
32698937Sdes
32798937Sdes    /* all done! */
32898937Sdes    return(0);
32998937Sdes}
33098937Sdes
33198937Sdeschar *format_header(uname_field)
33298937Sdes
33398937Sdesregister char *uname_field;
33498937Sdes
335221420Sdes{
336147005Sdes    register char *ptr;
33798937Sdes    static char Header[128];
33898937Sdes
33998937Sdes    snprintf(Header, sizeof(Header), smpmode ? smp_header : up_header,
340147005Sdes	     namelength, namelength, uname_field);
341147005Sdes
342147005Sdes    cmdlength = 80 - strlen(Header) + 6;
34398937Sdes
34498937Sdes    return Header;
34598937Sdes}
34698937Sdes
34798937Sdesstatic int swappgsin = -1;
34898937Sdesstatic int swappgsout = -1;
34998937Sdesextern struct timeval timeout;
350255767Sdes
35198937Sdesvoid
35298937Sdesget_system_info(si)
35398937Sdes
35498937Sdesstruct system_info *si;
35598937Sdes
35698937Sdes{
35798937Sdes    long total;
35898937Sdes    load_avg avenrun[3];
35998937Sdes
36098937Sdes    /* get the cp_time array */
36198937Sdes    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
36298937Sdes		   nlst[X_CP_TIME].n_name);
363221420Sdes    (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
364147005Sdes		   nlst[X_AVENRUN].n_name);
36598937Sdes
36698937Sdes    (void) getkval(lastpid_offset, (int *)(&lastpid), sizeof(lastpid),
36798937Sdes		   "!");
36898937Sdes
36998937Sdes    /* convert load averages to doubles */
37098937Sdes    {
37198937Sdes	register int i;
37298937Sdes	register double *infoloadp;
37398937Sdes	load_avg *avenrunp;
37498937Sdes
37598937Sdes#ifdef notyet
37698937Sdes	struct loadavg sysload;
37798937Sdes	int size;
37898937Sdes	getkerninfo(KINFO_LOADAVG, &sysload, &size, 0);
379147005Sdes#endif
380149753Sdes
381147005Sdes	infoloadp = si->load_avg;
382147005Sdes	avenrunp = avenrun;
38398937Sdes	for (i = 0; i < 3; i++)
38498937Sdes	{
38598937Sdes#ifdef notyet
38698937Sdes	    *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
38798937Sdes#endif
38898937Sdes	    *infoloadp++ = loaddouble(*avenrunp++);
389147005Sdes	}
39098937Sdes    }
39198937Sdes
392149753Sdes    /* convert cp_time counts to percentages */
393147005Sdes    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
39498937Sdes
39598937Sdes    /* sum memory & swap statistics */
39698937Sdes    {
39798937Sdes	struct vmmeter sum;
39898937Sdes	static unsigned int swap_delay = 0;
39998937Sdes	static int swapavail = 0;
40098937Sdes	static int swapfree = 0;
40198937Sdes	static int bufspace = 0;
40298937Sdes
40398937Sdes        (void) getkval(cnt_offset, (int *)(&sum), sizeof(sum),
40498937Sdes		   "_cnt");
40598937Sdes        (void) getkval(bufspace_offset, (int *)(&bufspace), sizeof(bufspace),
40698937Sdes		   "_bufspace");
40798937Sdes
40898937Sdes	/* convert memory stats to Kbytes */
40998937Sdes	memory_stats[0] = pagetok(sum.v_active_count);
41098937Sdes	memory_stats[1] = pagetok(sum.v_inactive_count);
41198937Sdes	memory_stats[2] = pagetok(sum.v_wire_count);
41298937Sdes	memory_stats[3] = pagetok(sum.v_cache_count);
413147005Sdes	memory_stats[4] = bufspace / 1024;
41498937Sdes	memory_stats[5] = pagetok(sum.v_free_count);
41598937Sdes	memory_stats[6] = -1;
41698937Sdes
41798937Sdes	/* first interval */
41898937Sdes        if (swappgsin < 0) {
41998937Sdes	    swap_stats[4] = 0;
42098937Sdes	    swap_stats[5] = 0;
421147005Sdes	}
42298937Sdes
42398937Sdes	/* compute differences between old and new swap statistic */
42498937Sdes	else {
42598937Sdes	    swap_stats[4] = pagetok(((sum.v_swappgsin - swappgsin)));
42698937Sdes	    swap_stats[5] = pagetok(((sum.v_swappgsout - swappgsout)));
42798937Sdes	}
42898937Sdes
42998937Sdes        swappgsin = sum.v_swappgsin;
430147005Sdes	swappgsout = sum.v_swappgsout;
43198937Sdes
43298937Sdes	/* call CPU heavy swapmode() only for changes */
433147005Sdes        if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) {
434147005Sdes	    swap_stats[3] = swapmode(&swapavail, &swapfree);
435147005Sdes	    swap_stats[0] = swapavail;
43698937Sdes	    swap_stats[1] = swapavail - swapfree;
43798937Sdes	    swap_stats[2] = swapfree;
43898937Sdes	}
43998937Sdes        swap_delay = 1;
44098937Sdes	swap_stats[6] = -1;
44198937Sdes    }
44298937Sdes
44398937Sdes    /* set arrays and strings */
44498937Sdes    si->cpustates = cpu_states;
445147005Sdes    si->memory = memory_stats;
44698937Sdes    si->swap = swap_stats;
44798937Sdes
44898937Sdes
44998937Sdes    if(lastpid > 0) {
45098937Sdes	si->last_pid = lastpid;
45198937Sdes    } else {
45298937Sdes	si->last_pid = -1;
45398937Sdes    }
45498937Sdes}
45598937Sdes
45698937Sdesstatic struct handle handle;
45798937Sdes
45898937Sdescaddr_t get_process_info(si, sel, compare)
45998937Sdes
460137019Sdesstruct system_info *si;
461149753Sdesstruct process_select *sel;
462149753Sdesint (*compare)();
463149753Sdes
464137019Sdes{
465137019Sdes    register int i;
466147005Sdes    register int total_procs;
467147005Sdes    register int active_procs;
468221420Sdes    register struct kinfo_proc **prefp;
469147005Sdes    register struct kinfo_proc *pp;
470221420Sdes
471147005Sdes    /* these are copied out of sel for speed */
472147005Sdes    int show_idle;
47398937Sdes    int show_self;
47498937Sdes    int show_system;
47598937Sdes    int show_uid;
47698937Sdes    int show_command;
47798937Sdes
47898937Sdes
479126277Sdes    pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
48098937Sdes    if (nproc > onproc)
48198937Sdes	pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
48298937Sdes		* (onproc = nproc));
48398937Sdes    if (pref == NULL || pbase == NULL) {
48498937Sdes	(void) fprintf(stderr, "top: Out of memory.\n");
48598937Sdes	quit(23);
48698937Sdes    }
48798937Sdes    /* get a pointer to the states summary array */
48898937Sdes    si->procstates = process_states;
48998937Sdes
49098937Sdes    /* set up flags which define what we are going to select */
49198937Sdes    show_idle = sel->idle;
49298937Sdes    show_self = sel->self;
493147005Sdes    show_system = sel->system;
49498937Sdes    show_uid = sel->uid != -1;
49598937Sdes    show_command = sel->command != NULL;
49698937Sdes
49798937Sdes    /* count up process states and get pointers to interesting procs */
49898937Sdes    total_procs = 0;
49998937Sdes    active_procs = 0;
50098937Sdes    memset((char *)process_states, 0, sizeof(process_states));
50198937Sdes    prefp = pref;
50298937Sdes    for (pp = pbase, i = 0; i < nproc; pp++, i++)
50398937Sdes    {
50498937Sdes	/*
50598937Sdes	 *  Place pointers to each valid proc structure in pref[].
50698937Sdes	 *  Process slots that are actually in use have a non-zero
50798937Sdes	 *  status field.  Processes with P_SYSTEM set are system
50898937Sdes	 *  processes---these get ignored unless show_sysprocs is set.
509207319Sdes	 */
510207319Sdes	if (PP(pp, p_stat) != 0 &&
511207319Sdes	    (show_self != PP(pp, p_pid)) &&
512207319Sdes	    (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0)))
51398937Sdes	{
514147005Sdes	    total_procs++;
51598937Sdes	    process_states[(unsigned char) PP(pp, p_stat)]++;
51698937Sdes	    if ((PP(pp, p_stat) != SZOMB) &&
517147005Sdes		(show_idle || (PP(pp, p_pctcpu) != 0) ||
518147005Sdes		 (PP(pp, p_stat) == SRUN)) &&
519147005Sdes		(!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
52098937Sdes	    {
52198937Sdes		*prefp++ = pp;
522147005Sdes		active_procs++;
523147005Sdes	    }
52498937Sdes	}
52598937Sdes    }
526147005Sdes
52798937Sdes    /* if requested, sort the "interesting" processes */
528147005Sdes    if (compare != NULL)
52998937Sdes    {
53098937Sdes	qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
53198937Sdes    }
53298937Sdes
53398937Sdes    /* remember active and total counts */
53498937Sdes    si->p_total = total_procs;
53598937Sdes    si->p_active = pref_len = active_procs;
53698937Sdes
53798937Sdes    /* pass back a handle */
53898937Sdes    handle.next_proc = pref;
53998937Sdes    handle.remaining = active_procs;
54098937Sdes    return((caddr_t)&handle);
54198937Sdes}
54298937Sdes
54398937Sdeschar fmt[128];		/* static area where result is built */
54498937Sdes
54598937Sdeschar *format_next_process(handle, get_userid)
54698937Sdes
54798937Sdescaddr_t handle;
54898937Sdeschar *(*get_userid)();
54998937Sdes
55098937Sdes{
55198937Sdes    register struct kinfo_proc *pp;
552147005Sdes    register long cputime;
553147005Sdes    register double pct;
554147005Sdes    struct handle *hp;
555147005Sdes    char status[16];
55698937Sdes
557149753Sdes    /* find and remember the next proc structure */
55898937Sdes    hp = (struct handle *)handle;
55998937Sdes    pp = *(hp->next_proc++);
560147005Sdes    hp->remaining--;
56198937Sdes
562147005Sdes
56398937Sdes    /* get the process's user struct and set cputime */
56498937Sdes    if ((PP(pp, p_flag) & P_INMEM) == 0) {
56598937Sdes	/*
566147005Sdes	 * Print swapped processes as <pname>
56798937Sdes	 */
56898937Sdes	char *comm = PP(pp, p_comm);
56998937Sdes#define COMSIZ sizeof(PP(pp, p_comm))
57098937Sdes	char buf[COMSIZ];
57198937Sdes	(void) strncpy(buf, comm, COMSIZ);
57298937Sdes	comm[0] = '<';
57398937Sdes	(void) strncpy(&comm[1], buf, COMSIZ - 2);
57498937Sdes	comm[COMSIZ - 2] = '\0';
57598937Sdes	(void) strncat(comm, ">", COMSIZ - 1);
57698937Sdes	comm[COMSIZ - 1] = '\0';
57798937Sdes    }
578147005Sdes
57998937Sdes#if 0
58098937Sdes    /* This does not produce the correct results */
581149753Sdes    cputime = PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks);
582147005Sdes#endif
58398937Sdes    /* This does not count interrupts */
58498937Sdes    cputime = (PP(pp, p_runtime) / 1000 + 500) / 1000;
58598937Sdes
58698937Sdes    /* calculate the base for cpu percentages */
587147005Sdes    pct = pctdouble(PP(pp, p_pctcpu));
588147005Sdes
58998937Sdes    /* generate "STATE" field */
59098937Sdes    switch (PP(pp, p_stat)) {
59198937Sdes	case SRUN:
59298937Sdes	    if (smpmode && PP(pp, p_oncpu) >= 0)
59398937Sdes		sprintf(status, "CPU%d", PP(pp, p_oncpu));
59498937Sdes	    else
59598937Sdes		strcpy(status, "RUN");
59698937Sdes	    break;
59798937Sdes	case SSLEEP:
59898937Sdes	    if (PP(pp, p_wmesg) != NULL) {
59998937Sdes		sprintf(status, "%.6s", EP(pp, e_wmesg));
60098937Sdes		break;
60198937Sdes	    }
60298937Sdes	    /* fall through */
60398937Sdes	default:
60498937Sdes	    sprintf(status, "%.6s", state_abbrev[(unsigned char) PP(pp, p_stat)]);
60598937Sdes	    break;
60698937Sdes    }
60798937Sdes
60898937Sdes    /* format this entry */
60998937Sdes    sprintf(fmt,
61098937Sdes	    smpmode ? smp_Proc_format : up_Proc_format,
61198937Sdes	    PP(pp, p_pid),
61298937Sdes	    namelength, namelength,
61398937Sdes	    (*get_userid)(EP(pp, e_pcred.p_ruid)),
61498937Sdes	    PP(pp, p_priority) - PZERO,
615147005Sdes
61698937Sdes	    /*
61798937Sdes	     * normal time      -> nice value -20 - +20
61898937Sdes	     * real time 0 - 31 -> nice value -52 - -21
61998937Sdes	     * idle time 0 - 31 -> nice value +21 - +52
62098937Sdes	     */
62198937Sdes	    (PP(pp, p_rtprio.type) ==  RTP_PRIO_NORMAL ?
62298937Sdes	    	PP(pp, p_nice) - NZERO :
62398937Sdes	    	(PP(pp, p_rtprio.type) ==  RTP_PRIO_REALTIME ?
62498937Sdes		    (PRIO_MIN - 1 - RTP_PRIO_MAX + PP(pp, p_rtprio.prio)) :
62598937Sdes		    (PRIO_MAX + 1 + PP(pp, p_rtprio.prio)))),
62698937Sdes	    format_k2(PROCSIZE(pp)),
62798937Sdes	    format_k2(pagetok(VP(pp, vm_rssize))),
62898937Sdes	    status,
62998937Sdes	    smpmode ? PP(pp, p_lastcpu) : 0,
63098937Sdes	    format_time(cputime),
631147005Sdes	    10000.0 * weighted_cpu(pct, pp) / hz,
63298937Sdes	    10000.0 * pct / hz,
63398937Sdes	    cmdlength,
634147005Sdes	    printable(PP(pp, p_comm)));
63598937Sdes
63698937Sdes    /* return the result */
63798937Sdes    return(fmt);
63898937Sdes}
63998937Sdes
64098937Sdes
64198937Sdes/*
64298937Sdes * check_nlist(nlst) - checks the nlist to see if any symbols were not
643113911Sdes *		found.  For every symbol that was not found, a one-line
644113911Sdes *		message is printed to stderr.  The routine returns the
645147005Sdes *		number of symbols NOT found.
646147005Sdes */
64798937Sdes
64898937Sdesstatic int check_nlist(nlst)
64998937Sdes
65098937Sdesregister struct nlist *nlst;
65198937Sdes
65298937Sdes{
65398937Sdes    register int i;
65498937Sdes
65598937Sdes    /* check to see if we got ALL the symbols we requested */
65698937Sdes    /* this will write one line to stderr for every symbol not found */
65798937Sdes
65898937Sdes    i = 0;
65998937Sdes    while (nlst->n_name != NULL)
660106130Sdes    {
66198937Sdes	if (nlst->n_type == 0)
66298937Sdes	{
66398937Sdes	    /* this one wasn't found */
66498937Sdes	    (void) fprintf(stderr, "kernel: no symbol named `%s'\n",
66598937Sdes			   nlst->n_name);
666106130Sdes	    i = 1;
66798937Sdes	}
66898937Sdes	nlst++;
66998937Sdes    }
67098937Sdes
67198937Sdes    return(i);
67298937Sdes}
67398937Sdes
67498937Sdes
67598937Sdes/*
67698937Sdes *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
67798937Sdes *	"offset" is the byte offset into the kernel for the desired value,
67898937Sdes *  	"ptr" points to a buffer into which the value is retrieved,
67998937Sdes *  	"size" is the size of the buffer (and the object to retrieve),
68098937Sdes *  	"refstr" is a reference string used when printing error meessages,
68198937Sdes *	    if "refstr" starts with a '!', then a failure on read will not
682147005Sdes *  	    be fatal (this may seem like a silly way to do things, but I
68398937Sdes *  	    really didn't want the overhead of another argument).
68498937Sdes *
68598937Sdes */
68698937Sdes
68798937Sdesstatic int getkval(offset, ptr, size, refstr)
68898937Sdes
68998937Sdesunsigned long offset;
690147005Sdesint *ptr;
691147005Sdesint size;
69298937Sdeschar *refstr;
693184122Sdes
694184122Sdes{
69598937Sdes    if (kvm_read(kd, offset, (char *) ptr, size) != size)
69698937Sdes    {
69798937Sdes	if (*refstr == '!')
69898937Sdes	{
69998937Sdes	    return(0);
70098937Sdes	}
701113911Sdes	else
702113911Sdes	{
703113911Sdes	    fprintf(stderr, "top: kvm_read for %s: %s\n",
704113911Sdes		refstr, strerror(errno));
705113911Sdes	    quit(23);
706113911Sdes	}
707113911Sdes    }
708113911Sdes    return(1);
709113911Sdes}
710113911Sdes
711113911Sdes/* comparison routines for qsort */
712113911Sdes
713113911Sdes/*
71498937Sdes *  proc_compare - comparison function for "qsort"
71598937Sdes *	Compares the resource consumption of two processes using five
71698937Sdes *  	distinct keys.  The keys (in descending order of importance) are:
71798937Sdes *  	percent cpu, cpu ticks, state, resident set size, total virtual
71898937Sdes *  	memory usage.  The process states are ordered as follows (from least
71998937Sdes *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
72098937Sdes *  	array declaration below maps a process state index into a number
72198937Sdes *  	that reflects this ordering.
72298937Sdes */
72398937Sdes
72498937Sdesstatic unsigned char sorted_state[] =
72598937Sdes{
72698937Sdes    0,	/* not used		*/
72798937Sdes    3,	/* sleep		*/
72898937Sdes    1,	/* ABANDONED (WAIT)	*/
729147005Sdes    6,	/* run			*/
73098937Sdes    5,	/* start		*/
73198937Sdes    2,	/* zombie		*/
732147005Sdes    4	/* stop			*/
73398937Sdes};
734147005Sdes
73598937Sdes
73698937Sdes#define ORDERKEY_PCTCPU \
73798937Sdes  if (lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu), \
73898937Sdes     (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
73998937Sdes
740113911Sdes#define ORDERKEY_CPTICKS \
741113911Sdes  if ((result = PP(p2, p_runtime) - PP(p1, p_runtime)) == 0)
742113911Sdes
74398937Sdes#define ORDERKEY_STATE \
744147005Sdes  if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - \
74598937Sdes                sorted_state[(unsigned char) PP(p1, p_stat)]) == 0)
74698937Sdes
74798937Sdes#define ORDERKEY_PRIO \
74898937Sdes  if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
74998937Sdes
75098937Sdes#define ORDERKEY_RSSIZE \
75198937Sdes  if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
75298937Sdes
75398937Sdes#define ORDERKEY_MEM \
75498937Sdes  if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 )
75598937Sdes
75698937Sdes/* compare_cpu - the comparison function for sorting by cpu percentage */
75798937Sdes
75898937Sdesint
75998937Sdes#ifdef ORDER
76098937Sdescompare_cpu(pp1, pp2)
761147005Sdes#else
76298937Sdesproc_compare(pp1, pp2)
763202213Sed#endif
764202213Sed
76598937Sdesstruct proc **pp1;
76698937Sdesstruct proc **pp2;
76798937Sdes
76898937Sdes{
76998937Sdes    register struct kinfo_proc *p1;
77098937Sdes    register struct kinfo_proc *p2;
77198937Sdes    register int result;
77298937Sdes    register pctcpu lresult;
77398937Sdes
77498937Sdes    /* remove one level of indirection */
775147005Sdes    p1 = *(struct kinfo_proc **) pp1;
776147005Sdes    p2 = *(struct kinfo_proc **) pp2;
77798937Sdes
77898937Sdes    ORDERKEY_PCTCPU
77998937Sdes    ORDERKEY_CPTICKS
78098937Sdes    ORDERKEY_STATE
78198937Sdes    ORDERKEY_PRIO
78298937Sdes    ORDERKEY_RSSIZE
783113911Sdes    ORDERKEY_MEM
784113911Sdes    ;
785113911Sdes
786113911Sdes    return(result);
787113911Sdes}
788113911Sdes
789113911Sdes#ifdef ORDER
790113911Sdes/* compare routines */
791113911Sdesint compare_size(), compare_res(), compare_time(), compare_prio();
792113911Sdes
793113911Sdesint (*proc_compares[])() = {
794113911Sdes    compare_cpu,
795113911Sdes    compare_size,
79698937Sdes    compare_res,
79798937Sdes    compare_time,
79898937Sdes    compare_prio,
79998937Sdes    NULL
80098937Sdes};
80198937Sdes
80298937Sdes/* compare_size - the comparison function for sorting by total memory usage */
80398937Sdes
80498937Sdesint
80598937Sdescompare_size(pp1, pp2)
80698937Sdes
80798937Sdesstruct proc **pp1;
80898937Sdesstruct proc **pp2;
80998937Sdes
81098937Sdes{
81198937Sdes    register struct kinfo_proc *p1;
81298937Sdes    register struct kinfo_proc *p2;
81398937Sdes    register int result;
81498937Sdes    register pctcpu lresult;
81598937Sdes
81698937Sdes    /* remove one level of indirection */
81798937Sdes    p1 = *(struct kinfo_proc **) pp1;
81898937Sdes    p2 = *(struct kinfo_proc **) pp2;
81998937Sdes
82098937Sdes    ORDERKEY_MEM
82198937Sdes    ORDERKEY_RSSIZE
82298937Sdes    ORDERKEY_PCTCPU
82398937Sdes    ORDERKEY_CPTICKS
82498937Sdes    ORDERKEY_STATE
82598937Sdes    ORDERKEY_PRIO
82698937Sdes    ;
827147005Sdes
82898937Sdes    return(result);
82998937Sdes}
83098937Sdes
831149753Sdes/* compare_res - the comparison function for sorting by resident set size */
832147005Sdes
833147005Sdesint
834147005Sdescompare_res(pp1, pp2)
83598937Sdes
83698937Sdesstruct proc **pp1;
83798937Sdesstruct proc **pp2;
83898937Sdes
83998937Sdes{
84098937Sdes    register struct kinfo_proc *p1;
84198937Sdes    register struct kinfo_proc *p2;
84298937Sdes    register int result;
84398937Sdes    register pctcpu lresult;
84498937Sdes
845147005Sdes    /* remove one level of indirection */
84698937Sdes    p1 = *(struct kinfo_proc **) pp1;
84798937Sdes    p2 = *(struct kinfo_proc **) pp2;
84898937Sdes
849147005Sdes    ORDERKEY_RSSIZE
85098937Sdes    ORDERKEY_MEM
85198937Sdes    ORDERKEY_PCTCPU
85298937Sdes    ORDERKEY_CPTICKS
85398937Sdes    ORDERKEY_STATE
85498937Sdes    ORDERKEY_PRIO
85598937Sdes    ;
856147005Sdes
857137019Sdes    return(result);
858137019Sdes}
85998937Sdes
86098937Sdes/* compare_time - the comparison function for sorting by total cpu time */
86198937Sdes
86298937Sdesint
86398937Sdescompare_time(pp1, pp2)
86498937Sdes
86598937Sdesstruct proc **pp1;
86698937Sdesstruct proc **pp2;
867137019Sdes
868137019Sdes{
869137019Sdes    register struct kinfo_proc *p1;
870137019Sdes    register struct kinfo_proc *p2;
871147005Sdes    register int result;
872221420Sdes    register pctcpu lresult;
873137019Sdes
874137019Sdes    /* remove one level of indirection */
875137019Sdes    p1 = *(struct kinfo_proc **) pp1;
876149753Sdes    p2 = *(struct kinfo_proc **) pp2;
877147005Sdes
878221420Sdes    ORDERKEY_CPTICKS
879137019Sdes    ORDERKEY_PCTCPU
880137019Sdes    ORDERKEY_STATE
88198937Sdes    ORDERKEY_PRIO
88298937Sdes    ORDERKEY_RSSIZE
88398937Sdes    ORDERKEY_MEM
88498937Sdes    ;
88598937Sdes
88698937Sdes      return(result);
887147005Sdes  }
888147005Sdes
889147005Sdes/* compare_prio - the comparison function for sorting by cpu percentage */
890147005Sdes
89198937Sdesint
892137019Sdescompare_prio(pp1, pp2)
893147005Sdes
894221420Sdesstruct proc **pp1;
895137019Sdesstruct proc **pp2;
896137019Sdes
897137019Sdes{
898147005Sdes    register struct kinfo_proc *p1;
899137019Sdes    register struct kinfo_proc *p2;
900221420Sdes    register int result;
901137019Sdes    register pctcpu lresult;
902137019Sdes
903147005Sdes    /* remove one level of indirection */
904137019Sdes    p1 = *(struct kinfo_proc **) pp1;
90598937Sdes    p2 = *(struct kinfo_proc **) pp2;
906221420Sdes
907221420Sdes    ORDERKEY_PRIO
908147005Sdes    ORDERKEY_CPTICKS
90998937Sdes    ORDERKEY_PCTCPU
910147005Sdes    ORDERKEY_STATE
911147005Sdes    ORDERKEY_RSSIZE
91298937Sdes    ORDERKEY_MEM
913147005Sdes    ;
91498937Sdes
91598937Sdes    return(result);
91698937Sdes}
91798937Sdes#endif
91898937Sdes
91998937Sdes/*
92098937Sdes * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
92198937Sdes *		the process does not exist.
92298937Sdes *		It is EXTREMLY IMPORTANT that this function work correctly.
92398937Sdes *		If top runs setuid root (as in SVR4), then this function
92498937Sdes *		is the only thing that stands in the way of a serious
92598937Sdes *		security problem.  It validates requests for the "kill"
926147005Sdes *		and "renice" commands.
927147005Sdes */
92898937Sdes
92998937Sdesint proc_owner(pid)
93098937Sdes
931147005Sdesint pid;
932147005Sdes
93398937Sdes{
93498937Sdes    register int cnt;
935147005Sdes    register struct kinfo_proc **prefp;
93698937Sdes    register struct kinfo_proc *pp;
93798937Sdes
93898937Sdes    prefp = pref;
93998937Sdes    cnt = pref_len;
94098937Sdes    while (--cnt >= 0)
94198937Sdes    {
94298937Sdes	pp = *prefp++;
94398937Sdes	if (PP(pp, p_pid) == (pid_t)pid)
94498937Sdes	{
94598937Sdes	    return((int)EP(pp, e_pcred.p_ruid));
94698937Sdes	}
947147005Sdes    }
948147005Sdes    return(-1);
94998937Sdes}
95098937Sdes
95198937Sdes
952147005Sdes/*
953147005Sdes * swapmode is based on a program called swapinfo written
95498937Sdes * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
95598937Sdes */
956147005Sdes
95798937Sdes#define	SVAR(var) __STRING(var)	/* to force expansion */
95898937Sdes#define	KGET(idx, var)							\
95998937Sdes	KGET1(idx, &var, sizeof(var), SVAR(var))
96098937Sdes#define	KGET1(idx, p, s, msg)						\
96198937Sdes	KGET2(nlst[idx].n_value, p, s, msg)
96298937Sdes#define	KGET2(addr, p, s, msg)						\
96398937Sdes	if (kvm_read(kd, (u_long)(addr), p, s) != s) {		        \
96498937Sdes		warnx("cannot read %s: %s", msg, kvm_geterr(kd));       \
965147005Sdes		return (0);                                             \
96698937Sdes       }
96798937Sdes#define	KGETRET(addr, p, s, msg)					\
968147005Sdes	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
96998937Sdes		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
97098937Sdes		return (0);						\
971147005Sdes	}
972147005Sdes
97398937Sdes
97498937Sdesint
97598937Sdesswapmode(retavail, retfree)
97698937Sdes	int *retavail;
97798937Sdes	int *retfree;
97898937Sdes{
97998937Sdes	char *header;
98098937Sdes	int hlen, nswap, nswdev, dmmax;
98198937Sdes	int i, div, avail, nfree, npfree, used;
98298937Sdes	struct swdevt *sw;
98398937Sdes	long blocksize, *perdev;
98498937Sdes	u_long ptr;
98598937Sdes	struct rlist head;
98698937Sdes#if __FreeBSD_version >= 220000
98798937Sdes	struct rlisthdr swaplist;
98898937Sdes#else
98998937Sdes	struct rlist *swaplist;
99098937Sdes#endif
99198937Sdes	struct rlist *swapptr;
99298937Sdes
99398937Sdes	/*
99498937Sdes	 * Counter for error messages. If we reach the limit,
99598937Sdes	 * stop reading information from swap devices and
99698937Sdes	 * return zero. This prevent endless 'bad address'
99798937Sdes	 * messages.
99898937Sdes	 */
99998937Sdes	static warning = 10;
100098937Sdes
100198937Sdes	if (warning <= 0) {
100298937Sdes	    /* a single warning */
1003147005Sdes	    if (!warning) {
100498937Sdes		warning--;
100598937Sdes		fprintf(stderr,
100698937Sdes			"Too much errors, stop reading swap devices ...\n");
100798937Sdes		(void)sleep(3);
100898937Sdes	    }
100998937Sdes	    return(0);
101098937Sdes	}
101198937Sdes	warning--; /* decrease counter, see end of function */
1012147005Sdes
1013147005Sdes	KGET(VM_NSWAP, nswap);
101498937Sdes	if (!nswap) {
101598937Sdes		fprintf(stderr, "No swap space available\n");
101698937Sdes		return(0);
101798937Sdes	}
101898937Sdes
101998937Sdes	KGET(VM_NSWDEV, nswdev);
102098937Sdes	KGET(VM_DMMAX, dmmax);
102198937Sdes	KGET1(VM_SWAPLIST, &swaplist, sizeof(swaplist), "swaplist");
102298937Sdes	if ((sw = (struct swdevt *)malloc(nswdev * sizeof(*sw))) == NULL ||
102398937Sdes	    (perdev = (long *)malloc(nswdev * sizeof(*perdev))) == NULL)
102498937Sdes		err(1, "malloc");
1025147005Sdes	KGET1(VM_SWDEVT, &ptr, sizeof ptr, "swdevt");
1026147005Sdes	KGET2(ptr, sw, nswdev * sizeof(*sw), "*swdevt");
102798937Sdes
102898937Sdes	/* Count up swap space. */
102998937Sdes	nfree = 0;
1030147005Sdes	memset(perdev, 0, nswdev * sizeof(*perdev));
1031147005Sdes#if  __FreeBSD_version >= 220000
103298937Sdes	swapptr = swaplist.rlh_list;
103398937Sdes	while (swapptr) {
1034147005Sdes#else
103598937Sdes	while (swaplist) {
103698937Sdes#endif
103798937Sdes		int	top, bottom, next_block;
103898937Sdes#if  __FreeBSD_version >= 220000
103998937Sdes		KGET2(swapptr, &head, sizeof(struct rlist), "swapptr");
104098937Sdes#else
104198937Sdes		KGET2(swaplist, &head, sizeof(struct rlist), "swaplist");
104298937Sdes#endif
104398937Sdes
104498937Sdes		top = head.rl_end;
104598937Sdes		bottom = head.rl_start;
104698937Sdes
104798937Sdes		nfree += top - bottom + 1;
104898937Sdes
104998937Sdes		/*
105098937Sdes		 * Swap space is split up among the configured disks.
105198937Sdes		 *
105298937Sdes		 * For interleaved swap devices, the first dmmax blocks
105398937Sdes		 * of swap space some from the first disk, the next dmmax
105498937Sdes		 * blocks from the next, and so on up to nswap blocks.
105598937Sdes		 *
1056147005Sdes		 * The list of free space joins adjacent free blocks,
105798937Sdes		 * ignoring device boundries.  If we want to keep track
105898937Sdes		 * of this information per device, we'll just have to
105998937Sdes		 * extract it ourselves.
106098937Sdes		 */
106198937Sdes		while (top / dmmax != bottom / dmmax) {
106298937Sdes			next_block = ((bottom + dmmax) / dmmax);
106398937Sdes			perdev[(bottom / dmmax) % nswdev] +=
1064147005Sdes				next_block * dmmax - bottom;
106598937Sdes			bottom = next_block * dmmax;
1066147005Sdes		}
106798937Sdes		perdev[(bottom / dmmax) % nswdev] +=
1068147005Sdes			top - bottom + 1;
1069147005Sdes
107098937Sdes#if  __FreeBSD_version >= 220000
107198937Sdes		swapptr = head.rl_next;
107298937Sdes#else
107398937Sdes		swaplist = head.rl_next;
107498937Sdes#endif
107598937Sdes	}
107698937Sdes
107798937Sdes	header = getbsize(&hlen, &blocksize);
107898937Sdes	div = blocksize / 512;
107998937Sdes	avail = npfree = 0;
108098937Sdes	for (i = 0; i < nswdev; i++) {
1081149753Sdes		int xsize, xfree;
1082147005Sdes
1083147005Sdes		/*
1084147005Sdes		 * Don't report statistics for partitions which have not
108598937Sdes		 * yet been activated via swapon(8).
108698937Sdes		 */
108798937Sdes		if (!(sw[i].sw_flags & SW_FREED))
108898937Sdes			continue;
108998937Sdes
109098937Sdes		/* The first dmmax is never allocated to avoid trashing of
109198937Sdes		 * disklabels
1092147005Sdes		 */
109398937Sdes		xsize = sw[i].sw_nblks - dmmax;
1094147005Sdes		xfree = perdev[i];
109598937Sdes		used = xsize - xfree;
109698937Sdes		npfree++;
1097124211Sdes		avail += xsize;
109898937Sdes	}
1099147005Sdes
110098937Sdes	/*
110198937Sdes	 * If only one partition has been set up via swapon(8), we don't
110298937Sdes	 * need to bother with totals.
1103147005Sdes	 */
1104147005Sdes	*retavail = avail / 2;
110598937Sdes	*retfree = nfree / 2;
110698937Sdes	used = avail - nfree;
110798937Sdes	free(sw); free(perdev);
110898937Sdes
110998937Sdes	/* increase counter, no errors occurs */
111098937Sdes	warning++;
111198937Sdes
111298937Sdes	return  (int)(((double)used / (double)avail * 100.0) + 0.5);
1113147005Sdes}
111498937Sdes