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