Return to BSD News archive
Path: sserve!newshost.anu.edu.au!harbinger.cc.monash.edu.au!msunews!agate!howland.reston.ans.net!Germany.EU.net!news.dfn.de!gs.dfn.de!prise.nz.dlr.de!zib-berlin.de!math.fu-berlin.de!cs.tu-berlin.de!wosch From: wosch@cs.tu-berlin.de (Wolfram Schneider) Newsgroups: comp.os.386bsd.apps Subject: top(1) machine-dependent module for FreeBSD-2.0 Date: 05 Dec 1994 15:00:10 GMT Organization: Hohenschoensiehstenich Lines: 911 Message-ID: <3bv9di$2cr@news.cs.tu-berlin.de> NNTP-Posting-Host: ole.cs.tu-berlin.de Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8bit New Features: o last pid o use the same values as vmstat -s o use swap usage (pstat -s) o SwapIO Gruß Wolfram ---------------------------------------------------------------------- #!/bin/sh # This is a shell archive (produced by shar 3.49) # To extract the files from this archive, save it to a file, remove # everything above the "!/bin/sh" line above, and type "sh file_name". # # made 12/05/1994 14:53 UTC by wosch@ole # Source directory /tmp/marflow # # existing files will NOT be overwritten unless -c is specified # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 21173 -rw-rw-r-- m_freebsd20.c # # ============= m_freebsd20.c ============== if test -f 'm_freebsd20.c' -a X"$1" != X"-c"; then echo 'x - skipping m_freebsd20.c (File already exists)' else echo 'x - extracting m_freebsd20.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'm_freebsd20.c' && /* X * top - a top users display for Unix X * X * SYNOPSIS: For a 4.4BSD system X * Note process resident sizes could be wrong, but ps shows X * zero for them too.. X * X * DESCRIPTION: X * Originally written for BSD4.4 system by Christos Zoulas. X * Ported to FreeBSD 2.0 by Steven Wallace. X * X * This is the machine-dependent module for FreeBSD 2.0 X * Works for: X * FreeBSD 2.0 X * X * LIBS: -lkvm X * X * AUTHOR: Christos Zoulas <christos@ee.cornell.edu> X * Steven Wallace <swallace@freebsd.org> X * Wolfram Schneider <wosch@cs.tu-berlin.de> X */ X X /* Changes from Wolfram Schneider <wosch@cs.tu-berlin.de> X */ X #define LASTPID /**/ /* use last pid, compiler depended */ #define VM_REAL /**/ /* use the same values as vmstat -s */ #define USE_SWAP /**/ /* use swap usage (pstat -s), X need to much cpu time */ X X #include <sys/types.h> #include <sys/signal.h> #include <sys/param.h> X #include "os.h" #include <stdio.h> #include <nlist.h> #include <math.h> #include <kvm.h> #include <sys/errno.h> #include <sys/sysctl.h> #include <sys/dir.h> #include <sys/dkstat.h> #include <sys/file.h> #include <sys/time.h> X #ifdef USE_SWAP #include <stdlib.h> #include <sys/rlist.h> #include <sys/conf.h> #endif X X static int check_nlist __P((struct nlist *)); static int getkval __P((unsigned long, int *, int, char *)); extern char* printable __P((char *)); X #include "top.h" #include "machine.h" X X /* get_process_info passes back a handle. This is what it looks like: */ X struct handle { X struct kinfo_proc **next_proc; /* points to next valid proc pointer */ X int remaining; /* number of pointers remaining */ }; X /* declarations for load_avg */ #include "loadavg.h" X #define PP(pp, field) ((pp)->kp_proc . field) #define EP(pp, field) ((pp)->kp_eproc . field) #define VP(pp, field) ((pp)->kp_eproc.e_vm . field) X /* define what weighted cpu is. */ #define weighted_cpu(pct, pp) (PP((pp), p_swtime) == 0 ? 0.0 : \ X ((pct) / (1.0 - exp(PP((pp), p_swtime) * logcpu)))) X /* what we consider to be process size: */ #define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize)) X /* definitions for indices in the nlist array */ X X static struct nlist nlst[] = { #define X_CCPU 0 X { "_ccpu" }, /* 0 */ #define X_CP_TIME 1 X { "_cp_time" }, /* 1 */ #define X_HZ 2 X { "_hz" }, /* 2 */ #define X_AVENRUN 3 X { "_averunnable" }, /* 3 */ #ifdef USE_SWAP #define VM_SWAPLIST 4 X { "_swaplist" },/* list of free swap areas */ #define VM_SWDEVT 5 X { "_swdevt" }, /* list of swap devices and sizes */ #define VM_NSWAP 6 X { "_nswap" }, /* size of largest swap device */ #define VM_NSWDEV 7 X { "_nswdev" }, /* number of swap devices */ #define VM_DMMAX 8 X { "_dmmax" }, /* maximum size of a swap block */ #endif #ifdef VM_REAL #ifdef USE_SWAP #define X_CNT 9 #else #define X_CNT 4 #endif X { "_cnt" }, /* struct vmmeter cnt */ #endif X #ifdef LASTPID #if (defined USE_SWAP && defined VM_REAL) #define X_LASTPID 10 #elif (defined VM_REAL) #define X_LASTPID 5 #else #define X_LASTPID 4 #endif X { "_nextpid.178" }, /* lastpid, compiler depended X * should be changed X * in /sys/kern/kern_fork.c */ #endif X X { 0 } }; X /* X * These definitions control the format of the per-process area X */ X static char header[] = X " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; /* 0123456 -- field to fill in starts at header+6 */ #define UNAME_START 6 X #define Proc_format \ X "%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %.14s" X X /* process state names for the "STATE" column of the display */ /* the extra nulls in the string "run" are for adding a slash and X the processor number when needed */ X char *state_abbrev[] = { X "", "start", "run\0\0\0", "sleep", "stop", "zomb", "WAIT" }; X X static kvm_t *kd; X /* values that we stash away in _init and use in later routines */ X static double logcpu; X /* these are retrieved from the kernel in _init */ X static long hz; static load_avg ccpu; X /* these are offsets obtained via nlist and used in the get_ functions */ X static unsigned long cp_time_offset; static unsigned long avenrun_offset; #ifdef LASTPID static unsigned long lastpid_offset; static long lastpid; #endif #ifdef VM_REAL static unsigned long cnt_offset; static long cnt; #endif /* these are for calculating cpu state percentages */ X static long cp_time[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; X /* these are for detailing the process states */ X int process_states[7]; char *procstatenames[] = { X "", " starting, ", " running, ", " sleeping, ", " stopped, ", X " zombie, ", " ABANDONED, ", X NULL }; X /* these are for detailing the cpu states */ X int cpu_states[CPUSTATES]; char *cpustatenames[] = { X "user", "nice", "system", "interrupt", "idle", NULL }; X /* these are for detailing the memory statistics */ X int memory_stats[8]; char *memorynames[] = { #ifndef VM_REAL X "Real: ", "K/", "K ", "Virt: ", "K/", X "K ", "Free: ", "K", NULL #else #if 0 X "K Act ", "K Inact ", "K Wired ", "K Free ", "% Swap, ", X "K/", "K SWIO", #else X "K Act ", "K Inact ", "K Wired ", "K Free ", "% Swap, ", X "K/", "K SWIO", #endif X NULL #endif }; X /* these are for keeping track of the proc array */ X static int nproc; static int onproc = -1; static int pref_len; static struct kinfo_proc *pbase; static struct kinfo_proc **pref; X /* these are for getting the memory statistics */ X static int pageshift; /* log base 2 of the pagesize */ X /* define pagetok in terms of pageshift */ X #define pagetok(size) ((size) << pageshift) X /* useful externals */ long percentages(); X int machine_init(statics) X struct statics *statics; X { X register int i = 0; X register int pagesize; X X if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL) X return -1; X X X /* get the list of symbols we want to access in the kernel */ X (void) kvm_nlist(kd, nlst); X if (nlst[0].n_type == 0) X { X fprintf(stderr, "top: nlist failed\n"); X return(-1); X } X X /* make sure they were all found */ X if (i > 0 && check_nlist(nlst) > 0) X { X return(-1); X } X X /* get the symbol values out of kmem */ X (void) getkval(nlst[X_HZ].n_value, (int *)(&hz), sizeof(hz), X nlst[X_HZ].n_name); X (void) getkval(nlst[X_CCPU].n_value, (int *)(&ccpu), sizeof(ccpu), X nlst[X_CCPU].n_name); X X /* stash away certain offsets for later use */ X cp_time_offset = nlst[X_CP_TIME].n_value; X avenrun_offset = nlst[X_AVENRUN].n_value; #ifdef LASTPID X lastpid_offset = nlst[X_LASTPID].n_value; #endif #ifdef VM_REAL X cnt_offset = nlst[X_CNT].n_value; #endif X X /* this is used in calculating WCPU -- calculate it ahead of time */ X logcpu = log(loaddouble(ccpu)); X X pbase = NULL; X pref = NULL; X nproc = 0; X onproc = -1; X /* get the page size with "getpagesize" and calculate pageshift from it */ X pagesize = getpagesize(); X pageshift = 0; X while (pagesize > 1) X { X pageshift++; X pagesize >>= 1; X } X X /* we only need the amount of log(2)1024 for our conversion */ X pageshift -= LOG1024; X X /* fill in the statics information */ X statics->procstate_names = procstatenames; X statics->cpustate_names = cpustatenames; X statics->memory_names = memorynames; X X /* all done! */ X return(0); } X char *format_header(uname_field) X register char *uname_field; X { X register char *ptr; X X ptr = header + UNAME_START; X while (*uname_field != '\0') X { X *ptr++ = *uname_field++; X } X X return(header); } X static int swappgsin = -1; static int swappgsout = -1; extern struct timeval timeout; X void get_system_info(si) X struct system_info *si; X { X long total; X load_avg avenrun[3]; X X /* get the cp_time array */ X (void) getkval(cp_time_offset, (int *)cp_time, sizeof(cp_time), X nlst[X_CP_TIME].n_name); X (void) getkval(avenrun_offset, (int *)avenrun, sizeof(avenrun), X nlst[X_AVENRUN].n_name); X #ifdef LASTPID X (void) getkval(lastpid_offset, (int *)(&lastpid), sizeof(lastpid), X "!"); #endif X X /* convert load averages to doubles */ X { X register int i; X register double *infoloadp; X load_avg *avenrunp; X #ifdef notyet X struct loadavg sysload; X int size; X getkerninfo(KINFO_LOADAVG, &sysload, &size, 0); #endif X X infoloadp = si->load_avg; X avenrunp = avenrun; X for (i = 0; i < 3; i++) X { #ifdef notyet X *infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale; #endif X *infoloadp++ = loaddouble(*avenrunp++); X } X } X X /* convert cp_time counts to percentages */ X total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); X X /* sum memory statistics */ X { #ifndef VM_REAL X struct vmtotal total; X int size = sizeof(total); X static int mib[] = { CTL_VM, VM_METER }; X X /* get total -- systemwide main memory usage structure */ X if (sysctl(mib, 2, &total, &size, NULL, 0) < 0) { X (void) fprintf(stderr, "top: sysctl failed: %s\n", strerror(errno)); X bzero(&total, sizeof(total)); X } X /* convert memory stats to Kbytes */ X memory_stats[0] = -1; X memory_stats[1] = pagetok(total.t_arm); X memory_stats[2] = pagetok(total.t_rm); X memory_stats[3] = -1; X memory_stats[4] = pagetok(total.t_avm); X memory_stats[5] = pagetok(total.t_vm); X memory_stats[6] = -1; X memory_stats[7] = pagetok(total.t_free); X } #else X struct vmmeter sum; X X (void) getkval(cnt_offset, (int *)(&sum), sizeof(sum), X "_cnt"); X X /* convert memory stats to Kbytes */ X memory_stats[0] = pagetok(sum.v_active_count); X memory_stats[1] = pagetok(sum.v_inactive_count); X memory_stats[2] = pagetok(sum.v_wire_count); X memory_stats[3] = pagetok(sum.v_free_count); #ifdef USE_SWAP X memory_stats[4] = swapmode(); #else X memory_stats[4] = 0; #endif X if (swappgsin < 0) { X memory_stats[5] = 0; X memory_stats[6] = 0; X } else { X memory_stats[5] = pagetok(((sum.v_swappgsin - swappgsin))); X memory_stats[6] = pagetok(((sum.v_swappgsout - swappgsout))); X } X swappgsin = sum.v_swappgsin; X swappgsout = sum.v_swappgsout; X X memory_stats[7] = -1; X } #endif X /* set arrays and strings */ X si->cpustates = cpu_states; X si->memory = memory_stats; #ifdef LASTPID X si->last_pid = lastpid; #else X si->last_pid = -1; #endif X } X static struct handle handle; X caddr_t get_process_info(si, sel, compare) X struct system_info *si; struct process_select *sel; int (*compare)(); X { X register int i; X register int total_procs; X register int active_procs; X register struct kinfo_proc **prefp; X register struct kinfo_proc *pp; X X /* these are copied out of sel for speed */ X int show_idle; X int show_system; X int show_uid; X int show_command; X X X pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); X if (nproc > onproc) X pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *) X * (onproc = nproc)); X if (pref == NULL || pbase == NULL) { X (void) fprintf(stderr, "top: Out of memory.\n"); X quit(23); X } X /* get a pointer to the states summary array */ X si->procstates = process_states; X X /* set up flags which define what we are going to select */ X show_idle = sel->idle; X show_system = sel->system; X show_uid = sel->uid != -1; X show_command = sel->command != NULL; X X /* count up process states and get pointers to interesting procs */ X total_procs = 0; X active_procs = 0; X memset((char *)process_states, 0, sizeof(process_states)); X prefp = pref; X for (pp = pbase, i = 0; i < nproc; pp++, i++) X { X /* X * Place pointers to each valid proc structure in pref[]. X * Process slots that are actually in use have a non-zero X * status field. Processes with P_SYSTEM set are system X * processes---these get ignored unless show_sysprocs is set. X */ X if (PP(pp, p_stat) != 0 && X (show_system || ((PP(pp, p_flag) & P_SYSTEM) == 0))) X { X total_procs++; X process_states[(unsigned char) PP(pp, p_stat)]++; X if ((PP(pp, p_stat) != SZOMB) && X (show_idle || (PP(pp, p_pctcpu) != 0) || X (PP(pp, p_stat) == SRUN)) && X (!show_uid || EP(pp, e_pcred.p_ruid) == (uid_t)sel->uid)) X { X *prefp++ = pp; X active_procs++; X } X } X } X X /* if requested, sort the "interesting" processes */ X if (compare != NULL) X { X qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), compare); X } X X /* remember active and total counts */ X si->p_total = total_procs; X si->p_active = pref_len = active_procs; X X /* pass back a handle */ X handle.next_proc = pref; X handle.remaining = active_procs; X return((caddr_t)&handle); } X char fmt[128]; /* static area where result is built */ X char *format_next_process(handle, get_userid) X caddr_t handle; char *(*get_userid)(); X { X register struct kinfo_proc *pp; X register long cputime; X register double pct; X struct handle *hp; X X /* find and remember the next proc structure */ X hp = (struct handle *)handle; X pp = *(hp->next_proc++); X hp->remaining--; X X X /* get the process's user struct and set cputime */ X if ((PP(pp, p_flag) & P_INMEM) == 0) { X /* X * Print swapped processes as <pname> X */ X char *comm = PP(pp, p_comm); #define COMSIZ sizeof(PP(pp, p_comm)) X char buf[COMSIZ]; X (void) strncpy(buf, comm, COMSIZ); X comm[0] = '<'; X (void) strncpy(&comm[1], buf, COMSIZ - 2); X comm[COMSIZ - 2] = '\0'; X (void) strncat(comm, ">", COMSIZ - 1); X comm[COMSIZ - 1] = '\0'; X } X #if 0 X /* This does not produce the correct results */ X cputime = PP(pp, p_uticks) + PP(pp, p_sticks) + PP(pp, p_iticks); #endif X cputime = PP(pp, p_rtime).tv_sec; /* This does not count interrupts */ X X /* calculate the base for cpu percentages */ X pct = pctdouble(PP(pp, p_pctcpu)); X X /* format this entry */ X sprintf(fmt, X Proc_format, X PP(pp, p_pid), X (*get_userid)(EP(pp, e_pcred.p_ruid)), X PP(pp, p_priority) - PZERO, X PP(pp, p_nice) - NZERO, X pagetok(PROCSIZE(pp)), X pagetok(VP(pp, vm_rssize)), X state_abbrev[(unsigned char) PP(pp, p_stat)], X cputime / 60l, X cputime % 60l, X 100.0 * weighted_cpu(pct, pp), X 100.0 * pct, X printable(PP(pp, p_comm))); X X /* return the result */ X return(fmt); } X X /* X * check_nlist(nlst) - checks the nlist to see if any symbols were not X * found. For every symbol that was not found, a one-line X * message is printed to stderr. The routine returns the X * number of symbols NOT found. X */ X static int check_nlist(nlst) X register struct nlist *nlst; X { X register int i; X X /* check to see if we got ALL the symbols we requested */ X /* this will write one line to stderr for every symbol not found */ X X i = 0; X while (nlst->n_name != NULL) X { X if (nlst->n_type == 0) X { X /* this one wasn't found */ X (void) fprintf(stderr, "kernel: no symbol named `%s'\n", X nlst->n_name); X i = 1; X } X nlst++; X } X X return(i); } X X /* X * getkval(offset, ptr, size, refstr) - get a value out of the kernel. X * "offset" is the byte offset into the kernel for the desired value, X * "ptr" points to a buffer into which the value is retrieved, X * "size" is the size of the buffer (and the object to retrieve), X * "refstr" is a reference string used when printing error meessages, X * if "refstr" starts with a '!', then a failure on read will not X * be fatal (this may seem like a silly way to do things, but I X * really didn't want the overhead of another argument). X * X */ X static int getkval(offset, ptr, size, refstr) X unsigned long offset; int *ptr; int size; char *refstr; X { X if (kvm_read(kd, offset, (char *) ptr, size) != size) X { X if (*refstr == '!') X { X return(0); X } X else X { X fprintf(stderr, "top: kvm_read for %s: %s\n", X refstr, strerror(errno)); X quit(23); X } X } X return(1); } X /* comparison routine for qsort */ X /* X * proc_compare - comparison function for "qsort" X * Compares the resource consumption of two processes using five X * distinct keys. The keys (in descending order of importance) are: X * percent cpu, cpu ticks, state, resident set size, total virtual X * memory usage. The process states are ordered as follows (from least X * to most important): WAIT, zombie, sleep, stop, start, run. The X * array declaration below maps a process state index into a number X * that reflects this ordering. X */ X static unsigned char sorted_state[] = { X 0, /* not used */ X 3, /* sleep */ X 1, /* ABANDONED (WAIT) */ X 6, /* run */ X 5, /* start */ X 2, /* zombie */ X 4 /* stop */ }; X int proc_compare(pp1, pp2) X struct proc **pp1; struct proc **pp2; X { X register struct kinfo_proc *p1; X register struct kinfo_proc *p2; X register int result; X register pctcpu lresult; X X /* remove one level of indirection */ X p1 = *(struct kinfo_proc **) pp1; X p2 = *(struct kinfo_proc **) pp2; X X /* compare percent cpu (pctcpu) */ X if ((lresult = PP(p2, p_pctcpu) - PP(p1, p_pctcpu)) == 0) X { X /* use cpticks to break the tie */ X if ((result = PP(p2, p_cpticks) - PP(p1, p_cpticks)) == 0) X { X /* use process state to break the tie */ X if ((result = sorted_state[(unsigned char) PP(p2, p_stat)] - X sorted_state[(unsigned char) PP(p1, p_stat)]) == 0) X { X /* use priority to break the tie */ X if ((result = PP(p2, p_priority) - PP(p1, p_priority)) == 0) X { X /* use resident set size (rssize) to break the tie */ X if ((result = VP(p2, vm_rssize) - VP(p1, vm_rssize)) == 0) X { X /* use total memory to break the tie */ X result = PROCSIZE(p2) - PROCSIZE(p1); X } X } X } X } X } X else X { X result = lresult < 0 ? -1 : 1; X } X X return(result); } X X /* X * proc_owner(pid) - returns the uid that owns process "pid", or -1 if X * the process does not exist. X * It is EXTREMLY IMPORTANT that this function work correctly. X * If top runs setuid root (as in SVR4), then this function X * is the only thing that stands in the way of a serious X * security problem. It validates requests for the "kill" X * and "renice" commands. X */ X int proc_owner(pid) X int pid; X { X register int cnt; X register struct kinfo_proc **prefp; X register struct kinfo_proc *pp; X X prefp = pref; X cnt = pref_len; X while (--cnt >= 0) X { X pp = *prefp++; X if (PP(pp, p_pid) == (pid_t)pid) X { X return((int)EP(pp, e_pcred.p_ruid)); X } X } X return(-1); } X X #ifdef USE_SWAP /* X * swapmode is based on a program called swapinfo written X * by Kevin Lahey <kml@rokkaku.atl.ga.us>. X */ X #define SVAR(var) __STRING(var) /* to force expansion */ #define KGET(idx, var) \ X KGET1(idx, &var, sizeof(var), SVAR(var)) #define KGET1(idx, p, s, msg) \ X KGET2(nlst[idx].n_value, p, s, msg) #define KGET2(addr, p, s, msg) \ X if (kvm_read(kd, (u_long)(addr), p, s) != s) \ X warnx("cannot read %s: %s", msg, kvm_geterr(kd)) #define KGETRET(addr, p, s, msg) \ X if (kvm_read(kd, (u_long)(addr), p, s) != s) { \ X warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \ X return (0); \ X } X X int swapmode() { X char *header; X int hlen, nswap, nswdev, dmmax; X int i, div, avail, nfree, npfree, used; X struct swdevt *sw; X long blocksize, *perdev; X struct rlist head; X struct rlist *swaplist; X X KGET(VM_NSWAP, nswap); X KGET(VM_NSWDEV, nswdev); X KGET(VM_DMMAX, dmmax); X KGET(VM_SWAPLIST, swaplist); X if ((sw = (struct swdevt *)malloc(nswdev * sizeof(*sw))) == NULL || X (perdev = (long *)malloc(nswdev * sizeof(*perdev))) == NULL) X err(1, "malloc"); X KGET1(VM_SWDEVT, sw, nswdev * sizeof(*sw), "swdevt"); X X /* Count up swap space. */ X nfree = 0; X memset(perdev, 0, nswdev * sizeof(*perdev)); X while (swaplist) { X int top, bottom, next_block; X X KGET2(swaplist, &head, sizeof(struct rlist), "swaplist"); X X top = head.rl_end; X bottom = head.rl_start; X X nfree += top - bottom + 1; X X /* X * Swap space is split up among the configured disks. X * X * For interleaved swap devices, the first dmmax blocks X * of swap space some from the first disk, the next dmmax X * blocks from the next, and so on up to nswap blocks. X * X * The list of free space joins adjacent free blocks, X * ignoring device boundries. If we want to keep track X * of this information per device, we'll just have to X * extract it ourselves. X */ X while (top / dmmax != bottom / dmmax) { X next_block = ((bottom + dmmax) / dmmax); X perdev[(bottom / dmmax) % nswdev] += X next_block * dmmax - bottom; X bottom = next_block * dmmax; X } X perdev[(bottom / dmmax) % nswdev] += X top - bottom + 1; X X swaplist = head.rl_next; X } X X header = getbsize(&hlen, &blocksize); X div = blocksize / 512; X avail = npfree = 0; X for (i = 0; i < nswdev; i++) { X int xsize, xfree; X X /* X * Don't report statistics for partitions which have not X * yet been activated via swapon(8). X */ X X xsize = sw[i].sw_nblks; X xfree = perdev[i]; X used = xsize - xfree; X npfree++; X avail += xsize; X } X X /* X * If only one partition has been set up via swapon(8), we don't X * need to bother with totals. X */ X used = avail - nfree; X free(sw); free(perdev); X return (int)(((double)used / (double)avail * 100.0) + 0.5); } X #endif SHAR_EOF chmod 0664 m_freebsd20.c || echo 'restore of m_freebsd20.c failed' Wc_c="`wc -c < 'm_freebsd20.c'`" test 21173 -eq "$Wc_c" || echo 'm_freebsd20.c: original size 21173, current size' "$Wc_c" fi exit 0 ----------------------------------------------------------------------