machine.c revision 1.26
1/*	$OpenBSD: machine.c,v 1.26 2001/12/05 02:29:19 art Exp $	*/
2
3/*
4 * top - a top users display for Unix
5 *
6 * SYNOPSIS:  For an OpenBSD system
7 *
8 * DESCRIPTION:
9 * This is the machine-dependent module for OpenBSD
10 * Tested on:
11 *	i386
12 *
13 * TERMCAP: -ltermlib
14 *
15 * CFLAGS: -DHAVE_GETOPT -DORDER
16 *
17 * AUTHOR:  Thorsten Lockert <tholo@sigmasoft.com>
18 *          Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu>
19 *          Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no>
20 *	    Patch for -DORDER by Kenneth Stailey <kstailey@disclosure.com>
21 *	    Patch for new swapctl(2) by Tobias Weingartner <weingart@openbsd.org>
22 */
23
24#include <sys/types.h>
25#include <sys/signal.h>
26#include <sys/param.h>
27
28#define DOSWAP
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <limits.h>
34#include <err.h>
35#include <math.h>
36#include <unistd.h>
37#include <sys/errno.h>
38#include <sys/sysctl.h>
39#include <sys/dir.h>
40#include <sys/dkstat.h>
41#include <sys/file.h>
42#include <sys/time.h>
43#include <sys/resource.h>
44
45#ifdef DOSWAP
46#include <sys/swap.h>
47#include <err.h>
48#endif
49
50static int swapmode __P((int *, int *));
51
52#include "top.h"
53#include "display.h"
54#include "machine.h"
55#include "utils.h"
56
57/* get_process_info passes back a handle.  This is what it looks like: */
58
59struct handle {
60	struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
61	int     remaining;	/* number of pointers remaining */
62};
63
64/* declarations for load_avg */
65#include "loadavg.h"
66
67#define PP(pp, field) ((pp)->kp_proc . field)
68#define EP(pp, field) ((pp)->kp_eproc . field)
69#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
70
71/* what we consider to be process size: */
72#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
73
74/*
75 *  These definitions control the format of the per-process area
76 */
77static char header[] =
78"  PID X        PRI NICE  SIZE   RES STATE WAIT     TIME    CPU COMMAND";
79/* 0123456   -- field to fill in starts at header+6 */
80#define UNAME_START 6
81
82#define Proc_format \
83	"%5d %-8.8s %3d %4d %5s %5s %-5s %-6.6s %6s %5.2f%% %.14s"
84
85
86/* process state names for the "STATE" column of the display */
87/* the extra nulls in the string "run" are for adding a slash and
88   the processor number when needed */
89
90char *state_abbrev[] = {
91	"", "start", "run\0\0\0", "sleep", "stop", "zomb",
92};
93
94
95static int stathz;
96
97/* these are for calculating cpu state percentages */
98static long cp_time[CPUSTATES];
99static long cp_old[CPUSTATES];
100static long cp_diff[CPUSTATES];
101
102/* these are for detailing the process states */
103int     process_states[7];
104char   *procstatenames[] = {
105	"", " starting, ", " running, ", " idle, ", " stopped, ", " zombie, ",
106	NULL
107};
108
109/* these are for detailing the cpu states */
110int     cpu_states[CPUSTATES];
111char   *cpustatenames[] = {
112	"user", "nice", "system", "interrupt", "idle", NULL
113};
114
115/* these are for detailing the memory statistics */
116int     memory_stats[8];
117char   *memorynames[] = {
118	"Real: ", "K/", "K act/tot  ", "Free: ", "K  ",
119#ifdef DOSWAP
120	"Swap: ", "K/", "K used/tot",
121#endif
122	NULL
123};
124
125#ifdef ORDER
126/* these are names given to allowed sorting orders -- first is default */
127char   *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL};
128#endif
129
130/* these are for keeping track of the proc array */
131static int nproc;
132static int onproc = -1;
133static int pref_len;
134static struct kinfo_proc *pbase;
135static struct kinfo_proc **pref;
136
137/* these are for getting the memory statistics */
138static int pageshift;		/* log base 2 of the pagesize */
139
140/* define pagetok in terms of pageshift */
141#define pagetok(size) ((size) << pageshift)
142
143int maxslp;
144
145int
146getstathz()
147{
148	struct clockinfo cinf;
149	size_t  size = sizeof(cinf);
150	int     mib[2];
151
152	mib[0] = CTL_KERN;
153	mib[1] = KERN_CLOCKRATE;
154	if (sysctl(mib, 2, &cinf, &size, NULL, 0) == -1)
155		return (-1);
156	return (cinf.stathz);
157}
158
159int
160machine_init(statics)
161	struct statics *statics;
162{
163	int pagesize;
164
165	stathz = getstathz();
166	if (stathz == -1)
167		return (-1);
168
169	pbase = NULL;
170	pref = NULL;
171	onproc = -1;
172	nproc = 0;
173
174	/* get the page size with "getpagesize" and calculate pageshift from
175	 * it */
176	pagesize = getpagesize();
177	pageshift = 0;
178	while (pagesize > 1) {
179		pageshift++;
180		pagesize >>= 1;
181	}
182
183	/* we only need the amount of log(2)1024 for our conversion */
184	pageshift -= LOG1024;
185
186	/* fill in the statics information */
187	statics->procstate_names = procstatenames;
188	statics->cpustate_names = cpustatenames;
189	statics->memory_names = memorynames;
190#ifdef ORDER
191	statics->order_names = ordernames;
192#endif
193	return (0);
194}
195
196char *
197format_header(uname_field)
198	char   *uname_field;
199{
200	char *ptr;
201
202	ptr = header + UNAME_START;
203	while (*uname_field != '\0') {
204		*ptr++ = *uname_field++;
205	}
206	return (header);
207}
208
209void
210get_system_info(si)
211	struct system_info *si;
212{
213	static int sysload_mib[] = {CTL_VM, VM_LOADAVG};
214	static int vmtotal_mib[] = {CTL_VM, VM_METER};
215	static int cp_time_mib[] = { CTL_KERN, KERN_CPTIME };
216	struct loadavg sysload;
217	struct vmtotal vmtotal;
218	double *infoloadp;
219	int total, i;
220	size_t  size;
221
222	size = sizeof(cp_time);
223	if (sysctl(cp_time_mib, 2, &cp_time, &size, NULL, 0) < 0) {
224		warn("sysctl kern.cp_time failed");
225		total = 0;
226	}
227
228	size = sizeof(sysload);
229	if (sysctl(sysload_mib, 2, &sysload, &size, NULL, 0) < 0) {
230		warn("sysctl failed");
231		total = 0;
232	}
233	infoloadp = si->load_avg;
234	for (i = 0; i < 3; i++)
235		*infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
236
237	/* convert cp_time counts to percentages */
238	total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
239
240	/* get total -- systemwide main memory usage structure */
241	size = sizeof(vmtotal);
242	if (sysctl(vmtotal_mib, 2, &vmtotal, &size, NULL, 0) < 0) {
243		warn("sysctl failed");
244		bzero(&vmtotal, sizeof(vmtotal));
245	}
246	/* convert memory stats to Kbytes */
247	memory_stats[0] = -1;
248	memory_stats[1] = pagetok(vmtotal.t_arm);
249	memory_stats[2] = pagetok(vmtotal.t_rm);
250	memory_stats[3] = -1;
251	memory_stats[4] = pagetok(vmtotal.t_free);
252	memory_stats[5] = -1;
253#ifdef DOSWAP
254	if (!swapmode(&memory_stats[6], &memory_stats[7])) {
255		memory_stats[6] = 0;
256		memory_stats[7] = 0;
257	}
258#endif
259
260	/* set arrays and strings */
261	si->cpustates = cpu_states;
262	si->memory = memory_stats;
263	si->last_pid = -1;
264}
265
266static struct handle handle;
267
268struct kinfo_proc *
269getprocs(op, arg, cnt)
270	int op, arg;
271	int *cnt;
272{
273	size_t size = sizeof(int);
274	int mib[4] = {CTL_KERN, KERN_PROC, op, arg};
275	int smib[2] = {CTL_KERN, KERN_NPROCS};
276	static int maxslp_mib[] = {CTL_VM, VM_MAXSLP};
277	static struct kinfo_proc *procbase;
278	int st;
279
280	size = sizeof(maxslp);
281	if (sysctl(maxslp_mib, 2, &maxslp, &size, NULL, 0) < 0) {
282		warn("sysctl vm.maxslp failed");
283		return (0);
284	}
285
286	st = sysctl(smib, 2, cnt, &size, NULL, 0);
287	if (st == -1) {
288		/* _kvm_syserr(kd, kd->program, "kvm_getprocs"); */
289		return (0);
290	}
291	if (procbase)
292		free(procbase);
293	size = (6 * (*cnt) * sizeof(struct kinfo_proc)) / 5;
294	procbase = (struct kinfo_proc *)malloc(size);
295	if (procbase == NULL)
296		return (0);
297	st = sysctl(mib, 4, procbase, &size, NULL, 0);
298	if (st == -1) {
299		/* _kvm_syserr(kd, kd->program, "kvm_getprocs"); */
300		return (0);
301	}
302	if (size % sizeof(struct kinfo_proc) != 0) {
303		/* _kvm_err(kd, kd->program,
304		    "proc size mismatch (%d total, %d chunks)",
305		    size, sizeof(struct kinfo_proc)); */
306		return (0);
307	}
308	return (procbase);
309}
310
311caddr_t
312get_process_info(si, sel, compare)
313	struct system_info *si;
314	struct process_select *sel;
315	int (*compare) __P((const void *, const void *));
316
317{
318	int show_idle, show_system, show_uid, show_command;
319	int total_procs, active_procs, i;
320	struct kinfo_proc **prefp, *pp;
321
322	if ((pbase = getprocs(KERN_PROC_KTHREAD, 0, &nproc)) == NULL) {
323		/* warnx("%s", kvm_geterr(kd)); */
324		quit(23);
325	}
326	if (nproc > onproc)
327		pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
328		    * (onproc = nproc));
329	if (pref == NULL) {
330		warnx("Out of memory.");
331		quit(23);
332	}
333	/* get a pointer to the states summary array */
334	si->procstates = process_states;
335
336	/* set up flags which define what we are going to select */
337	show_idle = sel->idle;
338	show_system = sel->system;
339	show_uid = sel->uid != -1;
340	show_command = sel->command != NULL;
341
342	/* count up process states and get pointers to interesting procs */
343	total_procs = 0;
344	active_procs = 0;
345	memset((char *) process_states, 0, sizeof(process_states));
346	prefp = pref;
347	for (pp = pbase, i = 0; i < nproc; pp++, i++) {
348		/*
349		 *  Place pointers to each valid proc structure in pref[].
350		 *  Process slots that are actually in use have a non-zero
351		 *  status field.  Processes with SSYS set are system
352		 *  processes---these get ignored unless show_sysprocs is set.
353		 */
354		if (PP(pp, p_stat) != 0 &&
355		    (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0))) {
356			total_procs++;
357			process_states[(unsigned char) PP(pp, p_stat)]++;
358			if ((PP(pp, p_stat) != SZOMB) &&
359			    (show_idle || (PP(pp, p_pctcpu) != 0) ||
360				(PP(pp, p_stat) == SRUN)) &&
361			    (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t) sel->uid)) {
362				*prefp++ = pp;
363				active_procs++;
364			}
365		}
366	}
367
368	/* if requested, sort the "interesting" processes */
369	if (compare != NULL) {
370		qsort((char *) pref, active_procs, sizeof(struct kinfo_proc *), compare);
371	}
372	/* remember active and total counts */
373	si->p_total = total_procs;
374	si->p_active = pref_len = active_procs;
375
376	/* pass back a handle */
377	handle.next_proc = pref;
378	handle.remaining = active_procs;
379	return ((caddr_t) & handle);
380}
381
382char    fmt[MAX_COLS];		/* static area where result is built */
383
384char *
385format_next_process(handle, get_userid)
386	caddr_t handle;
387	char *(*get_userid)();
388
389{
390	char waddr[sizeof(void *) * 2 + 3];	/* Hexify void pointer */
391	struct kinfo_proc *pp;
392	struct handle *hp;
393	char *p_wait;
394	int cputime;
395	double pct;
396
397	/* find and remember the next proc structure */
398	hp = (struct handle *) handle;
399	pp = *(hp->next_proc++);
400	hp->remaining--;
401
402	/* get the process's user struct and set cputime */
403	if ((PP(pp, p_flag) & P_INMEM) == 0) {
404		/*
405		 * Print swapped processes as <pname>
406		 */
407		char   *comm = PP(pp, p_comm);
408#define COMSIZ sizeof(PP(pp, p_comm))
409		char    buf[COMSIZ];
410		(void) strncpy(buf, comm, COMSIZ);
411		comm[0] = '<';
412		(void) strncpy(&comm[1], buf, COMSIZ - 2);
413		comm[COMSIZ - 2] = '\0';
414		(void) strncat(comm, ">", COMSIZ - 1);
415		comm[COMSIZ - 1] = '\0';
416	}
417	cputime = (PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks)) / stathz;
418
419	/* calculate the base for cpu percentages */
420	pct = pctdouble(PP(pp, p_pctcpu));
421
422	if (PP(pp, p_wchan))
423		if (PP(pp, p_wmesg))
424			p_wait = EP(pp, e_wmesg);
425		else {
426			snprintf(waddr, sizeof(waddr), "%lx",
427			    (unsigned long) (PP(pp, p_wchan)) & ~KERNBASE);
428			p_wait = waddr;
429		}
430	else
431		p_wait = "-";
432
433	/* format this entry */
434	snprintf(fmt, MAX_COLS,
435	    Proc_format,
436	    PP(pp, p_pid),
437	    (*get_userid) (EP(pp, e_pcred.p_ruid)),
438	    PP(pp, p_priority) - PZERO,
439	    PP(pp, p_nice) - NZERO,
440	    format_k(pagetok(PROCSIZE(pp))),
441	    format_k(pagetok(VP(pp, vm_rssize))),
442	    (PP(pp, p_stat) == SSLEEP && PP(pp, p_slptime) > maxslp)
443	    ? "idle" : state_abbrev[(unsigned char) PP(pp, p_stat)],
444	    p_wait,
445	    format_time(cputime),
446	    100.0 * pct,
447	    printable(PP(pp, p_comm)));
448
449	/* return the result */
450	return (fmt);
451}
452
453/* comparison routine for qsort */
454static unsigned char sorted_state[] =
455{
456	0,			/* not used		 */
457	4,			/* start		 */
458	5,			/* run			 */
459	2,			/* sleep		 */
460	3,			/* stop			 */
461	1			/* zombie		 */
462};
463#ifdef ORDER
464
465/*
466 *  proc_compares - comparison functions for "qsort"
467 */
468
469/*
470 * First, the possible comparison keys.  These are defined in such a way
471 * that they can be merely listed in the source code to define the actual
472 * desired ordering.
473 */
474
475
476#define ORDERKEY_PCTCPU \
477	if (lresult = (pctcpu)PP(p2, p_pctcpu) - (pctcpu)PP(p1, p_pctcpu), \
478	    (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
479#define ORDERKEY_CPUTIME \
480	if ((result = PP(p2, p_rtime.tv_sec) - PP(p1, p_rtime.tv_sec)) == 0) \
481		if ((result = PP(p2, p_rtime.tv_usec) - \
482		     PP(p1, p_rtime.tv_usec)) == 0)
483#define ORDERKEY_STATE \
484	if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - \
485	    sorted_state[(unsigned char) PP(p1, p_stat)])  == 0)
486#define ORDERKEY_PRIO \
487	if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
488#define ORDERKEY_RSSIZE \
489	if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
490#define ORDERKEY_MEM \
491	if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0)
492
493
494/* compare_cpu - the comparison function for sorting by cpu percentage */
495int
496compare_cpu(v1, v2)
497	const void *v1, *v2;
498{
499	struct proc **pp1 = (struct proc **) v1;
500	struct proc **pp2 = (struct proc **) v2;
501	struct kinfo_proc *p1;
502	struct kinfo_proc *p2;
503	int result;
504	pctcpu lresult;
505
506	/* remove one level of indirection */
507	p1 = *(struct kinfo_proc **) pp1;
508	p2 = *(struct kinfo_proc **) pp2;
509
510	ORDERKEY_PCTCPU
511	    ORDERKEY_CPUTIME
512	    ORDERKEY_STATE
513	    ORDERKEY_PRIO
514	    ORDERKEY_RSSIZE
515	    ORDERKEY_MEM
516	    ;
517	return (result);
518}
519
520/* compare_size - the comparison function for sorting by total memory usage */
521int
522compare_size(v1, v2)
523	const void *v1, *v2;
524{
525	struct proc **pp1 = (struct proc **) v1;
526	struct proc **pp2 = (struct proc **) v2;
527	struct kinfo_proc *p1;
528	struct kinfo_proc *p2;
529	int result;
530	pctcpu lresult;
531
532	/* remove one level of indirection */
533	p1 = *(struct kinfo_proc **) pp1;
534	p2 = *(struct kinfo_proc **) pp2;
535
536	ORDERKEY_MEM
537	    ORDERKEY_RSSIZE
538	    ORDERKEY_PCTCPU
539	    ORDERKEY_CPUTIME
540	    ORDERKEY_STATE
541	    ORDERKEY_PRIO
542	    ;
543	return (result);
544}
545
546/* compare_res - the comparison function for sorting by resident set size */
547int
548compare_res(v1, v2)
549	const void *v1, *v2;
550{
551	struct proc **pp1 = (struct proc **) v1;
552	struct proc **pp2 = (struct proc **) v2;
553	struct kinfo_proc *p1;
554	struct kinfo_proc *p2;
555	int result;
556	pctcpu lresult;
557
558	/* remove one level of indirection */
559	p1 = *(struct kinfo_proc **) pp1;
560	p2 = *(struct kinfo_proc **) pp2;
561
562	ORDERKEY_RSSIZE
563	    ORDERKEY_MEM
564	    ORDERKEY_PCTCPU
565	    ORDERKEY_CPUTIME
566	    ORDERKEY_STATE
567	    ORDERKEY_PRIO
568	    ;
569	return (result);
570}
571
572/* compare_time - the comparison function for sorting by CPU time */
573int
574compare_time(v1, v2)
575	const void *v1, *v2;
576{
577	struct proc **pp1 = (struct proc **) v1;
578	struct proc **pp2 = (struct proc **) v2;
579	struct kinfo_proc *p1;
580	struct kinfo_proc *p2;
581	int result;
582	pctcpu lresult;
583
584	/* remove one level of indirection */
585	p1 = *(struct kinfo_proc **) pp1;
586	p2 = *(struct kinfo_proc **) pp2;
587
588	ORDERKEY_CPUTIME
589	    ORDERKEY_PCTCPU
590	    ORDERKEY_STATE
591	    ORDERKEY_PRIO
592	    ORDERKEY_MEM
593	    ORDERKEY_RSSIZE
594	    ;
595	return (result);
596}
597
598/* compare_prio - the comparison function for sorting by CPU time */
599int
600compare_prio(v1, v2)
601	const void *v1, *v2;
602{
603	struct proc **pp1 = (struct proc **) v1;
604	struct proc **pp2 = (struct proc **) v2;
605	struct kinfo_proc *p1;
606	struct kinfo_proc *p2;
607	int result;
608	pctcpu lresult;
609
610	/* remove one level of indirection */
611	p1 = *(struct kinfo_proc **) pp1;
612	p2 = *(struct kinfo_proc **) pp2;
613
614	ORDERKEY_PRIO
615	    ORDERKEY_PCTCPU
616	    ORDERKEY_CPUTIME
617	    ORDERKEY_STATE
618	    ORDERKEY_RSSIZE
619	    ORDERKEY_MEM
620	    ;
621	return (result);
622}
623
624int     (*proc_compares[]) () = {
625	compare_cpu,
626	compare_size,
627	compare_res,
628	compare_time,
629	compare_prio,
630	NULL
631};
632#else
633/*
634 *  proc_compare - comparison function for "qsort"
635 *	Compares the resource consumption of two processes using five
636 *  	distinct keys.  The keys (in descending order of importance) are:
637 *  	percent cpu, cpu ticks, state, resident set size, total virtual
638 *  	memory usage.  The process states are ordered as follows (from least
639 *  	to most important):  zombie, sleep, stop, start, run.  The array
640 *  	declaration below maps a process state index into a number that
641 *  	reflects this ordering.
642 */
643int
644proc_compare(v1, v2)
645	const void *v1, *v2;
646{
647	struct proc **pp1 = (struct proc **) v1;
648	struct proc **pp2 = (struct proc **) v2;
649	struct kinfo_proc *p1;
650	struct kinfo_proc *p2;
651	int result;
652	pctcpu lresult;
653
654	/* remove one level of indirection */
655	p1 = *(struct kinfo_proc **) pp1;
656	p2 = *(struct kinfo_proc **) pp2;
657
658	/* compare percent cpu (pctcpu) */
659	if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0) {
660		/* use CPU usage to break the tie */
661		if ((result = PP(p2, p_rtime).tv_sec - PP(p1, p_rtime).tv_sec) == 0) {
662			/* use process state to break the tie */
663			if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] -
664				sorted_state[(unsigned char) PP(p1, p_stat)]) == 0) {
665				/* use priority to break the tie */
666				if ((result = PP(p2, p_priority) -
667				    PP(p1, p_priority)) == 0) {
668					/* use resident set size (rssize) to
669					 * break the tie */
670					if ((result = VP(p2, vm_rssize) -
671					    VP(p1, vm_rssize)) == 0) {
672						/* use total memory to break
673						 * the tie */
674						result = PROCSIZE(p2) - PROCSIZE(p1);
675					}
676				}
677			}
678		}
679	} else {
680		result = lresult < 0 ? -1 : 1;
681	}
682	return (result);
683}
684#endif
685
686/*
687 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
688 *		the process does not exist.
689 *		It is EXTREMLY IMPORTANT that this function work correctly.
690 *		If top runs setuid root (as in SVR4), then this function
691 *		is the only thing that stands in the way of a serious
692 *		security problem.  It validates requests for the "kill"
693 *		and "renice" commands.
694 */
695int
696proc_owner(pid)
697	pid_t   pid;
698{
699	struct kinfo_proc **prefp, *pp;
700	int cnt;
701
702	prefp = pref;
703	cnt = pref_len;
704	while (--cnt >= 0) {
705		pp = *prefp++;
706		if (PP(pp, p_pid) == pid) {
707			return ((int) EP(pp, e_pcred.p_ruid));
708		}
709	}
710	return (-1);
711}
712#ifdef DOSWAP
713/*
714 * swapmode is rewritten by Tobias Weingartner <weingart@openbsd.org>
715 * to be based on the new swapctl(2) system call.
716 */
717static int
718swapmode(used, total)
719	int    *used;
720	int    *total;
721{
722	int     nswap, rnswap, i;
723	struct swapent *swdev;
724
725	nswap = swapctl(SWAP_NSWAP, 0, 0);
726	if (nswap == 0)
727		return 0;
728
729	swdev = malloc(nswap * sizeof(*swdev));
730	if (swdev == NULL)
731		return 0;
732
733	rnswap = swapctl(SWAP_STATS, swdev, nswap);
734	if (rnswap == -1)
735		return 0;
736
737	/* if rnswap != nswap, then what? */
738
739	/* Total things up */
740	*total = *used = 0;
741	for (i = 0; i < nswap; i++) {
742		if (swdev[i].se_flags & SWF_ENABLE) {
743			*used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
744			*total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
745		}
746	}
747
748	free(swdev);
749	return 1;
750}
751#endif
752