machine.c revision 24143
1/*
2 * top - a top users display for Unix
3 *
4 * SYNOPSIS:  For FreeBSD-2.x system
5 *
6 * DESCRIPTION:
7 * Originally written for BSD4.4 system by Christos Zoulas.
8 * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider
9 *
10 * This is the machine-dependent module for FreeBSD 2.2
11 * Works for:
12 *	FreeBSD 2.2, and probably FreeBSD 2.1.x
13 *
14 * LIBS: -lkvm
15 *
16 * AUTHOR:  Christos Zoulas <christos@ee.cornell.edu>
17 *          Steven Wallace  <swallace@freebsd.org>
18 *          Wolfram Schneider <wosch@FreeBSD.org>
19 *
20 * $Id: m_freebsd2.c,v 1.15 1997/02/21 08:47:59 asami Exp $
21 */
22
23
24#include <sys/types.h>
25#include <sys/signal.h>
26#include <sys/param.h>
27
28#include "os.h"
29#include <stdio.h>
30#include <nlist.h>
31#include <math.h>
32#include <kvm.h>
33#include <sys/errno.h>
34#include <sys/sysctl.h>
35#include <sys/dkstat.h>
36#include <sys/file.h>
37#include <sys/time.h>
38#include <sys/proc.h>
39#include <sys/user.h>
40#include <sys/vmmeter.h>
41
42/* Swap */
43#include <stdlib.h>
44#include <sys/rlist.h>
45#include <sys/conf.h>
46
47#include <osreldate.h> /* for changes in kernel structures */
48
49#include "top.h"
50#include "machine.h"
51
52static int check_nlist __P((struct nlist *));
53static int getkval __P((unsigned long, int *, int, char *));
54extern char* printable __P((char *));
55int swapmode __P((int *retavail, int *retfree));
56
57
58
59/* get_process_info passes back a handle.  This is what it looks like: */
60
61struct handle
62{
63    struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
64    int remaining;		/* number of pointers remaining */
65};
66
67/* declarations for load_avg */
68#include "loadavg.h"
69
70#define PP(pp, field) ((pp)->kp_proc . field)
71#define EP(pp, field) ((pp)->kp_eproc . field)
72#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
73
74/* define what weighted cpu is.  */
75#define weighted_cpu(pct, pp) (PP((pp), p_swtime) == 0 ? 0.0 : \
76			 ((pct) / (1.0 - exp(PP((pp), p_swtime) * logcpu))))
77
78/* what we consider to be process size: */
79#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
80
81/* definitions for indices in the nlist array */
82
83
84static struct nlist nlst[] = {
85#define X_CCPU		0
86    { "_ccpu" },		/* 0 */
87#define X_CP_TIME	1
88    { "_cp_time" },		/* 1 */
89#define X_HZ		2
90    { "_hz" },		        /* 2 */
91#define X_STATHZ	3
92    { "_stathz" },		/* 3 */
93#define X_AVENRUN	4
94    { "_averunnable" },		/* 4 */
95
96/* Swap */
97#define VM_SWAPLIST	5
98	{ "_swaplist" },/* list of free swap areas */
99#define VM_SWDEVT	6
100	{ "_swdevt" },	/* list of swap devices and sizes */
101#define VM_NSWAP	7
102	{ "_nswap" },	/* size of largest swap device */
103#define VM_NSWDEV	8
104	{ "_nswdev" },	/* number of swap devices */
105#define VM_DMMAX	9
106	{ "_dmmax" },	/* maximum size of a swap block */
107#define X_BUFSPACE	10
108	{ "_bufspace" },	/* K in buffer cache */
109#define X_CNT           11
110    { "_cnt" },		        /* struct vmmeter cnt */
111
112/* Last pid */
113#define X_LASTPID	12
114    { "_nextpid" },
115    { 0 }
116};
117
118/*
119 *  These definitions control the format of the per-process area
120 */
121
122#ifdef P_IDLEPROC	/* FreeBSD SMP kernel */
123
124static char header[] =
125  "  PID X        PRI NICE SIZE    RES STATE C   TIME   WCPU    CPU COMMAND";
126/* 0123456   -- field to fill in starts at header+6 */
127#define UNAME_START 6
128
129#define Proc_format \
130	"%5d %-8.8s%3d%3d%7s %6s %-6.6s%1x%7s %5.2f%% %5.2f%% %.14s"
131
132#else			/* Standard kernel */
133
134static char header[] =
135  "  PID X        PRI NICE SIZE    RES STATE    TIME   WCPU    CPU COMMAND";
136/* 0123456   -- field to fill in starts at header+6 */
137#define UNAME_START 6
138
139#define Proc_format \
140	"%5d %-8.8s%3d %3d%7s %6s %-6.6s%7s %5.2f%% %5.2f%% %.14s"
141
142#endif
143
144
145/* process state names for the "STATE" column of the display */
146/* the extra nulls in the string "run" are for adding a slash and
147   the processor number when needed */
148
149char *state_abbrev[] =
150{
151    "", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB",
152};
153
154
155static kvm_t *kd;
156
157/* values that we stash away in _init and use in later routines */
158
159static double logcpu;
160
161/* these are retrieved from the kernel in _init */
162
163static          long hz;
164static load_avg  ccpu;
165
166/* these are offsets obtained via nlist and used in the get_ functions */
167
168static unsigned long cp_time_offset;
169static unsigned long avenrun_offset;
170static unsigned long lastpid_offset;
171static long lastpid;
172static unsigned long cnt_offset;
173static unsigned long bufspace_offset;
174static long cnt;
175
176/* these are for calculating cpu state percentages */
177
178static long cp_time[CPUSTATES];
179static long cp_old[CPUSTATES];
180static long cp_diff[CPUSTATES];
181
182/* these are for detailing the process states */
183
184int process_states[6];
185char *procstatenames[] = {
186    "", " starting, ", " running, ", " sleeping, ", " stopped, ",
187    " zombie, ",
188    NULL
189};
190
191/* these are for detailing the cpu states */
192
193int cpu_states[CPUSTATES];
194char *cpustatenames[] = {
195    "user", "nice", "system", "interrupt", "idle", NULL
196};
197
198/* these are for detailing the memory statistics */
199
200int memory_stats[7];
201char *memorynames[] = {
202    "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free",
203    NULL
204};
205
206int swap_stats[7];
207char *swapnames[] = {
208/*   0           1            2           3            4       5 */
209    "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out",
210    NULL
211};
212
213
214/* these are for keeping track of the proc array */
215
216static int nproc;
217static int onproc = -1;
218static int pref_len;
219static struct kinfo_proc *pbase;
220static struct kinfo_proc **pref;
221
222/* these are for getting the memory statistics */
223
224static int pageshift;		/* log base 2 of the pagesize */
225
226/* define pagetok in terms of pageshift */
227
228#define pagetok(size) ((size) << pageshift)
229
230/* useful externals */
231long percentages();
232
233int
234machine_init(statics)
235
236struct statics *statics;
237
238{
239    register int i = 0;
240    register int pagesize;
241
242    if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
243	return -1;
244
245
246    /* get the list of symbols we want to access in the kernel */
247    (void) kvm_nlist(kd, nlst);
248    if (nlst[0].n_type == 0)
249    {
250	fprintf(stderr, "top: nlist failed\n");
251	return(-1);
252    }
253
254    /* make sure they were all found */
255    if (i > 0 && check_nlist(nlst) > 0)
256    {
257	return(-1);
258    }
259
260    /* get the symbol values out of kmem */
261    (void) getkval(nlst[X_STATHZ].n_value, (int *)(&hz), sizeof(hz), "!");
262    if (!hz) {
263	(void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz),
264		       nlst[X_HZ].n_name);
265    }
266
267    (void) getkval(nlst[X_CCPU].n_value,   (int *)(&ccpu),	sizeof(ccpu),
268	    nlst[X_CCPU].n_name);
269
270    /* stash away certain offsets for later use */
271    cp_time_offset = nlst[X_CP_TIME].n_value;
272    avenrun_offset = nlst[X_AVENRUN].n_value;
273    lastpid_offset =  nlst[X_LASTPID].n_value;
274    cnt_offset = nlst[X_CNT].n_value;
275    bufspace_offset = nlst[X_BUFSPACE].n_value;
276
277    /* this is used in calculating WCPU -- calculate it ahead of time */
278    logcpu = log(loaddouble(ccpu));
279
280    pbase = NULL;
281    pref = NULL;
282    nproc = 0;
283    onproc = -1;
284    /* get the page size with "getpagesize" and calculate pageshift from it */
285    pagesize = getpagesize();
286    pageshift = 0;
287    while (pagesize > 1)
288    {
289	pageshift++;
290	pagesize >>= 1;
291    }
292
293    /* we only need the amount of log(2)1024 for our conversion */
294    pageshift -= LOG1024;
295
296    /* fill in the statics information */
297    statics->procstate_names = procstatenames;
298    statics->cpustate_names = cpustatenames;
299    statics->memory_names = memorynames;
300    statics->swap_names = swapnames;
301
302    /* all done! */
303    return(0);
304}
305
306char *format_header(uname_field)
307
308register char *uname_field;
309
310{
311    register char *ptr;
312
313    ptr = header + UNAME_START;
314    while (*uname_field != '\0')
315    {
316	*ptr++ = *uname_field++;
317    }
318
319    return(header);
320}
321
322static int swappgsin = -1;
323static int swappgsout = -1;
324extern struct timeval timeout;
325
326void
327get_system_info(si)
328
329struct system_info *si;
330
331{
332    long total;
333    load_avg avenrun[3];
334
335    /* get the cp_time array */
336    (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time),
337		   nlst[X_CP_TIME].n_name);
338    (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun),
339		   nlst[X_AVENRUN].n_name);
340
341    (void) getkval(lastpid_offset, (int *)(&lastpid), sizeof(lastpid),
342		   "!");
343
344    /* convert load averages to doubles */
345    {
346	register int i;
347	register double *infoloadp;
348	load_avg *avenrunp;
349
350#ifdef notyet
351	struct loadavg sysload;
352	int size;
353	getkerninfo(KINFO_LOADAVG, &sysload, &size, 0);
354#endif
355
356	infoloadp = si->load_avg;
357	avenrunp = avenrun;
358	for (i = 0; i < 3; i++)
359	{
360#ifdef notyet
361	    *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
362#endif
363	    *infoloadp++ = loaddouble(*avenrunp++);
364	}
365    }
366
367    /* convert cp_time counts to percentages */
368    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
369
370    /* sum memory & swap statistics */
371    {
372	struct vmmeter sum;
373	static unsigned int swap_delay = 0;
374	static int swapavail = 0;
375	static int swapfree = 0;
376	static int bufspace = 0;
377
378        (void) getkval(cnt_offset, (int *)(&sum), sizeof(sum),
379		   "_cnt");
380        (void) getkval(bufspace_offset, (int *)(&bufspace), sizeof(bufspace),
381		   "_bufspace");
382
383	/* convert memory stats to Kbytes */
384	memory_stats[0] = pagetok(sum.v_active_count);
385	memory_stats[1] = pagetok(sum.v_inactive_count);
386	memory_stats[2] = pagetok(sum.v_wire_count);
387	memory_stats[3] = pagetok(sum.v_cache_count);
388	memory_stats[4] = bufspace / 1024;
389	memory_stats[5] = pagetok(sum.v_free_count);
390	memory_stats[6] = -1;
391
392	/* first interval */
393        if (swappgsin < 0) {
394	    swap_stats[4] = 0;
395	    swap_stats[5] = 0;
396	}
397
398	/* compute differences between old and new swap statistic */
399	else {
400	    swap_stats[4] = pagetok(((sum.v_swappgsin - swappgsin)));
401	    swap_stats[5] = pagetok(((sum.v_swappgsout - swappgsout)));
402	}
403
404        swappgsin = sum.v_swappgsin;
405	swappgsout = sum.v_swappgsout;
406
407	/* call CPU heavy swapmode() only for changes */
408        if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) {
409	    swap_stats[3] = swapmode(&swapavail, &swapfree);
410	    swap_stats[0] = swapavail;
411	    swap_stats[1] = swapavail - swapfree;
412	    swap_stats[2] = swapfree;
413	}
414        swap_delay = 1;
415	swap_stats[6] = -1;
416    }
417
418    /* set arrays and strings */
419    si->cpustates = cpu_states;
420    si->memory = memory_stats;
421    si->swap = swap_stats;
422
423
424    if(lastpid > 0) {
425	si->last_pid = lastpid;
426    } else {
427	si->last_pid = -1;
428    }
429}
430
431static struct handle handle;
432
433caddr_t get_process_info(si, sel, compare)
434
435struct system_info *si;
436struct process_select *sel;
437int (*compare)();
438
439{
440    register int i;
441    register int total_procs;
442    register int active_procs;
443    register struct kinfo_proc **prefp;
444    register struct kinfo_proc *pp;
445
446    /* these are copied out of sel for speed */
447    int show_idle;
448    int show_system;
449    int show_uid;
450    int show_command;
451
452
453    pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
454    if (nproc > onproc)
455	pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
456		* (onproc = nproc));
457    if (pref == NULL || pbase == NULL) {
458	(void) fprintf(stderr, "top: Out of memory.\n");
459	quit(23);
460    }
461    /* get a pointer to the states summary array */
462    si->procstates = process_states;
463
464    /* set up flags which define what we are going to select */
465    show_idle = sel->idle;
466    show_system = sel->system;
467    show_uid = sel->uid != -1;
468    show_command = sel->command != NULL;
469
470    /* count up process states and get pointers to interesting procs */
471    total_procs = 0;
472    active_procs = 0;
473    memset((char *)process_states, 0, sizeof(process_states));
474    prefp = pref;
475    for (pp = pbase, i = 0; i < nproc; pp++, i++)
476    {
477	/*
478	 *  Place pointers to each valid proc structure in pref[].
479	 *  Process slots that are actually in use have a non-zero
480	 *  status field.  Processes with P_SYSTEM set are system
481	 *  processes---these get ignored unless show_sysprocs is set.
482	 */
483	if (PP(pp, p_stat) != 0 &&
484	    (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0)))
485	{
486	    total_procs++;
487	    process_states[(unsigned char) PP(pp, p_stat)]++;
488	    if ((PP(pp, p_stat) != SZOMB) &&
489		(show_idle || (PP(pp, p_pctcpu) != 0) ||
490		 (PP(pp, p_stat) == SRUN)) &&
491		(!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid))
492	    {
493		*prefp++ = pp;
494		active_procs++;
495	    }
496	}
497    }
498
499    /* if requested, sort the "interesting" processes */
500    if (compare != NULL)
501    {
502	qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare);
503    }
504
505    /* remember active and total counts */
506    si->p_total = total_procs;
507    si->p_active = pref_len = active_procs;
508
509    /* pass back a handle */
510    handle.next_proc = pref;
511    handle.remaining = active_procs;
512    return((caddr_t)&handle);
513}
514
515char fmt[128];		/* static area where result is built */
516
517char *format_next_process(handle, get_userid)
518
519caddr_t handle;
520char *(*get_userid)();
521
522{
523    register struct kinfo_proc *pp;
524    register long cputime;
525    register double pct;
526    struct handle *hp;
527    char status[16];
528
529    /* find and remember the next proc structure */
530    hp = (struct handle *)handle;
531    pp = *(hp->next_proc++);
532    hp->remaining--;
533
534
535    /* get the process's user struct and set cputime */
536    if ((PP(pp, p_flag) & P_INMEM) == 0) {
537	/*
538	 * Print swapped processes as <pname>
539	 */
540	char *comm = PP(pp, p_comm);
541#define COMSIZ sizeof(PP(pp, p_comm))
542	char buf[COMSIZ];
543	(void) strncpy(buf, comm, COMSIZ);
544	comm[0] = '<';
545	(void) strncpy(&comm[1], buf, COMSIZ - 2);
546	comm[COMSIZ - 2] = '\0';
547	(void) strncat(comm, ">", COMSIZ - 1);
548	comm[COMSIZ - 1] = '\0';
549    }
550
551#if 0
552    /* This does not produce the correct results */
553    cputime = PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks);
554#endif
555    cputime = PP(pp, p_rtime).tv_sec;	/* This does not count interrupts */
556
557    /* calculate the base for cpu percentages */
558    pct = pctdouble(PP(pp, p_pctcpu));
559
560    /* generate "STATE" field */
561    switch (PP(pp, p_stat)) {
562	case SRUN:
563#ifdef P_IDLEPROC	/* FreeBSD SMP kernel */
564	    if (PP(pp, p_oncpu) >= 0)
565		sprintf(status, "CPU%d", PP(pp, p_oncpu));
566	    else
567#endif
568		strcpy(status, "RUN");
569	    break;
570	case SSLEEP:
571	    if (PP(pp, p_wmesg) != NULL) {
572		sprintf(status, "%.6s", EP(pp, e_wmesg));
573		break;
574	    }
575	    /* fall through */
576	default:
577	    sprintf(status, "%.6s", state_abbrev[(unsigned char) PP(pp, p_stat)]);
578	    break;
579    }
580
581    /* format this entry */
582    sprintf(fmt,
583	    Proc_format,
584	    PP(pp, p_pid),
585	    (*get_userid)(EP(pp, e_pcred.p_ruid)),
586	    PP(pp, p_priority) - PZERO,
587	    PP(pp, p_nice) - NZERO,
588	    format_k2(pagetok(PROCSIZE(pp))),
589	    format_k2(pagetok(VP(pp, vm_rssize))),
590	    status,
591#ifdef P_IDLEPROC	/* FreeBSD SMP kernel */
592	    PP(pp, p_lastcpu),
593#endif
594	    format_time(cputime),
595	    10000.0 * weighted_cpu(pct, pp) / hz,
596	    10000.0 * pct / hz,
597	    printable(PP(pp, p_comm)));
598
599    /* return the result */
600    return(fmt);
601}
602
603
604/*
605 * check_nlist(nlst) - checks the nlist to see if any symbols were not
606 *		found.  For every symbol that was not found, a one-line
607 *		message is printed to stderr.  The routine returns the
608 *		number of symbols NOT found.
609 */
610
611static int check_nlist(nlst)
612
613register struct nlist *nlst;
614
615{
616    register int i;
617
618    /* check to see if we got ALL the symbols we requested */
619    /* this will write one line to stderr for every symbol not found */
620
621    i = 0;
622    while (nlst->n_name != NULL)
623    {
624	if (nlst->n_type == 0)
625	{
626	    /* this one wasn't found */
627	    (void) fprintf(stderr, "kernel: no symbol named `%s'\n",
628			   nlst->n_name);
629	    i = 1;
630	}
631	nlst++;
632    }
633
634    return(i);
635}
636
637
638/*
639 *  getkval(offset, ptr, size, refstr) - get a value out of the kernel.
640 *	"offset" is the byte offset into the kernel for the desired value,
641 *  	"ptr" points to a buffer into which the value is retrieved,
642 *  	"size" is the size of the buffer (and the object to retrieve),
643 *  	"refstr" is a reference string used when printing error meessages,
644 *	    if "refstr" starts with a '!', then a failure on read will not
645 *  	    be fatal (this may seem like a silly way to do things, but I
646 *  	    really didn't want the overhead of another argument).
647 *
648 */
649
650static int getkval(offset, ptr, size, refstr)
651
652unsigned long offset;
653int *ptr;
654int size;
655char *refstr;
656
657{
658    if (kvm_read(kd, offset, (char *) ptr, size) != size)
659    {
660	if (*refstr == '!')
661	{
662	    return(0);
663	}
664	else
665	{
666	    fprintf(stderr, "top: kvm_read for %s: %s\n",
667		refstr, strerror(errno));
668	    quit(23);
669	}
670    }
671    return(1);
672}
673
674/* comparison routine for qsort */
675
676/*
677 *  proc_compare - comparison function for "qsort"
678 *	Compares the resource consumption of two processes using five
679 *  	distinct keys.  The keys (in descending order of importance) are:
680 *  	percent cpu, cpu ticks, state, resident set size, total virtual
681 *  	memory usage.  The process states are ordered as follows (from least
682 *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
683 *  	array declaration below maps a process state index into a number
684 *  	that reflects this ordering.
685 */
686
687static unsigned char sorted_state[] =
688{
689    0,	/* not used		*/
690    3,	/* sleep		*/
691    1,	/* ABANDONED (WAIT)	*/
692    6,	/* run			*/
693    5,	/* start		*/
694    2,	/* zombie		*/
695    4	/* stop			*/
696};
697
698int
699proc_compare(pp1, pp2)
700
701struct proc **pp1;
702struct proc **pp2;
703
704{
705    register struct kinfo_proc *p1;
706    register struct kinfo_proc *p2;
707    register int result;
708    register pctcpu lresult;
709
710    /* remove one level of indirection */
711    p1 = *(struct kinfo_proc **) pp1;
712    p2 = *(struct kinfo_proc **) pp2;
713
714    /* compare percent cpu (pctcpu) */
715    if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0)
716    {
717	/* use cpticks to break the tie */
718	if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0)
719	{
720	    /* use process state to break the tie */
721	    if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] -
722			  sorted_state[(unsigned char) PP(p1, p_stat)])  == 0)
723	    {
724		/* use priority to break the tie */
725		if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0)
726		{
727		    /* use resident set size (rssize) to break the tie */
728		    if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0)
729		    {
730			/* use total memory to break the tie */
731			result = PROCSIZE(p2) - PROCSIZE(p1);
732		    }
733		}
734	    }
735	}
736    }
737    else
738    {
739	result = lresult < 0 ? -1 : 1;
740    }
741
742    return(result);
743}
744
745
746/*
747 * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
748 *		the process does not exist.
749 *		It is EXTREMLY IMPORTANT that this function work correctly.
750 *		If top runs setuid root (as in SVR4), then this function
751 *		is the only thing that stands in the way of a serious
752 *		security problem.  It validates requests for the "kill"
753 *		and "renice" commands.
754 */
755
756int proc_owner(pid)
757
758int pid;
759
760{
761    register int cnt;
762    register struct kinfo_proc **prefp;
763    register struct kinfo_proc *pp;
764
765    prefp = pref;
766    cnt = pref_len;
767    while (--cnt >= 0)
768    {
769	pp = *prefp++;
770	if (PP(pp, p_pid) == (pid_t)pid)
771	{
772	    return((int)EP(pp, e_pcred.p_ruid));
773	}
774    }
775    return(-1);
776}
777
778
779/*
780 * swapmode is based on a program called swapinfo written
781 * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
782 */
783
784#define	SVAR(var) __STRING(var)	/* to force expansion */
785#define	KGET(idx, var)							\
786	KGET1(idx, &var, sizeof(var), SVAR(var))
787#define	KGET1(idx, p, s, msg)						\
788	KGET2(nlst[idx].n_value, p, s, msg)
789#define	KGET2(addr, p, s, msg)						\
790	if (kvm_read(kd, (u_long)(addr), p, s) != s) {		        \
791		warnx("cannot read %s: %s", msg, kvm_geterr(kd));       \
792		return (0);                                             \
793       }
794#define	KGETRET(addr, p, s, msg)					\
795	if (kvm_read(kd, (u_long)(addr), p, s) != s) {			\
796		warnx("cannot read %s: %s", msg, kvm_geterr(kd));	\
797		return (0);						\
798	}
799
800
801int
802swapmode(retavail, retfree)
803	int *retavail;
804	int *retfree;
805{
806	char *header;
807	int hlen, nswap, nswdev, dmmax;
808	int i, div, avail, nfree, npfree, used;
809	struct swdevt *sw;
810	long blocksize, *perdev;
811	u_long ptr;
812	struct rlist head;
813#if __FreeBSD_version >= 220000
814	struct rlisthdr swaplist;
815#else
816	struct rlist *swaplist;
817#endif
818	struct rlist *swapptr;
819
820	/*
821	 * Counter for error messages. If we reach the limit,
822	 * stop reading information from swap devices and
823	 * return zero. This prevent endless 'bad address'
824	 * messages.
825	 */
826	static warning = 10;
827
828	if (warning <= 0) {
829	    /* a single warning */
830	    if (!warning) {
831		warning--;
832		fprintf(stderr,
833			"Too much errors, stop reading swap devices ...\n");
834		(void)sleep(3);
835	    }
836	    return(0);
837	}
838	warning--; /* decrease counter, see end of function */
839
840	KGET(VM_NSWAP, nswap);
841	if (!nswap) {
842		fprintf(stderr, "No swap space available\n");
843		return(0);
844	}
845
846	KGET(VM_NSWDEV, nswdev);
847	KGET(VM_DMMAX, dmmax);
848	KGET1(VM_SWAPLIST, &swaplist, sizeof(swaplist), "swaplist");
849	if ((sw = (struct swdevt *)malloc(nswdev * sizeof(*sw))) == NULL ||
850	    (perdev = (long *)malloc(nswdev * sizeof(*perdev))) == NULL)
851		err(1, "malloc");
852	KGET1(VM_SWDEVT, &ptr, sizeof ptr, "swdevt");
853	KGET2(ptr, sw, nswdev * sizeof(*sw), "*swdevt");
854
855	/* Count up swap space. */
856	nfree = 0;
857	memset(perdev, 0, nswdev * sizeof(*perdev));
858#if  __FreeBSD_version >= 220000
859	swapptr = swaplist.rlh_list;
860	while (swapptr) {
861#else
862	while (swaplist) {
863#endif
864		int	top, bottom, next_block;
865#if  __FreeBSD_version >= 220000
866		KGET2(swapptr, &head, sizeof(struct rlist), "swapptr");
867#else
868		KGET2(swaplist, &head, sizeof(struct rlist), "swaplist");
869#endif
870
871		top = head.rl_end;
872		bottom = head.rl_start;
873
874		nfree += top - bottom + 1;
875
876		/*
877		 * Swap space is split up among the configured disks.
878		 *
879		 * For interleaved swap devices, the first dmmax blocks
880		 * of swap space some from the first disk, the next dmmax
881		 * blocks from the next, and so on up to nswap blocks.
882		 *
883		 * The list of free space joins adjacent free blocks,
884		 * ignoring device boundries.  If we want to keep track
885		 * of this information per device, we'll just have to
886		 * extract it ourselves.
887		 */
888		while (top / dmmax != bottom / dmmax) {
889			next_block = ((bottom + dmmax) / dmmax);
890			perdev[(bottom / dmmax) % nswdev] +=
891				next_block * dmmax - bottom;
892			bottom = next_block * dmmax;
893		}
894		perdev[(bottom / dmmax) % nswdev] +=
895			top - bottom + 1;
896
897#if  __FreeBSD_version >= 220000
898		swapptr = head.rl_next;
899#else
900		swaplist = head.rl_next;
901#endif
902	}
903
904	header = getbsize(&hlen, &blocksize);
905	div = blocksize / 512;
906	avail = npfree = 0;
907	for (i = 0; i < nswdev; i++) {
908		int xsize, xfree;
909
910		/*
911		 * Don't report statistics for partitions which have not
912		 * yet been activated via swapon(8).
913		 */
914
915		xsize = sw[i].sw_nblks;
916		xfree = perdev[i];
917		used = xsize - xfree;
918		npfree++;
919		avail += xsize;
920	}
921
922	/*
923	 * If only one partition has been set up via swapon(8), we don't
924	 * need to bother with totals.
925	 */
926	*retavail = avail / 2;
927	*retfree = nfree / 2;
928	used = avail - nfree;
929	free(sw); free(perdev);
930
931	/* increase counter, no errors occurs */
932	warning++;
933
934	return  (int)(((double)used / (double)avail * 100.0) + 0.5);
935}
936