This is the long-awaited import of top into the base system (actually,

the src/contrib/top part right now).  This tools is simply too system-
dependant to maintain it in the ports collection.
This commit is contained in:
joerg 1997-03-23 18:51:21 +00:00
commit 1b9d0472b7
36 changed files with 7017 additions and 0 deletions

26
contrib/top/ADVERTISEMENT Normal file
View File

@ -0,0 +1,26 @@
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
Unix and Internet security
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".

503
contrib/top/Changes Normal file
View File

@ -0,0 +1,503 @@
Thu Aug 29 1996 - wnl (3.4)
Replaced modules (from Tim Pugh): next 32, next40.
Fixed bug in username.c: hashing negative uids.
Thu Aug 22 1996 - wnl (3.4beta3)
Patched modules: ultrix4, sunos4, sunos5, utek, decosf1, irix5.
Added modules: next40, next32.
Fixed procstates update bug in display.c.
Fixed divide by zero bug in utils.c.
Fixed bad number in layout.h
Minor fixes to Configure.
Complete overhaul of FAQ.
Tue Feb 13 1996 - wnl (3.4beta3)
Added convex module from Warren Vosper (originally written by
William Jones).
Tue Feb 13 1996 - wnl (3.4beta2)
Fixed format_k in utils.c to calculate K and M values correctly.
Added check for gigabyte values ('G'). Changed sumamry_format
in display.c to use format_k where appropriate.
Changed creation of distribution tar file to place everything in
a top level directory.
Tue Jan 30 1996 - wnl (3.4beta2)
Added m_aix41 module. Added new tag type to module comments:
TERMCAP, which defined the library to use for a termcap library.
If no TERMCAP tag is found in the module's initial comment, then
Configure will default to "-ltermcap". AIX needs this since it
put all the termcap routines in libcurses(!)
Added m_bsdos2 (found lingering in my mailbox).
Updated m_svr4 to include support for NCR multiprocessors.
Fixed small bug in utils.c
Thu Jan 25 1996 - wnl (3.4beta1)
Fixed m_sunos5 invocation of gettimeofday to include "NULL" as
second argument. This provides compatability with the Posix-
compliant template provided with SunOS 5.5, but doesn't hurt
previous versions since they do bother with a template for that
function.
Made changes (recommended by net users) to hpux10, ultrix4,
netbsd10, aux3 (replaced aux31). Added module for linux.
Fri Oct 10 1995 - wnl (3.4beta1)
Added user-contributed modules for SCO Unix, IRIX 5, HP/UX 10,
Pyramid DC/OSX. Changed Configure so that it runs in environments
whose c-shells have no 'eval'(!). Added support for multiple sort
ordering methods via the -o switch. This option requires support
from the machine dependent module: such support was added to
sunos5 (thus sunos54) and sunos4.
display.c: Changed CPU states display line to shorten the leading
tag if the data won't fit in the current width. Fixed a divide-by-
zero bug that affected ultrasparc servers (and potentially other
systems).
m_sunos5.c: Now asks the system for the correct pagesize rather than
assuming it is 4K.
Thu Mar 2 1995 - wnl (3.3 RELEASE)
Added module netbsd10 and renamed netbsd to netbsd08. Changed
Configure so that it does not use an initial default module name.
Made other compatability fixes to Configure. Added comments to
decosf1 concerning optimizer bug. Other documentation changes.
Added use of "prime.c" to Configure script.
Tue Feb 7 1995 - wnl (3.3beta6)
Still one more beta....
Fixes for sunos5 2.4 gcc core dump (it was an alignment problem).
Fixed and improvements for decosf1 (including use of format_k
for proper SIZE column formatting). Added modules freebsd20 and
ncr3000.
Thu Feb 2 1995 - wnl (3.3beta5)
One more beta....
Fixed a few bugs in the sunos5 port pertaining to casting and
very large memory counts. Added "ifndef HAVE_GETOPT" to getopt.c
to provide for conditional compilation of the getopt function.
Those systems that have getopt in libc can add -DHAVE_GETOPT to
the CFLAGS line in the module to prevent the function from being
compiled. Added sunos54 module to accomodate SunOS 5.4
peculiarities. Added module for aux3.1.
Wed Jan 4 1995 - wnl (3.3beta4)
This is really taking too long......sigh.
Fixed SIGWINCH handling once and for all. It now remembers the
number of processes you want displayed even thru window resizes.
Fixed buffer conflict in utils.c (itoa and itoa7).
Lots of small improvements to the various modules were made over
the past month: too numberous to list here. SunOS 5 module made
more secure thru use of seteuid calls (other SVR4 modules should
be modified similarly). One final MP fix to sunos5, too. Module
for decosf1 was modified to accomodate V3.0.
Mon Apr 18 1994 - wnl (3.3beta3)
I think I finally got a sunos5 module that will work on MP
machines. Fixed cpu states figure in osmp41a so that
percentages never exceed 100%. Added shell script "install"
since Unix vendors can't seem to make up their minds on what
options they want to use for the one that comes with the OS.
Added netbsd modules from Christos. Fixed lots of other little
things over the past few months that I have long since forgotten.
Wed Dec 15 1993 - wnl (3.3beta2)
Added module patches from various users: hpux9, sunos5.
Fixed bug with batch mode (screen_width wasn't getting set).
Changes to accomodate 64 bit machines.
Fixed some bugs in command parsing ("renice 19 " did something
unexpected).
Mon Aug 30 1993 - wnl (3.3beta)
Added lots of little patches from various users.
Added routines to utils.c for intelligent formatting of kilobytes
and time. These are intended to be used in the modules when
formatting a process line. Added code to "summary_format" in
display.c to do intelligent formatting of memory quantities.
Redid display.c to allow for varying line widths and dynamic
reallocation of the screen buffer.
Added a SIGWINCH handler to top.c!
Added a constant, MAX_COLS, to top.h which defines the absolute
widest line we will ever allow. Changed allocations of "char fmt"
in all machine modules to use this constant rather than an abitrary
number.
Fri Aug 13 1993 - wnl (3.3)
Changed return value definition of time-related functions in top.c,
display.c, and m_ultrix4.c to time_t (stuart@coral.cs.jcu.edu.au).
Fixed bug in display.c: line_update when start != 0.
Wed Aug 4 1993 - wnl (3.2 release)
Changes to Configure from Paul Vixie. Added modules for hpux9 and
bsd386.
Tue Jul 13 1993 - wnl (3.1 release)
More small changes and minor bug fixes. Brought bsd44 up to date
and added a module for svr4.2. Changed shar packaging to use Rich
Salz's cshar stuff.
Wed Jul 7 1993 - wnl (3.1BETA)
More changes and bug fixes to Configure. Applied some other
minor bug fixes and suggestions from the beta testers. Added
the "metatop" shell script and the "installmeta" rule to the
Makefile to make handling multiple machine models and OS versions
easier. Added INSTALL and FAQ files.
Tue May 18 1993 - wnl (3.1BETA)
Changed Configure to be compatible with most SVR4 environments
(differing output from "ls -lg"). Also changed Configure,
Makefile.X, etc., to look for module files in the subdirectory
"machine" (thanks to Christos Zoulas).
Tue Apr 20 1993 - wnl (3.1BETA)
Changed both occurences of "ls -1" in Configure to "ls". This
SHOULD produce the same result, and has the advantage that it
doesn't produce an error on a system 5 machine. Integrated other
changes recommended in the first round of beta testing.
Wed Mar 10 1993 - wnl (3.1BETA)
MAJOR CHANGE: I have added a required function to all machine
dependent modules, called proc_owner. It takes a pid as an argument
and returns the uid of the process's owner. Such capability is
necessary for top to run securely as a set-uid program, something
that is needed for SVR4 implementations to read /proc. I have
retrofitted all modules except dgux with this function, but was
not able to test most of them. Top should now run securely as
a setuid program. Added 386bsd and sunos5 modules. Added sunos4mp
module for MP Suns.
Sat Feb 20 1993 - wnl (3.1ALPHA)
Modified top.c and commands.c to compile correctly on System V
derived Unixes (especially SVR4), but in a way that doesn't rely
on an oracle-like declaration (that is, I don't use "ifdef SYSV").
Fixed some bugs in "Configure" and "getans". Added inspection of
env variable "TOP" for options, and made -I default to showing
idle processes. Added "u" command to change username restriction
on the fly. Created shell script "suntop" for poor multi-version
SunOS folks (like myself).
Wed Jun 3 1992 - wnl (3.0)
"max_topn" wasn't being used everywhere it was supposed to be
in top.c. Many cosmetic changes, including copyright notices in
all the .c files. Version number is now handled by version.c and
reflects the current patchlevel (which is initially set to 0).
Changed Configure and Makefile to allow configurable variables for
certain commands: shell, cc, awk, install. Updated README and
Porting. Ready to release to the world!
Mon May 18 1992 - wnl (2.9BETA)
Added modules provided by Christos Zoulas. Replaced screen.c
with one modified by Christos and that will appropriately select
and handle the sgtty, termio, or termios system. Integrated many
other changes recommended by Christos. Fixed (I hope) the "-b"
batch mode display bug. Had to change loadavg to load_avg to avoid
a conflict with 4.4BSD.
Mon Apr 27 1992 - wnl (2.8BETA)
Added modules provided by Daniel Trinkle. Added patchlevel.h,
but the patch level is not yet reflected in the version number.
Cleaned up m_sunos4.c a little.
Wed Apr 22 1992 - wnl (2.8BETA)
Major internal reorganization. All of the system dependent stuff
is now really and truly separated from everything else. The
system dependent functions are contained in a separate .c file
called a "module". The Configure script knows how to find and
set up these modules, but the human installer still needs to tell
Configure which module to use (no automagic determination of
machine type---sorry). Added -U option to specify one user's
processes, but there is no corresponding command...yet. Other
changes and improvements too numerous to mention here. Currently
there are only two modules: sunos4 and umax. But after this beta
release is sent around, I expect more to be written. I just hope
that the machine-dependent abstractions don't need to change in
the process.
Thu Mar 26 1992 - wnl (2.7BETA)
Beta release with minimal architecture support. Updated README
and added a first cut at a Porting guide. Added ioctl TIOCGWINSZ
code from top2.5+ (courtesy of David MacKenzie). I didn't even
try porting the Ultrix support since I don't have access to an
Ultrix machine.
Fri Oct 11 1991 - wnl (2.6)
This version was not widely released. It contained many changes.
Here are the major ones:
Put in Vixie's idle process hack.
Enhanced type field in new_message to handle delayed messages.
Changed u_process to automatically adjust for varying lines of
output. Management of screenbuf should now be completely contained
in display.c. Removed now extraneous code from CMD_number[12]
portion of command switch in top.c. This was the stuff that dealt
with zeroing out lines in screenbuf.
Finally made it all work correctly on a 386i. Problems I had to
overcome: kvm_nlist doesn't return 0 on success as advertised (it
returns 1 instead); the results of a kvm_nlist are different
(n_type can be zero even for a symbol that exists).
Serious rearrangement for processor dependent stuff. All nlists
are now in separate files with the suffix ".nlist". Most machine
specific code is in "machine.c" surrounded by appropriate ifdefs---
the goal is to eventually have all machine specific code in this
file. Managed to find a way to detect SunOS 4.x at compile-time:
this is contained in the include file "sun.h". Completely changed
the memory display line for SunOS 4.x---it now displays a far
more appropriate report.
Created the shell script "Configure" to aid in the configuration
step.
Fixed a bug in init_termcap: it will now tolerate an environment
which does not have TERM defined (thanks to Sam Horrocks for
pointing this out).
Tue Aug 9 1988 - wnl (2.5)
Added changes to make top work under version 4.0 of the Sun
operating system. Changes were provided by Scott Alexander of the
University of Pennsylvania. Thanks! Compile with "-Dsunos4" to
get them. Virtual memory statistics are not readily accessible
under 4.0, so they don't show up in the output.
Thu Jul 31 1987 - wnl (2.4)
Fixed a problem with the 4.0 Pyramid code. The label "cp_time"
doesn't exist in the 4.0 kernel anymore. I think the code Carl
sent me wants "percpu" instead. That is what I am using and it
appears to work. 375 code is still untested (at least by me).
Also picked a great deal of lint out of the source. Lint now only
complains about a very few nitpicky things (there are far too many
calls to "printf" to put a "(void)" in front of!), at least under
SunOS.
Tue Jul 28 1987 - wnl (2.4a)
Added changes for a Symmetrics Computer Systems s/375 machine.
Changes were provided by Paul Vixie. Thanks! According to Mr.
Vixie: "These changes were not made at, by, or for SCS proper.
SCS would probably be interested in them, but so far only the
users' group has them. They were made in February, 1987, to
version 2.1 of the program, by Paul Vixie
(dual!ptsfa!vixie!paul@ucbvax.Berkeley.EDU)." His changes were
integrated into version 2.3 to make version 2.4.
The SCS peculiarities are summarized in Changes.scs.
Tue Jun 9 1987 - wnl (2.3 for real)
Changed the includes for the extra code Carl sent me to only
compile on Version 4.0 Pyramid machines. This makes top still
compilable on pre-4.0 Pyramids. Specifically, this code is only
compiled when both "pyr" and "CPUFOUND" are defined.
Wed Jun 3 1987 - wnl (2.3 with Pyramid additions)
It's been a month and I still haven't done anything about
distributing this version. However, Carl Gutekunst from Pyramid
has sent me some extra patches for some of the Pyramid code. I
just added those and will make them part of 2.3. This fixes the
following Pyramid problems: adds the inclusion of <sys/systm.h>,
uses the correct size for getting the kernel value _ccpu (this bug
affected the Vax version as well), sums the elements of the percpu
array to calculate a cp_time value (for OSx 4.0).
Fri May 1 1987 - wnl (2.3)
I have finally finished all the changes for better support of
oddbal terminals. Added the low-level routine "clear_eol" which
makes handling terminals without "ce" easy: it uses spaces
instead. All direct uses of "clear_line" outside of screen.c have
been changed to use this primitive. A terminal with "os" is now
handled in such that all situations that need overwriting are
completely avoided (including several commands). This required
some changes to the way commands are translated into action (in
"top.c"). Made several important changes to display.c to prevent
overflowing of any of the fields. Specifically, more than 99
total processes and a cpu state that reaches 100%. Had to make a
small change to two casts in top.c, because the Sun 3.2 compiler
was giving warnings on them. Added the "-q" option which lets
root run top at a nice of -20 (in case he thinks he really needs it).
Tue Dec 30 1986 - wnl (2.2)
I think I fixed a bug reported by Julian Onions at Nottingham.
Occasionally, top will core dump when the sprintf in either
i_process or u_process overflows due to an exceptionally
unrealistic time value. I think it highly unlikely that top can
get a bad proc structure (although I suppose it is possible), but
the process time is read from the user structure, and that can
sometimes be part garbage. So, "get_ucpu" checks the value it
returns to make sure its formatted form will not overflow the
sprintf. If this doesn't fix the bug, then more drastic measures
will be necessary. I plan to make this version the official
"top 2.2". [[ This version was never distributed very widely. ]]
Tue Dec 2 1986 - wnl (2.2c)
Added to top.c the notion of a "failed command". When a command
produces a message (on the message line), an update does not
follow it. Before, the message was written and a new display was
shown---purposefully not overwriting the message. But the
improvements to handle overstriking terminals and terminals
without "ce" clear the screen before every display, which would
erase the message. Now, the message is displayed and top waits
another full time interval before updating the display. This
works much better all around.
Mon Nov 24 1986 - wnl (2.2b)
Created a new file, utils.c, and made appropriate changes to
Makefile. This new file holds all utility functions that can and
may be used by more than one "module". Improved i_memory and
u_memory (display.c) so that screen updates for the values
displayed are only changed when necessary. Also made the line
look better: the last fixes made for a rather ugly display.
Added the locally defined constant "LoadMax" and added code to
top.c to send the cursor home after a space command is entered if
the load average is higher than "LoadMax". This provides visual
feedback on loaded systems.
Mon Nov 3 1986 - wnl (2.2a)
Widened the format for memory usage so that it can display 5
digits. This makes that line look a little ugly---maybe I'll fix
that later. Screen handling now understands "os" and a missing
"ce". It treats them identically: clear the screen between each
display. Screen handling code now uses "cd" when appropriate
(i.e.: when user has shortened the screen). Made i_loadave clear
then screen and took out most of the explicit calls to "clear" in
top.c. This method is cleaner, especially in conjunction with
"os" handling. Added preprocessor variable "RANDOM_PW" for
systems that access the passwd file randomly (Sun's yp and 4.3).
With "RANDOM_PW" set, "getpwuid" is used instead of "getpwnam",
but uid->username mappings are still hashed internally (because
that is still faster than going to disk).
Mon Oct 6 1986 - wnl (2.1)
A bug with the kill command was pointed out by "dciem!tim"---
specifying a signal by name did not work correctly. This bug has
been fixed with a simple change to commands.c. Another bug made
the cpu state percentages incorrect the first time they were
displayed. This bug has also been fixed (changed top.c).
Thu Sep 4 1986 - wnl (2.0, at last)
This is the version that will (hopefully) get released to the
world as top 2.0.
Added the "r" and "k" commands for renice and kill, respectively.
This required adding a way to handle system call errors, and the
addition of the "e" command. Help screen and manual page were
changed to reflect this change. Changed all "#ifdef SUN" directives
to "#ifdef sun", and changed all "#ifdef PYRAMID" directives to
"#ifdef pyr". As much as I hate those choices of preprocessor
names (they too easily conflict with real variable names), it does
make automatic compilation possible---people don't have to change
the Makefile anymore for specific machines. The manual page was
changed to automatically incorporate the defaults as set in the
Makefile (including an infinite value for TOPN) and the way the
manual page is generated by the Makefile was changed to make
maintenance of this information automatic.
Mon Jul 28 1986 - wnl (still pre 2.0)
Real close now. I put in a new definition for the macro "pagetok"
that does an explicit shift of a constant expression involving
PGSHIFT. Appropriate checks are made if PGSHIFT is to small.
"pagetok" is now used exclusively everywhere to convert kernel
clicks to kilobytes. I added a full blown interactive mode with
the ability to change some of the runtime parameters (how many to
display, time delay, etc.) while top is running. I also
incorporated a few ideas from the net: control characters in the
command name are replaced with '?'; the '-S' option makes the
swapper and pager visible; options have been added to control the
number of displays produced (this makes it easier to make
performance snapshots with top). I have also added the notion of
"infinite" values for number of processes and number of displays.
I fixed a long-standing bug in the uid to username mapping code
that was only aggravated on the pyramids: it was an ill-defined
expression (akin to i = i++). I tweaked the proc_compar routine
for qsort slightly so that stopped processes were more likely to
show up. Manual page was updated to reflect all changes
noticeable to the user.
Tue Jul 1 1986 - wnl (pre 2.0 -- 1.9999?)
In the process of major revamping on the way to version 2.0.
I have completely done away with curses by adding my own screen
management routines in a separate file (screen.c). The rationale
for this is that top knows a whole lot more about what is and is
not redundant on the screen and can compare simple integer values
where curses would have to compare strings. This has turned out
to be a very big win speed-wise. The proc_compar routine for
sorting has been rewritten to include several more keys. I
decided this was necessary when I noticed that the "top" process
itself kept disappearing off the top 10 list on a Sun-3. All the
processes had the same percentage (0%) and the sort wasn't really
doing anything worthwhile. I changed the expression that computes
memory usage to use the ctob macro instead of just assuming that
pages were 512 bytes. More work still needs to be done before
this version is usable. I changed options-processing to use
getopt and added appropriate incantations to the Makefile.
Wed Feb 20 1985 - wnl (still 1.8)
Put in the ifdef FOUR_ONE statements to make top still compilable
on a 4.1 system. Apparently, there are some users out there that
need this functionality. Oh well. I don't guarantee any of it,
since I can't test it. Made appropriate changes to README and
final installation related changes to Makefile.
Sat Feb 2 1985 - wnl (1.8)
Removed all the ifdef FOUR_TWO statements and made "top" into a
4.2 only program. If someone really wants to still run it on 4.1,
then they can do all the work. We don't have a 4.1 machine
anymore, so I don't even know if the thing still works under 4.1.
Cleaned up the Makefile and the README. Added installation rules
to the Makefile, as requested by several sites. Fixed a very
obscure divide-by-zero bug. Added a second "key" to the qsort
comparison function (proc_compar) so that comparisons are based on
cpu ticks if the percentages are equal (provided by Jonathon
Feiber at Sun).
Tue Dec 11 1984 - wnl (1.7)
Added the virtual and real memory status line to the header area
(provided by Jonathon Feiber at Sun)
Tue Nov 20 1984 - wnl (1.6)
Added an "exit" if sbrk's fail. Added changes from Jonathon
Feiber at Sun: ifdef SUN to make top work on Suns (they don't use
doubles in the proc structure), register declarations, check for
getting a user structure that has disappeared since the proc array
was read (it used to die, now it just shows the process as swapped).
Tue Nov 13 1984 - wnl (1.5)
If the number of displayable processes ("active_procs") was less
than the number of requested processes ("topn"), top would
segmentation fault. This bug has been fixed. Thanks to Prentiss
Riddle at ut-sally for pointing out the existence of this bug.
Tue Oct 23 1984 - wnl (1.4)
Finally fixed the hash table bug that caused processes owned by
root to sometimes appear with either no name or a different name
that had UID 0 (such as "operator"). Removed all the ifdef DEBUG
blocks to make top ready for distribution to the real world.
Sun Apr 8 1984 - wnl (still 1.3)
Made some slight changes to the display format. It now looks more
aesthetically pleasing. Added some preprocessor constants so that
the two defaults (number of processes and seconds of delay) easier
to change.
Thu Apr 5 1984 - wnl (1.3)
Changed the order in which things are done at initialization time.
This way, if an error occurs before starting the main loop, curses
will never get started. Also changed other error handlers so that
endwin() is called before any flavor of exit. Specifying a number
of processes that is more than the screen can handle is no longer
fatal. It displays a warning message and pretends the user
specified the maximum for the screen. Finally cured all the TSTP
blues (well, almost all). I removed my TSTP handler and convinced
the system to always use the one that curses sets up. Turns out
that "sleep" was stepping all over it during a pause. So, I don't
use sleep anymore. The only problem that remains with it now is
redrawing the old display before updating it after a pause.
Tue Apr 3 1984 - wnl (from 1.0 to 1.2)
I changed the format of the TIME column from just "seconds" to
"minutes:seconds". I also made pausing work correctly. Screen
redraws with an up to date display. For compatibility with 4.2, I
changed the name of the "zero" function to "bzero". The makefile
has been altered to handle versions for 4.1 and 4.2, and README
has been updated to reflect these recent changes.

503
contrib/top/Configure Executable file
View File

@ -0,0 +1,503 @@
#!/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.4
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
set descs = `sed -e 's@\.c$@.desc@' $$.f`
sed -e "s@%descs%@$descs@" Make.desc.X >Make.desc
# build desc files and SYNOPSIS as needed
make -f Make.desc >/dev/null
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 $$.[fmn]
exit 1
endif
set random1 = `expr $random + 1`
cat <<EOF
Using these settings:
Bourne Shell $Cmdshell
C compiler $Cmdcc
Compiler options $cdefs
Awk command $Cmdawk
Install command $Cmdinstall
Module $module
LoadMax $LoadMax
Default TOPN $topn
Nominal TOPN $NominalTopn
Default Delay $delay
Random passwd access $yesno[$random1]
Table Size $TableSize
Owner $owner
Group Owner $group
Mode $mode
bin directory $bindir
man directory $mandir
man extension $manext
man style $mansty
EOF
goto fast
endif
cat <<'EOF'
You will be asked a series of questions. Each question will have a
default answer enclosed in brackets, such as "[5.0]". In most cases,
the default answer will work well. To use that value, merely press
return.
'EOF'
# display synopses
getmod:
cat <<'EOF'
The following machine-dependent modules are available:
'EOF'
awk -F: ' { printf "%-8s %s\n", $1, $2 }' SYNOPSIS
echo ''
./getans "What module is appropriate for this machine? " string "$module" .$$
set module = `cat .$$`
if ("$module" == "") then
echo "Please specify a valid module name."
goto getmod
endif
# is it a valid one?
grep -s "$module" $$.n >/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 <<EOF
What other options should be used with the $Cmdcc command (use "none" to
specify no options)?
EOF
./getans "Compiler options" string "$cdefs" .$$
set cdefs = `cat .$$`
if ("$cdefs" == "none") then
set cdefs = ""
endif
cat <<'EOF'
Now you need to answer some questions concerning the configuration of
top itself.
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. "LoadMax" should always be specified as a
floating point number.
'EOF'
./getans "LoadMax" number "$LoadMax" .$$
set LoadMax = `cat .$$`
cat <<'EOF'
"Default TOPN" is the default number of processes to show. This is the
number that will be used when the user does not specify the number of
processes to show. If you want "all" (or infinity) as the default, use
the value "-1".
'EOF'
./getans "Default TOPN" neginteger "$topn" .$$
set topn = `cat .$$`
cat <<'EOF'
"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".
'EOF'
./getans "Nominal TOPN" integer "$NominalTopn" .$$
set NominalTopn = `cat .$$`
cat <<'EOF'
Default Delay is the default number of seconds to wait between screen
updates.
'EOF'
./getans "Default Delay" integer "$delay" .$$
set delay = `cat .$$`
echo ""
set rand = 0
ypwhich >&/dev/null
if ($status == 0 || -e /etc/passwd.dir || -e /etc/pwd.db) then
set rand = 1
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`
set mapfile = "NIS map"
else
set cnt = `wc -l </etc/passwd`
set mapfile = "file"
endif
rm /tmp/$$.a
set double = `expr $cnt \* 2`
echo "I found $cnt entries in your passwd $mapfile. Top hashes the username to"
echo "uid mappings as it goes along and it needs a good guess on the size of"
echo "that hash table. This number should be the next highest prime number"
echo "after $double."
echo ""
if (-e prime) then
set pr = `./prime $double`
echo "I have calculated that to be $pr."
else if (-e $PRIME) then
set pr = `$PRIME $double | head -1`
echo "I have calculated that to be $pr."
else
set pr = $double
echo "I cannot calculate that prime number, so you will need to provide it for me."
endif
if ($TableSize == 0) then
set TableSize = $pr
endif
./getans "Enter the hash table size" integer "$TableSize" .$$
set TableSize = `cat .$$`
echo ""
# !!! I need to fix this: /dev/kmem might not exist on some machines !!!
# determine the right way to invoke ls to get full output
set ls = "ls -l"
if (`$ls getans | wc -w` < 9) then
set ls = "ls -lg"
endif
set t_owner = root
set t_group = `$ls -d /usr/bin | awk ' { print $4 }'`
if (-e /proc) then
cat <<EOF
Uh oh! I see /proc out there. Some new Unix variants provide the /proc
file system as a mechanism to get to a process's address space. This
directory is typically only accessible by root. However, there are a few
systems (such as DG/UX) on which this directory exists, but isn't used.
I'm going to assume that top needs to run setuid to root, but you should
double check and use mode 2755 (set group id) if top doesn't really need
root access. If you are running SunOS 5.x then you will need to install
top setuid root (owner root and mode 4711).
EOF
set t_mode = 4711
set mode = 4711
else if (-e /dev/kmem) then
$ls /dev/kmem >/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 -c "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
foreach v ($vars)
set tmp = `eval echo \$$v`
echo set $v = "'$tmp'" >>.defaults
end
endif
fast:
# set variables which contain module lists
set modules = `cat $$.f`
set manmodules = `cat $$.m`
# clean up
rm -f $$.f $$.m $$.n
# 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/^.[^:]*: *//'`
# default for tcap (termcap)
if ("$tcap" == "") then
set tcap="-ltermcap"
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|%cdefs%|$cdefs|" \
-e "s|%modules%|$modules|" \
-e "s|%manmodules%|$manmodules|" \
-e "s|%signal%|$signal|" \
-e "s|%cc%|$Cmdcc|" \
-e "s|%awk%|$Cmdawk|" \
-e "s|%install%|$Cmdinstall|" \
-e "s|%shell%|$Cmdshell|" \
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 .$$ $$.[fmn] /tmp/$$.[ab]
exit 1

30
contrib/top/DISCLAIMER Normal file
View File

@ -0,0 +1,30 @@
DISCLAIMER
"top" is distributed free of charge. It should not be considered an
official product of Argonne National Laboratory. 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, 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 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!

233
contrib/top/FAQ Normal file
View File

@ -0,0 +1,233 @@
TOP
Version 3.4
William LeFebvre
and a cast of dozens
FREQUENTLY ASKED QUESTIONS AND THEIR ANSWERS
This FAQ is broken out in to several topics.
GENERAL
1. "Where do I get the latest version of top?"
The latest version of top is now available at the site "ftp.groupsys.com" in
the directory "/pub/top". It is also available at "eecs.nwu.edu" in the
directory "/pub/top".
2. "Is there a web page for top?"
Not at this time, but I am planning one. When it is finally available, you
will be able to find it at "www.groupsys.com."
3. "Is there a mailing list for top?"
Currently 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.
I am planning a general mailing list for announcements and such, but it is
not yet available.
4. "How can I find out when all these things become available?"
Information about the web site and the mailing list will be made available
in future distributions of top. New distributions will be announced on the
appropriate Usenet newsgroups (including comp.sources.unix).
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 recently 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.5.
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.
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 <wnl@groupsys.com>). 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 a
script in the distribution called "fixincludes"), (2) compile machine.c
with "CFLAGS=-I/usr/include" then make the rest of the object files
normally, or (3) use "cc".
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 Solaris 2.0, 2.1, 2.2, 2.3, 2.4,
and 2.5. You probably compiled top with /usr/ucb/cc instead of the real C
compiler. See FAQ #10 for more details.
SCO PROBLEMS
20. "When I try to run Configure, it complains about a syntax error."
Some versions of SCO's csh do not understand the syntax "$<". This breaks
Configure. You'll just have to hack around it for now: the Configure script
is going to be completely redone in the near future anyway.
SVR42 PROBLEMS
21. "The load average and memory displays don'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
22. 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.

165
contrib/top/INSTALL Normal file
View File

@ -0,0 +1,165 @@
TOP
Version 3.4
William LeFebvre
and a cast of dozens
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 <modulename>". 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
For Solaris versions 2.0 thru 2.3, use the module sunos5. For Solaris
versions 2.4 and higher (including 2.5 and 2.5.1) use the module sunos54.
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!

23
contrib/top/Make.desc.X Normal file
View File

@ -0,0 +1,23 @@
# 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, Argonne National Laboratory
# (formerly of Northwestern University and Rice University)
# DO NOT EDIT "Make.desc"!!! Make changes to "Make.desc.X",
# then "make veryclean", then run "Configure".
DESCS=%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

130
contrib/top/Makefile.X Normal file
View File

@ -0,0 +1,130 @@
# Makefile for "top", a top 10 process display for Unix
#
# This makefile is for top, version 3
#
# Written by William LeFebvre, Argonne National Laboratory
# (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 - <signal.h> or <sys/signal.h>; 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%
TARFILES = README INSTALL DISCLAIMER FAQ ADVERTISEMENT \
Changes Configure Porting \
Makefile.X Make.desc.X getans install \
top.c commands.c display.c screen.c username.c \
utils.c version.c getopt.c prime.c \
boolean.h display.h layout.h loadavg.h screen.h \
machine.h patchlevel.h top.h top.local.H os.h utils.h \
sigconv.awk top.X m-template metatop \
machine
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%
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) -o top $(OBJS) $(TERMCAP) -lm $(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 utils.h
display.o: boolean.h display.h layout.h screen.h top.h top.local.h utils.h
machine.o: top.h machine.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
# automatically built include file
sigdesc.h: sigconv.awk $(SIGNAL)
$(AWK) -f sigconv.awk $(SIGNAL) >sigdesc.h
tar:
rm -f top.tar machine/*.desc machine/*~
tar cvf top.tar $(TARFILES)
shar:
rm -f top.shar* machine/*.desc
makekit -ntop.shar. -t"Now read README and INSTALL, then run Configure" machine $(TARFILES)/*
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)

165
contrib/top/Porting Normal file
View File

@ -0,0 +1,165 @@
Instructions for porting top to other architectures.
This is still a preliminary document. Suggestions for improvement are
most welcome.
My address is now "lefebvre@dis.anl.gov".
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
centralized, (3) I want to loosely track the various porting efforts.
You do not need to wait for an "okay", but I do want to know that you
are working on it. And of course, once it is finished, please send me
the module files so that I can add them to the main distribution!
----------
There is one set of functions which extract all the information that
top needs for display. These functions are collected in to one file.
To make top work on a different architecture simply requires a
different implementation of these functions. The functions for a
given architecture "foo" are stored in a file called "m_foo.c". The
Configure script looks for these files and lets the configurer choose
one of them. This file is called a "module". The idea is that making
top work on a different machine only requires one additional file and
does not require changes to any existing files.
A module template is included in the distribution, called "m-template".
To write your own module, it is a good idea to start with this template.
If you architecture is similar to one for which a module already
exists, then you can start with that module instead. If you do so,
remember to change the "AUTHOR" section at the top!
The first comment in a module contains information which is extracted
and used by Configure. This information is marked with words in all
capitals (such as "SYNOPSIS:" and "LIBS:"). Go look at m-template: it
is fairly self-explanatory. The text after "LIBS:" (on the same line)
is extracted and included in the LIBS definition of the Makefile so
that extra libraries which may be necessary on some machines (such as
"-lkvm") can be specified in the module. The text after "CFLAGS:"
(on the same line) is extracted and included as flags in the "CFLAGS"
definition of the Makefile (thus in every compilation step). This is
used for rare circumstances only: please don't abuse this hook.
Some operating systems have idiosyncrasies which will affect the form
and/or content of the information top displays. You may wish to
document such anomalies in the top man page. This can be done by adding
a file called m_{modulename}.man (where {modulename} is replaced with
the name of the module). Configure will automatically add this file to
the end of the man page. See m_sunos4.man for an example.
A module is concerned with two structures:
The statics struct is filled in by machine_init. Each item is a
pointer to a list of character pointers. The list is terminated
with a null pointer.
struct statics
{
char **procstate_names; /* process state names */
char **cpustate_names; /* cpu state names */
char **memory_names; /* memory information names */
};
The system_info struct is filled in by get_system_info and
get_process_info.
struct system_info
{
int last_pid; /* last pid assigned (0 means non-sequential assignment) */
double load_avg[NUM_AVERAGES]; /* see below */
int p_total; /* total number of processes */
int p_active; /* number of procs considered "active" */
int *procstates; /* array of process state counters */
int *cpustates; /* array of cpustate counters */
int *memory; /* memory information */
};
The last three pointers each point to an array of integers. The
length of the array is determined by the length of the corresponding
_names array in the statics structure. Furthermore, if an entry in a
_names array is the empty string ("") then the corresponding value in
the value array will be skipped over. The display routine displays,
for example, the string procstate_names[0] then the number
procstates[0], then procstate_names[1], procstates[1], etc. until
procstate_names[N] == NULL. This allows for a tremendous amount of
flexibility in labeling the displayed values.
"procstates" and "memory" are displayed as straight integer values.
Values in "cpustates" are displayed as a percentage * 10. For
example, the (integer) value 105 is displayed as 10.5%.
These routines must be defined by the machine dependent module.
int machine_init(struct statics *)
returns 0 on success and -1 on failure,
prints error messages
char *format_header(char *)
Returns a string which should be used as the header for the
process display area. The argument is a string used to label
the username column (either "USERNAME" or "UID") and is always
8 characters in length.
void get_system_info(struct system_info *)
caddr_t get_process_info(struct system_info *, int, int, int (*func)())
returns a handle to use with format_next_process
char *format_next_process(caddr_t, char *(*func)())
returns string which describes next process
int proc_compare(caddr_t, caddr_t)
qsort comparison function
uid_t proc_owner(pid_t)
Returns the uid owner of the process specified by the pid argument.
This function is VERY IMPORTANT. If it fails to do its job, then
top may pose a security risk.
get_process_info is called immediately after get_system_info. In
fact, the two functions could be rolled in to one. The reason they
are not is mostly historical.
Top relies on the existence of a function called "setpriority" to
change a process's priority. This exists as a kernel call on most 4.3
BSD derived Unixes. If neither your operating system nor your C
library supplies such a function, then you will need to add one to the
module. It is defined as follows:
int setpriority (int dummy, int who, int niceval)
For the purposes of top, the first argument is meaningless.
The second is the pid and the third is the new nice value.
This function should behave just like a kernel call, setting
errno and returning -1 in case of an error. This function MUST
check to make sure that a non-root user does not specify a nice
value less than the process's current value. If it detects such
a condition, it should set errno to EACCES and return -1.
Other possible ERRNO values: ESRCH when pid "who" does not exist,
EPERM when the invoker is not root and not the same as the
process owner.
Note that top checks process ownership and should never call setpriority
when the invoker's uid is not root and not the same as the process's owner
uid.
The file "machine.h" contains definitions which are useful to modules
and to top.c (such as the structure definitions). You SHOULD NOT need
to change it when porting to a new platform.
Porting to a new platform should NOT require any changes to existing
files. You should only need to add m_ files. If you feel you need a
change in one of the existing files, please contact me so that we can
discuss the details. I want to keep such changes as general as
possible.

178
contrib/top/README Normal file
View File

@ -0,0 +1,178 @@
TOP
Version 3.4
William LeFebvre
and a cast of dozens
If you do not want to read this entire file, then at least read
the section at the end entitled "KNOWN PROBLEMS".
If you are having any problems getting top to work, please read the
file "FAQ" *before* contacting me. Thank you.
"top" is a program that will give continual reports about the state of
the system, including a list of the top cpu using processes. Version 3
of "top" has three primary design goals: provide an accurate snapshot of
the system and process state, not be one of the top processes itself, be
as portable as possible.
Version 3 has many bug fixes from version 2.5, and it has also been
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.
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.
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.
Be sure to read the FAQ enclosed with the distrubution. It contains
answers to the most commonly asked questions about the configuration,
installation, and operation of top.
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 two best "top" archive sites:
<A HREF="ftp://ftp.groupsys.com/pub/top>Top archive (groupsys.com)</A>
<A HREF="ftp://eecs.nwu.edu/pub/top>Top archive (eecs.nwu.edu)</A>
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.
KNOWN PROBLEMS:
Gnu CC
Compiling via Gnu CC continued to be the source of most of the
questions I receive. By far the most common mistake made by those
attempting to compile top with Gnu CC is out of date include files.
When the operating system is upgraded, the include files that are part
of the gcc package MUST also be updated. Gcc maintains its own
include files. Even a minor OS upgrade can involve changes to some of
the kernel's internal data structures, which are defined in include
files in "sys". Top is very sensitive to these changes. If you are
compiling with gcc and experience any sort of strange problems, please
make sure the include files you are using are up to date BEFORE
sending me a bug report. Look in the gcc source distribution for the
shell script "fixincludes".
HP/UX 10.10
In their infinite wisdom, the folks at HP have decided that mere mortals
such as you and I don't need to know what the kernel's proc structure looks
like. To that end, they have removed all useful content from the include
file <sys/proc.h> in version 10.10. As a result, top will not compile
under 10.10. What HP is trying to accomplish with this move is to force
iconoclasts such as myself to use "pstat" for collecting all process
information. I have no immediate solution for this problem, but hope to
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
My perpetual thanks to all the people who have helped me support top
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 <boucher@sofkin.ca>
Marc Cohen <marc@aai.com>
David Cutter <dpc@grail.com>
Casper Dik <casper@fwi.uva.nl>
Charles Hedrick <hedrick@geneva.rutgers.edu>
Andrew Herbert <andrew@werple.apana.org.au>
Jeff Janvrin <jeff.janvrin@columbiasc.ncr.com>
Torsten Kasch <torsten@techfak.uni-bielefeld.de>
Petri Kutvonen <kutvonen@cs.helsinki.fi>
William L. Jones <jones@chpc>
Tim Pugh <tpugh@oce.orst.edu>
Steve Scherf <scherf@swdc.stratus.com>
Phillip Wu <pwu01@qantek.com.au>
(My apologies if I missed anyone.)
AUTHOR
William LeFebvre
Group sys Consulting
wnl@groupsys.com
U.S. Mail address:
William LeFebvre
170 Colton Crest Drive
Alpharetta, GA 30202
(770) 813-3224

5
contrib/top/boolean.h Normal file
View File

@ -0,0 +1,5 @@
/* My favorite names for boolean values */
#define No 0
#define Yes 1
#define Maybe 2 /* tri-state boolean, actually */

509
contrib/top/commands.c Normal file
View File

@ -0,0 +1,509 @@
/*
* 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
*/
/*
* 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
* "top" (i.e.: changing the number of processes to display).
*/
#include "os.h"
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "sigdesc.h" /* generated automatically */
#include "boolean.h"
#include "utils.h"
extern int errno;
extern char *copyright;
/* imported from screen.c */
extern int overstrike;
int err_compar();
char *err_string();
/*
* show_help() - display the help screen; invoked in response to
* either 'h' or '?'.
*/
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("\
d - change number of displays to show\n\
e - list errors generated by last \"kill\" or \"renice\" command\n\
i - toggle the displaying of idle processes\n\
I - same as 'i'\n\
k - kill processes; send a signal to a list of processes\n\
n or # - change number of processes to display\n", stdout);
#ifdef ORDER
fputs("\
o - specify sort order (size, res, cpu, time)\n", stdout);
#endif
fputs("\
r - renice a process\n\
s - change number of seconds to delay between updates\n\
u - display processes for only one user (+ selects all users)\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);
}
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);
}
/*
* 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
* the number of errors, so we arbitrarily choose 20.
*/
#define ERRMAX 20
struct errs /* structure for a system-call error */
{
int errno; /* value of errno (that is, the actual error) */
char *arg; /* argument that caused the error */
};
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 \
{ \
errs[errcnt].arg = (p); \
errs[errcnt++].errno = (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".
*/
#define STRMAX 80
char *err_string()
{
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;
/* loop thru the sorted list, building an error string */
while (cnt < errcnt)
{
errp = &(errs[cnt++]);
if (errp->errno != 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->errno;
first = Yes;
}
if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
{
return(err_listem);
}
first = No;
}
/* add final message */
stringlen = str_adderr(string, stringlen, currerr);
/* return the error string */
return(stringlen == 0 ? err_listem : string);
}
/*
* str_adderr(str, len, err) - add an explanation of error "err" to
* the string "str".
*/
str_adderr(str, len, err)
char *str;
int len;
int err;
{
register char *msg;
register int msglen;
msg = err == 0 ? "Not a number" : errmsg(err);
msglen = strlen(msg) + 2;
if (len <= msglen)
{
return(0);
}
(void) strcat(str, ": ");
(void) strcat(str, msg);
return(len - msglen);
}
/*
* 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).
*/
str_addarg(str, len, arg, first)
char *str;
int len;
char *arg;
int first;
{
register int arglen;
arglen = strlen(arg);
if (!first)
{
arglen += 2;
}
if (len <= arglen)
{
return(0);
}
if (!first)
{
(void) strcat(str, ", ");
}
(void) strcat(str, arg);
return(len - arglen);
}
/*
* err_compar(p1, p2) - comparison routine used by "qsort"
* for sorting errors.
*/
err_compar(p1, p2)
register struct errs *p1, *p2;
{
register int result;
if ((result = p1->errno - p2->errno) == 0)
{
return(strcmp(p1->arg, p2->arg));
}
return(result);
}
/*
* error_count() - return the number of errors currently logged.
*/
error_count()
{
return(errcnt);
}
/*
* show_errors() - display on stdout the current log of errors.
*/
show_errors()
{
register int cnt = 0;
register struct errs *errp = errs;
printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
while (cnt++ < errcnt)
{
printf("%5s: %s\n", errp->arg,
errp->errno == 0 ? "Not a number" : errmsg(errp->errno));
errp++;
}
}
/*
* kill_procs(str) - send signals to processes, much like the "kill"
* command does; invoked in response to 'k'.
*/
char *kill_procs(str)
char *str;
{
register char *nptr;
int signum = SIGTERM; /* default */
int procnum;
struct sigdesc *sigp;
int uid;
/* reset error array */
ERR_RESET;
/* remember our uid */
uid = getuid();
/* skip over leading white space */
while (isspace(*str)) str++;
if (str[0] == '-')
{
/* explicit signal specified */
if ((nptr = next_field(str)) == NULL)
{
return(" kill: no processes specified");
}
if (isdigit(str[1]))
{
(void) scanint(str + 1, &signum);
if (signum <= 0 || signum >= NSIG)
{
return(" invalid signal number");
}
}
else
{
/* translate the name into a number */
for (sigp = sigdesc; sigp->name != NULL; sigp++)
{
if (strcmp(sigp->name, str + 1) == 0)
{
signum = sigp->number;
break;
}
}
/* was it ever found */
if (sigp->name == NULL)
{
return(" bad signal name");
}
}
/* put the new pointer in place */
str = nptr;
}
/* loop thru the string, killing processes */
do
{
if (scanint(str, &procnum) == -1)
{
ERROR(str, 0);
}
else
{
/* check process owner if we're not root */
if (uid && (uid != proc_owner(procnum)))
{
ERROR(str, EACCES);
}
/* go in for the kill */
else if (kill(procnum, signum) == -1)
{
/* chalk up an error */
ERROR(str, errno);
}
}
} while ((str = next_field(str)) != NULL);
/* return appropriate error string */
return(err_string());
}
/*
* renice_procs(str) - change the "nice" of processes, much like the
* "renice" command does; invoked in response to 'r'.
*/
char *renice_procs(str)
char *str;
{
register char negate;
int prio;
int procnum;
int uid;
ERR_RESET;
uid = getuid();
/* allow for negative priority values */
if ((negate = (*str == '-')) != 0)
{
/* move past the minus sign */
str++;
}
/* use procnum as a temporary holding place and get the number */
procnum = scanint(str, &prio);
/* negate if necessary */
if (negate)
{
prio = -prio;
}
#if defined(PRIO_MIN) && defined(PRIO_MAX)
/* check for validity */
if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
{
return(" bad priority value");
}
#endif
/* move to the first process number */
if ((str = next_field(str)) == NULL)
{
return(" no processes specified");
}
/* loop thru the process numbers, renicing each one */
do
{
if (scanint(str, &procnum) == -1)
{
ERROR(str, 0);
}
/* check process owner if we're not root */
else if (uid && (uid != proc_owner(procnum)))
{
ERROR(str, EACCES);
}
else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
{
ERROR(str, errno);
}
} while ((str = next_field(str)) != NULL);
/* return appropriate error string */
return(err_string());
}

1129
contrib/top/display.c Normal file

File diff suppressed because it is too large Load Diff

7
contrib/top/display.h Normal file
View File

@ -0,0 +1,7 @@
/* constants needed for display.c */
/* "type" argument for new_message function */
#define MT_standout 1
#define MT_delayed 2

86
contrib/top/getans Executable file
View File

@ -0,0 +1,86 @@
#!/bin/csh -f
set ny = (no yes)
if ($2 == "yesno") then
@ i = $3 + 1
set pmpt = "$1 [$ny[$i]]: "
else
if ("$3" == "") then
set pmpt = "${1}"
else
set pmpt = "$1 [$3]: "
endif
endif
rpt:
echo -n "$pmpt"
set input = $<
switch ($2)
case number:
set tmp = `echo $input | tr -d 0123456789.`
if ("x$tmp" != x) then
echo "Invalid number. Please try again."
goto rpt
endif
breaksw
case integer:
set tmp = `echo $input | tr -d 0123456789`
if ("x$tmp" != x) then
echo "Invalid integer. Please try again."
goto rpt
endif
breaksw
case neginteger:
if ("x$input" != x-1) then
set tmp = `echo $input | tr -d 0123456789`
if ("x$tmp" != x) then
echo "Invalid integer. Please try again."
goto rpt
endif
endif
breaksw
case file:
if ("x$input" == "x") then
set input = $3
endif
if (! -e "$input") then
echo The file $input "does not exist. Please try again."
goto rpt
endif
breaksw
case path:
if ("x$input" == "x") then
set input = "$3"
endif
if (! -e "$input") then
foreach elt ($path)
if (-e "$elt/$input") breaksw
end
echo The command $input "was not found. Please try again."
goto rpt
endif
breaksw
case yesno:
if ("x$input" == xy || "x$input" == xyes) then
set input = 1
else if ("x$input" == xn || "x$input" == xno) then
set input = 0
else if ("x$input" != x) then
echo 'Please answer "yes" or "no".'
goto rpt
endif
breaksw
default:
breaksw
endsw
if ("x$input" == x) then
set input = "$3"
endif
echo $input > $4

90
contrib/top/getopt.c Normal file
View File

@ -0,0 +1,90 @@
/*
* "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 */

69
contrib/top/install Executable file
View File

@ -0,0 +1,69 @@
#!/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

27
contrib/top/layout.h Normal file
View File

@ -0,0 +1,27 @@
/*
* 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.
*/
#define x_lastpid 10
#define y_lastpid 0
#define x_loadave 33
#define x_loadave_nompid 15
#define y_loadave 0
#define x_procstate 0
#define y_procstate 1
#define x_brkdn 15
#define y_brkdn 1
#define x_mem 8
#define y_mem 3
#define y_message 4
#define x_header 0
#define y_header 5
#define x_idlecursor 0
#define y_idlecursor 4
#define y_procs 6
#define y_cpustates 2

57
contrib/top/loadavg.h Normal file
View File

@ -0,0 +1,57 @@
/*
* Top - a top users display for Berkeley Unix
*
* Defines required to access load average figures.
*
* This include file sets up everything we need to access the load average
* values in the kernel in a machine independent way. First, it sets the
* typedef "load_avg" to be either double or long (depending on what is
* needed), then it defines these macros appropriately:
*
* loaddouble(la) - convert load_avg to double.
* intload(i) - convert integer to load_avg.
*/
/*
* We assume that if FSCALE is defined, then avenrun and ccpu are type long.
* If your machine is an exception (mips, perhaps?) then make adjustments
* here.
*
* Defined types: load_avg for load averages, pctcpu for cpu percentages.
*/
#if defined(mips) && !defined(NetBSD)
# include <sys/fixpoint.h>
# if defined(FBITS) && !defined(FSCALE)
# define FSCALE (1 << FBITS) /* mips */
# endif
#endif
#ifdef FSCALE
# define FIXED_LOADAVG FSCALE
# define FIXED_PCTCPU FSCALE
#endif
#ifdef ibm032
# undef FIXED_LOADAVG
# undef FIXED_PCTCPU
# define FIXED_PCTCPU PCT_SCALE
#endif
#ifdef FIXED_PCTCPU
typedef long pctcpu;
# define pctdouble(p) ((double)(p) / FIXED_PCTCPU)
#else
typedef double pctcpu;
# define pctdouble(p) (p)
#endif
#ifdef FIXED_LOADAVG
typedef long load_avg;
# define loaddouble(la) ((double)(la) / FIXED_LOADAVG)
# define intload(i) ((int)((i) * FIXED_LOADAVG))
#else
typedef double load_avg;
# define loaddouble(la) (la)
# define intload(i) ((double)(i))
#endif

241
contrib/top/m-template Normal file
View File

@ -0,0 +1,241 @@
/*
* 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 <your@internet.address>
*/
#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);
}

58
contrib/top/machine.h Normal file
View File

@ -0,0 +1,58 @@
/*
* This file defines the interface between top and the machine-dependent
* module. It is NOT machine dependent and should not need to be changed
* for any specific machine.
*/
/*
* the statics struct is filled in by machine_init
*/
struct statics
{
char **procstate_names;
char **cpustate_names;
char **memory_names;
#ifdef ORDER
char **order_names;
#endif
};
/*
* the system_info struct is filled in by a machine dependent routine.
*/
struct system_info
{
int last_pid;
double load_avg[NUM_AVERAGES];
int p_total;
int p_active; /* number of procs considered "active" */
int *procstates;
int *cpustates;
int *memory;
};
/* cpu_states is an array of percentages * 10. For example,
the (integer) value 105 is 10.5% (or .105).
*/
/*
* the process_select struct tells get_process_info what processes we
* are interested in seeing
*/
struct process_select
{
int idle; /* show idle processes */
int system; /* show system processes */
int uid; /* only this uid (unless uid == -1) */
char *command; /* only this command (unless == NULL) */
};
/* routines defined by the machine dependent module */
char *format_header();
char *format_next_process();
/* non-int routines typically used by the machine dependent module */
char *printable();

25
contrib/top/metatop Executable file
View File

@ -0,0 +1,25 @@
#! /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` "$@"

29
contrib/top/os.h Normal file
View File

@ -0,0 +1,29 @@
#include <sys/types.h>
#include <sys/param.h> /* This defines BSD */
#if defined(BSD) && !defined(BSD4_4) && !defined(__osf__)
# include <stdio.h>
# include <strings.h>
# 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;
#else
typedef int sigret_t;
#endif
/* system routines that don't return int */
char *getenv();
caddr_t malloc();
#else
# include <stdio.h>
# define setbuffer(f, b, s) setvbuf((f), (b), (b) ? _IOFBF : _IONBF, (s))
# include <string.h>
# include <memory.h>
# include <stdlib.h>
# define memzero(a, b) memset((a), 0, (b))
typedef void sigret_t;
#endif

1
contrib/top/patchlevel.h Normal file
View File

@ -0,0 +1 @@
#define PATCHLEVEL 4

40
contrib/top/prime.c Normal file
View File

@ -0,0 +1,40 @@
/*
* Prime number generator. It prints on stdout the next prime number
* higher than the number specified as argv[1].
*/
#include <math.h>
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<i; j++)
{
if ((i/j)==floor(i/j))
{
f=0;
break;
}
}
if (f)
{
printf("%.0f\n", i);
exit(0);
}
}
}

494
contrib/top/screen.c Normal file
View File

@ -0,0 +1,494 @@
/*
* 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
*/
/* This file contains the routines that interface to termcap and stty/gtty.
*
* Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
*
* I put in code to turn on the TOSTOP bit while top was running, but I
* didn't really like the results. If you desire it, turn on the
* preprocessor variable "TOStop". --wnl
*/
#include "os.h"
#include "top.h"
#include <sys/ioctl.h>
#ifdef CBREAK
# include <sgtty.h>
# define SGTTY
#else
# ifdef TCGETA
# define TERMIO
# include <termio.h>
# else
# define TERMIOS
# include <termios.h>
# endif
#endif
#if defined(TERMIO) || defined(TERMIOS)
# ifndef TAB3
# ifdef OXTABS
# define TAB3 OXTABS
# else
# define TAB3 0
# endif
# endif
#endif
#include "screen.h"
#include "boolean.h"
extern char *myname;
int putstdout();
int overstrike;
int screen_length;
int screen_width;
char ch_erase;
char ch_kill;
char smart_terminal;
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;
short ospeed;
#ifdef SGTTY
static struct sgttyb old_settings;
static struct sgttyb new_settings;
#endif
#ifdef TERMIO
static struct termio old_settings;
static struct termio new_settings;
#endif
#ifdef TERMIOS
static struct termios old_settings;
static struct termios new_settings;
#endif
static char is_a_terminal = No;
#ifdef TOStop
static int old_lword;
static int new_lword;
#endif
#define STDIN 0
#define STDOUT 1
#define STDERR 2
init_termcap(interactive)
int interactive;
{
char *bufptr;
char *PCptr;
char *term_name;
char *getenv();
int status;
/* set defaults in case we aren't smart */
screen_width = MAX_COLS;
screen_length = 0;
if (!interactive)
{
/* pretend we have a dumb terminal */
smart_terminal = No;
return;
}
/* assume we have a smart terminal until proven otherwise */
smart_terminal = Yes;
/* get the terminal name */
term_name = getenv("TERM");
/* if there is no TERM, assume it's a dumb terminal */
/* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
if (term_name == NULL)
{
smart_terminal = No;
return;
}
/* now get the termcap entry */
if ((status = tgetent(termcap_buf, term_name)) != 1)
{
if (status == -1)
{
fprintf(stderr, "%s: can't open termcap file\n", myname);
}
else
{
fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
myname, term_name);
}
/* pretend it's dumb and proceed */
smart_terminal = No;
return;
}
/* "hardcopy" immediately indicates a very stupid terminal */
if (tgetflag("hc"))
{
smart_terminal = No;
return;
}
/* set up common terminal capabilities */
if ((screen_length = tgetnum("li")) <= 0)
{
screen_length = smart_terminal = 0;
return;
}
/* screen_width is a little different */
if ((screen_width = tgetnum("co")) == -1)
{
screen_width = 79;
}
else
{
screen_width -= 1;
}
/* terminals that overstrike need special attention */
overstrike = tgetflag("os");
/* initialize the pointer into the termcap string buffer */
bufptr = string_buffer;
/* get "ce", clear to end */
if (!overstrike)
{
clear_line = tgetstr("ce", &bufptr);
}
/* get necessary capabilities */
if ((clear_screen = tgetstr("cl", &bufptr)) == NULL ||
(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
{
smart_terminal = No;
return;
}
/* get some more sophisticated stuff -- these are optional */
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);
/* pad character */
PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
/* set convenience strings */
(void) strcpy(home, tgoto(cursor_motion, 0, 0));
/* (lower_left is set in get_screensize) */
/* 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();
/* if stdout is not a terminal, pretend we are a dumb terminal */
#ifdef SGTTY
if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1)
{
smart_terminal = No;
}
#endif
#ifdef TERMIO
if (ioctl(STDOUT, TCGETA, &old_settings) == -1)
{
smart_terminal = No;
}
#endif
#ifdef TERMIOS
if (tcgetattr(STDOUT, &old_settings) == -1)
{
smart_terminal = No;
}
#endif
}
init_screen()
{
/* get the old settings for safe keeping */
#ifdef SGTTY
if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1)
{
/* copy the settings so we can modify them */
new_settings = old_settings;
/* turn on CBREAK and turn off character echo and tab expansion */
new_settings.sg_flags |= CBREAK;
new_settings.sg_flags &= ~(ECHO|XTABS);
(void) ioctl(STDOUT, TIOCSETP, &new_settings);
/* remember the erase and kill characters */
ch_erase = old_settings.sg_erase;
ch_kill = old_settings.sg_kill;
#ifdef TOStop
/* get the local mode word */
(void) ioctl(STDOUT, TIOCLGET, &old_lword);
/* modify it */
new_lword = old_lword | LTOSTOP;
(void) ioctl(STDOUT, TIOCLSET, &new_lword);
#endif
/* remember that it really is a terminal */
is_a_terminal = Yes;
/* send the termcap initialization string */
putcap(terminal_init);
}
#endif
#ifdef TERMIO
if (ioctl(STDOUT, TCGETA, &old_settings) != -1)
{
/* copy the settings so we can modify them */
new_settings = old_settings;
/* turn off ICANON, character echo and tab expansion */
new_settings.c_lflag &= ~(ICANON|ECHO);
new_settings.c_oflag &= ~(TAB3);
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
(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];
/* remember that it really is a terminal */
is_a_terminal = Yes;
/* send the termcap initialization string */
putcap(terminal_init);
}
#endif
#ifdef TERMIOS
if (tcgetattr(STDOUT, &old_settings) != -1)
{
/* copy the settings so we can modify them */
new_settings = old_settings;
/* turn off ICANON, character echo and tab expansion */
new_settings.c_lflag &= ~(ICANON|ECHO);
new_settings.c_oflag &= ~(TAB3);
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
(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];
/* remember that it really is a terminal */
is_a_terminal = Yes;
/* send the termcap initialization string */
putcap(terminal_init);
}
#endif
if (!is_a_terminal)
{
/* not a terminal at all---consider it dumb */
smart_terminal = No;
}
}
end_screen()
{
/* move to the lower left, clear the line and send "te" */
if (smart_terminal)
{
putcap(lower_left);
putcap(clear_line);
fflush(stdout);
putcap(terminal_end);
}
/* if we have settings to reset, then do so */
if (is_a_terminal)
{
#ifdef SGTTY
(void) ioctl(STDOUT, TIOCSETP, &old_settings);
#ifdef TOStop
(void) ioctl(STDOUT, TIOCLSET, &old_lword);
#endif
#endif
#ifdef TERMIO
(void) ioctl(STDOUT, TCSETA, &old_settings);
#endif
#ifdef TERMIOS
(void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
#endif
}
}
reinit_screen()
{
/* install our settings if it is a terminal */
if (is_a_terminal)
{
#ifdef SGTTY
(void) ioctl(STDOUT, TIOCSETP, &new_settings);
#ifdef TOStop
(void) ioctl(STDOUT, TIOCLSET, &new_lword);
#endif
#endif
#ifdef TERMIO
(void) ioctl(STDOUT, TCSETA, &new_settings);
#endif
#ifdef TERMIOS
(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
#endif
}
/* send init string */
if (smart_terminal)
{
putcap(terminal_init);
}
}
get_screensize()
{
#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) strcpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1));
}
standout(msg)
char *msg;
{
if (smart_terminal)
{
putcap(start_standout);
fputs(msg, stdout);
putcap(end_standout);
}
else
{
fputs(msg, stdout);
}
}
clear()
{
if (smart_terminal)
{
putcap(clear_screen);
}
}
clear_eol(len)
int len;
{
if (smart_terminal && !overstrike && len > 0)
{
if (clear_line)
{
putcap(clear_line);
return(0);
}
else
{
while (len-- > 0)
{
putchar(' ');
}
return(1);
}
}
return(-1);
}
go_home()
{
if (smart_terminal)
{
putcap(home);
}
}
/* This has to be defined as a subroutine for tputs (instead of a macro) */
putstdout(ch)
char ch;
{
putchar(ch);
}

31
contrib/top/screen.h Normal file
View File

@ -0,0 +1,31 @@
/*
* top - a top users display for Unix 4.2
*
* This file contains all the definitions necessary to use the hand-written
* screen package in "screen.c"
*/
#define TCputs(str) tputs(str, 1, putstdout)
#define putcap(str) (void)((str) != NULL ? TCputs(str) : 0)
#define Move_to(x, y) TCputs(tgoto(cursor_motion, x, y))
/* declare return values for termcap functions */
char *tgetstr();
char *tgoto();
extern char ch_erase; /* set to the user's erase character */
extern char ch_kill; /* set to the user's kill character */
extern char smart_terminal; /* set if the terminal has sufficient termcap
capabilities for normal operation */
/* These are some termcap strings for use outside of "screen.c" */
extern char *cursor_motion;
extern char *clear_line;
extern char *clear_to_end;
/* rows and columns on the screen according to termcap */
extern int screen_length;
extern int screen_width;
/* a function that puts a single character on stdout */
int putstdout();

53
contrib/top/sigconv.awk Normal file
View File

@ -0,0 +1,53 @@
BEGIN {
nsig = 0;
j = 0;
print "/* This file was automatically generated */"
print "/* by the awk script \"sigconv.awk\". */\n"
print "struct sigdesc {"
print " char *name;"
print " int number;"
print "};\n"
print "struct sigdesc sigdesc[] = {"
}
/^#define[ \t][ \t]*SIG[A-Z]/ {
j = sprintf("%d", $3);
str = $2;
if (nsig < j)
nsig = j;
siglist[j] = sprintf("\"%s\",\t%2d,", \
substr(str, 4), j);
}
/^#[ \t]*define[ \t][ \t]*SIG[A-Z]/ {
j = sprintf("%d", $4);
str = $3;
if (nsig < j)
nsig = j;
siglist[j] = sprintf("\"%s\",\t%2d,", \
substr(str, 4), j);
}
/^#[ \t]*define[ \t][ \t]*_SIG[A-Z]/ {
j = sprintf("%d", $4);
str = $3;
if (nsig < j)
nsig = j;
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(" NULL,\t 0\n};\n");
}

324
contrib/top/top.X Normal file
View File

@ -0,0 +1,324 @@
.\" NOTE: changes to the manual page for "top" should be made in the
.\" file "top.X" and NOT in the file "top.1".
.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 \-SbiInqu
] [
.BI \-d count
] [
.BI \-s time
] [
.BI \-o field
] [
.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
.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 \-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 \-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 \-n
Use \*(lqnon-interactive\*(rq mode. This is indentical to \*(lqbatch\*(rq
mode.
.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
.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 \-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 \-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.
.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).
.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 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 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 e
Display a list of system errors (if any) generated by the last
.BR k ill
or
.BR r enice
command.
.TP
.B i
(or
.BR I)
Toggle the display of idle processes.
.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 physial 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, 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 \*(lqsleep\*(rq, \*(lqWAIT\*(rq,
\*(lqrun\*(rq, \*(lqidl\*(rq, \*(lqzomb\*(rq, or \*(lqstop\*(rq),
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<swapped>\*(rq).
.SH NOTES
The \*(lqABANDONED\*(rq state (known in the kernel as \*(lqSWAIT\*(rq) was
abandoned, thus the name. A process should never end up in this state.
.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
/vmunix 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)

996
contrib/top/top.c Normal file
View File

@ -0,0 +1,996 @@
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, 1990, 1992, William LeFebvre, Northwestern University
*/
/*
* See the file "Changes" for information on version-to-version changes.
*/
/*
* This file contains "main" and other high-level routines.
*/
/*
* 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
*/
#include "os.h"
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <sys/time.h>
/* includes specific to top */
#include "display.h" /* interface to display package */
#include "screen.h" /* interface to screen package */
#include "top.h"
#include "top.local.h"
#include "boolean.h"
#include "machine.h"
#include "utils.h"
/* Size of the stdio buffer given to stdout */
#define Buffersize 2048
/* The buffer that stdio will use */
char stdoutbuf[Buffersize];
/* build Signal masks */
#define Smask(s) (1 << ((s) - 1))
/* for system errors */
extern int errno;
/* for getopt: */
extern int optind;
extern char *optarg;
/* imported from screen.c */
extern int overstrike;
/* signal handling routines */
sigret_t leave();
sigret_t onalrm();
sigret_t tstop();
#ifdef SIGWINCH
sigret_t winch();
#endif
/* internal routines */
void quit();
/* values which need to be accessed by signal handlers */
static int max_topn; /* maximum displayable processes */
/* miscellaneous things */
char *myname = "top";
jmp_buf jmp_int;
/* routines that don't return int */
char *username();
char *ctime();
char *kill_procs();
char *renice_procs();
#ifdef ORDER
extern int (*proc_compares[])();
#else
extern int proc_compare();
#endif
time_t time();
caddr_t get_process_info();
/* different routines for displaying the user's identification */
/* (values assigned to get_userid) */
char *username();
char *itoa7();
/* display routines that need to be predeclared */
int i_loadave();
int u_loadave();
int i_procstates();
int u_procstates();
int i_cpustates();
int u_cpustates();
int i_memory();
int u_memory();
int i_message();
int u_message();
int i_header();
int u_header();
int i_process();
int u_process();
/* pointers to display routines */
int (*d_loadave)() = i_loadave;
int (*d_procstates)() = i_procstates;
int (*d_cpustates)() = i_cpustates;
int (*d_memory)() = i_memory;
int (*d_message)() = i_message;
int (*d_header)() = i_header;
int (*d_process)() = i_process;
main(argc, argv)
int argc;
char *argv[];
{
register int i;
register int active_procs;
register int change;
struct system_info system_info;
struct statics statics;
caddr_t processes;
static char tempbuf1[50];
static char tempbuf2[50];
int old_sigmask; /* only used for BSD-style signals */
int topn = Default_TOPN;
int delay = Default_DELAY;
int displays = 0; /* indicates unspecified */
time_t curr_time;
char *(*get_userid)() = username;
char *uname_field = "USERNAME";
char *header_text;
char *env_top;
char **preset_argv;
int preset_argc = 0;
char **av;
int ac;
char dostates = No;
char do_unames = Yes;
char interactive = Maybe;
char warnings = 0;
#if Default_TOPN == Infinity
char topn_specified = No;
#endif
char ch;
char *iptr;
char no_command = 1;
struct timeval timeout;
struct process_select ps;
#ifdef ORDER
char *order_name = NULL;
int order_index = 0;
#endif
#ifndef FD_SET
/* FD_SET and friends are not present: fake it */
typedef int fd_set;
#define FD_ZERO(x) (*(x) = 0)
#define FD_SET(f, x) (*(x) = f)
#endif
fd_set readfds;
#ifdef ORDER
static char command_chars[] = "\f qh?en#sdkriIuo";
#else
static char command_chars[] = "\f qh?en#sdkriIu";
#endif
/* these defines enumerate the "strchr"s of the commands in command_chars */
#define CMD_redraw 0
#define CMD_update 1
#define CMD_quit 2
#define CMD_help1 3
#define CMD_help2 4
#define CMD_OSLIMIT 4 /* terminals with OS can only handle commands */
#define CMD_errors 5 /* less than or equal to CMD_OSLIMIT */
#define CMD_number1 6
#define CMD_number2 7
#define CMD_delay 8
#define CMD_displays 9
#define CMD_kill 10
#define CMD_renice 11
#define CMD_idletog 12
#define CMD_idletog2 13
#define CMD_user 14
#ifdef ORDER
#define CMD_order 15
#endif
/* set the buffer for stdout */
#ifdef DEBUG
setbuffer(stdout, NULL, 0);
#else
setbuffer(stdout, stdoutbuf, Buffersize);
#endif
/* get our name */
if (argc > 0)
{
if ((myname = strrchr(argv[0], '/')) == 0)
{
myname = argv[0];
}
else
{
myname++;
}
}
/* initialize some selection options */
ps.idle = Yes;
ps.system = No;
ps.uid = -1;
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, "SIbinqus:d:U:o:")) != EOF)
{
switch(i)
{
case 'u': /* toggle uid/username display */
do_unames = !do_unames;
break;
case 'U': /* display only username's processes */
if ((ps.uid = userid(optarg)) == -1)
{
fprintf(stderr, "%s: unknown user\n", optarg);
exit(1);
}
break;
case 'S': /* show system processes */
ps.system = !ps.system;
break;
case 'I': /* show idle processes */
ps.idle = !ps.idle;
break;
case 'i': /* go interactive regardless */
interactive = Yes;
break;
case 'n': /* batch, or non-interactive */
case 'b':
interactive = No;
break;
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)
{
fprintf(stderr,
"%s: warning: seconds delay should be non-negative -- using default\n",
myname);
delay = Default_DELAY;
warnings++;
}
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
{
fprintf(stderr,
"%s: warning: `-q' option can only be used by root\n",
myname);
warnings++;
}
break;
case 'o': /* select sort order */
#ifdef ORDER
order_name = optarg;
#else
fprintf(stderr,
"%s: this platform does not support arbitrary ordering. Sorry.\n",
myname);
warnings++;
#endif
break;
default:
fprintf(stderr, "\
Top version %s\n\
Usage: %s [-ISbinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
version_string(), myname);
exit(1);
}
}
/* get count of top processes to display (if any) */
if (optind < ac)
{
if ((topn = atoiwi(av[optind])) == Invalid)
{
fprintf(stderr,
"%s: warning: process display count should be non-negative -- using default\n",
myname);
warnings++;
}
#if Default_TOPN == Infinity
else
{
topn_specified = Yes;
}
#endif
}
/* tricky: remember old value of preset_argc & set preset_argc = 0 */
i = preset_argc;
preset_argc = 0;
/* 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;
}
/* initialize the kernel memory interface */
if (machine_init(&statics) == -1)
{
exit(1);
}
#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)
{
fprintf(stderr, " %s", *pp++);
}
fputc('\n', stderr);
exit(1);
}
}
#endif
#ifdef no_initialization_needed
/* initialize the hashing stuff */
if (do_unames)
{
init_hash();
}
#endif
/* initialize termcap */
init_termcap(interactive);
/* get the string to use for the process area header */
header_text = format_header(uname_field);
/* 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
}
/* set header display accordingly */
display_header(topn > 0);
/* determine interactive state */
if (interactive == Maybe)
{
interactive = smart_terminal;
}
/* if # of displays not specified, fill it in */
if (displays == 0)
{
displays = smart_terminal ? Infinity : 1;
}
/* 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)
{
fputs("....", stderr);
fflush(stderr); /* why must I do this? */
sleep((unsigned)(3 * warnings));
fputc('\n', stderr);
}
/* setup the jump buffer for stops */
if (setjmp(jmp_int) != 0)
{
/* control ends up here after an interrupt */
reset_display();
}
/*
* main loop -- repeat while display count is positive or while it
* indicates infinity (by being -1)
*/
while ((displays == -1) || (displays-- > 0))
{
/* get the current stats */
get_system_info(&system_info);
/* get the current set of processes */
processes =
get_process_info(&system_info,
&ps,
#ifdef ORDER
proc_compares[order_index]);
#else
proc_compare);
#endif
/* 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_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 */
{
(*d_cpustates)(system_info.cpustates);
}
else
{
/* we'll do it next time */
if (smart_terminal)
{
z_cpustates();
}
else
{
putchar('\n');
}
dostates = Yes;
}
/* display memory stats */
(*d_memory)(system_info.memory);
/* handle message area */
(*d_message)();
/* update the header area */
(*d_header)(header_text);
if (topn > 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)
{
active_procs = topn;
}
if (active_procs > max_topn)
{
active_procs = max_topn;
}
/* now show the top "n" processes. */
for (i = 0; i < active_procs; i++)
{
(*d_process)(i, format_next_process(processes, get_userid));
}
}
else
{
i = 0;
}
/* do end-screen processing */
u_endscreen(i);
/* now, flush the output buffer */
fflush(stdout);
/* only do the rest if we have more displays to show */
if (displays)
{
/* switch out for new display on smart terminals */
if (smart_terminal)
{
if (overstrike)
{
reset_display();
}
else
{
d_loadave = u_loadave;
d_procstates = u_procstates;
d_cpustates = u_cpustates;
d_memory = u_memory;
d_message = u_message;
d_header = u_header;
d_process = u_process;
}
}
no_command = Yes;
if (!interactive)
{
/* set up alarm */
(void) signal(SIGALRM, onalrm);
(void) alarm((unsigned)delay);
/* wait for the rest of it .... */
pause();
}
else while (no_command)
{
/* assume valid command unless told otherwise */
no_command = No;
/* set up arguments for select with timeout */
FD_ZERO(&readfds);
FD_SET(1, &readfds); /* for standard input */
timeout.tv_sec = delay;
timeout.tv_usec = 0;
/* wait for either input or the end of the delay period */
if (select(32, &readfds, (fd_set *)NULL, (fd_set *)NULL, &timeout) > 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) */
(void) read(0, &ch, 1);
if ((iptr = strchr(command_chars, ch)) == NULL)
{
/* 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)
{
delay = i;
}
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, 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, 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_user:
new_message(MT_standout,
"Username to show: ");
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;
#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
default:
new_message(MT_standout, " BAD CASE IN SWITCH!");
putchar('\r');
}
}
/* flush out stuff that may have been written */
fflush(stdout);
}
}
}
}
quit(0);
/*NOTREACHED*/
}
/*
* reset_display() - reset all the display routine pointers so that entire
* screen will get redrawn.
*/
reset_display()
{
d_loadave = i_loadave;
d_procstates = i_procstates;
d_cpustates = i_cpustates;
d_memory = i_memory;
d_message = i_message;
d_header = i_header;
d_process = i_process;
}
/*
* signal handlers
*/
sigret_t leave() /* exit under normal conditions -- INT handler */
{
end_screen();
exit(0);
}
sigret_t tstop(i) /* SIGTSTP handler */
int i;
{
/* 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);
#else
(void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
#endif
(void) kill(0, SIGTSTP);
/* reset the signal handler */
(void) signal(SIGTSTP, tstop);
/* reinit screen */
reinit_screen();
/* jump to appropriate place */
longjmp(jmp_int, 1);
/*NOTREACHED*/
}
#ifdef SIGWINCH
sigret_t winch(i) /* SIGWINCH handler */
int i;
{
/* reascertain the screen dimensions */
get_screensize();
/* tell display to resize */
max_topn = display_resize();
/* reset the signal handler */
(void) signal(SIGWINCH, winch);
/* jump to appropriate place */
longjmp(jmp_int, 1);
}
#endif
void quit(status) /* exit under duress */
int status;
{
end_screen();
exit(status);
/*NOTREACHED*/
}
sigret_t onalrm() /* SIGALRM handler */
{
/* this is only used in batch mode to break out of the pause() */
/* return; */
}

36
contrib/top/top.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Top - a top users display for Berkeley Unix
*
* General (global) definitions
*/
/* Current major version number */
#define VERSION 3
/* Number of lines of header information on the standard screen */
#define Header_lines 6
/* Maximum number of columns allowed for display */
#define MAX_COLS 128
/* 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
/* maximum number we can have */
#define Largest 0x7fffffff
/*
* The entire display is based on these next numbers being defined as is.
*/
#define NUM_AVERAGES 3

68
contrib/top/top.local.H Normal file
View File

@ -0,0 +1,68 @@
/*
* 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

185
contrib/top/username.c Normal file
View File

@ -0,0 +1,185 @@
/*
* 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
*/
/*
* 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).
*
* 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.
*/
#include <stdio.h>
#include <pwd.h>
#include "top.local.h"
#include "utils.h"
struct hash_el {
int uid;
char name[9];
};
#define is_empty_hash(x) (hash_table[x].name[0] == 0)
/* 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];
init_hash()
{
/*
* 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.
*/
}
char *username(uid)
register int uid;
{
register int hashindex;
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;
/* Eventually we want this to enter everything in the hash table,
but for now we just do it simply and remember just the result.
*/
if ((pwd = getpwnam(username)) == 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)
register int uid;
register char *name;
int wecare; /* 1 = enter it always, 0 = nice to have */
{
register int hashindex;
#ifdef DEBUG
fprintf(stderr, "enter_hash(%d, %s, %d)\n", uid, name, wecare);
#endif
hashindex = hashit(uid);
if (!is_empty_hash(hashindex))
{
if (!wecare)
return 0; /* Don't clobber a slot for trash */
if (hash_table[hashindex].uid == uid)
return(hashindex); /* Fortuitous find */
}
/* empty or wrong slot -- fill it with new value */
hash_table[hashindex].uid = uid;
(void) strncpy(hash_table[hashindex].name, name, 8);
return(hashindex);
}
/*
* 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)
register int uid;
{
struct passwd *pwd;
#ifdef RANDOM_PW
/* no performance penalty for using getpwuid makes it easy */
if ((pwd = getpwuid(uid)) != NULL)
{
return(enter_user(pwd->pw_uid, pwd->pw_name, 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));
}

453
contrib/top/utils.c Normal file
View File

@ -0,0 +1,453 @@
/*
* 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
*/
/*
* This file contains various handy utilities used by top.
*/
#include "top.h"
#include "os.h"
int atoiwi(str)
char *str;
{
register int len;
len = strlen(str);
if (len != 0)
{
if (strncmp(str, "infinity", len) == 0 ||
strncmp(str, "all", len) == 0 ||
strncmp(str, "maximum", len) == 0)
{
return(Infinity);
}
else if (str[0] == '-')
{
return(Invalid);
}
else
{
return(atoi(str));
}
}
return(0);
}
/*
* itoa - convert integer (decimal) to ascii string for positive numbers
* only (we don't bother with negative numbers since we know we
* don't use them).
*/
/*
* How do we know that 16 will suffice?
* Because the biggest number that we will
* ever convert will be 2^32-1, which is 10
* digits.
*/
char *itoa(val)
register int val;
{
register char *ptr;
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);
*--ptr = '\0';
if (val == 0)
{
*--ptr = '0';
}
else while (val != 0)
{
*--ptr = (val % 10) + '0';
val /= 10;
}
return(ptr);
}
/*
* itoa7(val) - like itoa, except the number is right justified in a 7
* character field. This code is a duplication of itoa instead of
* a front end to a more general routine for efficiency.
*/
char *itoa7(val)
register int val;
{
register char *ptr;
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);
*--ptr = '\0';
if (val == 0)
{
*--ptr = '0';
}
else while (val != 0)
{
*--ptr = (val % 10) + '0';
val /= 10;
}
while (ptr > buffer + sizeof(buffer) - 7)
{
*--ptr = ' ';
}
return(ptr);
}
/*
* digits(val) - return number of decimal digits in val. Only works for
* positive numbers. If val <= 0 then digits(val) == 0.
*/
int digits(val)
int val;
{
register int cnt = 0;
while (val > 0)
{
cnt++;
val /= 10;
}
return(cnt);
}
/*
* strecpy(to, from) - copy string "from" into "to" and return a pointer
* to the END of the string "to".
*/
char *strecpy(to, from)
register char *to;
register char *from;
{
while ((*to++ = *from++) != '\0');
return(--to);
}
/*
* string_index(string, array) - find string in array and return index
*/
int string_index(string, array)
char *string;
char **array;
{
register int i = 0;
while (*array != NULL)
{
if (strcmp(string, *array) == 0)
{
return(i);
}
array++;
i++;
}
return(-1);
}
/*
* 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;
{
register char *from;
register char *to;
register int cnt;
register int ch;
int length;
int lastch;
register char **argv;
char **argarray;
char *args;
/* unfortunately, the only real way to do this is to go thru the
input string twice. */
/* step thru the string counting the white space sections */
from = line;
lastch = cnt = length = 0;
while ((ch = *from++) != '\0')
{
length++;
if (ch == ' ' && lastch != ' ')
{
cnt++;
}
lastch = ch;
}
/* add three to the count: one for the initial "dummy" argument,
one for the last argument and one for NULL */
cnt += 3;
/* allocate a char * array to hold the pointers */
argarray = (char **)malloc(cnt * sizeof(char *));
/* allocate another array to hold the strings themselves */
args = (char *)malloc(length+2);
/* initialization for main loop */
from = line;
to = args;
argv = argarray;
lastch = '\0';
/* create a dummy argument to keep getopt happy */
*argv++ = to;
*to++ = '\0';
cnt = 2;
/* now build argv while copying characters */
*argv++ = to;
while ((ch = *from++) != '\0')
{
if (ch != ' ')
{
if (lastch == ' ')
{
*to++ = '\0';
*argv++ = to;
cnt++;
}
*to++ = ch;
}
lastch = ch;
}
*to++ = '\0';
/* set cntp and return the allocated array */
*cntp = cnt;
return(argarray);
}
/*
* percentages(cnt, out, new, old, diffs) - calculate percentage change
* between array "old" and "new", putting the percentages i "out".
* "cnt" is size of each array and "diffs" is used for scratch space.
* The array "old" is updated on each call.
* The routine assumes modulo arithmetic. This function is especially
* 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;
{
register int i;
register long change;
register long total_change;
register long *dp;
long half_total;
/* initialization */
total_change = 0;
dp = diffs;
/* calculate changes for each state and the overall change */
for (i = 0; i < cnt; i++)
{
if ((change = *new - *old) < 0)
{
/* this only happens when the counter wraps */
change = (int)
((unsigned long)*new-(unsigned long)*old);
}
total_change += (*dp++ = change);
*old++ = *new++;
}
/* avoid divide by zero potential */
if (total_change == 0)
{
total_change = 1;
}
/* calculate percentages based on overall change, rounding up */
half_total = total_change / 2l;
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 */
return(total_change);
}
/*
* errmsg(errnum) - return an error message string appropriate to the
* error number "errnum". This is a substitute for the System V
* function "strerror" with one important difference: the string
* returned by this function does NOT end in a newline!
* N.B.: there appears to be no reliable way to determine if
* "strerror" exists at compile time, so I make do by providing
* something of similar functionality.
*/
/* externs referenced by errmsg */
extern char *sys_errlist[];
extern int sys_nerr;
char *errmsg(errnum)
int errnum;
{
if (errnum > 0 && errnum < sys_nerr)
{
return(sys_errlist[errnum]);
}
return("No error");
}
/* 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
* to be called more than once without overwriting previous data,
* then we will need to adopt a technique similar to the
* one used for format_k.
*/
/* Explanation:
We want to keep the output within 6 characters. For low values we use
the format mm:ss. For values that exceed 999:59, we switch to a format
that displays hours and fractions: hhh.tH. For values that exceed
999.9, we use hhhh.t and drop the "H" designator. For values that
exceed 9999.9, we use "???".
*/
char *format_time(seconds)
long seconds;
{
register int value;
register int digit;
register char *ptr;
static char result[10];
/* sanity protection */
if (seconds < 0 || seconds > (99999l * 360l))
{
strcpy(result, " ???");
}
else if (seconds >= (1000l * 60l))
{
/* alternate (slow) method displaying hours and tenths */
sprintf(result, "%5.1fH", (double)seconds / (double)(60l * 60l));
/* It is possible that the sprintf took more than 6 characters.
If so, then the "H" appears as result[6]. If not, then there
is a \0 in result[6]. Either way, it is safe to step on.
*/
result[6] = '\0';
}
else
{
/* standard method produces MMM:SS */
/* we avoid printf as must as possible to make this quick */
sprintf(result, "%3d:%02d", seconds / 60l, seconds % 60l);
}
return(result);
}
/*
* format_k(amt) - format a kilobyte memory value, returning a string
* suitable for display. Returns a pointer to a static
* area that changes each call. "amt" is converted to a
* string with a trailing "K". If "amt" is 10000 or greater,
* then it is formatted as megabytes (rounded) with a
* trailing "M".
*/
/*
* Compromise time. We need to return a string, but we don't want the
* caller to have to worry about freeing a dynamically allocated string.
* Unfortunately, we can't just return a pointer to a static area as one
* of the common uses of this function is in a large call to sprintf where
* it might get invoked several times. Our compromise is to maintain an
* array of strings and cycle thru them with each invocation. We make the
* array large enough to handle the above mentioned case. The constant
* NUM_STRINGS defines the number of strings in this array: we can tolerate
* up to NUM_STRINGS calls before we start overwriting old information.
* Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
* to convert the modulo operation into something quicker. What a hack!
*/
#define NUM_STRINGS 8
char *format_k(amt)
int 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];
index = (index + 1) % NUM_STRINGS;
if (amt >= 10000)
{
amt = (amt + 512) / 1024;
tag = 'M';
if (amt >= 10000)
{
amt = (amt + 512) / 1024;
tag = 'G';
}
}
p = strecpy(p, itoa(amt));
*p++ = tag;
*p = '\0';
return(ret);
}

23
contrib/top/utils.h Normal file
View File

@ -0,0 +1,23 @@
/*
* 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();

25
contrib/top/version.c Normal file
View File

@ -0,0 +1,25 @@
/*
* 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 "top.h"
#include "patchlevel.h"
static char version[16];
char *version_string()
{
sprintf(version, "%d.%d", VERSION, PATCHLEVEL);
#ifdef BETA
strcat(version, BETA);
#endif
return(version);
}