Newsgroups: comp.sources.unix From: wnl@groupsys.com (William Lefebvre) Subject: v29i105: top-3.4 - top process display, V3.4, Part04/22 References: <1.841608857.22962@gw.home.vix.com> Sender: unix-sources-moderator@gw.home.vix.com Approved: vixie@gw.home.vix.com Submitted-By: wnl@groupsys.com (William Lefebvre) Posting-Number: Volume 29, Issue 105 Archive-Name: top-3.4/part04 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'top-3.4/Configure' <<'END_OF_FILE' X#!/bin/csh -f X# X# Configuration script for top. X# X# Use with version 3.0 and higher. X# Xset PRIME = "/usr/games/primes" Xset vars = (module LoadMax topn NominalTopn delay owner group mode random \ X TableSize bindir mandir manext mansty \ X Cmdshell Cmdcc Cmdawk Cmdinstall cdefs) Xset fastrack = 0 Xset yesno = (no yes) X Xonintr byebye X X# make sure that getans is there and ready Xif (! -e getans) then X echo 'This package is not complete. The shell file "getans" is missing.' X exit 10 Xendif Xchmod +x getans X Xif ($#argv > 0) then X# fast track configuration X set fastrack = 1 Xelse Xcat <<'EOF' XConfiguration for top, version 3.4 X XOne moment.... X'EOF' Xendif X X# collect file names and module names Xls machine/m_*.c >$$.f Xls machine/m_*.man >$$.m Xsed -e 's@^machine/m_@@' -e 's/.c$//' $$.f >$$.n X X# build Make.desc Xset descs = `sed -e 's@\.c$@.desc@' $$.f` Xsed -e "s@%descs%@$descs@" Make.desc.X >Make.desc X X# build desc files and SYNOPSIS as needed Xmake -f Make.desc >/dev/null X Xif (-e .defaults) then X echo "" X echo "Reading configuration from last time..." X source .defaults X set nodefaults = 0 X if ($fastrack == 1) then X set module = $1 X endif Xelse X if ($fastrack == 1) then X echo "No previous configuration was found." X set fastrack = 0 X set module = $1 X else X set module = "" X endif X set LoadMax = 5.0 X set topn = 15 X set NominalTopn = 18 X set delay = 5 X set TableSize = 0 X set bindir = /usr/local/bin X set mandir = /usr/man/manl X set manext = l X set mansty = man X set nodefaults = 1 X set Cmdshell = /bin/sh X set Cmdawk = awk X set Cmdinstall = ./install X set Cmdcc = cc X set cdefs = -O Xendif Xecho "" X Xif ($fastrack == 1) then X grep -s $module $$.n >/dev/null X if ($status != 0) then X echo "$module is not recognized. To see a list of available modules" X echo 'run "Configure" with no arguments.' X rm -f $$.[fmn] X exit 1 X endif X set random1 = `expr $random + 1` X cat </dev/null Xif ($status != 0) then X echo "That is not a recognized module name." X goto getmod Xendif X X# display a full description Xsed -e '1,/DESCRIPTION:/d' -e '/^$/,$d' machine/m_${module}.desc X X# verify it Xecho "" X./getans "Is this what you want to use?" yesno 1 .$$ Xif (`cat .$$` == 0) then X goto getmod Xendif Xendif X Xcat <<'EOF' X XFirst we need to find out a little bit about the executables needed to Xcompile top. X X'EOF' X./getans "What is the full path name for the Bourne shell" file "$Cmdshell" .$$ Xset Cmdshell = `cat .$$` X Xcat <<'EOF' X XPlease supply the name of the appropriate command. It need not be a Xfull path name, but the named command does need to exist somewhere on Xthe current path. X X'EOF' X./getans "AWK Interpreter" path "$Cmdawk" .$$ Xset Cmdawk = `cat .$$` X./getans "C Compiler" path "$Cmdcc" .$$ Xset Cmdcc = `cat .$$` X Xcat <<'EOF' X XThe installer command needs to understand Berkeley-esque arguments: X"-o" for owner, "-g" for group, and "-m" for mode. A shell script Xcalled "install" is distributed with top and is suitable for use by Xtop. You can specify a different program here if you like, or use Xthe shell script (the default). X X'EOF' X./getans "Installer" path "$Cmdinstall" .$$ Xset Cmdinstall = `cat .$$` X Xcat <&/dev/null Xif ($status == 0 || -e /etc/passwd.dir || -e /etc/pwd.db) then X set rand = 1 Xendif X Xif ($rand == 1) then X echo "It looks like you have a passwd file that can be accessed at random." X set pr = 'Do you want top to take advantage of this' Xelse X echo "It looks like you have conventional passwd file access. Top can take" X echo "advantage of a random access passwd mechanism if such exists. Do" X echo "you want top to assume that accesses to the file /etc/passwd are done" X set pr = 'with random access rather than sequential' Xendif X Xif ($nodefaults == 1) then X set random = $rand Xendif X X./getans "${pr}?" yesno $random .$$ Xset random = `cat .$$` X Xecho "" Xecho "Compiling prime.c" X$Cmdcc $cdefs -o prime prime.c -lm Xif ($status != 0) then X echo "Oh well." X rm -f prime Xendif X Xecho "" X Xypcat passwd.byname >&/tmp/$$.a Xif ($status == 0) then X set cnt = `wc -l /tmp/$$.b X grep '^....r..r..' /tmp/$$.b >&/dev/null X if ($status == 1) then X grep '^....r..-..' /tmp/$$.b >&/dev/null X if ($status == 0) then X set t_group = `awk ' { print $4 }' /tmp/$$.b` X set t_mode = 2755 X echo "It looks like only group $t_group can read the memory devices." X else X set t_mode = 4755 X echo "It looks like only root can read the memory devices." X endif X else X set t_mode = 755 X echo "It looks like anybody can read the memory devices." X endif Xelse X echo "It looks like there are no memory device special files." X set t_mode = 755 Xendif Xif ($nodefaults) then X set owner = $t_owner X set group = $t_group X set mode = $t_mode Xendif Xecho "Tell me how to set the following when top is installed:" X./getans "Owner" user "$owner" .$$ Xset owner = `cat .$$` X./getans "Group owner" group "$group" .$$ Xset group = `cat .$$` X./getans "Mode" integer "$mode" .$$ Xset mode = `cat .$$` Xrm -f /tmp/$$.b X Xecho "" X./getans "Install the executable in this directory" file "$bindir" .$$ Xset bindir = `cat .$$` X Xecho "" X./getans "Install the manual page in this directory" file "$mandir" .$$ Xset mandir = `cat .$$` X Xecho "" X./getans "Install the manual page with this extension" string "$manext" .$$ Xset manext = `cat .$$` X Xecho "" X./getans "Install the manual page as 'man' or 'catman'" string "$mansty" .$$ Xset mansty = `cat .$$` X Xecho "" Xecho "We are done with the questions." X X# Some Unix environments are so poor that their csh doesn't even support X# the "eval" builtin. Check for this before relying on its use to save X# the current configuration. X/bin/csh -c "eval echo foo" >&/dev/null Xif ($status == 1) then X echo "Can't save configuration (nonfatal)" Xelse X echo "Saving configuration..." X# save settings to use as defaults the next time X rm -f .defaults X foreach v ($vars) X set tmp = `eval echo \$$v` X echo set $v = "'$tmp'" >>.defaults X end Xendif X Xfast: X X# set variables which contain module lists Xset modules = `cat $$.f` Xset manmodules = `cat $$.m` X X# clean up Xrm -f $$.f $$.m $$.n X X# set the link for machine.c Xrm -f machine.c machine.o Xln -s machine/m_${module}.c machine.c X X# get definitions out of the module file Xset libs = `grep LIBS: machine/m_${module}.desc | sed -e 's/^.[^:]*: *//'` Xset cflgs = `grep CFLAGS: machine/m_${module}.desc | sed -e 's/^.[^:]*: *//'` Xset tcap = `grep TERMCAP: machine/m_${module}.desc | sed -e 's/^.[^:]*: *//'` X X# default for tcap (termcap) Xif ("$tcap" == "") then X set tcap="-ltermcap" Xendif X Xif ( { grep -s SIGKILL /usr/include/signal.h } ) then X set signal="/usr/include/signal.h" Xelse X set signal="/usr/include/sys/signal.h" Xendif X X Xecho "Building Makefile..." Xsed -e "s|%topn%|$topn|" \ X -e "s|%delay%|$delay|" \ X -e "s|%owner%|$owner|" \ X -e "s|%group%|$group|" \ X -e "s|%mode%|$mode|" \ X -e "s|%bindir%|$bindir|" \ X -e "s|%mandir%|$mandir|" \ X -e "s|%manext%|$manext|" \ X -e "s|%mansty%|$mansty|" \ X -e "s|%tablesize%|$TableSize|" \ X -e "s|%libs%|$libs|" \ X -e "s|%cflgs%|$cflgs|" \ X -e "s|%termcap%|$tcap|" \ X -e "s|%cdefs%|$cdefs|" \ X -e "s|%modules%|$modules|" \ X -e "s|%manmodules%|$manmodules|" \ X -e "s|%signal%|$signal|" \ X -e "s|%cc%|$Cmdcc|" \ X -e "s|%awk%|$Cmdawk|" \ X -e "s|%install%|$Cmdinstall|" \ X -e "s|%shell%|$Cmdshell|" \ X Makefile.X >Makefile X Xecho "Building top.local.h..." Xsed -e "s|%LoadMax%|$LoadMax|" \ X -e "s|%TableSize%|$TableSize|" \ X -e "s|%NominalTopn%|$NominalTopn|" \ X -e "s|%topn%|$topn|" \ X -e "s|%delay%|$delay|" \ X -e "s|%random%|$random|" \ X top.local.H >top.local.h X Xecho "Building top.1..." Xsed -e "s|%topn%|$topn|" \ X -e "s|%delay%|$delay|" \ X top.X >top.1 Xif (-e machine/m_${module}.man ) then X cat machine/m_${module}.man >>top.1 Xendif X X# clean up Xrm -f .$$ X Xecho 'Doing a "make clean".' Xmake clean X Xecho 'To create the executable, type "make".' Xecho 'To install the executable, type "make install".' Xexit 0 X Xbyebye: Xrm -f .$$ $$.[fmn] /tmp/$$.[ab] Xexit 1 END_OF_FILE if test 13242 -ne `wc -c <'top-3.4/Configure'`; then echo shar: \"'top-3.4/Configure'\" unpacked with wrong size! fi chmod +x 'top-3.4/Configure' # end of 'top-3.4/Configure' fi if test -f 'top-3.4/machine/m_dgux.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'top-3.4/machine/m_dgux.c'\" else echo shar: Extracting \"'top-3.4/machine/m_dgux.c'\" \(12268 characters\) sed "s/^X//" >'top-3.4/machine/m_dgux.c' <<'END_OF_FILE' X/* X * top - a top users display for Unix X * X * SYNOPSIS: for DG AViiON with DG/UX 5.4+ X * X * DESCRIPTION: X * A top module for DG/UX 5.4 systems. X * Uses DG/UX system calls to get info from the kernel. X * (NB. top DOES NOT need to be installed setuid root under DG/UX 5.4.2) X * X * AUTHOR: Mike Williams X */ X X/* X * NOTE: This module will only work with top versions 3.1 and later! X */ X X#include X#include X#include X X#include X#include X#include X#include X X#include "top.h" X#include "machine.h" X#include "utils.h" X X/*--- process formatting --------------------------------------------------*/ X Xstatic char header[] = X " PID X PRI NICE C SIZE STATE TIME CPU COMMAND"; X/* ddddd ssssssss dddd ddd dd ddddddK ssssssdddd:dd dd.dd% sssssssssssssssss... X * 0123456 -- field to fill in starts at header+6 */ X#define UNAME_START 6 X X#define Proc_format \ X "%5d %-8.8s %4d %3d %2d %7s %-6s %6s %5.2f%% %.20s" X X/*--- process states ------------------------------------------------------*/ X Xstatic char* procStateNames[] = { X "", " sleeping, ", " waiting, ", " running, ", " starting, ", X " zombie, ", " stopped, ", X NULL X}; X Xstatic char* procStateAbbrevs[] = { X "", "sleep", "wait", "run", "start", "zombie", "stop", X NULL X}; X X#define N_PROCESS_STATES \ X(sizeof (procStateNames) / sizeof (procStateNames[0]) - 1) X Xstatic int processStats[N_PROCESS_STATES]; X X/*--- cpu states ----------------------------------------------------------*/ X Xenum { X CPU_user, X CPU_system, X CPU_idle, X CPU_io_wait, X}; X Xstatic char* cpuStateNames[] = { X "user", "system", "idle", "io_wait", X NULL X}; X X#define N_CPU_STATES \ X(sizeof (cpuStateNames) / sizeof (cpuStateNames[0]) - 1) X Xstatic int cpuStats[N_CPU_STATES]; X X/*--- memory statistics ---------------------------------------------------*/ X Xenum { X MEM_available, X MEM_used, X MEM_free, X MEM_freeswap, X}; X Xstatic char* memoryNames[] = { X "K physical, ", "K in use, ", "K free, ", "K free swap, ", NULL X}; X X#define N_MEMORY_STATS \ X(sizeof (memoryNames) / sizeof (memoryNames[0]) - 1) X Xstatic int memoryStats[N_MEMORY_STATS]; X X/*--- conversion macros ---------------------------------------------------*/ X X/* Convert clicks (kernel pages) to kbytes ... */ X#define pagetok(size) ctob(size) >> LOG1024 X X/* Convert timeval's to double */ X#define tvtod(tval) (1000000.0 * (tval).tv_sec + 1.0 * (tval).tv_usec) X X/* Scale timeval's onto longs */ X#define scaledtv(tval) (tvtod (tval) / 4096) X X/*--- process table -------------------------------------------------------*/ X Xtypedef struct _ProcInfo { X struct dg_process_info p_info; X double cpu_time; X double fraction_cpu; X} ProcInfo; X Xstatic ProcInfo* processInfo; Xstatic ProcInfo** activeProcessInfo; X Xint activeIndex; X Xtypedef struct _ProcTime { X pid_t pid; X double cpu_time; X} ProcTime; X Xstatic ProcTime* oldProcessTimes; Xstatic int n_oldProcessTimes; X Xstatic double lastTime; Xstatic double thisTime; Xstatic double timeSlice; X X/*=========================================================================*/ X/*=== top "Callback" routines =============================================*/ X Xstatic int IntCmp (i1, i2) X int* i1; X int* i2; X{ X return (*i2 - *i1); X} X X/*=== Data collection =====================================================*/ X Xint machine_init (statics) X /*~~~~~~~~~~~~ X */ X struct statics *statics; X{ X struct dg_sys_info_pm_info pm_info; X int table_size; X X /* fill in the statics information */ X statics->procstate_names = procStateNames; X statics->cpustate_names = cpuStateNames; X statics->memory_names = memoryNames; X X dg_sys_info ((long *)&pm_info, X DG_SYS_INFO_PM_INFO_TYPE, X DG_SYS_INFO_PM_VERSION_0); X table_size = pm_info.process_table_size + 1; X X processInfo = (ProcInfo *) X malloc (sizeof (processInfo[0]) * table_size); X activeProcessInfo = (ProcInfo **) X malloc (sizeof (activeProcessInfo[0]) * table_size); X oldProcessTimes = (ProcTime *) X malloc (sizeof (oldProcessTimes[0]) * table_size); X X lastTime = 0; X X return(0); X} X Xint get_system_info (si) X /*~~~~~~~~~~~~~~~ X */ X struct system_info *si; X{ X struct dg_sys_info_vm_info vm_info; X struct dg_sys_info_pm_info pm_info; X struct dg_sys_info_load_info load_info; X X static long cpu_time [N_CPU_STATES]; X static long cpu_old [N_CPU_STATES]; X static long cpu_diff [N_CPU_STATES]; X X /* memory info */ X X dg_sys_info ((long *)&vm_info, X DG_SYS_INFO_VM_INFO_TYPE, X DG_SYS_INFO_VM_VERSION_0); X X memoryStats[MEM_available] = sysconf (_SC_AVAILMEM); X memoryStats[MEM_free] = pagetok (vm_info.freemem); X memoryStats[MEM_used] = memoryStats[0] - memoryStats[2]; X memoryStats[MEM_freeswap] = pagetok (vm_info.freeswap); X si->memory = memoryStats; X X /* process info */ X X dg_sys_info ((long *)&pm_info, X DG_SYS_INFO_PM_INFO_TYPE, X DG_SYS_INFO_PM_VERSION_0); X X si->last_pid = 0; X si->p_total = pm_info.process_count; X si->p_active = pm_info.bound_runnable_process_count; X X cpu_time[CPU_user] = scaledtv (pm_info.user_time); X cpu_time[CPU_system] = scaledtv (pm_info.system_time); X cpu_time[CPU_idle] = scaledtv (pm_info.idle_time); X cpu_time[CPU_io_wait] = scaledtv (pm_info.io_wait_time); X percentages (N_CPU_STATES, cpuStats, cpu_time, cpu_old, cpu_diff); X si->cpustates = cpuStats; X X /* calculate timescale */ X X thisTime = tvtod (pm_info.current_time); X timeSlice = thisTime - lastTime; X lastTime = thisTime; X X /* load info */ X X dg_sys_info ((long *)&load_info, X DG_SYS_INFO_LOAD_INFO_TYPE, X DG_SYS_INFO_LOAD_VERSION_0); X X si->load_avg[0] = load_info.one_minute; X si->load_avg[1] = load_info.five_minute; X si->load_avg[2] = load_info.fifteen_minute; X X return 1; X} X Xcaddr_t get_process_info (si, sel, compare) X /* ~~~~~~~~~~~~~~~~ X */ X struct system_info* si; X struct process_select* sel; X int (*compare)(); X{ X long key = DG_PROCESS_INFO_INITIAL_KEY; X X int n_total = 0; X int n_active = 0; X X ProcInfo* pp; X int i; X X bzero((char *)processStats, sizeof(processStats)); X X while (dg_process_info (DG_PROCESS_INFO_SELECTOR_ALL_PROCESSES, 0, X DG_PROCESS_INFO_CMD_NAME_ONLY, X &key, X &(processInfo[n_total].p_info), X DG_PROCESS_INFO_CURRENT_VERSION) == 1) { X X ProcInfo* pp = &(processInfo[n_total++]); X int pid = pp->p_info.process_id; X ProcTime* old_time; X X /* Increment count for this process state */ X ++processStats[pp->p_info.state]; X X /* Calculate % CPU usage */ X pp->cpu_time = (tvtod (pp->p_info.system_time) + X tvtod (pp->p_info.user_time)); X old_time = (ProcTime *) X bsearch (&pid, oldProcessTimes, X n_oldProcessTimes, sizeof (ProcTime), X IntCmp); X pp->fraction_cpu = (old_time X ? ((pp->cpu_time - old_time->cpu_time) X / timeSlice) X : 0.0); X X /* Skip if process not classed as "active" */ X if ((pp->p_info.state == DG_PROCESS_INFO_STATUS_TERMINATED) || X (!sel->idle X && (pp->p_info.state != DG_PROCESS_INFO_STATUS_RUNNING) X && (pp->p_info.state != DG_PROCESS_INFO_STATUS_WAITING)) || X (sel->uid != -1 && pp->p_info.user_id != (uid_t)sel->uid) || X (!sel->system && (pp->p_info.user_id == 0 && X pp->p_info.parent_process_id == 1)) || X (sel->command && strcmp (pp->p_info.cmd, sel->command) != 0)) X continue; X X activeProcessInfo[n_active++] = pp; X X } X X activeProcessInfo[n_active] = NULL; X X si->p_total = n_total; X si->p_active = n_active; X si->procstates = processStats; X X /* If requested, sort the "interesting" processes */ X if (compare != NULL) qsort((void *)activeProcessInfo, X n_active, X sizeof (ProcInfo *), X compare); X X /* Record scaled CPU totals, for calculating %CPU */ X n_oldProcessTimes = n_total; X for (i = 0; i < n_oldProcessTimes; i++) { X oldProcessTimes[i].pid = processInfo[i].p_info.process_id; X oldProcessTimes[i].cpu_time = processInfo[i].cpu_time; X } X qsort (oldProcessTimes, n_oldProcessTimes, sizeof (ProcTime), IntCmp); X X /* pass back a handle */ X activeIndex = 0; X return ((caddr_t) &activeIndex); X} X X/*=== Process comparison routine ==========================================*/ X X/* X * Sort keys are (in descending order of importance): X * - percent cpu X * - cpu ticks X * - state X * - resident set size X * X * The process states are ordered as follows: X * - zombie X * - wait X * - sleep X * - stop X * - start X * - run X */ X Xstatic unsigned char sortedState[] = X{ X 0, /* not used */ X 3, /* sleep */ X 1, /* wait */ X 6, /* run */ X 5, /* start */ X 2, /* zombie */ X 4, /* stop */ X}; X Xint proc_compare(pp1, pp2) X /*~~~~~~~~~~~~ X */ X ProcInfo** pp1; X ProcInfo** pp2; X{ X register ProcInfo* p1; X register ProcInfo* p2; X register int result; X register float lresult; X X register long p1_cpu; X register long p2_cpu; X X /* remove one level of indirection */ X p1 = *pp1; X p2 = *pp2; X X /* calculate cpu totals */ X p1_cpu = p1->p_info.system_time.tv_sec + p1->p_info.user_time.tv_sec; X p2_cpu = p2->p_info.system_time.tv_sec + p2->p_info.user_time.tv_sec; X X /* Compare %CPU usage */ X if ((lresult = (p2->fraction_cpu - p1->fraction_cpu)) != 0) X return lresult < 0 ? -1 : 1; X X /* Compare other fields until one differs */ X ((result = (p2->p_info.cpu_usage - p1->p_info.cpu_usage)) || X (result = (sortedState [p2->p_info.state] - X sortedState [p1->p_info.state])) || X (result = (p2->p_info.priority - p1->p_info.priority)) || X (result = (p2->p_info.resident_process_size - X p1->p_info.resident_process_size)) || X (result = (p1->p_info.process_id - p2->p_info.process_id))); X X return result; X} X X/*=== Process owner validation ============================================*/ X X/* X * proc_owner(pid) - returns the uid that owns process "pid", or -1 if X * the process does not exist. X * It is EXTREMLY IMPORTANT that this function work correctly. X * If top runs setuid root (as in SVR4), then this function X * is the only thing that stands in the way of a serious X * security problem. It validates requests for the "kill" X * and "renice" commands. X */ X Xint proc_owner (pid) X /*~~~~~~~~~~ X */ X int pid; X{ X register int i; X ProcInfo* pp; X X for (i = 0; (pp = activeProcessInfo [i]); i++) { X if (pp->p_info.process_id == pid) X return (int)pp->p_info.user_id; X } X return(-1); X} X X/*=== Output formatting ===================================================*/ X Xchar* format_header (uname_field) X /* ~~~~~~~~~~~~~ X */ X register char* uname_field; X{ X register char* ptr; X X ptr = header + UNAME_START; X while (*uname_field != '\0') X { X *ptr++ = *uname_field++; X } X X return(header); X} X Xchar* format_next_process (index_ptr, get_userid) X /* ~~~~~~~~~~~~~~~~~~~ X */ X int* index_ptr; X char* (*get_userid)(); X{ X static char fmt[MAX_COLS]; X X int proc_index; X ProcInfo* pp; X long proc_cpu; X X proc_index = (*index_ptr)++; X pp = activeProcessInfo [proc_index]; X proc_cpu = pp->p_info.system_time.tv_sec + pp->p_info.user_time.tv_sec; X X /* format this entry */ X X sprintf (fmt, X Proc_format, X pp->p_info.process_id, X (*get_userid) (pp->p_info.user_id), X pp->p_info.priority, X pp->p_info.nice_value, X pp->p_info.cpu_usage, X format_k(pagetok (pp->p_info.resident_process_size)), X procStateAbbrevs[pp->p_info.state], X format_time(proc_cpu), X 100.0 * pp->fraction_cpu, X pp->p_info.cmd); X X return(fmt); X} X X/*=== END of m_dgux.c =====================================================*/ END_OF_FILE if test 12268 -ne `wc -c <'top-3.4/machine/m_dgux.c'`; then echo shar: \"'top-3.4/machine/m_dgux.c'\" unpacked with wrong size! fi # end of 'top-3.4/machine/m_dgux.c' fi if test -f 'top-3.4/machine/m_linux.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'top-3.4/machine/m_linux.c'\" else echo shar: Extracting \"'top-3.4/machine/m_linux.c'\" \(13878 characters\) sed "s/^X//" >'top-3.4/machine/m_linux.c' <<'END_OF_FILE' X/* X * top - a top users display for Unix X * X * SYNOPSIS: Linux 1.2.x, 1.3.x, using the /proc filesystem X * X * DESCRIPTION: X * This is the machine-dependent module for Linux 1.2.x or 1.3.x. X * X * LIBS: X * X * CFLAGS: -DHAVE_GETOPT X * X * AUTHOR: Richard Henderson X */ X X#include "top.h" X#include "machine.h" X#include "utils.h" X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#include /* for HZ */ X#include /* for PAGE_SHIFT */ X#include /* for NR_TASKS */ X X#if 0 X#include /* for PROC_SUPER_MAGIC */ X#else X#define PROC_SUPER_MAGIC 0x9fa0 X#endif X X#define PROCFS "/proc" Xextern char *myname; Xextern uid_t proc_owner(pid_t pid); X X/*=PROCESS INFORMATION==================================================*/ X Xstruct top_proc X{ X pid_t pid; X uid_t uid; X char name[64]; X int pri, nice; X unsigned long size, rss; /* in k */ X int state; X unsigned long time; X double pcpu, wcpu; X}; X X X/*=STATE IDENT STRINGS==================================================*/ X X#define NPROCSTATES 7 Xstatic char *state_abbrev[NPROCSTATES+1] = X{ X "", "run", "sleep", "disk", "zomb", "stop", "swap", X NULL X}; X Xstatic char *procstatenames[NPROCSTATES+1] = X{ X "", " running, ", " sleeping, ", " uninterruptable, ", X " zombie, ", " stopped, ", " swapping, ", X NULL X}; X X#define NCPUSTATES 4 Xstatic char *cpustatenames[NCPUSTATES+1] = X{ X "user", "nice", "system", "idle", X NULL X}; X X#define NMEMSTATS 6 Xstatic char *memorynames[NMEMSTATS+1] = X{ X "K used, ", "K free, ", "K shd, ", "K buf Swap: ", X "K used, ", "K free", X NULL X}; X Xstatic char fmt_header[] = X" PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; X X X/*=SYSTEM STATE INFO====================================================*/ X X/* these are for calculating cpu state percentages */ X Xstatic long cp_time[NCPUSTATES]; Xstatic long cp_old[NCPUSTATES]; Xstatic long cp_diff[NCPUSTATES]; X X/* for calculating the exponential average */ X Xstatic struct timeval lasttime; X X/* these are for keeping track of processes */ X X#define HASH_SIZE (NR_TASKS * 3 / 2) Xstatic struct top_proc ptable[HASH_SIZE]; Xstatic struct top_proc *pactive[NR_TASKS]; Xstatic struct top_proc **nextactive; X X/* these are for passing data back to the machine independant portion */ X Xstatic int cpu_states[NCPUSTATES]; Xstatic int process_states[NPROCSTATES]; Xstatic int memory_stats[NMEMSTATS]; X X/* usefull macros */ X#define bytetok(x) (((x) + 512) >> 10) X#define pagetok(x) ((x) << (PAGE_SHIFT - 10)) X#define HASH(x) (((x) * 1686629713U) % HASH_SIZE) X X/*======================================================================*/ X Xstatic inline char * Xskip_ws(const char *p) X{ X while (isspace(*p)) p++; X return (char *)p; X} X Xstatic inline char * Xskip_token(const char *p) X{ X while (isspace(*p)) p++; X while (*p && !isspace(*p)) p++; X return (char *)p; X} X X Xint Xmachine_init(statics) X struct statics *statics; X{ X /* make sure the proc filesystem is mounted */ X { X struct statfs sb; X if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC) X { X fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n", X myname); X return -1; X } X } X X /* chdir to the proc filesystem to make things easier */ X chdir(PROCFS); X X /* initialize the process hash table */ X { X int i; X for (i = 0; i < HASH_SIZE; ++i) X ptable[i].pid = -1; X } X X /* fill in the statics information */ X statics->procstate_names = procstatenames; X statics->cpustate_names = cpustatenames; X statics->memory_names = memorynames; X X /* all done! */ X return 0; X} X X Xvoid Xget_system_info(info) X struct system_info *info; X{ X char buffer[4096+1]; X int fd, len; X char *p; X X /* get load averages */ X { X fd = open("loadavg", O_RDONLY); X len = read(fd, buffer, sizeof(buffer)-1); X close(fd); X buffer[len] = '\0'; X X info->load_avg[0] = strtod(buffer, &p); X info->load_avg[1] = strtod(p, &p); X info->load_avg[2] = strtod(p, &p); X p = skip_token(p); /* skip running/tasks */ X p = skip_ws(p); X if (*p) X info->last_pid = atoi(p); X else X info->last_pid = -1; X } X X /* get the cpu time info */ X { X fd = open("stat", O_RDONLY); X len = read(fd, buffer, sizeof(buffer)-1); X close(fd); X buffer[len] = '\0'; X X p = skip_token(buffer); /* "cpu" */ X cp_time[0] = strtoul(p, &p, 0); X cp_time[1] = strtoul(p, &p, 0); X cp_time[2] = strtoul(p, &p, 0); X cp_time[3] = strtoul(p, &p, 0); X X /* convert cp_time counts to percentages */ X percentages(4, cpu_states, cp_time, cp_old, cp_diff); X } X X /* get system wide memory usage */ X { X char *p; X X fd = open("meminfo", O_RDONLY); X len = read(fd, buffer, sizeof(buffer)-1); X close(fd); X buffer[len] = '\0'; X X /* be prepared for extra columns to appear be seeking X to ends of lines */ X X p = strchr(buffer, '\n'); X p = skip_token(p); /* "Mem:" */ X p = skip_token(p); /* total memory */ X memory_stats[0] = strtoul(p, &p, 10); X memory_stats[1] = strtoul(p, &p, 10); X memory_stats[2] = strtoul(p, &p, 10); X memory_stats[3] = strtoul(p, &p, 10); X X p = strchr(p, '\n'); X p = skip_token(p); /* "Swap:" */ X p = skip_token(p); /* total swap */ X memory_stats[4] = strtoul(p, &p, 10); X memory_stats[5] = strtoul(p, &p, 10); X X memory_stats[0] = bytetok(memory_stats[0]); X memory_stats[1] = bytetok(memory_stats[1]); X memory_stats[2] = bytetok(memory_stats[2]); X memory_stats[3] = bytetok(memory_stats[3]); X memory_stats[4] = bytetok(memory_stats[4]); X memory_stats[5] = bytetok(memory_stats[5]); X } X X /* set arrays and strings */ X info->cpustates = cpu_states; X info->memory = memory_stats; X} X X Xstatic void Xread_one_proc_stat(pid_t pid, struct top_proc *proc) X{ X char buffer[4096], *p; X X /* grab the proc stat info in one go */ X { X int fd, len; X X sprintf(buffer, "%d/stat", pid); X X fd = open(buffer, O_RDONLY); X len = read(fd, buffer, sizeof(buffer)-1); X close(fd); X X buffer[len] = '\0'; X } X X proc->uid = proc_owner(pid); X X /* parse out the status */ X X p = buffer; X p = strchr(p, '(')+1; /* skip pid */ X { X char *q = strrchr(p, ')'); X int len = q-p; X if (len >= sizeof(proc->name)) X len = sizeof(proc->name)-1; X memcpy(proc->name, p, len); X proc->name[len] = 0; X p = q+1; X } X X p = skip_ws(p); X switch (*p++) X { X case 'R': proc->state = 1; break; X case 'S': proc->state = 2; break; X case 'D': proc->state = 3; break; X case 'Z': proc->state = 4; break; X case 'T': proc->state = 5; break; X case 'W': proc->state = 6; break; X } X X p = skip_token(p); /* skip ppid */ X p = skip_token(p); /* skip pgrp */ X p = skip_token(p); /* skip session */ X p = skip_token(p); /* skip tty */ X p = skip_token(p); /* skip tty pgrp */ X p = skip_token(p); /* skip flags */ X p = skip_token(p); /* skip min flt */ X p = skip_token(p); /* skip cmin flt */ X p = skip_token(p); /* skip maj flt */ X p = skip_token(p); /* skip cmaj flt */ X X proc->time = strtoul(p, &p, 10); /* utime */ X proc->time += strtoul(p, &p, 10); /* stime */ X X p = skip_token(p); /* skip cutime */ X p = skip_token(p); /* skip cstime */ X X proc->pri = strtol(p, &p, 10); /* priority */ X proc->nice = strtol(p, &p, 10); /* nice */ X X p = skip_token(p); /* skip timeout */ X p = skip_token(p); /* skip it_real_val */ X p = skip_token(p); /* skip start_time */ X X proc->size = bytetok(strtoul(p, &p, 10)); /* vsize */ X proc->rss = pagetok(strtoul(p, &p, 10)); /* rss */ X X#if 0 X /* for the record, here are the rest of the fields */ X p = skip_token(p); /* skip rlim */ X p = skip_token(p); /* skip start_code */ X p = skip_token(p); /* skip end_code */ X p = skip_token(p); /* skip start_stack */ X p = skip_token(p); /* skip sp */ X p = skip_token(p); /* skip pc */ X p = skip_token(p); /* skip signal */ X p = skip_token(p); /* skip sigblocked */ X p = skip_token(p); /* skip sigignore */ X p = skip_token(p); /* skip sigcatch */ X p = skip_token(p); /* skip wchan */ X#endif X} X X Xcaddr_t Xget_process_info(struct system_info *si, X struct process_select *sel, X int (*compare)()) X{ X struct timeval thistime; X double timediff, alpha, beta; X X /* calculate the time difference since our last check */ X gettimeofday(&thistime, 0); X if (lasttime.tv_sec) X { X timediff = ((thistime.tv_sec - lasttime.tv_sec) + X (thistime.tv_usec - lasttime.tv_usec) * 1e-6); X } X else X timediff = 1e9; X lasttime = thistime; X X /* calculate constants for the exponental average */ X if (timediff < 30.0) X { X alpha = 0.5 * (timediff / 30.0); X beta = 1.0 - alpha; X } X else X alpha = beta = 0.5; X timediff *= HZ; /* convert to ticks */ X X /* mark all hash table entries as not seen */ X { X int i; X for (i = 0; i < HASH_SIZE; ++i) X ptable[i].state = 0; X } X X /* read the process information */ X { X DIR *dir = opendir("."); X struct dirent *ent; X int total_procs = 0; X struct top_proc **active = pactive; X X int show_idle = sel->idle; X int show_uid = sel->uid != -1; X X memset(process_states, 0, sizeof(process_states)); X X while ((ent = readdir(dir)) != NULL) X { X struct top_proc *proc; X pid_t pid; X unsigned long otime; X X if (!isdigit(ent->d_name[0])) X continue; X X pid = atoi(ent->d_name); X X /* look up hash table entry */ X proc = &ptable[HASH(pid)]; X while (proc->pid != pid && proc->pid != -1) X { X if (++proc == ptable+HASH_SIZE) X proc = ptable; X } X X otime = proc->time; X X read_one_proc_stat(pid, proc); X X if (proc->state == 0) X continue; X X total_procs++; X process_states[proc->state]++; X X if (proc->pid == -1) X { X proc->pid = pid; X proc->wcpu = proc->pcpu = proc->time / timediff; X } X else X { X proc->pcpu = (proc->time - otime) / timediff; X proc->wcpu = proc->pcpu * alpha + proc->wcpu * beta; X } X X if ((show_idle || proc->state == 1 || proc->pcpu) && X (!show_uid || proc->uid == sel->uid)) X { X *active++ = proc; X } X } X closedir(dir); X X si->p_active = active - pactive; X si->p_total = total_procs; X si->procstates = process_states; X } X X /* flush old hash table entries */ X { X int i; X for (i = 0; i < HASH_SIZE; ++i) X if (ptable[i].state == 0) X ptable[i].pid = -1; X } X X /* if requested, sort the "active" procs */ X if (compare && si->p_active) X qsort(pactive, si->p_active, sizeof(struct top_proc *), compare); X X /* don't even pretend that the return value thing here isn't bogus */ X nextactive = pactive; X return (caddr_t)0; X} X X Xchar * Xformat_header(uname_field) X char *uname_field; X{ X int uname_len = strlen(uname_field); X if (uname_len > 8) X uname_len = 8; X X memcpy(strchr(fmt_header, 'X'), uname_field, uname_len); X X return fmt_header; X} X X Xchar * Xformat_next_process(handle, get_userid) X caddr_t handle; X char *(*get_userid)(); X{ X static char fmt[128]; /* static area where result is built */ X struct top_proc *p = *nextactive++; X X sprintf(fmt, X "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.14s", X p->pid, X (*get_userid)(p->uid), X p->pri, X p->nice, X format_k(p->size), X format_k(p->rss), X state_abbrev[p->state], X format_time(p->time / HZ), X p->wcpu * 100.0, X p->pcpu * 100.0, X p->name); X X /* return the result */ X return (fmt); X} X X X/* X * proc_compare - comparison function for "qsort" X * Compares the resource consumption of two processes using five X * distinct keys. The keys (in descending order of importance) are: X * percent cpu, cpu ticks, state, resident set size, total virtual X * memory usage. The process states are ordered as follows (from least X * to most important): WAIT, zombie, sleep, stop, start, run. The X * array declaration below maps a process state index into a number X * that reflects this ordering. X */ X X Xint Xproc_compare (pp1, pp2) X struct top_proc **pp1, **pp2; X{ X static unsigned char sort_state[] = X { X 0, /* empty */ X 6, /* run */ X 3, /* sleep */ X 5, /* disk wait */ X 1, /* zombie */ X 2, /* stop */ X 4 /* swap */ X }; X X struct top_proc *p1, *p2; X int result; X double dresult; X X /* remove one level of indirection */ X p1 = *pp1; X p2 = *pp2; X X /* compare percent cpu (pctcpu) */ X dresult = p2->pcpu - p1->pcpu; X if (dresult != 0.0) X return dresult > 0.0 ? 1 : -1; X X /* use cputicks to break the tie */ X if ((result = p2->time - p1->time) == 0) X { X /* use process state to break the tie */ X if ((result = (sort_state[p2->state] - sort_state[p1->state])) == 0) X { X /* use priority to break the tie */ X if ((result = p2->pri - p1->pri) == 0) X { X /* use resident set size (rssize) to break the tie */ X if ((result = p2->rss - p1->rss) == 0) X { X /* use total memory to break the tie */ X result = (p2->size - p1->size); X } X } X } X } X X return result == 0 ? 0 : result < 0 ? -1 : 1; X} X X X/* X * proc_owner(pid) - returns the uid that owns process "pid", or -1 if X * the process does not exist. X * It is EXTREMLY IMPORTANT that this function work correctly. X * If top runs setuid root (as in SVR4), then this function X * is the only thing that stands in the way of a serious X * security problem. It validates requests for the "kill" X * and "renice" commands. X */ X Xuid_t Xproc_owner(pid) X pid_t pid; X{ X struct stat sb; X char buffer[32]; X sprintf(buffer, "%d", pid); X X if (stat(buffer, &sb) < 0) X return -1; X else X return sb.st_uid; X} END_OF_FILE if test 13878 -ne `wc -c <'top-3.4/machine/m_linux.c'`; then echo shar: \"'top-3.4/machine/m_linux.c'\" unpacked with wrong size! fi # end of 'top-3.4/machine/m_linux.c' fi echo shar: End of archive 4 \(of 22\). cp /dev/null ark4isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 22 archives. echo "Now read README and INSTALL, then run Configure" rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0