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