Index: contrib/top/ADVERTISEMENT =================================================================== --- contrib/top/ADVERTISEMENT +++ contrib/top/ADVERTISEMENT @@ -1,28 +0,0 @@ - William LeFebvre - Group sys Consulting - wnl@groupsys.com - +1-770-813-3224 - - -William LeFebvre is available for consulting and teaching engagements -through the company Group sys Consulting. William's specialties are: - - Unix system administration issues - Local area network design - Design of safe connections to the Internet - Domain Name Service - Threaded programming with pthreads - Netscape Server API plugins - INN news server configuration - SunOS to Solaris migration - Troubleshooting - - -Although located in the Atlanta metropolitan area, William can easily -travel to any location in the United States and Canada. Trips to -other countries can be arranged as well. - -If you are interested in having William work for your organization, -contact him at +1-770-813-3224 or via the address "wnl@groupsys.com". -You may also wish to visit the Group sys web page at www.groupsys.com. - Index: contrib/top/Changes =================================================================== --- contrib/top/Changes +++ contrib/top/Changes @@ -1,3 +1,320 @@ +Tue May 6 2008 - wnl (3.8beta1) + Main code: fixed bugs in screen_cleareol and in display code. Fixed + bug in i_swap when all data is 0. Added ^W patch (from thaquis). + Fixed bug in xdprintf. Added command line options for the "t" and + "m" commands. + SunOS 5 changes: Support for showing individual threads. Redid + allocation of prpsinfo structures. Added a pidthr hash that uses + both pid and thread id for a key. Changed format_process_header + and format_next_process to use a table-driven method for generating + the columns. Status files from /proc (psinfo and lpsinfo) are now + cached to avoid repeatedly reopening them. Column showing number of + LWPs is now called "NLWP" and column showing lwpid is "LWP". + FreeBSD changes: Runtime check to ensure binary is running on + the same machine type it was compiled for. Lots of cleanup and + changed nearly everything to use sysctl rather than kvm, and + inability to open kvm is no longer fatal. Improved thread reporting: + disabled for 7.x and lower. Added lwpid hash for proper tracking + of threads. Changed format_process_header and format_next_process + to use a table-driven method for generating the columns. + Dec Alpha: configure uses compile-time options to properly trap + and handle exceptions from the Alpha FPU (from Brian Maly). + +Tue Feb 26 2008 - wnl (3.7) + Prepare for version 3.7 release. + +Fri Feb 1 2008 - wnl (3.7beta4) + Using the $ notation in printf formats for freebsd apparently was + causing problems on 64-bit systems. All such usage has been + removed and the process line is formatted piecemeal. + +Thu Dec 27 2007 - wnl (3.7beta3) + Improved function comments in display.c for message_error functions. + Changed some of the error messages in top.c to be more succint. + +Fri Dec 7 2007 - wnl (3.7beta3) + Changes to freebsd port: moved some functions up front to + eliminate forward references. Use sysctl to get all vm stats + information, as some of this isn't updated in the struct + vmmeter under FreeBSD 7.0. Added routines to support large-scale + sysctl access. + +Wed Nov 28 2007 - wnl (3.7beta3) + Changes to documentation: FAQ, README, man page. + +Tue Nov 27 2007 - wnl (3.7beta3) + For freebsd, added page faults, pageins, pageouts, and pages + freed to the kernel display line. These numbers reflect the + values presented in vmstat. For sunos5, added page faults, + pageins and pageouts to the kernel display line. + +Fri Nov 2 2007 - wnl (3.7beta3) + Added copyright notices to the top of every source and include file. + Added copyright information to the man page. + Removed a few outdated things from the manifest. + Minor changes to sigconv.awk. + +Sat Oct 27 2007 - wnl (3.7beta3) + Added check for sys_signame at configure time and if it is + present then it is used in commands.c to translate signal names + in to numbers. + Added alternate snprintf and vsnprintf functions from apache (in + ap_snprintf.c). Added configure magic to define and compile them in + where needed. Added check to configure for variadic macros. + Preprocessor defintion of dprintf (in utils.h) now depends on + support for variadic macros. Cleaned up m_linux code. + +Wed Oct 3 2007 - wnl (3.7beta3) + Lots of changes, thanks to Mark Wong. Most changes were to + clean the code up so that it would compile cleanly with -Wall + (all warnings). Changed function names in screen.c so that + they all start with "screen_". Isolated all interaction with + termcap to screen.c by adding a real function for cursor + addressing (in the past it was just a macro). Only screen.c + now needs to worry about defining templates for the termcap + functions. Added configure and preprocessor magic to ensure + that all the termcap functions used in the code are defined + with templates. Changed names of some other functions and + global variables to avoid name conflicts with functions in + curses and other well established libraries. Changed dprintf + macro to use variadic arguments so that the preprocessor can + gobble up the entire call when compiling without debugging + (this will have to be made more portable). All include files + are surrounded by #ifndef statements to accomodate multiple + inclusions. Platform module is now compiled with + -fno-strict-aliasing as some of the modules do type punning + that can confuse the optimizer. + +Wed Sep 26 2007 - wnl (3.7beta3) + For freebsd, priority is no longer normalized by PZERO. This + contradicts the behavior used by ps when it displays priority. + But normalizing by PZERO has become a bit of an anachronism + and it actually obscures the meaning of the priority without + adding any real value. + +Wed Sep 19 2007 - wnl (3.7beta3) + Many changes to improve the display of threads. Changed + process summary line to use the word "threads" when showing + individual threads. Added the system command to toggle the + display of system processes. Fixed bug in hash.c remove_pos. + For freebsd: count threads correctly when they are being + displayed, nice column is more closely in line with ps + (nothing fancy for real time processes), add two more process + states that didn't exist in older releases of freebsd (wait + and lock). + For linux: Threads done right. Now track individual threads + of multi-threaded processes separately so that we always know + their %cpu. Switch to format_process_header so that we can + change the column headings and remove the THR column when + displaying individual threads. Switched process (and thread) + tracking over to use generic hash table functions included + with the new version of top. Process states and total now + include threads when they are being shown. Added "SHR" column + to show the amount of shared memory per process. Improved + calculation of elapsed time and percent cpu to avoid + overflows. Remove weighted cpu calculations entirely as it is + an anachronism. + For Solaris: Moved check for libelf to accomodate older systems. + +Sun Sep 9 2007 - wnl (3.7beta2) + Documentation changes. Fixes to sunos5 port. Added display of + thread count and selection by command name to linux port. Removed + the use of inline functions from hash.c as that doesn't appear to + be very portable. + +Wed Sep 5 2007 - wnl (3.7beta1) + Fixed freebsd and linux configuration bugs. Added configuration + options for tweaking program defaults. Rewrote top level code + (top.c) from scratch, including command handling so that adding + new commands is much easier. Changed message-line handling to + ensure that the message is displayed for at least 5 seconds + regardless of the update frequency. Added a "miniupdate" that + occurs one second after the initial screen on systems that don't + already delay the first screen. The mini-update shows cpu state + percentages. Added ability to select output by command name on + some systems. Fixed color toggling via the "C" command. Added + long options via getopt_long to complement the existing single + character options. Added the freebsd "m" command to chose + alternate display modes. On freebsd this gives a process i/o + display. Added the freebsd "H" command to select the display of + individual threads. Added "-a" option ("all") to set number of + displays and number of processes to infinity (equivalent to + "-d all all"). Added dual architecture compilation for Solaris + to generate both a 32-bit and a 64-bit binary. This is on by + default when compiling on a 64-bit system and can be explicitly + set via "configure --enable-dualarch". Added uniform hashing + functions that use bucket hash for uint, pid, and string. Changed + username.c and the sunos and freebsd modules to use these functions. + Added the "kernel" information line to the display to show + statistics on what the kernel is doing (context switches, forks, + traps, etc.). This requires explicit support by the platform + module, currently only freebsd, linux, and sunos. + +Wed Apr 18 2007 - wnl (3.6.1) + Fixed a few bugs in sigconv.awk that were causing incorrect + results on FreeBSD. Changed configure.ac to fix a few linux + problems: signal include file and /proc/1/stat. + +Fri Apr 13 2007 - wnl (3.6.1) + Removed the use of VPATH for compiling the system module and used + an explicit dependency in the Makefile instead. VPATH is now set + to just srcdir to ensure that top will compile correctly when + configured from a different directory. On systems without VPATH + support, top will still configure and compile, but only + from within the source directory. This fixes bug 1699526. + +Fri Feb 2 2007 - wnl (3.6.1) + Revised the way that configure figures out owner, group, and mode. + For systems that don't use the kernel, it tries to match install + settings to allow access to stuff in /proc. More importantly, if + mode is 755 then neither owner nor group are set. This fixes bug + 1631136. Added patch from haanjdj@xs4all.nl to fix an occasional + core dump in m_decosf1.c. This checks return code from task_threads. + Made sure all get_system_info functions are declared void. Fixed + string termination bug. Cleaned up documetation for sunos5. + +Tue Aug 8 2006 - wnl (3.6.1) + For Solaris, changed the tag "swap" to "total swap" to clarify + what is beign displayed. Note that the calculations are still the + same: the display is just showing total rather than total - free. + +Thu Apr 27 2006 - wnl (3.6) + Added patches for linux-style sort shortcuts and for Unixware + support in configure (patch 1474427). Fixed sunos5 to do slow start + and to ensure cpucount is set (patch 1477386). Added pagination + routines to display.c and modified show_help to use it, since the + help screen is now longer than 24 lines. Applied patch for unixware + support that adds check for mas library (patch #1474423). Solaris + cpu percent now reflects a percentage of the entire server, rather + than a single cpu (bug 1478138). + +Mon Mar 27 2006 - wnl (3.6) + The production release of version 3.6. Fixed a minor scaling + bug in the decosf1 module. Support for MacOS X is officially + withdrawn although the macosx module is still part of the + distribution. Hopefully this is a temporary situation. + Documentation updated. + + +Wed Feb 15 2006 - wnl (3.6beta5) + Minor changes to eliminate warnings from the Sun Studio compiler. + These were mostly sloppy argument declarations. I also added + message.h to provide an interface file for just the message + related functions of display.c. + +Mon Dec 26 2005 - wnl (3.6beta4) + Added new netbsd module, courtesy of Simon Burge. + Fixed a few bugs in SVR4 module and added its use to + configure.ac, thanks to Sanchet Dighe. Also ensured that the + novpath Makefile was in the distribution. + Fixed portability problem in display.c + + +Mon Oct 24 2005 - wnl (3.6beta3) + Set up a color tagging mechanism in color.c to allow for the + dynamic creation of tag names to contol color highlighting. + These names are partially derived from the tags used to label + memory and swap information on the screen, thus are driven by + the machine module itself. Added -T option to list color + highlighting information. Help screen now includes the actual + list of sort order names. Incorporated some minor fixes to + the main code from the Freebsd source tree. Fixed bug #1324582. + Freebsd 5: removed WCPU column and added THR column. Display + for freebsd 4 and earlier unchanged since they don't track + threads in the kernel. Added LICENSE file to distribution. + +Wed Oct 12 2005 - wnl (3.6beta2) + Major overhaul to display.c. All lines of the display are + directly tracked and controlled via display_write and its + companion display_fmt. Added support for complete control + of ANSI color on the screen: this will be used in the future + to allow for full use of color everywhere on the screen. + Signal handling code now uses sigaction on all systems that + support it. Restored the freebsd module and did away with + freebsd4, and upgraded freebsd module to support 5.x. + Fix bug #1306099 (wio(wait) timer ignored on OSF1). + +Fri Sep 23 2005 - wnl (3.6beta1) + Fixed bugs #1266341 (compilation errors with gcc 4.x), + #1156464 (cpu% field for sunos), #1156243 (compilation + errors on AIX). Applied patches #1217855 (Solaris 10 + xarch flag). Overhaul of sunos5 module, making code more + efficient and easier to follow. Got rid of need for MEMTYPE + type in utils.h. Changed all memory statistics data in the + module specification from an int to a long to better support + 64-bit systems with lots of memory. Moved all unused modules + out of the distribution (I will add them back in as needed). + Moved freebsd module to freebsd4 as it won't work with 5.x + (a new module will be necessary). Added support to configure + for makes that don't understand VPATH. Updated documentation: + man page, FAQ, README, INSTALL. + +Mon Jan 24 2005 - wnl (3.6alpha10) + Updated aix43 module with ANSI function declarations and fixed + declaration of get_system_info. Configure now uses irixsgi + module for irix6* systems. Updates to the following modules: + irixsgi, sunos5. Fixed null pointer bug in color.c. Removed + some useless code and definitions in display.c + + +Sun Nov 28 2004 - wnl (3.6alpha9) + Replace AIX 5 module with alternate (bug 1056565). + Fixed vulnerability in use of snprintf. + +Fri Oct 22 2004 - wnl (3.6alpha8) + Support for linux 2.6, added more stuff to memory and swap lines. + Updated linuxthr module, which is only useful on 2.4 and earlier. + Added some color support back in (feature request 930588), but + still need to add it back to the per-process display. Added + OSF 5 support (untested). + Fixed bug 1017951 (invalid process count argument not caught) + +Tue Apr 20 2004 - wnl (3.6alpha7) + Added 64 bit support for AIX. + +Thu Apr 15 2004 - wnl (3.6alpha6) + Included fixes for decosf1 pid size and updated module. Also + added osf1 to list of recognized operating systems in configure.ac. + +Tue Mar 30 2004 - wnl (3.6alpha5) + Minor bug fixes and some code rearrangement. Changes to install + rule. Added several more platforms including: aix 4.2 thru 5, + MacOS 10, Dec OSF, HPUX 7 thru 11. Fixed the core dumping bug + in linux. Code cleanup, including sigdesc.h (by changing + sigconv.awk). Startup error messages are displayed on the + first screen rather than beforehand (no more pause). Cleaned + up interrupt handling to avoid a race condition. Eliminated + top.local.h. REMOVED Configure!!! + +Mon Mar 22 2004 - wnl (3.6alpha1) + Now using gnu autoconf. Eliminated the need for CFLAGS and LIBS + tags in the module source files. Autoconf tries to figure all + that out now. Machine module interface now uses flags to determine + if module supports sorting, selective display of idle processes, + viewing full commands. Added display of uptime for modules that + support it. Added display of full command lines for modules that + support it. 3.5 modules must be changed a bit to work for 3.6: + ORDER is no longer defined, and the module must fill in the + appropriate fields in struct statics to get the extra features. + Added a extenstion interface to allow for putting extra stuff + on the screen -- this is still half baked and not documented. + +Mon Feb 23 2004 - wnl (3.5) + Turned rc1 in to version 3.5. Only changes were to the FAQ. + +Mon Feb 2 2004 - wnl (3.5rc1) + Changed format_k (utils.c) to use MEMTYPE for its parameter. + On most systems this is a long, but if the module defines + USE_SIZE_T, this is set to be a size_t. The sunos5 module + now defines it, so that it will work correctly on 64-bit + machines. New "linuxthr" module for rolling up processes + that are really threads. Configure autodetects when running + on a 64-bit Solaris machine. + +Tue Dec 16 2003 - wnl (3.5beta13) + Improved linux module. For Solaris, changed "THR" column + heading to "LWP" since that's what they really are. + Thu Mar 30 2000 - wnl (3.5beta12) Updated modules: m_aix41.c, m_aix43.c, m_mtxinu.c, m_sco5.c, and m_ultrix4.c. Index: contrib/top/Configure =================================================================== --- contrib/top/Configure +++ contrib/top/Configure @@ -1,565 +0,0 @@ -#!/bin/csh -f -# -# Configuration script for top. -# -# Use with version 3.0 and higher. -# -set PRIME = "/usr/games/primes" -set vars = (module LoadMax topn NominalTopn delay owner group mode random \ - TableSize bindir mandir manext mansty \ - Cmdshell Cmdcc Cmdawk Cmdinstall cdefs) -set fastrack = 0 -set yesno = (no yes) - -onintr byebye - -# make sure that getans is there and ready -if (! -e getans) then - echo 'This package is not complete. The shell file "getans" is missing.' - exit 10 -endif -chmod +x getans - -if ($#argv > 0) then -# fast track configuration - set fastrack = 1 -else -cat <<'EOF' -Configuration for top, version 3.5 - -One moment.... -'EOF' -endif - -# collect file names and module names -ls machine/m_*.c >$$.f -ls machine/m_*.man >$$.m -sed -e 's@^machine/m_@@' -e 's/.c$//' $$.f >$$.n - -# build Make.desc -sed -e 's@\.c@.desc\\@' $$.f | sed -e '$s/\\//' >$$.a -sed -e "/^DESCS/r $$.a" Make.desc.X >Make.desc - -# build desc files and SYNOPSIS as needed -make -f Make.desc >/dev/null -if ($status != 0) then - echo "Unable to build the synopsis." - echo 'Make sure the command "make" is on your path and try' - echo 'running Configure again.' - exit 1 -endif - -if (-e .defaults) then - echo "" - echo "Reading configuration from last time..." - source .defaults - set nodefaults = 0 - if ($fastrack == 1) then - set module = $1 - endif -else - if ($fastrack == 1) then - echo "No previous configuration was found." - set fastrack = 0 - set module = $1 - else - set module = "" - endif - set LoadMax = 5.0 - set topn = 15 - set NominalTopn = 18 - set delay = 5 - set TableSize = 0 - set bindir = /usr/local/bin - set mandir = /usr/man/manl - set manext = l - set mansty = man - set nodefaults = 1 - set Cmdshell = /bin/sh - set Cmdawk = awk - set Cmdinstall = ./install - set Cmdcc = cc - set cdefs = -O -endif -echo "" - -if ($fastrack == 1) then - grep -s $module $$.n >/dev/null - if ($status != 0) then - echo "$module is not recognized. To see a list of available modules" - echo 'run "Configure" with no arguments.' - rm -f $$.[fmna] - exit 1 - endif - set random1 = `expr $random + 1` - cat </dev/null -if ($status != 0) then - echo "That is not a recognized module name." - goto getmod -endif - -# display a full description -sed -e '1,/DESCRIPTION:/d' -e '/^$/,$d' machine/m_${module}.desc - -# verify it -echo "" -./getans "Is this what you want to use?" yesno 1 .$$ -if (`cat .$$` == 0) then - goto getmod -endif -endif - -cat <<'EOF' - -First we need to find out a little bit about the executables needed to -compile top. - -'EOF' -./getans "What is the full path name for the Bourne shell" file "$Cmdshell" .$$ -set Cmdshell = `cat .$$` - -cat <<'EOF' - -Please supply the name of the appropriate command. It need not be a -full path name, but the named command does need to exist somewhere on -the current path. - -'EOF' -./getans "AWK Interpreter" path "$Cmdawk" .$$ -set Cmdawk = `cat .$$` -./getans "C Compiler" path "$Cmdcc" .$$ -set Cmdcc = `cat .$$` - -cat <<'EOF' - -The installer command needs to understand Berkeley-esque arguments: -"-o" for owner, "-g" for group, and "-m" for mode. A shell script -called "install" is distributed with top and is suitable for use by -top. You can specify a different program here if you like, or use -the shell script (the default). - -'EOF' -./getans "Installer" path "$Cmdinstall" .$$ -set Cmdinstall = `cat .$$` - -cat < 1) then - set rand = 1 - endif -else - ypwhich >&/dev/null - if ($status == 0 || -e /etc/passwd.dir || -e /etc/pwd.db) then - set rand = 1 - endif -endif - -if ($rand == 1) then - echo "It looks like you have a passwd file that can be accessed at random." - set pr = 'Do you want top to take advantage of this' -else - echo "It looks like you have conventional passwd file access. Top can take" - echo "advantage of a random access passwd mechanism if such exists. Do" - echo "you want top to assume that accesses to the file /etc/passwd are done" - set pr = 'with random access rather than sequential' -endif - -if ($nodefaults == 1) then - set random = $rand -endif - -./getans "${pr}?" yesno $random .$$ -set random = `cat .$$` - -echo "" -echo "Compiling prime.c" -$Cmdcc $cdefs -o prime prime.c -lm -if ($status != 0) then - echo "Oh well." - rm -f prime -endif - -echo "" - -ypcat passwd.byname >&/tmp/$$.a -if ($status == 0) then - set cnt = `wc -l &/tmp/$$.a - if ($status == 0) then - set cnt = `wc -l /tmp/$$.b - grep '^....r..r..' /tmp/$$.b >&/dev/null - if ($status == 1) then - grep '^....r..-..' /tmp/$$.b >&/dev/null - if ($status == 0) then - set t_group = `awk ' { print $4 }' /tmp/$$.b` - set t_mode = 2755 - echo "It looks like only group $t_group can read the memory devices." - else - set t_mode = 4755 - echo "It looks like only root can read the memory devices." - endif - else - set t_mode = 755 - echo "It looks like anybody can read the memory devices." - endif -else - echo "It looks like there are no memory device special files." - set t_mode = 755 -endif -if ($nodefaults) then - set owner = $t_owner - set group = $t_group - set mode = $t_mode -endif -echo "Tell me how to set the following when top is installed:" -./getans "Owner" user "$owner" .$$ -set owner = `cat .$$` -./getans "Group owner" group "$group" .$$ -set group = `cat .$$` -./getans "Mode" integer "$mode" .$$ -set mode = `cat .$$` -rm -f /tmp/$$.b - -echo "" -./getans "Install the executable in this directory" file "$bindir" .$$ -set bindir = `cat .$$` - -echo "" -./getans "Install the manual page in this directory" file "$mandir" .$$ -set mandir = `cat .$$` - -echo "" -./getans "Install the manual page with this extension" string "$manext" .$$ -set manext = `cat .$$` - -echo "" -./getans "Install the manual page as 'man' or 'catman'" string "$mansty" .$$ -set mansty = `cat .$$` - -echo "" -echo "We are done with the questions." - -# Some Unix environments are so poor that their csh doesn't even support -# the "eval" builtin. Check for this before relying on its use to save -# the current configuration. -/bin/csh -fc "eval echo foo" >&/dev/null -if ($status == 1) then - echo "Can't save configuration (nonfatal)" -else - echo "Saving configuration..." -# save settings to use as defaults the next time - rm -f .defaults - touch .defaults - foreach v ($vars) - set tmp = `eval echo \$$v` - echo set $v = "'$tmp'" >>.defaults - end -endif - -fast: - -# clean up -rm -f $$.[fmna] - -# set the link for machine.c -rm -f machine.c machine.o -ln -s machine/m_${module}.c machine.c - -# get definitions out of the module file -set libs = `grep LIBS: machine/m_${module}.desc | sed -e 's/^.[^:]*: *//'` -set cflgs = `grep CFLAGS: machine/m_${module}.desc | sed -e 's/^.[^:]*: *//'` -set tcap = `grep TERMCAP: machine/m_${module}.desc | sed -e 's/^.[^:]*: *//'` -set math = `grep MATH: machine/m_${module}.desc | sed -e 's/^.[^:]*: *//'` - -# get osrev defition, if we can -set uname="" -if (-e /usr/bin/uname) then - set uname=/usr/bin/uname -else if (-e /bin/uname) then - set uname=/bin/uname -endif - -if ("$uname" != "") then -# different versions of tr can't agree on the way to specify ranges, so -# we will have to give the range explicitly.....sigh. - set osrev="-DOSREV=`$uname -r | tr -cd ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`" -else - set osrev="" -endif - -# default for tcap (termcap) -if ("$tcap" == "") then - set tcap="-ltermcap" -else if ("$tcap" == "none") then - set tcap="" -endif - -# allow for the module to override or remove -lm -if ("$math" == "") then - set math="-lm" -else if ("$math" == "none") then - set math="" -endif - -if ( { grep -s SIGKILL /usr/include/signal.h } ) then - set signal="/usr/include/signal.h" -else - set signal="/usr/include/sys/signal.h" -endif - - -echo "Building Makefile..." -sed -e "s|%topn%|$topn|" \ - -e "s|%delay%|$delay|" \ - -e "s|%owner%|$owner|" \ - -e "s|%group%|$group|" \ - -e "s|%mode%|$mode|" \ - -e "s|%bindir%|$bindir|" \ - -e "s|%mandir%|$mandir|" \ - -e "s|%manext%|$manext|" \ - -e "s|%mansty%|$mansty|" \ - -e "s|%tablesize%|$TableSize|" \ - -e "s|%libs%|$libs|" \ - -e "s|%cflgs%|$cflgs|" \ - -e "s|%termcap%|$tcap|" \ - -e "s|%math%|$math|" \ - -e "s|%cdefs%|$cdefs|" \ - -e "s|%signal%|$signal|" \ - -e "s|%cc%|$Cmdcc|" \ - -e "s|%awk%|$Cmdawk|" \ - -e "s|%install%|$Cmdinstall|" \ - -e "s|%shell%|$Cmdshell|" \ - -e "s|%osrev%|$osrev|" \ - Makefile.X >Makefile - -echo "Building top.local.h..." -sed -e "s|%LoadMax%|$LoadMax|" \ - -e "s|%TableSize%|$TableSize|" \ - -e "s|%NominalTopn%|$NominalTopn|" \ - -e "s|%topn%|$topn|" \ - -e "s|%delay%|$delay|" \ - -e "s|%random%|$random|" \ - top.local.H >top.local.h - -echo "Building top.1..." -sed -e "s|%topn%|$topn|" \ - -e "s|%delay%|$delay|" \ - top.X >top.1 -if (-e machine/m_${module}.man ) then - cat machine/m_${module}.man >>top.1 -endif - -# clean up -rm -f .$$ - -echo 'Doing a "make clean".' -make clean - -echo 'To create the executable, type "make".' -echo 'To install the executable, type "make install".' -exit 0 - -byebye: -rm -f .$$ $$.[fmna] /tmp/$$.[ab] -exit 1 Index: contrib/top/DISCLAIMER =================================================================== --- contrib/top/DISCLAIMER +++ contrib/top/DISCLAIMER @@ -1,31 +0,0 @@ -DISCLAIMER - -"top" is distributed free of charge. It should not be considered an -official product of Group sys Consulting. William LeFebvre supports -"top" in his spare time and as time permits. - -NO WARRANTY: - -BECAUSE "top" IS DISTRIBUTED FREE OF CHARGE, THERE IS ABSOLUTELY NO -WARRANTY PROVIDED, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING, GROUP SYS CONSULTING, ARGONNE -NATIONAL LABORATORY, NORTHWESTERN UNIVERSITY, WILLIAM N. LeFEBVRE -AND/OR OTHER PARTIES PROVIDE "top" "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -PROGRAM IS WITH YOU. SHOULD THE "top" PROGRAM PROVE DEFECTIVE, YOU -ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -IN NO EVENT WILL GROUP SYS CONSULTING, ARGONNE NATIONAL LABORATORY, -NORTHWESTERN UNIVERSITY, WILLIAM N. LeFEBVRE, AND/OR ANY OTHER PARTY -WHO MAY MODIFY AND REDISTRIBUTE "top", BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL, INCIDENTAL -OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE -(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED -INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A FAILURE OF THE -PROGRAM TO OPERATE WITH OTHER PROGRAMS) THE PROGRAM, EVEN IF YOU HAVE -BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY -ANY OTHER PARTY. - -So there! Index: contrib/top/FAQ =================================================================== --- contrib/top/FAQ +++ contrib/top/FAQ @@ -1,264 +1,340 @@ - TOP - Version 3.5 - Beta Release 11 + TOP + Version 3.8beta1 - William LeFebvre - with much help from others + William LeFebvre + with much help from others + Frequently Asked Questions and their Answers -FREQUENTLY ASKED QUESTIONS AND THEIR ANSWERS -This FAQ is broken out in to several topics. + GENERAL + + 1. What is top? + + Top provies the user with a regularly updated display showing + information about the system and its top cpu-using processes. Think + of it as a full-screen "ps" output that gets updated at regular + intervals. + + 2. Where do I get the latest version of top? + + The official site for top is "ftp.unixtop.org" in the directory + "/pub/top". Top is also a SourceForge project, and the most recent + releases are available on any of the SourceForge mirrors. The + SourceForge project page is at + http://sourceforge.net/projects/unixtop. + + 3. Is there a web page for top? + + Yes. Point your browser at http://www.unixtop.org. It includes all + documentation, a nice interactive display which describes the various + components of the output of top, web-based retrieval of the package, + year 2000 information, and other neat stuff. + + 4. Is there a mailing list or on-line bulletin board for top? + + There is a mailing list used for general announcements regarding top, + including new releases. This mailing list is available to sourceforge + members and can be accessed from the unixtop sourceforge project + page. Visit SourceForge and search for the project "unixtop", then + click on "mailing lists". There are also on-line forums available + through SourceForge where members can post questions and comments. + + 5. What about Year 2000 compliance? + + Top did not experience any problems with the transition to the year + 2000. A full statement concerning top and the year 2000 can be found + in the file "Y2K" included with the distribution. + + 6. Will there be another major release of top? Will there be a top + version 4? + + I have some great ideas for the next major release of top, and I very + much want to make those ideas a reality. What I don't have much of + these days is free time. But I will keep poking at it and I hope to + have top version 4.0 ready by the fall of 2006. + + 7. Does top really support multi-processor systems? + + On platforms that support multiple processors, top is able to detect + and correctly summarize the information about those processors. What + top does not do is break down the cpu states summary (the third line + of the display) by cpu. Instead it collects the cpu state information + from all processors and combines them in to a single line. Some + vendors include a modified version of top that presents this + information for each cpu. Top 3.7 may have this functionality but it + is not present in the standard top 3.6 release. + + 8. Is top under CVS control? Can I access the sources via SourceForge + CVS or Subversion? + + I maintain top using subversion, not CVS. Although I utilize my own + private subversion repository, it is regularly mirrored in to the + SourceForge Subversion repository. You can access the SourceForge + repository here: https://svn.unixtop.org/unixtop/top-3. + + + COMPILING + + 9. We just upgraded our operating system to a new version and top broke. + What should we do? + + Recompile it. Top is very sensitive to changes in internal kernel + data structures. It is not uncommon for a new version of the + operating system to include changes to kernel data structures. + + + RUNNING + +10. I just finished compiling top and it works fine for root, but when I + try to run it as a regular user it either complains about files it + can't open or it doesn't display all the information it should. Did I + do something wrong? + + Well, you're just not done. On many operating systems today, access + to many of the kernel memory devices and other system files is + restricted to either root or a particular group. The configure script + figures this out (usually) and makes sure that the "install" rule in + the Makefile will install top so that anyone can run it successfully. + However, you have to *install* it first. Do this with the command + "make install". + +11. Top is (not) displaying idle processes and I don't (do) want it to. + + This default has only changed about a dozen times, and I finally got + tired of people whining about it. Go read the manual page for the + current version and pay special attention to the description of the + "TOP" environment variable. + +12. We have so much memory in our machine that the memory status display + (the fourth line) ends up being longer than 80 characters. This + completely messes up top's output. Is there a patch? + + Most modules have been changed to use new memory formatting functions + which will display large values in terms of megabytes instead of + kilobytes. This should fix all occurences of this problem. Also note + that newer versions of top can use columns beyond 79, and understand + window resizes. So you can always make your window wider. + +13. I tried to compile top with gcc and it doesn't work. I get + compilation errors in the include files, or I get an executable that + dumps core, or top displays incorrect numbers in some of the + displays. What's wrong? + + Gnu CC likes very much to use its own include files. Not being a gcc + expert, I can't explain why it does this. But I can tell you that if + you upgrade your operating system (say from Solaris 2.6 to Solaris + 2.7) after installing gcc, then the include files that gcc uses will + be incorrect, especially those found in the "sys" directory. Your + choices are: (1) rebuild and reinstall the "standard" include files + for gcc (look for scripts in the distribution called "fixincludes" + and "fixinc.svr4"), (2) compile machine.c with + "CFLAGS=-I/usr/include" then make the rest of the object files + normally, or (3) use a different compiler. + +14. The cpu state percentages are all wrong, indicating that my machine + is using 95% system time when it is clearly idle. What's wrong? + + This can happen if you compiled with gcc using the wrong include + files. See the previous question. + + + FREEBSD PROBLEMS + +15. This version of top does not show individual threads with the "t" or + "H" commands. Instead it says "command not available." Why? + + Previous versions of top attempted to support the display of + individual threads under FreeBSD through the use of the "t" command. + However, the FreeBSD kernel does not supply sufficient or correct + information on the individual threads within a process. So the data + that was being displayed was incorrect and misleading. Therefore, top + version 3.8 disables the use of this command to prevent the display + of incorrect information. FreeBSD 8.0 will correctly report + per-thread information and top version 3.8 supports the use of the + "t" command for version 8.0. + +16. The "f" command (to display full command lines for the processes) + does not work and instead says "command not available". Why? + + The current version of top is able to use sysctl to retrieve almost + all of the information it needs without having to open /dev/kmem. The + one piece of information not available via sysctl is the full command + line of each argument. If you run top as a regular user and it cannot + open /dev/kmem (in other words, it is not installed set-gid to the + kmem group) then it will disable the "f" command. Make sure the top + binary is installed with a group ownership of "kmem" and with the + set-gid bit on if you want the "f" command to work properly. + + + MACOSX PROBLEMS + +17. I tried to configure top on my Mac OSX system and I got an error + claiming "macosx not supported". What up? + + Since I don't have full time root access to a Mac OSX system I cannot + provide effective support for the platform. MacOSX uses Mach, and it + is very difficult to extract accurate system and process information + from the system. It takes a lot of trial and error, along with root + access. I have included the most up-to-date version of the macosx + module in the distribution, but I do not claim that it works. If you + want to try to use it, you can configure with "./configure + --with-module=macosx". + + + SUNOS PROBLEMS + +18. I tried compiling top under SunOS version 4.1.x and it got compile + time errors or run time errors. Is there a patch? + + If you try compiling top in a "System V environment" under SunOS + (that is, /usr/5bin is before /usr/bin on your path) then the + compilation may fail. This is mostly due to the fact that top thinks + its being compiled on a System V machine when it really isn't. The + only solution is to put /usr/bin and /usr/ucb before /usr/5bin on + your path and try again. + + + SOLARIS PROBLEMS + + + NOTE: the most common source of problems with top under Solaris is + the result of compiling it with the wrong front end. Make sure that + /usr/ucb is not on your path before attempting to compile top under + Solaris. + +19. Is there somewhere I can get a pre-compiled package? + + Yes. Although I don't provide pre-compiled binaries, you can get a + Sun-style package from www.sunfreeware.com. + +20. Under Solaris 2, when I type "make", the system says "language + optional software package not installed." What's going on? + + You tried to compile with /usr/ucb/cc. Make sure /usr/ucb is not on + your path. Furthermore, you do not have a Sun compiler installed on + your system. You need a compiler to make top. Either Sun's C compiler + or the Gnu C compiler will work fine. + +21. Under Solaris 2, when I run top as root it only shows root processes, + or it only shows processes with a PID less than 1000. It refuses to + show anything else. What do I do? + + You probably compiled it with /usr/ucb/cc instead of the real C + compiler. /usr/ucb/cc is a cc front end that compiles programs in BSD + source-level compatability mode. You do not want that. Make sure that + /usr/ucb is not on your path and try compiling top again. + +22. Under Solaris 2, I compiled top using what I am sure is the correct + compiler but when I try to run it it complains about missing dynamic + libraries. What is wrong? + + Check to see if you have LD_LIBRARY_PATH defined in your shell. If + you do, make sure that /usr/ucblib is not on the path anywhere. Then + try compiling top again. + +23. Under Solaris 2, when I try to run top it complains that it can't + open the library "libucb.so.1". So I changed the LIBS line in + m_sunos5.c to include -R/usr/ucblib to make sure that the dynamic + linker will look there when top runs. I figured this was just an + oversight. Was I right? + + No, you were not right. As distributed, top requires no alterations + for successful compilation and operations under any release of + Solaris 2. You probably compiled top with /usr/ucb/cc instead of the + real C compiler. See FAQ 22 for more details. + +24. On my 64-bit system some processes show up with incorrect information + (such as zero memory). + + If you are running a 64-bit system, then you need to make sure that + you are running the 64-bit top binary. Top's configure script + attempts to detect 64-bit systems, and will automatically generate + both 32-bit and 64-bit binaries on such systems. If you use or + install the 32-bit binary on a 64-bit system top will still run but + will not produce the correct results. This will also happen if you + configure your distribution on a 32-bit system then compile with that + configuration on a 64-bit system. You must configure and compile on + the same system. For Sparc systems the 32-bit binary will be created + in the subdirectory "sparcv7" and the 64-bit binary will be created + in the subdirectory "sparcv9". For Intel systems the directories will + be "i386" (32-bit) and "amd64" (64-bit). In all cases a copy of + /usr/lib/isaexec is made in the main directory and called "top". This + program will choose the correct binary to run from one of these + subdirectories. See isaexec(3c) for more details. + +25. Can I install both 32-bit and 64-bit binaries on a central file + server and have machines which mount it automatically use the correct + one? + + Yes. If you configure and compile on a 64-bit system, top's configure + script and makefile will automatically create both 32-bit and 64-bit + binaries. The "install" rule in the makefile will install these + binaries in subdirectories of /usr/local/bin appropriate to the + architecture (sparcv7/sparcv9 or i386/amd64) then create a copy of + /usr/lib/isaexec named "top" in /usr/local/bin to ensure that the + appropriate is run when a user types "top". If you make sure that you + configure and compile on a 64-bit system, then "make install" will do + the right thing. + +26. This version of top show less available swap space than previous + versions. Why does it no longer match the output of the swap summary + produced with "swap -s"? + + Starting with version 3.6 of top, the amount of swap space reported + by top has been changed to reflect only disk-based swap space. The + swap summary produced with "swap -s" also includes memory-based swap + space. This changed was made for several reasons. It makes the + display under Solaris more like those of other operating systems. The + display is more what users expect (except those used to previous + versions of top). Most importantly, "swap -s" gets its data via an + undocumented system interface. Now that top no longer displays that + data it can use publically documented and maintained system + interfaces to retrieve its data. + + + SVR4-DERIVED PROBLEMS + +27. When I run top on my SVR4-derived operating system, it displays all + the system information at the top but does not display any process + information (or only displays process information for my own + processes). Yet when I run it as root, everything works fine. What's + wrong? + + Your system probably uses the pseudo file system "/proc", which is by + default only accessible by root. Top needs to be installed setuid + root on such systems if it is going to function correctly for normal + users. + + + SVR42 PROBLEMS + +28. The memory display doesn't work right. Why? + + This is a known bug with the svr42 module. The problem has been + traced down to a potential bug in the "mem" driver. The author of the + svr42 module is working on a fix. + + + STILL STUCK + +29. I'm still stuck. To whom do I report problems with top? + + The most common problems are caused by top's sensitivity to internal + kernel data structures. So make sure that you are using the right + include files, and make sure that you test out top on the same + machine where you compiled it. Sun's BSD Source Compatability Mode is + also a common culprit. Make sure you aren't using either /usr/ucb/cc + or any of the libraries in /usr/ucblib. Finally, make sure you are + using the correct module. If there does not appear to be one + appropriate for your computer, then top probably will not work on + your system. + + If after reading all of this file and checking everything you can you + are still stuck, then please use SourceForge to submit a support + request or a bug. Top is supported by the SourceForge project named + "unixtop". On SourceForge you will find defect tracking, a mailing + list, and on-line forums. You can also contact the author through + SourceForge. -GENERAL - - 1. "Where do I get the latest version of top?" - -The official site for top is "ftp.groupsys.com" in the directory -"/pub/top". It is also available from the following mirror sites: -"pharos.dgim.doc.ca" in /packages/top, "uiarchive.uiuc.edu" in -/pub/packages/top, "sunsite.auc.dk" in /pub/unix/top. European -users should consider using the Denmark (dk) site. - - 2. "Is there a web page for top?" - -Yes. Point your browser at http://www.groupsys.com/top. It includes -all documentation, a nice interactive display which describes the -various components of the output of top, web-based retrieval of the -package, year 2000 information, and pointers to the mailing list. - - 3. "Is there a mailing list for top?" - -The official list for announcements is "top-announce@groupsys.com". -This list is managed by "majordomo@groupsys.com". Announcements of -importance to all top users will be sent to this list, including new -releases, availability of beta test versions, emergency revisions and -patches, etc. Anyone is welcome to join top-announce. This is a -read-only list. The list of subscribers will not (intentionally) be -made available, and postings to the list are limited. - -In addition, there is a top developers mailing list that is used by -beta testers and other people who help me port the program to various -machines. Membership to this list is solely at my discretion. If you -feel qualified to act as a beta tester, or if you are doing development -work on top (such as porting to a new platform), you may submit a -request by sending a message to "top-spinners-request@groupsys.com" -containing the word "subscribe". I will contact you within a few days, -as my schedule permits. - - 4. "What about Year 2000 compliance"? - -Top should not experience any problems with the transition to the year -2000. A full statement concerning top and the year 2000 can be found -in the file "Y2K" included with the distribution. - - - 5. "Why does it take so long for a new version of top to go through the - beta test process?" - -This is completely my fault. I have just not had the time to give top -the attention it deserves. I thank everyone for their patience, and I -hope that with the recent changes in the direction of my career that I -can spend more time on this. - - 6. "Top is not written in ANSI C. Do you ever plan to change that?" - -Top predates ANSI C by about 5 years. Yeah, it'll get "fixed" eventually. -Probably in 3.6. - - -CONFIGURING - - 7. "Configure said that it saw /proc and is recommending that I install top - setuid root. Is there any way around this? Is it safe?" - -There is no way around it. Complain to POSIX. Every effort has been made -to make top a secure setuid program. However, we cannot guarantee that -there are no security problems associated with this configuration. The -places where top is most vulnerable are the builtin kill and renice -commands. There is no internal top command that causes top to start a shell -as a subprocess. Some SVR4 systems may contain a bug that enables a user to -renice his own processes downward (to lower nice values that are more -favorable for the process). This problem has been fixed for the Solaris 2.x -modules, but may still exist in others. We will hopefully fix this up in -the next release. - - 8. "Why is Configure a c-shell script? I thought c-shell scripts were - evil?" - -They are. :-) I'll probably be rewriting the Configure script for the -next release, or switching to something like Gnu configure. - - -COMPILING - - 9. "We just upgraded our operating system to a new version and top broke. - What should we do?" - -Recompile it. Top is very sensitive to changes in internal kernel data -structures. It is not uncommon for a new version of the operating system to -include changes to kernel data structures. - - -RUNNING - -10. "I just finished compiling top and it works fine for root, but when - I try to run it as a regular user it either complains about files - it can't open or it doesn't display all the information it should. - Did I do something wrong?" - -Well, you're just not done. On many operating systems today, access to -many of the kernel memory devices and other system files is restricted to -either root or a particular group. The Configure script figures this out -(usually) and makes sure that the "intsall" rule in the Makefile will -install top so that anyone can run it successfully. However, you have to -*install* it first. Do this with the command "make install". - -11. "Top is (not) displaying idle processes and I don't (do) want it to." - -This default has only changed about a dozen times, and I finally got tired -of people whining about it. Go read the manual page for the current version -and pay special attention to the description of the "TOP" environment -variable. - -12. "We have so much memory in our machine that the memory status display - (the fourth line) ends up being longer than 80 characters. This - completely messes up top's output. Is there a patch?" - -Most modules have been changed to use new memory formatting functions which -will display large values in terms of megabytes instead of kilobytes. This -should fix all occurences of this problem. If you encounter a system where -this large memory display overflow is still occurring, please let me know -(send mail to ). Also note that newer versions of top can -use columns beyond 79, and understand window resizes. So you can always -make your window bigger. - -13. "I tried to compile top with gcc and it doesn't work. I get - compilation errors in the include files, or I get an executable that - dumps core, or top displays incorrect numbers in some of the displays. - What's wrong?" - -Gnu CC likes very much to use its own include files. Not being a gcc -expert, I can't explain why it does this. But I can tell you that if -you upgrade your operating system (say from Solaris 2.4 to Solaris -2.5) after installing gcc, then the include files that gcc uses will -be incorrect, especially those found in the "sys" directory. Your -choices are: (1) rebuild and reinstall the "standard" include files -for gcc (look for scripts in the distribution called "fixincludes" and -"fixinc.svr4"), (2) compile machine.c with "CFLAGS=-I/usr/include" -then make the rest of the object files normally, or (3) use "cc". -Solaris 2.6 users should also consult FAQ #20. - -14. "The cpu state percentages are all wrong, indicating that my machine is - using 95% system time when it is clearly idle. What's wrong?" - -This can happen if you compiled with gcc using the wrong include files. -See the previous question. - - -SUNOS PROBLEMS - -15. "I tried compiling top under SunOS version 4.1.x and it got compile time - errors. Is there a patch?" - -If you try compiling top in a "System V environment" under SunOS (that is, -/usr/5bin is before /usr/bin on your path) then the compilation may fail. -This is mostly due to the fact that top thinks its being compiled on a -System V machine when it really isn't. The only solution is to put /usr/bin -and /usr/ucb before /usr/5bin on your path and try again. - - -SVR4-derived PROBLEMS - -16. "When I run top on my SVR4-derived operating system, it displays all - the system information at the top but does not display any process - information (or only displayes process information for my own - processes). Yet when I run it as root, everything works fine." - -Your system probably uses the pseudo file system "/proc", which is by -default only accessible by root. Top needs to be installed setuid root on -such systems if it is going to function correctly for normal users. - - -SOLARIS PROBLEMS - -17. "Under Solaris 2, when I run top as root it only shows root processes, - or it only shows processes with a PID less than 1000. It refuses to - show anything else. What do I do?" - -You probably compiled it with /usr/ucb/cc instead of the real C compiler. -/usr/ucb/cc is a cc front end that compiles programs in BSD source-level -compatability mode. You do not want that. Make sure that /usr/ucb is not -on your path and try compiling top again. - -18. "Under Solaris 2, I compiled top using what I am sure is the correct - compiler but when I try to run it it complains about missing dynamic - libraries. What is wrong?" - -Check to see if you have LD_LIBRARY_PATH defined in your shell. If you do, -make sure that /usr/ucblib is not on the path anywhere. Then try compiling -top again. - -19. "Under Solaris 2, when I try to run top it complains that it can't open - the library "libucb.so.1". So I changed the LIBS line in m_sunos5.c - to include -R/usr/ucblib to make sure that the dynamic linker will look - there when top runs. I figured this was just an oversight. Was I - right?" - -No, you were not right. As distributed, top requires NO alterations -for successful compilation and operations under any release of Solaris -2. You probably compiled top with /usr/ucb/cc instead of the real C -compiler. See FAQ #10 for more details. - -20. "When I try to compile top under Solaris 2.6 using gcc I get compile - time errors. There appear to be problems with the include files, - such as 'u_rlimit has incomplete type' and/or 'u_saved_rlimit has - incomplete type'. I've already run fixinc.svr4 as per FAQ #13. - Why didn't that fix it?" - -Only top versions 3.5 and later are compatible with Solaris 2.6. Make -sure you are using the most up-to-date version. Earlier beta release -copies of version 3.5 had additional problems when compiled with gcc. -Retrieve the official version 3.5 (non-beta) release from one of the -sites listed in FAQ #1 or FAQ #2. - - -SCO PROBLEMS - -21. "When I try to run Configure, it complains about a syntax error." - -Some versions of SCO's csh do not understand the syntax "$<". Earlier -releases of top depended on this syntax to read input from the installer's -terminal during the installation process. Version 3.5 fixes this. - - -SVR42 PROBLEMS - -22. "The memory display doesn't work right. Why?" - -This is a known bug with the svr42 module. The problem has been traced down -to a potential bug in the "mem" driver. The author of the svr42 module is -working on a fix. - - -STILL STUCK - -23. I'm still stuck. To whom do I report problems with top?" - -The most common problems are caused by top's sensitivity to internal kernel -data structures. So make sure that you are using the right include files, -and make sure that you test out top on the same machine where you compiled -it. Sun's BSD Source Compatability Mode is also a common culprit. Make -sure you aren't using either /usr/ucb/cc or any of the libraries in -/usr/ucblib. Finally, make sure you are using the correct module. If there -does not appear to be one appropriate for your computer, then top probably -will not work on your system. - -If after reading all of this file and checking everything you can you are -still stuck, then send mail to "wnl@groupsys.com". I will answer your mail -when I have time. Please bear with me in that regard! If it looks like the -problem is machine-specific, I will forward the report along to the module's -author. If you would like to converse directly with the module author, the -authors' names are listed at the beginning of the module .c file in the -"machine" directory. Index: contrib/top/INSTALL =================================================================== --- contrib/top/INSTALL +++ contrib/top/INSTALL @@ -1,166 +1,54 @@ TOP - Version 3.5 + Version 3.8beta1 William LeFebvre and a cast of many INSTALLATION -Configuration and installation of top is very straightforward. After -unpacking the sources, run the script "Configure". It will present you -with a series of questions, all of which should be explained in the -presentation. After you have answered all the questions, "Configure" will -perform all the necessary configuration. Once this is finished, type -"make install". Make will compile the sources then install the resulting -executable and manual page in the appropriate places. - -The most difficult step in the configuration is the choice of an -appropriate machine-specific module. The Configure script gives you a -list of choices complete with brief descriptions of when each choice is -appropriate. Each module is contained in a separate c file in the -directory "machine". The module contains all of the machine-specific code -that makes top work correctly on the architecture in question. All of the -code in the top-level directory is machine-independent (or at least -strives to be). Hints for some module choices that are not obvious are -given at the end of this file. - -The first comment in each c file in that directory contains the synopsis -AND a detailed description of the machines for which that module is -appropriate. It also contains a list of authors for that module. If you -are really stumped in this choice, use grep to find your machine -manufacturer's name or operating system name in machine/*.c. If you still -can't find one that is appropriate, then chances are very good that one -hasn't been written yet. If that is the case, then you are out of luck. - -HANDLING MULTIPLE ARCHITECTURES - -If you need to recompile top for a different architecture (that is, using -a different module) you need to reconfigure top. A short cut is available -to make this a little easier. If all of your previous answers to the -configuration questions (except for the module name of course) are -adequate for the new architecture, then you can just use the command -"Configure ". The configuration script will reconfigure top -using the new module and all the answers you gave last time. It will -finish with a "make clean". Once that completes, type "make install" -and make will compile the sources and do the installation. - -HANDLING MULTIPLE OS VERSIONS - -By far the most frequently received bug report for top is something like -this: "We just upgraded our operating system to version 99.9.9.9 and top -broke. What should we do?" The simple answer is "recompile". - -Top is very sensitive to changes in internal kernel data structures -(especially the proc and user structures). Some operating systems -(especially SunOS) are notorious for changing these structure in every -minor release of the OS. This means that a top executable made under one -version of the OS will not always work correctly (if even at all) under -another version. This is just one of those tough facts of life. There is -really no way around it. - -To make life even worse, some operating systems (SunOS again) will use -slightly different proc and user structures on different models. For -example, "top" built on a SparcStation 2 will not run correctly on a -SparcStation 10, even if they are both running SunOS 4.1.3. These -unfortunate circumstances make maintaining top very difficult, especially -in an environment that runs several different versions of the same -operating system. - -But there is hope. If your operating system has a properly functioning -"uname" command then you can handle this problem rather gracefully. -Included in the distribution is a shell file called "metatop". All this -shell file does is: - - exec top-`uname -m`-`uname -r` "$@" - -So when you run this script, it execs a filename that is unique to your -specific machine architecture and your OS revision number. - -To use "metatop", do the following: - - . on any machine, run Configure and choose the module that is - appropriate for the machine - . for all machines which use the same module: - . group machines according to machine architecture AND OS - revision number (i.e.: sun4-4.1.1, sun4c-4.1.1, sun4c-4.1.2, - sun4-4.1.3, sun4c-4.1.3, sun4m-4.1.3, ...) - . for each group, choose one machine from that group and on it - run "make clean; make installmeta". - - -The "installmeta" rule in the makefile will insure that top is compiled, -install the shell file "metatop" as "top", then install the executable -"top" with a name appropriate to the machine architecture and OS revision. - - -HINTS FOR CHOOSING THE CORRECT MODULE: - -SOLARIS 2.x - -All versions of Solaris will now work with the module sunos5. Version -specific modules (such as sunos54) no longer exist. - - -SUNOS 4.x AND MULTIPROCESSOR ARCHITECTURES - -First, we need to be speaking the same language: - -sun4 a regular sparc sun 4 architecture machine (sparc station 1, - sparc station 2, IPC, SLC, etc.) - -sun4m a multiprocessor sparc (Sparc 10, 4/670, 4/690) - -I intended to write the sunos4 module so that an executable compiled on a -sun4m machine would work correctly on a sun4 machine. Unfortunately my -experiments indicate that this cannot be done. It turns out that the user -structure is so different between these two architectures that nothing -short of a serious hack will make the same executable work correctly on -both machines. I recommend that you use the separate module "sunos4mp" -when making an executable for a sun4m architecture, and use "sunos4" when -making an executable for sun4 or sun4c architectures. - -DIGITAL UNIX V4.0 - -This is the successor to DECOSF/1. Use the module decosf1. - -SOLBOURNE OPERATING SYSTEM (OS/MP) - -If you are running OS/MP version 4.1A, then use the module "osmp4.1a". - -If you are running a version of OS/MP OLDER than 4.1A (that is, one -of its predecessors), use the module "sunos4". - -If you are running OS/MP 4.1B or LATER, use the module "sunos4mp". - -HP/UX OPERATING SYSTEM - -The module hpux8 works on all version 8 systems. Some say that it works -with version 9 as well, but one user did send me a separate module for -version 9. This module has only been tested on series 800 machines. I -would recommend the following for those running version 9: try hpux9 and -if it doesn't work then try hpux8. If neither work, then send mail to me -and/or the modules' authors. Another note: we have a model 730 supposedly -running version 9.01. The module hpux9 did not compile successfully, but -the module hpux8 worked fine. The module hpux10 works on all revisions of -HP/UX 10 except 10.10, where HP removed the definition of the proc structure -from the system include files. - -NET/2 386BSD SYSTEMS - -If your version of the operating system has patchkit 2.4 installed, -then you will need to modify machine/m_386bsd.c and uncomment the -definition of PATCHED_KVM. This patchkit makes what more than a few -people believe to be a wholly unnecessary patch to the way the kvm -routines work. - -A/UX SYSTEMS - -There is a module for A/UX 3.0 and 3.1. Whether or not it works for -any other version is not known. Proceed at your own risk. - -Although AUX does not generally have a renice systemcall, it can be -implemented by tweeking kernel memory. The flag IMPLEMENT_SETPRIORITY -controls the inclusion of this code. It is off be default. While -such a simple hack should not be difficult to get right, USE THIS -FEATURE AT YOUR OWN RISK! - +Configuration and installation of top is easy. Top version 3.6 +comes with a configure script generated by gnu autoconf. After +unpacking the tar file, simply run "./configure". The script will +automatically perform a series of checks on the system and determine +what settings are appropriate for the Makefile and certain include +files. Once configure completes, simply type "make install" and +top will be compiled and installed. By default, the installation +location is /usr/local/bin. You can change the destination location +with the --prefix option to configure. + +In addition to the standard options, top's configure script supports +the following: + + --with-module=name + + Force the use of a particular module. Modules are located + in the subdirectory "machine". A module's name is derived + from the file's basename without the leading "m_". + + --with-ext=name + + Compile with the extension "name", found in the subdirectory + "ext". At the present time, there are no extensions in the + standard distribution. + + --enable-debug + --disable-debug + + Default off. Include debugging output in the compilation, + which can be seen with the -D switch. + + --enable-color + --disable-color + + Default on. Include code that allows for the use of color + in the output display. Use --disable-color if you do not + want this feature compiled in to the code. The configure + script also recognizes the spelling "colour". + + --enable-kill + --disable-kill + + Default on. Include code that allows for renicing and sending + signals to processes from within top (the 'kill' and 'renice' + commands). Use --disable-kill if you do not want this feature + compiled in to the code. Index: contrib/top/LICENSE =================================================================== --- contrib/top/LICENSE +++ contrib/top/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 1984 through 2008, William LeFebvre +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + + * Neither the name of William LeFebvre nor the names of other +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Index: contrib/top/Make.desc.X =================================================================== --- contrib/top/Make.desc.X +++ contrib/top/Make.desc.X @@ -1,24 +0,0 @@ -# Makefile for .desc files - -# This makefile is the prototype for "Make.desc", which is used by -# top's Configure script to build .desc files and the SYNOPSIS file. -# Configure then uses these files to ask appropriate questions. - -# Written by William LeFebvre, Group sys Consulting -# (formerly of Northwestern University and Rice University) - -# DO NOT EDIT "Make.desc"!!! Make changes to "Make.desc.X", -# then "make veryclean", then run "Configure". - -# The list of .desc files will be inserted after this next line: -DESCS=\ - -.SUFFIXES: .desc - -.c.desc: - sed -e '/^$$/,$$d' -e 's,^[/ *]*,,' $< > $@ - -all: SYNOPSIS - -SYNOPSIS: $(DESCS) - grep SYNOPSIS: $(DESCS) | sed -e 's@^machine/m_@@' -e 's@.desc:.[^:]*: *@:@' >SYNOPSIS Index: contrib/top/Makefile.X =================================================================== --- contrib/top/Makefile.X +++ contrib/top/Makefile.X @@ -1,117 +0,0 @@ -# Makefile for "top", a top 10 process display for Unix -# -# This makefile is for top, version 3 -# -# Written by William LeFebvre, Group sys Consulting -# (formerly of Northwestern University and Rice University) - -# DO NOT EDIT "Makefile"!!!! Make changes to "Makefile.X" and rerun -# Configure. - -# Executables (these should be obvious): - -SHELL = %shell% -CC = %cc% -AWK = %awk% -INSTALL = %install% - -# installation information: -# OWNER - name (or uid) for the installed executable's owner -# GROUP - group name (or gid) for the installed executable's group -# MODE - mode for the installed executable (should start with a 0) -# BINDIR - directory where the executable should live -# MANDIR - directory where the manual page should live -# MANEXT - installed man pages end in .$(MANEXT) -# MANSTY - "man" or "catman" depending on what's to be installed -# SIGNAL - or ; the one with signal definitions -# TROFF - most appropriate troff command - -OWNER = %owner% -GROUP = %group% -MODE = %mode% -BINDIR = %bindir% -MANDIR = %mandir% -MANEXT = %manext% -MANSTY = %mansty% -SIGNAL = %signal% - -# Values for the two defaults in "top": -# TOPN - default number of processes to display -# DELAY - default delay between updates -# -# set TOPN to -1 to indicate infinity (so that top will display as many -# as the screen will hold). - -TOPN = %topn% -DELAY = %delay% - -CFILES = top.c commands.c display.c screen.c username.c \ - utils.c version.c getopt.c machine.c -OBJS = top.o commands.o display.o screen.o username.o \ - utils.o version.o getopt.o machine.o - -CDEFS = %cdefs% -LIBS = %libs% -TERMCAP = %termcap% -MATH = %math% - -CFLAGS = %cflgs% $(CDEFS) -LINTFLAGS = -x $(CDEFS) - -all: Makefile top.local.h top - -Makefile: Makefile.X - @echo 'You need to run the script "Configure" before running "make".' - exit 10 - -top.local.h: top.local.H - @echo 'You need to run the script "Configure" before running "make".' - exit 10 - -top: $(OBJS) - rm -f top - $(CC) $(CDEFS) -o top $(OBJS) $(TERMCAP) $(MATH) $(LIBS) - -lint: sigdesc.h - $(LINT) $(LINTFLAGS) $(CFILES) - -# include file dependencies -top.o: boolean.h display.h screen.h top.h top.local.h utils.h machine.h -commands.o: boolean.h sigdesc.h top.h utils.h -display.o: boolean.h display.h layout.h screen.h top.h top.local.h utils.h -screen.o: boolean.h screen.h -utils.o: top.h -version.o: top.h patchlevel.h -username.o: top.local.h utils.h - -# when compiling machine.c, include os revision definition -machine.o: machine.c top.h machine.h utils.h - $(CC) "%osrev%" $(CFLAGS) -c machine.c - -# automatically built include file -sigdesc.h: sigconv.awk $(SIGNAL) - $(AWK) -f sigconv.awk $(SIGNAL) >sigdesc.h - -clean: - rm -f *.o top core core.* sigdesc.h - -veryclean: clean - rm -f Make.desc machine/*.desc .defaults top.tar SYNOPSIS Makefile top.local.h top.1 machine.c prime - -install: top top.1 install-top install-$(MANSTY) - -install-top: - $(INSTALL) -o $(OWNER) -m $(MODE) -g $(GROUP) top $(BINDIR) - -install-man: - $(INSTALL) top.1 $(MANDIR)/top.$(MANEXT) - -install-catman: - tbl top.1 | nroff -man > $(MANDIR)/top.$(MANEXT) - -installmeta: top top.1 - $(INSTALL) -o $(OWNER) -m 755 -g $(GROUP) metatop $(BINDIR)/top - @echo $(INSTALL) -o $(OWNER) -m $(MODE) -g $(GROUP) top $(BINDIR)/top-`uname -m`-`uname -r` - @$(INSTALL) -o $(OWNER) -m $(MODE) -g $(GROUP) \ - top $(BINDIR)/top-`uname -m`-`uname -r` - $(INSTALL) top.1 $(MANDIR)/top.$(MANEXT) Index: contrib/top/Porting =================================================================== --- contrib/top/Porting +++ contrib/top/Porting @@ -3,8 +3,6 @@ This is still a preliminary document. Suggestions for improvement are most welcome. -My address is now "wnl@groupsys.com". - Before you embark on a port, please send me a mail message telling me what platform you are porting top to. There are three reasons for this: (1) I may already have a port, (2) module naming needs to be @@ -163,3 +161,69 @@ discuss the details. I want to keep such changes as general as possible. +-------- + +Changes were made to the module interface between 3.5 and 3.6. Here are +the changes that need to be made to port a 3.5 module to 3.6: + +The array that stores memory statistics and is passed back in the system +information structure as "memory" must now be an array of (signed) longs. +This was done to more easily accomodate systems that have gigabytes of +memory. Since the numbers are supposed to be kilobytes, a long can still +represent up to 2 terabytes. Look for "int memory_stats[X]" (where "X" +is some arbitrary number) and change it to "long memory_stats[X]". If +the module support reporting swap information on a separate line, then +its "swap_stats" array also needs to be an array of longs. + +The argument to proc_owner should be an int, as in "int pid". When it is +used in proc_owner it should be cast as necessary. Many operating systems +will require it to be cast to a pid_t before being compared to the appropriate +element in the proc structure. + +In the function format_next_process, the last argument in the main call +to sprintf is the string that contains the command for the process. +Make sure that this last argument is enclosed in a call to "printable". +For example: "printable(MPP(pp, p_comm))". + +The third argument to "get_process_info" needs to be changed to an integer, +typically "int compare_index". The call to qsort in get_process_info may +be guarded by "if (compare != NULL)". If it is, remove the if statement. + +The other changes to get_process_info depends on whether or not the module +supports multiple sort orders. + +To support multiple keys: + +Create an array int (*proc_compares[])() and assign to it the list of +comparison functions, NULL terminated. For example: + +int (*proc_compares[])() = { + compare_cpu, + compare_size, + compare_res, + compare_time, + NULL }; + +In get_process_info there is a call to qsort which uses one of the +functions in proc_compares. It should be changed so that its fourth +argument is "proc_compares[compare_index]". + +If the module contains the function "proc_compare", it should be removed. + +There should also be a NULL-terminated array of strings which list the names +for the sort keys, for example: + +char *ordernames[] = +{"cpu", "size", "res", "time", NULL}; + +To indicate that this module supports multiple sort keys, add the following +line in machine_init: + + statics->order_names = ordernames; + +If there is no support for multiple keys: + +Leave statics->order_names alone and call the comparison function of +your choice in get_process_info, ignoring the third argument. + + Index: contrib/top/README =================================================================== --- contrib/top/README +++ contrib/top/README @@ -1,5 +1,5 @@ TOP - Version 3.5 + Version 3.8beta1 William LeFebvre and a cast of dozens @@ -21,46 +21,47 @@ reorganized in a major way to make it easy to port to other platforms. All system dependent code is now contained in one file. -Top now includes a configuration script called "Configure". It helps -the installer choose the correct parameters for this particular -installation. This script MUST be run before attempting to compile top. - -Top requires read access to the memory files "/dev/kmem" and "/dev/mem" -as well as the system image "/vmunix". Some installations have these -files protected from general access. These sites would have to install -this program in the same way that programs such as "ps" are installed. -In addition, on those Unix variants that support the proc filesystem -(such as SVR4 and Solaris 2), top requires read access to all the files -in /proc: typically dictating that top be installed setuid to root. +Starting with version 3.6, top includes a "configure" script generated +by Gnu's autoconf. This script MUST be run before attempting to +compile top. It will explore the system and generate approriate +contents for Makefile, config.h, and top.1. + +On some systems, top requires read access to the memory files +"/dev/kmem" and "/dev/mem" as well as the system's kernel image. Most +installations have these files protected from general access. These +sites would have to install this program in the same way that programs +such as "ps" are installed. On most systems with a /proc file system, +top will try to read everything it can from /proc, but may need extra +permissions to do so. The configure script will determine the +permissions needed by the top binary, and a "make install" as root +will get the binary installed correctly. Sometimes this requires that +the binary be installed with set-group-id privileges and, in rare +cases, set-user-id to root. CAVEAT: version 3 of top has internal commands that kill and renice processes. Although I have taken steps to insure that top makes appropriate checks with these commands, I cannot guarantee that these -internal commands are totally secure. IF YOU INSTALL top as a SETUID -program, you do so AT YOUR OWN RISK! I realize that some operating -systems will require top to run setuid, and I will do everything I can -to make sure that top is a secure setuid program. - -Configure will ask you to input values for certain parameters. Before -each parameter, Configure will display a description of what the -parameter does. Read the description and choose an appropriate value. -Sometimes a default will appear in brackets. Typing just return will -choose the default. - -System support now takes the form of "modules". Adding support for -a different architecture requires only adding a module. Configure -asks which module to use when it is configuring top. See the file -"Porting" for a description of how to write your own module. +internal commands are totally secure. IF YOU INSTALL top SET-USER-ID +TO ROOT, YOU DO SO AT YOUR OWN RISK! I realize that some operating +systems will require top to run setuid root, and I will do everything +I can to make sure that top is a secure setuid program. + +System support now takes the form of "modules". Adding support for a +different architecture requires only adding a module. These modules +are contained in the subdirectory "machine". The "configure" script +automatically determines which module is approproate. However, it may +not be able to determine what the correct module is. This can happen +either because it doesn't know about the system or there is no module +to support the system. In the former case, if you know which module +to use, you can force "configure" to choose a particular module with +the option "--with-module". For example, if you want to force the use +of the svr4 module (which appears as "machine/m_svr4.c") then use +"configure --with-module=svr4" to generate the correct Makefile. See +the file "Porting" for a description of how to write your own module. To compile and install "top", read the file "INSTALL" and follow the directions and advice contained therein. -Once you have created a binary for one particular type of machine, you -can reconfigure for another type with "./Configure modulename" where -"modulename" is replaced with the appropriate module name. All other -parameter values are kept the same. Note that in some cases this may -not be appropriate. - If you make any kind of change to "top" that you feel would be beneficial to others who use this program, or if you find and fix a bug, please send me the change. @@ -69,33 +70,50 @@ answers to the most commonly asked questions about the configuration, installation, and operation of top. +COLOR + +Version 3.6 incorporated the idea of using ANSI color sequences to +enhance information on the screen. By default, no color is used. But +you can configure the use of color through the environment variable +TOPCOLORS (or, for compatibility, TOPCOLOURS). The interface is +identical to the one first implemented by chris@spang.uk.eu.org, but +the implementation is entirely different. The option -C can be used +to diable the feature entirely. + +Any information at the top of the screen can be enhanced with color. +However, due to implementation difficulties, the per-process area +cannot be color-enhanced. A complete description of color support can +be found in the man page. References for ANSI color codes can be +found all over the Internet, but if you want a handy reference, look +in color.h. + AVAILABILITY -The latest version of "top" is now being made available via anonymous -FTP from the host "ftp.groupsys.com" in the directory "/pub/top". -Additional modules will be made available in the directory -"/pub/top/m". The site "eecs.nwu.edu" will continue to house copies -of the distribution as well. - -Here are HTML links for the four best "top" archive sites: - -Top archive (groupsys.com) -Top archive (eecs.nwu.edu) - Top mirror (dgim.doc.ca) -Top mirror (uiuc.edu) - -New releases will be posted to comp.sources.unix as they become -available. Sites which arhive that newsgroup will also contain copies -of the distribution. - -Announcements about availability will be made to the mailing list -"top-announce@groupsys.com". This is an open list maintained by -majordomo. To join the list, send a message containing the word -"subscribe" to "top-announce-request@groupsys.com". Addresses of -subscribers to this list are kept confidential and will never be used -for any purpose other than as recipients of announements concerning -this software. +Note that top is now a sourceforge project! Its project name is +"unixtop" and you can access its project page here: + +http://sourceforge.net/projects/unixtop + +On the project page you can find more information and access the +official bug and feature request trackers. If you find a bug, +want to request a feature, or need help, please submit a request +to the appropriate tracker on sourceforge. Thank you. + +Subversion access is also provided by Sourceforge. If Subversion is +installed on your system you can check out the project with the +following command: + + svn co https://svn.sourceforge.net/svnroot/unixtop unixtop + +There is also a web site dedicated to the project, and it is here: + +http://www.unixtop.org + +The latest version of "top" is available as a download through +sourceforge. Start here to access the downloadable files: + +http://sourceforge.net/project/showfiles.php?group_id=72892 KNOWN PROBLEMS: @@ -115,6 +133,16 @@ sending me a bug report. Look in the gcc source distribution for the shell script "fixincludes". +MacOS X + +Since I don't have full time root access to a MacOS X system I cannot +provide effective support for the platform. MacOS X uses Mach, and it +is very difficult to extract accurate system and process information +from the system. It takes a lot of trial and error, along with root +access. I have included the most up-to-date version of the macosx module +in the distribution, but I do not claim that it works. If you want to +try to use it, you can configure with "./configure --with-module=macosx". + HP/UX 10.10 In their infinite wisdom, the folks at HP have decided that mere mortals @@ -127,31 +155,6 @@ obtain a sufficiently complete definition of "struct proc" at some point in the near future. Stay tuned. -DIGITAL UNIX 4.0 (DECOSF/1 V4.0) - -A user has reported that idle processes are not displayed regardless -of the flags used when invoking top. We have not had time to track -this problem down. - -DECOSF/1 V3.0 - -There is a bug either in the module, in utils.c, or in DEC's optimizer that -is tickled by the decosf1 module when compiled under V3.0 (and perhaps -earlier versions). Top compiled using DEC's compiler with optimization -will consistently produce a segmentation fault (in format_next_process -while calling sprintf). To work around this problem, either compile top -with gcc or turn off optimization (compile without -O). We think that -one of the bugs fixed in utils.c fixed this problem as well, but we are -not certain. - - -System V R 4.2 - -Load average and memory displays do not work. The problem has been -traced down to a potential bug in the "mem" driver. The author -of the svr42 module is working on a fix. - - GRATITUDE @@ -159,34 +162,30 @@ on so many platforms. Without these people, top would not be what it is. Here is a partial list of contributors and other individuals. - Robert Boucher - Marc Cohen - David Cutter - Casper Dik - Charles Hedrick - Andrew Herbert - Jeff Janvrin - Torsten Kasch - Petri Kutvonen - William L. Jones - Tim Pugh - Steve Scherf - Phillip Wu + Robert Boucher, Marc Cohen, David Cutter, Casper Dik, + Charles Hedrick, Andrew Herbert, Jeff Janvrin, Torsten Kasch, + Petri Kutvonen, William L. Jones, Tim Pugh, Steve Scherf, + Phillip Wu (My apologies if I missed anyone.) +LICENSE + +Top is distributed free of charge under the same terms as the BSD +license. For an official statement, please refer to the file "LICENSE" +which should be included with the source distribution. + + AUTHOR - William LeFebvre - Group sys Consulting - wnl@groupsys.com +If you wish to contact me, please send a message to the sourceforge +username "wnl". + William LeFebvre U.S. Mail address: William LeFebvre - Group sys Consulting 11585 Jones Bridge Road - Suite 420-139 - Alpharetta, GA 30022 - (770) 813-3224 + Suite 420 PMB 139 + Alpharetta, GA 30202 Index: contrib/top/Y2K =================================================================== --- contrib/top/Y2K +++ contrib/top/Y2K @@ -22,5 +22,5 @@ THERE IS ABSOLUTELY NO WARRANTY PROVIDED WITH THIS SOFTWARE. -Please see the contents of the file "DISCLAIMER" for further +Please see the contents of the file "LICENSE" for further information. Index: contrib/top/color.h =================================================================== --- contrib/top/color.h +++ contrib/top/color.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top - a top users display for Unix + * + * Definition of the color interface. + */ + +#ifndef _COLOR_H_ +#define _COLOR_H_ + +#define COLOR_ANSI_SLOTS 20 + +int color_env_parse(char *env); +int color_tag(char *tag); +int color_test(int tagidx, int value); +char *color_setstr(int color); +void color_dump(FILE *f); +int color_activate(int i); + + +/* + * These color tag names are currently in use + * (or reserved for future use): + * + * cpu, size, res, time, 1min, 5min, 15min, host + */ + +/* + * Valid ANSI values for colors are: + * + * 0 Reset all attributes + * 1 Bright + * 2 Dim + * 4 Underscore + * 5 Blink + * 7 Reverse + * 8 Hidden + * + * Foreground Colours + * 30 Black + * 31 Red + * 32 Green + * 33 Yellow + * 34 Blue + * 35 Magenta + * 36 Cyan + * 37 White + * + * Background Colours + * 40 Black + * 41 Red + * 42 Green + * 43 Yellow + * 44 Blue + * 45 Magenta + * 46 Cyan + * 47 White + */ + +#endif /*_COLOR_H_ */ Index: contrib/top/color.c =================================================================== --- contrib/top/color.c +++ contrib/top/color.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top users/processes display for Unix + * Version 3 + */ + +/* + * This file handles color definitions and access for augmenting + * the output with ansi color sequences. + * + * The definition of a color setting is as follows, separated by + * colons: + * + * tag=minimum,maximum#code + * + * "tag" is the name of the value to display with color. + * + * "minimum" and "maximum" are positive integer values defining a range: + * when the value is within this range it will be shown with the + * specified color. A missing value indicates that no check should be + * made (i.e.: ",25" is n <= 25; "25,50" is 25 <= n <= 50; and "50," + * is 50 <= n). + * + * "code" is the ansi sequence that defines the color to use with the + * escape sequence "[m". Semi-colons are allowed in this string to + * combine attributes. + */ + +#include "os.h" +#include "display.h" +#include "message.h" +#include "color.h" +#include "utils.h" + +typedef struct color_entry { + char *tag; + int min; + int max; + char color; + struct color_entry *next; + struct color_entry *tagnext; +} color_entry; + +static color_entry *entries = NULL; + +static color_entry **bytag = NULL; +static char **bytag_names = NULL; +static int totaltags = 0; +static int tagcnt = 0; +static int color_off = 0; + +static char **color_ansi = NULL; +static int num_color_ansi = 0; +static int max_color_ansi = 0; + +static int +color_slot(char *str) + +{ + int i; + + for (i = 0; i < num_color_ansi; i++) + { + if (strcmp(color_ansi[i], str) == 0) + { + return i; + } + } + + /* need a new slot */ + if (num_color_ansi >= max_color_ansi) + { + max_color_ansi += COLOR_ANSI_SLOTS; + color_ansi = (char **)realloc(color_ansi, max_color_ansi * sizeof(char *)); + } + color_ansi[num_color_ansi] = strdup(str); + return num_color_ansi++; +} + +/* + * int color_env_parse(char *env) + * + * Parse a color specification "env" (such as one found in the environment) and + * add them to the list of entries. Always returns 0. Should only be called + * once. + */ + +int +color_env_parse(char *env) + +{ + char *p; + char *min; + char *max; + char *str; + int len; + color_entry *ce; + + /* initialization */ + color_ansi = (char **)malloc(COLOR_ANSI_SLOTS * sizeof(char *)); + max_color_ansi = COLOR_ANSI_SLOTS; + + /* color slot 0 is always "0" */ + color_slot("0"); + + if (env != NULL) + { + p = strtok(env, ":"); + while (p != NULL) + { + if ((min = strchr(p, '=')) != NULL && + (max = strchr(min, ',')) != NULL && + (str = strchr(max, '#')) != NULL) + { + ce = (color_entry *)malloc(sizeof(color_entry)); + len = min - p; + ce->tag = (char *)malloc(len + 1); + strncpy(ce->tag, p, len); + ce->tag[len] = '\0'; + ce->min = atoi(++min); + ce->max = atoi(++max); + ce->color = color_slot(++str); + ce->next = entries; + entries = ce; + } + else + { + if (min != NULL) + { + len = min - p; + } + else + { + len = strlen(p); + } + message_error(" %.*s: bad color entry", len, p); + } + p = strtok(NULL, ":"); + } + } + return 0; +} + +/* + * int color_tag(char *tag) + * + * Declare "tag" as a color tag. Return a tag index to use when testing + * a value against the tests for this tag. Should not be called before + * color_env_parse. + */ + +int +color_tag(char *tag) + +{ + color_entry *entryp; + color_entry *tp; + + /* check for absurd arguments */ + if (tag == NULL || *tag == '\0') + { + return -1; + } + + dprintf("color_tag(%s)\n", tag); + + /* initial allocation */ + if (bytag == NULL) + { + totaltags = 10; + bytag = (color_entry **)malloc(totaltags * sizeof(color_entry *)); + bytag_names = (char **)malloc(totaltags * sizeof(char *)); + } + + /* if we dont have enough room then reallocate */ + if (tagcnt >= totaltags) + { + totaltags *= 2; + bytag = (color_entry **)realloc(bytag, totaltags * sizeof(color_entry *)); + bytag_names = (char **)realloc(bytag_names, totaltags * sizeof(char *)); + } + + /* initialize scan */ + entryp = entries; + tp = NULL; + + /* look for tag in the list of entries */ + while (entryp != NULL) + { + if (strcmp(entryp->tag, tag) == 0) + { + entryp->tagnext = tp; + tp = entryp; + } + entryp = entryp->next; + } + + /* we track names in the array bytag */ + bytag[tagcnt] = tp; + bytag_names[tagcnt] = strdup(tag); + + /* return this index number as a reference */ + dprintf("color_tag: returns %d\n", tagcnt); + return (tagcnt++); +} + +/* + * int color_test(int tagidx, int value) + * + * Test "value" against tests for tag "tagidx", a number previously returned + * by color_tag. Return the correct color number to use when highlighting. + * If there is no match, return 0 (color 0). + */ + +int +color_test(int tagidx, int value) + +{ + color_entry *ce; + + /* sanity check */ + if (tagidx < 0 || tagidx >= tagcnt || color_off) + { + return 0; + } + + ce = bytag[tagidx]; + + while (ce != NULL) + { + if ((!ce->min || ce->min <= value) && + (!ce->max || ce->max >= value)) + { + return ce->color; + } + ce = ce->tagnext; + } + + return 0; +} + +/* + * char *color_setstr(int color) + * + * Return ANSI string to set the terminal for color number "color". + */ + +char * +color_setstr(int color) + +{ + static char v[32]; + + v[0] = '\0'; + if (color >= 0 && color < num_color_ansi) + { + snprintf(v, sizeof(v), "\033[%sm", color_ansi[color]); + } + return v; +} + +void +color_dump(FILE *f) + +{ + color_entry *ep; + int i; + int col; + int len; + + fputs("These color tags are available:", f); + col = 81; + for (i = 0; i < tagcnt; i++) + { + len = strlen(bytag_names[i]) + 1; + if (len + col > 79) + { + fputs("\n ", f); + col = 2; + } + fprintf(f, " %s", bytag_names[i]); + col += len; + } + + fputs("\n\nTop color settings:\n", f); + + for (i = 0; i < tagcnt; i++) + { + ep = bytag[i]; + while (ep != NULL) + { + fprintf(f, " %s (%d-", ep->tag, ep->min); + if (ep->max != 0) + { + fprintf(f, "%d", ep->max); + } + fprintf(f, "): ansi color %s, %sSample Text", + color_ansi[(int)ep->color], + color_setstr(ep->color)); + fprintf(f, "%s\n", color_setstr(0)); + ep = ep -> tagnext; + } + } +} + +void +color_debug(FILE *f) + +{ + color_entry *ep; + int i; + + fprintf(f, "color debug dump\n"); + ep = entries; + while (ep != NULL) + { + fprintf(f, "%s(%d,%d): slot %d, ansi %s, %sSample Text", + ep->tag, ep->min, ep->max, ep->color, color_ansi[(int)ep->color], + color_setstr(ep->color)); + fprintf(f, "%s\n", color_setstr(0)); + ep = ep -> next; + } + + fprintf(f, "\ntags:"); + for (i = 0; i < tagcnt; i++) + { + fprintf(f, " %s", bytag_names[i]); + } + fprintf(f, "\n"); +} + +int +color_activate(int i) + +{ + if (i == -1) + { + color_off = !color_off; + } + else + { + color_off = !i; + } + return color_off; +} Index: contrib/top/commands.h =================================================================== --- contrib/top/commands.h +++ contrib/top/commands.h @@ -1,21 +1,42 @@ /* - * Top users/processes display for Unix - * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * Copyright (c) 2016, Randy Westlund - * - * $FreeBSD$ + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef COMMANDS_H -#define COMMANDS_H -void show_errors(void); -int error_count(void); -void show_help(void); +/* call specifications for commands.c */ -#endif /* COMMANDS_H */ +int command_process(globalstate *gstate, int cmd); + +/* returns from command routines */ +#define CMD_ERROR -1 +#define CMD_OK 0 +#define CMD_REFRESH 1 +#define CMD_UNKNOWN 2 +#define CMD_NA 3 Index: contrib/top/commands.c =================================================================== --- contrib/top/commands.c +++ contrib/top/commands.c @@ -1,17 +1,43 @@ /* - * Top users/processes display for Unix - * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* + * Top users/processes display for Unix + * Version 3 + */ + +/* * This file contains the routines that implement some of the interactive * mode commands. Note that some of the commands are implemented in-line * in "main". This is necessary because they change the global state of @@ -19,160 +45,54 @@ */ #include "os.h" - -#include -#include - #include -#include #include +#include #include +#include +#include +#include +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif -#include "commands.h" +#if defined(HAVE_DECL_SYS_SIGLIST) & defined(HAVE_STRCASECMP) +#define USE_SYS_SIGLIST +#endif + +#ifdef USE_SYS_SIGLIST +extern const char * const sys_siglist[]; +extern const char * const sys_signame[]; +#else #include "sigdesc.h" /* generated automatically */ +#endif #include "top.h" +#include "machine.h" +#include "globalstate.h" #include "boolean.h" +#include "color.h" +#include "commands.h" +#include "display.h" +#include "screen.h" +#include "username.h" #include "utils.h" -#include "machine.h" +#include "version.h" extern int errno; extern char *copyright; -/* imported from screen.c */ -extern int overstrike; - -int err_compar(); -char *err_string(); -static int str_adderr(char *str, int len, int err); -static int str_addarg(char *str, int len, char *arg, int first); - -/* - * show_help() - display the help screen; invoked in response to - * either 'h' or '?'. - */ - -void -show_help() - -{ - printf("Top version %s, %s\n", version_string(), copyright); - fputs("\n\n\ -A top users display for Unix\n\ -\n\ -These single-character commands are available:\n\ -\n\ -^L - redraw screen\n\ -q - quit\n\ -h or ? - help; show this text\n", stdout); - - /* not all commands are availalbe with overstrike terminals */ - if (overstrike) - { - fputs("\n\ -Other commands are also available, but this terminal is not\n\ -sophisticated enough to handle those commands gracefully.\n\n", stdout); - } - else - { - fputs("\ -C - toggle the displaying of weighted CPU percentage\n\ -d - change number of displays to show\n\ -e - list errors generated by last \"kill\" or \"renice\" command\n\ -H - toggle the displaying of threads\n\ -i or I - toggle the displaying of idle processes\n\ -j - toggle the displaying of jail ID\n\ -J - display processes for only one jail (+ selects all jails)\n\ -k - kill processes; send a signal to a list of processes\n\ -m - toggle the display between 'cpu' and 'io' modes\n\ -n or # - change number of processes to display\n", stdout); -#ifdef ORDER - if (displaymode == DISP_CPU) - fputs("\ -o - specify sort order (pri, size, res, cpu, time, threads, jid, pid)\n", - stdout); - else - fputs("\ -o - specify sort order (vcsw, ivcsw, read, write, fault, total, jid, pid)\n", - stdout); -#endif - fputs("\ -P - toggle the displaying of per-CPU statistics\n\ -r - renice a process\n\ -s - change number of seconds to delay between updates\n\ -S - toggle the displaying of system processes\n\ -a - toggle the displaying of process titles\n\ -t - toggle the display of this process\n\ -u - display processes for only one user (+ selects all users)\n\ -z - toggle the displaying of the system idle process\n\ -\n\ -\n", stdout); - } -} - -/* - * Utility routines that help with some of the commands. - */ - -char *next_field(str) - -register char *str; - -{ - if ((str = strchr(str, ' ')) == NULL) - { - return(NULL); - } - *str = '\0'; - while (*++str == ' ') /* loop */; - - /* if there is nothing left of the string, return NULL */ - /* This fix is dedicated to Greg Earle */ - return(*str == '\0' ? NULL : str); -} - -int -scanint(str, intp) - -char *str; -int *intp; - -{ - register int val = 0; - register char ch; - - /* if there is nothing left of the string, flag it as an error */ - /* This fix is dedicated to Greg Earle */ - if (*str == '\0') - { - return(-1); - } - - while ((ch = *str++) != '\0') - { - if (isdigit(ch)) - { - val = val * 10 + (ch - '0'); - } - else if (isspace(ch)) - { - break; - } - else - { - return(-1); - } - } - *intp = val; - return(0); -} +typedef struct command { + int ch; + int (*cmd_func)(globalstate *); + char *help; +} command; /* * Some of the commands make system calls that could generate errors. * These errors are collected up in an array of structures for later * contemplation and display. Such routines return a string containing an - * error message, or NULL if no errors occurred. The next few routines are - * for manipulating and displaying these errors. We need an upper limit on + * error message, or NULL if no errors occurred. We need an upper limit on * the number of errors, so we arbitrarily choose 20. */ @@ -186,97 +106,43 @@ static struct errs errs[ERRMAX]; static int errcnt; -static char *err_toomany = " too many errors occurred"; -static char *err_listem = - " Many errors occurred. Press `e' to display the list of errors."; /* These macros get used to reset and log the errors */ #define ERR_RESET errcnt = 0 -#define ERROR(p, e) if (errcnt >= ERRMAX) \ - { \ - return(err_toomany); \ - } \ - else \ +#define ERROR(p, e) if (errcnt < ERRMAX) \ { \ errs[errcnt].arg = (p); \ errs[errcnt++].errnum = (e); \ } /* - * err_string() - return an appropriate error string. This is what the - * command will return for displaying. If no errors were logged, then - * return NULL. The maximum length of the error string is defined by - * "STRMAX". + * err_compar(p1, p2) - comparison routine used by "qsort" + * for sorting errors. */ -#define STRMAX 80 - -char *err_string() +int +err_compar(const void *p1, const void *p2) { - register struct errs *errp; - register int cnt = 0; - register int first = Yes; - register int currerr = -1; - int stringlen; /* characters still available in "string" */ - static char string[STRMAX]; - - /* if there are no errors, return NULL */ - if (errcnt == 0) - { - return(NULL); - } - - /* sort the errors */ - qsort((char *)errs, errcnt, sizeof(struct errs), err_compar); - - /* need a space at the front of the error string */ - string[0] = ' '; - string[1] = '\0'; - stringlen = STRMAX - 2; + register int result; - /* loop thru the sorted list, building an error string */ - while (cnt < errcnt) + if ((result = ((struct errs *)p1)->errnum - + ((struct errs *)p2)->errnum) == 0) { - errp = &(errs[cnt++]); - if (errp->errnum != currerr) - { - if (currerr != -1) - { - if ((stringlen = str_adderr(string, stringlen, currerr)) < 2) - { - return(err_listem); - } - (void) strcat(string, "; "); /* we know there's more */ - } - currerr = errp->errnum; - first = Yes; - } - if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0) - { - return(err_listem); - } - first = No; + return(strcmp(((struct errs *)p1)->arg, + ((struct errs *)p2)->arg)); } - - /* add final message */ - stringlen = str_adderr(string, stringlen, currerr); - - /* return the error string */ - return(stringlen == 0 ? err_listem : string); + return(result); } /* * str_adderr(str, len, err) - add an explanation of error "err" to - * the string "str". + * the string "str" without overflowing length "len". return + * number of characters remaining in str, or 0 if overflowed. */ -static int -str_adderr(str, len, err) - -char *str; -int len; -int err; +int +str_adderr(char *str, int len, int err) { register char *msg; @@ -295,17 +161,14 @@ /* * str_addarg(str, len, arg, first) - add the string argument "arg" to - * the string "str". This is the first in the group when "first" - * is set (indicating that a comma should NOT be added to the front). + * the string "str" without overflowing length "len". This is the + * first in the group when "first" is set (indicating that a comma + * should NOT be added to the front). Return number of characters + * remaining in str, or 0 if overflowed. */ -static int -str_addarg(str, len, arg, first) - -char *str; -int len; -char *arg; -int first; +int +str_addarg(char *str, int len, char *arg, int first) { register int arglen; @@ -328,23 +191,132 @@ } /* - * err_compar(p1, p2) - comparison routine used by "qsort" - * for sorting errors. + * void err_string() + * + * Use message_error to log errors in the errs array. This function + * will combine identical errors to make the message short, but if + * there is more than one type of error it will call message_error + * for each one. */ -int -err_compar(p1, p2) +#define STRMAX 80 -register struct errs *p1, *p2; +void +err_string() { - register int result; + register struct errs *errp; + register int cnt = 0; + register int first = Yes; + register int currerr = -1; + int stringlen = 0; /* characters still available in "string" */ + char string[STRMAX]; - if ((result = p1->errnum - p2->errnum) == 0) + /* if there are no errors, our job is easy */ + if (errcnt == 0) { - return(strcmp(p1->arg, p2->arg)); + return; } - return(result); + + /* sort the errors */ + qsort((char *)errs, errcnt, sizeof(struct errs), err_compar); + + /* initialize the buffer (probably not necessary) */ + string[0] = '\0'; + stringlen = STRMAX - 1; + + /* loop thru the sorted list, logging errors */ + while (cnt < errcnt) + { + /* point to the current error */ + errp = &(errs[cnt++]); + + /* note that on overflow "stringlen" will become 0 and all + subsequent calls to str_addarg or str_adderr will return 0 */ + + /* if the error number is different then add the error string */ + if (errp->errnum != currerr) + { + if (currerr != -1) + { + /* add error string and log the error */ + stringlen = str_adderr(string, stringlen, currerr); + message_error(" %s", string); + + } + /* reset the buffer */ + string[0] = '\0'; + stringlen = STRMAX - 1; + + /* move to next error num */ + currerr = errp->errnum; + first = Yes; + } + + /* add this arg */ + stringlen = str_addarg(string, stringlen, errp->arg, first); + first = No; + } + + /* add final message */ + stringlen = str_adderr(string, stringlen, currerr); + + /* write the error string */ + message_error(" %s", string); +} + +/* + * Utility routines that help with some of the commands. + */ + +char * +next_field(char *str) + + +{ + if ((str = strchr(str, ' ')) == NULL) + { + return(NULL); + } + *str = '\0'; + while (*++str == ' ') /* loop */; + + /* if there is nothing left of the string, return NULL */ + /* This fix is dedicated to Greg Earle */ + return(*str == '\0' ? NULL : str); +} + +int +scanint(char *str, int *intp) + +{ + register int val = 0; + register int ch; + + /* if there is nothing left of the string, flag it as an error */ + /* This fix is dedicated to Greg Earle */ + if (*str == '\0') + { + return(-1); + } + + while ((ch = *str++) != '\0') + { + if (isdigit(ch)) + { + val = val * 10 + (ch - '0'); + } + else if (isspace(ch)) + { + break; + } + else + { + return(-1); + } + } + *intp = val; + return(0); } /* @@ -383,16 +355,18 @@ * command does; invoked in response to 'k'. */ -char *kill_procs(str) - -char *str; +void +kill_procs(char *str) { register char *nptr; int signum = SIGTERM; /* default */ int procnum; - struct sigdesc *sigp; int uid; + int owner; +#ifndef USE_SYS_SIGLIST + struct sigdesc *sigp; +#endif /* reset error array */ ERR_RESET; @@ -401,30 +375,51 @@ uid = getuid(); /* skip over leading white space */ - while (isspace(*str)) str++; + while (isspace((int)*str)) str++; if (str[0] == '-') { /* explicit signal specified */ if ((nptr = next_field(str)) == NULL) { - return(" kill: no processes specified"); + message_error(" kill: no processes specified"); + return; } - if (isdigit(str[1])) + str++; + if (isdigit((int)str[0])) { - (void) scanint(str + 1, &signum); + (void) scanint(str, &signum); if (signum <= 0 || signum >= NSIG) { - return(" invalid signal number"); + message_error(" kill: invalid signal number"); + return; } } else { /* translate the name into a number */ +#ifdef USE_SYS_SIGLIST + for (signum = 1; signum < NSIG; signum++) + { + if (strcasecmp(sys_signame[signum], str) == 0) + { + break; + } + } + if (signum == NSIG) + { + message_error(" kill: bad signal name"); + return; + } +#else for (sigp = sigdesc; sigp->name != NULL; sigp++) { - if (strcmp(sigp->name, str + 1) == 0) +#ifdef HAVE_STRCASECMP + if (strcasecmp(sigp->name, str) == 0) +#else + if (strcmp(sigp->name, str) == 0) +#endif { signum = sigp->number; break; @@ -434,8 +429,10 @@ /* was it ever found */ if (sigp->name == NULL) { - return(" bad signal name"); + message_error(" kill: bad signal name"); + return; } +#endif } /* put the new pointer in place */ str = nptr; @@ -451,9 +448,10 @@ else { /* check process owner if we're not root */ - if (uid && (uid != proc_owner(procnum))) + owner = proc_owner(procnum); + if (uid && (uid != owner)) { - ERROR(str, EACCES); + ERROR(str, owner == -1 ? ESRCH : EACCES); } /* go in for the kill */ else if (kill(procnum, signum) == -1) @@ -464,8 +462,8 @@ } } while ((str = next_field(str)) != NULL); - /* return appropriate error string */ - return(err_string()); + /* process errors */ + err_string(); } /* @@ -473,9 +471,8 @@ * "renice" command does; invoked in response to 'r'. */ -char *renice_procs(str) - -char *str; +void +renice_procs(char *str) { register char negate; @@ -506,16 +503,17 @@ /* check for validity */ if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX) { - return(" bad priority value"); + message_error(" renice: bad priority value"); } #endif /* move to the first process number */ if ((str = next_field(str)) == NULL) { - return(" no processes specified"); + message_error(" remice: no processes specified"); } +#ifdef HAVE_SETPRIORITY /* loop thru the process numbers, renicing each one */ do { @@ -534,8 +532,552 @@ ERROR(str, errno); } } while ((str = next_field(str)) != NULL); + err_string(); +#else + message_error(" renice operation not supported"); +#endif +} + +/* COMMAND ROUTINES */ + +/* + * Each command routine is called by command_process and is passed a + * pointer to the current global state. Command routines are free + * to change anything in the global state, although changes to the + * statics structure are discouraged. Whatever a command routine + * returns will be returned by command_process. + */ + +void +cmd_quit(globalstate *gstate) - /* return appropriate error string */ - return(err_string()); +{ + quit(EX_OK); + /*NOTREACHED*/ +} + +int +cmd_update(globalstate *gstate) + +{ + /* go home for visual feedback */ + screen_home(); + fflush(stdout); + message_expire(); + return CMD_REFRESH; +} + +int +cmd_redraw(globalstate *gstate) + +{ + gstate->fulldraw = Yes; + return CMD_REFRESH; +} + +int +cmd_color(globalstate *gstate) + +{ + gstate->use_color = color_activate(-1); + gstate->fulldraw = Yes; + return CMD_REFRESH; +} + +int +cmd_number(globalstate *gstate) + +{ + int newval; + char tmpbuf[20]; + + message_prompt("Number of processes to show: "); + newval = readline(tmpbuf, 8, Yes); + if (newval > -1) + { + if (newval > gstate->max_topn) + { + message_error(" This terminal can only display %d processes", + gstate->max_topn); + } + + if (newval == 0) + { + /* inhibit the header */ + display_header(No); + } + + else if (gstate->topn == 0) + { + display_header(Yes); + } + + gstate->topn = newval; + } + return CMD_REFRESH; +} + +int +cmd_delay(globalstate *gstate) + +{ + int newval; + char tmpbuf[20]; + + message_prompt("Seconds to delay: "); + if ((newval = readline(tmpbuf, 8, Yes)) > -1) + { + if ((gstate->delay = newval) == 0 && getuid() != 0) + { + gstate->delay = 1; + } + } + return CMD_REFRESH; +} + +int +cmd_idle(globalstate *gstate) + +{ + gstate->pselect.idle = !gstate->pselect.idle; + message_error(" %sisplaying idle processes.", + gstate->pselect.idle ? "D" : "Not d"); + return CMD_REFRESH; +} + +int +cmd_displays(globalstate *gstate) + +{ + int i; + char tmpbuf[20]; + + message_prompt("Displays to show (currently %s): ", + gstate->displays == -1 ? "infinite" : + itoa(gstate->displays)); + + if ((i = readline(tmpbuf, 10, Yes)) > 0) + { + gstate->displays = i; + } + else if (i == 0) + { + quit(EX_OK); + /*NOTREACHED*/ + } + return CMD_OK; +} + +/* Toggle the display of jail IDs. */ +int +cmd_jailid(globalstate *gstate) + +{ + gstate->statics->flags.jids = !gstate->statics->flags.jids; + /* JID needs to be added or removed from the columns. */ + calculate_header_columns(gstate->statics); + message_error(" %sisplaying jail IDs.", + gstate->statics->flags.jids ? "D" : "Not d"); + return CMD_REFRESH; +} + +/* Filter by a particular jail ID. */ +int +cmd_filter_jail(globalstate *gstate) + +{ + char tmpbuf[50]; + + message_prompt("Filter by jail name or ID ('+' to disable): "); + if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) + { + /* A '+' disables filtering. */ + if (tmpbuf[0] == '+' && tmpbuf[1] == '\0') + gstate->pselect.filter_jail = 0; + /* Otherwise look up the jail ID. */ + else + { + if ((gstate->pselect.jail = jail_getid(tmpbuf)) == -1) + message_error(" No such jail."); + else + gstate->pselect.filter_jail = 1; + } + /* Include jail ID column. */ + gstate->statics->flags.jids = 1; + /* JID needs to be added or removed from the columns. */ + calculate_header_columns(gstate->statics); + + return CMD_REFRESH; + } + else return CMD_OK; } +int +cmd_cmdline(globalstate *gstate) + +{ + if (gstate->statics->flags.fullcmds) + { + gstate->pselect.fullcmd = !gstate->pselect.fullcmd; + message_error(" %sisplaying full command lines.", + gstate->pselect.fullcmd ? "D" : "Not d"); + return CMD_REFRESH; + } + message_error(" Full command display not supported."); + return CMD_OK; +} + +int +cmd_order(globalstate *gstate) + +{ + char tmpbuf[MAX_COLS]; + int i; + + if (gstate->statics->order_names != NULL) + { + message_prompt("Column to sort: "); + if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) + { + if ((i = string_index(tmpbuf, gstate->statics->order_names)) == -1) + { + message_error(" Sort order \"%s\" not recognized", tmpbuf); + } + else + { + gstate->order_index = i; + return CMD_REFRESH; + } + } + } + return CMD_OK; +} + +int +cmd_order_x(globalstate *gstate, char *name, ...) + +{ + va_list ap; + char *p; + char **names; + int i; + + names = gstate->statics->order_names; + if (names != NULL) + { + if ((i = string_index(name, names)) == -1) + { + /* check the alternate list */ + va_start(ap, name); + p = va_arg(ap, char *); + while (p != NULL) + { + if ((i = string_index(p, names)) != -1) + { + gstate->order_index = i; + return CMD_REFRESH; + } + p = va_arg(ap, char *); + } + message_error(" Sort order not recognized"); + } + else + { + gstate->order_index = i; + return CMD_REFRESH; + } + } + return CMD_OK; +} + +int +cmd_order_cpu(globalstate *gstate) + +{ + return cmd_order_x(gstate, "cpu", NULL); +} + +int +cmd_order_pid(globalstate *gstate) + +{ + return cmd_order_x(gstate, "pid", NULL); +} + +int +cmd_order_mem(globalstate *gstate) + +{ + return cmd_order_x(gstate, "mem", "size", NULL); +} + +int +cmd_order_time(globalstate *gstate) + +{ + return cmd_order_x(gstate, "time"); +} + +#ifdef ENABLE_KILL + +int +cmd_kill(globalstate *gstate) + +{ + char tmpbuf[MAX_COLS]; + + message_prompt_plain("kill "); + if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) + { + kill_procs(tmpbuf); + } + return CMD_OK; +} + +int +cmd_renice(globalstate *gstate) + +{ + char tmpbuf[MAX_COLS]; + + message_prompt_plain("renice "); + if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) + { + renice_procs(tmpbuf); + } + return CMD_OK; +} + +#endif + +int +cmd_user(globalstate *gstate) + +{ + char linebuf[MAX_COLS]; + int i; + int ret = CMD_OK; + + message_prompt("Username to show: "); + if (readline(linebuf, sizeof(linebuf), No) > 0) + { + if (linebuf[0] == '+' && + linebuf[1] == '\0') + { + gstate->pselect.uid = -1; + ret = CMD_REFRESH; + } + else if ((i = userid(linebuf)) == -1) + { + message_error(" %s: unknown user", linebuf); + } + else + { + gstate->pselect.uid = i; + ret = CMD_REFRESH; + } + } + return ret; +} + +int +cmd_command(globalstate *gstate) + +{ + char linebuf[MAX_COLS]; + + if (gstate->pselect.command != NULL) + { + free(gstate->pselect.command); + gstate->pselect.command = NULL; + } + + message_prompt("Command to show: "); + if (readline(linebuf, sizeof(linebuf), No) > 0) + { + if (linebuf[0] != '\0') + { + gstate->pselect.command = strdup(linebuf); + } + } + return CMD_REFRESH; +} + +int +cmd_useruid(globalstate *gstate) + +{ + gstate->pselect.usernames = !gstate->pselect.usernames; + display_header(2); + return CMD_REFRESH; +} + +int +cmd_mode(globalstate *gstate) + +{ + if (gstate->statics->modemax <= 1) + { + return CMD_NA; + } + gstate->pselect.mode = (gstate->pselect.mode + 1) % gstate->statics->modemax; + display_header(2); + return CMD_REFRESH; +} + +int +cmd_system(globalstate *gstate) + +{ + gstate->pselect.system = !gstate->pselect.system; + display_header(2); + return CMD_REFRESH; +} + +int +cmd_threads(globalstate *gstate) + +{ + if (gstate->statics->flags.threads) + { + gstate->pselect.threads = !gstate->pselect.threads; + display_header(2); + return CMD_REFRESH; + } + return CMD_NA; +} + +/* forward reference for cmd_help, as it needs to see the command_table */ +int cmd_help(globalstate *gstate); + +/* command table */ +command command_table[] = { + { '\014', cmd_redraw, "redraw screen" }, + { ' ', cmd_update, "update screen" }, + { '?', cmd_help, "help; show this text" }, + { 'h', cmd_help, NULL }, + { 'C', cmd_color, "toggle the use of color" }, + { 'H', cmd_threads, "toggle the display of individual threads" }, + { 't', cmd_threads, NULL }, + { 'M', cmd_order_mem, "sort by memory usage" }, + { 'N', cmd_order_pid, "sort by process id" }, + { 'P', cmd_order_cpu, "sort by CPU usage" }, + { 'S', cmd_system, "toggle the display of system processes" }, + { 'T', cmd_order_time, "sort by CPU time" }, + { 'U', cmd_useruid, "toggle the display of usernames or uids" }, + { 'c', cmd_command, "display processes by command name" }, + { 'd', cmd_displays, "change number of displays to show" }, + { 'f', cmd_cmdline, "toggle the display of full command paths" }, + { 'i', cmd_idle, "toggle the displaying of idle processes" }, + { 'I', cmd_idle, NULL }, +#ifdef ENABLE_KILL + { 'k', cmd_kill, "kill processes; send a signal to a list of processes" }, +#endif + { 'm', cmd_mode, "toggle between display modes" }, + { 'n', cmd_number, "change number of processes to display" }, + { '#', cmd_number, NULL }, + { 'o', cmd_order, "specify sort order (see below)" }, + { 'q', (int (*)(globalstate *))cmd_quit, "quit" }, +#ifdef ENABLE_KILL + { 'r', cmd_renice, "renice a process" }, +#endif + { 's', cmd_delay, "change number of seconds to delay between updates" }, + { 'u', cmd_user, "display processes for only one user (+ selects all users)" }, + { 'j', cmd_jailid, "toggle the display of jail IDs" }, + { 'J', cmd_filter_jail, "filter by a jail ID" }, + { '\0', NULL, NULL }, +}; + +int +cmd_help(globalstate *gstate) + +{ + command *c; + char buf[12]; + char *p; + char *help; + + display_pagerstart(); + + display_pager("Top version %s, %s\n", version_string(), copyright); + display_pager("Platform module: %s\n\n", MODULE); + display_pager("A top users display for Unix\n\n"); + display_pager("These single-character commands are available:\n\n"); + + c = command_table; + while (c->cmd_func != NULL) + { + /* skip null help strings */ + if ((help = c->help) == NULL) + { + continue; + } + + /* translate character in to something readable */ + if (c->ch < ' ') + { + buf[0] = '^'; + buf[1] = c->ch + '@'; + buf[2] = '\0'; + } + else if (c->ch == ' ') + { + strcpy(buf, ""); + } + else + { + buf[0] = c->ch; + buf[1] = '\0'; + } + + /* if the next command is the same, fold them onto one line */ + if ((c+1)->cmd_func == c->cmd_func) + { + strcat(buf, " or "); + p = buf + strlen(buf); + *p++ = (c+1)->ch; + *p = '\0'; + c++; + } + + display_pager("%-7s - %s\n", buf, help); + c++; + } + + display_pager("\nNot all commands are available on all systems.\n\n"); + display_pager("Available sort orders: %s\n", gstate->order_namelist); + display_pagerend(); + gstate->fulldraw = Yes; + return CMD_REFRESH; +} + +/* + * int command_process(globalstate *gstate, int cmd) + * + * Process the single-character command "cmd". The global state may + * be modified by the command to alter the output. Returns CMD_ERROR + * if there was a serious error that requires an immediate exit, CMD_OK + * to indicate success, CMD_REFRESH to indicate that the screen needs + * to be refreshed immediately, CMD_UNKNOWN when the command is not known, + * and CMD_NA when the command is not available. Error messages for + * CMD_NA and CMD_UNKNOWN must be handled by the caller. + */ + +int +command_process(globalstate *gstate, int cmd) + +{ + command *c; + + c = command_table; + while (c->cmd_func != NULL) + { + if (c->ch == cmd) + { + return (c->cmd_func)(gstate); + } + c++; + } + + return CMD_UNKNOWN; +} Index: contrib/top/config.h =================================================================== --- contrib/top/config.h +++ contrib/top/config.h @@ -0,0 +1,257 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Support for debugging output */ +/* #undef DEBUG */ + +/* Default delay */ +#define DEFAULT_DELAY 2 + +/* Default number of processes to display */ +#define DEFAULT_TOPN -1 + +/* Enable color */ +#define ENABLE_COLOR 1 + +/* Enable dual architecture */ +/* #undef ENABLE_DUALARCH */ + +/* Enable kill and renice */ +#define ENABLE_KILL 1 + +/* Supports C99 style variadic macros */ +#define HAVE_C99_VARIADIC_MACROS 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_CURSES_H 1 + +/* Define to 1 if you have the declaration of `sys_errlist', and to 0 if you + don't. */ +#define HAVE_DECL_SYS_ERRLIST 1 + +/* Define to 1 if you have the declaration of `sys_signame', and to 0 if you + don't. */ +#define HAVE_DECL_SYS_SIGNAME 1 + +/* Define to 1 if you have the declaration of `tgetent', and to 0 if you + don't. */ +#define HAVE_DECL_TGETENT 1 + +/* Define to 1 if you have the declaration of `tgetflag', and to 0 if you + don't. */ +#define HAVE_DECL_TGETFLAG 1 + +/* Define to 1 if you have the declaration of `tgetnum', and to 0 if you + don't. */ +#define HAVE_DECL_TGETNUM 1 + +/* Define to 1 if you have the declaration of `tgetstr', and to 0 if you + don't. */ +#define HAVE_DECL_TGETSTR 1 + +/* Define to 1 if you have the declaration of `tgoto', and to 0 if you don't. + */ +#define HAVE_DECL_TGOTO 1 + +/* Define to 1 if you have the declaration of `tputs', and to 0 if you don't. + */ +#define HAVE_DECL_TPUTS 1 + +/* Platform module */ +#define HAVE_FORMAT_PROCESS_HEADER 1 + +/* Define to 1 if you have the `getopt' function. */ +#define HAVE_GETOPT 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_GETOPT_H 1 + +/* Define to 1 if you have the `getopt_long' function. */ +#define HAVE_GETOPT_LONG 1 + +/* Define to 1 if you have the `gettimeofday' function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Supports gnu style variadic macros */ +#define HAVE_GNU_VARIADIC_MACROS 1 + +/* Define to 1 if the system has the type `id_t'. */ +#define HAVE_ID_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `elf' library (-lelf). */ +#define HAVE_LIBELF 1 + +/* Define to 1 if you have the `kstat' library (-lkstat). */ +/* #undef HAVE_LIBKSTAT */ + +/* Define to 1 if you have the `kvm' library (-lkvm). */ +#define HAVE_LIBKVM 1 + +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + +/* Define to 1 if you have the `mach' library (-lmach). */ +/* #undef HAVE_LIBMACH */ + +/* Define to 1 if you have the `mas' library (-lmas). */ +/* #undef HAVE_LIBMAS */ + +/* Define to 1 if you have the `perfstat' library (-lperfstat). */ +/* #undef HAVE_LIBPERFSTAT */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if the system has the type `lwpid_t'. */ +#define HAVE_LWPID_T 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if the system has the type `pid_t'. */ +#define HAVE_PID_T 1 + +/* Define to 1 if you have the `setbuffer' function. */ +#define HAVE_SETBUFFER 1 + +/* Define to 1 if you have the `setpriority' function. */ +#define HAVE_SETPRIORITY 1 + +/* Define to 1 if you have the `setvbuf' function. */ +#define HAVE_SETVBUF 1 + +/* Define to 1 if you have the `sigaction' function. */ +#define HAVE_SIGACTION 1 + +/* Define to 1 if you have the `sighold' function. */ +#define HAVE_SIGHOLD 1 + +/* Define to 1 if you have the `sigprocmask' function. */ +#define HAVE_SIGPROCMASK 1 + +/* Define to 1 if you have the `sigrelse' function. */ +#define HAVE_SIGRELSE 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcasecmp' function. */ +#define HAVE_STRCASECMP 1 + +/* Define to 1 if you have the `strchr' function. */ +#define HAVE_STRCHR 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `sysconf' function. */ +#define HAVE_SYSCONF 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYSEXITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_RESOURCE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TIME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_UTSNAME_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TERMCAP_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TERM_H 1 + +/* Define to 1 if the system has the type `time_t'. */ +#define HAVE_TIME_T 1 + +/* Define to 1 if the system has the type `uid_t'. */ +#define HAVE_UID_T 1 + +/* Define to 1 if you have the `uname' function. */ +#define HAVE_UNAME 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Platform module */ +#define MODULE "freebsd" + +/* Default number of processes to display on non-terminals when topn is all */ +#define NOMINAL_TOPN 18 + +/* Define the major OS revision number. */ +#define OSMAJOR 11 + +/* Define the OS revision. */ +#define OSREV 110CURRENT + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "top" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "top 3.8beta1" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "top" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "3.8beta1" + +/* Define as the return type of signal handlers (`int' or `void'). */ +#define RETSIGTYPE void + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define as the type for the argument to the putc function of tputs ('int' or + 'char') */ +#define TPUTS_PUTC_ARGTYPE int + +/* Define the system hardware platform */ +/* #define UNAME_HARDWARE "amd64" */ + +/* Include code that utilizes extensions */ +/* #undef WITH_EXT */ Index: contrib/top/display.h =================================================================== --- contrib/top/display.h +++ contrib/top/display.h @@ -1,41 +1,88 @@ -/* constants needed for display.c */ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ -/* "type" argument for new_message function */ +/* interface declaration for display.c */ -#define MT_standout 1 -#define MT_delayed 2 +#ifndef _DISPLAY_H +#define _DISPLAY_H -#include "machine.h" +#include "globalstate.h" -int display_updatecpus(struct statics *statics); -void clear_message(void); -int display_resize(void); -void i_header(char *text); -char *printable(char *string); -char *cpustates_tag(void); -void display_header(int t); -int display_init(struct statics *statics); -void i_arc(int *stats); -void i_cpustates(int *states); -void i_loadave(int mpid, double *avenrun); -void i_memory(int *stats); -void i_message(void); -void i_process(int line, char *thisline); -void i_procstates(int total, int *brkdn); -void i_swap(int *stats); -void i_timeofday(time_t *tod); -void i_uptime(struct timeval *bt, time_t *tod); -void new_message(); -int readline(char *buffer, int size, int numeric); -char *trim_header(char *text); -void u_arc(int *stats); -void u_cpustates(int *states); -void u_endscreen(int hi); -void u_header(char *text); -void u_loadave(int mpid, double *avenrun); -void u_memory(int *stats); -void u_message(void); -void u_process(int line, char *newline); -void u_procstates(int total, int *brkdn); -void u_swap(int *stats); -void z_cpustates(void); +void display_clear(); +int display_resize(); +int display_lines(); +int display_columns(); +int display_init(struct statics *statics); +void i_loadave(int mpid, double *avenrun); +void u_loadave(int mpid, double *avenrun); +void i_minibar(int (*formatter)(char *, int)); +void u_minibar(int (*formatter)(char *, int)); +void i_uptime(time_t *bt, time_t *tod); +void u_uptime(time_t *bt, time_t *tod); +void i_timeofday(time_t *tod); +void i_procstates(int total, int *brkdn, int threads); +void u_procstates(int total, int *brkdn, int threads); +void i_cpustates(int *states); +void u_cpustates(int *states); +void z_cpustates(); +void i_kernel(int *stats); +void u_kernel(int *stats); +void i_memory(long *stats); +void u_memory(long *stats); +void i_swap(long *stats); +void u_swap(long *stats); +void i_arc(long *stats); +void u_arc(long *stats); +void i_message(struct timeval *now); +void u_message(struct timeval *now); +void i_header(char *text); +void u_header(char *text); +void i_process(int line, char *thisline); +void u_process(int, char *); +void i_endscreen(); +void u_endscreen(); +void display_header(int t); +void new_message(char *msgfmt, ...); +void message_error(char *msgfmt, ...); +void message_mark(); +void message_clear(); +void message_expire(); +void message_prompt(char *msgfmt, ...); +void message_prompt_plain(char *msgfmt, ...); +int readline(char *buffer, int size, int numeric); +void display_pagerstart(); +void display_pagerend(); +void display_pager(char *fmt, ...); + +#endif Index: contrib/top/display.c =================================================================== --- contrib/top/display.c +++ contrib/top/display.c @@ -1,17 +1,43 @@ /* - * Top users/processes display for Unix - * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* + * Top users/processes display for Unix + * Version 3 + */ + +/* * This file contains the routines that display information on the screen. * Each section of the screen has two routines: one for initially writing * all constant and dynamic text, and one for only updating the text that @@ -23,117 +49,638 @@ * In this way, those routines can be safely used on terminals that * have minimal (or nonexistant) terminal capabilities. * - * The routines are called in this order: *_loadave, i_timeofday, - * *_procstates, *_cpustates, *_memory, *_message, *_header, - * *_process, u_endscreen. + * The routines should be called in this order: *_loadave, *_uptime, + * i_timeofday, *_procstates, *_cpustates, *_memory, *_swap, + * *_message, *_header, *_process, *_endscreen. */ #include "os.h" - -#include - #include -#include +#include +#include +#include #include +#include "top.h" +#include "machine.h" #include "screen.h" /* interface to screen package */ #include "layout.h" /* defines for screen position layout */ #include "display.h" -#include "top.h" -#include "top.local.h" #include "boolean.h" -#include "machine.h" /* we should eliminate this!!! */ #include "utils.h" -#ifdef DEBUG -FILE *debug; +#ifdef ENABLE_COLOR +#include "color.h" #endif +#define CURSOR_COST 8 + +#define MESSAGE_DISPLAY_TIME 5 + /* imported from screen.c */ extern int overstrike; -static int lmpid = 0; -static int last_hi = 0; /* used in u_process and u_endscreen */ -static int lastline = 0; +static int lmpid = -1; static int display_width = MAX_COLS; -#define lineindex(l) ((l)*display_width) +/* cursor positions of key points on the screen are maintained here */ +/* layout.h has static definitions, but we may change our minds on some + of the positions as we make decisions about what needs to be displayed */ + +static int x_lastpid = X_LASTPID; +static int y_lastpid = Y_LASTPID; +static int x_loadave = X_LOADAVE; +static int y_loadave = Y_LOADAVE; +static int x_minibar = X_MINIBAR; +static int y_minibar = Y_MINIBAR; +static int x_uptime = X_UPTIME; +static int y_uptime = Y_UPTIME; +static int x_procstate = X_PROCSTATE; +static int y_procstate = Y_PROCSTATE; +static int x_cpustates = X_CPUSTATES; +static int y_cpustates = Y_CPUSTATES; +static int x_kernel = X_KERNEL; +static int y_kernel = Y_KERNEL; +static int x_mem = X_MEM; +static int y_mem = Y_MEM; +static int x_arc = X_ARC; +static int y_arc = Y_ARC; +static int x_swap = X_SWAP; +static int y_swap = Y_SWAP; +static int y_message = Y_MESSAGE; +static int x_header = X_HEADER; +static int y_header = Y_HEADER; +static int x_idlecursor = X_IDLECURSOR; +static int y_idlecursor = Y_IDLECURSOR; +static int y_procs = Y_PROCS; + +/* buffer and colormask that describes the content of the screen */ +/* these are singly dimensioned arrays -- the row boundaries are + determined on the fly. +*/ +static char *screenbuf = NULL; +static char *colorbuf = NULL; +static char scratchbuf[MAX_COLS]; +static int bufsize = 0; + +/* lineindex tells us where the beginning of a line is in the buffer */ +#define lineindex(l) ((l)*MAX_COLS) + +/* screen's cursor */ +static int curr_x, curr_y; +static int curr_color; - -/* things initialized by display_init and used thruout */ - -/* buffer of proc information lines for display updating */ -char *screenbuf = NULL; +/* virtual cursor */ +static int virt_x, virt_y; static char **procstate_names; static char **cpustate_names; static char **memory_names; -static char **arc_names; static char **swap_names; +static char **kernel_names; +static char **arc_names; static int num_procstates; static int num_cpustates; static int num_memory; static int num_swap; +static int num_kernel; +static int num_arc; static int *lprocstates; static int *lcpustates; -static int *lmemory; -static int *lswap; -static int num_cpus; static int *cpustate_columns; static int cpustate_total_length; -static int cpustates_column; -static enum { OFF, ON, ERASE } header_status = ON; +static int header_status = Yes; + +/* pending messages are stored in a circular buffer, where message_first + is the next one to display, and message_last is the last one + in the buffer. Counters wrap around at MAX_MESSAGES. The buffer is + empty when message_first == message_last and full when + message_last + 1 == message_first. The pointer message_current holds + the message currently being displayed, or "" if there is none. +*/ +#define MAX_MESSAGES 16 +static char *message_buf[MAX_MESSAGES]; +static int message_first = 0; +static int message_last = 0; +static struct timeval message_time = {0, 0}; +static char *message_current = NULL; +static int message_length = 0; +static int message_hold = 1; +static int message_barrier = No; + +#ifdef ENABLE_COLOR +static int load_cidx[3]; +static int header_cidx; +static int *cpustate_cidx; +static int *memory_cidx; +static int *swap_cidx; +static int *kernel_cidx; +static int *arc_cidx; +#else +#define memory_cidx NULL +#define swap_cidx NULL +#define kernel_cidx NULL +#define arc_cidx NULL +#endif + + +/* internal support routines */ + +/* + * static int string_count(char **pp) + * + * Pointer "pp" points to an array of string pointers, which is + * terminated by a NULL. Return the number of string pointers in + * this array. + */ + +static int +string_count(char **pp) + +{ + register int cnt = 0; + + if (pp != NULL) + { + while (*pp++ != NULL) + { + cnt++; + } + } + return(cnt); +} + +void +display_clear() + +{ + dprintf("display_clear\n"); + screen_clear(); + memzero(screenbuf, bufsize); + memzero(colorbuf, bufsize); + curr_x = curr_y = 0; +} + +/* + * void display_move(int x, int y) + * + * Efficiently move the cursor to x, y. This assumes the cursor is + * currently located at curr_x, curr_y, and will only use cursor + * addressing when it is less expensive than overstriking what's + * already on the screen. + */ + +void +display_move(int x, int y) + +{ + char buff[128]; + char *p; + char *bufp; + char *colorp; + int cnt = 0; + int color = curr_color; + + dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y); + + /* are we in a position to do this without cursor addressing? */ + if (curr_y < y || (curr_y == y && curr_x <= x)) + { + /* start buffering up what it would take to move there by rewriting + what's on the screen */ + cnt = CURSOR_COST; + p = buff; + + /* one newline for every line */ + while (cnt > 0 && curr_y < y) + { +#ifdef ENABLE_COLOR + if (color != 0) + { + p = strcpyend(p, color_setstr(0)); + color = 0; + cnt -= 5; + } +#endif + *p++ = '\n'; + curr_y++; + curr_x = 0; + cnt--; + } + + /* write whats in the screenbuf */ + bufp = &screenbuf[lineindex(curr_y) + curr_x]; + colorp = &colorbuf[lineindex(curr_y) + curr_x]; + while (cnt > 0 && curr_x < x) + { +#ifdef ENABLE_COLOR + if (color != *colorp) + { + color = *colorp; + p = strcpyend(p, color_setstr(color)); + cnt -= 5; + } +#endif + if ((*p = *bufp) == '\0') + { + /* somwhere on screen we haven't been before */ + *p = *bufp = ' '; + } + p++; + bufp++; + colorp++; + curr_x++; + cnt--; + } + } + + /* move the cursor */ + if (cnt > 0) + { + /* screen rewrite is cheaper */ + *p = '\0'; + fputs(buff, stdout); + curr_color = color; + } + else + { + screen_move(x, y); + } + + /* update our position */ + curr_x = x; + curr_y = y; +} + +/* + * display_write(int x, int y, int newcolor, int eol, char *new) + * + * Optimized write to the display. This writes characters to the + * screen in a way that optimizes the number of characters actually + * sent, by comparing what is being written to what is already on + * the screen (according to screenbuf and colorbuf). The string to + * write is "new", the first character of "new" should appear at + * screen position x, y. If x is -1 then "new" begins wherever the + * cursor is currently positioned. The string is written with color + * "newcolor". If "eol" is true then the remainder of the line is + * cleared. It is expected that "new" will have no newlines and no + * escape sequences. + */ + +void +display_write(int x, int y, int newcolor, int eol, char *new) + +{ + char *bufp; + char *colorp; + int ch; + int diff; + + dprintf("display_write(%d, %d, %d, %d, \"%s\")\n", + x, y, newcolor, eol, new); + + /* dumb terminal handling here */ + if (!smart_terminal) + { + if (x != -1) + { + /* make sure we are on the right line */ + while (curr_y < y) + { + putchar('\n'); + curr_y++; + curr_x = 0; + } + + /* make sure we are on the right column */ + while (curr_x < x) + { + putchar(' '); + curr_x++; + } + } + + /* write */ + fputs(new, stdout); + curr_x += strlen(new); + + return; + } + + /* adjust for "here" */ + if (x == -1) + { + x = virt_x; + y = virt_y; + } + else + { + virt_x = x; + virt_y = y; + } + + /* a pointer to where we start */ + bufp = &screenbuf[lineindex(y) + x]; + colorp = &colorbuf[lineindex(y) + x]; + + /* main loop */ + while ((ch = *new++) != '\0') + { + /* if either character or color are different, an update is needed */ + /* but only when the screen is wide enough */ + if (x < display_width && (ch != *bufp || newcolor != *colorp)) + { + /* check cursor */ + if (y != curr_y || x != curr_x) + { + /* have to move the cursor */ + display_move(x, y); + } + + /* write character */ +#ifdef ENABLE_COLOR + if (curr_color != newcolor) + { + fputs(color_setstr(newcolor), stdout); + curr_color = newcolor; + } +#endif + putchar(ch); + *bufp = ch; + *colorp = curr_color; + curr_x++; + } + + /* move */ + x++; + virt_x++; + bufp++; + colorp++; + } + + /* eol handling */ + if (eol && *bufp != '\0') + { + dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp); + /* make sure we are color 0 */ +#ifdef ENABLE_COLOR + if (curr_color != 0) + { + fputs(color_setstr(0), stdout); + curr_color = 0; + } +#endif + + /* make sure we are at the end */ + if (x != curr_x || y != curr_y) + { + screen_move(x, y); + curr_x = x; + curr_y = y; + } + + /* clear to end */ + screen_cleareol(strlen(bufp)); + + /* clear out whats left of this line's buffer */ + diff = display_width - x; + if (diff > 0) + { + memzero(bufp, diff); + memzero(colorp, diff); + } + } +} + +void +display_fmt(int x, int y, int newcolor, int eol, char *fmt, ...) + +{ + va_list argp; + + va_start(argp, fmt); + + vsnprintf(scratchbuf, MAX_COLS, fmt, argp); + display_write(x, y, newcolor, eol, scratchbuf); +} + +void +display_cte() + +{ + int len; + int y; + char *p; + int need_clear = 0; + + /* is there anything out there that needs to be cleared? */ + p = &screenbuf[lineindex(virt_y) + virt_x]; + if (*p != '\0') + { + need_clear = 1; + } + else + { + /* this line is clear, what about the rest? */ + y = virt_y; + while (++y < screen_length) + { + if (screenbuf[lineindex(y)] != '\0') + { + need_clear = 1; + break; + } + } + } + + if (need_clear) + { + dprintf("display_cte: clearing\n"); + + /* we will need this later */ + len = lineindex(virt_y) + virt_x; + + /* move to x and y, then clear to end */ + display_move(virt_x, virt_y); + if (!screen_cte()) + { + /* screen has no clear to end, so do it by hand */ + p = &screenbuf[len]; + len = strlen(p); + if (len > 0) + { + screen_cleareol(len); + } + while (++virt_y < screen_length) + { + display_move(0, virt_y); + p = &screenbuf[lineindex(virt_y)]; + len = strlen(p); + if (len > 0) + { + screen_cleareol(len); + } + } + } + + /* clear the screenbuf */ + memzero(&screenbuf[len], bufsize - len); + memzero(&colorbuf[len], bufsize - len); + } +} + +static void +summary_format(int x, int y, int *numbers, char **names, int *cidx) + +{ + register int num; + register char *thisname; + register char *lastname = NULL; + register int color; + + /* format each number followed by its string */ + while ((thisname = *names++) != NULL) + { + /* get the number to format */ + num = *numbers++; + color = 0; + + /* display only non-zero numbers */ + if (num != 0) + { + /* write the previous name */ + if (lastname != NULL) + { + display_write(-1, -1, 0, 0, lastname); + } + +#ifdef ENABLE_COLOR + if (cidx != NULL) + { + /* choose a color */ + color = color_test(*cidx++, num); + } +#endif + + /* write this number if positive */ + if (num > 0) + { + display_write(x, y, color, 0, itoa(num)); + } + + /* defer writing this name */ + lastname = thisname; + + /* next iteration will not start at x, y */ + x = y = -1; + } + } + + /* if the last string has a separator on the end, it has to be + written with care */ + if (lastname != NULL) + { + if ((num = strlen(lastname)) > 1 && + lastname[num-2] == ',' && lastname[num-1] == ' ') + { + display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname); + } + else + { + display_write(-1, -1, 0, 1, lastname); + } + } +} -static int string_count(); -static void summary_format(); -static void line_update(); - -int x_lastpid = 10; -int y_lastpid = 0; -int x_loadave = 33; -int x_loadave_nompid = 15; -int y_loadave = 0; -int x_procstate = 0; -int y_procstate = 1; -int x_brkdn = 15; -int y_brkdn = 1; -int x_mem = 5; -int y_mem = 3; -int x_arc = 5; -int y_arc = 4; -int x_swap = 6; -int y_swap = 4; -int y_message = 5; -int x_header = 0; -int y_header = 6; -int x_idlecursor = 0; -int y_idlecursor = 5; -int y_procs = 7; - -int y_cpustates = 2; -int Header_lines = 7; - -int display_resize() +static void +summary_format_memory(int x, int y, long *numbers, char **names, int *cidx) { - register int lines; + register long num; + register int color; + register char *thisname; + register char *lastname = NULL; + + /* format each number followed by its string */ + while ((thisname = *names++) != NULL) + { + /* get the number to format */ + num = *numbers++; + color = 0; + + /* display only non-zero numbers */ + if (num != 0) + { + /* write the previous name */ + if (lastname != NULL) + { + display_write(-1, -1, 0, 0, lastname); + } + + /* defer writing this name */ + lastname = thisname; + +#ifdef ENABLE_COLOR + /* choose a color */ + color = color_test(*cidx++, num); +#endif + + /* is this number in kilobytes? */ + if (thisname[0] == 'K') + { + display_write(x, y, color, 0, format_k(num)); + lastname++; + } + else + { + display_write(x, y, color, 0, itoa((int)num)); + } + + /* next iteration will not start at x, y */ + x = y = -1; + } + } - /* first, deallocate any previous buffer that may have been there */ - if (screenbuf != NULL) + /* if the last string has a separator on the end, it has to be + written with care */ + if (lastname != NULL) { - free(screenbuf); + if ((num = strlen(lastname)) > 1 && + lastname[num-2] == ',' && lastname[num-1] == ' ') + { + display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname); + } + else + { + display_write(-1, -1, 0, 1, lastname); + } } +} + +/* + * int display_resize() + * + * Reallocate buffer space needed by the display package to accomodate + * a new screen size. Must be called whenever the screen's size has + * changed. Returns the number of lines available for displaying + * processes or -1 if there was a problem allocating space. + */ + +int +display_resize() + +{ + register int top_lines; + register int newsize; /* calculate the current dimensions */ /* if operating in "dumb" mode, we only need one line */ - lines = smart_terminal ? screen_length - Header_lines : 1; + top_lines = smart_terminal ? screen_length : 1; - if (lines < 0) - lines = 0; /* we don't want more than MAX_COLS columns, since the machine-dependent modules make static allocations based on MAX_COLS and we don't want to run off the end of their buffers */ @@ -143,138 +690,266 @@ display_width = MAX_COLS - 1; } - /* now, allocate space for the screen buffer */ - screenbuf = (char *)malloc(lines * display_width); - if (screenbuf == (char *)NULL) + /* see how much space we need */ + newsize = top_lines * (MAX_COLS + 1); + + /* reallocate only if we need more than we already have */ + if (newsize > bufsize) + { + /* deallocate any previous buffer that may have been there */ + if (screenbuf != NULL) + { + free(screenbuf); + } + if (colorbuf != NULL) + { + free(colorbuf); + } + + /* allocate space for the screen and color buffers */ + bufsize = newsize; + screenbuf = (char *)calloc(bufsize, sizeof(char)); + colorbuf = (char *)calloc(bufsize, sizeof(char)); + if (screenbuf == NULL || colorbuf == NULL) + { + /* oops! */ + return(-1); + } + } + else { - /* oops! */ - return(-1); + /* just clear them out */ + memzero(screenbuf, bufsize); + memzero(colorbuf, bufsize); } + /* adjust total lines on screen to lines available for procs */ + top_lines -= y_procs; + /* return number of lines available */ /* for dumb terminals, pretend like we can show any amount */ - return(smart_terminal ? lines : Largest); + return(smart_terminal ? top_lines : Largest); } -int display_updatecpus(statics) - -struct statics *statics; +int +display_lines() { - register int *lp; - register int lines; - register int i; - - /* call resize to do the dirty work */ - lines = display_resize(); - if (pcpu_stats) - num_cpus = statics->ncpus; - else - num_cpus = 1; - cpustates_column = 5; /* CPU: */ - if (num_cpus != 1) - cpustates_column += 2; /* CPU 0: */ - for (i = num_cpus; i > 9; i /= 10) - cpustates_column++; + return(smart_terminal ? screen_length : Largest); +} - /* fill the "last" array with all -1s, to insure correct updating */ - lp = lcpustates; - i = num_cpustates * num_cpus; - while (--i >= 0) - { - *lp++ = -1; - } - - return(lines); +int +display_columns() + +{ + return(display_width); } - -int display_init(statics) -struct statics *statics; +/* + * int display_init(struct statics *statics) + * + * Initialize the display system based on information in the statics + * structure. Returns the number of lines available for displaying + * processes or -1 if there was an error. + */ + +int +display_init(struct statics *statics) { - register int lines; + register int top_lines; register char **pp; + register char *p; register int *ip; register int i; - lines = display_updatecpus(statics); + /* certain things may influence the screen layout, + so look at those first */ + + /* a kernel line shifts parts of the display down */ + kernel_names = statics->kernel_names; + if ((num_kernel = string_count(kernel_names)) > 0) + { + /* adjust screen placements */ + y_mem++; + y_arc++; + y_swap++; + y_message++; + y_header++; + y_idlecursor++; + y_procs++; + } + + /* An ARC line shifts parts of the display down. */ + arc_names = statics->arc_names; + if ((num_arc = string_count(arc_names)) > 0) + { + /* Adjust screen placements. */ + y_swap++; + y_message++; + y_header++; + y_idlecursor++; + y_procs++; + } + + /* a swap line shifts parts of the display down one */ + swap_names = statics->swap_names; + if ((num_swap = string_count(swap_names)) > 0) + { + /* adjust screen placements */ + y_message++; + y_header++; + y_idlecursor++; + y_procs++; + } + + /* call resize to do the dirty work */ + top_lines = display_resize(); /* only do the rest if we need to */ - if (lines > -1) + if (top_lines > -1) { /* save pointers and allocate space for names */ procstate_names = statics->procstate_names; num_procstates = string_count(procstate_names); - lprocstates = (int *)malloc(num_procstates * sizeof(int)); + lprocstates = (int *)calloc(num_procstates, sizeof(int)); cpustate_names = statics->cpustate_names; - - swap_names = statics->swap_names; - num_swap = string_count(swap_names); - lswap = (int *)malloc(num_swap * sizeof(int)); num_cpustates = string_count(cpustate_names); - lcpustates = (int *)malloc(num_cpustates * sizeof(int) * statics->ncpus); - cpustate_columns = (int *)malloc(num_cpustates * sizeof(int)); - + lcpustates = (int *)calloc(num_cpustates, sizeof(int)); + cpustate_columns = (int *)calloc(num_cpustates, sizeof(int)); memory_names = statics->memory_names; num_memory = string_count(memory_names); - lmemory = (int *)malloc(num_memory * sizeof(int)); - arc_names = statics->arc_names; - /* calculate starting columns where needed */ cpustate_total_length = 0; pp = cpustate_names; ip = cpustate_columns; while (*pp != NULL) { - *ip++ = cpustate_total_length; - if ((i = strlen(*pp++)) > 0) - { - cpustate_total_length += i + 8; - } + *ip++ = cpustate_total_length; + if ((i = strlen(*pp++)) > 0) + { + cpustate_total_length += i + 8; + } + } + } + +#ifdef ENABLE_COLOR + /* set up color tags for loadavg */ + load_cidx[0] = color_tag("1min"); + load_cidx[1] = color_tag("5min"); + load_cidx[2] = color_tag("15min"); + + /* find header color */ + header_cidx = color_tag("header"); + + /* color tags for cpu states */ + cpustate_cidx = (int *)malloc(num_cpustates * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "cpu."); + while (i < num_cpustates) + { + strcpy(p, cpustate_names[i]); + cpustate_cidx[i++] = color_tag(scratchbuf); + } + + /* color tags for kernel */ + if (num_kernel > 0) + { + kernel_cidx = (int *)malloc(num_kernel * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "kernel."); + while (i < num_kernel) + { + strcpy(p, homogenize(kernel_names[i]+1)); + kernel_cidx[i++] = color_tag(scratchbuf); + } + } + + /* color tags for memory */ + memory_cidx = (int *)malloc(num_memory * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "memory."); + while (i < num_memory) + { + strcpy(p, homogenize(memory_names[i]+1)); + memory_cidx[i++] = color_tag(scratchbuf); + } + + /* color tags for ARC */ + if (num_arc > 0) + { + arc_cidx = (int *)malloc(num_arc * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "arc."); + while (i < num_arc) + { + strcpy(p, homogenize(arc_names[i]+1)); + arc_cidx[i++] = color_tag(scratchbuf); + } + } + + /* color tags for swap */ + if (num_swap > 0) + { + swap_cidx = (int *)malloc(num_swap * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "swap."); + while (i < num_swap) + { + strcpy(p, homogenize(swap_names[i]+1)); + swap_cidx[i++] = color_tag(scratchbuf); } } +#endif - /* return number of lines available */ - return(lines); + /* return number of lines available (or error) */ + return(top_lines); } -void -i_loadave(mpid, avenrun) +static void +pr_loadavg(double avg, int i) + +{ + int color = 0; + +#ifdef ENABLE_COLOR + color = color_test(load_cidx[i], (int)(avg * 100)); +#endif + display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0, + avg < 10.0 ? " %5.2f" : " %5.1f", avg); + display_write(-1, -1, 0, 0, (i < 2 ? "," : ";")); +} -int mpid; -double *avenrun; +void +i_loadave(int mpid, double *avenrun) { register int i; - /* i_loadave also clears the screen, since it is first */ - clear(); - /* mpid == -1 implies this system doesn't have an _mpid */ if (mpid != -1) { - printf("last pid: %5d; ", mpid); + display_fmt(0, 0, 0, 0, + "last pid: %5d; load avg:", mpid); + x_loadave = X_LOADAVE; + } + else + { + display_write(0, 0, 0, 0, "load averages:"); + x_loadave = X_LOADAVE - X_LASTPIDWIDTH; } - - printf("load averages"); - for (i = 0; i < 3; i++) { - printf("%c %5.2f", - i == 0 ? ':' : ',', - avenrun[i]); + pr_loadavg(avenrun[i], i); } + lmpid = mpid; } void -u_loadave(mpid, avenrun) - -int mpid; -double *avenrun; +u_loadave(int mpid, double *avenrun) { register int i; @@ -284,36 +959,80 @@ /* change screen only when value has really changed */ if (mpid != lmpid) { - Move_to(x_lastpid, y_lastpid); - printf("%5d", mpid); + display_fmt(x_lastpid, y_lastpid, 0, 0, + "%5d", mpid); lmpid = mpid; } - - /* i remembers x coordinate to move to */ - i = x_loadave; } - else + + /* display new load averages */ + for (i = 0; i < 3; i++) { - i = x_loadave_nompid; + pr_loadavg(avenrun[i], i); } +} - /* move into position for load averages */ - Move_to(i, y_loadave); +static char minibar_buffer[64]; +#define MINIBAR_WIDTH 20 - /* display new load averages */ - /* we should optimize this and only display changes */ - for (i = 0; i < 3; i++) +void +i_minibar(int (*formatter)(char *, int)) +{ + (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); + + display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); +} + +void +u_minibar(int (*formatter)(char *, int)) +{ + (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); + + display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); +} + +static int uptime_days; +static int uptime_hours; +static int uptime_mins; +static int uptime_secs; + +void +i_uptime(time_t *bt, time_t *tod) + +{ + time_t uptime; + + if (*bt != -1) { - printf("%s%5.2f", - i == 0 ? "" : ", ", - avenrun[i]); + uptime = *tod - *bt; + uptime += 30; + uptime_days = uptime / 86400; + uptime %= 86400; + uptime_hours = uptime / 3600; + uptime %= 3600; + uptime_mins = uptime / 60; + uptime_secs = uptime % 60; + + /* + * Display the uptime. + */ + + display_fmt(x_uptime, y_uptime, 0, 0, + " up %d+%02d:%02d:%02d", + uptime_days, uptime_hours, uptime_mins, uptime_secs); } } void -i_timeofday(tod) +u_uptime(time_t *bt, time_t *tod) + +{ + i_uptime(bt, tod); +} + -time_t *tod; +void +i_timeofday(time_t *tod) { /* @@ -321,102 +1040,80 @@ * "ctime" always returns a string that looks like this: * * Sun Sep 16 01:03:52 1973 - * 012345678901234567890123 + * 012345678901234567890123 * 1 2 * * We want indices 11 thru 18 (length 8). */ - if (smart_terminal) - { - Move_to(screen_width - 8, 0); - } - else - { - fputs(" ", stdout); - } -#ifdef DEBUG + int x; + + /* where on the screen do we start? */ + x = (smart_terminal ? screen_width : 79) - 8; + + /* but don't bump in to uptime */ + if (x < x_uptime + 19) { - char *foo; - foo = ctime(tod); - fputs(foo, stdout); + x = x_uptime + 19; } -#endif - printf("%-8.8s\n", &(ctime(tod)[11])); - lastline = 1; + + /* display it */ + display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11])); } static int ltotal = 0; -static char procstates_buffer[MAX_COLS]; +static int lthreads = 0; /* * *_procstates(total, brkdn, names) - print the process summary line - * - * Assumptions: cursor is at the beginning of the line on entry - * lastline is valid */ -void -i_procstates(total, brkdn) -int total; -int *brkdn; +void +i_procstates(int total, int *brkdn, int threads) { - register int i; - /* write current number of processes and remember the value */ - printf("%d processes:", total); + display_fmt(0, y_procstate, 0, 0, + "%d %s: ", total, threads ? "threads" : "processes"); ltotal = total; - /* put out enough spaces to get to column 15 */ - i = digits(total); - while (i++ < 4) - { - putchar(' '); - } + /* remember where the summary starts */ + x_procstate = virt_x; - /* format and print the process state summary */ - summary_format(procstates_buffer, brkdn, procstate_names); - fputs(procstates_buffer, stdout); + if (total > 0) + { + /* format and print the process state summary */ + summary_format(-1, -1, brkdn, procstate_names, NULL); - /* save the numbers for next time */ - memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); + /* save the numbers for next time */ + memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); + lthreads = threads; + } } void -u_procstates(total, brkdn) - -int total; -int *brkdn; +u_procstates(int total, int *brkdn, int threads) { - static char new[MAX_COLS]; - register int i; + /* if threads state has changed, do a full update */ + if (lthreads != threads) + { + i_procstates(total, brkdn, threads); + return; + } /* update number of processes only if it has changed */ if (ltotal != total) { - /* move and overwrite */ -#if (x_procstate == 0) - Move_to(x_procstate, y_procstate); -#else - /* cursor is already there...no motion needed */ - /* assert(lastline == 1); */ -#endif - printf("%d", total); + display_fmt(0, y_procstate, 0, 0, + "%d", total); /* if number of digits differs, rewrite the label */ if (digits(total) != digits(ltotal)) { - fputs(" processes:", stdout); - /* put out enough spaces to get to column 15 */ - i = digits(total); - while (i++ < 4) - { - putchar(' '); - } - /* cursor may end up right where we want it!!! */ + display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes"); + x_procstate = virt_x; } /* save new total */ @@ -424,25 +1121,22 @@ } /* see if any of the state numbers has changed */ - if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) + if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) { /* format and update the line */ - summary_format(new, brkdn, procstate_names); - line_update(procstates_buffer, new, x_brkdn, y_brkdn); + summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL); memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); } } -#ifdef no_more /* * *_cpustates(states, names) - print the cpu state percentages - * - * Assumptions: cursor is on the PREVIOUS line */ /* cpustates_tag() calculates the correct tag to use to label the line */ -char *cpustates_tag() +char * +cpustates_tag() { register char *use; @@ -461,36 +1155,30 @@ use = long_tag; } - /* set cpustates_column accordingly then return result */ - cpustates_column = strlen(use); + /* set x_cpustates accordingly then return result */ + x_cpustates = strlen(use); return(use); } -#endif void -i_cpustates(states) - -int *states; +i_cpustates(int *states) { - register int i = 0; - register int value; - register char **names; - register char *thisname; - int cpu; + int value; + char **names; + char *thisname; + int *colp; + int color = 0; +#ifdef ENABLE_COLOR + int *cidx = cpustate_cidx; +#endif -for (cpu = 0; cpu < num_cpus; cpu++) { + /* initialize */ names = cpustate_names; + colp = cpustate_columns; - /* print tag and bump lastline */ - if (num_cpus == 1) - printf("\nCPU: "); - else { - value = printf("\nCPU %d: ", cpu); - while (value++ <= cpustates_column) - printf(" "); - } - lastline++; + /* print tag */ + display_write(0, y_cpustates, 0, 0, cpustates_tag()); /* now walk thru the names and print the line */ while ((thisname = *names++) != NULL) @@ -498,40 +1186,46 @@ if (*thisname != '\0') { /* retrieve the value and remember it */ - value = *states++; + value = *states; + +#ifdef ENABLE_COLOR + /* determine color number to use */ + color = color_test(*cidx++, value/10); +#endif /* if percentage is >= 1000, print it as 100% */ - printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), - (i++ % num_cpustates) == 0 ? "" : ", ", - ((float)value)/10., - thisname); + display_fmt(x_cpustates + *colp, y_cpustates, + color, 0, + (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"), + ((float)value)/10., + thisname, + *names != NULL ? ", " : ""); + } + /* increment */ + colp++; + states++; } -} /* copy over values into "last" array */ - memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus); + memcpy(lcpustates, states, num_cpustates * sizeof(int)); } void -u_cpustates(states) - -int *states; +u_cpustates(int *states) { - register int value; - register char **names; - register char *thisname; - register int *lp; - register int *colp; - int cpu; - -for (cpu = 0; cpu < num_cpus; cpu++) { - names = cpustate_names; + int value; + char **names = cpustate_names; + char *thisname; + int *lp; + int *colp; + int color = 0; +#ifdef ENABLE_COLOR + int *cidx = cpustate_cidx; +#endif - Move_to(cpustates_column, y_cpustates + cpu); - lastline = y_cpustates + cpu; - lp = lcpustates + (cpu * num_cpustates); + lp = lcpustates; colp = cpustate_columns; /* we could be much more optimal about this */ @@ -542,20 +1236,26 @@ /* did the value change since last time? */ if (*lp != *states) { - /* yes, move and change */ - Move_to(cpustates_column + *colp, y_cpustates + cpu); - lastline = y_cpustates + cpu; - + /* yes, change it */ /* retrieve value and remember it */ value = *states; +#ifdef ENABLE_COLOR + /* determine color number to use */ + color = color_test(*cidx, value/10); +#endif + /* if percentage is >= 1000, print it as 100% */ - printf((value >= 1000 ? "%4.0f" : "%4.1f"), - ((double)value)/10.); + display_fmt(x_cpustates + *colp, y_cpustates, color, 0, + (value >= 1000 ? "%4.0f" : "%4.1f"), + ((double)value)/10.); /* remember it for next time */ *lp = value; } +#ifdef ENABLE_COLOR + cidx++; +#endif } /* increment and move on */ @@ -564,43 +1264,31 @@ colp++; } } -} void z_cpustates() { register int i = 0; - register char **names; + register char **names = cpustate_names; register char *thisname; register int *lp; - int cpu, value; -for (cpu = 0; cpu < num_cpus; cpu++) { - names = cpustate_names; - - /* show tag and bump lastline */ - if (num_cpus == 1) - printf("\nCPU: "); - else { - value = printf("\nCPU %d: ", cpu); - while (value++ <= cpustates_column) - printf(" "); - } - lastline++; + /* print tag */ + display_write(0, y_cpustates, 0, 0, cpustates_tag()); while ((thisname = *names++) != NULL) { if (*thisname != '\0') { - printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname); + display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ", + thisname); } } -} /* fill the "last" array with all -1s, to insure correct updating */ lp = lcpustates; - i = num_cpustates * num_cpus; + i = num_cpustates; while (--i >= 0) { *lp++ = -1; @@ -608,117 +1296,123 @@ } /* - * *_memory(stats) - print "Memory: " followed by the memory summary string + * *_kernel(stats) - print "Kernel: " followed by the kernel summary string * - * Assumptions: cursor is on "lastline" - * for i_memory ONLY: cursor is on the previous line + * Assumptions: cursor is on "lastline", the previous line */ -char memory_buffer[MAX_COLS]; - void -i_memory(stats) - -int *stats; +i_kernel(int *stats) { - fputs("\nMem: ", stdout); - lastline++; + if (num_kernel > 0) + { + display_write(0, y_kernel, 0, 0, "Kernel: "); - /* format and print the memory summary */ - summary_format(memory_buffer, stats, memory_names); - fputs(memory_buffer, stdout); + /* format and print the kernel summary */ + summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); + } } void -u_memory(stats) - -int *stats; +u_kernel(int *stats) { - static char new[MAX_COLS]; - - /* format the new line */ - summary_format(new, stats, memory_names); - line_update(memory_buffer, new, x_mem, y_mem); + if (num_kernel > 0) + { + /* format the new line */ + summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); + } } /* - * *_arc(stats) - print "ARC: " followed by the ARC summary string + * *_memory(stats) - print "Memory: " followed by the memory summary string * - * Assumptions: cursor is on "lastline" - * for i_arc ONLY: cursor is on the previous line + * Assumptions: cursor is on "lastline", the previous line */ -char arc_buffer[MAX_COLS]; void -i_arc(stats) - -int *stats; +i_memory(long *stats) { - if (arc_names == NULL) - return; - - fputs("\nARC: ", stdout); - lastline++; + display_write(0, y_mem, 0, 0, "Memory: "); /* format and print the memory summary */ - summary_format(arc_buffer, stats, arc_names); - fputs(arc_buffer, stdout); + summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); } void -u_arc(stats) - -int *stats; +u_memory(long *stats) { - static char new[MAX_COLS]; - - if (arc_names == NULL) - return; - /* format the new line */ - summary_format(new, stats, arc_names); - line_update(arc_buffer, new, x_arc, y_arc); + summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); } - /* * *_swap(stats) - print "Swap: " followed by the swap summary string * - * Assumptions: cursor is on "lastline" - * for i_swap ONLY: cursor is on the previous line + * Assumptions: cursor is on "lastline", the previous line + * + * These functions only print something when num_swap > 0 */ -char swap_buffer[MAX_COLS]; - void -i_swap(stats) - -int *stats; +i_swap(long *stats) { - fputs("\nSwap: ", stdout); - lastline++; + if (num_swap > 0) + { + /* print the tag */ + display_write(0, y_swap, 0, 0, "Swap: "); - /* format and print the swap summary */ - summary_format(swap_buffer, stats, swap_names); - fputs(swap_buffer, stdout); + /* format and print the swap summary */ + summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); + } } void -u_swap(stats) +u_swap(long *stats) + +{ + if (num_swap > 0) + { + /* format the new line */ + summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); + } +} + +/* + * *_arc(stats) - print "ARC: " followed by the ARC summary string + * + * Assumptions: cursor is on "lastline", the previous line + * + * These functions only print something when num_arc > 0 + */ -int *stats; +void +i_arc(long *stats) { - static char new[MAX_COLS]; + if (num_arc > 0) + { + /* print the tag */ + display_write(0, y_arc, 0, 0, "ARC: "); - /* format the new line */ - summary_format(new, stats, swap_names); - line_update(swap_buffer, new, x_swap, y_swap); + /* format and print the ARC summary */ + summary_format_memory(x_arc, y_arc, stats, arc_names, arc_cidx); + } +} + +void +u_arc(long *stats) + +{ + if (num_arc > 0) + { + /* format the new line */ + summary_format_memory(x_arc, y_arc, stats, arc_names, arc_cidx); + } } /* @@ -732,346 +1426,428 @@ /* * i_message is funny because it gets its message asynchronously (with - * respect to screen updates). + * respect to screen updates). Messages are taken out of the + * circular message_buf and displayed one at a time. + */ + +void +i_message(struct timeval *now) + +{ + struct timeval my_now; + int i = 0; + + dprintf("i_message(%08x)\n", now); + + /* if now is NULL we have to get it ourselves */ + if (now == NULL) + { + time_get(&my_now); + now = &my_now; + } + + /* now that we have been called, messages no longer need to be held */ + message_hold = 0; + + dprintf("i_message: now %d, message_time %d\n", + now->tv_sec, message_time.tv_sec); + + if (smart_terminal) + { + /* is it time to change the message? */ + if (timercmp(now, &message_time, > )) + { + /* yes, free the current message */ + dprintf("i_message: timer expired\n"); + if (message_current != NULL) + { + free(message_current); + message_current = NULL; + } + + /* is there a new message to be displayed? */ + if (message_first != message_last) + { + /* move index to next message */ + if (++message_first == MAX_MESSAGES) message_first = 0; + + /* make the next message the current one */ + message_current = message_buf[message_first]; + + /* show it */ + dprintf("i_message: showing \"%s\"\n", message_current); + display_move(0, y_message); + screen_standout(message_current); + i = strlen(message_current); + + /* set the expiration timer */ + message_time = *now; + message_time.tv_sec += MESSAGE_DISPLAY_TIME; + + /* clear the rest of the line */ + screen_cleareol(message_length - i); + putchar('\r'); + message_length = i; + } + else + { + /* just clear what was there before, if anything */ + if (message_length > 0) + { + display_move(0, y_message); + screen_cleareol(message_length); + putchar('\r'); + message_length = 0; + } + } + } + } +} + +void +u_message(struct timeval *now) + +{ + i_message(now); +} + +static int header_length; + +/* + * *_header(text) - print the header for the process area + * + * Assumptions: cursor is on the previous line and lastline is consistent + */ + +void +i_header(char *text) + +{ + int header_color = 0; + +#ifdef ENABLE_COLOR + header_color = color_test(header_cidx, 0); +#endif + header_length = strlen(text); + if (header_status) + { + display_write(x_header, y_header, header_color, 1, text); + } +} + +/*ARGSUSED*/ +void +u_header(char *text) + +{ + int header_color = 0; + +#ifdef ENABLE_COLOR + header_color = color_test(header_cidx, 0); +#endif + display_write(x_header, y_header, header_color, 1, + header_status ? text : ""); +} + +/* + * *_process(line, thisline) - print one process line + * + * Assumptions: lastline is consistent */ -static char next_msg[MAX_COLS + 5]; -static int msglen = 0; -/* Invariant: msglen is always the length of the message currently displayed - on the screen (even when next_msg doesn't contain that message). */ +void +i_process(int line, char *thisline) + +{ + /* truncate the line to conform to our current screen width */ + thisline[display_width] = '\0'; + + /* write the line out */ + display_write(0, y_procs + line, 0, 1, thisline); +} + +void +u_process(int line, char *new_line) + +{ + i_process(line, new_line); +} void -i_message() +i_endscreen() { - while (lastline < y_message) + if (smart_terminal) + { + /* move the cursor to a pleasant place */ + display_move(x_idlecursor, y_idlecursor); + } + else { - fputc('\n', stdout); - lastline++; + /* separate this display from the next with some vertical room */ + fputs("\n\n", stdout); } - if (next_msg[0] != '\0') + fflush(stdout); +} + +void +u_endscreen() + +{ + if (smart_terminal) { - standout(next_msg); - msglen = strlen(next_msg); - next_msg[0] = '\0'; + /* clear-to-end the display */ + display_cte(); + + /* move the cursor to a pleasant place */ + display_move(x_idlecursor, y_idlecursor); + fflush(stdout); } - else if (msglen > 0) + else { - (void) clear_eol(msglen); - msglen = 0; + /* separate this display from the next with some vertical room */ + fputs("\n\n", stdout); } } void -u_message() +display_header(int t) { - i_message(); + header_status = t != 0; } -static int header_length; - -/* - * Trim a header string to the current display width and return a newly - * allocated area with the trimmed header. - */ +void +message_mark() -char * -trim_header(text) +{ + message_barrier = Yes; +} -char *text; +void +message_expire() { - char *s; - int width; + message_time.tv_sec = 0; + message_time.tv_usec = 0; +} - s = NULL; - width = display_width; - header_length = strlen(text); - if (header_length >= width) { - s = malloc((width + 1) * sizeof(char)); - if (s == NULL) - return (NULL); - strncpy(s, text, width); - s[width] = '\0'; - } - return (s); +void +message_flush() + +{ + message_first = message_last; + message_time.tv_sec = 0; + message_time.tv_usec = 0; } /* - * *_header(text) - print the header for the process area + * void new_message_v(char *msgfmt, va_list ap) * - * Assumptions: cursor is on the previous line and lastline is consistent + * Display a message in the message area. This function takes a va_list for + * the arguments. Safe to call before display_init. This function only + * queues a message for display, and allowed for multiple messages to be + * queued. The i_message function drains the queue and actually writes the + * messages on the display. */ -void -i_header(text) -char *text; +void +new_message_v(char *msgfmt, va_list ap) { - char *s; - - s = trim_header(text); - if (s != NULL) - text = s; + int i; + int empty; + char msg[MAX_COLS]; - if (header_status == ON) - { - putchar('\n'); - fputs(text, stdout); - lastline++; - } - else if (header_status == ERASE) + /* if message_barrier is active, remove all pending messages */ + if (message_barrier) { - header_status = OFF; + message_flush(); + message_barrier = No; } - free(s); -} - -/*ARGSUSED*/ -void -u_header(text) -char *text __unused; /* ignored */ + /* first, format the message */ + (void) vsnprintf(msg, sizeof(msg), msgfmt, ap); -{ + /* where in the buffer will it go? */ + i = message_last + 1; + if (i >= MAX_MESSAGES) i = 0; - if (header_status == ERASE) + /* make sure the buffer is not full */ + if (i != message_first) { - putchar('\n'); - lastline++; - clear_eol(header_length); - header_status = OFF; + /* insert it in to message_buf */ + message_buf[i] = strdup(msg); + dprintf("new_message_v: new message inserted in slot %d\n", i); + + /* remember if the buffer is empty and set the index */ + empty = message_last == message_first; + message_last = i; + + /* is message_buf otherwise empty and have we started displaying? */ + if (empty && !message_hold) + { + /* we can display the message now */ + i_message(NULL); + } } } /* - * *_process(line, thisline) - print one process line + * void new_message(int type, char *msgfmt, ...) * - * Assumptions: lastline is consistent + * Display a message in the message area. It is safe to call this function + * before display_init. Messages logged before the display is drawn will be + * held and displayed later. */ void -i_process(line, thisline) - -int line; -char *thisline; +new_message(char *msgfmt, ...) { - register char *p; - register char *base; - - /* make sure we are on the correct line */ - while (lastline < y_procs + line) - { - putchar('\n'); - lastline++; - } - - /* truncate the line to conform to our current screen width */ - thisline[display_width] = '\0'; + va_list ap; - /* write the line out */ - fputs(thisline, stdout); - - /* copy it in to our buffer */ - base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; - p = strecpy(base, thisline); - - /* zero fill the rest of it */ - memzero(p, display_width - (p - base)); + va_start(ap, msgfmt); + new_message_v(msgfmt, ap); + va_end(ap); } -void -u_process(line, newline) +/* + * void message_error(char *msgfmt, ...) + * + * Put an error message in the message area. It is safe to call this function + * before display_init. Messages logged before the display is drawn will be + * held and displayed later. + */ -int line; -char *newline; +void +message_error(char *msgfmt, ...) { - register char *optr; - register int screen_line = line + Header_lines; - register char *bufferline; + va_list ap; - /* remember a pointer to the current line in the screen buffer */ - bufferline = &screenbuf[lineindex(line)]; - - /* truncate the line to conform to our current screen width */ - newline[display_width] = '\0'; - - /* is line higher than we went on the last display? */ - if (line >= last_hi) - { - /* yes, just ignore screenbuf and write it out directly */ - /* get positioned on the correct line */ - if (screen_line - lastline == 1) - { - putchar('\n'); - lastline++; - } - else - { - Move_to(0, screen_line); - lastline = screen_line; - } + va_start(ap, msgfmt); + new_message_v(msgfmt, ap); + fflush(stdout); + va_end(ap); +} - /* now write the line */ - fputs(newline, stdout); +/* + * void message_clear() + * + * Clear message area and flush all pending messages. + */ - /* copy it in to the buffer */ - optr = strecpy(bufferline, newline); +void +message_clear() - /* zero fill the rest of it */ - memzero(optr, display_width - (optr - bufferline)); - } - else +{ + /* remove any existing message */ + if (message_current != NULL) { - line_update(bufferline, newline, 0, line + Header_lines); + display_move(0, y_message); + screen_cleareol(message_length); + free(message_current); + message_current = 0; } + + /* flush all pending messages */ + message_flush(); } -void -u_endscreen(hi) +/* + * void message_prompt_v(int so, char *msgfmt, va_list ap) + * + * Place a prompt in the message area. A prompt is different from a + * message as follows: it is displayed immediately, overwriting any + * message that may already be there, it may be highlighted in standout + * mode (if "so" is true), the cursor is left to rest at the end of the + * prompt. This call causes all pending messages to be flushed. + */ -int hi; +void +message_prompt_v(int so, char *msgfmt, va_list ap) { - register int screen_line = hi + Header_lines; - register int i; + char msg[MAX_COLS]; + int i; - if (smart_terminal) - { - if (hi < last_hi) - { - /* need to blank the remainder of the screen */ - /* but only if there is any screen left below this line */ - if (lastline + 1 < screen_length) - { - /* efficiently move to the end of currently displayed info */ - if (screen_line - lastline < 5) - { - while (lastline < screen_line) - { - putchar('\n'); - lastline++; - } - } - else - { - Move_to(0, screen_line); - lastline = screen_line; - } + /* clear out the message buffer */ + message_flush(); - if (clear_to_end) - { - /* we can do this the easy way */ - putcap(clear_to_end); - } - else - { - /* use clear_eol on each line */ - i = hi; - while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) - { - putchar('\n'); - } - } - } - } - last_hi = hi; - - /* move the cursor to a pleasant place */ - Move_to(x_idlecursor, y_idlecursor); - lastline = y_idlecursor; - } - else - { - /* separate this display from the next with some vertical room */ - fputs("\n\n", stdout); - } -} + /* format the message */ + i = vsnprintf(msg, sizeof(msg), msgfmt, ap); -void -display_header(t) + /* this goes over any existing message */ + display_move(0, y_message); -int t; + /* clear the entire line */ + screen_cleareol(message_length); -{ - if (t) + /* show the prompt */ + if (so) { - header_status = ON; + screen_standout(msg); } - else if (header_status == ON) + else { - header_status = ERASE; + fputs(msg, stdout); } + + /* make it all visible */ + fflush(stdout); + + /* even though we dont keep a copy of the prompt, track its length */ + message_length = i < MAX_COLS ? i : MAX_COLS; } -/*VARARGS2*/ -void -new_message(type, msgfmt, a1, a2, a3) +/* + * void message_prompt(char *msgfmt, ...) + * + * Place a prompt in the message area (see message_prompt_v). + */ -int type; -char *msgfmt; -caddr_t a1, a2, a3; +void +message_prompt(char *msgfmt, ...) { - register int i; - - /* first, format the message */ - (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3); + va_list ap; - if (msglen > 0) - { - /* message there already -- can we clear it? */ - if (!overstrike) - { - /* yes -- write it and clear to end */ - i = strlen(next_msg); - if ((type & MT_delayed) == 0) - { - type & MT_standout ? standout(next_msg) : - fputs(next_msg, stdout); - (void) clear_eol(msglen - i); - msglen = i; - next_msg[0] = '\0'; - } - } - } - else - { - if ((type & MT_delayed) == 0) - { - type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); - msglen = strlen(next_msg); - next_msg[0] = '\0'; - } - } + va_start(ap, msgfmt); + message_prompt_v(Yes, msgfmt, ap); + va_end(ap); } void -clear_message() +message_prompt_plain(char *msgfmt, ...) { - if (clear_eol(msglen) == 1) - { - putchar('\r'); - } + va_list ap; + + va_start(ap, msgfmt); + message_prompt_v(No, msgfmt, ap); + va_end(ap); } -int -readline(buffer, size, numeric) +/* + * int readline(char *buffer, int size, int numeric) + * + * Read a line of input from the terminal. The line is placed in + * "buffer" not to exceed "size". If "numeric" is true then the input + * can only consist of digits. This routine handles all character + * editing while keeping the terminal in cbreak mode. If "numeric" + * is true then the number entered is returned. Otherwise the number + * of character read in to "buffer" is returned. + */ -char *buffer; -int size; -int numeric; +int +readline(char *buffer, int size, int numeric) { register char *ptr = buffer; register char ch; register char cnt = 0; - register char maxcnt = 0; /* allow room for null terminator */ size -= 1; @@ -1079,7 +1855,7 @@ /* read loop */ while ((fflush(stdout), read(0, ptr, 1) > 0)) { - /* newline means we are done */ + /* newline or return means we are done */ if ((ch = *ptr) == '\n' || ch == '\r') { break; @@ -1088,17 +1864,39 @@ /* handle special editing characters */ if (ch == ch_kill) { - /* kill line -- account for overstriking */ - if (overstrike) - { - msglen += maxcnt; - } - /* return null string */ *buffer = '\0'; putchar('\r'); return(-1); } + else if (ch == ch_werase) + { + /* erase previous word */ + if (cnt <= 0) + { + /* none to erase! */ + putchar('\7'); + } + else + { + /* + * First: remove all spaces till the first-non-space + * Second: remove all non-spaces till the first-space + */ + while(cnt > 0 && ptr[-1] == ' ') + { + fputs("\b \b", stdout); + ptr--; + cnt--; + } + while(cnt > 0 && ptr[-1] != ' ') + { + fputs("\b \b", stdout); + ptr--; + cnt--; + } + } + } else if (ch == ch_erase) { /* erase previous character */ @@ -1115,8 +1913,8 @@ } } /* check for character validity and buffer overflow */ - else if (cnt == size || (numeric && !isdigit(ch)) || - !isprint(ch)) + else if (cnt == size || (numeric && !isdigit((int)ch)) || + !isprint((int)ch)) { /* not legal */ putchar('\7'); @@ -1127,273 +1925,80 @@ putchar(ch); ptr++; cnt++; - if (cnt > maxcnt) - { - maxcnt = cnt; - } } } /* all done -- null terminate the string */ *ptr = '\0'; - /* account for the extra characters in the message area */ - /* (if terminal overstrikes, remember the furthest they went) */ - msglen += overstrike ? maxcnt : cnt; + /* add response length to message_length */ + message_length += cnt; /* return either inputted number or string length */ putchar('\r'); return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); } -/* internal support routines */ - -static int string_count(pp) - -register char **pp; +void +display_pagerstart() { - register int cnt; - - cnt = 0; - while (*pp++ != NULL) - { - cnt++; - } - return(cnt); + display_clear(); } -static void summary_format(str, numbers, names) - -char *str; -int *numbers; -register char **names; +void +display_pagerend() { - register char *p; - register int num; - register char *thisname; - register int useM = No; - - /* format each number followed by its string */ - p = str; - while ((thisname = *names++) != NULL) - { - /* get the number to format */ - num = *numbers++; - - /* display only non-zero numbers */ - if (num > 0) - { - /* is this number in kilobytes? */ - if (thisname[0] == 'K') - { - /* yes: format it as a memory value */ - p = strecpy(p, format_k(num)); + char ch; - /* skip over the K, since it was included by format_k */ - p = strecpy(p, thisname+1); - } - else - { - p = strecpy(p, itoa(num)); - p = strecpy(p, thisname); - } - } + screen_standout("Hit any key to continue: "); + fflush(stdout); + (void) read(0, &ch, 1); +} - /* ignore negative numbers, but display corresponding string */ - else if (num < 0) - { - p = strecpy(p, thisname); - } - } +void +display_pager(char *fmt, ...) - /* if the last two characters in the string are ", ", delete them */ - p -= 2; - if (p >= str && p[0] == ',' && p[1] == ' ') - { - *p = '\0'; - } -} +{ + va_list ap; -static void line_update(old, new, start, line) + int ch; + char readch; + char buffer[MAX_COLS]; + char *data; -register char *old; -register char *new; -int start; -int line; - -{ - register int ch; - register int diff; - register int newcol = start + 1; - register int lastcol = start; - char cursor_on_line = No; - char *current; - - /* compare the two strings and only rewrite what has changed */ - current = old; -#ifdef DEBUG - fprintf(debug, "line_update, starting at %d\n", start); - fputs(old, debug); - fputc('\n', debug); - fputs(new, debug); - fputs("\n-\n", debug); -#endif + /* format into buffer */ + va_start(ap, fmt); + (void) vsnprintf(buffer, MAX_COLS, fmt, ap); + va_end(ap); + data = buffer; - /* start things off on the right foot */ - /* this is to make sure the invariants get set up right */ - if ((ch = *new++) != *old) + while ((ch = *data++) != '\0') { - if (line - lastline == 1 && start == 0) - { - putchar('\n'); - } - else - { - Move_to(start, line); - } - cursor_on_line = Yes; putchar(ch); - *old = ch; - lastcol = 1; - } - old++; - - /* - * main loop -- check each character. If the old and new aren't the - * same, then update the display. When the distance from the - * current cursor position to the new change is small enough, - * the characters that belong there are written to move the - * cursor over. - * - * Invariants: - * lastcol is the column where the cursor currently is sitting - * (always one beyond the end of the last mismatch). - */ - do /* yes, a do...while */ - { - if ((ch = *new++) != *old) + if (ch == '\n') { - /* new character is different from old */ - /* make sure the cursor is on top of this character */ - diff = newcol - lastcol; - if (diff > 0) + if (++curr_y >= screen_length - 1) { - /* some motion is required--figure out which is shorter */ - if (diff < 6 && cursor_on_line) + screen_standout("...More..."); + fflush(stdout); + (void) read(0, &readch, 1); + putchar('\r'); + switch(readch) { - /* overwrite old stuff--get it out of the old buffer */ - printf("%.*s", diff, ¤t[lastcol-start]); - } - else - { - /* use cursor addressing */ - Move_to(newcol, line); - cursor_on_line = Yes; - } - /* remember where the cursor is */ - lastcol = newcol + 1; - } - else - { - /* already there, update position */ - lastcol++; - } - - /* write what we need to */ - if (ch == '\0') - { - /* at the end--terminate with a clear-to-end-of-line */ - (void) clear_eol(strlen(old)); - } - else - { - /* write the new character */ - putchar(ch); - } - /* put the new character in the screen buffer */ - *old = ch; - } - - /* update working column and screen buffer pointer */ - newcol++; - old++; - - } while (ch != '\0'); - - /* zero out the rest of the line buffer -- MUST BE DONE! */ - diff = display_width - newcol; - if (diff > 0) - { - memzero(old, diff); - } - - /* remember where the current line is */ - if (cursor_on_line) - { - lastline = line; - } -} + case '\r': + case '\n': + curr_y--; + break; -/* - * printable(str) - make the string pointed to by "str" into one that is - * printable (i.e.: all ascii), by converting all non-printable - * characters into '?'. Replacements are done in place and a pointer - * to the original buffer is returned. - */ - -char *printable(str) - -char *str; - -{ - register char *ptr; - register char ch; - - ptr = str; - while ((ch = *ptr) != '\0') - { - if (!isprint(ch)) - { - *ptr = '?'; - } - ptr++; - } - return(str); -} - -void -i_uptime(bt, tod) - -struct timeval* bt; -time_t *tod; + case 'q': + return; -{ - time_t uptime; - int days, hrs, mins, secs; - - if (bt->tv_sec != -1) { - uptime = *tod - bt->tv_sec; - days = uptime / 86400; - uptime %= 86400; - hrs = uptime / 3600; - uptime %= 3600; - mins = uptime / 60; - secs = uptime % 60; - - /* - * Display the uptime. - */ - - if (smart_terminal) - { - Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0); - } - else - { - fputs(" ", stdout); + default: + curr_y = 0; + } + } } - printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs); } } Index: contrib/top/getans =================================================================== --- contrib/top/getans +++ contrib/top/getans @@ -1,118 +0,0 @@ -#!/bin/sh -# getans prompt type default results_filename -# type is one of -# number -# integer -# neginteger -# file default=default filename -# path -# yesno default=0,1 corres yes or no -# string (default) - -RAWPMPT=$1 -TYP=$2 -DFLT=$3 -OFNM=$4 - -ny0="no"; ny1="yes" -if [ ${TYP} = "yesno" ]; then - eval ny=\$ny${DFLT} - pmpt="${RAWPMPT} [$ny]: " -else - if [ -z "${DFLT}" ]; then - pmpt="${RAWPMPT}" - else - pmpt="${RAWPMPT} [${DFLT}]: " - fi -fi -if [ x"`echo -n`" = x-n ] -then - c=\\c -else - n=-n -fi - -while : -do - echo $n "$pmpt"$c - read input - case "$TYP" in - number) - tmp=`echo $input | tr -d 0123456789.` - if [ -n "$tmp" ]; then - echo "Invalid number. Please try again." - continue - fi - ;; - - integer) - tmp=`echo $input | tr -d 0123456789` - if [ -n "$tmp" ]; then - echo "Invalid integer. Please try again." - continue - fi - ;; - - neginteger) - if [ "x$input" != "x-1" ]; then - tmp=`echo $input | tr -d 0123456789` - if [ -n "$tmp" ]; then - echo "Invalid integer. Please try again." - continue - fi - fi - ;; - - file) - if [ -z "$input" ]; then - input=${DFLT} - fi - if [ ! -f "$input" -a ! -d "$input" ]; then - echo "The file $input does not exist. Please try again." - continue - fi - ;; - - path) - if [ -z "$input" ]; then - input="${DFLT}" - fi - if [ ! -f "$input" ]; then - path=`echo $PATH | sed -e s'/::/ . /g' -e 's/:/ /g'` - x= - for elt in $path; do - if [ -f "$elt/$input" ]; then x=1; break; fi - done - if [ -z "$x" ] ;then - echo "The command $input was not found. Please try again." - continue - fi - fi - ;; - - yesno) - if [ -z "$input" ]; then - input="${DFLT}" - else - case $input in - y | yes) - input=1 ;; - n | no) - input=0 ;; - *) - echo 'Please answer "yes" or "no".' - continue ;; - esac - fi - ;; - - *) ;; - esac - break -done - -if [ -z "$input" ]; then - input="${DFLT}" -fi - -echo $input > ${OFNM} Index: contrib/top/getopt.c =================================================================== --- contrib/top/getopt.c +++ contrib/top/getopt.c @@ -1,90 +0,0 @@ -/* - * "getopt" routine customized for top. - */ - -/* - * Many modern-day Unix implementations already have this function - * in libc. The standard "getopt" is perfectly sufficient for top's - * needs. If such a function exists in libc then you certainly don't - * need to compile this one in. To prevent this function from being - * compiled, define "HAVE_GETOPT". This is usually done in the "CFLAGS" - * line of the corresponding machine module. - */ - -/* - * This empty declaration exists solely to placate overexhuberant C - * compilers that like to warn you about content-free files. - */ -static void __empty(); - -#ifndef HAVE_GETOPT - -/*LINTLIBRARY*/ - -#include "os.h" -#ifndef NULL -#define NULL 0 -#endif -#ifndef EOF -#define EOF (-1) -#endif -#define ERR(s, c) if(opterr){\ - extern int write();\ - char errbuf[2];\ - errbuf[0] = c; errbuf[1] = '\n';\ - (void) write(2, argv[0], strlen(argv[0]));\ - (void) write(2, s, strlen(s));\ - (void) write(2, errbuf, 2);} - - -int opterr = 1; -int optind = 1; -int optopt; -char *optarg; - -int -getopt(argc, argv, opts) -int argc; -char **argv, *opts; -{ - static int sp = 1; - register int c; - register char *cp; - - if(sp == 1) - if(optind >= argc || - argv[optind][0] != '-' || argv[optind][1] == '\0') - return(EOF); - else if(strcmp(argv[optind], "--") == 0) { - optind++; - return(EOF); - } - optopt = c = argv[optind][sp]; - if(c == ':' || (cp=strchr(opts, c)) == NULL) { - ERR(": unknown option, -", c); - if(argv[optind][++sp] == '\0') { - optind++; - sp = 1; - } - return('?'); - } - if(*++cp == ':') { - if(argv[optind][sp+1] != '\0') - optarg = &argv[optind++][sp+1]; - else if(++optind >= argc) { - ERR(": argument missing for -", c); - sp = 1; - return('?'); - } else - optarg = argv[optind++]; - sp = 1; - } else { - if(argv[optind][++sp] == '\0') { - sp = 1; - optind++; - } - optarg = NULL; - } - return(c); -} -#endif /* HAVE_GETOPT */ Index: contrib/top/globalstate.h =================================================================== --- contrib/top/globalstate.h +++ contrib/top/globalstate.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The global state of top is described in this structure. It is passed + * to routines that may need to examine or alter it. + */ + +#ifndef _GLOBALSTATE_H_ +#define _GLOBALSTATE_H_ + +#include "machine.h" + +typedef struct globalstate { + struct timeval now; + struct timeval refresh; + int fulldraw; + int topn; + int max_topn; + int delay; + int displays; + int order_index; + int show_cpustates; + int show_tags; + int show_usernames; + int use_color; + int smart_terminal; + int interactive; + char *header_text; + char *order_name; /* only used before call to machine_init */ + char *order_namelist; + char *(*get_userid)(int); + struct process_select pselect; + struct statics *statics; +} globalstate; + +#endif /* _GLOBALSTATE_H_ */ Index: contrib/top/hash.h =================================================================== --- contrib/top/hash.h +++ contrib/top/hash.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* hash.m4h */ + +/* Interface definition for hash.c */ + +/* The file hash.h is generated from hash.m4h via the preprocessor M4 */ + +#ifndef _HASH_H +#define _HASH_H + +#include + +typedef struct pidthr_t { + pid_t k_pid; + id_t k_thr; +} pidthr_t; + +typedef struct llistitem { + void *datum; + struct llistitem *next; +} llistitem; + +typedef struct llist { + llistitem *head; + unsigned int count; +} llist; + +typedef struct bucket { + llist list; +} bucket_t; + +typedef struct hash_table { + int num_buckets; + bucket_t *buckets; +} hash_table; + +typedef struct hash_pos { + int num_buckets; + int curr; + bucket_t *hash_bucket; + llistitem *ll_item; + llistitem *ll_last; +} hash_pos; + +hash_table *hash_create(int num); +void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht); + + + + +typedef struct hash_item_uint { + unsigned int key; + void *value; +} hash_item_uint; + +void *hash_add_uint(hash_table *ht, unsigned int key, void *value); +void *hash_replace_uint(hash_table *ht, unsigned int key, void *value); +void *hash_lookup_uint(hash_table *ht, unsigned int key); +void *hash_remove_uint(hash_table *ht, unsigned int key); +hash_item_uint *hash_first_uint(hash_table *ht, hash_pos *pos); +hash_item_uint *hash_next_uint(hash_pos *pos); +void *hash_remove_pos_uint(hash_pos *pos); + + +typedef struct hash_item_pid { + pid_t key; + void *value; +} hash_item_pid; + +void *hash_add_pid(hash_table *ht, pid_t key, void *value); +void *hash_replace_pid(hash_table *ht, pid_t key, void *value); +void *hash_lookup_pid(hash_table *ht, pid_t key); +void *hash_remove_pid(hash_table *ht, pid_t key); +hash_item_pid *hash_first_pid(hash_table *ht, hash_pos *pos); +hash_item_pid *hash_next_pid(hash_pos *pos); +void *hash_remove_pos_pid(hash_pos *pos); + + +typedef struct hash_item_string { + char * key; + void *value; +} hash_item_string; + +void *hash_add_string(hash_table *ht, char * key, void *value); +void *hash_replace_string(hash_table *ht, char * key, void *value); +void *hash_lookup_string(hash_table *ht, char * key); +void *hash_remove_string(hash_table *ht, char * key); +hash_item_string *hash_first_string(hash_table *ht, hash_pos *pos); +hash_item_string *hash_next_string(hash_pos *pos); +void *hash_remove_pos_string(hash_pos *pos); + + +typedef struct hash_item_pidthr { + pidthr_t key; + void *value; +} hash_item_pidthr; + +void *hash_add_pidthr(hash_table *ht, pidthr_t key, void *value); +void *hash_replace_pidthr(hash_table *ht, pidthr_t key, void *value); +void *hash_lookup_pidthr(hash_table *ht, pidthr_t key); +void *hash_remove_pidthr(hash_table *ht, pidthr_t key); +hash_item_pidthr *hash_first_pidthr(hash_table *ht, hash_pos *pos); +hash_item_pidthr *hash_next_pidthr(hash_pos *pos); +void *hash_remove_pos_pidthr(hash_pos *pos); + +#if HAVE_LWPID_T + +typedef struct hash_item_lwpid { + lwpid_t key; + void *value; +} hash_item_lwpid; + +void *hash_add_lwpid(hash_table *ht, lwpid_t key, void *value); +void *hash_replace_lwpid(hash_table *ht, lwpid_t key, void *value); +void *hash_lookup_lwpid(hash_table *ht, lwpid_t key); +void *hash_remove_lwpid(hash_table *ht, lwpid_t key); +hash_item_lwpid *hash_first_lwpid(hash_table *ht, hash_pos *pos); +hash_item_lwpid *hash_next_lwpid(hash_pos *pos); +void *hash_remove_pos_lwpid(hash_pos *pos); + +#endif + + +#endif Index: contrib/top/hash.c =================================================================== --- contrib/top/hash.c +++ contrib/top/hash.c @@ -0,0 +1,2001 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* hash.m4c */ + +/* The file hash.c is generated from hash.m4c via the preprocessor M4 */ + +/* + * Hash table functions: init, add, lookup, first, next, sizeinfo. + * This is a conventional "bucket hash". The key is hashed in to a number + * less than or equal to the number of buckets and the result is used + * to index in to the array of buckets. Each bucket is a linked list + * that contains all the key/value pairs which hashed to that index. + */ + +#include "config.h" + +#if STDC_HEADERS +#include +#include +#define memzero(a, b) memset((a), 0, (b)) +#else /* !STDC_HEADERS */ +#ifdef HAVE_MEMCPY +#define memzero(a, b) memset((a), 0, (b)) +#else +#define memcpy(a, b, c) bcopy((b), (a), (c)) +#define memzero(a, b) bzero((a), (b)) +#define memcmp(a, b, c) bcmp((a), (b), (c)) +#endif /* HAVE_MEMCPY */ +#ifdef HAVE_STRINGS_H +#include +#else +#ifdef HAVE_STRING_H +#include +#endif +#endif +void *malloc(); +void free(); +char *strdup(); +#endif /* !STDC_HEADERS */ + +/* After all that there are still some systems that don't have NULL defined */ +#ifndef NULL +#define NULL 0 +#endif + +#ifdef HAVE_MATH_H +#include +#endif + +#if !HAVE_PID_T +typedef long pid_t; +#endif +#if !HAVE_ID_T +typedef long id_t; +#endif + +#include "hash.h" + + + + + + +static int +next_prime(int x) + +{ + double i, j; + int f; + + i = x; + while (i++) + { + f=1; + for (j=2; jnum_buckets); +} + +void ll_init(llist *q) + +{ + q->head = NULL; + q->count = 0; +} + +llistitem *ll_newitem(int size) + +{ + llistitem *qi; + + qi = (llistitem *)malloc(sizeof(llistitem) + size); + qi->datum = ((void *)qi + sizeof(llistitem)); + return qi; +} + +void ll_freeitem(llistitem *li) + +{ + free(li); +} + +void ll_add(llist *q, llistitem *new) + +{ + new->next = q->head; + q->head = new; + q->count++; +} + +void ll_extract(llist *q, llistitem *qi, llistitem *last) + +{ + if (last == NULL) + { + q->head = qi->next; + } + else + { + last->next = qi->next; + } + qi->next = NULL; + q->count--; +} + +#define LL_FIRST(q) ((q)->head) +llistitem * +ll_first(llist *q) + +{ + return q->head; +} + +#define LL_NEXT(q, qi) ((qi) != NULL ? (qi)->next : NULL) +llistitem * +ll_next(llist *q, llistitem *qi) + +{ + return (qi != NULL ? qi->next : NULL); +} + +#define LL_ISEMPTY(ll) ((ll)->count == 0) +int +ll_isempty(llist *ll) + +{ + return (ll->count == 0); +} + +/* + * hash_table *hash_create(int num) + * + * Creates a hash table structure with at least "num" buckets. + */ + +hash_table * +hash_create(int num) + +{ + hash_table *result; + bucket_t *b; + int bytes; + int i; + + /* create the resultant structure */ + result = (hash_table *)malloc(sizeof(hash_table)); + + /* adjust bucket count to be prime */ + num = next_prime(num); + + /* create the buckets */ + bytes = sizeof(bucket_t) * num; + result->buckets = b = (bucket_t *)malloc(bytes); + result->num_buckets = num; + + /* create each bucket as a linked list */ + i = num; + while (--i >= 0) + { + ll_init(&(b->list)); + b++; + } + + return result; +} + +/* + * unsigned int hash_count(hash_table *ht) + * + * Return total number of elements contained in hash table. + */ + +unsigned int +hash_count(hash_table *ht) + +{ + register int i = 0; + register int cnt = 0; + register bucket_t *bucket; + + bucket = ht->buckets; + while (i++ < ht->num_buckets) + { + cnt += bucket->list.count; + bucket++; + } + + return cnt; +} + +/* + * void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht) + * + * Fill in "sizes" with information about bucket lengths in hash + * table "max". + */ + +void +hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht) + +{ + register int i; + register int idx; + register bucket_t *bucket; + + memzero(sizes, max * sizeof(unsigned int)); + + bucket = ht->buckets; + i = 0; + while (i++ < ht->num_buckets) + { + idx = bucket->list.count; + sizes[idx >= max ? max-1 : idx]++; + bucket++; + } +} + + + + + + + +/* + * void hash_add_uint(hash_table *ht, unsigned int key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is unsigned int + */ + +void * +hash_add_uint(hash_table *ht, unsigned int key, void *value) + +{ + bucket_t *bucket; + hash_item_uint *hi; + hash_item_uint *h; + llist *ll; + llistitem *li; + llistitem *newli; + unsigned int k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_uint)); + hi = (hash_item_uint *)newli->datum; + + /* fill in the values */ + hi->key = key; + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_uint *)li->datum; + k1 = h->key; + if (key == k1) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_uint *)(li->datum))->value; + } +} + +/* + * void *hash_replace_uint(hash_table *ht, unsigned int key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is unsigned int + */ + +void * +hash_replace_uint(hash_table *ht, unsigned int key, void *value) + +{ + bucket_t *bucket; + hash_item_uint *hi; + llist *ll; + llistitem *li; + void *result = NULL; + unsigned int k1; + + /* find the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_uint *)li->datum; + k1 = hi->key; + if (key == k1) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_uint)); + hi = (hash_item_uint *)li->datum; + hi->key = key; + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_uint(hash_table *ht, unsigned int key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is unsigned int + */ + +void * +hash_lookup_uint(hash_table *ht, unsigned int key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_uint *h; + void *result; + unsigned int k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_uint *)li->datum; + k1 = h->key; + if (key == k1) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_uint(hash_table *ht, unsigned int key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_uint(hash_table *ht, unsigned int key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_uint *hi; + void *result; + unsigned int k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_uint *)li->datum; + k1 = hi->key; + if (key == k1) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + ; + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_uint *hash_first_uint(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_uint * +hash_first_uint(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_uint *)pos->ll_item->datum; +} + + +/* + * hash_item_uint *hash_next_uint(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_uint * +hash_next_uint(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_uint *)li->datum; +} + +/* + * void *hash_remove_pos_uint(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_uint(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_uint *hi; + unsigned int key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_uint *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + ; + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + + + +/* + * void hash_add_pid(hash_table *ht, pid_t key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is pid_t + */ + +void * +hash_add_pid(hash_table *ht, pid_t key, void *value) + +{ + bucket_t *bucket; + hash_item_pid *hi; + hash_item_pid *h; + llist *ll; + llistitem *li; + llistitem *newli; + pid_t k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_pid)); + hi = (hash_item_pid *)newli->datum; + + /* fill in the values */ + hi->key = key; + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_pid *)li->datum; + k1 = h->key; + if (key == k1) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_pid *)(li->datum))->value; + } +} + +/* + * void *hash_replace_pid(hash_table *ht, pid_t key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is pid_t + */ + +void * +hash_replace_pid(hash_table *ht, pid_t key, void *value) + +{ + bucket_t *bucket; + hash_item_pid *hi; + llist *ll; + llistitem *li; + void *result = NULL; + pid_t k1; + + /* find the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_pid *)li->datum; + k1 = hi->key; + if (key == k1) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_pid)); + hi = (hash_item_pid *)li->datum; + hi->key = key; + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_pid(hash_table *ht, pid_t key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is pid_t + */ + +void * +hash_lookup_pid(hash_table *ht, pid_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_pid *h; + void *result; + pid_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_pid *)li->datum; + k1 = h->key; + if (key == k1) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_pid(hash_table *ht, pid_t key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_pid(hash_table *ht, pid_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_pid *hi; + void *result; + pid_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_pid *)li->datum; + k1 = hi->key; + if (key == k1) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + ; + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_pid *hash_first_pid(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_pid * +hash_first_pid(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_pid *)pos->ll_item->datum; +} + + +/* + * hash_item_pid *hash_next_pid(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_pid * +hash_next_pid(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_pid *)li->datum; +} + +/* + * void *hash_remove_pos_pid(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_pid(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_pid *hi; + pid_t key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_pid *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + ; + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + + + +/* + * void hash_add_string(hash_table *ht, char * key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is char * + */ + +void * +hash_add_string(hash_table *ht, char * key, void *value) + +{ + bucket_t *bucket; + hash_item_string *hi; + hash_item_string *h; + llist *ll; + llistitem *li; + llistitem *newli; + char * k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_string)); + hi = (hash_item_string *)newli->datum; + + /* fill in the values */ + hi->key = strdup(key); + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[string_hash(ht, key)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_string *)li->datum; + k1 = h->key; + if (strcmp(key, k1) == 0) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_string *)(li->datum))->value; + } +} + +/* + * void *hash_replace_string(hash_table *ht, char * key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is char * + */ + +void * +hash_replace_string(hash_table *ht, char * key, void *value) + +{ + bucket_t *bucket; + hash_item_string *hi; + llist *ll; + llistitem *li; + void *result = NULL; + char * k1; + + /* find the bucket */ + bucket = &(ht->buckets[string_hash(ht, key)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_string *)li->datum; + k1 = hi->key; + if (strcmp(key, k1) == 0) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_string)); + hi = (hash_item_string *)li->datum; + hi->key = strdup(key); + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_string(hash_table *ht, char * key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is char * + */ + +void * +hash_lookup_string(hash_table *ht, char * key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_string *h; + void *result; + char * k1; + + result = NULL; + if ((bucket = &(ht->buckets[string_hash(ht, key)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_string *)li->datum; + k1 = h->key; + if (strcmp(key, k1) == 0) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_string(hash_table *ht, char * key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_string(hash_table *ht, char * key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_string *hi; + void *result; + char * k1; + + result = NULL; + if ((bucket = &(ht->buckets[string_hash(ht, key)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_string *)li->datum; + k1 = hi->key; + if (strcmp(key, k1) == 0) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + free(key); + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_string *hash_first_string(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_string * +hash_first_string(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_string *)pos->ll_item->datum; +} + + +/* + * hash_item_string *hash_next_string(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_string * +hash_next_string(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_string *)li->datum; +} + +/* + * void *hash_remove_pos_string(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_string(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_string *hi; + char * key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_string *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + free(key); + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + + + +/* + * void hash_add_pidthr(hash_table *ht, pidthr_t key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is pidthr_t + */ + +void * +hash_add_pidthr(hash_table *ht, pidthr_t key, void *value) + +{ + bucket_t *bucket; + hash_item_pidthr *hi; + hash_item_pidthr *h; + llist *ll; + llistitem *li; + llistitem *newli; + pidthr_t k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_pidthr)); + hi = (hash_item_pidthr *)newli->datum; + + /* fill in the values */ + hi->key = key; + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_pidthr *)li->datum; + k1 = h->key; + if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_pidthr *)(li->datum))->value; + } +} + +/* + * void *hash_replace_pidthr(hash_table *ht, pidthr_t key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is pidthr_t + */ + +void * +hash_replace_pidthr(hash_table *ht, pidthr_t key, void *value) + +{ + bucket_t *bucket; + hash_item_pidthr *hi; + llist *ll; + llistitem *li; + void *result = NULL; + pidthr_t k1; + + /* find the bucket */ + bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_pidthr *)li->datum; + k1 = hi->key; + if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_pidthr)); + hi = (hash_item_pidthr *)li->datum; + hi->key = key; + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_pidthr(hash_table *ht, pidthr_t key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is pidthr_t + */ + +void * +hash_lookup_pidthr(hash_table *ht, pidthr_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_pidthr *h; + void *result; + pidthr_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_pidthr *)li->datum; + k1 = h->key; + if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_pidthr(hash_table *ht, pidthr_t key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_pidthr(hash_table *ht, pidthr_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_pidthr *hi; + void *result; + pidthr_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_pidthr *)li->datum; + k1 = hi->key; + if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + ; + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_pidthr *hash_first_pidthr(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_pidthr * +hash_first_pidthr(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_pidthr *)pos->ll_item->datum; +} + + +/* + * hash_item_pidthr *hash_next_pidthr(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_pidthr * +hash_next_pidthr(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_pidthr *)li->datum; +} + +/* + * void *hash_remove_pos_pidthr(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_pidthr(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_pidthr *hi; + pidthr_t key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_pidthr *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + ; + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + +#if HAVE_LWPID_T + + +/* + * void hash_add_lwpid(hash_table *ht, lwpid_t key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is lwpid_t + */ + +void * +hash_add_lwpid(hash_table *ht, lwpid_t key, void *value) + +{ + bucket_t *bucket; + hash_item_lwpid *hi; + hash_item_lwpid *h; + llist *ll; + llistitem *li; + llistitem *newli; + lwpid_t k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_lwpid)); + hi = (hash_item_lwpid *)newli->datum; + + /* fill in the values */ + hi->key = key; + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_lwpid *)li->datum; + k1 = h->key; + if (key == k1) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_lwpid *)(li->datum))->value; + } +} + +/* + * void *hash_replace_lwpid(hash_table *ht, lwpid_t key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is lwpid_t + */ + +void * +hash_replace_lwpid(hash_table *ht, lwpid_t key, void *value) + +{ + bucket_t *bucket; + hash_item_lwpid *hi; + llist *ll; + llistitem *li; + void *result = NULL; + lwpid_t k1; + + /* find the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_lwpid *)li->datum; + k1 = hi->key; + if (key == k1) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_lwpid)); + hi = (hash_item_lwpid *)li->datum; + hi->key = key; + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_lwpid(hash_table *ht, lwpid_t key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is lwpid_t + */ + +void * +hash_lookup_lwpid(hash_table *ht, lwpid_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_lwpid *h; + void *result; + lwpid_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_lwpid *)li->datum; + k1 = h->key; + if (key == k1) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_lwpid(hash_table *ht, lwpid_t key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_lwpid(hash_table *ht, lwpid_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_lwpid *hi; + void *result; + lwpid_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_lwpid *)li->datum; + k1 = hi->key; + if (key == k1) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + ; + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_lwpid *hash_first_lwpid(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_lwpid * +hash_first_lwpid(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_lwpid *)pos->ll_item->datum; +} + + +/* + * hash_item_lwpid *hash_next_lwpid(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_lwpid * +hash_next_lwpid(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_lwpid *)li->datum; +} + +/* + * void *hash_remove_pos_lwpid(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_lwpid(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_lwpid *hi; + lwpid_t key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_lwpid *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + ; + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + +#endif Index: contrib/top/install-sh =================================================================== --- contrib/top/install-sh +++ contrib/top/install-sh @@ -1,69 +0,0 @@ -#!/bin/sh -# -# this shell script is amazingly similar to the old and lamented -# BSD "install" command. It recognized the following options: -# -# -o target file owner -# -m target file mode -# -g target file group owner -# -# -# scan the options -# -while [ $# -gt 0 ]; do - case $1 in - -o) - owner=$2 - shift ; shift - ;; - - -m) - mode=$2 - shift; shift - ;; - - -g) - group=$2 - shift ; shift - ;; - - -*) - echo "install: unknown option $1" - exit - ;; - - *) - break - ;; - esac -done -# -# we need two more: filename and destination -# -if [ $# -ne 2 ]; then - echo "Usage: install [ -o owner ] [ -m mode ] [ -g group ] file destination" - exit -fi -# -# first, copy -# -cp $1 $2 -# -# normalize the name -# -dest=$2 -if [ -d $2 ]; then - dest=$2/`basename $1` -fi -# -# do optional things -# -if [ "$owner" ]; then - chown $owner $dest -fi -if [ "$group" ]; then - chgrp $group $dest -fi -if [ "$mode" ]; then - chmod $mode $dest -fi Index: contrib/top/layout.h =================================================================== --- contrib/top/layout.h +++ contrib/top/layout.h @@ -1,33 +1,73 @@ /* - * Top - a top users display for Berkeley Unix - * - * This file defines the locations on tne screen for various parts of the - * display. These definitions are used by the routines in "display.c" for - * cursor addressing. + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -extern int x_lastpid; /* 10 */ -extern int y_lastpid; /* 0 */ -extern int x_loadave; /* 33 */ -extern int x_loadave_nompid; /* 15 */ -extern int y_loadave; /* 0 */ -extern int x_procstate; /* 0 */ -extern int y_procstate; /* 1 */ -extern int x_brkdn; /* 15 */ -extern int y_brkdn; /* 1 */ -extern int x_mem; /* 5 */ -extern int y_mem; /* 3 */ -extern int x_arc; /* 5 */ -extern int y_arc; /* 4 */ -extern int x_swap; /* 6 */ -extern int y_swap; /* 4 */ -extern int y_message; /* 5 */ -extern int x_header; /* 0 */ -extern int y_header; /* 6 */ -extern int x_idlecursor; /* 0 */ -extern int y_idlecursor; /* 5 */ -extern int y_procs; /* 7 */ +/* + * Top - a top users display for Unix + * + * This file defines the default locations on the screen for various parts + * of the display. These definitions are used by the routines in "display.c" + * for cursor addressing. + */ + +#define X_LASTPID 10 +#define Y_LASTPID 0 +#define X_LASTPIDWIDTH 13 +#define X_LOADAVE 27 +#define Y_LOADAVE 0 +#define X_LOADAVEWIDTH 7 +#define X_MINIBAR 50 +#define Y_MINIBAR 0 +#define X_UPTIME 48 +#define Y_UPTIME 0 +#define X_PROCSTATE 15 +#define Y_PROCSTATE 1 +#define X_BRKDN 15 +#define Y_BRKDN 1 +#define X_CPUSTATES 0 +#define Y_CPUSTATES 2 +#define X_KERNEL 8 +#define Y_KERNEL 3 +#define X_MEM 8 +#define Y_MEM 3 +#define X_ARC 5 +#define Y_ARC 4 +#define X_SWAP 6 +#define Y_SWAP 4 +#define Y_MESSAGE 4 +#define X_HEADER 0 +#define Y_HEADER 5 +#define X_IDLECURSOR 0 +#define Y_IDLECURSOR 4 +#define Y_PROCS 6 -extern int y_cpustates; /* 2 */ Index: contrib/top/loadavg.h =================================================================== --- contrib/top/loadavg.h +++ contrib/top/loadavg.h @@ -1,4 +1,36 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Top - a top users display for Berkeley Unix * * Defines required to access load average figures. @@ -19,10 +51,10 @@ * * Defined types: load_avg for load averages, pctcpu for cpu percentages. */ -#if defined(mips) && !(defined(NetBSD) || defined(FreeBSD)) +#if defined(mips) && !defined(NetBSD) # include # if defined(FBITS) && !defined(FSCALE) -# define FSCALE (1 << FBITS) /* RISC/os on mips */ +# define FSCALE (1 << FBITS) /* mips */ # endif #endif @@ -47,7 +79,7 @@ #endif #ifdef FIXED_LOADAVG - typedef fixpt_t load_avg; + typedef long load_avg; # define loaddouble(la) ((double)(la) / FIXED_LOADAVG) # define intload(i) ((int)((i) * FIXED_LOADAVG)) #else Index: contrib/top/m-template =================================================================== --- contrib/top/m-template +++ contrib/top/m-template @@ -1,241 +0,0 @@ -/* - * top - a top users display for Unix - * - * THIS IS A TEMPLATE FILE FOR A MACHINE DEPENDENT (m_...c) FILE - * - * SYNOPSIS: one line description of machine this module works with - * - * DESCRIPTION: - * Detailed description of this machine dependent module. - * It can be multiple lines, but a blank comment line (one with only an - * asterisk) is considered to end it. Place here a complete list of - * the machines and OS versions that this module works on. - * - * LIBS: list of special libraries to include at link step (REMOVE THIS LINE IF NOT NEEDED) - * - * AUTHOR: your name and - */ - -#include "top.h" -#include "machine.h" - - -/* - * These definitions control the format of the per-process area - */ - -static char header[] = - " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; -/* 0123456 -- field to fill in starts at header+6 */ -#define UNAME_START 6 - -#define Proc_format \ - "%5d %-8.8s %3d %4d%6dK %4dK %-5s%4d:%02d %5.2f%% %5.2f%% %.14s" - -/* these are for detailing the process states */ - -int process_states[?]; -char *procstatenames[] = { - "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ", - " zombie, ", " stopped, ", - NULL -}; - -/* these are for detailing the cpu states */ - -int cpu_states[?]; -char *cpustatenames[] = { - "user", "nice", "system", "idle", - NULL -}; - -/* these are for detailing the memory statistics */ - -int memory_stats[?]; -char *memorynames[] = { - "K available, ", "K in use, ", "K free, ", "K locked", NULL -}; - -/* useful externals */ -extern int errno; -extern char *sys_errlist[]; - -long lseek(); -long time(); -long percentages(); - -machine_init(statics) - -struct statics *statics; - -{ - return(0); -} - -char *format_header(uname_field) - -register char *uname_field; - -{ - register char *ptr; - - ptr = header + UNAME_START; - while (*uname_field != '\0') - { - *ptr++ = *uname_field++; - } - - return(header); -} - -get_system_info(si) - -struct system_info *si; - -{ -} - -static struct handle handle; - -caddr_t get_process_info(si, sel, compare) - -struct system_info *si; -struct process_select *sel; -int (*compare)(); - -{ - return((caddr_t)&handle); -} - -char fmt[128]; /* static area where result is built */ - -/* define what weighted cpu is. */ -#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \ - ((pct) / (1.0 - exp((pp)->p_time * logcpu)))) - -char *format_next_process(handle, get_userid) - -caddr_t handle; -char *(*get_userid)(); - -{ - return(fmt); -} - -/* - * getkval(offset, ptr, size, refstr) - get a value out of the kernel. - * "offset" is the byte offset into the kernel for the desired value, - * "ptr" points to a buffer into which the value is retrieved, - * "size" is the size of the buffer (and the object to retrieve), - * "refstr" is a reference string used when printing error meessages, - * if "refstr" starts with a '!', then a failure on read will not - * be fatal (this may seem like a silly way to do things, but I - * really didn't want the overhead of another argument). - * - */ - -getkval(offset, ptr, size, refstr) - -unsigned long offset; -int *ptr; -int size; -char *refstr; - -{ - if (kvm_read(kd, offset, ptr, size) != size) - { - if (*refstr == '!') - { - return(0); - } - else - { - fprintf(stderr, "top: kvm_read for %s: %s\n", - refstr, sys_errlist[errno]); - quit(23); - } - } - return(1); -} - -/* comparison routine for qsort */ -/* NOTE: this is specific to the BSD proc structure, but it should - give you a good place to start. */ - -/* - * proc_compare - comparison function for "qsort" - * Compares the resource consumption of two processes using five - * distinct keys. The keys (in descending order of importance) are: - * percent cpu, cpu ticks, state, resident set size, total virtual - * memory usage. The process states are ordered as follows (from least - * to most important): WAIT, zombie, sleep, stop, start, run. The - * array declaration below maps a process state index into a number - * that reflects this ordering. - */ - -static unsigned char sorted_state[] = -{ - 0, /* not used */ - 3, /* sleep */ - 1, /* ABANDONED (WAIT) */ - 6, /* run */ - 5, /* start */ - 2, /* zombie */ - 4 /* stop */ -}; - -proc_compare(pp1, pp2) - -struct proc **pp1; -struct proc **pp2; - -{ - register struct proc *p1; - register struct proc *p2; - register int result; - register pctcpu lresult; - - /* remove one level of indirection */ - p1 = *pp1; - p2 = *pp2; - - /* compare percent cpu (pctcpu) */ - if ((lresult = p2->p_pctcpu - p1->p_pctcpu) == 0) - { - /* use cpticks to break the tie */ - if ((result = p2->p_cpticks - p1->p_cpticks) == 0) - { - /* use process state to break the tie */ - if ((result = sorted_state[p2->p_stat] - - sorted_state[p1->p_stat]) == 0) - { - /* use priority to break the tie */ - if ((result = p2->p_pri - p1->p_pri) == 0) - { - /* use resident set size (rssize) to break the tie */ - if ((result = p2->p_rssize - p1->p_rssize) == 0) - { - /* use total memory to break the tie */ - result = PROCSIZE(p2) - PROCSIZE(p1); - } - } - } - } - } - else - { - result = lresult < 0 ? -1 : 1; - } - - return(result); -} - -proc_owner(pid) - -int pid; - -{ - /* returns uid of owner of process pid */ - return(uid); -} - Index: contrib/top/m_freebsd.c =================================================================== --- contrib/top/m_freebsd.c +++ contrib/top/m_freebsd.c @@ -0,0 +1,1880 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/* + * top - a top users display for Unix + * + * SYNOPSIS: For FreeBSD 5.x, 6.x, 7.x, 8.x + * + * DESCRIPTION: + * Originally written for BSD4.4 system by Christos Zoulas. + * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider + * Order support hacked in from top-3.5beta6/machine/m_aix41.c + * by Monte Mitzelfelt + * Ported to FreeBSD 5.x and higher by William LeFebvre + * + * AUTHOR: Christos Zoulas + * Steven Wallace + * Wolfram Schneider + */ + + +#include +#include +#include +#include + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +/* Swap */ +#include +#include + +#include /* for changes in kernel structures */ + +#include "top.h" +#include "machine.h" +#include "utils.h" +#include "username.h" +#include "hash.h" +#include "display.h" + +extern char* printable __P((char *)); +int swapmode __P((int *retavail, int *retfree)); +static int smpmode; +static int namelength; + +/* + * Versions prior to 5.x do not track threads in kinfo_proc, so we + * simply do not display any information about them. + * Versions 5.x, 6.x, and 7.x track threads but the data reported + * as runtime for each thread is actually per-process and is just + * duplicated across all threads. It would be very wrong to show + * this data individually for each thread. Therefore we will show + * a THR column (number of threads) but not provide any sort of + * per-thread display. We distinguish between these three ways of + * handling threads as follows: HAS_THREADS indicates that the + * system has and tracks kernel threads (a THR column will appear + * in the display). HAS_SHOWTHREADS indicates that the system + * reports correct per-thread information and we will provide a + * per-thread display (the 'H' and 't' command) upon request. + * HAS_SHOWTHREADS implies HAS_THREADS. + */ + +/* HAS_THREADS for anything 5.x and up */ +#if OSMAJOR >= 5 +#define HAS_THREADS +#endif + +/* HAS_SHOWTHREADS for anything 8.x and up */ +#if OSMAJOR >=8 +#define HAS_SHOWTHREADS +#endif + +/* get_process_info passes back a handle. This is what it looks like: */ + +struct handle +{ + struct kinfo_proc **next_proc; /* points to next valid proc pointer */ + int remaining; /* number of pointers remaining */ +}; + +/* declarations for load_avg */ +#include "loadavg.h" + +/* + * Macros to access process information: + * In versions 4.x and earlier the kinfo_proc structure was a collection of + * substructures (kp_proc and kp_eproc). Starting with 5.0 kinfo_proc was + * redesigned and "flattene" so that most of the information was available + * in a single structure. We use macros to access the various types of + * information and define these macros according to the OS revision. The + * names PP, EP, and VP are due to the fact that information was originally + * contained in the different substructures. We retain these names in the + * code for backward compatibility. These macros use ANSI concatenation. + * PP: proc + * EP: extented proc + * VP: vm (virtual memory information) + * PRUID: Real uid + * RP: rusage + * PPCPU: where we store calculated cpu% data + * SPPTR: where we store pointer to extra calculated data + * SP: access to the extra calculated data pointed to by SPPTR + */ +#if OSMAJOR <= 4 +#define PP(pp, field) ((pp)->kp_proc . p_##field) +#define EP(pp, field) ((pp)->kp_eproc . e_##field) +#define VP(pp, field) ((pp)->kp_eproc.e_vm . vm_##field) +#define PRUID(pp) ((pp)->kp_eproc.e_pcred.p_ruid) +#else +#define PP(pp, field) ((pp)->ki_##field) +#define EP(pp, field) ((pp)->ki_##field) +#define VP(pp, field) ((pp)->ki_##field) +#define PRUID(pp) ((pp)->ki_ruid) +#define RP(pp, field) ((pp)->ki_rusage.ru_##field) +#define PPCPU(pp) ((pp)->ki_sparelongs[0]) +#define SPPTR(pp) ((pp)->ki_spareptrs[0]) +#define SP(pp, field) (((struct save_proc *)((pp)->ki_spareptrs[0]))->sp_##field) +#endif + +/* what we consider to be process size: */ +#if OSMAJOR <= 4 +#define PROCSIZE(pp) (VP((pp), map.size) / 1024) +#else +#define PROCSIZE(pp) (((pp)->ki_size) / 1024) +#endif + +/* calculate a per-second rate using milliseconds */ +#define per_second(n, msec) (((n) * 1000) / (msec)) + +/* process state names for the "STATE" column of the display */ +/* the extra nulls in the string "run" are for adding a slash and + the processor number when needed */ + +char *state_abbrev[] = +{ + "?", "START", "RUN", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK" +}; +#define NUM_STATES 8 + +/* kernel access */ +static kvm_t *kd; + +/* these are for dealing with sysctl-based data */ +#define MAXMIBLEN 8 +struct sysctl_mib { + char *name; + int mib[MAXMIBLEN]; + size_t miblen; +}; +static struct sysctl_mib mibs[] = { + { "vm.stats.sys.v_swtch" }, +#define V_SWTCH 0 + { "vm.stats.sys.v_trap" }, +#define V_TRAP 1 + { "vm.stats.sys.v_intr" }, +#define V_INTR 2 + { "vm.stats.sys.v_soft" }, +#define V_SOFT 3 + { "vm.stats.vm.v_forks" }, +#define V_FORKS 4 + { "vm.stats.vm.v_vforks" }, +#define V_VFORKS 5 + { "vm.stats.vm.v_rforks" }, +#define V_RFORKS 6 + { "vm.stats.vm.v_vm_faults" }, +#define V_VM_FAULTS 7 + { "vm.stats.vm.v_swapin" }, +#define V_SWAPIN 8 + { "vm.stats.vm.v_swapout" }, +#define V_SWAPOUT 9 + { "vm.stats.vm.v_tfree" }, +#define V_TFREE 10 + { "vm.stats.vm.v_vnodein" }, +#define V_VNODEIN 11 + { "vm.stats.vm.v_vnodeout" }, +#define V_VNODEOUT 12 + { "vm.stats.vm.v_active_count" }, +#define V_ACTIVE_COUNT 13 + { "vm.stats.vm.v_inactive_count" }, +#define V_INACTIVE_COUNT 14 + { "vm.stats.vm.v_wire_count" }, +#define V_WIRE_COUNT 15 + { "vm.stats.vm.v_cache_count" }, +#define V_CACHE_COUNT 16 + { "vm.stats.vm.v_free_count" }, +#define V_FREE_COUNT 17 + { "vm.stats.vm.v_swappgsin" }, +#define V_SWAPPGSIN 18 + { "vm.stats.vm.v_swappgsout" }, +#define V_SWAPPGSOUT 19 + { "vfs.bufspace" }, +#define VFS_BUFSPACE 20 + { "kern.cp_time" }, +#define K_CP_TIME 21 +#ifdef HAS_SHOWTHREADS + { "kern.proc.all" }, +#else + { "kern.proc.proc" }, +#endif +#define K_PROC 22 + { "kern.lastpid" }, +#define K_LASTPID 23 +/* ARC sysctls. */ + { "kstat.zfs.misc.arcstats.size" }, +#define ZFS_ARC_SIZE 24 + { "vfs.zfs.mfu_size" }, +#define ZFS_MFU_SIZE 25 + { "vfs.zfs.mru_size" }, +#define ZFS_MRU_SIZE 26 + { "vfs.zfs.anon_size" }, +#define ZFS_ANON_SIZE 27 + { "kstat.zfs.misc.arcstats.hdr_size" }, +#define ZFS_HDR_SIZE 28 + { "kstat.zfs.misc.arcstats.l2_hdr_size" }, +#define ZFS_L2_HDR_SIZE 29 + { "kstat.zfs.misc.arcstats.other_size" }, +#define ZFS_OTHER_SIZE 30 + { NULL } +}; + + +/* these are for calculating cpu state percentages */ + +static long cp_time[CPUSTATES]; +static long cp_old[CPUSTATES]; +static long cp_diff[CPUSTATES]; + +/* these are for detailing the process states */ + +int process_states[8]; +char *procstatenames[] = { + "", " starting, ", " running, ", " sleeping, ", " stopped, ", " zombie, ", + " waiting, ", " locked, ", + NULL +}; + +/* these are for detailing the cpu states */ + +int cpu_states[CPUSTATES]; +char *cpustatenames[] = { + "user", "nice", "system", "interrupt", "idle", NULL +}; + +/* these are for detailing the kernel information */ + +int kernel_stats[9]; +char *kernelnames[] = { + " ctxsw, ", " trap, ", " intr, ", " soft, ", " fork, ", + " flt, ", " pgin, ", " pgout, ", " fr", + NULL +}; + +/* these are for detailing the memory statistics */ + +long memory_stats[7]; +char *memorynames[] = { + "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free", + NULL +}; + +static int arc_enabled; +long arc_stats[7]; +char *arcnames[] = { + "K Total, ", "K MFU, ", "K MRU, ", "K Anon, ", "K Header, ", "K Other", + NULL +}; + +long swap_stats[7]; +char *swapnames[] = { +/* 0 1 2 3 4 5 */ + "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", + NULL +}; + + +/* + * pbase points to the array that holds the kinfo_proc structures. pref + * (pronounced p-ref) points to an array of kinfo_proc pointers and is where + * we build up a list of processes we wish to display. Both pbase and pref are + * potentially resized on every call to get_process_info. psize is the number + * of procs for which we currently have space allocated. pref_len is the number + * of valid pointers in pref (this is used by proc_owner). We start psize off + * at -1 to ensure that space gets allocated on the first call to + * get_process_info. + */ + +static int psize = -1; +static int pref_len; +static struct kinfo_proc *pbase = NULL; +static struct kinfo_proc **pref = NULL; + +/* this structure retains information from the proc array between samples */ +struct save_proc { + pid_t sp_pid; + u_int64_t sp_runtime; + long sp_vcsw; + long sp_ivcsw; + long sp_inblock; + long sp_oublock; + long sp_majflt; + long sp_totalio; + long sp_old_nvcsw; + long sp_old_nivcsw; + long sp_old_inblock; + long sp_old_oublock; + long sp_old_majflt; +}; +hash_table *procs; + +struct proc_field { + char *name; + int width; + int rjust; + int min_screenwidth; + int (*format)(char *, int, struct kinfo_proc *); +}; + +/* these are for getting the memory statistics */ + +static int pagesize; /* kept from getpagesize */ +static int pageshift; /* log base 2 of the pagesize */ + +/* define pagetok in terms of pageshift */ + +#define pagetok(size) ((size) << pageshift) + +/* things that we track between updates */ +static u_int ctxsws = 0; +static u_int traps = 0; +static u_int intrs = 0; +static u_int softs = 0; +static u_int64_t forks = 0; +static u_int pfaults; +static u_int pagein; +static u_int pageout; +static u_int tfreed; +static int swappgsin = -1; +static int swappgsout = -1; +extern struct timeval timeout; +static struct timeval lasttime = { 0, 0 }; +static long elapsed_time; +static long elapsed_msecs; +static int lastpid; + +/* things that we track during an update */ +static long total_io; +static int show_fullcmd; +static struct handle handle; +static int username_length; +static int show_usernames; +static int display_mode; +static int *display_fields; +#ifdef HAS_SHOWTHREADS +static int show_threads = 0; +#endif + + +/* sorting orders. first is default */ +char *ordernames[] = { + "cpu", "size", "res", "time", "pri", "io", "pid", NULL +}; + +/* compare routines */ +int proc_compare(), compare_size(), compare_res(), compare_time(), + compare_prio(), compare_io(), compare_pid(); + +int (*proc_compares[])() = { + proc_compare, + compare_size, + compare_res, + compare_time, + compare_prio, + compare_io, + compare_pid, + NULL +}; + +/* swap related calculations */ + +static int mib_swapinfo[16]; +static int *mib_swapinfo_idx; +static int mib_swapinfo_size = 0; + +void +swap_init() + +{ + size_t m; + + m = sizeof(mib_swapinfo) / sizeof(mib_swapinfo[0]); + if (sysctlnametomib("vm.swap_info", mib_swapinfo, &m) != -1) + { + mib_swapinfo_size = m + 1; + mib_swapinfo_idx = &(mib_swapinfo[m]); + } +} + +int +swap_getdata(long long *retavail, long long *retfree) + +{ + int n; + size_t size; + long long total = 0; + long long used = 0; + struct xswdev xsw; + + n = 0; + if (mib_swapinfo_size > 0) + { + *mib_swapinfo_idx = 0; + while (size = sizeof(xsw), + sysctl(mib_swapinfo, mib_swapinfo_size, &xsw, &size, NULL, 0) != -1) + { + dprintf("swap_getdata: swaparea %d: nblks %d, used %d\n", + n, xsw.xsw_nblks, xsw.xsw_used); + total += (long long)xsw.xsw_nblks; + used += (long long)xsw.xsw_used; + *mib_swapinfo_idx = ++n; + } + + *retavail = pagetok(total); + *retfree = pagetok(total) - pagetok(used); + + if (total > 0) + { + n = (int)((double)used * 100.0 / (double)total); + } + else + { + n = 0; + } + } + else + { + *retavail = 0; + *retfree = 0; + } + + dprintf("swap_getdata: avail %lld, free %lld, %d%%\n", + *retavail, *retfree, n); + return(n); +} + +/* + * getkval(offset, ptr, size) - get a value out of the kernel. + * "offset" is the byte offset into the kernel for the desired value, + * "ptr" points to a buffer into which the value is retrieved, + * "size" is the size of the buffer (and the object to retrieve). + * Return 0 on success, -1 on any kind of failure. + */ + +static int +getkval(unsigned long offset, int *ptr, int size) + +{ + if (kd != NULL) + { + if (kvm_read(kd, offset, (char *) ptr, size) == size) + { + return(0); + } + } + return(-1); +} + +int +get_sysctl_mibs() + +{ + struct sysctl_mib *mp; + size_t len; + + mp = mibs; + while (mp->name != NULL) + { + len = MAXMIBLEN; + if (sysctlnametomib(mp->name, mp->mib, &len) == -1) + { + message_error(" sysctlnametomib: %s", strerror(errno)); + return -1; + } + mp->miblen = len; + mp++; + } + return 0; +} + +int +get_sysctl(int idx, void *v, size_t l) + +{ + struct sysctl_mib *m; + size_t len; + + m = &(mibs[idx]); + len = l; + if (sysctl(m->mib, m->miblen, v, &len, NULL, 0) == -1) + { + message_error(" sysctl: %s", strerror(errno)); + return -1; + } + return len; +} + +size_t +get_sysctlsize(int idx) + +{ + size_t len; + struct sysctl_mib *m; + + m = &(mibs[idx]); + if (sysctl(m->mib, m->miblen, NULL, &len, NULL, 0) == -1) + { + message_error(" sysctl (size): %s", strerror(errno)); + len = 0; + } + return len; +} + +int +fmt_pid(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6d", PP(pp, pid)); +} + +int +fmt_jid(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6d", PP(pp, jid)); +} + +int +fmt_username(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%-*.*s", + username_length, username_length, username(PRUID(pp))); +} + +int +fmt_uid(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6d", PRUID(pp)); +} + +int +fmt_thr(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%3d", PP(pp, numthreads)); +} + +int +fmt_pri(char *buf, int sz, struct kinfo_proc *pp) + +{ +#if OSMAJOR <= 4 + return snprintf(buf, sz, "%3d", PP(pp, priority)); +#else + return snprintf(buf, sz, "%3d", PP(pp, pri.pri_level)); +#endif +} + +int +fmt_nice(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%4d", PP(pp, nice) - NZERO); +} + +int +fmt_size(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%5s", format_k(PROCSIZE(pp))); +} + +int +fmt_res(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%5s", format_k(pagetok(VP(pp, rssize)))); +} + +int +fmt_state(char *buf, int sz, struct kinfo_proc *pp) + +{ + int state; + char status[16]; + + state = PP(pp, stat); + switch(state) + { + case SRUN: + if (smpmode && PP(pp, oncpu) != 0xff) + sprintf(status, "CPU%d", PP(pp, oncpu)); + else + strcpy(status, "RUN"); + break; + + case SSLEEP: + if (EP(pp, wmesg) != NULL) { + sprintf(status, "%.6s", EP(pp, wmesg)); + break; + } + /* fall through */ + default: + if (state >= 0 && state < NUM_STATES) + sprintf(status, "%.6s", state_abbrev[(unsigned char) state]); + else + sprintf(status, "?%-5d", state); + break; + } + + return snprintf(buf, sz, "%-6.6s", status); +} + +int +fmt_flags(char *buf, int sz, struct kinfo_proc *pp) + +{ + long flag; + char chrs[12]; + char *p; + + flag = PP(pp, flag); + p = chrs; + if (PP(pp, nice) < NZERO) + *p++ = '<'; + else if (PP(pp, nice) > NZERO) + *p++ = 'N'; + if (flag & P_TRACED) + *p++ = 'X'; + if (flag & P_WEXIT && PP(pp, stat) != SZOMB) + *p++ = 'E'; + if (flag & P_PPWAIT) + *p++ = 'V'; + if (flag & P_SYSTEM || PP(pp, lock) > 0) + *p++ = 'L'; + if (PP(pp, kiflag) & KI_SLEADER) + *p++ = 's'; + if (flag & P_CONTROLT) + *p++ = '+'; + if (flag & P_JAILED) + *p++ = 'J'; + *p = '\0'; + + return snprintf(buf, sz, "%-3.3s", chrs); +} + +int +fmt_c(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%1x", PP(pp, lastcpu)); +} + +int +fmt_time(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6s", + format_time((PP(pp, runtime) + 500000) / 1000000)); +} + +int +fmt_cpu(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%5.2f%%", (double)PPCPU(pp) / 100.0); +} + +int +fmt_command(char *buf, int sz, struct kinfo_proc *pp) + +{ + int inmem; + char cmd[MAX_COLS]; + char *bufp; + struct pargs pargs; + int len; + +#if OSMAJOR <= 4 + inmem = (PP(pp, flag) & P_INMEM); +#else + inmem = (PP(pp, sflag) & PS_INMEM); +#endif + + if (show_fullcmd && inmem) + { + /* get the pargs structure */ + if (getkval((unsigned long)PP(pp, args), (int *)&pargs, sizeof(pargs)) != -1) + { + /* determine workable length */ + if ((len = pargs.ar_length) >= MAX_COLS) + { + len = MAX_COLS - 1; + } + + /* get the string from that */ + if (len > 0 && getkval((unsigned long)PP(pp, args) + + sizeof(pargs.ar_ref) + + sizeof(pargs.ar_length), + (int *)cmd, len) != -1) + { + /* successfull retrieval: now convert nulls in to spaces */ + bufp = cmd; + while (len-- > 0) + { + if (*bufp == '\0') + { + *bufp = ' '; + } + bufp++; + } + + /* null terminate cmd */ + *--bufp = '\0'; + + /* format cmd as our answer */ + return snprintf(buf, sz, "%s", cmd); + } + } + } + /* Show the thread name in {}. */ + if (show_threads) + return snprintf(buf, sz, inmem ? "%s{%s}" : "<%s{%s}>", + printable(PP(pp, comm)), printable(PP(pp,tdname))); + + /* for anything else we just display comm */ + else + return snprintf(buf, sz, inmem ? "%s" : "<%s>", printable(PP(pp, comm))); +} + +int +fmt_vcsw(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, vcsw), elapsed_msecs)); +} + +int +fmt_ivcsw(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, ivcsw), elapsed_msecs)); +} + +int +fmt_read(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, inblock), elapsed_msecs)); +} + +int +fmt_write(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, oublock), elapsed_msecs)); +} + +int +fmt_fault(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, majflt), elapsed_msecs)); +} + +int +fmt_iototal(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, totalio), elapsed_msecs)); +} + +int +fmt_iopct(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6.2f", (SP(pp, totalio) * 100.) / total_io); +} + + +struct proc_field proc_field[] = { + { "PID", 6, 1, 0, fmt_pid }, + { "JID", 6, 1, 0, fmt_jid }, + { "USERNAME", 8, 0, 0, fmt_username }, +#define FIELD_USERNAME 2 + { "UID", 6, 1, 0, fmt_uid }, +#define FIELD_UID 3 + { "THR", 3, 1, 0, fmt_thr }, + { "PRI", 3, 1, 0, fmt_pri }, + { "NICE", 4, 1, 0, fmt_nice }, + { "SIZE", 5, 1, 0, fmt_size }, + { "RES", 5, 1, 0, fmt_res }, + { "STATE", 6, 0, 0, fmt_state }, + { "FLG", 3, 0, 84, fmt_flags }, + { "C", 1, 0, 0, fmt_c }, + { "TIME", 6, 1, 0, fmt_time }, + { "CPU", 6, 1, 0, fmt_cpu }, + { "COMMAND", 7, 0, 0, fmt_command }, + { "VCSW", 6, 1, 0, fmt_vcsw }, + { "IVCSW", 6, 1, 0, fmt_ivcsw }, + { "READ", 6, 1, 0, fmt_read }, + { "WRITE", 6, 1, 0, fmt_write }, + { "FAULT", 6, 1, 0, fmt_fault }, + { "TOTAL", 6, 1, 0, fmt_iototal }, + { "PERCENT", 7, 1, 0, fmt_iopct }, + { NULL, 0, 0, 0, NULL } +}; +#define MAX_FIELDS 25 + +static int mode0_display[MAX_FIELDS]; +static int mode0thr_display[MAX_FIELDS]; +static int mode1_display[MAX_FIELDS]; + +int +field_index(char *col) + +{ + struct proc_field *fp; + int i = 0; + + fp = proc_field; + while (fp->name != NULL) + { + if (strcmp(col, fp->name) == 0) + { + return i; + } + fp++; + i++; + } + + return -1; +} + +void +field_subst(int *fp, int old, int new) + +{ + while (*fp != -1) + { + if (*fp == old) + { + *fp = new; + } + fp++; + } +} + +int +machine_init(struct statics *statics) + +{ + int i = 0; + size_t len; + + struct timeval boottime; + + len = sizeof(smpmode); + if ((sysctlbyname("machdep.smp_active", &smpmode, &len, NULL, 0) < 0 && + sysctlbyname("kern.smp.active", &smpmode, &len, NULL, 0) < 0) || + len != sizeof(smpmode)) + { + smpmode = 0; + } + smpmode = smpmode != 0; + + /* kvm_open the active kernel: its okay if this fails */ + /* Second arg must be /dev/null to allow normal users. */ + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL); + + /* get boot time */ + len = sizeof(boottime); + if (sysctlbyname("kern.boottime", &boottime, &len, NULL, 0) == -1) + { + /* we have no boottime to report */ + boottime.tv_sec = -1; + } + + pbase = NULL; + pref = NULL; + + /* get the page size with "getpagesize" and calculate pageshift from it */ + i = pagesize = getpagesize(); + pageshift = 0; + while (i > 1) + { + pageshift++; + i >>= 1; + } + + /* translate sysctl paths to mibs for faster access */ + get_sysctl_mibs(); + + /* initialize swap stuff */ + swap_init(); + + /* create the hash table that remembers proc data */ + procs = hash_create(2039); + + /* we only need the amount of log(2)1024 for our conversion */ + pageshift -= LOG1024; + + /* fill in the statics information */ + statics->procstate_names = procstatenames; + statics->cpustate_names = cpustatenames; + statics->memory_names = memorynames; + statics->kernel_names = kernelnames; + statics->boottime = boottime.tv_sec; + statics->swap_names = swapnames; + statics->order_names = ordernames; + statics->flags.warmup = 1; + statics->modemax = 2; +#ifdef HAS_SHOWTHREADS + statics->flags.threads = 1; +#endif + + /* Determine whether the system is running with ZFS. */ + uint64_t arc_size; + size_t size = sizeof(arc_size); + if (get_sysctl(ZFS_ARC_SIZE, &arc_size, sizeof(arc_size)) > 0 + && arc_size != 0) + { + statics->arc_names = arcnames; + arc_enabled = 1; + } + else + { + statics->arc_names = NULL; + arc_enabled = 0; + } + + /* we need kvm descriptor in order to show full commands */ + statics->flags.fullcmds = kd != NULL; + + calculate_header_columns(statics); + + /* all done! */ + return(0); +} + +/* Recalculate which columns are used in each mode. This was moved out of + * machine_init because some commands need to change which columns are + * displayed and I don't want to create three additional modeN_display vars. + */ +void +calculate_header_columns(struct statics *statics) +{ + int *ip; + + /* set up the display indices for mode0 */ + ip = mode0_display; + *ip++ = field_index("PID"); + if (statics->flags.jids) + *ip++ = field_index("JID"); + *ip++ = field_index("USERNAME"); +#ifdef HAS_THREADS + *ip++ = field_index("THR"); +#endif + *ip++ = field_index("PRI"); + *ip++ = field_index("NICE"); + *ip++ = field_index("SIZE"); + *ip++ = field_index("RES"); + *ip++ = field_index("STATE"); + *ip++ = field_index("FLG"); + if (smpmode) + *ip++ = field_index("C"); + *ip++ = field_index("TIME"); + *ip++ = field_index("CPU"); + *ip++ = field_index("COMMAND"); + *ip = -1; + +#ifdef HAS_SHOWTHREADS + /* set up the display indices for mode0 showing threads */ + ip = mode0thr_display; + *ip++ = field_index("PID"); + if (statics->flags.jids) + *ip++ = field_index("JID"); + *ip++ = field_index("USERNAME"); + *ip++ = field_index("PRI"); + *ip++ = field_index("NICE"); + *ip++ = field_index("SIZE"); + *ip++ = field_index("RES"); + *ip++ = field_index("STATE"); + *ip++ = field_index("FLG"); + if (smpmode) + *ip++ = field_index("C"); + *ip++ = field_index("TIME"); + *ip++ = field_index("CPU"); + *ip++ = field_index("COMMAND"); + *ip = -1; +#endif + + /* set up the display indices for mode1 */ + ip = mode1_display; + *ip++ = field_index("PID"); + if (statics->flags.jids) + *ip++ = field_index("JID"); + *ip++ = field_index("USERNAME"); + *ip++ = field_index("VCSW"); + *ip++ = field_index("IVCSW"); + *ip++ = field_index("READ"); + *ip++ = field_index("WRITE"); + *ip++ = field_index("FAULT"); + *ip++ = field_index("TOTAL"); + *ip++ = field_index("PERCENT"); + *ip++ = field_index("COMMAND"); + *ip = -1; +} + +char *format_header(char *uname_field) + +{ + return ""; +} + +void +get_vm_sum(struct vmmeter *sum) + +{ +#define GET_VM_STAT(v, s) (void)get_sysctl(v, &(sum->s), sizeof(sum->s)) + + GET_VM_STAT(V_SWTCH, v_swtch); + GET_VM_STAT(V_TRAP, v_trap); + GET_VM_STAT(V_INTR, v_intr); + GET_VM_STAT(V_SOFT, v_soft); + GET_VM_STAT(V_VFORKS, v_vforks); + GET_VM_STAT(V_FORKS, v_forks); + GET_VM_STAT(V_RFORKS, v_rforks); + GET_VM_STAT(V_VM_FAULTS, v_vm_faults); + GET_VM_STAT(V_SWAPIN, v_swapin); + GET_VM_STAT(V_SWAPOUT, v_swapout); + GET_VM_STAT(V_TFREE, v_tfree); + GET_VM_STAT(V_VNODEIN, v_vnodein); + GET_VM_STAT(V_VNODEOUT, v_vnodeout); + GET_VM_STAT(V_ACTIVE_COUNT, v_active_count); + GET_VM_STAT(V_INACTIVE_COUNT, v_inactive_count); + GET_VM_STAT(V_WIRE_COUNT, v_wire_count); + GET_VM_STAT(V_CACHE_COUNT, v_cache_count); + GET_VM_STAT(V_FREE_COUNT, v_free_count); + GET_VM_STAT(V_SWAPPGSIN, v_swappgsin); + GET_VM_STAT(V_SWAPPGSOUT, v_swappgsout); +} + +void +get_system_info(struct system_info *si) + +{ + long total; + struct timeval thistime; + struct timeval timediff; + + /* timestamp and time difference */ + gettimeofday(&thistime, 0); + timersub(&thistime, &lasttime, &timediff); + elapsed_time = timediff.tv_sec * 1000000 + timediff.tv_usec; + elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000; + + /* get the load averages */ + if (getloadavg(si->load_avg, NUM_AVERAGES) == -1) + { + /* failed: fill in with zeroes */ + (void) memset(si->load_avg, 0, sizeof(si->load_avg)); + } + + /* get the cp_time array */ + (void)get_sysctl(K_CP_TIME, &cp_time, sizeof(cp_time)); + + /* convert cp_time counts to percentages */ + total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); + + /* sum memory & swap statistics */ + { + struct vmmeter sum; + static unsigned int swap_delay = 0; + static long long swapavail = 0; + static long long swapfree = 0; + static int bufspace = 0; + + get_vm_sum(&sum); + + /* get bufspace */ + bufspace = 0; + (void) get_sysctl(VFS_BUFSPACE, &bufspace, sizeof(bufspace)); + + /* kernel stats */ + dprintf("kernel: swtch %d, trap %d, intr %d, soft %d, vforks %d\n", + sum.v_swtch, sum.v_trap, sum.v_intr, sum.v_soft, sum.v_vforks); + kernel_stats[0] = per_second(sum.v_swtch - ctxsws, elapsed_msecs); + kernel_stats[1] = per_second(sum.v_trap - traps, elapsed_msecs); + kernel_stats[2] = per_second(sum.v_intr - intrs, elapsed_msecs); + kernel_stats[3] = per_second(sum.v_soft - softs, elapsed_msecs); + kernel_stats[4] = per_second(sum.v_vforks + sum.v_forks + + sum.v_rforks - forks, elapsed_msecs); + kernel_stats[5] = per_second(sum.v_vm_faults - pfaults, elapsed_msecs); + kernel_stats[6] = per_second(sum.v_swapin + sum.v_vnodein - pagein, elapsed_msecs); + kernel_stats[7] = per_second(sum.v_swapout + sum.v_vnodeout - pageout, elapsed_msecs); + kernel_stats[8] = per_second(sum.v_tfree - tfreed, elapsed_msecs); + ctxsws = sum.v_swtch; + traps = sum.v_trap; + intrs = sum.v_intr; + softs = sum.v_soft; + forks = (u_int64_t)sum.v_vforks + sum.v_forks + sum.v_rforks; + pfaults = sum.v_vm_faults; + pagein = sum.v_swapin + sum.v_vnodein; + pageout = sum.v_swapout + sum.v_vnodeout; + tfreed = sum.v_tfree; + + /* convert memory stats to Kbytes */ + memory_stats[0] = pagetok(sum.v_active_count); + memory_stats[1] = pagetok(sum.v_inactive_count); + memory_stats[2] = pagetok(sum.v_wire_count); + memory_stats[3] = pagetok(sum.v_cache_count); + memory_stats[4] = bufspace / 1024; + memory_stats[5] = pagetok(sum.v_free_count); + memory_stats[6] = -1; + + /* first interval */ + if (swappgsin < 0) + { + swap_stats[4] = 0; + swap_stats[5] = 0; + } + + /* compute differences between old and new swap statistic */ + else + { + swap_stats[4] = pagetok(sum.v_swappgsin - swappgsin); + swap_stats[5] = pagetok(sum.v_swappgsout - swappgsout); + } + + swappgsin = sum.v_swappgsin; + swappgsout = sum.v_swappgsout; + + /* call CPU heavy swap_getdata() only for changes */ + if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) + { + swap_stats[3] = swap_getdata(&swapavail, &swapfree); + swap_stats[0] = swapavail; + swap_stats[1] = swapavail - swapfree; + swap_stats[2] = swapfree; + } + swap_delay = 1; + swap_stats[6] = -1; + } + + /* If we're running with ZFS, get ARC stats. */ + if (arc_enabled) + { + uint64_t arc_stat, arc_stat2; + get_sysctl(ZFS_ARC_SIZE, &arc_stat, sizeof(arc_stat)); + arc_stats[0] = arc_stat >> 10; + get_sysctl(ZFS_MFU_SIZE, &arc_stat, sizeof(arc_stat)); + arc_stats[1] = arc_stat >> 10; + get_sysctl(ZFS_MRU_SIZE, &arc_stat, sizeof(arc_stat)); + arc_stats[2] = arc_stat >> 10; + get_sysctl(ZFS_ANON_SIZE, &arc_stat, sizeof(arc_stat)); + arc_stats[3] = arc_stat >> 10; + get_sysctl(ZFS_HDR_SIZE, &arc_stat, sizeof(arc_stat)); + get_sysctl(ZFS_L2_HDR_SIZE, &arc_stat2, sizeof(arc_stat2)); + arc_stats[4] = arc_stat + arc_stat2 >> 10; + get_sysctl(ZFS_OTHER_SIZE, &arc_stat, sizeof(arc_stat)); + arc_stats[5] = arc_stat >> 10; + si->arc = arc_stats; + } + + /* set arrays and strings */ + si->cpustates = cpu_states; + si->kernel = kernel_stats; + si->memory = memory_stats; + si->swap = swap_stats; + + + /* Get the kern.lastpid sysctl. */ + if (get_sysctl(K_LASTPID, &lastpid, sizeof(lastpid)) != -1) + si->last_pid = lastpid; + else + si->last_pid = -1; + + lasttime = thistime; +} + +caddr_t +get_process_info(struct system_info *si, + struct process_select *sel, + int compare_index) + +{ + int i; + int total_procs; + int active_procs; + struct kinfo_proc **prefp; + struct kinfo_proc *pp; + struct kinfo_proc *prev_pp = NULL; + struct save_proc *savep; + long proc_io; + pid_t pid; + size_t size; + int nproc; + + /* these are copied out of sel for speed */ + int show_idle; + int show_self; + int show_system; + int show_uid; + char *show_command; + + /* get proc table size and give it a boost */ + nproc = (int)get_sysctlsize(K_PROC) / sizeof(struct kinfo_proc); + nproc += nproc >> 4; + size = nproc * sizeof(struct kinfo_proc); + dprintf("get_process_info: nproc %d, psize %d, size %d\n", nproc, psize, size); + + /* make sure we have enough space allocated */ + if (nproc > psize) + { + /* reallocate both pbase and pref */ + pbase = (struct kinfo_proc *)realloc(pbase, size); + pref = (struct kinfo_proc **)realloc(pref, + sizeof(struct kinfo_proc *) * nproc); + psize = nproc; + } + + /* make sure we got the space we asked for */ + if (pref == NULL || pbase == NULL) + { + /* abandon all hope */ + message_error(" Out of memory!"); + nproc = psize = 0; + si->p_total = 0; + si->p_active = 0; + return NULL; + } + + /* get all process information (threads, too) */ + if (size > 0) + { + nproc = get_sysctl(K_PROC, pbase, size); + if (nproc == -1) + { + nproc = 0; + } + else + { + nproc /= sizeof(struct kinfo_proc); + } + } + + /* get a pointer to the states summary array */ + si->procstates = process_states; + + /* set up flags which define what we are going to select */ + show_idle = sel->idle; + show_self = 0; + show_system = sel->system; + show_uid = sel->uid != -1; + show_fullcmd = sel->fullcmd; + show_command = sel->command; + show_usernames = sel->usernames; + display_mode = sel->mode; +#ifdef HAS_SHOWTHREADS + show_threads = sel->threads; +#endif + + /* count up process states and get pointers to interesting procs */ + total_procs = 0; + active_procs = 0; + total_io = 0; + memset((char *)process_states, 0, sizeof(process_states)); + prefp = pref; + for (pp = pbase, i = 0; i < nproc; pp++, i++) + { + /* + * Place pointers to each valid proc structure in pref[]. + * Process slots that are actually in use have a non-zero + * status field. Processes with P_SYSTEM set are system + * processes---these get ignored unless show_sysprocs is set. + */ + pid = PP(pp, pid); + if (PP(pp, stat) != 0) + { +#ifdef HAS_SHOWTHREADS + int is_thread; + lwpid_t tid; + + /* get thread id */ + tid = PP(pp, tid); + + /* is this just a thread? */ + is_thread = (prev_pp != NULL && PP(prev_pp, pid) == pid); + + /* count this process and its state */ + /* only count threads if we are showing them */ + if (show_threads || !is_thread) + { + total_procs++; + process_states[(unsigned char) PP(pp, stat)]++; + } + + /* grab old data from hash */ + if ((savep = hash_lookup_lwpid(procs, tid)) != NULL) + { + /* verify that this is not a new or different thread */ + /* (freebsd reuses thread ids fairly quickly) */ + /* pids must match and time can't have gone backwards */ + if (pid != savep->sp_pid || PP(pp, runtime) < savep->sp_runtime) + { + /* not the same thread -- reuse the save_proc structure */ + memset(savep, 0, sizeof(struct save_proc)); + savep->sp_pid = pid; + } + } + else + { + /* havent seen this one before */ + savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); + savep->sp_pid = pid; + hash_add_lwpid(procs, tid, savep); + } + +#else /* !HAS_SHOWTHREADS */ + total_procs++; + process_states[(unsigned char) PP(pp, stat)]++; + + /* grab old data from hash */ + if ((savep = hash_lookup_pid(procs, pid)) == NULL) + { + /* havent seen this one before */ + savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); + savep->sp_pid = pid; + hash_add_pid(procs, pid, savep); + } +#endif + + /* save the pointer to the sp struct */ + SPPTR(pp) = (void *)savep; + + /* calculate %cpu */ + PPCPU(pp) = ((PP(pp, runtime) - savep->sp_runtime) * 10000) / + elapsed_time; + dprintf("%d (%d): runtime %lld, saved_pid %d, saved_runtime %lld, elapsed_time %d, ppcpu %d\n", + pid, PP(pp, tid), PP(pp, runtime), savep->sp_pid, savep->sp_runtime, + elapsed_time, PPCPU(pp)); + + /* calculate io differences */ + proc_io = 0; + savep->sp_vcsw = (RP(pp, nvcsw) - savep->sp_old_nvcsw); + savep->sp_ivcsw = (RP(pp, nivcsw) - savep->sp_old_nivcsw); + proc_io += (savep->sp_inblock = (RP(pp, inblock) - savep->sp_old_inblock)); + proc_io += (savep->sp_oublock = (RP(pp, oublock) - savep->sp_old_oublock)); + proc_io += (savep->sp_majflt = (RP(pp, majflt) - savep->sp_old_majflt)); + total_io += proc_io; + savep->sp_totalio = proc_io; + + /* save data for next time */ + savep->sp_runtime = PP(pp, runtime); + savep->sp_old_nvcsw = RP(pp, nvcsw); + savep->sp_old_nivcsw = RP(pp, nivcsw); + savep->sp_old_inblock = RP(pp, inblock); + savep->sp_old_oublock = RP(pp, oublock); + savep->sp_old_majflt = RP(pp, majflt); + + /* is this one selected for viewing? */ + if ((PP(pp, stat) != SZOMB) && + (show_system || ((PP(pp, flag) & P_SYSTEM) == 0)) && + (show_idle || (PP(pp, pctcpu) != 0) || + (PP(pp, stat) == SRUN)) && + (!show_uid || PRUID(pp) == (uid_t)sel->uid) && + (show_command == NULL || + strcasestr(PP(pp, comm), show_command) != NULL) && + (sel->filter_jail == 0 || sel->jail == PP(pp, jid))) + { +#ifdef HAS_SHOWTHREADS + /* yes, but make sure it isn't just a thread */ + if (show_threads || !is_thread) + { + /* we will be showing this thread */ + *prefp++ = pp; + active_procs++; + } + else + { + /* we will not be showing this thread, but we need to roll + up its cpu usage in to its process */ + PP(prev_pp, pctcpu) += PP(pp, pctcpu); + } +#else /* !HAS_SHOWTHREADS */ + /* we will be showing this process */ + *prefp++ = pp; + active_procs++; +#endif + } + prev_pp = pp; + } + } + + dprintf("total_io: %d\n", total_io); + if (total_io == 0) total_io = 1; + + /* if requested, sort the "interesting" processes */ + if (active_procs > 1) + { + qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), + proc_compares[compare_index]); + } + + /* remember active and total counts */ + si->p_total = total_procs; + si->p_active = pref_len = active_procs; + + /* pass back a handle */ + handle.next_proc = pref; + handle.remaining = active_procs; + return((caddr_t)&handle); +} + +static char p_header[MAX_COLS]; + +char * +format_process_header(struct process_select *sel, caddr_t handle, int count) + +{ + int cols; + int n; + int w; + char *p; + int *fi; + struct kinfo_proc **kip; + struct proc_field *fp; + + /* check for null handle */ + if (handle == NULL) + { + return(""); + } + + /* remember how many columns there are on the display */ + cols = display_columns(); + + /* mode & threads dictate format */ + fi = display_fields = + sel->mode == 0 ? + (sel->threads == 0 ? mode0_display : mode0thr_display) : + mode1_display; + + /* set username field correctly */ + if (!sel->usernames) + { + /* display uids */ + field_subst(fi, FIELD_USERNAME, FIELD_UID); + } + else + { + /* display usernames */ + field_subst(fi, FIELD_UID, FIELD_USERNAME); + + /* we also need to determine the longest username for column width */ + /* calculate namelength from first "count" processes */ + kip = ((struct handle *)handle)->next_proc; + n = ((struct handle *)handle)->remaining; + if (n > count) + n = count; + namelength = 0; + while (n-- > 0) + { + w = strlen(username(PRUID(*kip))); + if (w > namelength) namelength = w; + kip++; + } + dprintf("format_process_header: namelength %d\n", namelength); + + /* place it in bounds */ + if (namelength < 8) + { + namelength = 8; + } + + /* set the column width */ + proc_field[FIELD_USERNAME].width = username_length = namelength; + } + + /* walk thru fields and construct header */ + /* are we worried about overflow??? */ + p = p_header; + while (*fi != -1) + { + fp = &(proc_field[*fi++]); + if (fp->min_screenwidth <= cols) + { + p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name); + *p++ = ' '; + } + } + *--p = '\0'; + + return p_header; +} + +static char fmt[MAX_COLS]; /* static area where result is built */ + +char * +format_next_process(caddr_t handle, char *(*get_userid)(int)) + +{ + struct kinfo_proc *pp; + struct handle *hp; + struct proc_field *fp; + int *fi; + int i; + int cols; + char *p; + int len; + int x; + + /* find and remember the next proc structure */ + hp = (struct handle *)handle; + pp = *(hp->next_proc++); + hp->remaining--; + + /* mode & threads dictate format */ + fi = display_fields; + + /* screen width is a consideration, too */ + cols = display_columns(); + + /* build output by field */ + p = fmt; + len = MAX_COLS; + while ((i = *fi++) != -1) + { + fp = &(proc_field[i]); + if (len > 0 && fp->min_screenwidth <= cols) + { + x = (*(fp->format))(p, len, pp); + if (x >= len) + { + dprintf("format_next_process: formatter overflow: x %d, len %d, p %08x => %08x, fmt %08x - %08x\n", + x, len, p, p + len, fmt, fmt + sizeof(fmt)); + p += len; + len = 0; + } + else + { + p += x; + *p++ = ' '; + len -= x + 1; + } + } + } + *--p = '\0'; + + /* return the result */ + return(fmt); +} + +/* comparison routines for qsort */ + +/* + * proc_compare - comparison function for "qsort" + * Compares the resource consumption of two processes using five + * distinct keys. The keys (in descending order of importance) are: + * percent cpu, cpu ticks, state, resident set size, total virtual + * memory usage. The process states are ordered as follows (from least + * to most important): WAIT, zombie, sleep, stop, start, run. The + * array declaration below maps a process state index into a number + * that reflects this ordering. + */ + +static unsigned char sorted_state[] = +{ + 0, /* not used */ + 3, /* sleep */ + 1, /* ABANDONED (WAIT) */ + 6, /* run */ + 5, /* start */ + 2, /* zombie */ + 4 /* stop */ +}; + + +#define ORDERKEY_PCTCPU \ + if (lresult = (long) PPCPU(p2) - (long) PPCPU(p1), \ + (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) + +#define ORDERKEY_CPTICKS \ + if ((result = PP(p2, runtime) > PP(p1, runtime) ? 1 : \ + PP(p2, runtime) < PP(p1, runtime) ? -1 : 0) == 0) + +#define ORDERKEY_STATE \ + if ((result = sorted_state[(unsigned char) PP(p2, stat)] - \ + sorted_state[(unsigned char) PP(p1, stat)]) == 0) + +#if OSMAJOR <= 4 +#define ORDERKEY_PRIO \ + if ((result = PP(p2, priority) - PP(p1, priority)) == 0) +#else +#define ORDERKEY_PRIO \ + if ((result = PP(p2, pri.pri_level) - PP(p1, pri.pri_level)) == 0) +#endif + +#define ORDERKEY_RSSIZE \ + if ((result = VP(p2, rssize) - VP(p1, rssize)) == 0) + +#define ORDERKEY_MEM \ + if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 ) + +#define ORDERKEY_IO \ + if ( (result = SP(p2, totalio) - SP(p1, totalio)) == 0) + +#define ORDERKEY_PID \ + if ( (result = PP(p1, pid) - PP(p2, pid)) == 0) + +/* compare_cpu - the comparison function for sorting by cpu percentage */ + +int +proc_compare(struct proc **pp1, struct proc **pp2) + +{ + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; + + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; + + ORDERKEY_PCTCPU + ORDERKEY_CPTICKS + ORDERKEY_STATE + ORDERKEY_PRIO + ORDERKEY_RSSIZE + ORDERKEY_MEM + ; + + return(result); +} + +/* compare_size - the comparison function for sorting by total memory usage */ + +int +compare_size(struct proc **pp1, struct proc **pp2) + +{ + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; + + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; + + ORDERKEY_MEM + ORDERKEY_RSSIZE + ORDERKEY_PCTCPU + ORDERKEY_CPTICKS + ORDERKEY_STATE + ORDERKEY_PRIO + ; + + return(result); +} + +/* compare_res - the comparison function for sorting by resident set size */ + +int +compare_res(struct proc **pp1, struct proc **pp2) + +{ + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; + + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; + + ORDERKEY_RSSIZE + ORDERKEY_MEM + ORDERKEY_PCTCPU + ORDERKEY_CPTICKS + ORDERKEY_STATE + ORDERKEY_PRIO + ; + + return(result); +} + +/* compare_time - the comparison function for sorting by total cpu time */ + +int +compare_time(struct proc **pp1, struct proc **pp2) + +{ + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; + + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; + + ORDERKEY_CPTICKS + ORDERKEY_PCTCPU + ORDERKEY_STATE + ORDERKEY_PRIO + ORDERKEY_RSSIZE + ORDERKEY_MEM + ; + + return(result); + } + +/* compare_prio - the comparison function for sorting by priority */ + +int +compare_prio(struct proc **pp1, struct proc **pp2) + +{ + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; + + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; + + ORDERKEY_PRIO + ORDERKEY_CPTICKS + ORDERKEY_PCTCPU + ORDERKEY_STATE + ORDERKEY_RSSIZE + ORDERKEY_MEM + ; + + return(result); +} + +/* compare_io - the comparison function for sorting by io count */ + +int +compare_io(struct proc **pp1, struct proc **pp2) + +{ + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; + + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; + + ORDERKEY_IO + ORDERKEY_PCTCPU + ORDERKEY_CPTICKS + ORDERKEY_STATE + ORDERKEY_PRIO + ORDERKEY_RSSIZE + ORDERKEY_MEM + ; + + return(result); +} + +/* compare_pid - the comparison function for sorting by process id */ + +int +compare_pid(struct proc **pp1, struct proc **pp2) + +{ + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; + + ORDERKEY_PID + ; + + return(result); +} + +/* + * proc_owner(pid) - returns the uid that owns process "pid", or -1 if + * the process does not exist. + * It is EXTREMLY IMPORTANT that this function work correctly. + * If top runs setuid root (as in SVR4), then this function + * is the only thing that stands in the way of a serious + * security problem. It validates requests for the "kill" + * and "renice" commands. + */ + +int +proc_owner(int pid) + +{ + int cnt; + struct kinfo_proc **prefp; + struct kinfo_proc *pp; + + prefp = pref; + cnt = pref_len; + while (--cnt >= 0) + { + pp = *prefp++; + if (PP(pp, pid) == (pid_t)pid) + { + return((int)PRUID(pp)); + } + } + return(-1); +} + Index: contrib/top/machine.h =================================================================== --- contrib/top/machine.h +++ contrib/top/machine.h @@ -1,4 +1,34 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * * $FreeBSD$ */ @@ -7,13 +37,15 @@ * module. It is NOT machine dependent and should not need to be changed * for any specific machine. */ -#ifndef MACHINE_H -#define MACHINE_H + +#ifndef _MACHINE_H_ +#define _MACHINE_H_ #include "top.h" /* - * the statics struct is filled in by machine_init + * The statics struct is filled in by machine_init. Fields marked as + * "optional" are not filled in by every module. */ struct statics { @@ -21,11 +53,19 @@ char **cpustate_names; char **memory_names; char **arc_names; - char **swap_names; -#ifdef ORDER - char **order_names; -#endif - int ncpus; + char **swap_names; /* optional */ + char **order_names; /* optional */ + char **top_color_names; /* optional */ + char **kernel_names; /* optional */ + time_t boottime; /* optional */ + int modemax; /* optional */ + struct { + unsigned int fullcmds : 1; + unsigned int idle : 1; + unsigned int warmup : 1; + unsigned int threads : 1; + unsigned int jids: 1; + } flags; }; /* @@ -46,11 +86,10 @@ int P_ACTIVE; /* number of procs considered "active" */ int *procstates; int *cpustates; - int *memory; - int *arc; - int *swap; - struct timeval boottime; - int ncpus; + int *kernel; + long *memory; + long *swap; + long *arc; }; /* cpu_states is an array of percentages * 10. For example, @@ -65,27 +104,27 @@ struct process_select { int idle; /* show idle processes */ - int self; /* show self */ int system; /* show system processes */ - int thread; /* show threads */ + int fullcmd; /* show full command */ + int usernames; /* show usernames */ int uid; /* only this uid (unless uid == -1) */ - int wcpu; /* show weighted cpu */ - int jid; /* only this jid (unless jid == -1) */ - int jail; /* show jail ID */ - int kidle; /* show per-CPU idle threads */ char *command; /* only this command (unless == NULL) */ + int mode; /* select display mode (0 is default) */ + int threads; /* show threads separately */ + int filter_jail;/* Whether to filter by jail or ignore .jail. */ + int jail; /* Only from this jail. */ }; /* routines defined by the machine dependent module */ +int machine_init(struct statics *); +void get_system_info(struct system_info *); +caddr_t get_process_info(struct system_info *, struct process_select *, int); +void calculate_header_columns(struct statics *statics); +char *format_header(char *); +char *format_next_process(caddr_t, char *(*)(int)); +int proc_owner(int); +#ifdef HAVE_FORMAT_PROCESS_HEADER -char *format_header(); -char *format_next_process(); -void toggle_pcpustats(void); -void get_system_info(struct system_info *si); -int machine_init(struct statics *statics, char do_unames); -int proc_owner(int pid); - -/* non-int routines typically used by the machine dependent module */ -char *printable(); - -#endif /* MACHINE_H */ +#endif /* _MACHINE_H_ */ +char *format_process_header(struct process_select *sel, caddr_t handle, int count); +#endif Index: contrib/top/message.h =================================================================== --- contrib/top/message.h +++ contrib/top/message.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* interface declaration for display messages */ +/* This is a small subset of the interface from display.c that + just contains the calls for displaying messages. Do not include + this and display.h at the same time. */ + +#ifndef _MESSAGE_H +#define _MESSAGE_H + +void error_message(char *msgfmt, ...); +void clear_message(); + +#endif /* _MESSAGE_H_ */ Index: contrib/top/metatop =================================================================== --- contrib/top/metatop +++ contrib/top/metatop @@ -1,25 +0,0 @@ -#! /bin/sh -# -# Top is very sensitive to differences in the kernel, so much so that an -# executable created on one sub-architecture may not work on others. It -# is also quite common for a minor OS revision to require recompilation of -# top. Both of these problems are especially prevalent on Suns. For -# example, a top executable made under SunOS 4.1.1 will not run correctly -# under SunOS 4.1.2, and vice versa. "metatop" attempts to solve this -# problem by choosing one of several possible top executables to run then -# executing it. -# -# To use metatop your operating system needs to have the command "uname" -# as part of the standard OS release. MAKE SURE IT DOES before proceeding. -# It will try to execute the command "top-`uname -m`-`uname -r`" For -# example, on a sparcstation 1 running SunOS 4.1.1, it will try to run -# "top-sun4c-4.1.1". -# -# INSTALLATION is easy. Just compile top as normal. Then use the command -# "make metainstall" (on the same machine!) instead of the usual. "make" -# will insure that this shell script is installed correctly then will install -# the most recently made top executable with the correct name. Remember: -# you will need to "make clean" and "make metainstall" on every different -# combination of sub-architecture and OS version that you have. -# -exec $0-`uname -m`-`uname -r` "$@" Index: contrib/top/os.h =================================================================== --- contrib/top/os.h +++ contrib/top/os.h @@ -1,38 +1,143 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + #include -#include /* This defines BSD */ -#if defined(BSD) && !defined(BSD4_4) && !defined(__osf__) -# include -# include -# define strchr(a, b) index((a), (b)) -# define strrchr(a, b) rindex((a), (b)) -# define memcpy(a, b, c) bcopy((b), (a), (c)) -# define memzero(a, b) bzero((a), (b)) -# define memcmp(a, b, c) bcmp((a), (b), (c)) -#if defined(NeXT) - typedef void sigret_t; +#include +#include + +#ifdef HAVE_LIMITS_H +#include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include #else - typedef int sigret_t; +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif #endif -/* system routines that don't return int */ +#if STDC_HEADERS +#include +#include +#define setbuffer(f, b, s) setvbuf((f), (b), (b) ? _IOFBF : _IONBF, (s)) +#define memzero(a, b) memset((a), 0, (b)) +#else /* !STDC_HEADERS */ +#ifndef HAVE_STRCHR +#define strchr(a, b) index((a), (b)) +#define strrchr(a, b) rindex((a), (b)) +#endif /* HAVE_STRCHR */ +#ifdef HAVE_MEMCPY +#define memzero(a, b) memset((a), 0, (b)) +#else +#define memcpy(a, b, c) bcopy((b), (a), (c)) +#define memzero(a, b) bzero((a), (b)) +#define memcmp(a, b, c) bcmp((a), (b), (c)) +#endif /* HAVE_MEMCPY */ +#ifdef HAVE_STRINGS_H +#include +#else +#ifdef HAVE_STRING_H +#include +#endif +#endif char *getenv(); caddr_t malloc(); +#endif /* STDC_HEADERS */ + +/* If snprintf or vsnprintf aren't available, we substitute our own. + But we have to include stdarg in order to be able to define them. +*/ +#ifdef HAVE_STDARG_H +#include +#ifndef HAVE_SNPRINTF +int ap_snprintf(char *buf, size_t len, const char *format,...); +#define snprintf ap_snprintf +#endif +#ifndef HAVE_VSNPRINTF +int ap_vsnprintf(char *buf, size_t len, const char *format,va_list ap); +#define vsnprintf ap_vsnprintf +#endif +#endif -#else -# include -# define setbuffer(f, b, s) setvbuf((f), (b), (b) ? _IOFBF : _IONBF, (s)) -# include -# include -# include -# define memzero(a, b) memset((a), 0, (b)) - typedef void sigret_t; -#endif - -/* some systems declare sys_errlist in stdio.h! */ -#if defined(__NetBSD__) || defined(__FreeBSD__) -#if !defined(__m68k__) -# if !defined(__NetBSD132__) -#define SYS_ERRLIST_DECLARED -# endif /* __NetBSD132__ */ +#if !HAVE_PID_T +typedef long pid_t; #endif +#if !HAVE_TIME_T +typedef long time_t; +#endif +#if !HAVE_UID_T +typedef long uid_t; +#endif + +#ifndef INT_MAX +#define INT_MAX (0x7fffffff) +#endif + +#ifndef UINT_MAX +#define UINT_MAX (0xffffffffU) +#endif + +/* we must have both sighold and sigrelse to use them */ +#if defined(HAVE_SIGHOLD) && !defined(HAVE_SIGRELSE) +#undef HAVE_SIGHOLD +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_SYSEXITS_H +#include +#else +#define EX_OK 0 /* successful termination */ +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ #endif Index: contrib/top/patchlevel.h =================================================================== --- contrib/top/patchlevel.h +++ contrib/top/patchlevel.h @@ -1,2 +0,0 @@ -#define PATCHLEVEL 5 -#define BETA "beta12" Index: contrib/top/prime.c =================================================================== --- contrib/top/prime.c +++ contrib/top/prime.c @@ -1,41 +0,0 @@ -/* - * Prime number generator. It prints on stdout the next prime number - * higher than the number specified as argv[1]. - */ - -#include -#include - -main(argc, argv) - -int argc; -char *argv[]; - -{ - double i, j; - int f; - - if (argc < 2) - { - exit(1); - } - - i = atoi(argv[1]); - while (i++) - { - f=1; - for (j=2; j +#include +#else +#if HAVE_TERMCAP_H +#include +#else +#if HAVE_CURSES_H +#include +#endif +#endif +#endif + +#if !HAVE_DECL_TPUTS +int tputs(const char *, int, int (*)(int)); +#endif +#if !HAVE_DECL_TGOTO +char *tgoto(const char *, int, int); +#endif +#if !HAVE_DECL_TGETENT +int tgetent(const char *, char *); +#endif +#if !HAVE_DECL_TGETFLAG +int tgetflag(const char *); +#endif +#if !HAVE_DECL_TGETNUM +int tgetnum(const char *); +#endif +#if !HAVE_DECL_TGETSTR +char *tgetstr(const char *, char **); +#endif + #include #ifdef CBREAK # include -# define SGTTY +# define USE_SGTTY #else # ifdef TCGETA -# define TERMIO +# define USE_TERMIO # include # else -# define TERMIOS +# define USE_TERMIOS # include # endif #endif -#if defined(TERMIO) || defined(TERMIOS) +#if defined(USE_TERMIO) || defined(USE_TERMIOS) # ifndef TAB3 # ifdef OXTABS # define TAB3 OXTABS @@ -45,43 +103,46 @@ # endif # endif #endif + #include "screen.h" #include "boolean.h" -extern char *myname; +#define putcap(str) (void)((str) != NULL ? tputs(str, 1, putstdout) : 0) +extern char *myname; -int overstrike; -int screen_length; -int screen_width; char ch_erase; char ch_kill; +char ch_werase; char smart_terminal; +int screen_length; +int screen_width; + char PC; -char *tgetstr(); -char *tgoto(); -char termcap_buf[1024]; -char string_buffer[1024]; -char home[15]; -char lower_left[15]; -char *clear_line; -char *clear_screen; -char *clear_to_end; -char *cursor_motion; -char *start_standout; -char *end_standout; -char *terminal_init; -char *terminal_end; -#ifdef SGTTY +static int tc_overstrike; +static char termcap_buf[1024]; +static char string_buffer[1024]; +static char home[15]; +static char lower_left[15]; +static char *tc_clear_line; +static char *tc_clear_screen; +static char *tc_clear_to_end; +static char *tc_cursor_motion; +static char *tc_start_standout; +static char *tc_end_standout; +static char *terminal_init; +static char *terminal_end; + +#ifdef USE_SGTTY static struct sgttyb old_settings; static struct sgttyb new_settings; #endif -#ifdef TERMIO +#ifdef USE_TERMIO static struct termio old_settings; static struct termio new_settings; #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS static struct termios old_settings; static struct termios new_settings; #endif @@ -95,10 +156,62 @@ #define STDOUT 1 #define STDERR 2 +/* This has to be defined as a subroutine for tputs (instead of a macro) */ + +static int +putstdout(TPUTS_PUTC_ARGTYPE ch) + +{ + return putchar((int)ch); +} + void -init_termcap(interactive) +screen_getsize() + +{ + +#ifdef TIOCGWINSZ + + struct winsize ws; + + if (ioctl (1, TIOCGWINSZ, &ws) != -1) + { + if (ws.ws_row != 0) + { + screen_length = ws.ws_row; + } + if (ws.ws_col != 0) + { + screen_width = ws.ws_col - 1; + } + } + +#else +#ifdef TIOCGSIZE + + struct ttysize ts; + + if (ioctl (1, TIOCGSIZE, &ts) != -1) + { + if (ts.ts_lines != 0) + { + screen_length = ts.ts_lines; + } + if (ts.ts_cols != 0) + { + screen_width = ts.ts_cols - 1; + } + } -int interactive; +#endif /* TIOCGSIZE */ +#endif /* TIOCGWINSZ */ + + (void) strlcpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1), + sizeof(lower_left)); +} + +int +screen_readtermcap(int interactive) { char *bufptr; @@ -111,11 +224,11 @@ screen_width = MAX_COLS; screen_length = 0; - if (!interactive) + if (interactive == No) { /* pretend we have a dumb terminal */ smart_terminal = No; - return; + return No; } /* assume we have a smart terminal until proven otherwise */ @@ -129,7 +242,7 @@ if (term_name == NULL) { smart_terminal = No; - return; + return No; } /* now get the termcap entry */ @@ -147,21 +260,21 @@ /* pretend it's dumb and proceed */ smart_terminal = No; - return; + return No; } /* "hardcopy" immediately indicates a very stupid terminal */ if (tgetflag("hc")) { smart_terminal = No; - return; + return No; } /* set up common terminal capabilities */ if ((screen_length = tgetnum("li")) <= 0) { screen_length = smart_terminal = 0; - return; + return No; } /* screen_width is a little different */ @@ -175,72 +288,73 @@ } /* terminals that overstrike need special attention */ - overstrike = tgetflag("os"); + tc_overstrike = tgetflag("os"); /* initialize the pointer into the termcap string buffer */ bufptr = string_buffer; /* get "ce", clear to end */ - if (!overstrike) + if (!tc_overstrike) { - clear_line = tgetstr("ce", &bufptr); + tc_clear_line = tgetstr("ce", &bufptr); } /* get necessary capabilities */ - if ((clear_screen = tgetstr("cl", &bufptr)) == NULL || - (cursor_motion = tgetstr("cm", &bufptr)) == NULL) + if ((tc_clear_screen = tgetstr("cl", &bufptr)) == NULL || + (tc_cursor_motion = tgetstr("cm", &bufptr)) == NULL) { smart_terminal = No; - return; + return No; } /* get some more sophisticated stuff -- these are optional */ - clear_to_end = tgetstr("cd", &bufptr); + tc_clear_to_end = tgetstr("cd", &bufptr); terminal_init = tgetstr("ti", &bufptr); terminal_end = tgetstr("te", &bufptr); - start_standout = tgetstr("so", &bufptr); - end_standout = tgetstr("se", &bufptr); + tc_start_standout = tgetstr("so", &bufptr); + tc_end_standout = tgetstr("se", &bufptr); /* pad character */ PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0; /* set convenience strings */ - (void) strncpy(home, tgoto(cursor_motion, 0, 0), sizeof(home) - 1); - home[sizeof(home) - 1] = '\0'; - /* (lower_left is set in get_screensize) */ + (void) strlcpy(home, tgoto(tc_cursor_motion, 0, 0), sizeof(home)); + /* (lower_left is set in screen_getsize) */ /* get the actual screen size with an ioctl, if needed */ /* This may change screen_width and screen_length, and it always sets lower_left. */ - get_screensize(); + screen_getsize(); /* if stdout is not a terminal, pretend we are a dumb terminal */ -#ifdef SGTTY +#ifdef USE_SGTTY if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1) { smart_terminal = No; } #endif -#ifdef TERMIO +#ifdef USE_TERMIO if (ioctl(STDOUT, TCGETA, &old_settings) == -1) { smart_terminal = No; } #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS if (tcgetattr(STDOUT, &old_settings) == -1) { smart_terminal = No; } #endif + + return smart_terminal; } void -init_screen() +screen_init() { /* get the old settings for safe keeping */ -#ifdef SGTTY +#ifdef USE_SGTTY if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1) { /* copy the settings so we can modify them */ @@ -254,6 +368,7 @@ /* remember the erase and kill characters */ ch_erase = old_settings.sg_erase; ch_kill = old_settings.sg_kill; + ch_werase = old_settings.sg_werase; #ifdef TOStop /* get the local mode word */ @@ -270,7 +385,7 @@ putcap(terminal_init); } #endif -#ifdef TERMIO +#ifdef USE_TERMIO if (ioctl(STDOUT, TCGETA, &old_settings) != -1) { /* copy the settings so we can modify them */ @@ -284,8 +399,9 @@ (void) ioctl(STDOUT, TCSETA, &new_settings); /* remember the erase and kill characters */ - ch_erase = old_settings.c_cc[VERASE]; - ch_kill = old_settings.c_cc[VKILL]; + ch_erase = old_settings.c_cc[VERASE]; + ch_kill = old_settings.c_cc[VKILL]; + ch_werase = old_settings.c_cc[VWERASE]; /* remember that it really is a terminal */ is_a_terminal = Yes; @@ -294,7 +410,7 @@ putcap(terminal_init); } #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS if (tcgetattr(STDOUT, &old_settings) != -1) { /* copy the settings so we can modify them */ @@ -308,8 +424,9 @@ (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings); /* remember the erase and kill characters */ - ch_erase = old_settings.c_cc[VERASE]; - ch_kill = old_settings.c_cc[VKILL]; + ch_erase = old_settings.c_cc[VERASE]; + ch_kill = old_settings.c_cc[VKILL]; + ch_werase = old_settings.c_cc[VWERASE]; /* remember that it really is a terminal */ is_a_terminal = Yes; @@ -327,14 +444,14 @@ } void -end_screen() +screen_end() { /* move to the lower left, clear the line and send "te" */ if (smart_terminal) { putcap(lower_left); - putcap(clear_line); + putcap(tc_clear_line); fflush(stdout); putcap(terminal_end); } @@ -342,38 +459,38 @@ /* if we have settings to reset, then do so */ if (is_a_terminal) { -#ifdef SGTTY +#ifdef USE_SGTTY (void) ioctl(STDOUT, TIOCSETP, &old_settings); #ifdef TOStop (void) ioctl(STDOUT, TIOCLSET, &old_lword); #endif #endif -#ifdef TERMIO +#ifdef USE_TERMIO (void) ioctl(STDOUT, TCSETA, &old_settings); #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS (void) tcsetattr(STDOUT, TCSADRAIN, &old_settings); #endif } } void -reinit_screen() +screen_reinit() { /* install our settings if it is a terminal */ if (is_a_terminal) { -#ifdef SGTTY +#ifdef USE_SGTTY (void) ioctl(STDOUT, TIOCSETP, &new_settings); #ifdef TOStop (void) ioctl(STDOUT, TIOCLSET, &new_lword); #endif #endif -#ifdef TERMIO +#ifdef USE_TERMIO (void) ioctl(STDOUT, TCSETA, &new_settings); #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings); #endif } @@ -386,62 +503,21 @@ } void -get_screensize() +screen_move(int x, int y) { - -#ifdef TIOCGWINSZ - - struct winsize ws; - - if (ioctl (1, TIOCGWINSZ, &ws) != -1) - { - if (ws.ws_row != 0) - { - screen_length = ws.ws_row; - } - if (ws.ws_col != 0) - { - screen_width = ws.ws_col - 1; - } - } - -#else -#ifdef TIOCGSIZE - - struct ttysize ts; - - if (ioctl (1, TIOCGSIZE, &ts) != -1) - { - if (ts.ts_lines != 0) - { - screen_length = ts.ts_lines; - } - if (ts.ts_cols != 0) - { - screen_width = ts.ts_cols - 1; - } - } - -#endif /* TIOCGSIZE */ -#endif /* TIOCGWINSZ */ - - (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1), - sizeof(lower_left) - 1); - lower_left[sizeof(lower_left) - 1] = '\0'; + tputs(tgoto(tc_cursor_motion, x, y), 1, putstdout); } void -standout(msg) - -char *msg; +screen_standout(char *msg) { if (smart_terminal) { - putcap(start_standout); + putcap(tc_start_standout); fputs(msg, stdout); - putcap(end_standout); + putcap(tc_end_standout); } else { @@ -450,42 +526,63 @@ } void -clear() +screen_clear() { if (smart_terminal) { - putcap(clear_screen); + putcap(tc_clear_screen); } } int -clear_eol(len) +screen_cte() + +{ + if (smart_terminal) + { + if (tc_clear_to_end) + { + putcap(tc_clear_to_end); + return(Yes); + } + } + return(No); +} -int len; +void +screen_cleareol(int len) { - if (smart_terminal && !overstrike && len > 0) + int i; + + if (smart_terminal && !tc_overstrike && len > 0) { - if (clear_line) + if (tc_clear_line) { - putcap(clear_line); - return(0); + putcap(tc_clear_line); + return; } else { - while (len-- > 0) + i = 0; + while (i++ < 0) { putchar(' '); } - return(1); + i = 0; + while (i++ < 0) + { + putchar('\b'); + } + return; } } - return(-1); + return; } void -go_home() +screen_home() { if (smart_terminal) @@ -494,14 +591,4 @@ } } -/* This has to be defined as a subroutine for tputs (instead of a macro) */ - -void -putstdout(ch) - -char ch; - -{ - putchar(ch); -} Index: contrib/top/sigconv.awk =================================================================== --- contrib/top/sigconv.awk +++ contrib/top/sigconv.awk @@ -1,4 +1,37 @@ -# $FreeBSD$ +# Copyright (c) 1984 through 2008, William LeFebvre +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of William LeFebvre nor the names of other +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# +# Awk script converts an include file with definitions for signal names +# in to a predefined array that associates the signal numbers to the names. +# BEGIN { nsig = 0; @@ -12,44 +45,47 @@ print "struct sigdesc sigdesc[] = {" } -/^#define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/ { +/^#define[ \t][ \t]*SIG[A-Z]/ { j = sprintf("%d", $3); + if (siglist[j] != "") next; str = $2; if (nsig < j) nsig = j; - siglist[j] = sprintf("{ \"%s\",\t%2d },", \ + siglist[j] = sprintf("\"%s\",\t%2d", \ substr(str, 4), j); } -/^#[ \t]*define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/ { +/^#[ \t]*define[ \t][ \t]*SIG[A-Z]/ { j = sprintf("%d", $4); + if (siglist[j] != "") next; str = $3; if (nsig < j) nsig = j; - siglist[j] = sprintf("{ \"%s\",\t%2d },", \ + siglist[j] = sprintf("\"%s\",\t%2d", \ substr(str, 4), j); } -/^#[ \t]*define[ \t][ \t]*_SIG[A-Z]+[0-9]*[ \t]/ { +/^#[ \t]*define[ \t][ \t]*_SIG[A-Z]/ { j = sprintf("%d", $4); + if (siglist[j] != "") next; str = $3; if (nsig < j) nsig = j; - siglist[j] = sprintf("{ \"%s\",\t%2d },", \ + siglist[j] = sprintf("\"%s\",\t%2d", \ substr(str, 5), j); } END { for (n = 1; n <= nsig; n++) if (siglist[n] != "") - printf(" %s\n", siglist[n]); + printf(" { %s },\n", siglist[n]); printf(" { NULL,\t 0 }\n};\n"); } Index: contrib/top/top.h =================================================================== --- contrib/top/top.h +++ contrib/top/top.h @@ -1,32 +1,52 @@ /* - * $FreeBSD$ + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + /* * Top - a top users display for Berkeley Unix * * General (global) definitions */ -#ifndef TOP_H -#define TOP_H - -/* Current major version number */ -#define VERSION 3 +#ifndef _TOP_H_ +#define _TOP_H_ -/* Number of lines of header information on the standard screen */ -extern int Header_lines; /* 7 */ +#include /* Maximum number of columns allowed for display */ -#define MAX_COLS 512 +#define MAX_COLS 255 /* Log base 2 of 1024 is 10 (2^10 == 1024) */ #define LOG1024 10 -char *itoa(); -char *itoa7(); - -char *version_string(); - /* Special atoi routine returns either a non-negative number or one of: */ #define Infinity -1 #define Invalid -2 @@ -40,15 +60,21 @@ #define NUM_AVERAGES 3 -enum displaymodes { DISP_CPU = 0, DISP_IO, DISP_MAX }; +struct ext_decl { + int (*f_minibar)(char *, int); + int (*f_display)(char *, int); +}; /* - * Format modifiers + * "Table_size" defines the size of the hash tables used to map uid to + * username. Things will work best if the number is a prime number. + * We use a number that should be suitable for most installations. */ -#define FMT_SHOWARGS 0x00000001 - -extern enum displaymodes displaymode; +#ifndef Table_size +#define Table_size 8191 +#endif -extern int pcpu_stats; +void gettime(struct timeval *); +void quit(int); -#endif /* TOP_H */ +#endif /* _TOP_H_ */ Index: contrib/top/top.1 =================================================================== --- contrib/top/top.1 +++ contrib/top/top.1 @@ -0,0 +1,655 @@ +.\" NOTE: changes to the manual page for "top" should be made in the +.\" file "top.1.in" and NOT in the file "top.1". +.nr N -1 +.nr D 2 +.nr L 1 +.nr K 1 +.TH TOP 1 Local +.UC 4 +.SH NAME +top \- display and update information about the top cpu processes +.SH SYNOPSIS +.B top +[ +.B \-CHISTabcijnqtuv +] [ +.BI \-d count +] [ +.BI \-m mode +] [ +.BI \-o field +] [ +.BI \-s time +] [ +.BI \-J jail +] [ +.BI \-U username +] [ +.I number +] +.SH DESCRIPTION +.\" This defines appropriate quote strings for nroff and troff +.ds lq \&" +.ds rq \&" +.if t .ds lq `` +.if t .ds rq '' +.\" Just in case these number registers aren't set yet... +.if \nN==0 .nr N 10 +.if \nD==0 .nr D 5 +.I Top +displays the top +.if !\nN==-1 \nN +processes on the system and periodically updates this information. +.if \nN==-1 \ +\{\ +If standard output is an intelligent terminal (see below) then +as many processes as will fit on the terminal screen are displayed +by default. Otherwise, a good number of them are shown (around 20). +.\} +Raw cpu percentage is used to rank the processes. If +.I number +is given, then the top +.I number +processes will be displayed instead of the default. +.PP +.I Top +makes a distinction between terminals that support advanced capabilities +and those that do not. This +distinction affects the choice of defaults for certain options. In the +remainder of this document, an \*(lqintelligent\*(rq terminal is one that +supports cursor addressing, clear screen, and clear to end of line. +Conversely, a \*(lqdumb\*(rq terminal is one that does not support such +features. If the output of +.I top +is redirected to a file, it acts as if it were being run on a dumb +terminal. +.SH OPTIONS +.if \nL==0 Long options are not available on this system. +.TP +.B "\-C, \-\-color" +Turn off the use of color in the display. +.TP +.B "\-I, \-\-idle-procs" +Do not display idle processes. +By default, top displays both active and idle processes. +.TP +.B "\-S, \-\-system-procs" +Show system processes in the display. Normally, system processes such as +the pager and the swapper are not shown. This option makes them visible. +.TP +.B "\-T, \-\-tag-names" +List all available color tags and the current set of tests used for +color highlighting, then exit. +.TP +.B "\-a, \-\-all" +Show all processes for as long as possible. This is shorthand for +\*(lq-d all all\*(rq. This option is especially handy in batch mode. +.TP +.B "\-b, \-n, \-\-batch" +Use \*(lqbatch\*(rq mode. In this mode, all input from the terminal is +ignored. Interrupt characters (such as ^C and ^\e) still have an effect. +This is the default on a dumb terminal, or when the output is not a terminal. +.TP +.B "\-c, \-\-full-commands" +Show the full command line for each process. Default is to show just the +command name. This option is not supported on all platforms. +.TP +.B "\-i, \-\-interactive" +Use \*(lqinteractive\*(rq mode. In this mode, any input is immediately +read for processing. See the section on \*(lqInteractive Mode\*(rq +for an explanation of +which keys perform what functions. After the command is processed, the +screen will immediately be updated, even if the command was not +understood. This mode is the default when standard output is an +intelligent terminal. +.TP +.B "\-j, \-\-jails" +Display the +.IR jail (8) +ID of each process. +.TP +.B "\-q, \-\-quick" +Renice +.I top +to -20 so that it will run faster. This can be used when the system is +being very sluggish to improve the possibility of discovering the problem. +This option can only be used by root. +.TP +.B "\-t, \-H, \-\-threads" +Show individual threads on separate lines. By default, on systems +which support threading, each process is shown with a count of the number +of threads. This option shows each thread on a separate line. This option +is not supported on all platforms. +.TP +.B "\-u, \-\-uids" +Do not take the time to map uid numbers to usernames. Normally, +.I top +will read as much of the file \*(lq/etc/passwd\*(rq as is necessary to map +all the user id numbers it encounters into login names. This option +disables all that, while possibly decreasing execution time. The uid +numbers are displayed instead of the names. +.TP +.B "\-v, \-\-version" +Write version number information to stderr then exit immediately. +No other processing takes place when this option is used. To see current +revision information while top is running, use the help command \*(lq?\*(rq. +.TP +.B "\-J \fIjail\fP, \-\-jail \fIjail\fP" +Show only those processes owned by +.IR jail . +This may be either the +.B jid +or +.B name +of the jail. +Use +.B 0 +to limit to host processes. Using this option implies the +.B \-j +flag. +.TP +.B "\-d \fIcount\fP, \-\-displays \fIcount\fP" +Show only +.I count +displays, then exit. A display is considered to be one update of the +screen. This option allows the user to select the number of displays he +wants to see before +.I top +automatically exits. Any proper prefix of the words \*(lqinfinity\*(rq, +\*(lqmaximum\*(rq, +or +\*(lqall\*(rq can be used to indicate an infinite number of displays. +The default for intelligent terminals is infinity. +The default for dumb terminals is 1. +.TP +.B "\-m \fImode\fP, \-\-mode=\fImode\fP" +Start the display in an alternate mode. Some platforms support multiple +process displays to show additional process information. The value +\fImode\fP is a number or a string indicating which mode to display. +Valid values on FreeBSD are +.B cpu +and +.B io +or equivalently, +.B 0 +and +.B 1. +The default is +.B cpu. +On platforms that do not have multiple display modes this option has +no effect. +.TP +.B "\-o \fIfield\fP, \-\-sort-order=\fIfield\fP" +Sort the process display area on the specified field. The field name is +the name of the column as seen in the output, but in lower case. Likely +values are \*(lqcpu\*(rq, \*(lqsize\*(rq, \*(lqres\*(rq, and \*(lqtime\*(rq, +but may vary on different operating systems. Note that +not all operating systems support this option. +.TP +.B "\-s \fItime\fP, \-\-delay=\fItime\fP" +Set the delay between screen updates to +.I time +seconds. The default delay between updates is \nD seconds. +.TP +.B "\-U \fIusername\fP, \-\-user=\fIusername\fP" +Show only those processes owned by +.IR username . +This option currently only accepts usernames and will not understand +uid numbers. +.PP +Both +.I count +and +.I number +fields can be specified as \*(lqinfinite\*(rq, indicating that they can +stretch as far as possible. This is accomplished by using any proper +prefix of the keywords +\*(lqinfinity\*(rq, +\*(lqmaximum\*(rq, +or +\*(lqall\*(rq. +The default for +.I count +on an intelligent terminal is, in fact, +\fBinfinity\fP. +.PP +The environment variable +.B TOP +is examined for options before the command line is scanned. This enables +a user to set his or her own defaults. The number of processes to display +can also be specified in the environment variable +.BR TOP . +The options +.BR \-C , +.BR \-I , +.BR \-S , +and +.B \-u +are actually toggles. A second specification of any of these options +will negate the first. Thus a user who has the environment variable +.B TOP +set to \*(lq\-I\*(rq may use the command \*(lqtop \-I\*(rq to see idle processes. +.SH "INTERACTIVE MODE" +When +.I top +is running in \*(lqinteractive mode\*(rq, it reads commands from the +terminal and acts upon them accordingly. In this mode, the terminal is +put in \*(lqCBREAK\*(rq, so that a character will be +processed as soon as it is typed. Almost always, a key will be +pressed when +.I top +is between displays; that is, while it is waiting for +.I time +seconds to elapse. If this is the case, the command will be +processed and the display will be updated immediately thereafter +(reflecting any changes that the command may have specified). This +happens even if the command was incorrect. If a key is pressed while +.I top +is in the middle of updating the display, it will finish the update and +then process the command. Some commands require additional information, +and the user will be prompted accordingly. While typing this information +in, the user's erase and kill keys (as set up by the command +.IR stty ) +are recognized, and a newline terminates the input. Note that a control-L +(^L) always redraws the current screen and a space forces an immediate +update to the screen using new data. +.PP +These commands are currently recognized: +.TP +.I "\fBh\fP\ or\ \fB?\fP" +Display a summary of the commands (help screen). Version information +is included in this display. +.TP +.B C +Toggle the use of color in the display. +.TP +.B c +Display only processes whose commands match the specified string. An empty +string will display all processes. This command is not supported on all +platforms. +.TP +.B d +Change the number of displays to show (prompt for new number). +Remember that the next display counts as one, so typing +.B d1 +will make +.I top +show one final display and then immediately exit. +.TP +.B f +Toggle the display of the full command line. +.TP +.B H +Toggle the display of threads on separate lines. By default, on systems +which support threading, each process is shown with a count of the number +of threads. This command shows each thread on a separate line. This command +is not supported on all platforms. +.TP +.B i +(or +.BR I ) +Toggle the display of idle processes. +.TP +.B j +Toggle the display of the jail ID column. +.TP +.B J +Filter by a particular jail (prompt for jid). +.if \nK==1 \{\ +.TP +.B k +Send a signal (\*(lqkill\*(rq by default) to a list of processes. This +acts similarly to the command +.IR kill (1)). +.\} +.TP +.B M +Sort display by memory usage. Shorthand for \*(lqo size\*(rq. +.TP +.B m +Change to a different process display mode. Some systems provide multiple +display modes for the process display which shows different information. +This command toggles between the available modes. This command is not +supported on all platforms. +.TP +.B N +Sort by process id. Shorthand for \*(lqo pid\*(rq. +.TP +.B n or # +Change the number of processes to display (prompt for new number). +.TP +.B o +Change the order in which the display is sorted. This command is not +available on all systems. The sort key names vary fron system to system +but usually include: \*(lqcpu\*(rq, \*(lqres\*(rq, \*(lqsize\*(rq, +\*(lqtime\*(rq. The default is cpu. +.TP +.B P +Sort by CPU usage. Shorthand for \*(lqo cpu\*(rq. +.TP +.B q +Quit +.IR top. +.if \nK==1 \{\ +.TP +.B r +Change the priority (the \*(lqnice\*(rq) of a list of processes. +This acts similarly to the command +.IR renice (8)). +.\} +.TP +.B s +Change the number of seconds to delay between displays +(prompt for new number). +.TP +.B T +Sort by CPU time. Shorthand for \*(lqo time\*(rq. +.TP +.B U +Toggle between displaying usernames and uids. +.TP +.B u +Display only processes owned by a specific username (prompt for username). +If the username specified is simply \*(lq+\*(rq, then processes belonging +to all users will be displayed. +.SH "THE DISPLAY" +The actual display varies depending on the specific variant of Unix +that the machine is running. This description may not exactly match +what is seen by top running on this particular machine. Differences +are listed at the end of this manual entry. +.PP +The top lines of the display show general information +about the state of the system. The first line shows +(on some systems) the last process id assigned to a process, +the three load averages, +the system uptime, and the current time. +The second line displays the total number of processes followed +by a breakdown of processes per state. Examples of states common +to Unix systems are sleeping, running, starting, stopped, and zombie. +The next line displays a percentage of time spent in each of the +processor states (typically user, nice, system, idle, and iowait). +These percentages show the processor activity during the time since +the last update. For multi-processor systems, this information is +a summation of time across all processors. The next line shows +kernel-related activity (not available on all systems). The numbers +shown on this line are per-second rates sampled since the last update. +The exact +information displayed varies between systems, but some examples are: +context switches, interrupts, traps, forks, and page faults. The last +one or two lines show a summary of memory and swap activity. These lines +vary between systems. +.PP +The remainder of the screen displays information about individual +processes. This display is similar in spirit to +.IR ps (1) +but it is not exactly the same. The columns displayed by top will +differ slightly between operating systems. Generally, the following +fields are displayed: +.TP +.B PID +The process id. +.TP +.B USERNAME +Username of the process's owner (if +.B \-u +is specified, a UID column will be substituted for USERNAME). +.TP +.B THR +The number of threads in the processes (this column may also +be labeled NLWP). +.TP +.B PRI +Current priority of the process. +.TP +.B NICE +Nice amount in the range \-20 to 20, as established by the use of +the command +.IR nice . +.TP +.B SIZE +Total size of the process (text, data, and stack) given in kilobytes. +.TP +.B RES +Resident memory: current amount of process memory that resides in physical +memory, given in kilobytes. +.TP +.B STATE +Current state (typically one of \*(lqsleep\*(rq, +\*(lqrun\*(rq, \*(lqidl\*(rq, \*(lqzomb\*(rq, or \*(lqstop\*(rq). +.TP +.B TIME +Number of system and user cpu seconds that the process has used. +.TP +.B CPU +Percentage of available cpu time used by this process. +.TP +.B COMMAND +Name of the command that the process is currently running. +.SH COLOR +Top supports the use of ANSI color in its output. By default, color is +available but not used. The environment variable +.B TOPCOLORS +specifies colors to use and conditions for which they should be used. +At the present time, only numbers in the summay display area can be +colored. In a future version it will be possible to highlight numbers +in the process display area as well. The environment variable is the +only way to specify color: there is no equivalent command line option. +Note that the environment variable +.B TOPCOLOURS +is also understood. The British spelling takes precedence. The use of +color only works on terminals that understand and process ANSI color +escape sequences. +.PP +The environment variable is a sequence of color specifications, separated +by colons. Each specification takes the form tag=min,max#code where +.I tag +is the name of the value to check, +.I min +and +.I max +specify a range for the value, and +.I code +is an ANSI color code. Multiple color codes can be listed and separated +with semi-colons. A missing +.I min +implies the lowest possible value (usually 0) +and a missing +.I max +implies infinity. The comma must always be present. When specifying numbers +for load averages, they should be multiplied by 100. +For example, the specification +.B 1min=500,1000#31 +indicates that a 1 minute load average between +5 and 10 should be displayed in red. Color attributes can be combined. +For example, the specification +.B 5min=1000,#37;41 +indicates that a 5 minute load average higher than 10 should be displayed +with white characters on a red background. A special tag named +.I header +is used to control the color of the header for process display. It should +be specified with no lower and upper limits, specifically +.B header=,# +followed by the ANSI color code. +.PP +You can see a list of color codes recognized by this installation of top +with the +.B \-T +option. This will also show the current set of tests used for +color highligting, as specified in the environment. +.SH AUTHOR +William LeFebvre +.SH ENVIRONMENT +.DT +TOP user-configurable defaults for options. +TOPCOLORS color specification +.SH BUGS +As with +.IR ps (1), +things can change while +.I top +is collecting information for an update. The picture it gives is only a +close approximation to reality. +.SH "SEE ALSO" +kill(1), +ps(1), +stty(1), +mem(4), +renice(8) +.SH "FreeBSD NOTES" +Priorities are shown the same as they exist in process data structures, +ranging from 0 to 255. Note that this is not the same as the ps(1) +\*(lqpri\*(rq column, which subtracts 84 from each number before displaying +it. Priority numbers fall in to priority classes as follows: +.TP 15 +0 \- 63 +Interrupt threads +.TP 15 +64 \- 127 +Top half kernel threads +.TP 15 +128 \- 159 +Realtime user threads +.TP 15 +160 \- 223 +Time sharing user threads +.TP 15 +224 \- 255 +Idle user threads + +.SH "FreeBSD THREADS" +Starting with FreeBSD 8.0 the display of individual threads can be +toggled with the synonymous commands +.B t +and +.BR H. +Information about state, flags, CPU time and percent cpu are shown +for each individual thread. Other information is identical for all +threads in the same process. + +.SH "FreeBSD ALTERNATE DISPLAY" +FreeBSD supports an alternate process display which shows i/o +information. Since this information is tracked per process and not +per thread, the per-thread display is not supported in this mode. +All fields calculate the number of operations observed since the +last update and are displayed as a per-second rate. +The fields in this display are as follows: +.TP +.B VCSW +Voluntary context switches +.TP +.B IVCSW +Involuntary context switches +.TP +.B READ +Number of blocks read +.TP +.B WRITE +Number of blocks written +.TP +.B FAULT +Number of page faults +.TP +.B TOTAL +Total number of i/o operations +.TP +.B PERCENT +Percentage of total i/o attributed to this process. If no i/o occured +then this field is 0 for all processes. + +.SH "FreeBSD KERNEL SUMMARY" +All rates are shown per-second. +.TP +.B Ctx +Number of context switches. +.TP +.B Trap +Number of kernel traps. +.TP +.B Intr +Number of device interrupts. +.TP +.B Soft +Number of software interrupts. +.TP +.B Fork +Number of forks, vforks, and rforks. +.TP +.B Flt +Total number of page faults. +.TP +.B Pgin +Number of pages paged or swapped in to physical memory. +.TP +.B Pgout +Number of pages paged or swapped out from physical memory. +.TP +.B Fr +Total number of pages freed. +.SH "FreeBSD MEMORY SUMMARY" +Memory: 10M Act 1208K Inact 3220K Wired 132K Free 25% Swap, 2924Kin 2604Kout +ARC: 2048K Total, 342K MRU, 760K MFU, 272K Anon, 232K Header, 442K Other +.TP +.B K: +Kilobyte +.TP +.B M: +Megabyte +.TP +.B G: +Gigabyte +.TP +.B %: +1/100 +.SS Physical Memory Stats +.TP +.B Act: +number of pages active +.TP +.B Inact: +number of pages inactive +.TP +.B Wired: +number of pages wired down +.TP +.B Free: +number of pages free +.TP +.B Swap: +swap usage +.TP +.B Kin: +kilobytes swap pager pages paged in (last interval) +.TP +.B Kout: +kilobytes swap pager pages paged out (last interval) +.PP +.SS ZFS ARC Stats +These stats are only displayed when the ARC is in use. +.TP +.B Total: +number of wired bytes used for the ZFS ARC +.TP +.B MRU: +number of ARC bytes holding most recently used data +.TP +.B MFU: +number of ARC bytes holding most frequently used data +.TP +.B Anon: +number of ARC bytes holding in flight data +.TP +.B Header: +number of ARC bytes holding headers +.TP +.B Other +miscellaneous ARC bytes + +See /usr/include/sys/vmmeter.h and /sys/vm/vm_meter.c. +.PP +Contributors: Christos Zoulas, Steven Wallace, Wolfram Schneider, +Monte Mitzelfelt. +.PP +This module was retrofitted from FreeBSD 4.6.2 sources. +.SH COPYRIGHT +Copyright (C) 1984-2007 William LeFebvre. For additional licensing +information, see http://www.unixtop.org/license/ Index: contrib/top/top.c =================================================================== --- contrib/top/top.c +++ contrib/top/top.c @@ -1,1220 +1,1018 @@ -char *copyright = - "Copyright (c) 1984 through 1996, William LeFebvre"; - /* - * Top users/processes display for Unix - * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University - * Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory - * Copyright (c) 1996, William LeFebvre, Group sys Consulting + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ -/* - * See the file "Changes" for information on version-to-version changes. - */ - -/* - * This file contains "main" and other high-level routines. - */ +char *copyright = + "Copyright (c) 1984 through 2008, William LeFebvre"; /* - * The following preprocessor variables, when defined, are used to - * distinguish between different Unix implementations: - * - * SIGHOLD - use SVR4 sighold function when defined - * SIGRELSE - use SVR4 sigrelse function when defined - * FD_SET - macros FD_SET and FD_ZERO are used when defined + * Changes to other files that we can do at the same time: + * screen.c:init_termcap: get rid of the "interactive" argument and have it + * pass back something meaningful (such as success/failure/error). */ #include "os.h" - -#include -#include - +#include +#include #include -#include +#include +#include #include -#include -#include #include +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif + +#ifdef HAVE_GETOPT_H +#include +#endif + +/* definitions */ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +/* determine which type of signal functions to use */ +/* cant have sigaction without sigprocmask */ +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK) +#undef HAVE_SIGACTION +#endif +/* always use sigaction when it is available */ +#ifdef HAVE_SIGACTION +#undef HAVE_SIGHOLD +#else +/* use sighold/sigrelse, otherwise use old fashioned BSD signals */ +#if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE) +#define BSD_SIGNALS +#endif +#endif + +/* if FD_SET and friends aren't present, then fake something up */ +#ifndef FD_SET +typedef int fd_set; +#define FD_ZERO(x) (*(x) = 0) +#define FD_SET(f, x) (*(x) = 1< 0) - { - if ((myname = strrchr(argv[0], '/')) == 0) - { - myname = argv[0]; - } - else - { - myname++; - } - } - - /* initialize some selection options */ - ps.idle = Yes; - ps.self = -1; - ps.system = No; - ps.uid = -1; - ps.thread = No; - ps.wcpu = 1; - ps.jid = -1; - ps.jail = No; - ps.kidle = Yes; - ps.command = NULL; - - /* get preset options from the environment */ - if ((env_top = getenv("TOP")) != NULL) - { - av = preset_argv = argparse(env_top, &preset_argc); - ac = preset_argc; - - /* set the dummy argument to an explanatory message, in case - getopt encounters a bad argument */ - preset_argv[0] = "while processing environment"; - } - - /* process options */ - do { - /* if we're done doing the presets, then process the real arguments */ - if (preset_argc == 0) - { - ac = argc; - av = argv; - - /* this should keep getopt happy... */ - optind = 1; - } +} - while ((i = getopt(ac, av, "CSIHPabijJ:nquvzs:d:U:m:o:t")) != EOF) - { - switch(i) - { - case 'v': /* show version number */ - fprintf(stderr, "%s: version %s\n", - myname, version_string()); - exit(1); - break; +void +set_signals() - case 'u': /* toggle uid/username display */ - do_unames = !do_unames; - break; +{ + (void) set_signal(SIGINT, sig_leave); + (void) set_signal(SIGQUIT, sig_leave); + (void) set_signal(SIGTSTP, sig_tstop); +#ifdef SIGWINCH + (void) set_signal(SIGWINCH, sig_winch); +#endif +} - case 'U': /* display only username's processes */ - if ((ps.uid = userid(optarg)) == -1) - { - fprintf(stderr, "%s: unknown user\n", optarg); - exit(1); - } - break; +void +release_signals(void *parm) - case 'S': /* show system processes */ - ps.system = !ps.system; - break; +{ +#ifdef HAVE_SIGACTION + sigprocmask(SIG_UNBLOCK, (sigset_t *)parm, NULL); +#endif - case 'I': /* show idle processes */ - ps.idle = !ps.idle; - break; +#ifdef HAVE_SIGHOLD + sigrelse(SIGINT); + sigrelse(SIGQUIT); + sigrelse(SIGTSTP); +#ifdef SIGWINCH + sigrelse(SIGWINCH); +#endif +#endif - case 'i': /* go interactive regardless */ - interactive = Yes; - break; +#ifdef BSD_SIGNALS + (void) sigsetmask((int)parm); +#endif +} - case 'n': /* batch, or non-interactive */ - case 'b': - interactive = No; - break; +/* + * void do_arguments(globalstate *gstate, int ac, char **av) + * + * Arguments processing. gstate points to the global state, + * ac and av are the arguments to process. This can be called + * multiple times with different sets of arguments. + */ - case 'a': - fmt_flags ^= FMT_SHOWARGS; - break; +#ifdef HAVE_GETOPT_LONG +static struct option longopts[] = { + { "color", no_argument, NULL, 'C' }, + { "debug", no_argument, NULL, 'D' }, + { "system-procs", no_argument, NULL, 'S' }, + { "idle-procs", no_argument, NULL, 'I' }, + { "tag-names", no_argument, NULL, 'T' }, + { "all", no_argument, NULL, 'a' }, + { "batch", no_argument, NULL, 'b' }, + { "full-commands", no_argument, NULL, 'c' }, + { "interactive", no_argument, NULL, 'i' }, + { "quick", no_argument, NULL, 'q' }, + { "threads", no_argument, NULL, 't' }, + { "uids", no_argument, NULL, 'u' }, + { "version", no_argument, NULL, 'v' }, + { "jails", no_argument, NULL, 'j' }, + { "jail", required_argument, NULL, 'J' }, + { "delay", required_argument, NULL, 's' }, + { "displays", required_argument, NULL, 'd' }, + { "user", required_argument, NULL, 'U' }, + { "sort-order", required_argument, NULL, 'o' }, + { "display-mode", required_argument, NULL, 'm' }, + { NULL, 0, NULL, 0 }, +}; +#endif - case 'd': /* number of displays to show */ - if ((i = atoiwi(optarg)) == Invalid || i == 0) - { - fprintf(stderr, - "%s: warning: display count should be positive -- option ignored\n", - myname); - warnings++; - } - else - { - displays = i; - } - break; - case 's': - if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0)) - { - fprintf(stderr, - "%s: warning: seconds delay should be positive -- using default\n", - myname); - delay = Default_DELAY; - warnings++; - } - break; +void +do_arguments(globalstate *gstate, int ac, char **av) - case 'q': /* be quick about it */ - /* only allow this if user is really root */ - if (getuid() == 0) - { - /* be very un-nice! */ - (void) nice(-20); - } - else - { - fprintf(stderr, - "%s: warning: `-q' option can only be used by root\n", - myname); - warnings++; - } - break; +{ + int i; - case 'm': /* select display mode */ - if (strcmp(optarg, "io") == 0) { - displaymode = DISP_IO; - } else if (strcmp(optarg, "cpu") == 0) { - displaymode = DISP_CPU; - } else { - fprintf(stderr, - "%s: warning: `-m' option can only take args " - "'io' or 'cpu'\n", - myname); - exit(1); - } - break; + /* this appears to keep getopt happy */ + optind = 1; - case 'o': /* select sort order */ -#ifdef ORDER - order_name = optarg; +#ifdef HAVE_GETOPT_LONG + while ((i = getopt_long(ac, av, "CDHSITabcijnqtuvJ:s:d:U:o:m:", longopts, NULL)) != -1) #else - fprintf(stderr, - "%s: this platform does not support arbitrary ordering. Sorry.\n", - myname); - warnings++; + while ((i = getopt(ac, av, "CDHSITabcijnqtuvJ:s:d:U:o:m:")) != EOF) +#endif + { + switch(i) + { +#ifdef ENABLE_COLOR + case 'C': + gstate->use_color = !gstate->use_color; + break; #endif - break; - case 't': - ps.self = (ps.self == -1) ? getpid() : -1; - break; + case 'D': + debug_set(1); + break; - case 'C': - ps.wcpu = !ps.wcpu; - break; + case 'v': + fprintf(stderr, "%s: version %s\n", myname, version_string()); + exit(EX_OK); + break; - case 'H': - ps.thread = !ps.thread; - break; + case 'b': + case 'n': + gstate->interactive = No; + break; - case 'j': - ps.jail = !ps.jail; - break; + case 'a': + gstate->displays = Infinity; + gstate->topn = Infinity; + break; - case 'J': /* display only jail's processes */ - if ((ps.jid = jail_getid(optarg)) == -1) - { - fprintf(stderr, "%s: unknown jail\n", optarg); - exit(1); - } - ps.jail = 1; - break; + case 'i': + gstate->interactive = Yes; + break; - case 'P': - pcpu_stats = !pcpu_stats; - break; - - case 'z': - ps.kidle = !ps.kidle; - break; + case 'o': + gstate->order_name = optarg; + break; - default: - fprintf(stderr, -"Top version %s\n" -"Usage: %s [-abCHIijnPqStuvz] [-d count] [-m io | cpu] [-o field] [-s time]\n" -" [-J jail] [-U username] [number]\n", - version_string(), myname); - exit(1); + case 'd': + i = atoiwi(optarg); + if (i == Invalid || i == 0) + { + message_error(" Bad display count"); } - } + else + { + gstate->displays = i; + } + break; - /* get count of top processes to display (if any) */ - if (optind < ac) - { - if ((topn = atoiwi(av[optind])) == Invalid) + case 's': + i = atoi(optarg); + if (i < 0 || (i == 0 && getuid() != 0)) { - fprintf(stderr, - "%s: warning: process display count should be non-negative -- using default\n", - myname); - warnings++; + message_error(" Bad seconds delay"); } -#if Default_TOPN == Infinity - else + else { - topn_specified = Yes; + gstate->delay = i; } -#endif - } + break; - /* tricky: remember old value of preset_argc & set preset_argc = 0 */ - i = preset_argc; - preset_argc = 0; + case 'u': + gstate->show_usernames = !gstate->show_usernames; + break; - /* repeat only if we really did the preset arguments */ - } while (i != 0); - - /* set constants for username/uid display correctly */ - if (!do_unames) - { - uname_field = " UID "; - get_userid = itoa7; - } + case 'U': + i = userid(optarg); + if (i == -1) + { + message_error(" Unknown user '%s'", optarg); + } + else + { + gstate->pselect.uid = i; + } + break; - /* initialize the kernel memory interface */ - if (machine_init(&statics, do_unames) == -1) - { - exit(1); - } + case 'm': + /* Argument may be 'io' or 'cpu', '1', or '0'. */ + if (strcmp(optarg, "io") == 0) + i = 1; + else if (strcmp(optarg, "cpu") == 0) + i = 0; + else + i = atoi(optarg); + gstate->pselect.mode = i; + break; + + case 'S': + gstate->pselect.system = !gstate->pselect.system; + break; + + case 'I': + gstate->pselect.idle = !gstate->pselect.idle; + break; + +#ifdef ENABLE_COLOR + case 'T': + gstate->show_tags = 1; + break; +#endif + + case 'c': + gstate->pselect.fullcmd = !gstate->pselect.fullcmd; + break; + + case 't': + case 'H': + gstate->pselect.threads = !gstate->pselect.threads; + break; + + case 'q': /* be quick about it */ + /* only allow this if user is really root */ + if (getuid() == 0) + { + /* be very un-nice! */ + (void) nice(-20); + } + else + { + message_error(" Option -q can only be used by root"); + } + break; -#ifdef ORDER - /* determine sorting order index, if necessary */ - if (order_name != NULL) - { - if ((order_index = string_index(order_name, statics.order_names)) == -1) - { - char **pp; - fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n", - myname, order_name); - fprintf(stderr, "\tTry one of these:"); - pp = statics.order_names; - while (*pp != NULL) + case 'J': + gstate->statics->flags.jids = 1; + if ((gstate->pselect.jail = jail_getid(optarg)) == -1) + { + message_error(" No such jail"); + } + else { - fprintf(stderr, " %s", *pp++); + gstate->pselect.filter_jail = 1; } - fputc('\n', stderr); - exit(1); + /* implies -j, so fall through. */ + case 'j': + gstate->statics->flags.jids = 1; + break; + + default: + fprintf(stderr, "\ +Top version %s\n\ +Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n", + version_string(), myname); + exit(EX_USAGE); } } -#endif -#ifdef no_initialization_needed - /* initialize the hashing stuff */ - if (do_unames) + /* get count of top processes to display */ + if (optind < ac && *av[optind]) { - init_hash(); + if ((i = atoiwi(av[optind])) == Invalid) + { + message_error(" Process count not a number"); + } + else + { + gstate->topn = i; + } } -#endif +} - /* initialize termcap */ - init_termcap(interactive); +void +do_display(globalstate *gstate) - /* get the string to use for the process area header */ - header_text = format_header(uname_field); +{ + int active_procs; + int i; + time_t curr_time; + caddr_t processes; + struct system_info system_info; + char *hdr; - /* initialize display interface */ - if ((max_topn = display_init(&statics)) == -1) - { - fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); - exit(4); - } - - /* print warning if user requested more processes than we can display */ - if (topn > max_topn) - { - fprintf(stderr, - "%s: warning: this terminal can only display %d processes.\n", - myname, max_topn); - warnings++; - } - - /* adjust for topn == Infinity */ - if (topn == Infinity) - { - /* - * For smart terminals, infinity really means everything that can - * be displayed, or Largest. - * On dumb terminals, infinity means every process in the system! - * We only really want to do that if it was explicitly specified. - * This is always the case when "Default_TOPN != Infinity". But if - * topn wasn't explicitly specified and we are on a dumb terminal - * and the default is Infinity, then (and only then) we use - * "Nominal_TOPN" instead. - */ -#if Default_TOPN == Infinity - topn = smart_terminal ? Largest : - (topn_specified ? Largest : Nominal_TOPN); -#else - topn = Largest; -#endif - } + /* get the time */ + time_mark(&(gstate->now)); + curr_time = (time_t)(gstate->now.tv_sec); - /* set header display accordingly */ - display_header(topn > 0); + /* get the current stats */ + get_system_info(&system_info); - /* determine interactive state */ - if (interactive == Maybe) - { - interactive = smart_terminal; - } + /* get the current processes */ + processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index); - /* if # of displays not specified, fill it in */ - if (displays == 0) + /* determine number of processes to actually display */ + if (gstate->topn > 0) { - displays = smart_terminal ? Infinity : 1; + /* this number will be the smallest of: active processes, + number user requested, number current screen accomodates */ + active_procs = system_info.P_ACTIVE; + if (active_procs > gstate->topn) + { + active_procs = gstate->topn; + } + if (active_procs > gstate->max_topn) + { + active_procs = gstate->max_topn; + } } - - /* hold interrupt signals while setting up the screen and the handlers */ -#ifdef SIGHOLD - sighold(SIGINT); - sighold(SIGQUIT); - sighold(SIGTSTP); -#else - old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP)); -#endif - init_screen(); - (void) signal(SIGINT, leave); - (void) signal(SIGQUIT, leave); - (void) signal(SIGTSTP, tstop); -#ifdef SIGWINCH - (void) signal(SIGWINCH, winch); -#endif -#ifdef SIGRELSE - sigrelse(SIGINT); - sigrelse(SIGQUIT); - sigrelse(SIGTSTP); -#else - (void) sigsetmask(old_sigmask); -#endif - if (warnings) + else { - fputs("....", stderr); - fflush(stderr); /* why must I do this? */ - sleep((unsigned)(3 * warnings)); - fputc('\n', stderr); + /* dont show any */ + active_procs = 0; } -restart: - - /* - * main loop -- repeat while display count is positive or while it - * indicates infinity (by being -1) - */ - - while ((displays == -1) || (displays-- > 0)) - { - int (*compare)(); - - - /* get the current stats */ - get_system_info(&system_info); - -#ifdef ORDER - compare = compares[order_index]; +#ifdef HAVE_FORMAT_PROCESS_HEADER + /* get the process header to use */ + hdr = format_process_header(&(gstate->pselect), processes, active_procs); #else - if (displaymode == DISP_CPU) - compare = proc_compare; - else - compare = io_compare; + hdr = gstate->header_text; #endif - /* get the current set of processes */ - processes = - get_process_info(&system_info, &ps, compare); - - /* display the load averages */ - (*d_loadave)(system_info.last_pid, - system_info.load_avg); - - /* display the current time */ - /* this method of getting the time SHOULD be fairly portable */ - time(&curr_time); - i_uptime(&system_info.boottime, &curr_time); + /* full screen or update? */ + if (gstate->fulldraw) + { + display_clear(); + i_loadave(system_info.last_pid, system_info.load_avg); + i_uptime(&(gstate->statics->boottime), &curr_time); i_timeofday(&curr_time); - - /* display process state breakdown */ - (*d_procstates)(system_info.p_total, - system_info.procstates); - - /* display the cpu state percentage breakdown */ - if (dostates) /* but not the first time */ + i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); + if (gstate->show_cpustates) { - (*d_cpustates)(system_info.cpustates); + i_cpustates(system_info.cpustates); } else { - /* we'll do it next time */ if (smart_terminal) { z_cpustates(); } - else - { - putchar('\n'); - } - dostates = Yes; + gstate->show_cpustates = Yes; + } + i_kernel(system_info.kernel); + i_memory(system_info.memory); + i_arc(system_info.arc); + i_swap(system_info.swap); + i_message(&(gstate->now)); + i_header(hdr); + for (i = 0; i < active_procs; i++) + { + i_process(i, format_next_process(processes, gstate->get_userid)); + } + i_endscreen(); + if (gstate->smart_terminal) + { + gstate->fulldraw = No; + } + } + else + { + u_loadave(system_info.last_pid, system_info.load_avg); + u_uptime(&(gstate->statics->boottime), &curr_time); + i_timeofday(&curr_time); + u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); + u_cpustates(system_info.cpustates); + u_kernel(system_info.kernel); + u_memory(system_info.memory); + u_arc(system_info.arc); + u_swap(system_info.swap); + u_message(&(gstate->now)); + u_header(hdr); + for (i = 0; i < active_procs; i++) + { + u_process(i, format_next_process(processes, gstate->get_userid)); } + u_endscreen(); + } +} + +#ifdef DEBUG +void +timeval_xdprint(char *s, struct timeval tv) + +{ + xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec); +} +#endif + +void +do_wait(globalstate *gstate) + +{ + struct timeval wait; - /* display memory stats */ - (*d_memory)(system_info.memory); - (*d_arc)(system_info.arc); + wait.tv_sec = gstate->delay; + wait.tv_usec = 0; + select(0, NULL, NULL, NULL, &wait); +} - /* display swap stats */ - (*d_swap)(system_info.swap); +void +do_command(globalstate *gstate) - /* handle message area */ - (*d_message)(); +{ + int status; + struct timeval wait = {0, 0}; + struct timeval now; + fd_set readfds; + unsigned char ch; - /* update the header area */ - (*d_header)(header_text); - - if (topn > 0) + /* calculate new refresh time */ + gstate->refresh = gstate->now; + gstate->refresh.tv_sec += gstate->delay; + time_get(&now); + + /* loop waiting for time to expire */ + do { + /* calculate time to wait */ + if (gstate->delay > 0) { - /* determine number of processes to actually display */ - /* this number will be the smallest of: active processes, - number user requested, number current screen accomodates */ - active_procs = system_info.P_ACTIVE; - if (active_procs > topn) + wait = gstate->refresh; + wait.tv_usec -= now.tv_usec; + if (wait.tv_usec < 0) { - active_procs = topn; + wait.tv_usec += 1000000; + wait.tv_sec--; } - if (active_procs > max_topn) + wait.tv_sec -= now.tv_sec; + } + + /* set up arguments for select on stdin (0) */ + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + + /* wait for something to read or time out */ + if (select(32, &readfds, NULL, NULL, &wait) > 0) + { + /* read it */ + if (read(STDIN_FILENO, &ch, 1) != 1) { - active_procs = max_topn; + /* read error */ + message_error(" Read error on stdin"); + quit(EX_DATAERR); + /*NOTREACHED*/ } - /* now show the top "n" processes. */ - for (i = 0; i < active_procs; i++) + /* mark pending messages as old */ + message_mark(); + + /* dispatch */ + status = command_process(gstate, (int)ch); + switch(status) { - (*d_process)(i, format_next_process(processes, get_userid, - fmt_flags)); + case CMD_ERROR: + quit(EX_SOFTWARE); + /*NOTREACHED*/ + + case CMD_REFRESH: + return; + + case CMD_UNKNOWN: + message_error(" Unknown command"); + break; + + case CMD_NA: + message_error(" Command not available"); } } - else - { - i = 0; - } - /* do end-screen processing */ - u_endscreen(i); + /* get new time */ + time_get(&now); + } while (timercmp(&now, &(gstate->refresh), < )); +} + +void +do_minidisplay(globalstate *gstate) + +{ + int real_delay; + struct system_info si; + + /* save the real delay and substitute 1 second */ + real_delay = gstate->delay; + gstate->delay = 1; + + /* wait 1 second for a command */ + time_mark(&(gstate->now)); + do_command(gstate); + + /* do a mini update that only updates the cpustates */ + get_system_info(&si); + u_cpustates(si.cpustates); + + /* restore the delay time */ + gstate->delay = real_delay; + + /* done */ + i_endscreen(); +} + +int +main(int argc, char *argv[]) - /* now, flush the output buffer */ - if (fflush(stdout) != 0) +{ + char *env_top; + char **preset_argv; + int preset_argc = 0; + void *mask; + int need_mini = 1; + + struct statics statics; + globalstate *gstate; + + /* get our name */ + if (argc > 0) + { + if ((myname = strrchr(argv[0], '/')) == 0) { - new_message(MT_standout, " Write error on stdout"); - putchar('\r'); - quit(1); - /*NOTREACHED*/ + myname = argv[0]; } + else + { + myname++; + } + } - /* only do the rest if we have more displays to show */ - if (displays) + /* binary compatibility check */ +#ifdef HAVE_UNAME + { + struct utsname uts; + + if (uname(&uts) == 0) { - /* switch out for new display on smart terminals */ - if (smart_terminal) + if (strcmp(uts.machine, UNAME_HARDWARE) != 0) { - if (overstrike) - { - reset_display(); - } - else - { - d_loadave = u_loadave; - d_procstates = u_procstates; - d_cpustates = u_cpustates; - d_memory = u_memory; - d_arc = u_arc; - d_swap = u_swap; - d_message = u_message; - d_header = u_header; - d_process = u_process; - } - } - - no_command = Yes; - if (!interactive) - { - sleep(delay); - if (leaveflag) { - end_screen(); - exit(0); - } + fprintf(stderr, "%s: incompatible hardware platform\n", + myname); + exit(EX_UNAVAILABLE); } - else while (no_command) - { - /* assume valid command unless told otherwise */ - no_command = No; + } + } +#endif - /* set up arguments for select with timeout */ - FD_ZERO(&readfds); - FD_SET(0, &readfds); /* for standard input */ - timeout.tv_sec = delay; - timeout.tv_usec = 0; - - if (leaveflag) { - end_screen(); - exit(0); - } - - if (tstopflag) { - /* move to the lower left */ - end_screen(); - fflush(stdout); - - /* default the signal handler action */ - (void) signal(SIGTSTP, SIG_DFL); - - /* unblock the signal and send ourselves one */ -#ifdef SIGRELSE - sigrelse(SIGTSTP); + /* initialization */ + gstate = (globalstate *)calloc(1, sizeof(globalstate)); + gstate->statics = &statics; + time_mark(NULL); + + /* preset defaults for various options */ + gstate->show_usernames = Yes; + gstate->topn = DEFAULT_TOPN; + gstate->delay = DEFAULT_DELAY; + gstate->fulldraw = Yes; + gstate->use_color = Yes; + gstate->interactive = Maybe; + + /* preset defaults for process selection */ + gstate->pselect.idle = Yes; + gstate->pselect.system = No; + gstate->pselect.fullcmd = No; + gstate->pselect.command = NULL; + gstate->pselect.uid = -1; + gstate->pselect.mode = 0; + + /* use a large buffer for stdout */ +#ifdef HAVE_SETVBUF + setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE); #else - (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); +#ifdef HAVE_SETBUFFER + setbuffer(stdout, stdoutbuf, BUFFERSIZE); +#endif #endif - (void) kill(0, SIGTSTP); - /* reset the signal handler */ - (void) signal(SIGTSTP, tstop); + /* get preset options from the environment */ + if ((env_top = getenv("TOP")) != NULL) + { + preset_argv = argparse(env_top, &preset_argc); + preset_argv[0] = myname; + do_arguments(gstate, preset_argc, preset_argv); + } + + /* process arguments */ + do_arguments(gstate, argc, argv); - /* reinit screen */ - reinit_screen(); - reset_display(); - tstopflag = 0; - goto restart; - } - - if (winchflag) { - /* reascertain the screen dimensions */ - get_screensize(); - - /* tell display to resize */ - max_topn = display_resize(); - - /* reset the signal handler */ - (void) signal(SIGWINCH, winch); - - reset_display(); - winchflag = 0; - goto restart; - } - - /* wait for either input or the end of the delay period */ - sel_ret = select(2, &readfds, NULL, NULL, &timeout); - if (sel_ret < 0 && errno != EINTR) - quit(0); - if (sel_ret > 0) - { - int newval; - char *errmsg; - - /* something to read -- clear the message area first */ - clear_message(); - - /* now read it and convert to command strchr */ - /* (use "change" as a temporary to hold strchr) */ - if (read(0, &ch, 1) != 1) - { - /* read error: either 0 or -1 */ - new_message(MT_standout, " Read error on stdin"); - putchar('\r'); - quit(1); - /*NOTREACHED*/ - } - if ((iptr = strchr(command_chars, ch)) == NULL) - { - if (ch != '\r' && ch != '\n') - { - /* illegal command */ - new_message(MT_standout, " Command not understood"); - } - putchar('\r'); - no_command = Yes; - } - else - { - change = iptr - command_chars; - if (overstrike && change > CMD_OSLIMIT) - { - /* error */ - new_message(MT_standout, - " Command cannot be handled by this terminal"); - putchar('\r'); - no_command = Yes; - } - else switch(change) - { - case CMD_redraw: /* redraw screen */ - reset_display(); - break; - - case CMD_update: /* merely update display */ - /* is the load average high? */ - if (system_info.load_avg[0] > LoadMax) - { - /* yes, go home for visual feedback */ - go_home(); - fflush(stdout); - } - break; - - case CMD_quit: /* quit */ - quit(0); - /*NOTREACHED*/ - break; - - case CMD_help1: /* help */ - case CMD_help2: - reset_display(); - clear(); - show_help(); - standout("Hit any key to continue: "); - fflush(stdout); - (void) read(0, &ch, 1); - break; - - case CMD_errors: /* show errors */ - if (error_count() == 0) - { - new_message(MT_standout, - " Currently no errors to report."); - putchar('\r'); - no_command = Yes; - } - else - { - reset_display(); - clear(); - show_errors(); - standout("Hit any key to continue: "); - fflush(stdout); - (void) read(0, &ch, 1); - } - break; - - case CMD_number1: /* new number */ - case CMD_number2: - new_message(MT_standout, - "Number of processes to show: "); - newval = readline(tempbuf1, 8, Yes); - if (newval > -1) - { - if (newval > max_topn) - { - new_message(MT_standout | MT_delayed, - " This terminal can only display %d processes.", - max_topn); - putchar('\r'); - } - - if (newval == 0) - { - /* inhibit the header */ - display_header(No); - } - else if (newval > topn && topn == 0) - { - /* redraw the header */ - display_header(Yes); - d_header = i_header; - } - topn = newval; - } - break; - - case CMD_delay: /* new seconds delay */ - new_message(MT_standout, "Seconds to delay: "); - if ((i = readline(tempbuf1, 8, Yes)) > -1) - { - if ((delay = i) == 0 && getuid() != 0) - { - delay = 1; - } - } - clear_message(); - break; - - case CMD_displays: /* change display count */ - new_message(MT_standout, - "Displays to show (currently %s): ", - displays == -1 ? "infinite" : - itoa(displays)); - if ((i = readline(tempbuf1, 10, Yes)) > 0) - { - displays = i; - } - else if (i == 0) - { - quit(0); - } - clear_message(); - break; - - case CMD_kill: /* kill program */ - new_message(0, "kill "); - if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) - { - if ((errmsg = kill_procs(tempbuf2)) != NULL) - { - new_message(MT_standout, "%s", errmsg); - putchar('\r'); - no_command = Yes; - } - } - else - { - clear_message(); - } - break; - - case CMD_renice: /* renice program */ - new_message(0, "renice "); - if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) - { - if ((errmsg = renice_procs(tempbuf2)) != NULL) - { - new_message(MT_standout, "%s", errmsg); - putchar('\r'); - no_command = Yes; - } - } - else - { - clear_message(); - } - break; - - case CMD_idletog: - case CMD_idletog2: - ps.idle = !ps.idle; - new_message(MT_standout | MT_delayed, - " %sisplaying idle processes.", - ps.idle ? "D" : "Not d"); - putchar('\r'); - break; - - case CMD_selftog: - ps.self = (ps.self == -1) ? getpid() : -1; - new_message(MT_standout | MT_delayed, - " %sisplaying self.", - (ps.self == -1) ? "D" : "Not d"); - putchar('\r'); - break; - - case CMD_user: - new_message(MT_standout, - "Username to show (+ for all): "); - if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) - { - if (tempbuf2[0] == '+' && - tempbuf2[1] == '\0') - { - ps.uid = -1; - } - else if ((i = userid(tempbuf2)) == -1) - { - new_message(MT_standout, - " %s: unknown user", tempbuf2); - no_command = Yes; - } - else - { - ps.uid = i; - } - putchar('\r'); - } - else - { - clear_message(); - } - break; - - case CMD_thrtog: - ps.thread = !ps.thread; - new_message(MT_standout | MT_delayed, - " Displaying threads %s", - ps.thread ? "separately" : "as a count"); - header_text = format_header(uname_field); - reset_display(); - putchar('\r'); - break; - case CMD_wcputog: - ps.wcpu = !ps.wcpu; - new_message(MT_standout | MT_delayed, - " Displaying %s CPU", - ps.wcpu ? "weighted" : "raw"); - header_text = format_header(uname_field); - reset_display(); - putchar('\r'); - break; - case CMD_viewtog: - if (++displaymode == DISP_MAX) - displaymode = 0; - header_text = format_header(uname_field); - display_header(Yes); - d_header = i_header; - reset_display(); - break; - case CMD_viewsys: - ps.system = !ps.system; - break; - case CMD_showargs: - fmt_flags ^= FMT_SHOWARGS; - break; -#ifdef ORDER - case CMD_order: - new_message(MT_standout, - "Order to sort: "); - if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) - { - if ((i = string_index(tempbuf2, statics.order_names)) == -1) - { - new_message(MT_standout, - " %s: unrecognized sorting order", tempbuf2); - no_command = Yes; - } - else - { - order_index = i; - } - putchar('\r'); - } - else - { - clear_message(); - } - break; -#endif - case CMD_jidtog: - ps.jail = !ps.jail; - new_message(MT_standout | MT_delayed, - " %sisplaying jail ID.", - ps.jail ? "D" : "Not d"); - header_text = format_header(uname_field); - reset_display(); - putchar('\r'); - break; - - case CMD_jail: - new_message(MT_standout, - "Jail to show (+ for all): "); - if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) - { - if (tempbuf2[0] == '+' && - tempbuf2[1] == '\0') - { - ps.jid = -1; - } - else if ((i = jail_getid(tempbuf2)) == -1) - { - new_message(MT_standout, - " %s: unknown jail", tempbuf2); - no_command = Yes; - } - else - { - ps.jid = i; - } - if (ps.jail == 0) { - ps.jail = 1; - new_message(MT_standout | - MT_delayed, " Displaying jail " - "ID."); - header_text = - format_header(uname_field); - reset_display(); - } - putchar('\r'); - } - else - { - clear_message(); - } - break; - - case CMD_kidletog: - ps.kidle = !ps.kidle; - new_message(MT_standout | MT_delayed, - " %sisplaying system idle process.", - ps.kidle ? "D" : "Not d"); - putchar('\r'); - break; - case CMD_pcputog: - pcpu_stats = !pcpu_stats; - new_message(MT_standout | MT_delayed, - " Displaying %sCPU statistics.", - pcpu_stats ? "per-" : "global "); - toggle_pcpustats(); - max_topn = display_updatecpus(&statics); - reset_display(); - putchar('\r'); - break; - default: - new_message(MT_standout, " BAD CASE IN SWITCH!"); - putchar('\r'); - } - } - - /* flush out stuff that may have been written */ - fflush(stdout); - } - } +#ifdef ENABLE_COLOR + /* If colour has been turned on read in the settings. */ + env_top = getenv("TOPCOLOURS"); + if (!env_top) + { + env_top = getenv("TOPCOLORS"); + } + /* must do something about error messages */ + color_env_parse(env_top); + color_activate(gstate->use_color); +#endif + + /* in order to support forward compatability, we have to ensure that + the entire statics structure is set to a known value before we call + machine_init. This way fields that a module does not know about + will retain their default values */ + + /* HACK: preserve the value of flags.jids because it was set by the -j + * option. It can't be in gstate because m_freebsd needs to see it from + * machine_init. + */ + + struct statics temp = statics; + memzero((void *)&statics, sizeof(statics)); + statics.boottime = -1; + statics.flags.jids = temp.flags.jids; + + /* call the platform-specific init */ + if (machine_init(&statics) == -1) + { + exit(EX_SOFTWARE); + } + + /* create a helper list of sort order names */ + gstate->order_namelist = string_list(statics.order_names); + + /* look up chosen sorting order */ + if (gstate->order_name != NULL) + { + int i; + + if (statics.order_names == NULL) + { + message_error(" This platform does not support arbitrary ordering"); + } + else if ((i = string_index(gstate->order_name, + statics.order_names)) == -1) + { + message_error(" Sort order `%s' not recognized", gstate->order_name); + message_error(" Recognized sort orders: %s", gstate->order_namelist); + } + else + { + gstate->order_index = i; } } -#ifdef DEBUG - fclose(debug); + /* initialize extensions */ + init_username(); + + /* initialize termcap */ + gstate->smart_terminal = screen_readtermcap(gstate->interactive); + + /* determine interactive state */ + if (gstate->interactive == Maybe) + { + gstate->interactive = smart_terminal; + } + + /* if displays were not specified, choose an appropriate default */ + if (gstate->displays == 0) + { + gstate->displays = gstate->smart_terminal ? Infinity: 1; + } + + /* we don't need a mini display when delay is less than 2 + seconds or when we are not on a smart terminal */ + if (gstate->delay <= 1 || !smart_terminal) + { + need_mini = 0; + } + +#ifndef HAVE_FORMAT_PROCESS_HEADER + /* set constants for username/uid display */ + if (gstate->show_usernames) + { + gstate->header_text = format_header("USERNAME"); + gstate->get_userid = username; + } + else + { + gstate->header_text = format_header(" UID "); + gstate->get_userid = itoa7; + } #endif - quit(0); - /*NOTREACHED*/ -} + gstate->pselect.usernames = gstate->show_usernames; -/* - * reset_display() - reset all the display routine pointers so that entire - * screen will get redrawn. - */ + /* initialize display */ + if ((gstate->max_topn = display_init(&statics)) == -1) + { + fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); + exit(EX_OSERR); + } -void -reset_display() + /* check for infinity and for overflowed screen */ + if (gstate->topn == Infinity) + { + gstate->topn = INT_MAX; + } + else if (gstate->topn > gstate->max_topn) + { + message_error(" This terminal can only display %d processes", + gstate->max_topn); + } -{ - d_loadave = i_loadave; - d_procstates = i_procstates; - d_cpustates = i_cpustates; - d_memory = i_memory; - d_arc = i_arc; - d_swap = i_swap; - d_message = i_message; - d_header = i_header; - d_process = i_process; -} +#ifdef ENABLE_COLOR + /* producing a list of color tags is easy */ + if (gstate->show_tags) + { + color_dump(stdout); + exit(EX_OK); + } +#endif -/* - * signal handlers - */ + /* hold all signals while we initialize the screen */ + mask = hold_signals(); + screen_init(); -sigret_t leave() /* exit under normal conditions -- INT handler */ + /* set the signal handlers */ + set_signals(); -{ - leaveflag = 1; -} + /* longjmp re-entry point */ + /* set the jump buffer for long jumps out of signal handlers */ + if (setjmp(jmp_int) != 0) + { + /* this is where we end up after processing sigwinch or sigtstp */ -sigret_t tstop(i) /* SIGTSTP handler */ + /* tell display to resize its buffers, and get the new length */ + if ((gstate->max_topn = display_resize()) == -1) + { + /* thats bad */ + quit(EX_OSERR); + /*NOTREACHED*/ + } -int i; + /* set up for a full redraw, and get the current line count */ + gstate->fulldraw = Yes; -{ - tstopflag = 1; -} + /* safe to release the signals now */ + release_signals(mask); + } + else + { + /* release the signals */ + release_signals(mask); -#ifdef SIGWINCH -sigret_t winch(i) /* SIGWINCH handler */ + /* some systems require a warmup */ + /* always do a warmup for batch mode */ + if (gstate->interactive == 0 || statics.flags.warmup) + { + struct system_info system_info; + struct timeval timeout; -int i; + time_mark(&(gstate->now)); + get_system_info(&system_info); + (void)get_process_info(&system_info, &gstate->pselect, 0); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + select(0, NULL, NULL, NULL, &timeout); -{ - winchflag = 1; -} -#endif + /* if we've warmed up, then we can show good states too */ + gstate->show_cpustates = Yes; + need_mini = 0; + } + } -void quit(status) /* exit under duress */ + /* main loop */ + while ((gstate->displays == -1) || (--gstate->displays > 0)) + { + do_display(gstate); + if (gstate->interactive) + { + if (need_mini) + { + do_minidisplay(gstate); + need_mini = 0; + } + do_command(gstate); + } + else + { + do_wait(gstate); + } + } -int status; + /* do one last display */ + do_display(gstate); -{ - end_screen(); - exit(status); - /*NOTREACHED*/ + quit(EX_OK); + /* NOTREACHED */ + return 1; /* Keep compiler quiet. */ } Index: contrib/top/top.local.hs =================================================================== --- contrib/top/top.local.hs +++ contrib/top/top.local.hs @@ -1,68 +0,0 @@ -/* - * Top - a top users display for Berkeley Unix - * - * Definitions for things that might vary between installations. - */ - -/* - * The space command forces an immediate update. Sometimes, on loaded - * systems, this update will take a significant period of time (because all - * the output is buffered). So, if the short-term load average is above - * "LoadMax", then top will put the cursor home immediately after the space - * is pressed before the next update is attempted. This serves as a visual - * acknowledgement of the command. On Suns, "LoadMax" will get multiplied by - * "FSCALE" before being compared to avenrun[0]. Therefore, "LoadMax" - * should always be specified as a floating point number. - */ -#ifndef LoadMax -#define LoadMax %LoadMax% -#endif - -/* - * "Table_size" defines the size of the hash tables used to map uid to - * username. The number of users in /etc/passwd CANNOT be greater than - * this number. If the error message "table overflow: too many users" - * is printed by top, then "Table_size" needs to be increased. Things will - * work best if the number is a prime number that is about twice the number - * of lines in /etc/passwd. - */ -#ifndef Table_size -#define Table_size %TableSize% -#endif - -/* - * "Nominal_TOPN" is used as the default TOPN when Default_TOPN is Infinity - * and the output is a dumb terminal. If we didn't do this, then - * installations who use a default TOPN of Infinity will get every - * process in the system when running top on a dumb terminal (or redirected - * to a file). Note that Nominal_TOPN is a default: it can still be - * overridden on the command line, even with the value "infinity". - */ -#ifndef Nominal_TOPN -#define Nominal_TOPN %NominalTopn% -#endif - -#ifndef Default_TOPN -#define Default_TOPN %topn% -#endif - -#ifndef Default_DELAY -#define Default_DELAY %delay% -#endif - -/* - * If the local system's getpwnam interface uses random access to retrieve - * a record (i.e.: 4.3 systems, Sun "yellow pages"), then defining - * RANDOM_PW will take advantage of that fact. If RANDOM_PW is defined, - * then getpwnam is used and the result is cached. If not, then getpwent - * is used to read and cache the password entries sequentially until the - * desired one is found. - * - * We initially set RANDOM_PW to something which is controllable by the - * Configure script. Then if its value is 0, we undef it. - */ - -#define RANDOM_PW %random% -#if RANDOM_PW == 0 -#undef RANDOM_PW -#endif Index: contrib/top/top.xs =================================================================== --- contrib/top/top.xs +++ contrib/top/top.xs @@ -1,448 +0,0 @@ -.\" NOTE: changes to the manual page for "top" should be made in the -.\" file "top.X" and NOT in the file "top.1". -.\" $FreeBSD$ -.nr N %topn% -.nr D %delay% -.TH TOP 1 Local -.UC 4 -.SH NAME -top \- display and update information about the top cpu processes -.SH SYNOPSIS -.B top -[ -.B \-abCHIijnPqStuvz -] [ -.BI \-d count -] [ -.BI \-m io | cpu -] [ -.BI \-o field -] [ -.BI \-s time -] [ -.BI \-J jail -] [ -.BI \-U username -] [ -.I number -] -.SH DESCRIPTION -.\" This defines appropriate quote strings for nroff and troff -.ds lq \&" -.ds rq \&" -.if t .ds lq `` -.if t .ds rq '' -.\" Just in case these number registers aren't set yet... -.if \nN==0 .nr N 10 -.if \nD==0 .nr D 2 -.I Top -displays the top -.if !\nN==-1 \nN -processes on the system and periodically updates this information. -.if \nN==-1 \ -\{\ -If standard output is an intelligent terminal (see below) then -as many processes as will fit on the terminal screen are displayed -by default. Otherwise, a good number of them are shown (around 20). -.\} -Raw cpu percentage is used to rank the processes. If -.I number -is given, then the top -.I number -processes will be displayed instead of the default. -.PP -.I Top -makes a distinction between terminals that support advanced capabilities -and those that do not. This -distinction affects the choice of defaults for certain options. In the -remainder of this document, an \*(lqintelligent\*(rq terminal is one that -supports cursor addressing, clear screen, and clear to end of line. -Conversely, a \*(lqdumb\*(rq terminal is one that does not support such -features. If the output of -.I top -is redirected to a file, it acts as if it were being run on a dumb -terminal. -.SH OPTIONS -.TP -.B \-C -Toggle CPU display mode. -By default top displays the weighted CPU percentage in the WCPU column -(this is the same value that -.IR ps (1) -displays as CPU). -Each time -.B \-C -flag is passed it toggles between \*(lqraw cpu\*(rq mode -and \*(lqweighted cpu\*(rq mode, showing the \*(lqCPU\*(rq or -the \*(lqWCPU\*(rq column respectively. -.TP -.B \-S -Show system processes in the display. Normally, system processes such as -the pager and the swapper are not shown. This option makes them visible. -.TP -.B \-a -Display command names derived from the argv[] vector, rather than real -executable name. It's useful when you want to watch applications, that -puts their status information there. If the real name differs from argv[0], -it will be displayed in parenthesis. -.TP -.B \-b -Use \*(lqbatch\*(rq mode. In this mode, all input from the terminal is -ignored. Interrupt characters (such as ^C and ^\e) still have an effect. -This is the default on a dumb terminal, or when the output is not a terminal. -.TP -.B \-H -Display each thread for a multithreaded process individually. -By default a single summary line is displayed for each process. -.TP -.B \-i -Use \*(lqinteractive\*(rq mode. In this mode, any input is immediately -read for processing. See the section on \*(lqInteractive Mode\*(rq -for an explanation of -which keys perform what functions. After the command is processed, the -screen will immediately be updated, even if the command was not -understood. This mode is the default when standard output is an -intelligent terminal. -.TP -.B \-I -Do not display idle processes. -By default, top displays both active and idle processes. -.TP -.B \-j -Display the -.IR jail (8) -ID. -.TP -.B \-t -Do not display the -.I top -process. -.TP -.BI \-m display -Display either 'cpu' or 'io' statistics. Default is 'cpu'. -.TP -.B \-n -Use \*(lqnon-interactive\*(rq mode. This is identical to \*(lqbatch\*(rq -mode. -.TP -.B \-P -Display per-cpu CPU usage statistics. -.TP -.B \-q -Renice -.I top -to -20 so that it will run faster. This can be used when the system is -being very sluggish to improve the possibility of discovering the problem. -This option can only be used by root. -.TP -.B \-u -Do not take the time to map uid numbers to usernames. Normally, -.I top -will read as much of the file \*(lq/etc/passwd\*(rq as is necessary to map -all the user id numbers it encounters into login names. This option -disables all that, while possibly decreasing execution time. The uid -numbers are displayed instead of the names. -.TP -.B \-v -Write version number information to stderr then exit immediately. -No other processing takes place when this option is used. To see current -revision information while top is running, use the help command \*(lq?\*(rq. -.TP -.B \-z -Do not display the system idle process. -.TP -.BI \-d count -Show only -.I count -displays, then exit. A display is considered to be one update of the -screen. This option allows the user to select the number of displays he -wants to see before -.I top -automatically exits. For intelligent terminals, no upper limit -is set. The default is 1 for dumb terminals. -.TP -.BI \-s time -Set the delay between screen updates to -.I time -seconds. The default delay between updates is \nD seconds. -.TP -.BI \-o field -Sort the process display area on the specified field. The field name is -the name of the column as seen in the output, but in lower case. Likely -values are \*(lqcpu\*(rq, \*(lqsize\*(rq, \*(lqres\*(rq, and \*(lqtime\*(rq, -but may vary on different operating systems. Note that -not all operating systems support this option. -.TP -.BI \-J jail -Show only those processes owned by -.IR jail . -This may be either the -.B jid -or -.B name -of the jail. -Use -.B 0 -to limit to host processes. -Using this option implies the -.B \-j -flag. -.PP -.BI \-U username -Show only those processes owned by -.IR username . -This option currently only accepts usernames and will not understand -uid numbers. -.PP -Both -.I count -and -.I number -fields can be specified as \*(lqinfinite\*(rq, indicating that they can -stretch as far as possible. This is accomplished by using any proper -prefix of the keywords -\*(lqinfinity\*(rq, -\*(lqmaximum\*(rq, -or -\*(lqall\*(rq. -The default for -.I count -on an intelligent terminal is, in fact, -.BI infinity . -.PP -The environment variable -.B TOP -is examined for options before the command line is scanned. This enables -a user to set his or her own defaults. The number of processes to display -can also be specified in the environment variable -.BR TOP . -The options -.BR \-a , -.BR \-C , -.BR \-H , -.BR \-I , -.BR \-j , -.BR \-P , -.BR \-S , -.BR \-t , -.BR \-u , -and -.B \-z -are actually toggles. A second specification of any of these options -will negate the first. Thus a user who has the environment variable -.B TOP -set to \*(lq\-I\*(rq may use the command \*(lqtop \-I\*(rq to see idle processes. -.SH "INTERACTIVE MODE" -When -.I top -is running in \*(lqinteractive mode\*(rq, it reads commands from the -terminal and acts upon them accordingly. In this mode, the terminal is -put in \*(lqCBREAK\*(rq, so that a character will be -processed as soon as it is typed. Almost always, a key will be -pressed when -.I top -is between displays; that is, while it is waiting for -.I time -seconds to elapse. If this is the case, the command will be -processed and the display will be updated immediately thereafter -(reflecting any changes that the command may have specified). This -happens even if the command was incorrect. If a key is pressed while -.I top -is in the middle of updating the display, it will finish the update and -then process the command. Some commands require additional information, -and the user will be prompted accordingly. While typing this information -in, the user's erase and kill keys (as set up by the command -.IR stty ) -are recognized, and a newline terminates the input. -.PP -These commands are currently recognized (^L refers to control-L): -.TP -.B ^L -Redraw the screen. -.IP "\fBh\fP\ or\ \fB?\fP" -Display a summary of the commands (help screen). Version information -is included in this display. -.TP -.B q -Quit -.IR top. -.TP -.B d -Change the number of displays to show (prompt for new number). -Remember that the next display counts as one, so typing -.B d1 -will make -.I top -show one final display and then immediately exit. -.TP -.B m -Toggle the display between 'cpu' and 'io' modes. -.TP -.B n or # -Change the number of processes to display (prompt for new number). -.TP -.B s -Change the number of seconds to delay between displays -(prompt for new number). -.TP -.B S -Toggle the display of system processes. -.TP -.B a -Toggle the display of process titles. -.TP -.B k -Send a signal (\*(lqkill\*(rq by default) to a list of processes. This -acts similarly to the command -.IR kill (1)). -.TP -.B r -Change the priority (the \*(lqnice\*(rq) of a list of processes. -This acts similarly to the command -.IR renice (8)). -.TP -.B u -Display only processes owned by a specific username (prompt for username). -If the username specified is simply \*(lq+\*(rq, then processes belonging -to all users will be displayed. -.TP -.B o -Change the order in which the display is sorted. This command is not -available on all systems. The sort key names vary from system to system -but usually include: \*(lqcpu\*(rq, \*(lqres\*(rq, \*(lqsize\*(rq, -\*(lqtime\*(rq. The default is cpu. -.TP -.B e -Display a list of system errors (if any) generated by the last -.BR k ill -or -.BR r enice -command. -.TP -.B H -Toggle the display of threads. -.TP -.B i -(or -.BR I ) -Toggle the display of idle processes. -.TP -.B j -Toggle the display of -.IR jail (8) -ID. -.TP -.B J -Display only processes owned by a specific jail (prompt for jail). -If the jail specified is simply \*(lq+\*(rq, then processes belonging -to all jails and the host will be displayed. -This will also enable the display of JID. -.TP -.B P -Toggle the display of per-CPU statistics. -.TP -.B t -Toggle the display of the -.I top -process. -.TP -.B z -Toggle the display of the system idle process. -.SH "THE DISPLAY" -The actual display varies depending on the specific variant of Unix -that the machine is running. This description may not exactly match -what is seen by top running on this particular machine. Differences -are listed at the end of this manual entry. -.PP -The top few lines of the display show general information -about the state of the system, including -the last process id assigned to a process (on most systems), -the three load averages, -the current time, -the number of existing processes, -the number of processes in each state -(sleeping, running, starting, zombies, and stopped), -and a percentage of time spent in each of the processor states -(user, nice, system, and idle). -It also includes information about physical and virtual memory allocation. -.PP -The remainder of the screen displays information about individual -processes. This display is similar in spirit to -.IR ps (1) -but it is not exactly the same. PID is the process id, -JID, when displayed, is the -.IR jail (8) -ID corresponding to the process, -USERNAME is the name of the process's owner (if -.B \-u -is specified, a UID column will be substituted for USERNAME), -PRI is the current priority of the process, -NICE is the nice amount (in the range \-20 to 20), -SIZE is the total size of the process (text, data, and stack), -RES is the current amount of resident memory (both SIZE and RES are -given in kilobytes), -STATE is the current state (one of \*(lqSTART\*(rq, \*(lqRUN\*(rq -(shown as \*(lqCPUn\*(rq on SMP systems), \*(lqSLEEP\*(rq, \*(lqSTOP\*(rq, -\*(lqZOMB\*(rq, \*(lqWAIT\*(rq, \*(lqLOCK\*(rq or the event on which the -process waits), -C is the processor number on which the process is executing -(visible only on SMP systems), -TIME is the number of system and user cpu seconds that the process has used, -WCPU, when displayed, is the weighted cpu percentage (this is the same -value that -.IR ps (1) -displays as CPU), -CPU is the raw percentage and is the field that is sorted to determine -the order of the processes, and -COMMAND is the name of the command that the process is currently running -(if the process is swapped out, this column is marked \*(lq\*(rq). -.SH NOTES -If a process is in the \*(lqSLEEP\*(rq or \*(lqLOCK\*(rq state, -the state column will report the name of the event or lock on which the -process is waiting. -Lock names are prefixed with an asterisk \*(lq*\*(rq while sleep events -are not. -.SH AUTHOR -William LeFebvre, EECS Department, Northwestern University -.SH ENVIRONMENT -.DT -TOP user-configurable defaults for options. -.SH FILES -.DT -/dev/kmem kernel memory -.br -/dev/mem physical memory -.br -/etc/passwd used to map uid numbers to user names -.br -/boot/kernel/kernel system image -.SH BUGS -Don't shoot me, but the default for -.B \-I -has changed once again. So many people were confused by the fact that -.I top -wasn't showing them all the processes that I have decided to make the -default behavior show idle processes, just like it did in version 2. -But to appease folks who can't stand that behavior, I have added the -ability to set \*(lqdefault\*(rq options in the environment variable -.B TOP -(see the OPTIONS section). Those who want the behavior that version -3.0 had need only set the environment variable -.B TOP -to \*(lq\-I\*(rq. -.PP -The command name for swapped processes should be tracked down, but this -would make the program run slower. -.PP -As with -.IR ps (1), -things can change while -.I top -is collecting information for an update. The picture it gives is only a -close approximation to reality. -.SH "SEE ALSO" -kill(1), -ps(1), -stty(1), -mem(4), -renice(8) Index: contrib/top/username.h =================================================================== --- contrib/top/username.h +++ contrib/top/username.h @@ -1,23 +1,42 @@ /* - * Top users/processes display for Unix - * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * Copyright (c) 2016, Randy Westlund - * - * $FreeBSD$ + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef USERNAME_H -#define USERNAME_H -int enter_user(int uid, char *name, int wecare); -int get_user(int uid); -void init_hash(void); -char *username(int uid); -int userid(char *username); +/* interface for username.c */ -#endif /* USERNAME_H */ +#ifndef _USERNAME_H_ +#define _USERNAME_H_ + +void init_username(); +char *username(int uid); +int userid(char *username); + +#endif /* _USERNAME_H_ */ Index: contrib/top/username.c =================================================================== --- contrib/top/username.c +++ contrib/top/username.c @@ -1,195 +1,150 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Top users/processes display for Unix * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * - * $FreeBSD$ */ /* * Username translation code for top. * - * These routines handle uid to username mapping. - * They use a hashing table scheme to reduce reading overhead. - * For the time being, these are very straightforward hashing routines. - * Maybe someday I'll put in something better. But with the advent of - * "random access" password files, it might not be worth the effort. - * - * Changes to these have been provided by John Gilmore (gnu@toad.com). + * These routines handle uid to username mapping. They use a hash table to + * reduce reading overhead. Entries are refreshed every EXPIRETIME seconds. * - * The hash has been simplified in this release, to avoid the - * table overflow problems of previous releases. If the value - * at the initial hash location is not right, it is replaced - * by the right value. Collisions will cause us to call getpw* - * but hey, this is a cache, not the Library of Congress. - * This makes the table size independent of the passwd file size. + * The old ad-hoc hash functions have been replaced with something a little + * more formal and (hopefully) more robust (found in hash.c) */ -#include -#include +#include "os.h" #include -#include -#include -#include -#include "top.local.h" +#include "top.h" #include "utils.h" -#include "username.h" +#include "hash.h" -struct hash_el { - int uid; - char name[MAXLOGNAME]; -}; +#define EXPIRETIME (60 * 5) -#define is_empty_hash(x) (hash_table[x].name[0] == 0) +/* we need some sort of idea how long usernames can be */ +#ifndef MAXLOGNAME +#ifdef _POSIX_LOGIN_NAME_MAX +#define MAXLOGNAME _POSIX_LOGIN_NAME_MAX +#else +#define MAXLOGNAME 9 +#endif +#endif -/* simple minded hashing function */ -/* Uid "nobody" is -2 results in hashit(-2) = -2 which is out of bounds for - the hash_table. Applied abs() function to fix. 2/16/96 tpugh -*/ -#define hashit(i) (abs(i) % Table_size) - -/* K&R requires that statically declared tables be initialized to zero. */ -/* We depend on that for hash_table and YOUR compiler had BETTER do it! */ -struct hash_el hash_table[Table_size]; +struct hash_data { + int uid; + char name[MAXLOGNAME]; /* big enough? */ + time_t expire; +}; + +hash_table *userhash; void -init_hash() +init_username() { - /* - * There used to be some steps we had to take to initialize things. - * We don't need to do that anymore, but we will leave this stub in - * just in case future changes require initialization steps. - */ + userhash = hash_create(211); } -char *username(uid) - -int uid; +char * +username(int uid) { - register int hashindex; + struct hash_data *data; + struct passwd *pw; + time_t now; - hashindex = hashit(uid); - if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid)) - { - /* not here or not right -- get it out of passwd */ - hashindex = get_user(uid); - } - return(hash_table[hashindex].name); -} - -int userid(username) - -char *username; - -{ - struct passwd *pwd; + /* what time is it? */ + now = time(NULL); - /* Eventually we want this to enter everything in the hash table, - but for now we just do it simply and remember just the result. - */ + /* get whatever is in the cache */ + data = hash_lookup_uint(userhash, (unsigned int)uid); - if ((pwd = getpwnam(username)) == NULL) + /* if we had a cache miss, then create space for a new entry */ + if (data == NULL) { - return(-1); - } - - /* enter the result in the hash table */ - enter_user(pwd->pw_uid, username, 1); - - /* return our result */ - return(pwd->pw_uid); -} - -int enter_user(uid, name, wecare) - -int uid; -char *name; -int wecare; /* 1 = enter it always, 0 = nice to have */ + /* make space */ + data = (struct hash_data *)malloc(sizeof(struct hash_data)); -{ - register int hashindex; - -#ifdef DEBUG - fprintf(stderr, "enter_hash(%d, %s, %d)\n", uid, name, wecare); -#endif + /* fill in some data, including an already expired time */ + data->uid = uid; + data->expire = (time_t)0; - hashindex = hashit(uid); + /* add it to the hash: the rest gets filled in later */ + hash_add_uint(userhash, uid, data); + } - if (!is_empty_hash(hashindex)) + /* Now data points to the correct hash entry for "uid". If this is + a new entry, then expire is 0 and the next test will be true. */ + if (data->expire <= now) { - if (!wecare) - return 0; /* Don't clobber a slot for trash */ - if (hash_table[hashindex].uid == uid) - return(hashindex); /* Fortuitous find */ + if ((pw = getpwuid(uid)) != NULL) + { + strncpy(data->name, pw->pw_name, MAXLOGNAME-1); + data->expire = now + EXPIRETIME; + dprintf("username: updating %d with %s, expires %d\n", + data->uid, data->name, data->expire); + } + else + { + /* username doesnt exist ... so invent one */ + snprintf(data->name, sizeof(data->name), "%d", uid); + data->expire = now + EXPIRETIME; + dprintf("username: updating %d with %s, expires %d\n", + data->uid, data->name, data->expire); + } } - /* empty or wrong slot -- fill it with new value */ - hash_table[hashindex].uid = uid; - (void) strncpy(hash_table[hashindex].name, name, MAXLOGNAME - 1); - return(hashindex); + /* return what we have */ + return data->name; } -/* - * Get a userid->name mapping from the system. - * If the passwd database is hashed (#define RANDOM_PW), we - * just handle this uid. Otherwise we scan the passwd file - * and cache any entries we pass over while looking. - */ - -int get_user(uid) - -int uid; +int +userid(char *username) { struct passwd *pwd; -#ifdef RANDOM_PW - /* no performance penalty for using getpwuid makes it easy */ - if ((pwd = getpwuid(uid)) != NULL) + if ((pwd = getpwnam(username)) == NULL) { - return(enter_user(pwd->pw_uid, pwd->pw_name, 1)); + return(-1); } -#else - int from_start = 0; - - /* - * If we just called getpwuid each time, things would be very slow - * since that just iterates through the passwd file each time. So, - * we walk through the file instead (using getpwent) and cache each - * entry as we go. Once the right record is found, we cache it and - * return immediately. The next time we come in, getpwent will get - * the next record. In theory, we never have to read the passwd file - * a second time (because we cache everything we read). But in - * practice, the cache may not be large enough, so if we don't find - * it the first time we have to scan the file a second time. This - * is not very efficient, but it will do for now. - */ - - while (from_start++ < 2) - { - while ((pwd = getpwent()) != NULL) - { - if (pwd->pw_uid == uid) - { - return(enter_user(pwd->pw_uid, pwd->pw_name, 1)); - } - (void) enter_user(pwd->pw_uid, pwd->pw_name, 0); - } - /* try again */ - setpwent(); - } -#endif - /* if we can't find the name at all, then use the uid as the name */ - return(enter_user(uid, itoa7(uid), 1)); + /* return our result */ + return(pwd->pw_uid); } + Index: contrib/top/utils.h =================================================================== --- contrib/top/utils.h +++ contrib/top/utils.h @@ -1,26 +1,78 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Top users/processes display for Unix - * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University */ /* prototypes for functions found in utils.c */ -int atoiwi(); -char *itoa(); -char *itoa7(); -int digits(); -char *strecpy(); -char **argparse(); -long percentages(); -char *errmsg(); -char *format_time(); -char *format_k(); -char *format_k2(unsigned long long); -int string_index(char *string, char **array); +#ifndef _UTILS_H +#define _UTILS_H + +int atoiwi(char *); +char *itoa(int); +char *itoa_w(int, int); +char *itoa7(int); +int digits(int); +char *printable(char *); +char *strcpyend(char *, char *); +char *homogenize(char *); +int string_index(char *, char **); +char **argparse(char *, int *); +long percentages(int, int *, long *, long *, long *); +char *errmsg(int); +char *format_percent(double); +char *format_time(long); +char *format_k(long); +char *string_list(char **); +void time_get(struct timeval *); +void time_mark(struct timeval *); +unsigned int time_elapsed(); +unsigned int diff_per_second(unsigned int, unsigned int); +void debug_set(int); +#ifdef DEBUG +#define dprintf xdprintf +void xdprintf(char *fmt, ...); +#else +#ifdef HAVE_C99_VARIADIC_MACROS +#define dprintf(...) +#else +#ifdef HAVE_GNU_VARIADIC_MACROS +#define dprintf(x...) +#else +#define dprintf if (0) +#endif +#endif +#endif +#endif Index: contrib/top/utils.c =================================================================== --- contrib/top/utils.c +++ contrib/top/utils.c @@ -1,26 +1,74 @@ /* - * Top users/processes display for Unix - * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* + * Top users/processes display for Unix + * Version 3 + */ + +/* * This file contains various handy utilities used by top. */ -#include "top.h" #include "os.h" +#include +#ifdef HAVE_STDARG_H +#include +#else +#undef DEBUG +#endif +#include "top.h" +#include "utils.h" + +static int +alldigits(char *s) + +{ + int ch; -int atoiwi(str) + while ((ch = *s++) != '\0') + { + if (!isdigit(ch)) + { + return 0; + } + } + return 1; +} -char *str; +int +atoiwi(char *str) { register int len; @@ -34,13 +82,13 @@ { return(Infinity); } - else if (str[0] == '-') + else if (alldigits(str)) { - return(Invalid); + return(atoi(str)); } else { - return(atoi(str)); + return(Invalid); } } return(0); @@ -58,11 +106,11 @@ * ever convert will be 2^32-1, which is 10 * digits. */ +/* If ints are ever wider than this, the following code will buffer overflow */ _Static_assert(sizeof(int) <= 4, "buffer too small for this sized int"); -char *itoa(val) - -register int val; +char * +itoa(int val) { register char *ptr; @@ -91,18 +139,22 @@ * a front end to a more general routine for efficiency. */ -char *itoa7(val) - -register int val; +char * +itoa_w(int val, int w) { - register char *ptr; + char *ptr; + char *eptr; static char buffer[16]; /* result is built here */ /* 16 is sufficient since the largest number we will ever convert will be 2^32-1, which is 10 digits. */ - ptr = buffer + sizeof(buffer); + if (w > 15) + { + w = 15; + } + eptr = ptr = buffer + sizeof(buffer); *--ptr = '\0'; if (val == 0) { @@ -113,25 +165,36 @@ *--ptr = (val % 10) + '0'; val /= 10; } - while (ptr > buffer + sizeof(buffer) - 7) + while (ptr >= eptr - w) { *--ptr = ' '; } return(ptr); } +char * +itoa7(int val) + +{ + return itoa_w(val, 7); +} + /* * digits(val) - return number of decimal digits in val. Only works for - * positive numbers. If val <= 0 then digits(val) == 0. + * positive numbers. If val < 0 then digits(val) == 0, but + * digits(0) == 1. */ -int digits(val) - -int val; +int +digits(int val) { register int cnt = 0; + if (val == 0) + { + return 1; + } while (val > 0) { cnt++; @@ -141,14 +204,38 @@ } /* - * strecpy(to, from) - copy string "from" into "to" and return a pointer - * to the END of the string "to". + * printable(char *str) - make the string pointed to by "str" into one that is + * printable (i.e.: all ascii), by converting all non-printable + * characters into '?'. Replacements are done in place and a pointer + * to the original buffer is returned. */ -char *strecpy(to, from) +char * +printable(char *str) + +{ + register char *ptr; + register int ch; + + ptr = str; + while ((ch = *ptr) != '\0') + { + if (!isprint(ch)) + { + *ptr = '?'; + } + ptr++; + } + return(str); +} -register char *to; -register char *from; +/* + * strcpyend(to, from) - copy string "from" into "to" and return a pointer + * to the END of the string "to". + */ + +char * +strcpyend(char *to, char *from) { while ((*to++ = *from++) != '\0'); @@ -156,13 +243,40 @@ } /* - * string_index(string, array) - find string in array and return index + * char * + * homogenize(char *str) + * + * Remove unwanted characters from "str" and make everything lower case. + * Newly allocated string is returned: the original is not altered. */ -int string_index(string, array) +char *homogenize(char *str) + +{ + char *ans; + char *fr; + char *to; + int ch; + + to = fr = ans = strdup(str); + while ((ch = *fr++) != '\0') + { + if (isalnum(ch)) + { + *to++ = tolower(ch); + } + } + + *to = '\0'; + return ans; +} + +/* + * string_index(string, array) - find string in array and return index + */ -char *string; -char **array; +int +string_index(char *string, char **array) { register int i = 0; @@ -180,16 +294,54 @@ } /* + * char *string_list(char **strings) + * + * Create a comma-separated list of the strings in the NULL-terminated + * "strings". Returned string is malloc-ed and should be freed when the + * caller is done. Note that this is not an efficient function. + */ + +char *string_list(char **strings) + +{ + int cnt = 0; + char **pp; + char *p; + char *result = NULL; + char *resp = NULL; + + pp = strings; + while ((p = *pp++) != NULL) + { + cnt += strlen(p) + 2; + } + + if (cnt > 0) + { + resp = result = (char *)malloc(cnt); + pp = strings; + while ((p = *pp++) != NULL) + { + resp = strcpyend(resp, p); + if (*pp != NULL) + { + resp = strcpyend(resp, ", "); + } + } + } + + return result; +} + +/* * argparse(line, cntp) - parse arguments in string "line", separating them * out into an argv-like array, and setting *cntp to the number of * arguments encountered. This is a simple parser that doesn't understand * squat about quotes. */ -char **argparse(line, cntp) - -char *line; -int *cntp; +char ** +argparse(char *line, int *cntp) { register char *from; @@ -271,13 +423,8 @@ * useful on BSD mchines for calculating cpu state percentages. */ -long percentages(cnt, out, new, old, diffs) - -int cnt; -int *out; -register long *new; -register long *old; -long *diffs; +long +percentages(int cnt, int *out, long *new, long *old, long *diffs) { register int i; @@ -311,13 +458,9 @@ /* calculate percentages based on overall change, rounding up */ half_total = total_change / 2l; - - /* Do not divide by 0. Causes Floating point exception */ - if(total_change) { - for (i = 0; i < cnt; i++) - { - *out++ = (int)((*diffs++ * 1000 + half_total) / total_change); - } + for (i = 0; i < cnt; i++) + { + *out++ = (int)((*diffs++ * 1000 + half_total) / total_change); } /* return the total in case the caller wants to use it */ @@ -338,17 +481,15 @@ /* externs referenced by errmsg */ #ifndef HAVE_STRERROR -#ifndef SYS_ERRLIST_DECLARED -#define SYS_ERRLIST_DECLARED +#if !HAVE_DECL_SYS_ERRLIST extern char *sys_errlist[]; #endif extern int sys_nerr; #endif -char *errmsg(errnum) - -int errnum; +char * +errmsg(int errnum) { #ifdef HAVE_STRERROR @@ -360,12 +501,43 @@ #else if (errnum > 0 && errnum < sys_nerr) { - return((char *)sys_errlist[errnum]); + return((char *)(sys_errlist[errnum])); } #endif return("No error"); } +/* format_percent(v) - format a double as a percentage in a manner that + * does not exceed 5 characters (excluding any trailing + * percent sign). Since it is possible for the value + * to exceed 100%, we format such values with no fractional + * component to fit within the 5 characters. + */ + +char * +format_percent(double v) + +{ + static char result[10]; + + /* enumerate the possibilities */ + if (v < 0 || v >= 100000.) + { + /* we dont want to try extreme values */ + strcpy(result, " ???"); + } + else if (v > 99.99) + { + sprintf(result, "%5.0f", v); + } + else + { + sprintf(result, "%5.2f", v); + } + + return result; +} + /* format_time(seconds) - format number of seconds into a suitable * display that will fit within 6 characters. Note that this * routine builds its string in a static area. If it needs @@ -382,14 +554,10 @@ exceed 9999.9, we use "???". */ -char *format_time(seconds) - -long seconds; +char * +format_time(long seconds) { - register int value; - register int digit; - register char *ptr; static char result[10]; /* sanity protection */ @@ -412,8 +580,7 @@ { /* standard method produces MMM:SS */ /* we avoid printf as must as possible to make this quick */ - sprintf(result, "%3ld:%02ld", - (long)(seconds / 60), (long)(seconds % 60)); + sprintf(result, "%3ld:%02ld", seconds / 60l, seconds % 60l); } return(result); } @@ -443,18 +610,16 @@ #define NUM_STRINGS 8 -char *format_k(amt) - -int amt; +char * +format_k(long amt) { static char retarray[NUM_STRINGS][16]; static int index = 0; - register char *p; register char *ret; register char tag = 'K'; - p = ret = retarray[index]; + ret = retarray[index]; index = (index + 1) % NUM_STRINGS; if (amt >= 10000) @@ -468,41 +633,117 @@ } } - p = strecpy(p, itoa(amt)); - *p++ = tag; - *p = '\0'; + snprintf(ret, sizeof(retarray[index])-1, "%ld%c", amt, tag); return(ret); } -char *format_k2(amt) +/* + * Time keeping functions. + */ + +static struct timeval lasttime = { 0, 0 }; +static unsigned int elapsed_msecs = 0; -unsigned long long amt; +void +time_get(struct timeval *tv) { - static char retarray[NUM_STRINGS][16]; - static int index = 0; - register char *p; - register char *ret; - register char tag = 'K'; + /* get the current time */ +#ifdef HAVE_GETTIMEOFDAY + gettimeofday(tv, NULL); +#else + tv->tv_sec = (long)time(NULL); + tv->tv_usec = 0; +#endif +} - p = ret = retarray[index]; - index = (index + 1) % NUM_STRINGS; +void +time_mark(struct timeval *tv) + +{ + struct timeval thistime; + struct timeval timediff; - if (amt >= 100000) + /* if the caller didnt provide one then use our own */ + if (tv == NULL) { - amt = (amt + 512) / 1024; - tag = 'M'; - if (amt >= 100000) - { - amt = (amt + 512) / 1024; - tag = 'G'; - } + tv = &thistime; } - p = strecpy(p, itoa((int)amt)); - *p++ = tag; - *p = '\0'; + /* get the current time */ +#ifdef HAVE_GETTIMEOFDAY + gettimeofday(tv, NULL); +#else + tv->tv_sec = (long)time(NULL); + tv->tv_usec = 0; +#endif - return(ret); + /* calculate the difference */ + timediff.tv_sec = tv->tv_sec - lasttime.tv_sec; + timediff.tv_usec = tv->tv_usec - lasttime.tv_usec; + if (timediff.tv_usec < 0) { + timediff.tv_sec--; + timediff.tv_usec += 1000000; + } + + /* convert to milliseconds */ + elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000; + if (elapsed_msecs == 0) + { + elapsed_msecs = 1; + } + + /* save for next time */ + lasttime = *tv; +} + +unsigned int +time_elapsed() + +{ + return elapsed_msecs; } + +unsigned int +diff_per_second(unsigned int x, unsigned int y) + +{ + return (y > x ? UINT_MAX - y + x + 1 : x - y) * 1000 / elapsed_msecs; +} + +static int debug_on = 0; + +#ifdef DEBUG +FILE *debugfile; +#endif + +void +debug_set(int i) + +{ + debug_on = i; +#ifdef DEBUG + debugfile = fopen("/tmp/top.debug", "w"); +#endif +} + +#ifdef DEBUG +void +xdprintf(char *fmt, ...) + +{ + va_list argp; + + va_start(argp, fmt); + + if (debug_on) + { + vfprintf(debugfile, fmt, argp); + fflush(debugfile); + } + + va_end(argp); +} +#endif + Index: contrib/top/version.h =================================================================== --- contrib/top/version.h +++ contrib/top/version.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top users/processes display for Unix + */ + +char *version_string(); Index: contrib/top/version.c =================================================================== --- contrib/top/version.c +++ contrib/top/version.c @@ -1,28 +1,46 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Top users/processes display for Unix * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University */ -#include -#include - +#include "config.h" #include "top.h" -#include "patchlevel.h" - -static char version[16]; -char *version_string() +char * +version_string() { - sprintf(version, "%d.%d", VERSION, PATCHLEVEL); -#ifdef BETA - strcat(version, BETA); -#endif - return(version); + return(PACKAGE_VERSION); } Index: usr.bin/top/Makefile =================================================================== --- usr.bin/top/Makefile +++ usr.bin/top/Makefile @@ -4,11 +4,11 @@ .PATH: ${TOPDIR} PROG= top -SRCS= commands.c display.c machine.c screen.c top.c \ +SRCS= color.c commands.c display.c hash.c m_freebsd.c screen.c top.c \ username.c utils.c version.c -SRCS+= sigdesc.h top.local.h -CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER -CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I. +SRCS+= sigdesc.h +CFLAGS+= -DUNAME_HARDWARE=\"${MACHINE_CPUARCH}\" +CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I. -O0 WARNS?= 0 @@ -17,6 +17,8 @@ # large as the number of lines in /etc/passwd. The default number # is 20011; use /etc/make.conf to override this. # +# NOTE: default in code is actualy 8191 unless I change it here; 20011 seems +# excesssive .if defined(TOP_TABLE_SIZE) CFLAGS+= -D"Table_size=${TOP_TABLE_SIZE}" .endif @@ -30,20 +32,8 @@ sigdesc.h: ${SIGCONV_AWK} ${SIGNAL_H} awk -f ${SIGCONV_AWK} < ${SIGNAL_H} > ${.TARGET} -CLEANFILES+= top.local.h top.x -.SUFFIXES: .xs .x .hs .h -.xs.x .hs.h: - @${ECHO} Making ${.TARGET} from ${.IMPSRC} - @sed -e's,%LoadMax%,5.0,g' \ - -e's,%TableSize%,20011,g' \ - -e's,%NominalTopn%,18,g' \ - -e's,%topn%,-1,g' \ - -e's,%delay%,2,g' \ - -e's,%random%,1,g' \ - ${.IMPSRC} > ${.TARGET} - -CLEANFILES+= top.1 -top.1: top.x top.local.1 - cat ${.ALLSRC} > ${.TARGET} +.SUFFIXES: .x .h + +MAN= ${.CURDIR}/../../contrib/top/top.1 .include Index: usr.bin/top/machine.c =================================================================== --- usr.bin/top/machine.c +++ usr.bin/top/machine.c @@ -1,1602 +0,0 @@ -/* - * top - a top users display for Unix - * - * SYNOPSIS: For FreeBSD-2.x and later - * - * DESCRIPTION: - * Originally written for BSD4.4 system by Christos Zoulas. - * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider - * Order support hacked in from top-3.5beta6/machine/m_aix41.c - * by Monte Mitzelfelt (for latest top see http://www.groupsys.com/topinfo/) - * - * This is the machine-dependent module for FreeBSD 2.2 - * Works for: - * FreeBSD 2.2.x, 3.x, 4.x, and probably FreeBSD 2.1.x - * - * LIBS: -lkvm - * - * AUTHOR: Christos Zoulas - * Steven Wallace - * Wolfram Schneider - * Thomas Moestl - * - * $FreeBSD$ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "top.h" -#include "machine.h" -#include "screen.h" -#include "utils.h" -#include "layout.h" - -#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var)) -#define SMPUNAMELEN 13 -#define UPUNAMELEN 15 - -extern struct process_select ps; -extern char* printable(char *); -static int smpmode; -enum displaymodes displaymode; -#ifdef TOP_USERNAME_LEN -static int namelength = TOP_USERNAME_LEN; -#else -static int namelength = 8; -#endif -/* TOP_JID_LEN based on max of 999999 */ -#define TOP_JID_LEN 7 -static int jidlength; -static int cmdlengthdelta; - -/* Prototypes for top internals */ -void quit(int); - -/* get_process_info passes back a handle. This is what it looks like: */ - -struct handle { - struct kinfo_proc **next_proc; /* points to next valid proc pointer */ - int remaining; /* number of pointers remaining */ -}; - -/* declarations for load_avg */ -#include "loadavg.h" - -/* define what weighted cpu is. */ -#define weighted_cpu(pct, pp) ((pp)->ki_swtime == 0 ? 0.0 : \ - ((pct) / (1.0 - exp((pp)->ki_swtime * logcpu)))) - -/* what we consider to be process size: */ -#define PROCSIZE(pp) ((pp)->ki_size / 1024) - -#define RU(pp) (&(pp)->ki_rusage) -#define RUTOT(pp) \ - (RU(pp)->ru_inblock + RU(pp)->ru_oublock + RU(pp)->ru_majflt) - -#define PCTCPU(pp) (pcpu[pp - pbase]) - -/* definitions for indices in the nlist array */ - -/* - * These definitions control the format of the per-process area - */ - -static char io_header[] = - " PID%*s %-*.*s VCSW IVCSW READ WRITE FAULT TOTAL PERCENT COMMAND"; - -#define io_Proc_format \ - "%5d%*s %-*.*s %6ld %6ld %6ld %6ld %6ld %6ld %6.2f%% %.*s" - -static char smp_header_thr[] = - " PID%*s %-*.*s THR PRI NICE SIZE RES STATE C TIME %7s COMMAND"; -static char smp_header[] = - " PID%*s %-*.*s " "PRI NICE SIZE RES STATE C TIME %7s COMMAND"; - -#define smp_Proc_format \ - "%5d%*s %-*.*s %s%3d %4s%7s %6s %-6.6s %2d%7s %6.2f%% %.*s" - -static char up_header_thr[] = - " PID%*s %-*.*s THR PRI NICE SIZE RES STATE TIME %7s COMMAND"; -static char up_header[] = - " PID%*s %-*.*s " "PRI NICE SIZE RES STATE TIME %7s COMMAND"; - -#define up_Proc_format \ - "%5d%*s %-*.*s %s%3d %4s%7s %6s %-6.6s%.0d%7s %6.2f%% %.*s" - - -/* process state names for the "STATE" column of the display */ -/* the extra nulls in the string "run" are for adding a slash and - the processor number when needed */ - -char *state_abbrev[] = { - "", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK" -}; - - -static kvm_t *kd; - -/* values that we stash away in _init and use in later routines */ - -static double logcpu; - -/* these are retrieved from the kernel in _init */ - -static load_avg ccpu; - -/* these are used in the get_ functions */ - -static int lastpid; - -/* these are for calculating cpu state percentages */ - -static long cp_time[CPUSTATES]; -static long cp_old[CPUSTATES]; -static long cp_diff[CPUSTATES]; - -/* these are for detailing the process states */ - -int process_states[8]; -char *procstatenames[] = { - "", " starting, ", " running, ", " sleeping, ", " stopped, ", - " zombie, ", " waiting, ", " lock, ", - NULL -}; - -/* these are for detailing the cpu states */ - -int cpu_states[CPUSTATES]; -char *cpustatenames[] = { - "user", "nice", "system", "interrupt", "idle", NULL -}; - -/* these are for detailing the memory statistics */ - -int memory_stats[7]; -char *memorynames[] = { - "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", - "K Free", NULL -}; - -int arc_stats[7]; -char *arcnames[] = { - "K Total, ", "K MFU, ", "K MRU, ", "K Anon, ", "K Header, ", "K Other", - NULL -}; - -int swap_stats[7]; -char *swapnames[] = { - "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", - NULL -}; - - -/* these are for keeping track of the proc array */ - -static int nproc; -static int onproc = -1; -static int pref_len; -static struct kinfo_proc *pbase; -static struct kinfo_proc **pref; -static struct kinfo_proc *previous_procs; -static struct kinfo_proc **previous_pref; -static int previous_proc_count = 0; -static int previous_proc_count_max = 0; -static int previous_thread; - -/* data used for recalculating pctcpu */ -static double *pcpu; -static struct timespec proc_uptime; -static struct timeval proc_wall_time; -static struct timeval previous_wall_time; -static uint64_t previous_interval = 0; - -/* total number of io operations */ -static long total_inblock; -static long total_oublock; -static long total_majflt; - -/* these are for getting the memory statistics */ - -static int arc_enabled; -static int pageshift; /* log base 2 of the pagesize */ - -/* define pagetok in terms of pageshift */ - -#define pagetok(size) ((size) << pageshift) - -/* useful externals */ -long percentages(); - -#ifdef ORDER -/* - * Sorting orders. The first element is the default. - */ -char *ordernames[] = { - "cpu", "size", "res", "time", "pri", "threads", - "total", "read", "write", "fault", "vcsw", "ivcsw", - "jid", "pid", NULL -}; -#endif - -/* Per-cpu time states */ -static int maxcpu; -static int maxid; -static int ncpus; -static u_long cpumask; -static long *times; -static long *pcpu_cp_time; -static long *pcpu_cp_old; -static long *pcpu_cp_diff; -static int *pcpu_cpu_states; - -static int compare_jid(const void *a, const void *b); -static int compare_pid(const void *a, const void *b); -static int compare_tid(const void *a, const void *b); -static const char *format_nice(const struct kinfo_proc *pp); -static void getsysctl(const char *name, void *ptr, size_t len); -static int swapmode(int *retavail, int *retfree); -static void update_layout(void); - -void -toggle_pcpustats(void) -{ - - if (ncpus == 1) - return; - update_layout(); -} - -/* Adjust display based on ncpus and the ARC state. */ -static void -update_layout(void) -{ - - y_mem = 3; - y_arc = 4; - y_swap = 4 + arc_enabled; - y_idlecursor = 5 + arc_enabled; - y_message = 5 + arc_enabled; - y_header = 6 + arc_enabled; - y_procs = 7 + arc_enabled; - Header_lines = 7 + arc_enabled; - - if (pcpu_stats) { - y_mem += ncpus - 1; - y_arc += ncpus - 1; - y_swap += ncpus - 1; - y_idlecursor += ncpus - 1; - y_message += ncpus - 1; - y_header += ncpus - 1; - y_procs += ncpus - 1; - Header_lines += ncpus - 1; - } -} - -int -machine_init(struct statics *statics, char do_unames) -{ - int i, j, empty, pagesize; - uint64_t arc_size; - size_t size; - struct passwd *pw; - - size = sizeof(smpmode); - if ((sysctlbyname("machdep.smp_active", &smpmode, &size, - NULL, 0) != 0 && - sysctlbyname("kern.smp.active", &smpmode, &size, - NULL, 0) != 0) || - size != sizeof(smpmode)) - smpmode = 0; - - size = sizeof(arc_size); - if (sysctlbyname("kstat.zfs.misc.arcstats.size", &arc_size, &size, - NULL, 0) == 0 && arc_size != 0) - arc_enabled = 1; - - if (do_unames) { - while ((pw = getpwent()) != NULL) { - if (strlen(pw->pw_name) > namelength) - namelength = strlen(pw->pw_name); - } - } - if (smpmode && namelength > SMPUNAMELEN) - namelength = SMPUNAMELEN; - else if (namelength > UPUNAMELEN) - namelength = UPUNAMELEN; - - kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); - if (kd == NULL) - return (-1); - - GETSYSCTL("kern.ccpu", ccpu); - - /* this is used in calculating WCPU -- calculate it ahead of time */ - logcpu = log(loaddouble(ccpu)); - - pbase = NULL; - pref = NULL; - pcpu = NULL; - nproc = 0; - onproc = -1; - - /* get the page size and calculate pageshift from it */ - pagesize = getpagesize(); - pageshift = 0; - while (pagesize > 1) { - pageshift++; - pagesize >>= 1; - } - - /* we only need the amount of log(2)1024 for our conversion */ - pageshift -= LOG1024; - - /* fill in the statics information */ - statics->procstate_names = procstatenames; - statics->cpustate_names = cpustatenames; - statics->memory_names = memorynames; - if (arc_enabled) - statics->arc_names = arcnames; - else - statics->arc_names = NULL; - statics->swap_names = swapnames; -#ifdef ORDER - statics->order_names = ordernames; -#endif - - /* Allocate state for per-CPU stats. */ - cpumask = 0; - ncpus = 0; - GETSYSCTL("kern.smp.maxcpus", maxcpu); - size = sizeof(long) * maxcpu * CPUSTATES; - times = malloc(size); - if (times == NULL) - err(1, "malloc %zu bytes", size); - if (sysctlbyname("kern.cp_times", times, &size, NULL, 0) == -1) - err(1, "sysctlbyname kern.cp_times"); - pcpu_cp_time = calloc(1, size); - maxid = (size / CPUSTATES / sizeof(long)) - 1; - for (i = 0; i <= maxid; i++) { - empty = 1; - for (j = 0; empty && j < CPUSTATES; j++) { - if (times[i * CPUSTATES + j] != 0) - empty = 0; - } - if (!empty) { - cpumask |= (1ul << i); - ncpus++; - } - } - size = sizeof(long) * ncpus * CPUSTATES; - pcpu_cp_old = calloc(1, size); - pcpu_cp_diff = calloc(1, size); - pcpu_cpu_states = calloc(1, size); - statics->ncpus = ncpus; - - update_layout(); - - /* all done! */ - return (0); -} - -char * -format_header(char *uname_field) -{ - static char Header[128]; - const char *prehead; - - if (ps.jail) - jidlength = TOP_JID_LEN + 1; /* +1 for extra left space. */ - else - jidlength = 0; - - switch (displaymode) { - case DISP_CPU: - /* - * The logic of picking the right header format seems reverse - * here because we only want to display a THR column when - * "thread mode" is off (and threads are not listed as - * separate lines). - */ - prehead = smpmode ? - (ps.thread ? smp_header : smp_header_thr) : - (ps.thread ? up_header : up_header_thr); - snprintf(Header, sizeof(Header), prehead, - jidlength, ps.jail ? " JID" : "", - namelength, namelength, uname_field, - ps.wcpu ? "WCPU" : "CPU"); - break; - case DISP_IO: - prehead = io_header; - snprintf(Header, sizeof(Header), prehead, - jidlength, ps.jail ? " JID" : "", - namelength, namelength, uname_field); - break; - } - cmdlengthdelta = strlen(Header) - 7; - return (Header); -} - -static int swappgsin = -1; -static int swappgsout = -1; -extern struct timeval timeout; - - -void -get_system_info(struct system_info *si) -{ - long total; - struct loadavg sysload; - int mib[2]; - struct timeval boottime; - uint64_t arc_stat, arc_stat2; - int i, j; - size_t size; - - /* get the CPU stats */ - size = (maxid + 1) * CPUSTATES * sizeof(long); - if (sysctlbyname("kern.cp_times", pcpu_cp_time, &size, NULL, 0) == -1) - err(1, "sysctlbyname kern.cp_times"); - GETSYSCTL("kern.cp_time", cp_time); - GETSYSCTL("vm.loadavg", sysload); - GETSYSCTL("kern.lastpid", lastpid); - - /* convert load averages to doubles */ - for (i = 0; i < 3; i++) - si->load_avg[i] = (double)sysload.ldavg[i] / sysload.fscale; - - /* convert cp_time counts to percentages */ - for (i = j = 0; i <= maxid; i++) { - if ((cpumask & (1ul << i)) == 0) - continue; - percentages(CPUSTATES, &pcpu_cpu_states[j * CPUSTATES], - &pcpu_cp_time[j * CPUSTATES], - &pcpu_cp_old[j * CPUSTATES], - &pcpu_cp_diff[j * CPUSTATES]); - j++; - } - percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); - - /* sum memory & swap statistics */ - { - static unsigned int swap_delay = 0; - static int swapavail = 0; - static int swapfree = 0; - static long bufspace = 0; - static int nspgsin, nspgsout; - - GETSYSCTL("vfs.bufspace", bufspace); - GETSYSCTL("vm.stats.vm.v_active_count", memory_stats[0]); - GETSYSCTL("vm.stats.vm.v_inactive_count", memory_stats[1]); - GETSYSCTL("vm.stats.vm.v_wire_count", memory_stats[2]); - GETSYSCTL("vm.stats.vm.v_cache_count", memory_stats[3]); - GETSYSCTL("vm.stats.vm.v_free_count", memory_stats[5]); - GETSYSCTL("vm.stats.vm.v_swappgsin", nspgsin); - GETSYSCTL("vm.stats.vm.v_swappgsout", nspgsout); - /* convert memory stats to Kbytes */ - memory_stats[0] = pagetok(memory_stats[0]); - memory_stats[1] = pagetok(memory_stats[1]); - memory_stats[2] = pagetok(memory_stats[2]); - memory_stats[3] = pagetok(memory_stats[3]); - memory_stats[4] = bufspace / 1024; - memory_stats[5] = pagetok(memory_stats[5]); - memory_stats[6] = -1; - - /* first interval */ - if (swappgsin < 0) { - swap_stats[4] = 0; - swap_stats[5] = 0; - } - - /* compute differences between old and new swap statistic */ - else { - swap_stats[4] = pagetok(((nspgsin - swappgsin))); - swap_stats[5] = pagetok(((nspgsout - swappgsout))); - } - - swappgsin = nspgsin; - swappgsout = nspgsout; - - /* call CPU heavy swapmode() only for changes */ - if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) { - swap_stats[3] = swapmode(&swapavail, &swapfree); - swap_stats[0] = swapavail; - swap_stats[1] = swapavail - swapfree; - swap_stats[2] = swapfree; - } - swap_delay = 1; - swap_stats[6] = -1; - } - - if (arc_enabled) { - GETSYSCTL("kstat.zfs.misc.arcstats.size", arc_stat); - arc_stats[0] = arc_stat >> 10; - GETSYSCTL("vfs.zfs.mfu_size", arc_stat); - arc_stats[1] = arc_stat >> 10; - GETSYSCTL("vfs.zfs.mru_size", arc_stat); - arc_stats[2] = arc_stat >> 10; - GETSYSCTL("vfs.zfs.anon_size", arc_stat); - arc_stats[3] = arc_stat >> 10; - GETSYSCTL("kstat.zfs.misc.arcstats.hdr_size", arc_stat); - GETSYSCTL("kstat.zfs.misc.arcstats.l2_hdr_size", arc_stat2); - arc_stats[4] = arc_stat + arc_stat2 >> 10; - GETSYSCTL("kstat.zfs.misc.arcstats.other_size", arc_stat); - arc_stats[5] = arc_stat >> 10; - si->arc = arc_stats; - } - - /* set arrays and strings */ - if (pcpu_stats) { - si->cpustates = pcpu_cpu_states; - si->ncpus = ncpus; - } else { - si->cpustates = cpu_states; - si->ncpus = 1; - } - si->memory = memory_stats; - si->swap = swap_stats; - - - if (lastpid > 0) { - si->last_pid = lastpid; - } else { - si->last_pid = -1; - } - - /* - * Print how long system has been up. - * (Found by looking getting "boottime" from the kernel) - */ - mib[0] = CTL_KERN; - mib[1] = KERN_BOOTTIME; - size = sizeof(boottime); - if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && - boottime.tv_sec != 0) { - si->boottime = boottime; - } else { - si->boottime.tv_sec = -1; - } -} - -#define NOPROC ((void *)-1) - -/* - * We need to compare data from the old process entry with the new - * process entry. - * To facilitate doing this quickly we stash a pointer in the kinfo_proc - * structure to cache the mapping. We also use a negative cache pointer - * of NOPROC to avoid duplicate lookups. - * XXX: this could be done when the actual processes are fetched, we do - * it here out of laziness. - */ -const struct kinfo_proc * -get_old_proc(struct kinfo_proc *pp) -{ - struct kinfo_proc **oldpp, *oldp; - - /* - * If this is the first fetch of the kinfo_procs then we don't have - * any previous entries. - */ - if (previous_proc_count == 0) - return (NULL); - /* negative cache? */ - if (pp->ki_udata == NOPROC) - return (NULL); - /* cached? */ - if (pp->ki_udata != NULL) - return (pp->ki_udata); - /* - * Not cached, - * 1) look up based on pid. - * 2) compare process start. - * If we fail here, then setup a negative cache entry, otherwise - * cache it. - */ - oldpp = bsearch(&pp, previous_pref, previous_proc_count, - sizeof(*previous_pref), ps.thread ? compare_tid : compare_pid); - if (oldpp == NULL) { - pp->ki_udata = NOPROC; - return (NULL); - } - oldp = *oldpp; - if (bcmp(&oldp->ki_start, &pp->ki_start, sizeof(pp->ki_start)) != 0) { - pp->ki_udata = NOPROC; - return (NULL); - } - pp->ki_udata = oldp; - return (oldp); -} - -/* - * Return the total amount of IO done in blocks in/out and faults. - * store the values individually in the pointers passed in. - */ -long -get_io_stats(struct kinfo_proc *pp, long *inp, long *oup, long *flp, - long *vcsw, long *ivcsw) -{ - const struct kinfo_proc *oldp; - static struct kinfo_proc dummy; - long ret; - - oldp = get_old_proc(pp); - if (oldp == NULL) { - bzero(&dummy, sizeof(dummy)); - oldp = &dummy; - } - *inp = RU(pp)->ru_inblock - RU(oldp)->ru_inblock; - *oup = RU(pp)->ru_oublock - RU(oldp)->ru_oublock; - *flp = RU(pp)->ru_majflt - RU(oldp)->ru_majflt; - *vcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw; - *ivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw; - ret = - (RU(pp)->ru_inblock - RU(oldp)->ru_inblock) + - (RU(pp)->ru_oublock - RU(oldp)->ru_oublock) + - (RU(pp)->ru_majflt - RU(oldp)->ru_majflt); - return (ret); -} - -/* - * If there was a previous update, use the delta in ki_runtime over - * the previous interval to calculate pctcpu. Otherwise, fall back - * to using the kernel's ki_pctcpu. - */ -static double -proc_calc_pctcpu(struct kinfo_proc *pp) -{ - const struct kinfo_proc *oldp; - - if (previous_interval != 0) { - oldp = get_old_proc(pp); - if (oldp != NULL) - return ((double)(pp->ki_runtime - oldp->ki_runtime) - / previous_interval); - - /* - * If this process/thread was created during the previous - * interval, charge it's total runtime to the previous - * interval. - */ - else if (pp->ki_start.tv_sec > previous_wall_time.tv_sec || - (pp->ki_start.tv_sec == previous_wall_time.tv_sec && - pp->ki_start.tv_usec >= previous_wall_time.tv_usec)) - return ((double)pp->ki_runtime / previous_interval); - } - return (pctdouble(pp->ki_pctcpu)); -} - -/* - * Return true if this process has used any CPU time since the - * previous update. - */ -static int -proc_used_cpu(struct kinfo_proc *pp) -{ - const struct kinfo_proc *oldp; - - oldp = get_old_proc(pp); - if (oldp == NULL) - return (PCTCPU(pp) != 0); - return (pp->ki_runtime != oldp->ki_runtime || - RU(pp)->ru_nvcsw != RU(oldp)->ru_nvcsw || - RU(pp)->ru_nivcsw != RU(oldp)->ru_nivcsw); -} - -/* - * Return the total number of block in/out and faults by a process. - */ -long -get_io_total(struct kinfo_proc *pp) -{ - long dummy; - - return (get_io_stats(pp, &dummy, &dummy, &dummy, &dummy, &dummy)); -} - -static struct handle handle; - -caddr_t -get_process_info(struct system_info *si, struct process_select *sel, - int (*compare)(const void *, const void *)) -{ - int i; - int total_procs; - long p_io; - long p_inblock, p_oublock, p_majflt, p_vcsw, p_ivcsw; - long nsec; - int active_procs; - struct kinfo_proc **prefp; - struct kinfo_proc *pp; - struct timespec previous_proc_uptime; - - /* these are copied out of sel for speed */ - int show_idle; - int show_jid; - int show_self; - int show_system; - int show_uid; - int show_command; - int show_kidle; - - /* - * If thread state was toggled, don't cache the previous processes. - */ - if (previous_thread != sel->thread) - nproc = 0; - previous_thread = sel->thread; - - /* - * Save the previous process info. - */ - if (previous_proc_count_max < nproc) { - free(previous_procs); - previous_procs = malloc(nproc * sizeof(*previous_procs)); - free(previous_pref); - previous_pref = malloc(nproc * sizeof(*previous_pref)); - if (previous_procs == NULL || previous_pref == NULL) { - (void) fprintf(stderr, "top: Out of memory.\n"); - quit(23); - } - previous_proc_count_max = nproc; - } - if (nproc) { - for (i = 0; i < nproc; i++) - previous_pref[i] = &previous_procs[i]; - bcopy(pbase, previous_procs, nproc * sizeof(*previous_procs)); - qsort(previous_pref, nproc, sizeof(*previous_pref), - ps.thread ? compare_tid : compare_pid); - } - previous_proc_count = nproc; - previous_proc_uptime = proc_uptime; - previous_wall_time = proc_wall_time; - previous_interval = 0; - - pbase = kvm_getprocs(kd, sel->thread ? KERN_PROC_ALL : KERN_PROC_PROC, - 0, &nproc); - (void)gettimeofday(&proc_wall_time, NULL); - if (clock_gettime(CLOCK_UPTIME, &proc_uptime) != 0) - memset(&proc_uptime, 0, sizeof(proc_uptime)); - else if (previous_proc_uptime.tv_sec != 0 && - previous_proc_uptime.tv_nsec != 0) { - previous_interval = (proc_uptime.tv_sec - - previous_proc_uptime.tv_sec) * 1000000; - nsec = proc_uptime.tv_nsec - previous_proc_uptime.tv_nsec; - if (nsec < 0) { - previous_interval -= 1000000; - nsec += 1000000000; - } - previous_interval += nsec / 1000; - } - if (nproc > onproc) { - pref = realloc(pref, sizeof(*pref) * nproc); - pcpu = realloc(pcpu, sizeof(*pcpu) * nproc); - onproc = nproc; - } - if (pref == NULL || pbase == NULL || pcpu == NULL) { - (void) fprintf(stderr, "top: Out of memory.\n"); - quit(23); - } - /* get a pointer to the states summary array */ - si->procstates = process_states; - - /* set up flags which define what we are going to select */ - show_idle = sel->idle; - show_jid = sel->jid != -1; - show_self = sel->self == -1; - show_system = sel->system; - show_uid = sel->uid != -1; - show_command = sel->command != NULL; - show_kidle = sel->kidle; - - /* count up process states and get pointers to interesting procs */ - total_procs = 0; - active_procs = 0; - total_inblock = 0; - total_oublock = 0; - total_majflt = 0; - memset((char *)process_states, 0, sizeof(process_states)); - prefp = pref; - for (pp = pbase, i = 0; i < nproc; pp++, i++) { - - if (pp->ki_stat == 0) - /* not in use */ - continue; - - if (!show_self && pp->ki_pid == sel->self) - /* skip self */ - continue; - - if (!show_system && (pp->ki_flag & P_SYSTEM)) - /* skip system process */ - continue; - - p_io = get_io_stats(pp, &p_inblock, &p_oublock, &p_majflt, - &p_vcsw, &p_ivcsw); - total_inblock += p_inblock; - total_oublock += p_oublock; - total_majflt += p_majflt; - total_procs++; - process_states[pp->ki_stat]++; - - if (pp->ki_stat == SZOMB) - /* skip zombies */ - continue; - - if (!show_kidle && pp->ki_tdflags & TDF_IDLETD) - /* skip kernel idle process */ - continue; - - PCTCPU(pp) = proc_calc_pctcpu(pp); - if (sel->thread && PCTCPU(pp) > 1.0) - PCTCPU(pp) = 1.0; - if (displaymode == DISP_CPU && !show_idle && - (!proc_used_cpu(pp) || - pp->ki_stat == SSTOP || pp->ki_stat == SIDL)) - /* skip idle or non-running processes */ - continue; - - if (displaymode == DISP_IO && !show_idle && p_io == 0) - /* skip processes that aren't doing I/O */ - continue; - - if (show_jid && pp->ki_jid != sel->jid) - /* skip proc. that don't belong to the selected JID */ - continue; - - if (show_uid && pp->ki_ruid != (uid_t)sel->uid) - /* skip proc. that don't belong to the selected UID */ - continue; - - *prefp++ = pp; - active_procs++; - } - - /* if requested, sort the "interesting" processes */ - if (compare != NULL) - qsort(pref, active_procs, sizeof(*pref), compare); - - /* remember active and total counts */ - si->p_total = total_procs; - si->p_active = pref_len = active_procs; - - /* pass back a handle */ - handle.next_proc = pref; - handle.remaining = active_procs; - return ((caddr_t)&handle); -} - -static char fmt[512]; /* static area where result is built */ - -char * -format_next_process(caddr_t handle, char *(*get_userid)(int), int flags) -{ - struct kinfo_proc *pp; - const struct kinfo_proc *oldp; - long cputime; - double pct; - struct handle *hp; - char status[16]; - int cpu, state; - struct rusage ru, *rup; - long p_tot, s_tot; - char *proc_fmt, thr_buf[6], jid_buf[TOP_JID_LEN + 1]; - char *cmdbuf = NULL; - char **args; - const int cmdlen = 128; - - /* find and remember the next proc structure */ - hp = (struct handle *)handle; - pp = *(hp->next_proc++); - hp->remaining--; - - /* get the process's command name */ - if ((pp->ki_flag & P_INMEM) == 0) { - /* - * Print swapped processes as - */ - size_t len; - - len = strlen(pp->ki_comm); - if (len > sizeof(pp->ki_comm) - 3) - len = sizeof(pp->ki_comm) - 3; - memmove(pp->ki_comm + 1, pp->ki_comm, len); - pp->ki_comm[0] = '<'; - pp->ki_comm[len + 1] = '>'; - pp->ki_comm[len + 2] = '\0'; - } - - /* - * Convert the process's runtime from microseconds to seconds. This - * time includes the interrupt time although that is not wanted here. - * ps(1) is similarly sloppy. - */ - cputime = (pp->ki_runtime + 500000) / 1000000; - - /* calculate the base for cpu percentages */ - pct = PCTCPU(pp); - - /* generate "STATE" field */ - switch (state = pp->ki_stat) { - case SRUN: - if (smpmode && pp->ki_oncpu != NOCPU) - sprintf(status, "CPU%d", pp->ki_oncpu); - else - strcpy(status, "RUN"); - break; - case SLOCK: - if (pp->ki_kiflag & KI_LOCKBLOCK) { - sprintf(status, "*%.6s", pp->ki_lockname); - break; - } - /* fall through */ - case SSLEEP: - if (pp->ki_wmesg != NULL) { - sprintf(status, "%.6s", pp->ki_wmesg); - break; - } - /* FALLTHROUGH */ - default: - - if (state >= 0 && - state < sizeof(state_abbrev) / sizeof(*state_abbrev)) - sprintf(status, "%.6s", state_abbrev[state]); - else - sprintf(status, "?%5d", state); - break; - } - - cmdbuf = (char *)malloc(cmdlen + 1); - if (cmdbuf == NULL) { - warn("malloc(%d)", cmdlen + 1); - return NULL; - } - - if (!(flags & FMT_SHOWARGS)) { - if (ps.thread && pp->ki_flag & P_HADTHREADS && - pp->ki_tdname[0]) { - snprintf(cmdbuf, cmdlen, "%s{%s}", pp->ki_comm, - pp->ki_tdname); - } else { - snprintf(cmdbuf, cmdlen, "%s", pp->ki_comm); - } - } else { - if (pp->ki_flag & P_SYSTEM || - pp->ki_args == NULL || - (args = kvm_getargv(kd, pp, cmdlen)) == NULL || - !(*args)) { - if (ps.thread && pp->ki_flag & P_HADTHREADS && - pp->ki_tdname[0]) { - snprintf(cmdbuf, cmdlen, - "[%s{%s}]", pp->ki_comm, pp->ki_tdname); - } else { - snprintf(cmdbuf, cmdlen, - "[%s]", pp->ki_comm); - } - } else { - char *src, *dst, *argbuf; - char *cmd; - size_t argbuflen; - size_t len; - - argbuflen = cmdlen * 4; - argbuf = (char *)malloc(argbuflen + 1); - if (argbuf == NULL) { - warn("malloc(%zu)", argbuflen + 1); - free(cmdbuf); - return NULL; - } - - dst = argbuf; - - /* Extract cmd name from argv */ - cmd = strrchr(*args, '/'); - if (cmd == NULL) - cmd = *args; - else - cmd++; - - for (; (src = *args++) != NULL; ) { - if (*src == '\0') - continue; - len = (argbuflen - (dst - argbuf) - 1) / 4; - strvisx(dst, src, - MIN(strlen(src), len), - VIS_NL | VIS_CSTYLE); - while (*dst != '\0') - dst++; - if ((argbuflen - (dst - argbuf) - 1) / 4 > 0) - *dst++ = ' '; /* add delimiting space */ - } - if (dst != argbuf && dst[-1] == ' ') - dst--; - *dst = '\0'; - - if (strcmp(cmd, pp->ki_comm) != 0) { - if (ps.thread && pp->ki_flag & P_HADTHREADS && - pp->ki_tdname[0]) - snprintf(cmdbuf, cmdlen, - "%s (%s){%s}", argbuf, pp->ki_comm, - pp->ki_tdname); - else - snprintf(cmdbuf, cmdlen, - "%s (%s)", argbuf, pp->ki_comm); - } else { - if (ps.thread && pp->ki_flag & P_HADTHREADS && - pp->ki_tdname[0]) - snprintf(cmdbuf, cmdlen, - "%s{%s}", argbuf, pp->ki_tdname); - else - strlcpy(cmdbuf, argbuf, cmdlen); - } - free(argbuf); - } - } - - if (ps.jail == 0) - jid_buf[0] = '\0'; - else - snprintf(jid_buf, sizeof(jid_buf), "%*d", - jidlength - 1, pp->ki_jid); - - if (displaymode == DISP_IO) { - oldp = get_old_proc(pp); - if (oldp != NULL) { - ru.ru_inblock = RU(pp)->ru_inblock - - RU(oldp)->ru_inblock; - ru.ru_oublock = RU(pp)->ru_oublock - - RU(oldp)->ru_oublock; - ru.ru_majflt = RU(pp)->ru_majflt - RU(oldp)->ru_majflt; - ru.ru_nvcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw; - ru.ru_nivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw; - rup = &ru; - } else { - rup = RU(pp); - } - p_tot = rup->ru_inblock + rup->ru_oublock + rup->ru_majflt; - s_tot = total_inblock + total_oublock + total_majflt; - - snprintf(fmt, sizeof(fmt), io_Proc_format, - pp->ki_pid, - jidlength, jid_buf, - namelength, namelength, (*get_userid)(pp->ki_ruid), - rup->ru_nvcsw, - rup->ru_nivcsw, - rup->ru_inblock, - rup->ru_oublock, - rup->ru_majflt, - p_tot, - s_tot == 0 ? 0.0 : (p_tot * 100.0 / s_tot), - screen_width > cmdlengthdelta ? - screen_width - cmdlengthdelta : 0, - printable(cmdbuf)); - - free(cmdbuf); - - return (fmt); - } - - /* format this entry */ - if (smpmode) { - if (state == SRUN && pp->ki_oncpu != NOCPU) - cpu = pp->ki_oncpu; - else - cpu = pp->ki_lastcpu; - } else - cpu = 0; - proc_fmt = smpmode ? smp_Proc_format : up_Proc_format; - if (ps.thread != 0) - thr_buf[0] = '\0'; - else - snprintf(thr_buf, sizeof(thr_buf), "%*d ", - (int)(sizeof(thr_buf) - 2), pp->ki_numthreads); - - snprintf(fmt, sizeof(fmt), proc_fmt, - pp->ki_pid, - jidlength, jid_buf, - namelength, namelength, (*get_userid)(pp->ki_ruid), - thr_buf, - pp->ki_pri.pri_level - PZERO, - format_nice(pp), - format_k2(PROCSIZE(pp)), - format_k2(pagetok(pp->ki_rssize)), - status, - cpu, - format_time(cputime), - ps.wcpu ? 100.0 * weighted_cpu(pct, pp) : 100.0 * pct, - screen_width > cmdlengthdelta ? screen_width - cmdlengthdelta : 0, - printable(cmdbuf)); - - free(cmdbuf); - - /* return the result */ - return (fmt); -} - -static void -getsysctl(const char *name, void *ptr, size_t len) -{ - size_t nlen = len; - - if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { - fprintf(stderr, "top: sysctl(%s...) failed: %s\n", name, - strerror(errno)); - quit(23); - } - if (nlen != len) { - fprintf(stderr, "top: sysctl(%s...) expected %lu, got %lu\n", - name, (unsigned long)len, (unsigned long)nlen); - quit(23); - } -} - -static const char * -format_nice(const struct kinfo_proc *pp) -{ - const char *fifo, *kproc; - int rtpri; - static char nicebuf[4 + 1]; - - fifo = PRI_NEED_RR(pp->ki_pri.pri_class) ? "" : "F"; - kproc = (pp->ki_flag & P_KPROC) ? "k" : ""; - switch (PRI_BASE(pp->ki_pri.pri_class)) { - case PRI_ITHD: - return ("-"); - case PRI_REALTIME: - /* - * XXX: the kernel doesn't tell us the original rtprio and - * doesn't really know what it was, so to recover it we - * must be more chummy with the implementation than the - * implementation is with itself. pri_user gives a - * constant "base" priority, but is only initialized - * properly for user threads. pri_native gives what the - * kernel calls the "base" priority, but it isn't constant - * since it is changed by priority propagation. pri_native - * also isn't properly initialized for all threads, but it - * is properly initialized for kernel realtime and idletime - * threads. Thus we use pri_user for the base priority of - * user threads (it is always correct) and pri_native for - * the base priority of kernel realtime and idletime threads - * (there is nothing better, and it is usually correct). - * - * The field width and thus the buffer are too small for - * values like "kr31F", but such values shouldn't occur, - * and if they do then the tailing "F" is not displayed. - */ - rtpri = ((pp->ki_flag & P_KPROC) ? pp->ki_pri.pri_native : - pp->ki_pri.pri_user) - PRI_MIN_REALTIME; - snprintf(nicebuf, sizeof(nicebuf), "%sr%d%s", - kproc, rtpri, fifo); - break; - case PRI_TIMESHARE: - if (pp->ki_flag & P_KPROC) - return ("-"); - snprintf(nicebuf, sizeof(nicebuf), "%d", pp->ki_nice - NZERO); - break; - case PRI_IDLE: - /* XXX: as above. */ - rtpri = ((pp->ki_flag & P_KPROC) ? pp->ki_pri.pri_native : - pp->ki_pri.pri_user) - PRI_MIN_IDLE; - snprintf(nicebuf, sizeof(nicebuf), "%si%d%s", - kproc, rtpri, fifo); - break; - default: - return ("?"); - } - return (nicebuf); -} - -/* comparison routines for qsort */ - -static int -compare_pid(const void *p1, const void *p2) -{ - const struct kinfo_proc * const *pp1 = p1; - const struct kinfo_proc * const *pp2 = p2; - - if ((*pp2)->ki_pid < 0 || (*pp1)->ki_pid < 0) - abort(); - - return ((*pp1)->ki_pid - (*pp2)->ki_pid); -} - -static int -compare_tid(const void *p1, const void *p2) -{ - const struct kinfo_proc * const *pp1 = p1; - const struct kinfo_proc * const *pp2 = p2; - - if ((*pp2)->ki_tid < 0 || (*pp1)->ki_tid < 0) - abort(); - - return ((*pp1)->ki_tid - (*pp2)->ki_tid); -} - -/* - * proc_compare - comparison function for "qsort" - * Compares the resource consumption of two processes using five - * distinct keys. The keys (in descending order of importance) are: - * percent cpu, cpu ticks, state, resident set size, total virtual - * memory usage. The process states are ordered as follows (from least - * to most important): WAIT, zombie, sleep, stop, start, run. The - * array declaration below maps a process state index into a number - * that reflects this ordering. - */ - -static int sorted_state[] = { - 0, /* not used */ - 3, /* sleep */ - 1, /* ABANDONED (WAIT) */ - 6, /* run */ - 5, /* start */ - 2, /* zombie */ - 4 /* stop */ -}; - - -#define ORDERKEY_PCTCPU(a, b) do { \ - double diff; \ - if (ps.wcpu) \ - diff = weighted_cpu(PCTCPU((b)), (b)) - \ - weighted_cpu(PCTCPU((a)), (a)); \ - else \ - diff = PCTCPU((b)) - PCTCPU((a)); \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) - -#define ORDERKEY_CPTICKS(a, b) do { \ - int64_t diff = (int64_t)(b)->ki_runtime - (int64_t)(a)->ki_runtime; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) - -#define ORDERKEY_STATE(a, b) do { \ - int diff = sorted_state[(b)->ki_stat] - sorted_state[(a)->ki_stat]; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) - -#define ORDERKEY_PRIO(a, b) do { \ - int diff = (int)(b)->ki_pri.pri_level - (int)(a)->ki_pri.pri_level; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) - -#define ORDERKEY_THREADS(a, b) do { \ - int diff = (int)(b)->ki_numthreads - (int)(a)->ki_numthreads; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) - -#define ORDERKEY_RSSIZE(a, b) do { \ - long diff = (long)(b)->ki_rssize - (long)(a)->ki_rssize; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) - -#define ORDERKEY_MEM(a, b) do { \ - long diff = (long)PROCSIZE((b)) - (long)PROCSIZE((a)); \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) - -#define ORDERKEY_JID(a, b) do { \ - int diff = (int)(b)->ki_jid - (int)(a)->ki_jid; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) - -/* compare_cpu - the comparison function for sorting by cpu percentage */ - -int -#ifdef ORDER -compare_cpu(void *arg1, void *arg2) -#else -proc_compare(void *arg1, void *arg2) -#endif -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); - - return (0); -} - -#ifdef ORDER -/* "cpu" compare routines */ -int compare_size(), compare_res(), compare_time(), compare_prio(), - compare_threads(); - -/* - * "io" compare routines. Context switches aren't i/o, but are displayed - * on the "io" display. - */ -int compare_iototal(), compare_ioread(), compare_iowrite(), compare_iofault(), - compare_vcsw(), compare_ivcsw(); - -int (*compares[])() = { - compare_cpu, - compare_size, - compare_res, - compare_time, - compare_prio, - compare_threads, - compare_iototal, - compare_ioread, - compare_iowrite, - compare_iofault, - compare_vcsw, - compare_ivcsw, - compare_jid, - NULL -}; - -/* compare_size - the comparison function for sorting by total memory usage */ - -int -compare_size(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - - ORDERKEY_MEM(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - - return (0); -} - -/* compare_res - the comparison function for sorting by resident set size */ - -int -compare_res(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - - return (0); -} - -/* compare_time - the comparison function for sorting by total cpu time */ - -int -compare_time(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); - - return (0); -} - -/* compare_prio - the comparison function for sorting by priority */ - -int -compare_prio(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - - ORDERKEY_PRIO(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); - - return (0); -} - -/* compare_threads - the comparison function for sorting by threads */ -int -compare_threads(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - - ORDERKEY_THREADS(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); - - return (0); -} - -/* compare_jid - the comparison function for sorting by jid */ -static int -compare_jid(const void *arg1, const void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - - ORDERKEY_JID(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); - - return (0); -} -#endif /* ORDER */ - -/* assorted comparison functions for sorting by i/o */ - -int -#ifdef ORDER -compare_iototal(void *arg1, void *arg2) -#else -io_compare(void *arg1, void *arg2) -#endif -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - - return (get_io_total(p2) - get_io_total(p1)); -} - -#ifdef ORDER -int -compare_ioread(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, inp1, inp2; - - (void) get_io_stats(p1, &inp1, &dummy, &dummy, &dummy, &dummy); - (void) get_io_stats(p2, &inp2, &dummy, &dummy, &dummy, &dummy); - - return (inp2 - inp1); -} - -int -compare_iowrite(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, oup1, oup2; - - (void) get_io_stats(p1, &dummy, &oup1, &dummy, &dummy, &dummy); - (void) get_io_stats(p2, &dummy, &oup2, &dummy, &dummy, &dummy); - - return (oup2 - oup1); -} - -int -compare_iofault(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, flp1, flp2; - - (void) get_io_stats(p1, &dummy, &dummy, &flp1, &dummy, &dummy); - (void) get_io_stats(p2, &dummy, &dummy, &flp2, &dummy, &dummy); - - return (flp2 - flp1); -} - -int -compare_vcsw(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, flp1, flp2; - - (void) get_io_stats(p1, &dummy, &dummy, &dummy, &flp1, &dummy); - (void) get_io_stats(p2, &dummy, &dummy, &dummy, &flp2, &dummy); - - return (flp2 - flp1); -} - -int -compare_ivcsw(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, flp1, flp2; - - (void) get_io_stats(p1, &dummy, &dummy, &dummy, &dummy, &flp1); - (void) get_io_stats(p2, &dummy, &dummy, &dummy, &dummy, &flp2); - - return (flp2 - flp1); -} -#endif /* ORDER */ - -/* - * proc_owner(pid) - returns the uid that owns process "pid", or -1 if - * the process does not exist. - * It is EXTREMELY IMPORTANT that this function work correctly. - * If top runs setuid root (as in SVR4), then this function - * is the only thing that stands in the way of a serious - * security problem. It validates requests for the "kill" - * and "renice" commands. - */ - -int -proc_owner(int pid) -{ - int cnt; - struct kinfo_proc **prefp; - struct kinfo_proc *pp; - - prefp = pref; - cnt = pref_len; - while (--cnt >= 0) { - pp = *prefp++; - if (pp->ki_pid == (pid_t)pid) - return ((int)pp->ki_ruid); - } - return (-1); -} - -static int -swapmode(int *retavail, int *retfree) -{ - int n; - int pagesize = getpagesize(); - struct kvm_swap swapary[1]; - - *retavail = 0; - *retfree = 0; - -#define CONVERT(v) ((quad_t)(v) * pagesize / 1024) - - n = kvm_getswapinfo(kd, swapary, 1, 0); - if (n < 0 || swapary[0].ksw_total == 0) - return (0); - - *retavail = CONVERT(swapary[0].ksw_total); - *retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used); - - n = (int)(swapary[0].ksw_used * 100.0 / swapary[0].ksw_total); - return (n); -} Index: usr.bin/top/top.local.1 =================================================================== --- usr.bin/top/top.local.1 +++ usr.bin/top/top.local.1 @@ -1,75 +0,0 @@ -.\" $FreeBSD$ -.SH "FreeBSD NOTES" - -.SH DESCRIPTION OF MEMORY -Mem: 9220K Active, 1M Inact, 3284K Wired, 1M Cache, 2M Buf, 1320K Free -ARC: 2048K Total, 342K MRU, 760K MFU, 272K Anon, 232K Header, 442K Other -Swap: 91M Total, 79M Free, 13% Inuse, 80K In, 104K Out -.TP -.B K: -Kilobyte -.TP -.B M: -Megabyte -.TP -.B G: -Gigabyte -.TP -.B %: -1/100 -.SS Physical Memory Stats -.TP -.B Active: -number of bytes active -.TP -.B Inact: -number of bytes inactive -.TP -.B Wired: -number of bytes wired down, including BIO-level cached file data pages -.TP -.B Cache: -number of clean bytes caching data that are available for -immediate reallocation -.TP -.B Buf: -number of bytes used for BIO-level disk caching -.TP -.B Free: -number of bytes free -.SS ZFS ARC Stats -These stats are only displayed when the ARC is in use. -.TP -.B Total: -number of wired bytes used for the ZFS ARC -.TP -.B MRU: -number of ARC bytes holding most recently used data -.TP -.B MFU: -number of ARC bytes holding most frequently used data -.TP -.B Anon: -number of ARC bytes holding in flight data -.TP -.B Header: -number of ARC bytes holding headers -.TP -.B Other -miscellaneous ARC bytes -.SS Swap Stats -.TP -.B Total: -total available swap usage -.TP -.B Free: -total free swap usage -.TP -.B Inuse: -swap usage -.TP -.B In: -bytes paged in from swap devices (last interval) -.TP -.B Out: -bytes paged out to swap devices (last interval)