Date: Fri, 26 Sep 2008 01:21:40 +1000 (EST) From: Edwin Groothuis <edwin@mavetju.org> To: FreeBSD-gnats-submit@FreeBSD.org Subject: bin/127633: [patch] Update top(1) to 3.8b1 Message-ID: <20080925152140.2C5FA6BE@k7.mavetju> Resent-Message-ID: <200809251530.m8PFU3dW081287@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 127633 >Category: bin >Synopsis: [patch] Update top(1) to 3.8b1 >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Thu Sep 25 15:30:03 UTC 2008 >Closed-Date: >Last-Modified: >Originator: Edwin Groothuis >Release: FreeBSD 7.1-PRERELEASE i386 >Organization: - >Environment: System: FreeBSD k7.mavetju 7.1-PRERELEASE FreeBSD 7.1-PRERELEASE #0 r183246M: Mon Sep 22 15:32:35 EST 2008 edwin@k7.mavetju:/mnt/ad8/obj/usr/home/edwin/svn/stable-7/sys/GENERIC i386 >Description: >How-To-Repeat: >Fix: Number of lines changed in the FreeBSD CVS repository: 12921 Number of lines changed from the top 3.8b1 code: 1502 There are three patches: - The first patch is for base/usr.bin/top. - The second patch is for base/contrib/top. - The third patch is for against the clean 3.8b1 code. Suggested approach: - Import stock 3.8b1 code in base/vendor/top/dist and tag it. - Merge into base/head/contrib/top and base/head/contrib/usr.bin/top. This will give a lot of conflicts etc. - Remove all files in base/head/contrib/top and resolve all issues. - Unpack 3.8b1 code in base/head/contrib/top. - Apply third patch against base/head/contrib/top and base/head/usr.bin/top. - Commit everything. In src/usr.bin/top: Index: machine.c =================================================================== --- machine.c (revision 183246) +++ machine.c (working copy) @@ -1,1380 +1,1810 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $FreeBSD$ + */ + +/* * top - a top users display for Unix * - * SYNOPSIS: For FreeBSD-2.x and later + * SYNOPSIS: For FreeBSD 5.x, 6.x, 7.x, 8.x * * DESCRIPTION: * Originally written for BSD4.4 system by Christos Zoulas. * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider * Order support hacked in from top-3.5beta6/machine/m_aix41.c - * by Monte Mitzelfelt (for latest top see http://www.groupsys.com/topinfo/) + * by Monte Mitzelfelt + * Ported to FreeBSD 5.x and higher by William LeFebvre + * Updates for the import in FreeBSD 7 by Edwin Groothuis. * - * This is the machine-dependent module for FreeBSD 2.2 - * Works for: - * FreeBSD 2.2.x, 3.x, 4.x, and probably FreeBSD 2.1.x - * - * LIBS: -lkvm - * * AUTHOR: Christos Zoulas <christos@ee.cornell.edu> * Steven Wallace <swallace@freebsd.org> * Wolfram Schneider <wosch@FreeBSD.org> - * Thomas Moestl <tmoestl@gmx.net> - * - * $FreeBSD$ */ + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/signal.h> #include <sys/param.h> + +#include "config.h" +#include <stdio.h> +#include <string.h> +#include <nlist.h> +#include <math.h> +#include <kvm.h> +#include <paths.h> +#include <pwd.h> #include <sys/errno.h> +#include <sys/sysctl.h> +#include <sys/dkstat.h> #include <sys/file.h> +#include <sys/time.h> #include <sys/proc.h> +#include <sys/user.h> +#include <sys/vmmeter.h> #include <sys/resource.h> #include <sys/rtprio.h> -#include <sys/signal.h> -#include <sys/sysctl.h> -#include <sys/time.h> -#include <sys/user.h> -#include <sys/vmmeter.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif -#include <kvm.h> -#include <math.h> -#include <nlist.h> -#include <paths.h> -#include <pwd.h> -#include <stdio.h> +/* Swap */ #include <stdlib.h> -#include <string.h> -#include <strings.h> -#include <unistd.h> -#include <vis.h> +#include <sys/conf.h> +#include <osreldate.h> /* for changes in kernel structures */ + #include "top.h" #include "machine.h" -#include "screen.h" #include "utils.h" -#include "layout.h" +#include "username.h" +#include "hash.h" +#include "display.h" -#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var)) -#define SMPUNAMELEN 13 -#define UPUNAMELEN 15 +extern char* printable __P((char *)); +int swapmode __P((int *retavail, int *retfree)); +static int smpmode; +static int namelength; -extern struct process_select ps; -extern char* printable(char *); -static int smpmode; -enum displaymodes displaymode; -#ifdef TOP_USERNAME_LEN -static int namelength = TOP_USERNAME_LEN; -#else -static int namelength = 8; +/* + * Versions prior to 5.x do not track threads in kinfo_proc, so we + * simply do not display any information about them. + * Versions 5.x, 6.x, and 7.x track threads but the data reported + * as runtime for each thread is actually per-process and is just + * duplicated across all threads. It would be very wrong to show + * this data individually for each thread. Therefore we will show + * a THR column (number of threads) but not provide any sort of + * per-thread display. We distinguish between these three ways of + * handling threads as follows: HAS_THREADS indicates that the + * system has and tracks kernel threads (a THR column will appear + * in the display). HAS_SHOWTHREADS indicates that the system + * reports correct per-thread information and we will provide a + * per-thread display (the 'H' and 't' command) upon request. + * HAS_SHOWTHREADS implies HAS_THREADS. + */ + +/* HAS_THREADS for anything 5.x and up */ +#if OSMAJOR >= 5 +#define HAS_THREADS #endif -static int cmdlengthdelta; -/* Prototypes for top internals */ -void quit(int); +/* HAS_SHOWTHREADS for anything 8.x and up */ +#if OSMAJOR >=8 +#define HAS_SHOWTHREADS +#endif /* get_process_info passes back a handle. This is what it looks like: */ -struct handle { - struct kinfo_proc **next_proc; /* points to next valid proc pointer */ - int remaining; /* number of pointers remaining */ +struct handle +{ + struct kinfo_proc **next_proc; /* points to next valid proc pointer */ + int remaining; /* number of pointers remaining */ }; /* declarations for load_avg */ #include "loadavg.h" -/* define what weighted cpu is. */ -#define weighted_cpu(pct, pp) ((pp)->ki_swtime == 0 ? 0.0 : \ - ((pct) / (1.0 - exp((pp)->ki_swtime * logcpu)))) - -/* what we consider to be process size: */ -#define PROCSIZE(pp) ((pp)->ki_size / 1024) - -#define RU(pp) (&(pp)->ki_rusage) -#define RUTOT(pp) \ - (RU(pp)->ru_inblock + RU(pp)->ru_oublock + RU(pp)->ru_majflt) - - -/* definitions for indices in the nlist array */ - /* - * These definitions control the format of the per-process area + * Macros to access process information: + * In versions 4.x and earlier the kinfo_proc structure was a collection of + * substructures (kp_proc and kp_eproc). Starting with 5.0 kinfo_proc was + * redesigned and "flattene" so that most of the information was available + * in a single structure. We use macros to access the various types of + * information and define these macros according to the OS revision. The + * names PP, EP, and VP are due to the fact that information was originally + * contained in the different substructures. We retain these names in the + * code for backward compatibility. These macros use ANSI concatenation. + * PP: proc + * EP: extented proc + * VP: vm (virtual memory information) + * PRUID: Real uid + * RP: rusage + * PPCPU: where we store calculated cpu% data + * SPPTR: where we store pointer to extra calculated data + * SP: access to the extra calculated data pointed to by SPPTR */ +#if OSMAJOR <= 4 +#define PP(pp, field) ((pp)->kp_proc . p_##field) +#define EP(pp, field) ((pp)->kp_eproc . e_##field) +#define VP(pp, field) ((pp)->kp_eproc.e_vm . vm_##field) +#define PRUID(pp) ((pp)->kp_eproc.e_pcred.p_ruid) +#else +#define PP(pp, field) ((pp)->ki_##field) +#define EP(pp, field) ((pp)->ki_##field) +#define VP(pp, field) ((pp)->ki_##field) +#define PRUID(pp) ((pp)->ki_ruid) +#define RP(pp, field) ((pp)->ki_rusage.ru_##field) +#define PPCPU(pp) ((pp)->ki_sparelongs[0]) +#define SPPTR(pp) ((pp)->ki_spareptrs[0]) +#define SP(pp, field) (((struct save_proc *)((pp)->ki_spareptrs[0]))->sp_##field) +#endif -static char io_header[] = - " PID%s %-*.*s VCSW IVCSW READ WRITE FAULT TOTAL PERCENT COMMAND"; +/* what we consider to be process size: */ +#if OSMAJOR <= 4 +#define PROCSIZE(pp) (VP((pp), map.size) / 1024) +#else +#define PROCSIZE(pp) (((pp)->ki_size) / 1024) +#endif -#define io_Proc_format \ - "%5d%s %-*.*s %6ld %6ld %6ld %6ld %6ld %6ld %6.2f%% %.*s" +/* calculate a per-second rate using milliseconds */ +#define per_second(n, msec) (((n) * 1000) / (msec)) -static char smp_header_thr[] = - " PID%s %-*.*s THR PRI NICE SIZE RES STATE C TIME %6s COMMAND"; -static char smp_header[] = - " PID%s %-*.*s " "PRI NICE SIZE RES STATE C TIME %6s COMMAND"; - -#define smp_Proc_format \ - "%5d%s %-*.*s %s%3d %4s%7s %6s %-6.6s %1x%7s %5.2f%% %.*s" - -static char up_header_thr[] = - " PID%s %-*.*s THR PRI NICE SIZE RES STATE TIME %6s COMMAND"; -static char up_header[] = - " PID%s %-*.*s " "PRI NICE SIZE RES STATE TIME %6s COMMAND"; - -#define up_Proc_format \ - "%5d%s %-*.*s %s%3d %4s%7s %6s %-6.6s%.0d%7s %5.2f%% %.*s" - - /* process state names for the "STATE" column of the display */ /* the extra nulls in the string "run" are for adding a slash and the processor number when needed */ -char *state_abbrev[] = { - "", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK" +char *state_abbrev[] = +{ + "?", "START", "RUN", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK" }; +#define NUM_STATES 8 - +/* kernel access */ static kvm_t *kd; -/* values that we stash away in _init and use in later routines */ +/* these are for dealing with sysctl-based data */ +#define MAXMIBLEN 8 +struct sysctl_mib { + char *name; + int mib[MAXMIBLEN]; + size_t miblen; +}; +static struct sysctl_mib mibs[] = { + { "vm.stats.sys.v_swtch" }, +#define V_SWTCH 0 + { "vm.stats.sys.v_trap" }, +#define V_TRAP 1 + { "vm.stats.sys.v_intr" }, +#define V_INTR 2 + { "vm.stats.sys.v_soft" }, +#define V_SOFT 3 + { "vm.stats.vm.v_forks" }, +#define V_FORKS 4 + { "vm.stats.vm.v_vforks" }, +#define V_VFORKS 5 + { "vm.stats.vm.v_rforks" }, +#define V_RFORKS 6 + { "vm.stats.vm.v_vm_faults" }, +#define V_VM_FAULTS 7 + { "vm.stats.vm.v_swapin" }, +#define V_SWAPIN 8 + { "vm.stats.vm.v_swapout" }, +#define V_SWAPOUT 9 + { "vm.stats.vm.v_tfree" }, +#define V_TFREE 10 + { "vm.stats.vm.v_vnodein" }, +#define V_VNODEIN 11 + { "vm.stats.vm.v_vnodeout" }, +#define V_VNODEOUT 12 + { "vm.stats.vm.v_active_count" }, +#define V_ACTIVE_COUNT 13 + { "vm.stats.vm.v_inactive_count" }, +#define V_INACTIVE_COUNT 14 + { "vm.stats.vm.v_wire_count" }, +#define V_WIRE_COUNT 15 + { "vm.stats.vm.v_cache_count" }, +#define V_CACHE_COUNT 16 + { "vm.stats.vm.v_free_count" }, +#define V_FREE_COUNT 17 + { "vm.stats.vm.v_swappgsin" }, +#define V_SWAPPGSIN 18 + { "vm.stats.vm.v_swappgsout" }, +#define V_SWAPPGSOUT 19 + { "vfs.bufspace" }, +#define VFS_BUFSPACE 20 + { "kern.cp_time" }, +#define K_CP_TIME 21 + { "kern.cp_times" }, +#define K_CP_TIMES 22 +#ifdef HAS_SHOWTHREADS + { "kern.proc.all" }, +#else + { "kern.proc.proc" }, +#endif +#define K_PROC 23 +#define K_LASTPID 24 + { "kern.lastpid" }, + { NULL } +}; + +/* Local pointer */ +static struct statics *statics; -static double logcpu; - -/* these are retrieved from the kernel in _init */ - -static load_avg ccpu; - -/* these are used in the get_ functions */ - -static int lastpid; - /* these are for calculating cpu state percentages */ -static long cp_time[CPUSTATES]; -static long cp_old[CPUSTATES]; -static long cp_diff[CPUSTATES]; +static long *cp_time; /* num_cpus * CPUSTATES */ +static long *cp_old; /* num_cpus * CPUSTATES */ +static long *cp_diff; /* num_cpus * CPUSTATES */ /* these are for detailing the process states */ int process_states[8]; char *procstatenames[] = { - "", " starting, ", " running, ", " sleeping, ", " stopped, ", - " zombie, ", " waiting, ", " lock, ", - NULL + "", " starting, ", " running, ", " sleeping, ", " stopped, ", " zombie, ", + " waiting, ", " locked, ", + NULL }; /* these are for detailing the cpu states */ -int cpu_states[CPUSTATES]; +int *cpu_states; char *cpustatenames[] = { - "user", "nice", "system", "interrupt", "idle", NULL + "user", "nice", "system", "interrupt", "idle", NULL }; +/* these are for detailing the kernel information */ + +int kernel_stats[9]; +char *kernelnames[] = { + " ctxsw, ", " trap, ", " intr, ", " soft, ", " fork, ", + " flt, ", " pgin, ", " pgout, ", " fr", + NULL +}; + /* these are for detailing the memory statistics */ -int memory_stats[7]; +long memory_stats[7]; char *memorynames[] = { - "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", - "K Free", NULL + "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free", + NULL }; -int swap_stats[7]; +long swap_stats[7]; char *swapnames[] = { - "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", - NULL +/* 0 1 2 3 4 5 */ + "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out", + NULL }; -/* these are for keeping track of the proc array */ +/* + * pbase points to the array that holds the kinfo_proc structures. pref + * (pronounced p-ref) points to an array of kinfo_proc pointers and is where + * we build up a list of processes we wish to display. Both pbase and pref are + * potentially resized on every call to get_process_info. psize is the number + * of procs for which we currently have space allocated. pref_len is the number + * of valid pointers in pref (this is used by proc_owner). We start psize off + * at -1 to ensure that space gets allocated on the first call to + * get_process_info. + */ -static int nproc; -static int onproc = -1; +static int psize = -1; static int pref_len; -static struct kinfo_proc *pbase; -static struct kinfo_proc **pref; -static struct kinfo_proc *previous_procs; -static struct kinfo_proc **previous_pref; -static int previous_proc_count = 0; -static int previous_proc_count_max = 0; +static struct kinfo_proc *pbase = NULL; +static struct kinfo_proc **pref = NULL; -/* total number of io operations */ -static long total_inblock; -static long total_oublock; -static long total_majflt; +/* this structure retains information from the proc array between samples */ +struct save_proc { + pid_t sp_pid; + u_int64_t sp_runtime; + long sp_vcsw; + long sp_ivcsw; + long sp_inblock; + long sp_oublock; + long sp_majflt; + long sp_totalio; + long sp_old_nvcsw; + long sp_old_nivcsw; + long sp_old_inblock; + long sp_old_oublock; + long sp_old_majflt; +}; +hash_table *procs; +struct proc_field { + char *name; + int width; + int rjust; + int min_screenwidth; + int (*format)(char *, int, struct kinfo_proc *); +}; + /* these are for getting the memory statistics */ +static int pagesize; /* kept from getpagesize */ static int pageshift; /* log base 2 of the pagesize */ /* define pagetok in terms of pageshift */ #define pagetok(size) ((size) << pageshift) -/* useful externals */ -long percentages(); +/* things that we track between updates */ +static u_int ctxsws = 0; +static u_int traps = 0; +static u_int intrs = 0; +static u_int softs = 0; +static u_int64_t forks = 0; +static u_int pfaults; +static u_int pagein; +static u_int pageout; +static u_int tfreed; +static int swappgsin = -1; +static int swappgsout = -1; +extern struct timeval timeout; +static struct timeval lasttime = { 0, 0 }; +static long elapsed_time; +static long elapsed_msecs; -#ifdef ORDER -/* - * Sorting orders. The first element is the default. - */ +/* things that we track during an update */ +static long total_io; +static int show_fullcmd; +static struct handle handle; +static int username_length; +static int show_usernames; +static int display_mode; +static int *display_fields; +#ifdef HAS_SHOWTHREADS +static int show_threads = 0; +#endif + + +/* sorting orders. first is default */ char *ordernames[] = { - "cpu", "size", "res", "time", "pri", "threads", - "total", "read", "write", "fault", "vcsw", "ivcsw", - "jid", NULL + "cpu", "size", "res", "time", "pri", "io", "pid", "jid", NULL }; -#endif -/* Per-cpu time states */ -static int maxcpu; -static int maxid; -static int ncpus; -static u_long cpumask; -static long *times; -static long *pcpu_cp_time; -static long *pcpu_cp_old; -static long *pcpu_cp_diff; -static int *pcpu_cpu_states; +/* compare routines */ +int proc_compare(), compare_size(), compare_res(), compare_time(), + compare_prio(), compare_io(), compare_pid(), compare_jid(); -static int compare_jid(const void *a, const void *b); -static int compare_pid(const void *a, const void *b); -static const char *format_nice(const struct kinfo_proc *pp); -static void getsysctl(const char *name, void *ptr, size_t len); -static int swapmode(int *retavail, int *retfree); +int (*proc_compares[])() = { + proc_compare, + compare_size, + compare_res, + compare_time, + compare_prio, + compare_io, + compare_pid, + compare_jid, + NULL +}; +/* swap related calculations */ + +static int mib_swapinfo[16]; +static int *mib_swapinfo_idx; +static int mib_swapinfo_size = 0; + +void +swap_init() + +{ + size_t m; + + m = sizeof(mib_swapinfo) / sizeof(mib_swapinfo[0]); + if (sysctlnametomib("vm.swap_info", mib_swapinfo, &m) != -1) + { + mib_swapinfo_size = m + 1; + mib_swapinfo_idx = &(mib_swapinfo[m]); + } +} + int -machine_init(struct statics *statics, char do_unames) +swap_getdata(long long *retavail, long long *retfree) + { - int pagesize; - size_t modelen; - struct passwd *pw; + int n; + size_t size; + long long total = 0; + long long used = 0; + struct xswdev xsw; - modelen = sizeof(smpmode); - if ((sysctlbyname("machdep.smp_active", &smpmode, &modelen, - NULL, 0) != 0 && - sysctlbyname("kern.smp.active", &smpmode, &modelen, - NULL, 0) != 0) || - modelen != sizeof(smpmode)) - smpmode = 0; + n = 0; + if (mib_swapinfo_size > 0) + { + *mib_swapinfo_idx = 0; + while (size = sizeof(xsw), + sysctl(mib_swapinfo, mib_swapinfo_size, &xsw, &size, NULL, 0) != -1) + { + dprintf("swap_getdata: swaparea %d: nblks %d, used %d\n", + n, xsw.xsw_nblks, xsw.xsw_used); + total += (long long)xsw.xsw_nblks; + used += (long long)xsw.xsw_used; + *mib_swapinfo_idx = ++n; + } - if (do_unames) { - while ((pw = getpwent()) != NULL) { - if (strlen(pw->pw_name) > namelength) - namelength = strlen(pw->pw_name); - } + *retavail = pagetok(total); + *retfree = pagetok(total) - pagetok(used); + + if (total > 0) + { + n = (int)((double)used * 100.0 / (double)total); } - if (smpmode && namelength > SMPUNAMELEN) - namelength = SMPUNAMELEN; - else if (namelength > UPUNAMELEN) - namelength = UPUNAMELEN; + else + { + n = 0; + } + } + else + { + *retavail = 0; + *retfree = 0; + } - kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); - if (kd == NULL) - return (-1); + dprintf("swap_getdata: avail %lld, free %lld, %d%%\n", + *retavail, *retfree, n); + return(n); +} - GETSYSCTL("kern.ccpu", ccpu); +/* + * getkval(offset, ptr, size) - get a value out of the kernel. + * "offset" is the byte offset into the kernel for the desired value, + * "ptr" points to a buffer into which the value is retrieved, + * "size" is the size of the buffer (and the object to retrieve). + * Return 0 on success, -1 on any kind of failure. + */ +#ifdef notmore +static int +getkval(unsigned long offset, int *ptr, int size) - /* this is used in calculating WCPU -- calculate it ahead of time */ - logcpu = log(loaddouble(ccpu)); +{ + if (kd != NULL) + { + if (kvm_read(kd, offset, (char *) ptr, size) == size) + { + return(0); + } + } + return(-1); +} +#endif - pbase = NULL; - pref = NULL; - nproc = 0; - onproc = -1; +int +get_sysctl_mibs() - /* get the page size and calculate pageshift from it */ - pagesize = getpagesize(); - pageshift = 0; - while (pagesize > 1) { - pageshift++; - pagesize >>= 1; +{ + struct sysctl_mib *mp; + size_t len; + + mp = mibs; + while (mp->name != NULL) + { + len = MAXMIBLEN; + if (sysctlnametomib(mp->name, mp->mib, &len) == -1) + { + message_error(" sysctlnametomib: %s", strerror(errno)); + return -1; } + mp->miblen = len; + mp++; + } + return 0; +} - /* we only need the amount of log(2)1024 for our conversion */ - pageshift -= LOG1024; +int +get_sysctl(int idx, void *v, size_t l) - /* fill in the statics information */ - statics->procstate_names = procstatenames; - statics->cpustate_names = cpustatenames; - statics->memory_names = memorynames; - statics->swap_names = swapnames; -#ifdef ORDER - statics->order_names = ordernames; +{ + struct sysctl_mib *m; + size_t len; + + m = &(mibs[idx]); + len = l; + if (sysctl(m->mib, m->miblen, v, &len, NULL, 0) == -1) + { + message_error(" sysctl: %s", strerror(errno)); + return -1; + } + return len; +} + +size_t +get_sysctlsize(int idx) + +{ + size_t len; + struct sysctl_mib *m; + + m = &(mibs[idx]); + if (sysctl(m->mib, m->miblen, NULL, &len, NULL, 0) == -1) + { + message_error(" sysctl (size): %s", strerror(errno)); + len = 0; + } + return len; +} + +int +fmt_pid(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6d", PP(pp, pid)); +} + +int +fmt_jid(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%3d", pp->ki_jid); +} + +int +fmt_username(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%-*.*s", + username_length, username_length, username(PRUID(pp))); +} + +int +fmt_uid(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6d", PRUID(pp)); +} + +int +fmt_thr(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%3d", PP(pp, numthreads)); +} + +int +fmt_pri(char *buf, int sz, struct kinfo_proc *pp) + +{ +#if OSMAJOR <= 4 + return snprintf(buf, sz, "%3d", PP(pp, priority)); +#else + return snprintf(buf, sz, "%3d", PP(pp, pri.pri_level)); #endif +} - /* Adjust display based on ncpus */ - if (pcpu_stats) { - int i, j, empty; - size_t size; +int +fmt_nice(char *buf, int sz, struct kinfo_proc *pp) - cpumask = 0; - ncpus = 0; - GETSYSCTL("kern.smp.maxcpus", maxcpu); - size = sizeof(long) * maxcpu * CPUSTATES; - times = malloc(size); - if (times == NULL) - err(1, "malloc %zd bytes", size); - if (sysctlbyname("kern.cp_times", times, &size, NULL, 0) == -1) - err(1, "sysctlbyname kern.cp_times"); - pcpu_cp_time = calloc(1, size); - maxid = (size / CPUSTATES / sizeof(long)) - 1; - for (i = 0; i <= maxid; i++) { - empty = 1; - for (j = 0; empty && j < CPUSTATES; j++) { - if (times[i * CPUSTATES + j] != 0) - empty = 0; - } - if (!empty) { - cpumask |= (1ul << i); - ncpus++; - } - } +{ + return snprintf(buf, sz, "%4d", PP(pp, nice) - NZERO); +} - if (ncpus > 1) { - y_mem += ncpus - 1; /* 3 */ - y_swap += ncpus - 1; /* 4 */ - y_idlecursor += ncpus - 1; /* 5 */ - y_message += ncpus - 1; /* 5 */ - y_header += ncpus - 1; /* 6 */ - y_procs += ncpus - 1; /* 7 */ - Header_lines += ncpus - 1; /* 7 */ - } - size = sizeof(long) * ncpus * CPUSTATES; - pcpu_cp_old = calloc(1, size); - pcpu_cp_diff = calloc(1, size); - pcpu_cpu_states = calloc(1, size); - statics->ncpus = ncpus; - } else { - statics->ncpus = 1; - } +int +fmt_size(char *buf, int sz, struct kinfo_proc *pp) - /* all done! */ - return (0); +{ + return snprintf(buf, sz, "%5s", format_k(PROCSIZE(pp))); } -char * -format_header(char *uname_field) +int +fmt_res(char *buf, int sz, struct kinfo_proc *pp) + { - static char Header[128]; - const char *prehead; + return snprintf(buf, sz, "%5s", format_k(pagetok(VP(pp, rssize)))); +} - switch (displaymode) { - case DISP_CPU: - /* - * The logic of picking the right header format seems reverse - * here because we only want to display a THR column when - * "thread mode" is off (and threads are not listed as - * separate lines). - */ - prehead = smpmode ? - (ps.thread ? smp_header : smp_header_thr) : - (ps.thread ? up_header : up_header_thr); - snprintf(Header, sizeof(Header), prehead, - ps.jail ? " JID" : "", - namelength, namelength, uname_field, - ps.wcpu ? "WCPU" : "CPU"); - break; - case DISP_IO: - prehead = io_header; - snprintf(Header, sizeof(Header), prehead, - ps.jail ? " JID" : "", - namelength, namelength, uname_field); - break; +int +fmt_state(char *buf, int sz, struct kinfo_proc *pp) + +{ + int state; + char status[16]; + + state = PP(pp, stat); + switch(state) + { + case SRUN: + if (smpmode && PP(pp, oncpu) != 0xff) + sprintf(status, "CPU%d", PP(pp, oncpu)); + else + strcpy(status, "RUN"); + break; + + case SSLEEP: + if (EP(pp, wmesg) != NULL) { + sprintf(status, "%.6s", EP(pp, wmesg)); + break; } - cmdlengthdelta = strlen(Header) - 7; - return (Header); + /* fall through */ + default: + if (state >= 0 && state < NUM_STATES) + sprintf(status, "%.6s", state_abbrev[(unsigned char) state]); + else + sprintf(status, "?%-5d", state); + break; + } + + return snprintf(buf, sz, "%-6.6s", status); } -static int swappgsin = -1; -static int swappgsout = -1; -extern struct timeval timeout; +int +fmt_flags(char *buf, int sz, struct kinfo_proc *pp) +{ + long flag; + char chrs[12]; + char *p; -void -get_system_info(struct system_info *si) + flag = PP(pp, flag); + p = chrs; + if (PP(pp, nice) < NZERO) + *p++ = '<'; + else if (PP(pp, nice) > NZERO) + *p++ = 'N'; + if (flag & P_TRACED) + *p++ = 'X'; + if (flag & P_WEXIT && PP(pp, stat) != SZOMB) + *p++ = 'E'; + if (flag & P_PPWAIT) + *p++ = 'V'; + if (flag & P_SYSTEM || PP(pp, lock) > 0) + *p++ = 'L'; + if (PP(pp, kiflag) & KI_SLEADER) + *p++ = 's'; + if (flag & P_CONTROLT) + *p++ = '+'; + if (flag & P_JAILED) + *p++ = 'J'; + *p = '\0'; + + return snprintf(buf, sz, "%-3.3s", chrs); +} + +int +fmt_c(char *buf, int sz, struct kinfo_proc *pp) + { - long total; - struct loadavg sysload; - int mib[2]; - struct timeval boottime; - size_t bt_size; - int i, j; - size_t size; + return snprintf(buf, sz, "%1x", PP(pp, lastcpu)); +} - /* get the cp_time array */ - if (pcpu_stats) { - size = (maxid + 1) * CPUSTATES * sizeof(long); - if (sysctlbyname("kern.cp_times", pcpu_cp_time, &size, NULL, 0) == -1) - err(1, "sysctlbyname kern.cp_times"); - } else { - GETSYSCTL("kern.cp_time", cp_time); - } - GETSYSCTL("vm.loadavg", sysload); - GETSYSCTL("kern.lastpid", lastpid); +int +fmt_time(char *buf, int sz, struct kinfo_proc *pp) - /* convert load averages to doubles */ - for (i = 0; i < 3; i++) - si->load_avg[i] = (double)sysload.ldavg[i] / sysload.fscale; +{ + return snprintf(buf, sz, "%6s", + format_time((PP(pp, runtime) + 500000) / 1000000)); +} - if (pcpu_stats) { - for (i = j = 0; i <= maxid; i++) { - if ((cpumask & (1ul << i)) == 0) - continue; - /* convert cp_time counts to percentages */ - percentages(CPUSTATES, &pcpu_cpu_states[j * CPUSTATES], - &pcpu_cp_time[j * CPUSTATES], - &pcpu_cp_old[j * CPUSTATES], - &pcpu_cp_diff[j * CPUSTATES]); - j++; - } - } else { - /* convert cp_time counts to percentages */ - percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); - } +int +fmt_cpu(char *buf, int sz, struct kinfo_proc *pp) - /* sum memory & swap statistics */ - { - static unsigned int swap_delay = 0; - static int swapavail = 0; - static int swapfree = 0; - static int bufspace = 0; - static int nspgsin, nspgsout; +{ + return snprintf(buf, sz, "%5.2f%%", (double)PPCPU(pp) / 100.0); +} - GETSYSCTL("vfs.bufspace", bufspace); - GETSYSCTL("vm.stats.vm.v_active_count", memory_stats[0]); - GETSYSCTL("vm.stats.vm.v_inactive_count", memory_stats[1]); - GETSYSCTL("vm.stats.vm.v_wire_count", memory_stats[2]); - GETSYSCTL("vm.stats.vm.v_cache_count", memory_stats[3]); - GETSYSCTL("vm.stats.vm.v_free_count", memory_stats[5]); - GETSYSCTL("vm.stats.vm.v_swappgsin", nspgsin); - GETSYSCTL("vm.stats.vm.v_swappgsout", nspgsout); - /* convert memory stats to Kbytes */ - memory_stats[0] = pagetok(memory_stats[0]); - memory_stats[1] = pagetok(memory_stats[1]); - memory_stats[2] = pagetok(memory_stats[2]); - memory_stats[3] = pagetok(memory_stats[3]); - memory_stats[4] = bufspace / 1024; - memory_stats[5] = pagetok(memory_stats[5]); - memory_stats[6] = -1; +int +fmt_command(char *buf, int sz, struct kinfo_proc *pp) - /* first interval */ - if (swappgsin < 0) { - swap_stats[4] = 0; - swap_stats[5] = 0; - } +{ + int inmem; + char cmd[MAX_COLS]; + char *bufp; + char **args; + int argc; - /* compute differences between old and new swap statistic */ - else { - swap_stats[4] = pagetok(((nspgsin - swappgsin))); - swap_stats[5] = pagetok(((nspgsout - swappgsout))); - } +#if OSMAJOR <= 4 + inmem = (PP(pp, flag) & P_INMEM); +#else + inmem = (PP(pp, sflag) & PS_INMEM); +#endif - swappgsin = nspgsin; - swappgsout = nspgsout; + if (show_fullcmd && inmem) + { + /* get the pargs structure */ + if ((args = kvm_getargv(kd, pp, sz)) != NULL) + { + /* successfull retrieval: now convert nulls in to spaces */ + bufp = cmd; + cmd[0] = '\0'; + argc = 0; + while (args[argc] != NULL) + { + if (cmd[0] != '\0') + strcat(cmd, " "); + strcat(cmd, args[argc++]); + } - /* call CPU heavy swapmode() only for changes */ - if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) { - swap_stats[3] = swapmode(&swapavail, &swapfree); - swap_stats[0] = swapavail; - swap_stats[1] = swapavail - swapfree; - swap_stats[2] = swapfree; - } - swap_delay = 1; - swap_stats[6] = -1; - } + /* format cmd as our answer */ + return snprintf(buf, sz, "%s", cmd); + } + } - /* set arrays and strings */ - if (pcpu_stats) { - si->cpustates = pcpu_cpu_states; - si->ncpus = ncpus; - } else { - si->cpustates = cpu_states; - si->ncpus = 1; - } - si->memory = memory_stats; - si->swap = swap_stats; + /* for anything else we just display comm */ + return snprintf(buf, sz, inmem ? "%s" : "<%s>", printable(PP(pp, comm))); +} +int +fmt_vcsw(char *buf, int sz, struct kinfo_proc *pp) - if (lastpid > 0) { - si->last_pid = lastpid; - } else { - si->last_pid = -1; - } +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, vcsw), elapsed_msecs)); +} - /* - * Print how long system has been up. - * (Found by looking getting "boottime" from the kernel) - */ - mib[0] = CTL_KERN; - mib[1] = KERN_BOOTTIME; - bt_size = sizeof(boottime); - if (sysctl(mib, 2, &boottime, &bt_size, NULL, 0) != -1 && - boottime.tv_sec != 0) { - si->boottime = boottime; - } else { - si->boottime.tv_sec = -1; - } +int +fmt_ivcsw(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, ivcsw), elapsed_msecs)); } -#define NOPROC ((void *)-1) +int +fmt_read(char *buf, int sz, struct kinfo_proc *pp) -/* - * We need to compare data from the old process entry with the new - * process entry. - * To facilitate doing this quickly we stash a pointer in the kinfo_proc - * structure to cache the mapping. We also use a negative cache pointer - * of NOPROC to avoid duplicate lookups. - * XXX: this could be done when the actual processes are fetched, we do - * it here out of laziness. - */ -const struct kinfo_proc * -get_old_proc(struct kinfo_proc *pp) { - struct kinfo_proc **oldpp, *oldp; + return snprintf(buf, sz, "%6ld", per_second(SP(pp, inblock), elapsed_msecs)); +} - /* - * If this is the first fetch of the kinfo_procs then we don't have - * any previous entries. - */ - if (previous_proc_count == 0) - return (NULL); - /* negative cache? */ - if (pp->ki_udata == NOPROC) - return (NULL); - /* cached? */ - if (pp->ki_udata != NULL) - return (pp->ki_udata); - /* - * Not cached, - * 1) look up based on pid. - * 2) compare process start. - * If we fail here, then setup a negative cache entry, otherwise - * cache it. - */ - oldpp = bsearch(&pp, previous_pref, previous_proc_count, - sizeof(*previous_pref), compare_pid); - if (oldpp == NULL) { - pp->ki_udata = NOPROC; - return (NULL); - } - oldp = *oldpp; - if (bcmp(&oldp->ki_start, &pp->ki_start, sizeof(pp->ki_start)) != 0) { - pp->ki_udata = NOPROC; - return (NULL); - } - pp->ki_udata = oldp; - return (oldp); +int +fmt_write(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, oublock), elapsed_msecs)); } -/* - * Return the total amount of IO done in blocks in/out and faults. - * store the values individually in the pointers passed in. - */ -long -get_io_stats(struct kinfo_proc *pp, long *inp, long *oup, long *flp, - long *vcsw, long *ivcsw) +int +fmt_fault(char *buf, int sz, struct kinfo_proc *pp) + { - const struct kinfo_proc *oldp; - static struct kinfo_proc dummy; - long ret; + return snprintf(buf, sz, "%6ld", per_second(SP(pp, majflt), elapsed_msecs)); +} - oldp = get_old_proc(pp); - if (oldp == NULL) { - bzero(&dummy, sizeof(dummy)); - oldp = &dummy; - } - *inp = RU(pp)->ru_inblock - RU(oldp)->ru_inblock; - *oup = RU(pp)->ru_oublock - RU(oldp)->ru_oublock; - *flp = RU(pp)->ru_majflt - RU(oldp)->ru_majflt; - *vcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw; - *ivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw; - ret = - (RU(pp)->ru_inblock - RU(oldp)->ru_inblock) + - (RU(pp)->ru_oublock - RU(oldp)->ru_oublock) + - (RU(pp)->ru_majflt - RU(oldp)->ru_majflt); - return (ret); +int +fmt_iototal(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6ld", per_second(SP(pp, totalio), elapsed_msecs)); } +int +fmt_iopct(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%6.2f%%", (SP(pp, totalio) * 100.) / total_io); +} + + /* - * Return the total number of block in/out and faults by a process. + * The order of proc_fields and proc_field must match! */ -long -get_io_total(struct kinfo_proc *pp) + +enum proc_fields { + FIELD_PID = 0, FIELD_JID, FIELD_USERNAME, FIELD_UID, + FIELD_THR, FIELD_PRI, FIELD_NICE, FIELD_SIZE, FIELD_RES, + FIELD_STATE, FIELD_FLG, FIELD_C, FIELD_TIME, FIELD_CPU, + FIELD_WCPU, FIELD_COMMAND, FIELD_VCSW, FIELD_IVCSW, FIELD_READ, + FIELD_WRITE, FIELD_FAULT, FIELD_TOTAL, FIELD_PERCENT + +}; + +#define MAX_FIELDS 25 +struct proc_field proc_field[MAX_FIELDS] = { + { "PID", 6, 1, 0, fmt_pid }, + { "JID", 3, 1, 0, fmt_jid }, + { "USERNAME", 8, 0, 0, fmt_username }, + { "UID", 6, 1, 0, fmt_uid }, + { "THR", 3, 1, 0, fmt_thr }, + { "PRI", 3, 1, 0, fmt_pri }, + { "NICE", 4, 1, 0, fmt_nice }, + { "SIZE", 5, 1, 0, fmt_size }, + { "RES", 5, 1, 0, fmt_res }, + { "STATE", 6, 0, 0, fmt_state }, + { "FLG", 3, 0, 84, fmt_flags }, + { "C", 1, 0, 0, fmt_c }, + { "TIME", 6, 1, 0, fmt_time }, + { "CPU", 6, 1, 0, fmt_cpu }, + { "WCPU", 6, 1, 0, fmt_cpu }, + { "COMMAND", 7, 0, 0, fmt_command }, + { "VCSW", 6, 1, 0, fmt_vcsw }, + { "IVCSW", 6, 1, 0, fmt_ivcsw }, + { "READ", 6, 1, 0, fmt_read }, + { "WRITE", 6, 1, 0, fmt_write }, + { "FAULT", 6, 1, 0, fmt_fault }, + { "TOTAL", 6, 1, 0, fmt_iototal }, + { "PERCENT", 7, 1, 0, fmt_iopct }, + { NULL, 0, 0, 0, NULL } +}; + +static int mode0_display[MAX_FIELDS]; +static int mode1_display[MAX_FIELDS]; + +int +field_index(char *col) + { - long dummy; + struct proc_field *fp; + int i = 0; - return (get_io_stats(pp, &dummy, &dummy, &dummy, &dummy, &dummy)); + fp = proc_field; + while (fp->name != NULL) + { + if (strcmp(col, fp->name) == 0) + { + return i; + } + fp++; + i++; + } + + return -1; } -static struct handle handle; +void +field_subst(int *fp, int old, int new) -caddr_t -get_process_info(struct system_info *si, struct process_select *sel, - int (*compare)(const void *, const void *)) { - int i; - int total_procs; - long p_io; - long p_inblock, p_oublock, p_majflt, p_vcsw, p_ivcsw; - int active_procs; - struct kinfo_proc **prefp; - struct kinfo_proc *pp; - struct kinfo_proc *prev_pp = NULL; + while (*fp != -1) + { + if (*fp == old) + { + *fp = new; + } + fp++; + } +} - /* these are copied out of sel for speed */ - int show_idle; - int show_self; - int show_system; - int show_uid; - int show_command; +int +machine_init(struct statics *_statics) - /* - * Save the previous process info. - */ - if (previous_proc_count_max < nproc) { - free(previous_procs); - previous_procs = malloc(nproc * sizeof(*previous_procs)); - free(previous_pref); - previous_pref = malloc(nproc * sizeof(*previous_pref)); - if (previous_procs == NULL || previous_pref == NULL) { - (void) fprintf(stderr, "top: Out of memory.\n"); - quit(23); - } - previous_proc_count_max = nproc; - } - if (nproc) { - for (i = 0; i < nproc; i++) - previous_pref[i] = &previous_procs[i]; - bcopy(pbase, previous_procs, nproc * sizeof(*previous_procs)); - qsort(previous_pref, nproc, sizeof(*previous_pref), - compare_pid); - } - previous_proc_count = nproc; +{ + int i = 0; + size_t len; + int *ip; - pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); - if (nproc > onproc) - pref = realloc(pref, sizeof(*pref) * (onproc = nproc)); - if (pref == NULL || pbase == NULL) { - (void) fprintf(stderr, "top: Out of memory.\n"); - quit(23); - } - /* get a pointer to the states summary array */ - si->procstates = process_states; + statics = _statics; /* Keep a local copy */ - /* set up flags which define what we are going to select */ - show_idle = sel->idle; - show_self = sel->self == -1; - show_system = sel->system; - show_uid = sel->uid != -1; - show_command = sel->command != NULL; + struct timeval boottime; - /* count up process states and get pointers to interesting procs */ - total_procs = 0; - active_procs = 0; - total_inblock = 0; - total_oublock = 0; - total_majflt = 0; - memset((char *)process_states, 0, sizeof(process_states)); - prefp = pref; - for (pp = pbase, i = 0; i < nproc; pp++, i++) { + len = sizeof(statics->ncpus); + if (sysctlbyname("kern.smp.cpus", &(statics->ncpus), &len, NULL, 0) < 0 || + len != sizeof(statics->ncpus)) + statics->ncpus = 1; + smpmode = statics->ncpus != 1; - if (pp->ki_stat == 0) - /* not in use */ - continue; + len = sizeof(statics->maxcpus); + if (sysctlbyname("kern.smp.maxcpus", &(statics->maxcpus), &len, NULL, 0) < 0 || + len != sizeof(statics->maxcpus)) + statics->maxcpus = 1; - if (!show_self && pp->ki_pid == sel->self) - /* skip self */ - continue; + cp_time = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long)); + cp_old = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long)); + cp_diff = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long)); + cpu_states = (int *)calloc(statics->maxcpus * CPUSTATES, sizeof(int)); - if (!show_system && (pp->ki_flag & P_SYSTEM)) - /* skip system process */ - continue; + /* kvm_open the active kernel: its okay if this fails */ + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL); - p_io = get_io_stats(pp, &p_inblock, &p_oublock, &p_majflt, - &p_vcsw, &p_ivcsw); - total_inblock += p_inblock; - total_oublock += p_oublock; - total_majflt += p_majflt; - total_procs++; - process_states[pp->ki_stat]++; + /* get boot time */ + len = sizeof(boottime); + if (sysctlbyname("kern.boottime", &boottime, &len, NULL, 0) == -1) + { + /* we have no boottime to report */ + boottime.tv_sec = -1; + } - if (pp->ki_stat == SZOMB) - /* skip zombies */ - continue; + pbase = NULL; + pref = NULL; - if (displaymode == DISP_CPU && !show_idle && - (pp->ki_pctcpu == 0 || - pp->ki_stat == SSTOP || pp->ki_stat == SIDL)) - /* skip idle or non-running processes */ - continue; + /* get the page size with "getpagesize" and calculate pageshift from it */ + i = pagesize = getpagesize(); + pageshift = 0; + while (i > 1) + { + pageshift++; + i >>= 1; + } - if (displaymode == DISP_IO && !show_idle && p_io == 0) - /* skip processes that aren't doing I/O */ - continue; + /* translate sysctl paths to mibs for faster access */ + get_sysctl_mibs(); - if (show_uid && pp->ki_ruid != (uid_t)sel->uid) - /* skip proc. that don't belong to the selected UID */ - continue; + /* initialize swap stuff */ + swap_init(); - /* - * When not showing threads, take the first thread - * for output and add the fields that we can from - * the rest of the process's threads rather than - * using the system's mostly-broken KERN_PROC_PROC. - */ - if (sel->thread || prev_pp == NULL || - prev_pp->ki_pid != pp->ki_pid) { - *prefp++ = pp; - active_procs++; - prev_pp = pp; - } else { - prev_pp->ki_pctcpu += pp->ki_pctcpu; - prev_pp->ki_runtime += pp->ki_runtime; - } - } + /* create the hash table that remembers proc data */ + procs = hash_create(2039); - /* if requested, sort the "interesting" processes */ - if (compare != NULL) - qsort(pref, active_procs, sizeof(*pref), compare); + /* we only need the amount of log(2)1024 for our conversion */ + pageshift -= LOG1024; - /* remember active and total counts */ - si->p_total = total_procs; - si->p_active = pref_len = active_procs; + /* fill in the statics information */ + statics->procstate_names = procstatenames; + statics->cpustate_names = cpustatenames; + statics->memory_names = memorynames; + statics->kernel_names = kernelnames; + statics->boottime = boottime.tv_sec; + statics->swap_names = swapnames; + statics->order_names = ordernames; + statics->flags.warmup = 0; + statics->modemax = 2; +#ifdef HAS_SHOWTHREADS + statics->flags.threads = 1; +#endif - /* pass back a handle */ - handle.next_proc = pref; - handle.remaining = active_procs; - return ((caddr_t)&handle); + /* we need kvm descriptor in order to show full commands */ + statics->flags.fullcmds = kd != NULL; + + /* set up the display indices for mode0 (DISP_CPU) */ + ip = mode0_display; + *ip++ = field_index("PID"); + *ip++ = field_index("JID"); + *ip++ = field_index("USERNAME"); +#ifdef HAS_THREADS + *ip++ = field_index("THR"); +#endif + *ip++ = field_index("PRI"); + *ip++ = field_index("NICE"); + *ip++ = field_index("SIZE"); + *ip++ = field_index("RES"); + *ip++ = field_index("STATE"); + *ip++ = field_index("FLG"); + *ip++ = field_index("C"); + *ip++ = field_index("TIME"); + *ip++ = field_index("CPU"); + *ip++ = field_index("COMMAND"); + *ip = -1; + + /* set up the display indices for mode1 (DISP_IO) */ + ip = mode1_display; + *ip++ = field_index("PID"); + *ip++ = field_index("JID"); + *ip++ = field_index("USERNAME"); + *ip++ = field_index("VCSW"); + *ip++ = field_index("IVCSW"); + *ip++ = field_index("READ"); + *ip++ = field_index("WRITE"); + *ip++ = field_index("FAULT"); + *ip++ = field_index("TOTAL"); + *ip++ = field_index("PERCENT"); + *ip++ = field_index("COMMAND"); + *ip = -1; + + /* all done! */ + return(0); } -static char fmt[128]; /* static area where result is built */ +char *format_header(char *uname_field) -char * -format_next_process(caddr_t handle, char *(*get_userid)(int), int flags) { - struct kinfo_proc *pp; - const struct kinfo_proc *oldp; - long cputime; - double pct; - struct handle *hp; - char status[16]; - int state; - struct rusage ru, *rup; - long p_tot, s_tot; - char *proc_fmt, thr_buf[6], jid_buf[6]; - char *cmdbuf = NULL; - char **args; + return ""; +} - /* find and remember the next proc structure */ - hp = (struct handle *)handle; - pp = *(hp->next_proc++); - hp->remaining--; +void +get_vm_sum(struct vmmeter *sum) - /* get the process's command name */ - if ((pp->ki_flag & P_INMEM) == 0) { - /* - * Print swapped processes as <pname> - */ - size_t len; +{ +#define GET_VM_STAT(v, s) (void)get_sysctl(v, &(sum->s), sizeof(sum->s)) - len = strlen(pp->ki_comm); - if (len > sizeof(pp->ki_comm) - 3) - len = sizeof(pp->ki_comm) - 3; - memmove(pp->ki_comm + 1, pp->ki_comm, len); - pp->ki_comm[0] = '<'; - pp->ki_comm[len + 1] = '>'; - pp->ki_comm[len + 2] = '\0'; - } + GET_VM_STAT(V_SWTCH, v_swtch); + GET_VM_STAT(V_TRAP, v_trap); + GET_VM_STAT(V_INTR, v_intr); + GET_VM_STAT(V_SOFT, v_soft); + GET_VM_STAT(V_VFORKS, v_vforks); + GET_VM_STAT(V_FORKS, v_forks); + GET_VM_STAT(V_RFORKS, v_rforks); + GET_VM_STAT(V_VM_FAULTS, v_vm_faults); + GET_VM_STAT(V_SWAPIN, v_swapin); + GET_VM_STAT(V_SWAPOUT, v_swapout); + GET_VM_STAT(V_TFREE, v_tfree); + GET_VM_STAT(V_VNODEIN, v_vnodein); + GET_VM_STAT(V_VNODEOUT, v_vnodeout); + GET_VM_STAT(V_ACTIVE_COUNT, v_active_count); + GET_VM_STAT(V_INACTIVE_COUNT, v_inactive_count); + GET_VM_STAT(V_WIRE_COUNT, v_wire_count); + GET_VM_STAT(V_CACHE_COUNT, v_cache_count); + GET_VM_STAT(V_FREE_COUNT, v_free_count); + GET_VM_STAT(V_SWAPPGSIN, v_swappgsin); + GET_VM_STAT(V_SWAPPGSOUT, v_swappgsout); +} - /* - * Convert the process's runtime from microseconds to seconds. This - * time includes the interrupt time although that is not wanted here. - * ps(1) is similarly sloppy. - */ - cputime = (pp->ki_runtime + 500000) / 1000000; +void +get_system_info(struct system_info *si) - /* calculate the base for cpu percentages */ - pct = pctdouble(pp->ki_pctcpu); +{ + struct timeval thistime; + struct timeval timediff; + int i; - /* generate "STATE" field */ - switch (state = pp->ki_stat) { - case SRUN: - if (smpmode && pp->ki_oncpu != 0xff) - sprintf(status, "CPU%d", pp->ki_oncpu); - else - strcpy(status, "RUN"); - break; - case SLOCK: - if (pp->ki_kiflag & KI_LOCKBLOCK) { - sprintf(status, "*%.6s", pp->ki_lockname); - break; - } - /* fall through */ - case SSLEEP: - if (pp->ki_wmesg != NULL) { - sprintf(status, "%.6s", pp->ki_wmesg); - break; - } - /* FALLTHROUGH */ - default: + /* timestamp and time difference */ + gettimeofday(&thistime, 0); + timersub(&thistime, &lasttime, &timediff); + elapsed_time = timediff.tv_sec * 1000000 + timediff.tv_usec; + elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000; - if (state >= 0 && - state < sizeof(state_abbrev) / sizeof(*state_abbrev)) - sprintf(status, "%.6s", state_abbrev[state]); - else - sprintf(status, "?%5d", state); - break; + /* get the load averages */ + if (getloadavg(si->load_avg, NUM_AVERAGES) == -1) + { + /* failed: fill in with zeroes */ + (void) memset(si->load_avg, 0, sizeof(si->load_avg)); + } + + /* get the cp_times array ... */ + if (statics->ncpus == 1) { + (void)get_sysctl(K_CP_TIME, cp_time, sizeof(long) * CPUSTATES); + } else { + (void)get_sysctl(K_CP_TIMES, cp_time, sizeof(long) * CPUSTATES * statics->maxcpus); + } + + /* ... and convert cp_time counts to percentages */ + for (i = 0; i < statics->ncpus; i++) { + percentages(CPUSTATES, + &cpu_states[i * CPUSTATES], &cp_time[i * CPUSTATES], + &cp_old[i * CPUSTATES], &cp_diff[i * CPUSTATES]); + } + + /* sum memory & swap statistics */ + { + struct vmmeter sum; + static unsigned int swap_delay = 0; + static long long swapavail = 0; + static long long swapfree = 0; + static int bufspace = 0; + + get_vm_sum(&sum); + + /* get bufspace */ + bufspace = 0; + (void) get_sysctl(VFS_BUFSPACE, &bufspace, sizeof(bufspace)); + + /* kernel stats */ + dprintf("kernel: swtch %d, trap %d, intr %d, soft %d, vforks %d\n", + sum.v_swtch, sum.v_trap, sum.v_intr, sum.v_soft, sum.v_vforks); + kernel_stats[0] = per_second(sum.v_swtch - ctxsws, elapsed_msecs); + kernel_stats[1] = per_second(sum.v_trap - traps, elapsed_msecs); + kernel_stats[2] = per_second(sum.v_intr - intrs, elapsed_msecs); + kernel_stats[3] = per_second(sum.v_soft - softs, elapsed_msecs); + kernel_stats[4] = per_second(sum.v_vforks + sum.v_forks + + sum.v_rforks - forks, elapsed_msecs); + kernel_stats[5] = per_second(sum.v_vm_faults - pfaults, elapsed_msecs); + kernel_stats[6] = per_second(sum.v_swapin + sum.v_vnodein - pagein, elapsed_msecs); + kernel_stats[7] = per_second(sum.v_swapout + sum.v_vnodeout - pageout, elapsed_msecs); + kernel_stats[8] = per_second(sum.v_tfree - tfreed, elapsed_msecs); + ctxsws = sum.v_swtch; + traps = sum.v_trap; + intrs = sum.v_intr; + softs = sum.v_soft; + forks = (u_int64_t)sum.v_vforks + sum.v_forks + sum.v_rforks; + pfaults = sum.v_vm_faults; + pagein = sum.v_swapin + sum.v_vnodein; + pageout = sum.v_swapout + sum.v_vnodeout; + tfreed = sum.v_tfree; + + /* convert memory stats to Kbytes */ + memory_stats[0] = pagetok(sum.v_active_count); + memory_stats[1] = pagetok(sum.v_inactive_count); + memory_stats[2] = pagetok(sum.v_wire_count); + memory_stats[3] = pagetok(sum.v_cache_count); + memory_stats[4] = bufspace / 1024; + memory_stats[5] = pagetok(sum.v_free_count); + memory_stats[6] = -1; + + /* first interval */ + if (swappgsin < 0) + { + swap_stats[4] = 0; + swap_stats[5] = 0; + } + + /* compute differences between old and new swap statistic */ + else + { + swap_stats[4] = pagetok(sum.v_swappgsin - swappgsin); + swap_stats[5] = pagetok(sum.v_swappgsout - swappgsout); } - cmdbuf = (char *)malloc(cmdlengthdelta + 1); - if (cmdbuf == NULL) { - warn("malloc(%d)", cmdlengthdelta + 1); - return NULL; + swappgsin = sum.v_swappgsin; + swappgsout = sum.v_swappgsout; + + /* call CPU heavy swap_getdata() only for changes */ + if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) + { + swap_stats[3] = swap_getdata(&swapavail, &swapfree); + swap_stats[0] = swapavail; + swap_stats[1] = swapavail - swapfree; + swap_stats[2] = swapfree; } + swap_delay = 1; + swap_stats[6] = -1; + } - if (!(flags & FMT_SHOWARGS)) { - if (ps.thread && pp->ki_flag & P_HADTHREADS && - pp->ki_ocomm[0]) { - snprintf(cmdbuf, cmdlengthdelta, "{%s}", pp->ki_ocomm); - } else { - snprintf(cmdbuf, cmdlengthdelta, "%s", pp->ki_comm); - } - } else { - if (pp->ki_flag & P_SYSTEM || - pp->ki_args == NULL || - (args = kvm_getargv(kd, pp, cmdlengthdelta)) == NULL || - !(*args)) { - if (ps.thread && pp->ki_flag & P_HADTHREADS && - pp->ki_ocomm[0]) { - snprintf(cmdbuf, cmdlengthdelta, - "{%s}", pp->ki_ocomm); - } else { - snprintf(cmdbuf, cmdlengthdelta, - "[%s]", pp->ki_comm); - } - } else { - char *src, *dst, *argbuf; - char *cmd; - size_t argbuflen; - size_t len; + /* set arrays and strings */ + si->cpustates = cpu_states; + si->kernel = kernel_stats; + si->memory = memory_stats; + si->swap = swap_stats; - argbuflen = cmdlengthdelta * 4; - argbuf = (char *)malloc(argbuflen + 1); - if (argbuf == NULL) { - warn("malloc(%d)", argbuflen + 1); - free(cmdbuf); - return NULL; - } + get_sysctl(K_LASTPID, &si->last_pid, sizeof(si->last_pid)); + lasttime = thistime; +} - dst = argbuf; +caddr_t +get_process_info(struct system_info *si, + struct process_select *sel, + int compare_index) - /* Extract cmd name from argv */ - cmd = strrchr(*args, '/'); - if (cmd == NULL) - cmd = *args; - else - cmd++; +{ + int i; + int total_procs; + int active_procs; + struct kinfo_proc **prefp; + struct kinfo_proc *pp; + struct kinfo_proc *prev_pp = NULL; + struct save_proc *savep; + long proc_io; + pid_t pid; + size_t size; + int nproc; - for (; (src = *args++) != NULL; ) { - if (*src == '\0') - continue; - len = (argbuflen - (dst - argbuf) - 1) / 4; - strvisx(dst, src, - strlen(src) < len ? strlen(src) : len, - VIS_NL | VIS_CSTYLE); - while (*dst != '\0') - dst++; - if ((argbuflen - (dst - argbuf) - 1) / 4 > 0) - *dst++ = ' '; /* add delimiting space */ - } - if (dst != argbuf && dst[-1] == ' ') - dst--; - *dst = '\0'; + /* these are copied out of sel for speed */ + int show_idle; + int show_self; + int show_system; + int show_uid; + char *show_command; - if (strcmp(cmd, pp->ki_comm) != 0 ) - snprintf(cmdbuf, cmdlengthdelta, - "%s (%s)",argbuf, pp->ki_comm); - else - strlcpy(cmdbuf, argbuf, cmdlengthdelta); + /* get proc table size and give it a boost */ + nproc = (int)get_sysctlsize(K_PROC) / sizeof(struct kinfo_proc); + nproc += nproc >> 4; + size = nproc * sizeof(struct kinfo_proc); + dprintf("get_process_info: nproc %d, psize %d, size %d\n", nproc, psize, size); - free(argbuf); - } + /* make sure we have enough space allocated */ + if (nproc > psize) + { + /* reallocate both pbase and pref */ + pbase = (struct kinfo_proc *)realloc(pbase, size); + pref = (struct kinfo_proc **)realloc(pref, + sizeof(struct kinfo_proc *) * nproc); + psize = nproc; + } + + /* make sure we got the space we asked for */ + if (pref == NULL || pbase == NULL) + { + /* abandon all hope */ + message_error(" Out of memory!"); + nproc = psize = 0; + si->p_total = 0; + si->p_active = 0; + return NULL; + } + + /* get all process information (threads, too) */ + if (size > 0) + { + nproc = get_sysctl(K_PROC, pbase, size); + if (nproc == -1) + { + nproc = 0; } - - if (ps.jail == 0) - jid_buf[0] = '\0'; else - snprintf(jid_buf, sizeof(jid_buf), " %*d", - sizeof(jid_buf) - 3, pp->ki_jid); + { + nproc /= sizeof(struct kinfo_proc); + } + } - if (displaymode == DISP_IO) { - oldp = get_old_proc(pp); - if (oldp != NULL) { - ru.ru_inblock = RU(pp)->ru_inblock - - RU(oldp)->ru_inblock; - ru.ru_oublock = RU(pp)->ru_oublock - - RU(oldp)->ru_oublock; - ru.ru_majflt = RU(pp)->ru_majflt - RU(oldp)->ru_majflt; - ru.ru_nvcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw; - ru.ru_nivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw; - rup = &ru; - } else { - rup = RU(pp); + /* get a pointer to the states summary array */ + si->procstates = process_states; + + /* set up flags which define what we are going to select */ + show_idle = sel->idle; + show_self = 0; + show_system = sel->system; + show_uid = sel->uid != -1; + show_fullcmd = sel->fullcmd; + show_command = sel->command; + show_usernames = sel->usernames; + display_mode = sel->mode; +#ifdef HAS_SHOWTHREADS + show_threads = sel->threads; +#endif + + /* count up process states and get pointers to interesting procs */ + total_procs = 0; + active_procs = 0; + total_io = 0; + memset((char *)process_states, 0, sizeof(process_states)); + prefp = pref; + for (pp = pbase, i = 0; i < nproc; pp++, i++) + { + /* + * Place pointers to each valid proc structure in pref[]. + * Process slots that are actually in use have a non-zero + * status field. Processes with P_SYSTEM set are system + * processes---these get ignored unless show_sysprocs is set. + */ + pid = PP(pp, pid); + if (PP(pp, stat) != 0) + { +#ifdef HAS_SHOWTHREADS + int is_thread; + lwpid_t tid; + + /* get thread id */ + tid = PP(pp, tid); + + /* is this just a thread? */ + is_thread = (prev_pp != NULL && PP(prev_pp, pid) == pid); + + /* count this process and its state */ + /* only count threads if we are showing them */ + if (show_threads || !is_thread) + { + total_procs++; + process_states[(unsigned char) PP(pp, stat)]++; + } + + /* grab old data from hash */ + if ((savep = hash_lookup_lwpid(procs, tid)) != NULL) + { + /* verify that this is not a new or different thread */ + /* (freebsd reuses thread ids fairly quickly) */ + /* pids must match and time can't have gone backwards */ + if (pid != savep->sp_pid || PP(pp, runtime) < savep->sp_runtime) + { + /* not the same thread -- reuse the save_proc structure */ + memset(savep, 0, sizeof(struct save_proc)); + savep->sp_pid = pid; } - p_tot = rup->ru_inblock + rup->ru_oublock + rup->ru_majflt; - s_tot = total_inblock + total_oublock + total_majflt; + } + else + { + /* havent seen this one before */ + savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); + savep->sp_pid = pid; + hash_add_lwpid(procs, tid, savep); + } - sprintf(fmt, io_Proc_format, - pp->ki_pid, - jid_buf, - namelength, namelength, (*get_userid)(pp->ki_ruid), - rup->ru_nvcsw, - rup->ru_nivcsw, - rup->ru_inblock, - rup->ru_oublock, - rup->ru_majflt, - p_tot, - s_tot == 0 ? 0.0 : (p_tot * 100.0 / s_tot), - screen_width > cmdlengthdelta ? - screen_width - cmdlengthdelta : 0, - printable(cmdbuf)); +#else /* !HAS_SHOWTHREADS */ + total_procs++; + process_states[(unsigned char) PP(pp, stat)]++; - free(cmdbuf); + /* grab old data from hash */ + if ((savep = hash_lookup_pid(procs, pid)) == NULL) + { + /* havent seen this one before */ + savep = (struct save_proc *)calloc(1, sizeof(struct save_proc)); + savep->sp_pid = pid; + hash_add_pid(procs, pid, savep); + } +#endif - return (fmt); + /* save the pointer to the sp struct */ + SPPTR(pp) = (void *)savep; + + /* calculate %cpu */ + PPCPU(pp) = ((PP(pp, runtime) - savep->sp_runtime) * 10000) / + elapsed_time; + dprintf("%d (%d): runtime %lld, saved_pid %d, saved_runtime %lld, elapsed_time %d, ppcpu %d\n", + pid, PP(pp, tid), PP(pp, runtime), savep->sp_pid, savep->sp_runtime, + elapsed_time, PPCPU(pp)); + + /* calculate io differences */ + proc_io = 0; + savep->sp_vcsw = (RP(pp, nvcsw) - savep->sp_old_nvcsw); + savep->sp_ivcsw = (RP(pp, nivcsw) - savep->sp_old_nivcsw); + proc_io += (savep->sp_inblock = (RP(pp, inblock) - savep->sp_old_inblock)); + proc_io += (savep->sp_oublock = (RP(pp, oublock) - savep->sp_old_oublock)); + proc_io += (savep->sp_majflt = (RP(pp, majflt) - savep->sp_old_majflt)); + total_io += proc_io; + savep->sp_totalio = proc_io; + + /* save data for next time */ + savep->sp_runtime = PP(pp, runtime); + savep->sp_old_nvcsw = RP(pp, nvcsw); + savep->sp_old_nivcsw = RP(pp, nivcsw); + savep->sp_old_inblock = RP(pp, inblock); + savep->sp_old_oublock = RP(pp, oublock); + savep->sp_old_majflt = RP(pp, majflt); + + /* is this one selected for viewing? */ + if ((PP(pp, stat) != SZOMB) && + (show_system || ((PP(pp, flag) & P_SYSTEM) == 0)) && + (show_idle || (PP(pp, pctcpu) != 0) || + (PP(pp, stat) == SRUN)) && + (!show_uid || PRUID(pp) == (uid_t)sel->uid) && + (show_command == NULL || + strcasestr(PP(pp, comm), show_command) != NULL)) + { +#ifdef HAS_SHOWTHREADS + /* yes, but make sure it isn't just a thread */ + if (show_threads || !is_thread) + { + /* we will be showing this thread */ + *prefp++ = pp; + active_procs++; + } + else + { + /* we will not be showing this thread, but we need to roll + up its cpu usage in to its process */ + PP(prev_pp, pctcpu) += PP(pp, pctcpu); + } +#else /* !HAS_SHOWTHREADS */ + /* we will be showing this process */ + *prefp++ = pp; + active_procs++; +#endif + } + prev_pp = pp; } + } - /* format this entry */ - proc_fmt = smpmode ? smp_Proc_format : up_Proc_format; - if (ps.thread != 0) - thr_buf[0] = '\0'; - else - snprintf(thr_buf, sizeof(thr_buf), "%*d ", - sizeof(thr_buf) - 2, pp->ki_numthreads); + dprintf("total_io: %d\n", total_io); + if (total_io == 0) total_io = 1; - sprintf(fmt, proc_fmt, - pp->ki_pid, - jid_buf, - namelength, namelength, (*get_userid)(pp->ki_ruid), - thr_buf, - pp->ki_pri.pri_level - PZERO, - format_nice(pp), - format_k2(PROCSIZE(pp)), - format_k2(pagetok(pp->ki_rssize)), - status, - smpmode ? pp->ki_lastcpu : 0, - format_time(cputime), - ps.wcpu ? 100.0 * weighted_cpu(pct, pp) : 100.0 * pct, - screen_width > cmdlengthdelta ? screen_width - cmdlengthdelta : 0, - printable(cmdbuf)); + /* if requested, sort the "interesting" processes */ + if (active_procs > 1) + { + qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *), + proc_compares[compare_index]); + } - free(cmdbuf); + /* remember active and total counts */ + si->p_total = total_procs; + si->p_active = pref_len = active_procs; - /* return the result */ - return (fmt); + /* pass back a handle */ + handle.next_proc = pref; + handle.remaining = active_procs; + return((caddr_t)&handle); } -static void -getsysctl(const char *name, void *ptr, size_t len) +static char p_header[MAX_COLS]; + +char * +format_process_header(struct process_select *sel, caddr_t handle, int count) + { - size_t nlen = len; + int cols; + int n; + int w; + char *p; + int *fi; + struct kinfo_proc **kip; + struct proc_field *fp; - if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) { - fprintf(stderr, "top: sysctl(%s...) failed: %s\n", name, - strerror(errno)); - quit(23); + /* check for null handle */ + if (handle == NULL) + { + return(""); + } + + /* remember how many columns there are on the display */ + cols = display_columns(); + + /* mode & threads dictate format */ + fi = display_fields = + sel->mode == DISP_CPU ? + mode0_display : + mode1_display; + + /* set CPU vs Weighted CPU */ + if (sel->wcpu) { + field_subst(fi, FIELD_CPU, FIELD_WCPU); + } else { + field_subst(fi, FIELD_WCPU, FIELD_CPU); + } + + /* set username field correctly */ + if (!sel->usernames) + { + /* display uids */ + field_subst(fi, FIELD_USERNAME, FIELD_UID); + } + else + { + /* display usernames */ + field_subst(fi, FIELD_UID, FIELD_USERNAME); + + /* we also need to determine the longest username for column width */ + /* calculate namelength from first "count" processes */ + kip = ((struct handle *)handle)->next_proc; + n = ((struct handle *)handle)->remaining; + if (n > count) + n = count; + namelength = 0; + while (n-- > 0) + { + w = strlen(username(PRUID(*kip))); + if (w > namelength) namelength = w; + kip++; } - if (nlen != len) { - fprintf(stderr, "top: sysctl(%s...) expected %lu, got %lu\n", - name, (unsigned long)len, (unsigned long)nlen); - quit(23); + dprintf("format_process_header: namelength %d\n", namelength); + + /* place it in bounds */ + if (namelength < 8) + { + namelength = 8; } -} -static const char * -format_nice(const struct kinfo_proc *pp) -{ - const char *fifo, *kthread; - int rtpri; - static char nicebuf[4 + 1]; + /* set the column width */ + proc_field[FIELD_USERNAME].width = username_length = namelength; + } - fifo = PRI_NEED_RR(pp->ki_pri.pri_class) ? "" : "F"; - kthread = (pp->ki_flag & P_KTHREAD) ? "k" : ""; - switch (PRI_BASE(pp->ki_pri.pri_class)) { - case PRI_ITHD: - return ("-"); - case PRI_REALTIME: - /* - * XXX: the kernel doesn't tell us the original rtprio and - * doesn't really know what it was, so to recover it we - * must be more chummy with the implementation than the - * implementation is with itself. pri_user gives a - * constant "base" priority, but is only initialized - * properly for user threads. pri_native gives what the - * kernel calls the "base" priority, but it isn't constant - * since it is changed by priority propagation. pri_native - * also isn't properly initialized for all threads, but it - * is properly initialized for kernel realtime and idletime - * threads. Thus we use pri_user for the base priority of - * user threads (it is always correct) and pri_native for - * the base priority of kernel realtime and idletime threads - * (there is nothing better, and it is usually correct). - * - * The field width and thus the buffer are too small for - * values like "kr31F", but such values shouldn't occur, - * and if they do then the tailing "F" is not displayed. - */ - rtpri = ((pp->ki_flag & P_KTHREAD) ? pp->ki_pri.pri_native : - pp->ki_pri.pri_user) - PRI_MIN_REALTIME; - snprintf(nicebuf, sizeof(nicebuf), "%sr%d%s", - kthread, rtpri, fifo); - break; - case PRI_TIMESHARE: - if (pp->ki_flag & P_KTHREAD) - return ("-"); - snprintf(nicebuf, sizeof(nicebuf), "%d", pp->ki_nice - NZERO); - break; - case PRI_IDLE: - /* XXX: as above. */ - rtpri = ((pp->ki_flag & P_KTHREAD) ? pp->ki_pri.pri_native : - pp->ki_pri.pri_user) - PRI_MIN_IDLE; - snprintf(nicebuf, sizeof(nicebuf), "%si%d%s", - kthread, rtpri, fifo); - break; - default: - return ("?"); + /* walk thru fields and construct header */ + /* are we worried about overflow??? */ + p = p_header; + while (*fi != -1) + { + if ((*fi == FIELD_JID && sel->jail == 0) || + (*fi == FIELD_C && smpmode == 0) || + (*fi == FIELD_THR && sel->threads != 0)) { + fi++; + continue; } - return (nicebuf); + + fp = &(proc_field[*fi++]); + + if (fp->min_screenwidth <= cols) + { + p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name); + *p++ = ' '; + } + } + *--p = '\0'; + + return p_header; } -/* comparison routines for qsort */ +static char fmt[MAX_COLS]; /* static area where result is built */ -static int -compare_pid(const void *p1, const void *p2) +char * +format_next_process(struct process_select *sel, caddr_t handle, char *(*get_userid)(int)) + { - const struct kinfo_proc * const *pp1 = p1; - const struct kinfo_proc * const *pp2 = p2; + struct kinfo_proc *pp; + struct handle *hp; + struct proc_field *fp; + int *fi; + int cols; + char *p; + int len; + int x; - if ((*pp2)->ki_pid < 0 || (*pp1)->ki_pid < 0) - abort(); + /* find and remember the next proc structure */ + hp = (struct handle *)handle; + pp = *(hp->next_proc++); + hp->remaining--; + + /* mode & threads dictate format */ + fi = display_fields; - return ((*pp1)->ki_pid - (*pp2)->ki_pid); + /* screen width is a consideration, too */ + cols = display_columns(); + + /* build output by field */ + p = fmt; + len = MAX_COLS; + while (*fi != -1) + { + if ((*fi == FIELD_JID && sel->jail == 0) || + (*fi == FIELD_C && smpmode == 0) || + (*fi == FIELD_THR && sel->threads != 0)) { + fi++; + continue; + } + + fp = &(proc_field[*fi++]); + if (len > 0 && fp->min_screenwidth <= cols) + { + x = (*(fp->format))(p, len, pp); + if (x >= len) + { + dprintf("format_next_process: formatter overflow: x %d, len %d, p %08x => %08x, fmt %08x - %08x\n", + x, len, p, p + len, fmt, fmt + sizeof(fmt)); + p += len; + len = 0; + } + else + { + p += x; + *p++ = ' '; + len -= x + 1; + } + } + } + *--p = '\0'; + + /* return the result */ + return(fmt); } +/* comparison routines for qsort */ + /* * proc_compare - comparison function for "qsort" * Compares the resource consumption of two processes using five - * distinct keys. The keys (in descending order of importance) are: - * percent cpu, cpu ticks, state, resident set size, total virtual - * memory usage. The process states are ordered as follows (from least - * to most important): WAIT, zombie, sleep, stop, start, run. The - * array declaration below maps a process state index into a number - * that reflects this ordering. + * distinct keys. The keys (in descending order of importance) are: + * percent cpu, cpu ticks, state, resident set size, total virtual + * memory usage. The process states are ordered as follows (from least + * to most important): WAIT, zombie, sleep, stop, start, run. The + * array declaration below maps a process state index into a number + * that reflects this ordering. */ -static int sorted_state[] = { - 0, /* not used */ - 3, /* sleep */ - 1, /* ABANDONED (WAIT) */ - 6, /* run */ - 5, /* start */ - 2, /* zombie */ - 4 /* stop */ +static unsigned char sorted_state[] = +{ + 0, /* not used */ + 3, /* sleep */ + 1, /* ABANDONED (WAIT) */ + 6, /* run */ + 5, /* start */ + 2, /* zombie */ + 4 /* stop */ }; + +#define ORDERKEY_PCTCPU \ + if (lresult = (long) PPCPU(p2) - (long) PPCPU(p1), \ + (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) -#define ORDERKEY_PCTCPU(a, b) do { \ - long diff; \ - if (ps.wcpu) \ - diff = floor(1.0E6 * weighted_cpu(pctdouble((b)->ki_pctcpu), \ - (b))) - \ - floor(1.0E6 * weighted_cpu(pctdouble((a)->ki_pctcpu), \ - (a))); \ - else \ - diff = (long)(b)->ki_pctcpu - (long)(a)->ki_pctcpu; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) +#define ORDERKEY_CPTICKS \ + if ((result = PP(p2, runtime) > PP(p1, runtime) ? 1 : \ + PP(p2, runtime) < PP(p1, runtime) ? -1 : 0) == 0) -#define ORDERKEY_CPTICKS(a, b) do { \ - int64_t diff = (int64_t)(b)->ki_runtime - (int64_t)(a)->ki_runtime; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) +#define ORDERKEY_STATE \ + if ((result = sorted_state[(unsigned char) PP(p2, stat)] - \ + sorted_state[(unsigned char) PP(p1, stat)]) == 0) -#define ORDERKEY_STATE(a, b) do { \ - int diff = sorted_state[(b)->ki_stat] - sorted_state[(a)->ki_stat]; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) +#if OSMAJOR <= 4 +#define ORDERKEY_PRIO \ + if ((result = PP(p2, priority) - PP(p1, priority)) == 0) +#else +#define ORDERKEY_PRIO \ + if ((result = PP(p2, pri.pri_level) - PP(p1, pri.pri_level)) == 0) +#endif -#define ORDERKEY_PRIO(a, b) do { \ - int diff = (int)(b)->ki_pri.pri_level - (int)(a)->ki_pri.pri_level; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) +#define ORDERKEY_RSSIZE \ + if ((result = VP(p2, rssize) - VP(p1, rssize)) == 0) -#define ORDERKEY_THREADS(a, b) do { \ - int diff = (int)(b)->ki_numthreads - (int)(a)->ki_numthreads; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) +#define ORDERKEY_MEM \ + if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 ) -#define ORDERKEY_RSSIZE(a, b) do { \ - long diff = (long)(b)->ki_rssize - (long)(a)->ki_rssize; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) +#define ORDERKEY_IO \ + if ( (result = SP(p2, totalio) - SP(p1, totalio)) == 0) -#define ORDERKEY_MEM(a, b) do { \ - long diff = (long)PROCSIZE((b)) - (long)PROCSIZE((a)); \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) +#define ORDERKEY_PID \ + if ( (result = PP(p1, pid) - PP(p2, pid)) == 0) -#define ORDERKEY_JID(a, b) do { \ - int diff = (int)(b)->ki_jid - (int)(a)->ki_jid; \ - if (diff != 0) \ - return (diff > 0 ? 1 : -1); \ -} while (0) +#define ORDERKEY_JID \ + if ( (result = PP(p2, jid) - PP(p1, jid)) == 0) /* compare_cpu - the comparison function for sorting by cpu percentage */ int -#ifdef ORDER -compare_cpu(void *arg1, void *arg2) -#else -proc_compare(void *arg1, void *arg2) -#endif +proc_compare(struct proc **pp1, struct proc **pp2) + { - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; - return (0); + ORDERKEY_PCTCPU + ORDERKEY_CPTICKS + ORDERKEY_STATE + ORDERKEY_PRIO + ORDERKEY_RSSIZE + ORDERKEY_MEM + ; + + return(result); } -#ifdef ORDER -/* "cpu" compare routines */ -int compare_size(), compare_res(), compare_time(), compare_prio(), - compare_threads(); - -/* - * "io" compare routines. Context switches aren't i/o, but are displayed - * on the "io" display. - */ -int compare_iototal(), compare_ioread(), compare_iowrite(), compare_iofault(), - compare_vcsw(), compare_ivcsw(); - -int (*compares[])() = { - compare_cpu, - compare_size, - compare_res, - compare_time, - compare_prio, - compare_threads, - compare_iototal, - compare_ioread, - compare_iowrite, - compare_iofault, - compare_vcsw, - compare_ivcsw, - compare_jid, - NULL -}; - /* compare_size - the comparison function for sorting by total memory usage */ int -compare_size(void *arg1, void *arg2) +compare_size(struct proc **pp1, struct proc **pp2) + { - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; - ORDERKEY_MEM(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; - return (0); + ORDERKEY_MEM + ORDERKEY_RSSIZE + ORDERKEY_PCTCPU + ORDERKEY_CPTICKS + ORDERKEY_STATE + ORDERKEY_PRIO + ; + + return(result); } /* compare_res - the comparison function for sorting by resident set size */ int -compare_res(void *arg1, void *arg2) +compare_res(struct proc **pp1, struct proc **pp2) + { - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; - return (0); + ORDERKEY_RSSIZE + ORDERKEY_MEM + ORDERKEY_PCTCPU + ORDERKEY_CPTICKS + ORDERKEY_STATE + ORDERKEY_PRIO + ; + + return(result); } /* compare_time - the comparison function for sorting by total cpu time */ int -compare_time(void *arg1, void *arg2) +compare_time(struct proc **pp1, struct proc **pp2) + { - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; + + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); + ORDERKEY_CPTICKS + ORDERKEY_PCTCPU + ORDERKEY_STATE + ORDERKEY_PRIO + ORDERKEY_RSSIZE + ORDERKEY_MEM + ; - return (0); -} - + return(result); + } + /* compare_prio - the comparison function for sorting by priority */ int -compare_prio(void *arg1, void *arg2) +compare_prio(struct proc **pp1, struct proc **pp2) + { - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; - ORDERKEY_PRIO(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; - return (0); + ORDERKEY_PRIO + ORDERKEY_CPTICKS + ORDERKEY_PCTCPU + ORDERKEY_STATE + ORDERKEY_RSSIZE + ORDERKEY_MEM + ; + + return(result); } -/* compare_threads - the comparison function for sorting by threads */ +/* compare_io - the comparison function for sorting by io count */ + int -compare_threads(void *arg1, void *arg2) +compare_io(struct proc **pp1, struct proc **pp2) + { - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + pctcpu lresult; - ORDERKEY_THREADS(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; - return (0); -} + ORDERKEY_IO + ORDERKEY_PCTCPU + ORDERKEY_CPTICKS + ORDERKEY_STATE + ORDERKEY_PRIO + ORDERKEY_RSSIZE + ORDERKEY_MEM + ; -/* compare_jid - the comparison function for sorting by jid */ -static int -compare_jid(const void *arg1, const void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - - ORDERKEY_JID(p1, p2); - ORDERKEY_PCTCPU(p1, p2); - ORDERKEY_CPTICKS(p1, p2); - ORDERKEY_STATE(p1, p2); - ORDERKEY_PRIO(p1, p2); - ORDERKEY_RSSIZE(p1, p2); - ORDERKEY_MEM(p1, p2); - - return (0); + return(result); } -#endif /* ORDER */ -/* assorted comparison functions for sorting by i/o */ +/* compare_pid - the comparison function for sorting by process id */ int -#ifdef ORDER -compare_iototal(void *arg1, void *arg2) -#else -io_compare(void *arg1, void *arg2) -#endif +compare_pid(struct proc **pp1, struct proc **pp2) + { - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; - return (get_io_total(p2) - get_io_total(p1)); -} + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; -#ifdef ORDER -int -compare_ioread(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, inp1, inp2; + ORDERKEY_PID + ; - (void) get_io_stats(p1, &inp1, &dummy, &dummy, &dummy, &dummy); - (void) get_io_stats(p2, &inp2, &dummy, &dummy, &dummy, &dummy); - - return (inp2 - inp1); + return(result); } -int -compare_iowrite(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, oup1, oup2; +/* compare_jid - the comparison function for sorting by jail id */ - (void) get_io_stats(p1, &dummy, &oup1, &dummy, &dummy, &dummy); - (void) get_io_stats(p2, &dummy, &oup2, &dummy, &dummy, &dummy); - - return (oup2 - oup1); -} - int -compare_iofault(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, flp1, flp2; +compare_jid(struct proc **pp1, struct proc **pp2) - (void) get_io_stats(p1, &dummy, &dummy, &flp1, &dummy, &dummy); - (void) get_io_stats(p2, &dummy, &dummy, &flp2, &dummy, &dummy); - - return (flp2 - flp1); -} - -int -compare_vcsw(void *arg1, void *arg2) { - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, flp1, flp2; + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; - (void) get_io_stats(p1, &dummy, &dummy, &dummy, &flp1, &dummy); - (void) get_io_stats(p2, &dummy, &dummy, &dummy, &flp2, &dummy); + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; - return (flp2 - flp1); -} + ORDERKEY_JID + ; -int -compare_ivcsw(void *arg1, void *arg2) -{ - struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1; - struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2; - long dummy, flp1, flp2; - - (void) get_io_stats(p1, &dummy, &dummy, &dummy, &dummy, &flp1); - (void) get_io_stats(p2, &dummy, &dummy, &dummy, &dummy, &flp2); - - return (flp2 - flp1); + return(result); } -#endif /* ORDER */ /* * proc_owner(pid) - returns the uid that owns process "pid", or -1 if @@ -1388,40 +1818,22 @@ int proc_owner(int pid) + { - int cnt; - struct kinfo_proc **prefp; - struct kinfo_proc *pp; + int cnt; + struct kinfo_proc **prefp; + struct kinfo_proc *pp; - prefp = pref; - cnt = pref_len; - while (--cnt >= 0) { - pp = *prefp++; - if (pp->ki_pid == (pid_t)pid) - return ((int)pp->ki_ruid); + prefp = pref; + cnt = pref_len; + while (--cnt >= 0) + { + pp = *prefp++; + if (PP(pp, pid) == (pid_t)pid) + { + return((int)PRUID(pp)); } - return (-1); + } + return(-1); } -static int -swapmode(int *retavail, int *retfree) -{ - int n; - int pagesize = getpagesize(); - struct kvm_swap swapary[1]; - - *retavail = 0; - *retfree = 0; - -#define CONVERT(v) ((quad_t)(v) * pagesize / 1024) - - n = kvm_getswapinfo(kd, swapary, 1, 0); - if (n < 0 || swapary[0].ksw_total == 0) - return (0); - - *retavail = CONVERT(swapary[0].ksw_total); - *retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used); - - n = (int)(swapary[0].ksw_used * 100.0 / swapary[0].ksw_total); - return (n); -} Index: Makefile =================================================================== --- Makefile (revision 183246) +++ Makefile (working copy) @@ -5,10 +5,10 @@ PROG= top SRCS= commands.c display.c machine.c screen.c top.c \ - username.c utils.c version.c -SRCS+= sigdesc.h top.local.h -CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER -CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I. + username.c utils.c version.c color.c hash.c +SRCS+= sigdesc.h config.h +CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER -DSIGWINCH -DHAS_SHOWTHREADS +CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I. -Wall -g # # The table size should be a prime number approximately twice as @@ -40,8 +40,28 @@ -e's,%random%,1,g' \ ${.IMPSRC} > ${.TARGET} +CLEANFILES+= config.h +config.h: config.h.in + @${ECHO} Making config.h from config.h.in + @sed \ + -e 's/@DEFAULT_TOPN@/30/' \ + -e 's/@DEFAULT_DELAY@/2/' \ + -e 's/@HAVE_GETOPT_LONG@/1/' \ + -e 's/@ENABLE_KILL@/1/' \ + < config.h.in > config.h + +CLEANFILES+= top.1.local +top.1.local: top.1.in + @${ECHO} Making top.1.local from top.1.in + @sed \ + -e 's/@DEFAULT_TOPN@/30/' \ + -e 's/@DEFAULT_DELAY@/2/' \ + -e 's/@HAVE_GETOPT_LONG@/1/' \ + -e 's/@ENABLE_KILL@/1/' \ + < ${TOPDIR}/top.1.in > top.1.local + CLEANFILES+= top.1 -top.1: top.x top.local.1 +top.1: top.1.local machine.man cat ${.ALLSRC} > ${.TARGET} .include <bsd.prog.mk> In src/contrib/top: Index: utils.c =================================================================== --- utils.c (revision 183246) +++ utils.c (working copy) @@ -1,27 +1,77 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * - * $FreeBSD$ */ /* * This file contains various handy utilities used by top. */ +#include "os.h" +#include <ctype.h> +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#else +#undef DEBUG +#endif #include "top.h" -#include "os.h" +#include "utils.h" -int atoiwi(str) +static int +alldigits(char *s) -char *str; +{ + int ch; + while ((ch = *s++) != '\0') + { + if (!isdigit(ch)) + { + return 0; + } + } + return 1; +} + +int +atoiwi(char *str) + { register int len; @@ -34,13 +84,13 @@ { return(Infinity); } - else if (str[0] == '-') + else if (alldigits(str)) { - return(Invalid); + return(atoi(str)); } else { - return(atoi(str)); + return(Invalid); } } return(0); @@ -59,10 +109,9 @@ * digits. */ -char *itoa(val) +char * +itoa(int val) -register int val; - { register char *ptr; static char buffer[16]; /* result is built here */ @@ -90,18 +139,22 @@ * a front end to a more general routine for efficiency. */ -char *itoa7(val) +char * +itoa_w(int val, int w) -register int val; - { - register char *ptr; + char *ptr; + char *eptr; static char buffer[16]; /* result is built here */ /* 16 is sufficient since the largest number we will ever convert will be 2^32-1, which is 10 digits. */ - ptr = buffer + sizeof(buffer); + if (w > 15) + { + w = 15; + } + eptr = ptr = buffer + sizeof(buffer); *--ptr = '\0'; if (val == 0) { @@ -112,25 +165,36 @@ *--ptr = (val % 10) + '0'; val /= 10; } - while (ptr > buffer + sizeof(buffer) - 7) + while (ptr >= eptr - w) { *--ptr = ' '; } return(ptr); } +char * +itoa7(int val) + +{ + return itoa_w(val, 7); +} + /* * digits(val) - return number of decimal digits in val. Only works for - * positive numbers. If val <= 0 then digits(val) == 0. + * positive numbers. If val < 0 then digits(val) == 0, but + * digits(0) == 1. */ -int digits(val) +int +digits(int val) -int val; - { register int cnt = 0; + if (val == 0) + { + return 1; + } while (val > 0) { cnt++; @@ -140,29 +204,80 @@ } /* - * strecpy(to, from) - copy string "from" into "to" and return a pointer + * printable(char *str) - make the string pointed to by "str" into one that is + * printable (i.e.: all ascii), by converting all non-printable + * characters into '?'. Replacements are done in place and a pointer + * to the original buffer is returned. + */ + +char * +printable(char *str) + +{ + register char *ptr; + register int ch; + + ptr = str; + while ((ch = *ptr) != '\0') + { + if (!isprint(ch)) + { + *ptr = '?'; + } + ptr++; + } + return(str); +} + +/* + * strcpyend(to, from) - copy string "from" into "to" and return a pointer * to the END of the string "to". */ -char *strecpy(to, from) +char * +strcpyend(char *to, char *from) -register char *to; -register char *from; - { while ((*to++ = *from++) != '\0'); return(--to); } /* + * char * + * homogenize(char *str) + * + * Remove unwanted characters from "str" and make everything lower case. + * Newly allocated string is returned: the original is not altered. + */ + +char *homogenize(char *str) + +{ + char *ans; + char *fr; + char *to; + int ch; + + to = fr = ans = strdup(str); + while ((ch = *fr++) != '\0') + { + if (isalnum(ch)) + { + *to++ = tolower(ch); + } + } + + *to = '\0'; + return ans; +} + +/* * string_index(string, array) - find string in array and return index */ -int string_index(string, array) +int +string_index(char *string, char **array) -char *string; -char **array; - { register int i = 0; @@ -179,17 +294,55 @@ } /* + * char *string_list(char **strings) + * + * Create a comma-separated list of the strings in the NULL-terminated + * "strings". Returned string is malloc-ed and should be freed when the + * caller is done. Note that this is not an efficient function. + */ + +char *string_list(char **strings) + +{ + int cnt = 0; + char **pp; + char *p; + char *result = NULL; + char *resp = NULL; + + pp = strings; + while ((p = *pp++) != NULL) + { + cnt += strlen(p) + 2; + } + + if (cnt > 0) + { + resp = result = (char *)malloc(cnt); + pp = strings; + while ((p = *pp++) != NULL) + { + resp = strcpyend(resp, p); + if (*pp != NULL) + { + resp = strcpyend(resp, ", "); + } + } + } + + return result; +} + +/* * argparse(line, cntp) - parse arguments in string "line", separating them * out into an argv-like array, and setting *cntp to the number of * arguments encountered. This is a simple parser that doesn't understand * squat about quotes. */ -char **argparse(line, cntp) +char ** +argparse(char *line, int *cntp) -char *line; -int *cntp; - { register char *from; register char *to; @@ -270,14 +423,9 @@ * useful on BSD mchines for calculating cpu state percentages. */ -long percentages(cnt, out, new, old, diffs) +long +percentages(int cnt, int *out, long *new, long *old, long *diffs) -int cnt; -int *out; -register long *new; -register long *old; -long *diffs; - { register int i; register long change; @@ -312,11 +460,11 @@ half_total = total_change / 2l; /* Do not divide by 0. Causes Floating point exception */ - if(total_change) { - for (i = 0; i < cnt; i++) - { - *out++ = (int)((*diffs++ * 1000 + half_total) / total_change); - } + if (total_change != 0) { + 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 */ @@ -337,18 +485,16 @@ /* externs referenced by errmsg */ #ifndef HAVE_STRERROR -#ifndef SYS_ERRLIST_DECLARED -#define SYS_ERRLIST_DECLARED +#if !HAVE_DECL_SYS_ERRLIST extern char *sys_errlist[]; #endif extern int sys_nerr; #endif -char *errmsg(errnum) +char * +errmsg(int errnum) -int errnum; - { #ifdef HAVE_STRERROR char *msg = strerror(errnum); @@ -359,12 +505,43 @@ #else if (errnum > 0 && errnum < sys_nerr) { - return((char *)sys_errlist[errnum]); + return((char *)(sys_errlist[errnum])); } #endif return("No error"); } +/* format_percent(v) - format a double as a percentage in a manner that + * does not exceed 5 characters (excluding any trailing + * percent sign). Since it is possible for the value + * to exceed 100%, we format such values with no fractional + * component to fit within the 5 characters. + */ + +char * +format_percent(double v) + +{ + static char result[10]; + + /* enumerate the possibilities */ + if (v < 0 || v >= 100000.) + { + /* we dont want to try extreme values */ + strcpy(result, " ???"); + } + else if (v > 99.99) + { + sprintf(result, "%5.0f", v); + } + else + { + sprintf(result, "%5.2f", v); + } + + return result; +} + /* format_time(seconds) - format number of seconds into a suitable * display that will fit within 6 characters. Note that this * routine builds its string in a static area. If it needs @@ -381,14 +558,10 @@ exceed 9999.9, we use "???". */ -char *format_time(seconds) +char * +format_time(long seconds) -long seconds; - { - register int value; - register int digit; - register char *ptr; static char result[10]; /* sanity protection */ @@ -411,8 +584,7 @@ { /* standard method produces MMM:SS */ /* we avoid printf as must as possible to make this quick */ - sprintf(result, "%3ld:%02ld", - (long)(seconds / 60), (long)(seconds % 60)); + sprintf(result, "%3ld:%02ld", seconds / 60l, seconds % 60l); } return(result); } @@ -442,18 +614,16 @@ #define NUM_STRINGS 8 -char *format_k(amt) +char * +format_k(long 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]; + ret = retarray[index]; index = (index + 1) % NUM_STRINGS; if (amt >= 10000) @@ -467,41 +637,117 @@ } } - p = strecpy(p, itoa(amt)); - *p++ = tag; - *p = '\0'; + snprintf(ret, sizeof(retarray[index])-1, "%ld%c", amt, tag); return(ret); } -char *format_k2(amt) +/* + * Time keeping functions. + */ -int amt; +static struct timeval lasttime = { 0, 0 }; +static unsigned int elapsed_msecs = 0; +void +time_get(struct timeval *tv) + { - static char retarray[NUM_STRINGS][16]; - static int index = 0; - register char *p; - register char *ret; - register char tag = 'K'; + /* get the current time */ +#ifdef HAVE_GETTIMEOFDAY + gettimeofday(tv, NULL); +#else + tv->tv_sec = (long)time(NULL); + tv->tv_usec = 0; +#endif +} - p = ret = retarray[index]; - index = (index + 1) % NUM_STRINGS; +void +time_mark(struct timeval *tv) - if (amt >= 100000) +{ + struct timeval thistime; + struct timeval timediff; + + /* if the caller didnt provide one then use our own */ + if (tv == NULL) { - amt = (amt + 512) / 1024; - tag = 'M'; - if (amt >= 100000) - { - amt = (amt + 512) / 1024; - tag = 'G'; - } + tv = &thistime; } - p = strecpy(p, itoa(amt)); - *p++ = tag; - *p = '\0'; + /* get the current time */ +#ifdef HAVE_GETTIMEOFDAY + gettimeofday(tv, NULL); +#else + tv->tv_sec = (long)time(NULL); + tv->tv_usec = 0; +#endif - return(ret); + /* calculate the difference */ + timediff.tv_sec = tv->tv_sec - lasttime.tv_sec; + timediff.tv_usec = tv->tv_usec - lasttime.tv_usec; + if (timediff.tv_usec < 0) { + timediff.tv_sec--; + timediff.tv_usec += 1000000; + } + + /* convert to milliseconds */ + elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000; + if (elapsed_msecs == 0) + { + elapsed_msecs = 1; + } + + /* save for next time */ + lasttime = *tv; } + +unsigned int +time_elapsed() + +{ + return elapsed_msecs; +} + +unsigned int +diff_per_second(unsigned int x, unsigned int y) + +{ + return (y > x ? UINT_MAX - y + x + 1 : x - y) * 1000 / elapsed_msecs; +} + +static int debug_on = 0; + +#ifdef DEBUG +FILE *debugfile; +#endif + +void +debug_set(int i) + +{ + debug_on = i; +#ifdef DEBUG + debugfile = fopen("/tmp/top.debug", "w"); +#endif +} + +#ifdef DEBUG +void +xdprintf(char *fmt, ...) + +{ + va_list argp; + + va_start(argp, fmt); + + if (debug_on) + { + vfprintf(debugfile, fmt, argp); + fflush(debugfile); + } + + va_end(argp); +} +#endif + Index: Porting =================================================================== --- Porting (revision 183246) +++ Porting (working copy) @@ -3,8 +3,6 @@ This is still a preliminary document. Suggestions for improvement are most welcome. -My address is now "wnl@groupsys.com". - Before you embark on a port, please send me a mail message telling me what platform you are porting top to. There are three reasons for this: (1) I may already have a port, (2) module naming needs to be @@ -163,3 +161,69 @@ discuss the details. I want to keep such changes as general as possible. +-------- + +Changes were made to the module interface between 3.5 and 3.6. Here are +the changes that need to be made to port a 3.5 module to 3.6: + +The array that stores memory statistics and is passed back in the system +information structure as "memory" must now be an array of (signed) longs. +This was done to more easily accomodate systems that have gigabytes of +memory. Since the numbers are supposed to be kilobytes, a long can still +represent up to 2 terabytes. Look for "int memory_stats[X]" (where "X" +is some arbitrary number) and change it to "long memory_stats[X]". If +the module support reporting swap information on a separate line, then +its "swap_stats" array also needs to be an array of longs. + +The argument to proc_owner should be an int, as in "int pid". When it is +used in proc_owner it should be cast as necessary. Many operating systems +will require it to be cast to a pid_t before being compared to the appropriate +element in the proc structure. + +In the function format_next_process, the last argument in the main call +to sprintf is the string that contains the command for the process. +Make sure that this last argument is enclosed in a call to "printable". +For example: "printable(MPP(pp, p_comm))". + +The third argument to "get_process_info" needs to be changed to an integer, +typically "int compare_index". The call to qsort in get_process_info may +be guarded by "if (compare != NULL)". If it is, remove the if statement. + +The other changes to get_process_info depends on whether or not the module +supports multiple sort orders. + +To support multiple keys: + +Create an array int (*proc_compares[])() and assign to it the list of +comparison functions, NULL terminated. For example: + +int (*proc_compares[])() = { + compare_cpu, + compare_size, + compare_res, + compare_time, + NULL }; + +In get_process_info there is a call to qsort which uses one of the +functions in proc_compares. It should be changed so that its fourth +argument is "proc_compares[compare_index]". + +If the module contains the function "proc_compare", it should be removed. + +There should also be a NULL-terminated array of strings which list the names +for the sort keys, for example: + +char *ordernames[] = +{"cpu", "size", "res", "time", NULL}; + +To indicate that this module supports multiple sort keys, add the following +line in machine_init: + + statics->order_names = ordernames; + +If there is no support for multiple keys: + +Leave statics->order_names alone and call the comparison function of +your choice in get_process_info, ignoring the third argument. + + Index: commands.c =================================================================== --- commands.c (revision 183246) +++ commands.c (working copy) @@ -1,14 +1,42 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * - * $FreeBSD$ */ /* @@ -21,144 +49,51 @@ #include "os.h" #include <ctype.h> #include <signal.h> +#include <stdarg.h> +#include <unistd.h> +#include <color.h> #include <errno.h> -#include <sys/time.h> +#ifdef HAVE_SYS_RESOURCE_H #include <sys/resource.h> +#endif +#if defined(HAVE_DECL_SYS_SIGLIST) & defined(HAVE_STRCASECMP) +#define USE_SYS_SIGLIST +#endif + +#ifdef USE_SYS_SIGLIST +extern const char * const sys_siglist[]; +extern const char * const sys_signame[]; +#else #include "sigdesc.h" /* generated automatically */ +#endif #include "top.h" +#include "machine.h" +#include "globalstate.h" #include "boolean.h" +#include "color.h" +#include "commands.h" +#include "display.h" +#include "screen.h" +#include "username.h" #include "utils.h" +#include "version.h" extern int errno; extern char *copyright; -/* imported from screen.c */ -extern int overstrike; +typedef struct command { + int ch; + int (*cmd_func)(globalstate *); + char *help; +} command; -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("\ -C - toggle the displaying of weighted CPU percentage\n\ -d - change number of displays to show\n\ -e - list errors generated by last \"kill\" or \"renice\" command\n\ -H - toggle the displaying of threads\n\ -i or I - toggle the displaying of idle processes\n\ -j - toggle the displaying of jail ID\n\ -k - kill processes; send a signal to a list of processes\n\ -m - toggle the display between 'cpu' and 'io' modes\n\ -n or # - change number of processes to display\n", stdout); -#ifdef ORDER - if (displaymode == DISP_CPU) - fputs("\ -o - specify sort order (pri, size, res, cpu, time, threads)\n", stdout); - else - fputs("\ -o - specify sort order (vcsw, ivcsw, read, write, fault, total)\n", stdout); -#endif - fputs("\ -r - renice a process\n\ -s - change number of seconds to delay between updates\n\ -S - toggle the displaying of system processes\n\ -a - toggle the displaying of process titles\n\ -t - toggle the display of this process\n\ -u - display processes for only one user (+ selects all users)\n\ -\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 + * error message, or NULL if no errors occurred. We need an upper limit on * the number of errors, so we arbitrarily choose 20. */ @@ -172,97 +107,44 @@ 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) \ +#define ERROR(p, e) if (errcnt < ERRMAX) \ { \ - return(err_toomany); \ - } \ - else \ - { \ errs[errcnt].arg = (p); \ errs[errcnt++].errnum = (e); \ } /* - * err_string() - return an appropriate error string. This is what the - * command will return for displaying. If no errors were logged, then - * return NULL. The maximum length of the error string is defined by - * "STRMAX". + * err_compar(p1, p2) - comparison routine used by "qsort" + * for sorting errors. */ -#define STRMAX 80 +int +err_compar(const void *p1, const void *p2) -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]; + register int result; - /* if there are no errors, return NULL */ - if (errcnt == 0) + if ((result = ((struct errs *)p1)->errnum - + ((struct errs *)p2)->errnum) == 0) { - return(NULL); + return(strcmp(((struct errs *)p1)->arg, + ((struct errs *)p2)->arg)); } - - /* 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->errnum != currerr) - { - if (currerr != -1) - { - if ((stringlen = str_adderr(string, stringlen, currerr)) < 2) - { - return(err_listem); - } - (void) strcat(string, "; "); /* we know there's more */ - } - currerr = errp->errnum; - first = Yes; - } - if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0) - { - return(err_listem); - } - first = No; - } - - /* add final message */ - stringlen = str_adderr(string, stringlen, currerr); - - /* return the error string */ - return(stringlen == 0 ? err_listem : string); + return(result); } /* * str_adderr(str, len, err) - add an explanation of error "err" to - * the string "str". + * the string "str" without overflowing length "len". return + * number of characters remaining in str, or 0 if overflowed. */ -str_adderr(str, len, err) +int +str_adderr(char *str, int len, int err) -char *str; -int len; -int err; - { register char *msg; register int msglen; @@ -280,17 +162,15 @@ /* * str_addarg(str, len, arg, first) - add the string argument "arg" to - * the string "str". This is the first in the group when "first" - * is set (indicating that a comma should NOT be added to the front). + * the string "str" without overflowing length "len". This is the + * first in the group when "first" is set (indicating that a comma + * should NOT be added to the front). Return number of characters + * remaining in str, or 0 if overflowed. */ -str_addarg(str, len, arg, first) +int +str_addarg(char *str, int len, char *arg, int first) -char *str; -int len; -char *arg; -int first; - { register int arglen; @@ -312,28 +192,139 @@ } /* - * err_compar(p1, p2) - comparison routine used by "qsort" - * for sorting errors. + * void err_string() + * + * Use message_error to log errors in the errs array. This function + * will combine identical errors to make the message short, but if + * there is more than one type of error it will call message_error + * for each one. */ -err_compar(p1, p2) +#define STRMAX 80 -register struct errs *p1, *p2; +void +err_string() { - register int result; + register struct errs *errp; + register int cnt = 0; + register int first = Yes; + register int currerr = -1; + int stringlen = 0; /* characters still available in "string" */ + char string[STRMAX]; - if ((result = p1->errnum - p2->errnum) == 0) + /* if there are no errors, our job is easy */ + if (errcnt == 0) { - return(strcmp(p1->arg, p2->arg)); + return; } - return(result); + + /* sort the errors */ + qsort((char *)errs, errcnt, sizeof(struct errs), err_compar); + + /* initialize the buffer (probably not necessary) */ + string[0] = '\0'; + stringlen = STRMAX - 1; + + /* loop thru the sorted list, logging errors */ + while (cnt < errcnt) + { + /* point to the current error */ + errp = &(errs[cnt++]); + + /* note that on overflow "stringlen" will become 0 and all + subsequent calls to str_addarg or str_adderr will return 0 */ + + /* if the error number is different then add the error string */ + if (errp->errnum != currerr) + { + if (currerr != -1) + { + /* add error string and log the error */ + stringlen = str_adderr(string, stringlen, currerr); + message_error(" %s", string); + + } + /* reset the buffer */ + string[0] = '\0'; + stringlen = STRMAX - 1; + + /* move to next error num */ + currerr = errp->errnum; + first = Yes; + } + + /* add this arg */ + stringlen = str_addarg(string, stringlen, errp->arg, first); + first = No; + } + + /* add final message */ + stringlen = str_adderr(string, stringlen, currerr); + + /* write the error string */ + message_error(" %s", string); } /* + * Utility routines that help with some of the commands. + */ + +char * +next_field(char *str) + + +{ + if ((str = strchr(str, ' ')) == NULL) + { + return(NULL); + } + *str = '\0'; + while (*++str == ' ') /* loop */; + + /* if there is nothing left of the string, return NULL */ + /* This fix is dedicated to Greg Earle */ + return(*str == '\0' ? NULL : str); +} + +int +scanint(char *str, int *intp) + +{ + register int val = 0; + register int ch; + + /* if there is nothing left of the string, flag it as an error */ + /* This fix is dedicated to Greg Earle */ + if (*str == '\0') + { + return(-1); + } + + while ((ch = *str++) != '\0') + { + if (isdigit(ch)) + { + val = val * 10 + (ch - '0'); + } + else if (isspace(ch)) + { + break; + } + else + { + return(-1); + } + } + *intp = val; + return(0); +} + +/* * error_count() - return the number of errors currently logged. */ +int error_count() { @@ -344,6 +335,7 @@ * show_errors() - display on stdout the current log of errors. */ +void show_errors() { @@ -364,16 +356,18 @@ * command does; invoked in response to 'k'. */ -char *kill_procs(str) +void +kill_procs(char *str) -char *str; - { register char *nptr; int signum = SIGTERM; /* default */ int procnum; + int uid; + int owner; +#ifndef USE_SYS_SIGLIST struct sigdesc *sigp; - int uid; +#endif /* reset error array */ ERR_RESET; @@ -382,30 +376,51 @@ uid = getuid(); /* skip over leading white space */ - while (isspace(*str)) str++; + while (isspace((int)*str)) str++; if (str[0] == '-') { /* explicit signal specified */ if ((nptr = next_field(str)) == NULL) { - return(" kill: no processes specified"); + message_error(" kill: no processes specified"); + return; } - if (isdigit(str[1])) + str++; + if (isdigit((int)str[0])) { - (void) scanint(str + 1, &signum); + (void) scanint(str, &signum); if (signum <= 0 || signum >= NSIG) { - return(" invalid signal number"); + message_error(" kill: invalid signal number"); + return; } } else { /* translate the name into a number */ +#ifdef USE_SYS_SIGLIST + for (signum = 1; signum < NSIG; signum++) + { + if (strcasecmp(sys_signame[signum], str) == 0) + { + break; + } + } + if (signum == NSIG) + { + message_error(" kill: bad signal name"); + return; + } +#else for (sigp = sigdesc; sigp->name != NULL; sigp++) { - if (strcmp(sigp->name, str + 1) == 0) +#ifdef HAVE_STRCASECMP + if (strcasecmp(sigp->name, str) == 0) +#else + if (strcmp(sigp->name, str) == 0) +#endif { signum = sigp->number; break; @@ -415,8 +430,10 @@ /* was it ever found */ if (sigp->name == NULL) { - return(" bad signal name"); + message_error(" kill: bad signal name"); + return; } +#endif } /* put the new pointer in place */ str = nptr; @@ -432,9 +449,10 @@ else { /* check process owner if we're not root */ - if (uid && (uid != proc_owner(procnum))) + owner = proc_owner(procnum); + if (uid && (uid != owner)) { - ERROR(str, EACCES); + ERROR(str, owner == -1 ? ESRCH : EACCES); } /* go in for the kill */ else if (kill(procnum, signum) == -1) @@ -445,8 +463,8 @@ } } while ((str = next_field(str)) != NULL); - /* return appropriate error string */ - return(err_string()); + /* process errors */ + err_string(); } /* @@ -454,10 +472,9 @@ * "renice" command does; invoked in response to 'r'. */ -char *renice_procs(str) +void +renice_procs(char *str) -char *str; - { register char negate; int prio; @@ -487,16 +504,17 @@ /* check for validity */ if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX) { - return(" bad priority value"); + message_error(" renice: bad priority value"); } #endif /* move to the first process number */ if ((str = next_field(str)) == NULL) { - return(" no processes specified"); + message_error(" remice: no processes specified"); } +#ifdef HAVE_SETPRIORITY /* loop thru the process numbers, renicing each one */ do { @@ -515,8 +533,543 @@ ERROR(str, errno); } } while ((str = next_field(str)) != NULL); + err_string(); +#else + message_error(" renice operation not supported"); +#endif +} - /* return appropriate error string */ - return(err_string()); +/* COMMAND ROUTINES */ + +/* + * Each command routine is called by command_process and is passed a + * pointer to the current global state. Command routines are free + * to change anything in the global state, although changes to the + * statics structure are discouraged. Whatever a command routine + * returns will be returned by command_process. + */ + +void +cmd_quit(globalstate *gstate) + +{ + quit(EX_OK); + /*NOTREACHED*/ } +int +cmd_update(globalstate *gstate) + +{ + /* go home for visual feedback */ + screen_home(); + fflush(stdout); + message_expire(); + return CMD_REFRESH; +} + +int +cmd_redraw(globalstate *gstate) + +{ + gstate->fulldraw = Yes; + return CMD_REFRESH; +} + +int +cmd_color(globalstate *gstate) + +{ + gstate->use_color = color_activate(-1); + gstate->fulldraw = Yes; + return CMD_REFRESH; +} + +int +cmd_number(globalstate *gstate) + +{ + int newval; + char tmpbuf[20]; + + message_prompt("Number of processes to show: "); + newval = readline(tmpbuf, 8, Yes); + if (newval > -1) + { + if (newval > gstate->max_topn) + { + message_error(" This terminal can only display %d processes", + gstate->max_topn); + } + + if (newval == 0) + { + /* inhibit the header */ + display_header(No); + } + + else if (gstate->topn == 0) + { + display_header(Yes); + } + + gstate->topn = newval; + } + return CMD_REFRESH; +} + +int +cmd_delay(globalstate *gstate) + +{ + int newval; + char tmpbuf[20]; + + message_prompt("Seconds to delay: "); + if ((newval = readline(tmpbuf, 8, Yes)) > -1) + { + if ((gstate->delay = newval) == 0 && getuid() != 0) + { + gstate->delay = 1; + } + } + return CMD_REFRESH; +} + +int +cmd_idle(globalstate *gstate) + +{ + gstate->pselect.idle = !gstate->pselect.idle; + message_error(" %sisplaying idle processes.", + gstate->pselect.idle ? "D" : "Not d"); + return CMD_REFRESH; +} + +int +cmd_weightedcpu(globalstate *gstate) + +{ + gstate->pselect.wcpu = !gstate->pselect.wcpu; + message_error(" Displaying %sCPU.", + gstate->pselect.wcpu ? "W" : ""); + return CMD_REFRESH; +} + +int +cmd_jails(globalstate *gstate) + +{ + gstate->pselect.jail = !gstate->pselect.jail; + message_error(" %sisplaying jail ID.", + gstate->pselect.jail ? "D" : "Not d"); + return CMD_REFRESH; +} + +int +cmd_thisprocess(globalstate *gstate) + +{ + gstate->pselect.self = !gstate->pselect.self; + message_error(" %sisplaying self.", + gstate->pselect.self ? "D" : "Not d"); + return CMD_REFRESH; +} + +int +cmd_displays(globalstate *gstate) + +{ + int i; + char tmpbuf[20]; + + message_prompt("Displays to show (currently %s): ", + gstate->displays == -1 ? "infinite" : + itoa(gstate->displays)); + + if ((i = readline(tmpbuf, 10, Yes)) > 0) + { + gstate->displays = i; + } + else if (i == 0) + { + quit(EX_OK); + /*NOTREACHED*/ + } + return CMD_OK; +} + +int +cmd_cmdline(globalstate *gstate) + +{ + if (gstate->statics->flags.fullcmds) + { + gstate->pselect.fullcmd = !gstate->pselect.fullcmd; + message_error(" %sisplaying full command lines.", + gstate->pselect.fullcmd ? "D" : "Not d"); + return CMD_REFRESH; + } + message_error(" Full command display not supported."); + return CMD_OK; +} + +int +cmd_order(globalstate *gstate) + +{ + char tmpbuf[MAX_COLS]; + int i; + + if (gstate->statics->order_names != NULL) + { + message_prompt("Column to sort: "); + if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) + { + if ((i = string_index(tmpbuf, gstate->statics->order_names)) == -1) + { + message_error(" Sort order \"%s\" not recognized", tmpbuf); + } + else + { + gstate->order_index = i; + return CMD_REFRESH; + } + } + } + return CMD_OK; +} + +int +cmd_order_x(globalstate *gstate, char *name, ...) + +{ + va_list ap; + char *p; + char **names; + int i; + + names = gstate->statics->order_names; + if (names != NULL) + { + if ((i = string_index(name, names)) == -1) + { + /* check the alternate list */ + va_start(ap, name); + p = va_arg(ap, char *); + while (p != NULL) + { + if ((i = string_index(p, names)) != -1) + { + gstate->order_index = i; + return CMD_REFRESH; + } + p = va_arg(ap, char *); + } + message_error(" Sort order not recognized"); + } + else + { + gstate->order_index = i; + return CMD_REFRESH; + } + } + return CMD_OK; +} + +int +cmd_order_cpu(globalstate *gstate) + +{ + return cmd_order_x(gstate, "cpu", NULL); +} + +int +cmd_order_pid(globalstate *gstate) + +{ + return cmd_order_x(gstate, "pid", NULL); +} + +int +cmd_order_mem(globalstate *gstate) + +{ + return cmd_order_x(gstate, "mem", "size", NULL); +} + +int +cmd_order_time(globalstate *gstate) + +{ + return cmd_order_x(gstate, "time"); +} + +#ifdef ENABLE_KILL + +int +cmd_kill(globalstate *gstate) + +{ + char tmpbuf[MAX_COLS]; + + message_prompt_plain("kill "); + if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) + { + kill_procs(tmpbuf); + } + return CMD_OK; +} + +int +cmd_renice(globalstate *gstate) + +{ + char tmpbuf[MAX_COLS]; + + message_prompt_plain("renice "); + if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) + { + renice_procs(tmpbuf); + } + return CMD_OK; +} + +#endif + +int +cmd_user(globalstate *gstate) + +{ + char linebuf[MAX_COLS]; + int i; + int ret = CMD_OK; + + message_prompt("Username to show: "); + if (readline(linebuf, sizeof(linebuf), No) > 0) + { + if (linebuf[0] == '+' && + linebuf[1] == '\0') + { + gstate->pselect.uid = -1; + ret = CMD_REFRESH; + } + else if ((i = userid(linebuf)) == -1) + { + message_error(" %s: unknown user", linebuf); + } + else + { + gstate->pselect.uid = i; + ret = CMD_REFRESH; + } + } + return ret; +} + +int +cmd_command(globalstate *gstate) + +{ + char linebuf[MAX_COLS]; + + if (gstate->pselect.command != NULL) + { + free(gstate->pselect.command); + gstate->pselect.command = NULL; + } + + message_prompt("Command to show: "); + if (readline(linebuf, sizeof(linebuf), No) > 0) + { + if (linebuf[0] != '\0') + { + gstate->pselect.command = strdup(linebuf); + } + } + return CMD_REFRESH; +} + +int +cmd_useruid(globalstate *gstate) + +{ + gstate->pselect.usernames = !gstate->pselect.usernames; + display_header(2); + return CMD_REFRESH; +} + +int +cmd_mode(globalstate *gstate) + +{ + if (gstate->statics->modemax <= 1) + { + return CMD_NA; + } + gstate->pselect.mode = (gstate->pselect.mode + 1) % gstate->statics->modemax; + display_header(2); + return CMD_REFRESH; +} + +int +cmd_system(globalstate *gstate) + +{ + gstate->pselect.system = !gstate->pselect.system; + display_header(2); + message_error(" %showing system processes.", + gstate->pselect.system ? "S" : "Not s"); + return CMD_REFRESH; +} + +int +cmd_threads(globalstate *gstate) + +{ + if (gstate->statics->flags.threads) + { + gstate->pselect.threads = !gstate->pselect.threads; + display_header(2); + message_error(" Displaying threads as %s.", + gstate->pselect.threads ? "seperately" : "a count"); + return CMD_REFRESH; + } + message_error(" Thread support not enabled."); + return CMD_NA; +} + +/* forward reference for cmd_help, as it needs to see the command_table */ +int cmd_help(globalstate *gstate); + +/* command table */ +command command_table[] = { + { '\014', cmd_redraw, "redraw screen" }, + { ' ', cmd_update, "update screen" }, + { '?', cmd_help, "help; show this text" }, + { 'h', cmd_help, NULL }, + { 'C', cmd_weightedcpu, "toggle the displaying of weighted CPU percentage" }, + { 'E', cmd_color, "toggle the use of color" }, + { 'H', cmd_threads, "toggle the display of individual threads" }, + { 'j', cmd_jails, "toggle the displaying of jail ID" }, + { 'M', cmd_order_mem, "sort by memory usage" }, + { 'N', cmd_order_pid, "sort by process id" }, + { 'P', cmd_order_cpu, "sort by CPU usage" }, + { 'S', cmd_system, "toggle the display of system processes" }, + { 'T', cmd_order_time, "sort by CPU time" }, + { 'U', cmd_useruid, "toggle the display of usernames or uids" }, + { 'c', cmd_command, "display processes by command name" }, + { 'd', cmd_displays, "change number of displays to show" }, + { 'a', cmd_cmdline, "toggle the display of full command paths" }, + { 'i', cmd_idle, "toggle the displaying of idle processes" }, + { 'I', cmd_idle, NULL }, +#ifdef ENABLE_KILL + { 'k', cmd_kill, "kill processes; send a signal to a list of processes" }, +#endif + { 'm', cmd_mode, "toggle between 'cpu' and 'io' modes" }, + { 'n', cmd_number, "change number of processes to display" }, + { '#', cmd_number, NULL }, + { 'o', cmd_order, "specify sort order (see below)" }, + { 'q', (int (*)(globalstate *))cmd_quit, "quit" }, +#ifdef ENABLE_KILL + { 'r', cmd_renice, "renice a process" }, +#endif + { 's', cmd_delay, "change number of seconds to delay between updates" }, + { 't', cmd_thisprocess, "toggle the display of this process" }, + { 'u', cmd_user, "display processes for only one user (+ selects all users)" }, + { '\0', NULL, NULL }, +}; + +int +cmd_help(globalstate *gstate) + +{ + command *c; + char buf[12]; + char *p; + char *help; + + display_pagerstart(); + + display_pager("Top version %s, %s\n", version_string(), copyright); + display_pager("Platform module: %s\n\n", MODULE); + display_pager("A top users display for Unix\n\n"); + display_pager("These single-character commands are available:\n\n"); + + c = command_table; + while (c->cmd_func != NULL) + { + /* skip null help strings */ + if ((help = c->help) == NULL) + { + continue; + } + + /* translate character in to something readable */ + if (c->ch < ' ') + { + buf[0] = '^'; + buf[1] = c->ch + '@'; + buf[2] = '\0'; + } + else if (c->ch == ' ') + { + strcpy(buf, "<sp>"); + } + else + { + buf[0] = c->ch; + buf[1] = '\0'; + } + + /* if the next command is the same, fold them onto one line */ + if ((c+1)->cmd_func == c->cmd_func) + { + strcat(buf, " or "); + p = buf + strlen(buf); + *p++ = (c+1)->ch; + *p = '\0'; + c++; + } + + display_pager("%-7s - %s\n", buf, help); + c++; + } + + display_pager("\nNot all commands are available on all systems.\n\n"); + display_pager("Available sort orders: %s\n", gstate->order_namelist); + display_pagerend(); + gstate->fulldraw = Yes; + return CMD_REFRESH; +} + +/* + * int command_process(globalstate *gstate, int cmd) + * + * Process the single-character command "cmd". The global state may + * be modified by the command to alter the output. Returns CMD_ERROR + * if there was a serious error that requires an immediate exit, CMD_OK + * to indicate success, CMD_REFRESH to indicate that the screen needs + * to be refreshed immediately, CMD_UNKNOWN when the command is not known, + * and CMD_NA when the command is not available. Error messages for + * CMD_NA and CMD_UNKNOWN must be handled by the caller. + */ + +int +command_process(globalstate *gstate, int cmd) + +{ + command *c; + + c = command_table; + while (c->cmd_func != NULL) + { + if (c->ch == cmd) + { + return (c->cmd_func)(gstate); + } + c++; + } + + return CMD_UNKNOWN; +} Index: top.c =================================================================== --- top.c (revision 183246) +++ top.c (working copy) @@ -1,1161 +1,1025 @@ -char *copyright = - "Copyright (c) 1984 through 1996, William LeFebvre"; - /* - * Top users/processes display for Unix - * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University - * Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory - * Copyright (c) 1996, William LeFebvre, Group sys Consulting - * - * $FreeBSD$ + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * See the file "Changes" for information on version-to-version changes. + * $FreeBSD$ */ -/* - * This file contains "main" and other high-level routines. - */ +char *copyright = + "Copyright (c) 1984 through 2008, William LeFebvre"; /* - * The following preprocessor variables, when defined, are used to - * distinguish between different Unix implementations: - * - * SIGHOLD - use SVR4 sighold function when defined - * SIGRELSE - use SVR4 sigrelse function when defined - * FD_SET - macros FD_SET and FD_ZERO are used when defined + * Changes to other files that we can do at the same time: + * screen.c:init_termcap: get rid of the "interactive" argument and have it + * pass back something meaningful (such as success/failure/error). */ #include "os.h" #include <errno.h> #include <signal.h> -#include <setjmp.h> #include <ctype.h> -#include <sys/time.h> +#include <setjmp.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +#ifdef HAVE_SYS_UTSNAME_H +#include <sys/utsname.h> +#endif + +#ifdef HAVE_GETOPT_H +#include <getopt.h> +#endif + +/* definitions */ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +/* determine which type of signal functions to use */ +/* cant have sigaction without sigprocmask */ +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK) +#undef HAVE_SIGACTION +#endif +/* always use sigaction when it is available */ +#ifdef HAVE_SIGACTION +#undef HAVE_SIGHOLD +#else +/* use sighold/sigrelse, otherwise use old fashioned BSD signals */ +#if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE) +#define BSD_SIGNALS +#endif +#endif + +/* if FD_SET and friends aren't present, then fake something up */ +#ifndef FD_SET +typedef int fd_set; +#define FD_ZERO(x) (*(x) = 0) +#define FD_SET(f, x) (*(x) = 1<<f) +#endif + /* 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 "globalstate.h" +#include "commands.h" +#include "display.h" +#include "screen.h" +#include "boolean.h" +#include "username.h" #include "utils.h" +#include "version.h" +#ifdef ENABLE_COLOR +#include "color.h" +#endif -/* Size of the stdio buffer given to stdout */ -#define Buffersize 2048 +/* definitions */ +#define BUFFERSIZE 4096 +#define JMP_RESUME 1 +#define JMP_RESIZE 2 -/* The buffer that stdio will use */ -char stdoutbuf[Buffersize]; - -/* build Signal masks */ -#define Smask(s) (1 << ((s) - 1)) - -/* for getopt: */ +/* externs for getopt: */ extern int optind; extern char *optarg; -/* imported from screen.c */ -extern int overstrike; +/* statics */ +static char stdoutbuf[BUFFERSIZE]; +static jmp_buf jmp_int; -static int fmt_flags = 0; +/* globals */ +char *myname = "top"; int pcpu_stats = No; -/* signal handling routines */ -sigret_t leave(); -sigret_t onalrm(); -sigret_t tstop(); -#ifdef SIGWINCH -sigret_t winch(); -#endif +void +quit(int status) -volatile sig_atomic_t leaveflag; -volatile sig_atomic_t tstopflag; -volatile sig_atomic_t winchflag; +{ + screen_end(); + chdir("/tmp"); + exit(status); + /* NOTREACHED */ +} -/* internal routines */ -void quit(); +/* + * signal handlers + */ -/* values which need to be accessed by signal handlers */ -static int max_topn; /* maximum displayable processes */ +void +set_signal(int sig, RETSIGTYPE (*handler)(int)) -/* miscellaneous things */ -struct process_select ps; -char *myname = "top"; -jmp_buf jmp_int; +{ +#ifdef HAVE_SIGACTION + struct sigaction action; -/* routines that don't return int */ + action.sa_handler = handler; + action.sa_flags = 0; + (void) sigaction(sig, &action, NULL); +#else + (void) signal(sig, handler); +#endif +} -char *username(); -char *ctime(); -char *kill_procs(); -char *renice_procs(); +void +release_signal(int sig) -#ifdef ORDER -extern int (*compares[])(); -#else -extern int proc_compare(); -extern int io_compare(); +{ +#ifdef HAVE_SIGACTION + sigset_t set; + sigemptyset(&set); + sigaddset(&set, sig); + sigprocmask(SIG_UNBLOCK, &set, NULL); #endif -time_t time(); -caddr_t get_process_info(); +#ifdef HAVE_SIGHOLD + sigrelse(sig); +#endif -/* different routines for displaying the user's identification */ -/* (values assigned to get_userid) */ -char *username(); -char *itoa7(); +#ifdef BSD_SIGNALS + (void) sigsetmask(sigblock(0) & ~(sigmask(sig))); +#endif +} -/* 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_swap(); -int u_swap(); -int i_message(); -int u_message(); -int i_header(); -int u_header(); -int i_process(); -int u_process(); +RETSIGTYPE +sig_leave(int i) /* exit under normal conditions -- INT handler */ -/* 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_swap)() = i_swap; -int (*d_message)() = i_message; -int (*d_header)() = i_header; -int (*d_process)() = i_process; +{ + screen_end(); + exit(EX_OK); +} +RETSIGTYPE +sig_tstop(int i) /* SIGTSTP handler */ -main(argc, argv) +{ + /* move to the lower left */ + screen_end(); + fflush(stdout); -int argc; -char *argv[]; + /* default the signal handler action */ + set_signal(SIGTSTP, SIG_DFL); + /* unblock the TSTP signal */ + release_signal(SIGTSTP); + + /* send ourselves a TSTP to stop the process */ + (void) kill(0, SIGTSTP); + + /* reset the signal handler */ + set_signal(SIGTSTP, sig_tstop); + + /* reinit screen */ + screen_reinit(); + + /* jump back to a known place in the main loop */ + longjmp(jmp_int, JMP_RESUME); + + /* NOTREACHED */ +} + +#ifdef SIGWINCH +RETSIGTYPE +sig_winch(int i) /* SIGWINCH handler */ + { - register int i; - register int active_procs; - register int change; + /* reascertain the screen dimensions */ + screen_getsize(); - struct system_info system_info; - struct statics statics; - caddr_t processes; + /* jump back to a known place in the main loop */ + longjmp(jmp_int, JMP_RESIZE); +} +#endif - 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 */ - int sel_ret = 0; - 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; +#ifdef HAVE_SIGACTION +static sigset_t signalset; #endif - char ch; - char *iptr; - char no_command = 1; - struct timeval timeout; -#ifdef ORDER - char *order_name = NULL; - int order_index = 0; + +void * +hold_signals() + +{ +#ifdef HAVE_SIGACTION + sigemptyset(&signalset); + sigaddset(&signalset, SIGINT); + sigaddset(&signalset, SIGQUIT); + sigaddset(&signalset, SIGTSTP); +#ifdef SIGWINCH + sigaddset(&signalset, SIGWINCH); #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) = 1<<f) + sigprocmask(SIG_BLOCK, &signalset, NULL); + return (void *)(&signalset); #endif - fd_set readfds; -#ifdef ORDER - static char command_chars[] = "\f qh?en#sdkriIutHmSCajo"; -#else - static char command_chars[] = "\f qh?en#sdkriIutHmSCaj"; +#ifdef HAVE_SIGHOLD + sighold(SIGINT); + sighold(SIGQUIT); + sighold(SIGTSTP); +#ifdef SIGWINCH + sighold(SIGWINCH); + return NULL; #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 -#define CMD_selftog 15 -#define CMD_thrtog 16 -#define CMD_viewtog 17 -#define CMD_viewsys 18 -#define CMD_wcputog 19 -#define CMD_showargs 20 -#define CMD_jidtog 21 -#ifdef ORDER -#define CMD_order 22 #endif - /* set the buffer for stdout */ -#ifdef DEBUG - extern FILE *debug; - debug = fopen("debug.run", "w"); - setbuffer(stdout, NULL, 0); +#ifdef BSD_SIGNALS + int mask; +#ifdef SIGWINCH + mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | + sigmask(SIGTSTP) | sigmask(SIGWINCH)); #else - setbuffer(stdout, stdoutbuf, Buffersize); + mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTSTP)); + return (void *)mask; #endif +#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.self = -1; - ps.system = No; - ps.uid = -1; - ps.thread = No; - ps.wcpu = 1; - ps.jail = No; - ps.command = NULL; +void +set_signals() - /* get preset options from the environment */ - if ((env_top = getenv("TOP")) != NULL) - { - av = preset_argv = argparse(env_top, &preset_argc); - ac = preset_argc; +{ + (void) set_signal(SIGINT, sig_leave); + (void) set_signal(SIGQUIT, sig_leave); + (void) set_signal(SIGTSTP, sig_tstop); +#ifdef SIGWINCH + (void) set_signal(SIGWINCH, sig_winch); +#endif +} - /* set the dummy argument to an explanatory message, in case - getopt encounters a bad argument */ - preset_argv[0] = "while processing environment"; - } +void +release_signals(void *parm) - /* process options */ - do { - /* if we're done doing the presets, then process the real arguments */ - if (preset_argc == 0) - { - ac = argc; - av = argv; +{ +#ifdef HAVE_SIGACTION + sigprocmask(SIG_UNBLOCK, (sigset_t *)parm, NULL); +#endif - /* this should keep getopt happy... */ - optind = 1; - } +#ifdef HAVE_SIGHOLD + sigrelse(SIGINT); + sigrelse(SIGQUIT); + sigrelse(SIGTSTP); +#ifdef SIGWINCH + sigrelse(SIGWINCH); +#endif +#endif - while ((i = getopt(ac, av, "CSIHPabijnquvs:d:U:m:o:t")) != EOF) - { - switch(i) - { - case 'v': /* show version number */ - fprintf(stderr, "%s: version %s\n", - myname, version_string()); - exit(1); - break; +#ifdef BSD_SIGNALS + (void) sigsetmask((int)parm); +#endif +} - case 'u': /* toggle uid/username display */ - do_unames = !do_unames; - break; +/* + * void do_arguments(globalstate *gstate, int ac, char **av) + * + * Arguments processing. gstate points to the global state, + * ac and av are the arguments to process. This can be called + * multiple times with different sets of arguments. + */ - case 'U': /* display only username's processes */ - if ((ps.uid = userid(optarg)) == -1) - { - fprintf(stderr, "%s: unknown user\n", optarg); - exit(1); - } - break; +#ifdef HAVE_GETOPT_LONG +static struct option longopts[] = { + { "color", no_argument, NULL, 'C' }, + { "debug", no_argument, NULL, 'D' }, + { "system-procs", no_argument, NULL, 'S' }, + { "idle-procs", no_argument, NULL, 'I' }, + { "tag-names", no_argument, NULL, 'T' }, + { "all", no_argument, NULL, 'a' }, + { "batch", no_argument, NULL, 'b' }, + { "full-commands", no_argument, NULL, 'c' }, + { "interactive", no_argument, NULL, 'i' }, + { "quick", no_argument, NULL, 'q' }, + { "threads", no_argument, NULL, 't' }, + { "uids", no_argument, NULL, 'u' }, + { "version", no_argument, NULL, 'v' }, + { "delay", required_argument, NULL, 's' }, + { "displays", required_argument, NULL, 'd' }, + { "user", required_argument, NULL, 'U' }, + { "sort-order", required_argument, NULL, 'o' }, + { "display-mode", required_argument, NULL, 'm' }, + { NULL, 0, NULL, 0 }, +}; +#endif - case 'S': /* show system processes */ - ps.system = !ps.system; - break; - case 'I': /* show idle processes */ - ps.idle = !ps.idle; - break; +void +do_arguments(globalstate *gstate, int ac, char **av) - case 'i': /* go interactive regardless */ - interactive = Yes; - break; +{ + int i; - case 'n': /* batch, or non-interactive */ - case 'b': - interactive = No; - break; + /* this appears to keep getopt happy */ + optind = 1; - case 'a': - fmt_flags ^= FMT_SHOWARGS; - 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 || (delay == 0 && getuid() != 0)) - { - fprintf(stderr, - "%s: warning: seconds delay should be positive -- 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 'm': /* select display mode */ - if (strcmp(optarg, "io") == 0) { - displaymode = DISP_IO; - } else if (strcmp(optarg, "cpu") == 0) { - displaymode = DISP_CPU; - } else { - fprintf(stderr, - "%s: warning: `-m' option can only take args " - "'io' or 'cpu'\n", - myname); - exit(1); - } - break; - - case 'o': /* select sort order */ -#ifdef ORDER - order_name = optarg; +#ifdef HAVE_GETOPT_LONG + while ((i = getopt_long(ac, av, "CDEHSITabcijnqtuvs:d:U:o:m:P", longopts, NULL)) != -1) #else - fprintf(stderr, - "%s: this platform does not support arbitrary ordering. Sorry.\n", - myname); - warnings++; + while ((i = getopt(ac, av, "CDEHSITabcijnqtuvs:d:U:o:m:P")) != EOF) #endif - break; + { + switch(i) + { +#ifdef ENABLE_COLOR + case 'E': + gstate->use_color = !gstate->use_color; + break; +#endif - case 't': - ps.self = (ps.self == -1) ? getpid() : -1; - break; + case 'D': + debug_set(1); + break; - case 'C': - ps.wcpu = !ps.wcpu; - break; + case 'v': + fprintf(stderr, "%s: version %s\n", myname, version_string()); + exit(EX_OK); + break; - case 'H': - ps.thread = !ps.thread; - break; + case 'b': + case 'n': + gstate->interactive = No; + break; - case 'j': - ps.jail = !ps.jail; - break; + case 'a': + gstate->displays = Infinity; + gstate->topn = Infinity; + break; - case 'P': - pcpu_stats = Yes; - break; + case 'i': + gstate->interactive = Yes; + break; - default: - fprintf(stderr, -"Top version %s\n" -"Usage: %s [-abCHIijnPqStuv] [-d count] [-m io | cpu] [-o field] [-s time]\n" -" [-U username] [number]\n", - version_string(), myname); - exit(1); + case 'o': + gstate->order_name = optarg; + break; + + case 'd': + i = atoiwi(optarg); + if (i == Invalid || i == 0) + { + message_error(" Bad display count"); } - } + else + { + gstate->displays = i; + } + break; - /* get count of top processes to display (if any) */ - if (optind < ac) - { - if ((topn = atoiwi(av[optind])) == Invalid) + case 's': + i = atoi(optarg); + if (i < 0 || (i == 0 && getuid() != 0)) { - fprintf(stderr, - "%s: warning: process display count should be non-negative -- using default\n", - myname); - warnings++; + message_error(" Bad seconds delay"); } -#if Default_TOPN == Infinity - else + else { - topn_specified = Yes; + gstate->delay = i; } -#endif - } + break; - /* tricky: remember old value of preset_argc & set preset_argc = 0 */ - i = preset_argc; - preset_argc = 0; + case 'u': + gstate->show_usernames = !gstate->show_usernames; + break; - /* repeat only if we really did the preset arguments */ - } while (i != 0); + case 'U': + i = userid(optarg); + if (i == -1) + { + message_error(" Unknown user '%s'", optarg); + } + else + { + gstate->pselect.uid = i; + } + break; - /* set constants for username/uid display correctly */ - if (!do_unames) - { - uname_field = " UID "; - get_userid = itoa7; - } + case 'm': + if (strcmp(optarg, "io") == 0) { + gstate->pselect.mode = DISP_IO; + } else if (strcmp(optarg, "cpu") == 0) { + gstate->pselect.mode = DISP_CPU; + } else { + fprintf(stderr, + "%s: warning: `-m' option can only take args " + "'io' or 'cpu'\n", + myname); + exit(1); + } + break; - /* initialize the kernel memory interface */ - if (machine_init(&statics, do_unames) == -1) - { - exit(1); - } + case 'H': + gstate->pselect.threads = !gstate->pselect.threads; + break; -#ifdef ORDER - /* determine sorting order index, if necessary */ - if (order_name != NULL) - { - if ((order_index = string_index(order_name, statics.order_names)) == -1) - { - char **pp; + case 'C': + gstate->pselect.wcpu = !gstate->pselect.wcpu; + break; - fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n", - myname, order_name); - fprintf(stderr, "\tTry one of these:"); - pp = statics.order_names; - while (*pp != NULL) + case 'j': + gstate->pselect.jail = !gstate->pselect.jail; + break; + + case 'S': + gstate->pselect.system = !gstate->pselect.system; + break; + + case 'I': + gstate->pselect.idle = !gstate->pselect.idle; + break; + +#ifdef ENABLE_COLOR + case 'T': + gstate->show_tags = 1; + break; +#endif + + case 'c': + gstate->pselect.fullcmd = !gstate->pselect.fullcmd; + break; + + case 't': + gstate->pselect.self = !gstate->pselect.self; + break; + + case 'q': /* be quick about it */ + /* only allow this if user is really root */ + if (getuid() == 0) { - fprintf(stderr, " %s", *pp++); + /* be very un-nice! */ + (void) nice(-20); } - fputc('\n', stderr); - exit(1); + else + { + message_error(" Option -q can only be used by root"); + } + break; + + case 'P': + pcpu_stats = Yes; + break; + + default: + fprintf(stderr, +"Top version %s\n" +"Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n", + version_string(), myname); + exit(EX_USAGE); } } -#endif -#ifdef no_initialization_needed - /* initialize the hashing stuff */ - if (do_unames) + /* get count of top processes to display */ + if (optind < ac && *av[optind]) { - init_hash(); + if ((i = atoiwi(av[optind])) == Invalid) + { + message_error(" Process count not a number"); + } + else + { + gstate->topn = i; + } } -#endif +} - /* initialize termcap */ - init_termcap(interactive); +void +do_display(globalstate *gstate) - /* get the string to use for the process area header */ - header_text = format_header(uname_field); +{ + int active_procs; + int i; + time_t curr_time; + caddr_t processes; + struct system_info system_info; + char *hdr; - /* initialize display interface */ - if ((max_topn = display_init(&statics)) == -1) - { - fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); - exit(4); - } - - /* print warning if user requested more processes than we can display */ - if (topn > max_topn) - { - fprintf(stderr, - "%s: warning: this terminal can only display %d processes.\n", - myname, max_topn); - warnings++; - } + /* get the time */ + time_mark(&(gstate->now)); + curr_time = (time_t)(gstate->now.tv_sec); - /* adjust for topn == Infinity */ - if (topn == Infinity) - { - /* - * For smart terminals, infinity really means everything that can - * be displayed, or Largest. - * On dumb terminals, infinity means every process in the system! - * We only really want to do that if it was explicitly specified. - * This is always the case when "Default_TOPN != Infinity". But if - * topn wasn't explicitly specified and we are on a dumb terminal - * and the default is Infinity, then (and only then) we use - * "Nominal_TOPN" instead. - */ -#if Default_TOPN == Infinity - topn = smart_terminal ? Largest : - (topn_specified ? Largest : Nominal_TOPN); -#else - topn = Largest; -#endif - } + /* get the current stats */ + get_system_info(&system_info); - /* set header display accordingly */ - display_header(topn > 0); + /* get the current processes */ + processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index); - /* determine interactive state */ - if (interactive == Maybe) + /* determine number of processes to actually display */ + if (gstate->topn > 0) { - interactive = smart_terminal; + /* this number will be the smallest of: active processes, + number user requested, number current screen accomodates */ + active_procs = system_info.P_ACTIVE; + if (active_procs > gstate->topn) + { + active_procs = gstate->topn; + } + if (active_procs > gstate->max_topn) + { + active_procs = gstate->max_topn; + } } - - /* if # of displays not specified, fill it in */ - if (displays == 0) + else { - displays = smart_terminal ? Infinity : 1; + /* dont show any */ + active_procs = 0; } - /* hold interrupt signals while setting up the screen and the handlers */ -#ifdef SIGHOLD - sighold(SIGINT); - sighold(SIGQUIT); - sighold(SIGTSTP); +#ifdef HAVE_FORMAT_PROCESS_HEADER + /* get the process header to use */ + hdr = format_process_header(&(gstate->pselect), processes, active_procs); #else - old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP)); + hdr = gstate->header_text; #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); - } -restart: - - /* - * main loop -- repeat while display count is positive or while it - * indicates infinity (by being -1) - */ - - while ((displays == -1) || (displays-- > 0)) + /* full screen or update? */ + if (gstate->fulldraw) { - int (*compare)(); - - - /* get the current stats */ - get_system_info(&system_info); - -#ifdef ORDER - compare = compares[order_index]; -#else - if (displaymode == DISP_CPU) - compare = proc_compare; - else - compare = io_compare; -#endif - - /* get the current set of processes */ - processes = - get_process_info(&system_info, &ps, compare); - - /* display the load averages */ - (*d_loadave)(system_info.last_pid, - system_info.load_avg); - - /* display the current time */ - /* this method of getting the time SHOULD be fairly portable */ - time(&curr_time); - i_uptime(&system_info.boottime, &curr_time); + display_clear(); + i_loadave(system_info.last_pid, system_info.load_avg); + i_uptime(&(gstate->statics->boottime), &curr_time); i_timeofday(&curr_time); - - /* display process state breakdown */ - (*d_procstates)(system_info.p_total, - system_info.procstates); - - /* display the cpu state percentage breakdown */ - if (dostates) /* but not the first time */ + i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); + if (gstate->show_cpustates) { - (*d_cpustates)(system_info.cpustates); + i_cpustates(system_info.cpustates); } else { - /* we'll do it next time */ if (smart_terminal) { z_cpustates(); } - else - { - putchar('\n'); - } - dostates = Yes; + gstate->show_cpustates = Yes; } + i_kernel(system_info.kernel); + i_memory(system_info.memory); + i_swap(system_info.swap); + i_message(&(gstate->now)); + i_header(hdr); + for (i = 0; i < active_procs; i++) + { + i_process(i, + format_next_process(&(gstate->pselect), processes, gstate->get_userid)); + } + i_endscreen(); + if (gstate->smart_terminal) + { + gstate->fulldraw = No; + } + } + else + { + u_loadave(system_info.last_pid, system_info.load_avg); + i_timeofday(&curr_time); + u_uptime(&(gstate->statics->boottime), &curr_time); + u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); + u_cpustates(system_info.cpustates); + u_kernel(system_info.kernel); + u_memory(system_info.memory); + u_swap(system_info.swap); + u_message(&(gstate->now)); + u_header(hdr); + for (i = 0; i < active_procs; i++) + { + u_process(i, + format_next_process(&(gstate->pselect), processes, gstate->get_userid)); + } + u_endscreen(); + } +} - /* display memory stats */ - (*d_memory)(system_info.memory); +#ifdef DEBUG +void +timeval_xdprint(char *s, struct timeval tv) - /* display swap stats */ - (*d_swap)(system_info.swap); +{ + xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec); +} +#endif - /* handle message area */ - (*d_message)(); +void +do_wait(globalstate *gstate) - /* update the header area */ - (*d_header)(header_text); - - if (topn > 0) +{ + struct timeval wait; + + wait.tv_sec = gstate->delay; + wait.tv_usec = 0; + select(0, NULL, NULL, NULL, &wait); +} + +void +do_command(globalstate *gstate) + +{ + int status; + struct timeval wait = {0, 0}; + struct timeval now; + fd_set readfds; + unsigned char ch; + int sel_ret = 0; + + /* calculate new refresh time */ + gstate->refresh = gstate->now; + gstate->refresh.tv_sec += gstate->delay; + time_get(&now); + + /* loop waiting for time to expire */ + do { + /* calculate time to wait */ + if (gstate->delay > 0) { - /* determine number of processes to actually display */ - /* this number will be the smallest of: active processes, - number user requested, number current screen accomodates */ - active_procs = system_info.P_ACTIVE; - if (active_procs > topn) + wait = gstate->refresh; + wait.tv_usec -= now.tv_usec; + if (wait.tv_usec < 0) { - active_procs = topn; + wait.tv_usec += 1000000; + wait.tv_sec--; } - if (active_procs > max_topn) + wait.tv_sec -= now.tv_sec; + } + + /* set up arguments for select on stdin (0) */ + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + + /* wait for something to read or time out */ + sel_ret = select(2, &readfds, NULL, NULL, &wait); + if (sel_ret < 0 && errno != EINTR) + quit(0); + if (sel_ret > 0) + { + /* read it */ + if (read(STDIN_FILENO, &ch, 1) != 1) { - active_procs = max_topn; + /* read error */ + message_error(" Read error on stdin"); + quit(EX_DATAERR); + /*NOTREACHED*/ } - /* now show the top "n" processes. */ - for (i = 0; i < active_procs; i++) + /* mark pending messages as old */ + message_mark(); + + /* dispatch */ + status = command_process(gstate, (int)ch); + switch(status) { - (*d_process)(i, format_next_process(processes, get_userid, - fmt_flags)); + case CMD_ERROR: + quit(EX_SOFTWARE); + /*NOTREACHED*/ + + case CMD_REFRESH: + return; + + case CMD_UNKNOWN: + message_error(" Unknown command"); + break; + + case CMD_NA: + message_error(" Command not available"); } } + + /* get new time */ + time_get(&now); + } while (timercmp(&now, &(gstate->refresh), < )); +} + +void +do_minidisplay(globalstate *gstate) + +{ + int real_delay; + struct system_info si; + + /* save the real delay and substitute 1 second */ + real_delay = gstate->delay; + gstate->delay = 1; + + /* wait 1 second for a command */ + time_mark(&(gstate->now)); + do_command(gstate); + + /* do a mini update that only updates the cpustates */ + get_system_info(&si); + u_cpustates(si.cpustates); + + /* restore the delay time */ + gstate->delay = real_delay; + + /* done */ + i_endscreen(); +} + +int +main(int argc, char *argv[]) + +{ + char *env_top; + char **preset_argv; + int preset_argc = 0; + void *mask; + int need_mini = 1; + + struct statics statics; + globalstate *gstate; + + /* get our name */ + if (argc > 0) + { + if ((myname = strrchr(argv[0], '/')) == 0) + { + myname = argv[0]; + } else { - i = 0; + myname++; } + } - /* do end-screen processing */ - u_endscreen(i); + /* binary compatibility check */ +#ifdef HAVE_UNAME + { + struct utsname uts; - /* now, flush the output buffer */ - if (fflush(stdout) != 0) + if (uname(&uts) == 0) { - new_message(MT_standout, " Write error on stdout"); - putchar('\r'); - quit(1); - /*NOTREACHED*/ - } - - /* 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 (strcmp(uts.machine, UNAME_HARDWARE) != 0) { - if (overstrike) - { - reset_display(); - } - else - { - d_loadave = u_loadave; - d_procstates = u_procstates; - d_cpustates = u_cpustates; - d_memory = u_memory; - d_swap = u_swap; - d_message = u_message; - d_header = u_header; - d_process = u_process; - } + fprintf(stderr, "%s: incompatible hardware platform\n", + myname); + exit(EX_UNAVAILABLE); } - - 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; + } + } +#endif - /* set up arguments for select with timeout */ - FD_ZERO(&readfds); - FD_SET(0, &readfds); /* for standard input */ - timeout.tv_sec = delay; - timeout.tv_usec = 0; + /* initialization */ + gstate = (globalstate *)calloc(1, sizeof(globalstate)); + gstate->statics = &statics; + time_mark(NULL); - if (leaveflag) { - end_screen(); - exit(0); - } + /* preset defaults for various options */ + gstate->show_usernames = Yes; + gstate->topn = DEFAULT_TOPN; + gstate->delay = DEFAULT_DELAY; + gstate->fulldraw = Yes; + gstate->use_color = Yes; + gstate->interactive = Maybe; - if (tstopflag) { - /* move to the lower left */ - end_screen(); - fflush(stdout); + /* preset defaults for process selection */ + gstate->pselect.idle = Yes; + gstate->pselect.self = -1; + gstate->pselect.threads = No; + gstate->pselect.wcpu = 1; + gstate->pselect.jail = No; + gstate->pselect.system = No; + gstate->pselect.fullcmd = No; + gstate->pselect.command = NULL; + gstate->pselect.uid = -1; + gstate->pselect.mode = DISP_CPU; - /* default the signal handler action */ - (void) signal(SIGTSTP, SIG_DFL); - - /* unblock the signal and send ourselves one */ -#ifdef SIGRELSE - sigrelse(SIGTSTP); + /* use a large buffer for stdout */ +#ifdef HAVE_SETVBUF + setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE); #else - (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); +#ifdef HAVE_SETBUFFER + setbuffer(stdout, stdoutbuf, BUFFERSIZE); #endif - (void) kill(0, SIGTSTP); +#endif - /* reset the signal handler */ - (void) signal(SIGTSTP, tstop); + /* get preset options from the environment */ + if ((env_top = getenv("TOP")) != NULL) + { + preset_argv = argparse(env_top, &preset_argc); + preset_argv[0] = myname; + do_arguments(gstate, preset_argc, preset_argv); + } - /* reinit screen */ - reinit_screen(); - reset_display(); - tstopflag = 0; - goto restart; - } + /* process arguments */ + do_arguments(gstate, argc, argv); - if (winchflag) { - /* reascertain the screen dimensions */ - get_screensize(); +#ifdef ENABLE_COLOR + /* If colour has been turned on read in the settings. */ + env_top = getenv("TOPCOLOURS"); + if (!env_top) + { + env_top = getenv("TOPCOLORS"); + } + /* must do something about error messages */ + color_env_parse(env_top); + color_activate(gstate->use_color); +#endif - /* tell display to resize */ - max_topn = display_resize(); + /* in order to support forward compatability, we have to ensure that + the entire statics structure is set to a known value before we call + machine_init. This way fields that a module does not know about + will retain their default values */ + memzero((void *)&statics, sizeof(statics)); + statics.boottime = -1; - /* reset the signal handler */ - (void) signal(SIGWINCH, winch); + /* call the platform-specific init */ + if (machine_init(&statics) == -1) + { + exit(EX_SOFTWARE); + } + if (pcpu_stats == No) + statics.ncpus = 1; - reset_display(); - winchflag = 0; - goto restart; - } + /* create a helper list of sort order names */ + gstate->order_namelist = string_list(statics.order_names); - /* wait for either input or the end of the delay period */ - sel_ret = select(2, &readfds, NULL, NULL, &timeout); - if (sel_ret < 0 && errno != EINTR) - quit(0); - if (sel_ret > 0) - { - int newval; - char *errmsg; - - /* something to read -- clear the message area first */ - clear_message(); + /* look up chosen sorting order */ + if (gstate->order_name != NULL) + { + int i; - /* now read it and convert to command strchr */ - /* (use "change" as a temporary to hold strchr) */ - if (read(0, &ch, 1) != 1) - { - /* read error: either 0 or -1 */ - new_message(MT_standout, " Read error on stdin"); - putchar('\r'); - quit(1); - /*NOTREACHED*/ - } - if ((iptr = strchr(command_chars, ch)) == NULL) - { - if (ch != '\r' && ch != '\n') - { - /* illegal command */ - new_message(MT_standout, " Command not understood"); - } - putchar('\r'); - no_command = Yes; - } - else - { - change = iptr - command_chars; - if (overstrike && change > CMD_OSLIMIT) - { - /* error */ - new_message(MT_standout, - " Command cannot be handled by this terminal"); - putchar('\r'); - no_command = Yes; - } - else switch(change) - { - case CMD_redraw: /* redraw screen */ - reset_display(); - break; - - case CMD_update: /* merely update display */ - /* is the load average high? */ - if (system_info.load_avg[0] > LoadMax) - { - /* yes, go home for visual feedback */ - go_home(); - fflush(stdout); - } - break; - - case CMD_quit: /* quit */ - quit(0); - /*NOTREACHED*/ - break; - - case CMD_help1: /* help */ - case CMD_help2: - reset_display(); - clear(); - show_help(); - standout("Hit any key to continue: "); - fflush(stdout); - (void) read(0, &ch, 1); - break; - - case CMD_errors: /* show errors */ - if (error_count() == 0) - { - new_message(MT_standout, - " Currently no errors to report."); - putchar('\r'); - no_command = Yes; - } - else - { - reset_display(); - clear(); - show_errors(); - standout("Hit any key to continue: "); - fflush(stdout); - (void) read(0, &ch, 1); - } - break; - - case CMD_number1: /* new number */ - case CMD_number2: - new_message(MT_standout, - "Number of processes to show: "); - newval = readline(tempbuf1, 8, Yes); - if (newval > -1) - { - if (newval > max_topn) - { - new_message(MT_standout | MT_delayed, - " This terminal can only display %d processes.", - max_topn); - putchar('\r'); - } + if (statics.order_names == NULL) + { + message_error(" This platform does not support arbitrary ordering"); + } + else if ((i = string_index(gstate->order_name, + statics.order_names)) == -1) + { + message_error(" Sort order `%s' not recognized", gstate->order_name); + message_error(" Recognized sort orders: %s", gstate->order_namelist); + } + else + { + gstate->order_index = i; + } + } - if (newval == 0) - { - /* inhibit the header */ - display_header(No); - } - else if (newval > topn && topn == 0) - { - /* redraw the header */ - display_header(Yes); - d_header = i_header; - } - topn = newval; - } - break; - - case CMD_delay: /* new seconds delay */ - new_message(MT_standout, "Seconds to delay: "); - if ((i = readline(tempbuf1, 8, Yes)) > -1) - { - if ((delay = i) == 0 && getuid() != 0) - { - delay = 1; - } - } - clear_message(); - break; - - case CMD_displays: /* change display count */ - new_message(MT_standout, - "Displays to show (currently %s): ", - displays == -1 ? "infinite" : - itoa(displays)); - if ((i = readline(tempbuf1, 10, Yes)) > 0) - { - displays = i; - } - else if (i == 0) - { - quit(0); - } - clear_message(); - break; - - case CMD_kill: /* kill program */ - new_message(0, "kill "); - if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) - { - if ((errmsg = kill_procs(tempbuf2)) != NULL) - { - new_message(MT_standout, "%s", errmsg); - putchar('\r'); - no_command = Yes; - } - } - else - { - clear_message(); - } - break; - - case CMD_renice: /* renice program */ - new_message(0, "renice "); - if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) - { - if ((errmsg = renice_procs(tempbuf2)) != NULL) - { - new_message(MT_standout, "%s", errmsg); - putchar('\r'); - no_command = Yes; - } - } - else - { - clear_message(); - } - break; + /* initialize extensions */ + init_username(); - 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; + /* initialize termcap */ + gstate->smart_terminal = screen_readtermcap(gstate->interactive); - case CMD_selftog: - ps.self = (ps.self == -1) ? getpid() : -1; - new_message(MT_standout | MT_delayed, - " %sisplaying self.", - (ps.self == -1) ? "D" : "Not d"); - putchar('\r'); - break; + /* determine interactive state */ + if (gstate->interactive == Maybe) + { + gstate->interactive = smart_terminal; + } - 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; - - case CMD_thrtog: - ps.thread = !ps.thread; - new_message(MT_standout | MT_delayed, - "Displaying threads %s", - ps.thread ? "separately" : "as a count"); - header_text = format_header(uname_field); - reset_display(); - putchar('\r'); - break; - case CMD_wcputog: - ps.wcpu = !ps.wcpu; - new_message(MT_standout | MT_delayed, - "Displaying %sCPU", - ps.wcpu ? "W" : ""); - header_text = format_header(uname_field); - reset_display(); - putchar('\r'); - break; - case CMD_viewtog: - if (++displaymode == DISP_MAX) - displaymode = 0; - header_text = format_header(uname_field); - display_header(Yes); - d_header = i_header; - reset_display(); - break; - case CMD_viewsys: - ps.system = !ps.system; - break; - case CMD_showargs: - fmt_flags ^= FMT_SHOWARGS; - break; -#ifdef ORDER - case CMD_order: - new_message(MT_standout, - "Order to sort: "); - if (readline(tempbuf2, sizeof(tempbuf2), No) > 0) - { - if ((i = string_index(tempbuf2, statics.order_names)) == -1) - { - new_message(MT_standout, - " %s: unrecognized sorting order", tempbuf2); - no_command = Yes; - } - else - { - order_index = i; - } - putchar('\r'); - } - else - { - clear_message(); - } - break; -#endif - case CMD_jidtog: - ps.jail = !ps.jail; - new_message(MT_standout | MT_delayed, - " %sisplaying jail ID.", - ps.jail ? "D" : "Not d"); - header_text = format_header(uname_field); - reset_display(); - putchar('\r'); - break; - - default: - new_message(MT_standout, " BAD CASE IN SWITCH!"); - putchar('\r'); - } - } + /* if displays were not specified, choose an appropriate default */ + if (gstate->displays == 0) + { + gstate->displays = gstate->smart_terminal ? Infinity: 1; + } - /* flush out stuff that may have been written */ - fflush(stdout); - } - } - } + /* we don't need a mini display when delay is less than 2 + seconds or when we are not on a smart terminal */ + if (gstate->delay <= 1 || !smart_terminal) + { + need_mini = 0; } -#ifdef DEBUG - fclose(debug); +#ifndef HAVE_FORMAT_PROCESS_HEADER + /* set constants for username/uid display */ + if (gstate->show_usernames) + { + gstate->header_text = format_header("USERNAME"); + gstate->get_userid = username; + } + else + { + gstate->header_text = format_header(" UID "); + gstate->get_userid = itoa7; + } #endif - quit(0); - /*NOTREACHED*/ -} + gstate->pselect.usernames = gstate->show_usernames; -/* - * reset_display() - reset all the display routine pointers so that entire - * screen will get redrawn. - */ + /* initialize display */ + if ((gstate->max_topn = display_init(&statics)) == -1) + { + fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); + exit(EX_OSERR); + } -reset_display() + /* check for infinity and for overflowed screen */ + gstate->topn = gstate->max_topn; + if (gstate->topn == Infinity) + { + gstate->topn = INT_MAX; + } + else if (gstate->topn > gstate->max_topn) + { + message_error(" This terminal can only display %d processes", + gstate->max_topn); + } -{ - d_loadave = i_loadave; - d_procstates = i_procstates; - d_cpustates = i_cpustates; - d_memory = i_memory; - d_swap = i_swap; - d_message = i_message; - d_header = i_header; - d_process = i_process; -} +#ifdef ENABLE_COLOR + /* producing a list of color tags is easy */ + if (gstate->show_tags) + { + color_dump(stdout); + exit(EX_OK); + } +#endif -/* - * signal handlers - */ + /* hold all signals while we initialize the screen */ + mask = hold_signals(); + screen_init(); -sigret_t leave() /* exit under normal conditions -- INT handler */ + /* set the signal handlers */ + set_signals(); -{ - leaveflag = 1; -} + /* longjmp re-entry point */ + /* set the jump buffer for long jumps out of signal handlers */ + if (setjmp(jmp_int) != 0) + { + /* this is where we end up after processing sigwinch or sigtstp */ -sigret_t tstop(i) /* SIGTSTP handler */ + /* tell display to resize its buffers, and get the new length */ + if ((gstate->max_topn = display_resize()) == -1) + { + /* thats bad */ + quit(EX_OSERR); + /*NOTREACHED*/ + } + gstate->topn = gstate->max_topn; -int i; + /* set up for a full redraw, and get the current line count */ + gstate->fulldraw = Yes; -{ - tstopflag = 1; -} + /* safe to release the signals now */ + release_signals(mask); + } + else + { + /* release the signals */ + release_signals(mask); -#ifdef SIGWINCH -sigret_t winch(i) /* SIGWINCH handler */ + /* some systems require a warmup */ + /* always do a warmup for batch mode */ + if (gstate->interactive == 0 || statics.flags.warmup) + { + struct system_info system_info; + struct timeval timeout; -int i; + time_mark(&(gstate->now)); + get_system_info(&system_info); + (void)get_process_info(&system_info, &gstate->pselect, 0); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + select(0, NULL, NULL, NULL, &timeout); -{ - winchflag = 1; -} -#endif + /* if we've warmed up, then we can show good states too */ + gstate->show_cpustates = Yes; + need_mini = 0; + } + } -void quit(status) /* exit under duress */ + /* main loop */ + while ((gstate->displays == -1) || (--gstate->displays > 0)) + { + do_display(gstate); + if (gstate->interactive) + { + if (need_mini) + { + do_minidisplay(gstate); + need_mini = 0; + } + do_command(gstate); + } + else + { + do_wait(gstate); + } + } -int status; + /* do one last display */ + do_display(gstate); -{ - end_screen(); - exit(status); - /*NOTREACHED*/ + quit(EX_OK); + /* NOTREACHED */ + return 1; /* Keep compiler quiet. */ } - -sigret_t onalrm() /* SIGALRM handler */ - -{ - /* this is only used in batch mode to break out of the pause() */ - /* return; */ -} - Index: getopt.c =================================================================== --- getopt.c (revision 183246) +++ getopt.c (working copy) @@ -1,4 +1,36 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * "getopt" routine customized for top. */ @@ -43,9 +75,8 @@ char *optarg; int -getopt(argc, argv, opts) -int argc; -char **argv, *opts; +getopt(int argc, char **argv, char *opts) + { static int sp = 1; register int c; Index: display.c =================================================================== --- display.c (revision 183246) +++ display.c (working copy) @@ -1,14 +1,42 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * - * $FreeBSD$ */ /* @@ -23,55 +51,104 @@ * In this way, those routines can be safely used on terminals that * have minimal (or nonexistant) terminal capabilities. * - * The routines are called in this order: *_loadave, i_timeofday, - * *_procstates, *_cpustates, *_memory, *_message, *_header, - * *_process, u_endscreen. + * The routines should be called in this order: *_loadave, *_uptime, + * i_timeofday, *_procstates, *_cpustates, *_memory, *_swap, + * *_message, *_header, *_process, *_endscreen. */ #include "os.h" #include <ctype.h> -#include <time.h> -#include <sys/time.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +#include "top.h" +#include "machine.h" #include "screen.h" /* interface to screen package */ #include "layout.h" /* defines for screen position layout */ #include "display.h" -#include "top.h" -#include "top.local.h" #include "boolean.h" -#include "machine.h" /* we should eliminate this!!! */ #include "utils.h" -#ifdef DEBUG -FILE *debug; +#ifdef ENABLE_COLOR +#include "color.h" #endif +#define CURSOR_COST 8 + +#define MESSAGE_DISPLAY_TIME 5 + /* imported from screen.c */ extern int overstrike; -static int lmpid = 0; -static int last_hi = 0; /* used in u_process and u_endscreen */ -static int lastline = 0; +static int lmpid = -1; static int display_width = MAX_COLS; -#define lineindex(l) ((l)*display_width) +/* cursor positions of key points on the screen are maintained here */ +/* layout.h has static definitions, but we may change our minds on some + of the positions as we make decisions about what needs to be displayed */ -char *printable(); +static int x_lastpid = X_LASTPID; +static int y_lastpid = Y_LASTPID; +static int x_loadave = X_LOADAVE; +static int x_loadave_nompid = X_LOADAVE_NOMPID; +static int y_loadave = Y_LOADAVE; +static int x_minibar = X_MINIBAR; +static int y_minibar = Y_MINIBAR; +static int x_uptime = X_UPTIME; +static int y_uptime = Y_UPTIME; +static int x_procstate = X_PROCSTATE; +static int y_procstate = Y_PROCSTATE; +static int x_cpustates = X_CPUSTATES; +static int y_cpustates = Y_CPUSTATES; +static int x_kernel = X_KERNEL; +static int y_kernel = Y_KERNEL; +static int x_brkdn = X_BRKDN; +static int y_brkdn = Y_BRKDN; +static int x_mem = X_MEM; +static int y_mem = Y_MEM; +static int x_swap = X_SWAP; +static int y_swap = Y_SWAP; +static int y_message = Y_MESSAGE; +static int x_header = X_HEADER; +static int y_header = Y_HEADER; +static int x_idlecursor = X_IDLECURSOR; +static int y_idlecursor = Y_IDLECURSOR; +static int y_procs = Y_PROCS; -/* things initialized by display_init and used thruout */ +static int Header_lines = 7; /* XXXEG */ -/* buffer of proc information lines for display updating */ -char *screenbuf = NULL; +/* buffer and colormask that describes the content of the screen */ +/* these are singly dimensioned arrays -- the row boundaries are + determined on the fly. +*/ +static char *screenbuf = NULL; +static char *colorbuf = NULL; +static char scratchbuf[MAX_COLS]; +static int bufsize = 0; +/* lineindex tells us where the beginning of a line is in the buffer */ +#define lineindex(l) ((l)*MAX_COLS) + +/* screen's cursor */ +static int curr_x, curr_y; +static int curr_color; + +/* virtual cursor */ +static int virt_x, virt_y; + static char **procstate_names; static char **cpustate_names; static char **memory_names; static char **swap_names; +static char **kernel_names; static int num_procstates; static int num_cpustates; static int num_memory; static int num_swap; +static int num_kernel; static int *lprocstates; static int *lcpustates; @@ -83,52 +160,534 @@ static int cpustate_total_length; static int cpustates_column; -static enum { OFF, ON, ERASE } header_status = ON; +static int header_status = Yes; -static int string_count(); -static void summary_format(); -static void line_update(); +/* pending messages are stored in a circular buffer, where message_first + is the next one to display, and message_last is the last one + in the buffer. Counters wrap around at MAX_MESSAGES. The buffer is + empty when message_first == message_last and full when + message_last + 1 == message_first. The pointer message_current holds + the message currently being displayed, or "" if there is none. +*/ +#define MAX_MESSAGES 16 +static char *message_buf[MAX_MESSAGES]; +static int message_first = 0; +static int message_last = 0; +static struct timeval message_time = {0, 0}; +static char *message_current = NULL; +static int message_length = 0; +static int message_hold = 1; +static int message_barrier = No; -int x_lastpid = 10; -int y_lastpid = 0; -int x_loadave = 33; -int x_loadave_nompid = 15; -int y_loadave = 0; -int x_procstate = 0; -int y_procstate = 1; -int x_brkdn = 15; -int y_brkdn = 1; -int x_mem = 5; -int y_mem = 3; -int x_swap = 6; -int y_swap = 4; -int y_message = 5; -int x_header = 0; -int y_header = 6; -int x_idlecursor = 0; -int y_idlecursor = 5; -int y_procs = 7; +#ifdef ENABLE_COLOR +static int load_cidx[3]; +static int header_cidx; +static int *cpustate_cidx; +static int *memory_cidx; +static int *swap_cidx; +static int *kernel_cidx; +#else +#define memory_cidx NULL +#define swap_cidx NULL +#define kernel_cidx NULL +#endif -int y_cpustates = 2; -int Header_lines = 7; -int display_resize() +/* internal support routines */ +/* + * static int string_count(char **pp) + * + * Pointer "pp" points to an array of string pointers, which is + * terminated by a NULL. Return the number of string pointers in + * this array. + */ + +static int +string_count(char **pp) + { - register int lines; + register int cnt = 0; - /* first, deallocate any previous buffer that may have been there */ - if (screenbuf != NULL) + if (pp != NULL) { - free(screenbuf); + while (*pp++ != NULL) + { + cnt++; + } } + return(cnt); +} +void +display_clear() + +{ + dprintf("display_clear\n"); + screen_clear(); + memzero(screenbuf, bufsize); + memzero(colorbuf, bufsize); + curr_x = curr_y = 0; +} + +/* + * void display_move(int x, int y) + * + * Efficiently move the cursor to x, y. This assumes the cursor is + * currently located at curr_x, curr_y, and will only use cursor + * addressing when it is less expensive than overstriking what's + * already on the screen. + */ + +void +display_move(int x, int y) + +{ + char buff[128]; + char *p; + char *bufp; + char *colorp; + int cnt = 0; + int color = curr_color; + + dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y); + + /* are we in a position to do this without cursor addressing? */ + if (curr_y < y || (curr_y == y && curr_x <= x)) + { + /* start buffering up what it would take to move there by rewriting + what's on the screen */ + cnt = CURSOR_COST; + p = buff; + + /* one newline for every line */ + while (cnt > 0 && curr_y < y) + { +#ifdef ENABLE_COLOR + if (color != 0) + { + p = strcpyend(p, color_setstr(0)); + color = 0; + cnt -= 5; + } +#endif + *p++ = '\n'; + curr_y++; + curr_x = 0; + cnt--; + } + + /* write whats in the screenbuf */ + bufp = &screenbuf[lineindex(curr_y) + curr_x]; + colorp = &colorbuf[lineindex(curr_y) + curr_x]; + while (cnt > 0 && curr_x < x) + { +#ifdef ENABLE_COLOR + if (color != *colorp) + { + color = *colorp; + p = strcpyend(p, color_setstr(color)); + cnt -= 5; + } +#endif + if ((*p = *bufp) == '\0') + { + /* somwhere on screen we haven't been before */ + *p = *bufp = ' '; + } + p++; + bufp++; + colorp++; + curr_x++; + cnt--; + } + } + + /* move the cursor */ + if (cnt > 0) + { + /* screen rewrite is cheaper */ + *p = '\0'; + fputs(buff, stdout); + curr_color = color; + } + else + { + screen_move(x, y); + } + + /* update our position */ + curr_x = x; + curr_y = y; +} + +/* + * display_write(int x, int y, int newcolor, int eol, char *new) + * + * Optimized write to the display. This writes characters to the + * screen in a way that optimizes the number of characters actually + * sent, by comparing what is being written to what is already on + * the screen (according to screenbuf and colorbuf). The string to + * write is "new", the first character of "new" should appear at + * screen position x, y. If x is -1 then "new" begins wherever the + * cursor is currently positioned. The string is written with color + * "newcolor". If "eol" is true then the remainder of the line is + * cleared. It is expected that "new" will have no newlines and no + * escape sequences. + */ + +void +display_write(int x, int y, int newcolor, int eol, char *new) + +{ + char *bufp; + char *colorp; + int ch; + int diff; + + dprintf("display_write(%d, %d, %d, %d, \"%s\")\n", + x, y, newcolor, eol, new); + + /* dumb terminal handling here */ + if (!smart_terminal) + { + if (x != -1) + { + /* make sure we are on the right line */ + while (curr_y < y) + { + putchar('\n'); + curr_y++; + curr_x = 0; + } + + /* make sure we are on the right column */ + while (curr_x < x) + { + putchar(' '); + curr_x++; + } + } + + /* write */ + fputs(new, stdout); + curr_x += strlen(new); + + return; + } + + /* adjust for "here" */ + if (x == -1) + { + x = virt_x; + y = virt_y; + } + else + { + virt_x = x; + virt_y = y; + } + + /* a pointer to where we start */ + bufp = &screenbuf[lineindex(y) + x]; + colorp = &colorbuf[lineindex(y) + x]; + + /* main loop */ + while ((ch = *new++) != '\0') + { + /* if either character or color are different, an update is needed */ + /* but only when the screen is wide enough */ + if (x < display_width && (ch != *bufp || newcolor != *colorp)) + { + /* check cursor */ + if (y != curr_y || x != curr_x) + { + /* have to move the cursor */ + display_move(x, y); + } + + /* write character */ +#ifdef ENABLE_COLOR + if (curr_color != newcolor) + { + fputs(color_setstr(newcolor), stdout); + curr_color = newcolor; + } +#endif + putchar(ch); + *bufp = ch; + *colorp = curr_color; + curr_x++; + } + + /* move */ + x++; + virt_x++; + bufp++; + colorp++; + } + + /* eol handling */ + if (eol && *bufp != '\0') + { + dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp); + /* make sure we are color 0 */ +#ifdef ENABLE_COLOR + if (curr_color != 0) + { + fputs(color_setstr(0), stdout); + curr_color = 0; + } +#endif + + /* make sure we are at the end */ + if (x != curr_x || y != curr_y) + { + screen_move(x, y); + curr_x = x; + curr_y = y; + } + + /* clear to end */ + screen_cleareol(strlen(bufp)); + + /* clear out whats left of this line's buffer */ + diff = display_width - x; + if (diff > 0) + { + memzero(bufp, diff); + memzero(colorp, diff); + } + } +} + +void +display_fmt(int x, int y, int newcolor, int eol, char *fmt, ...) + +{ + va_list argp; + + va_start(argp, fmt); + + vsnprintf(scratchbuf, MAX_COLS, fmt, argp); + display_write(x, y, newcolor, eol, scratchbuf); +} + +void +display_cte() + +{ + int len; + int y; + char *p; + int need_clear = 0; + + /* is there anything out there that needs to be cleared? */ + p = &screenbuf[lineindex(virt_y) + virt_x]; + if (*p != '\0') + { + need_clear = 1; + } + else + { + /* this line is clear, what about the rest? */ + y = virt_y; + while (++y < screen_length) + { + if (screenbuf[lineindex(y)] != '\0') + { + need_clear = 1; + break; + } + } + } + + if (need_clear) + { + dprintf("display_cte: clearing\n"); + + /* we will need this later */ + len = lineindex(virt_y) + virt_x; + + /* move to x and y, then clear to end */ + display_move(virt_x, virt_y); + if (!screen_cte()) + { + /* screen has no clear to end, so do it by hand */ + p = &screenbuf[len]; + len = strlen(p); + if (len > 0) + { + screen_cleareol(len); + } + while (++virt_y < screen_length) + { + display_move(0, virt_y); + p = &screenbuf[lineindex(virt_y)]; + len = strlen(p); + if (len > 0) + { + screen_cleareol(len); + } + } + } + + /* clear the screenbuf */ + memzero(&screenbuf[len], bufsize - len); + memzero(&colorbuf[len], bufsize - len); + } +} + +static void +summary_format(int x, int y, int *numbers, char **names, int *cidx) + +{ + register int num; + register char *thisname; + register char *lastname = NULL; + register int color; + + /* format each number followed by its string */ + while ((thisname = *names++) != NULL) + { + /* get the number to format */ + num = *numbers++; + color = 0; + + /* display only non-zero numbers */ + if (num != 0) + { + /* write the previous name */ + if (lastname != NULL) + { + display_write(-1, -1, 0, 0, lastname); + } + +#ifdef ENABLE_COLOR + if (cidx != NULL) + { + /* choose a color */ + color = color_test(*cidx++, num); + } +#endif + + /* write this number if positive */ + if (num > 0) + { + display_write(x, y, color, 0, itoa(num)); + } + + /* defer writing this name */ + lastname = thisname; + + /* next iteration will not start at x, y */ + x = y = -1; + } + } + + /* if the last string has a separator on the end, it has to be + written with care */ + if (lastname != NULL) + { + if ((num = strlen(lastname)) > 1 && + lastname[num-2] == ',' && lastname[num-1] == ' ') + { + display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname); + } + else + { + display_write(-1, -1, 0, 1, lastname); + } + } +} + +static void +summary_format_memory(int x, int y, long *numbers, char **names, int *cidx) + +{ + register long num; + register int color; + register char *thisname; + register char *lastname = NULL; + + /* format each number followed by its string */ + while ((thisname = *names++) != NULL) + { + /* get the number to format */ + num = *numbers++; + color = 0; + + /* display only non-zero numbers */ + if (num != 0) + { + /* write the previous name */ + if (lastname != NULL) + { + display_write(-1, -1, 0, 0, lastname); + } + + /* defer writing this name */ + lastname = thisname; + +#ifdef ENABLE_COLOR + /* choose a color */ + color = color_test(*cidx++, num); +#endif + + /* is this number in kilobytes? */ + if (thisname[0] == 'K') + { + display_write(x, y, color, 0, format_k(num)); + lastname++; + } + else + { + display_write(x, y, color, 0, itoa((int)num)); + } + + /* next iteration will not start at x, y */ + x = y = -1; + } + } + + /* if the last string has a separator on the end, it has to be + written with care */ + if (lastname != NULL) + { + if ((num = strlen(lastname)) > 1 && + lastname[num-2] == ',' && lastname[num-1] == ' ') + { + display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname); + } + else + { + display_write(-1, -1, 0, 1, lastname); + } + } +} + +/* + * int display_resize() + * + * Reallocate buffer space needed by the display package to accomodate + * a new screen size. Must be called whenever the screen's size has + * changed. Returns the number of lines available for displaying + * processes or -1 if there was a problem allocating space. + */ + +int +display_resize() + +{ + register int top_lines; + register int newsize; + /* calculate the current dimensions */ /* if operating in "dumb" mode, we only need one line */ - lines = smart_terminal ? screen_length - Header_lines : 1; + top_lines = smart_terminal ? screen_length : 1; + if (top_lines < 0) + top_lines = 0; - if (lines < 0) - lines = 0; /* we don't want more than MAX_COLS columns, since the machine-dependent modules make static allocations based on MAX_COLS and we don't want to run off the end of their buffers */ @@ -138,58 +697,137 @@ display_width = MAX_COLS - 1; } - /* now, allocate space for the screen buffer */ - screenbuf = (char *)malloc(lines * display_width); - if (screenbuf == (char *)NULL) + /* see how much space we need */ + newsize = top_lines * (MAX_COLS + 1); + + /* reallocate only if we need more than we already have */ + if (newsize > bufsize) { - /* oops! */ - return(-1); + /* deallocate any previous buffer that may have been there */ + if (screenbuf != NULL) + { + free(screenbuf); + } + if (colorbuf != NULL) + { + free(colorbuf); + } + + /* allocate space for the screen and color buffers */ + bufsize = newsize; + screenbuf = (char *)calloc(bufsize, sizeof(char)); + colorbuf = (char *)calloc(bufsize, sizeof(char)); + if (screenbuf == NULL || colorbuf == NULL) + { + /* oops! */ + return(-1); + } } + else + { + /* just clear them out */ + memzero(screenbuf, bufsize); + memzero(colorbuf, bufsize); + } + /* adjust total lines on screen to lines available for procs */ + top_lines -= y_procs; + /* return number of lines available */ /* for dumb terminals, pretend like we can show any amount */ - return(smart_terminal ? lines : Largest); + return(smart_terminal ? top_lines : Largest); } -int display_init(statics) +int +display_lines() -struct statics *statics; +{ + return(smart_terminal ? screen_length : Largest); +} +int +display_columns() + { - register int lines; + return(display_width); +} + +/* + * int display_init(struct statics *statics) + * + * Initialize the display system based on information in the statics + * structure. Returns the number of lines available for displaying + * processes or -1 if there was an error. + */ + +int +display_init(struct statics *statics) + +{ + register int top_lines; register char **pp; + register char *p; register int *ip; register int i; + /* certain things may influence the screen layout, + so look at those first */ + + /* a kernel line shifts parts of the display down */ + kernel_names = statics->kernel_names; + if ((num_kernel = string_count(kernel_names)) > 0) + { + /* adjust screen placements */ + y_mem++; + y_swap++; + y_message++; + y_header++; + y_idlecursor++; + y_procs++; + } + + /* a swap line shifts parts of the display down one */ + swap_names = statics->swap_names; + if ((num_swap = string_count(swap_names)) > 0) + { + /* adjust screen placements */ + y_message++; + y_header++; + y_idlecursor++; + y_procs++; + } + lswap = (int *)malloc(num_swap * sizeof(int)); + /* call resize to do the dirty work */ - lines = display_resize(); + top_lines = display_resize(); num_cpus = statics->ncpus; cpustates_column = 5; /* CPU: */ if (num_cpus != 1) - cpustates_column += 2; /* CPU 0: */ + cpustates_column += 2; /* CPU 0: */ for (i = num_cpus; i > 9; i /= 10) cpustates_column++; + y_kernel += num_cpus - 1; + y_mem += num_cpus - 1; + y_swap += num_cpus - 1; + y_header += num_cpus - 1; + y_idlecursor += num_cpus - 1; + y_message += num_cpus - 1; + y_procs += num_cpus - 1; /* only do the rest if we need to */ - if (lines > -1) + if (top_lines > -1) { /* save pointers and allocate space for names */ procstate_names = statics->procstate_names; num_procstates = string_count(procstate_names); - lprocstates = (int *)malloc(num_procstates * sizeof(int)); + lprocstates = (int *)calloc(num_procstates, sizeof(int)); cpustate_names = statics->cpustate_names; - - swap_names = statics->swap_names; - num_swap = string_count(swap_names); - lswap = (int *)malloc(num_swap * sizeof(int)); num_cpustates = string_count(cpustate_names); - lcpustates = (int *)malloc(num_cpustates * sizeof(int) * num_cpus); - cpustate_columns = (int *)malloc(num_cpustates * sizeof(int)); - + lcpustates = (int *)calloc(num_cpustates, sizeof(int) * num_cpus); + cpustate_columns = (int *)calloc(num_cpustates, sizeof(int)); memory_names = statics->memory_names; num_memory = string_count(memory_names); - lmemory = (int *)malloc(num_memory * sizeof(int)); /* calculate starting columns where needed */ cpustate_total_length = 0; @@ -205,43 +843,109 @@ } } - /* return number of lines available */ - return(lines); +#ifdef ENABLE_COLOR + /* set up color tags for loadavg */ + load_cidx[0] = color_tag("1min"); + load_cidx[1] = color_tag("5min"); + load_cidx[2] = color_tag("15min"); + + /* find header color */ + header_cidx = color_tag("header"); + + /* color tags for cpu states */ + cpustate_cidx = (int *)malloc(num_cpustates * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "cpu."); + while (i < num_cpustates) + { + strcpy(p, cpustate_names[i]); + cpustate_cidx[i++] = color_tag(scratchbuf); + } + + /* color tags for kernel */ + if (num_kernel > 0) + { + kernel_cidx = (int *)malloc(num_kernel * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "kernel."); + while (i < num_kernel) + { + strcpy(p, homogenize(kernel_names[i]+1)); + kernel_cidx[i++] = color_tag(scratchbuf); + } + } + + /* color tags for memory */ + memory_cidx = (int *)malloc(num_memory * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "memory."); + while (i < num_memory) + { + strcpy(p, homogenize(memory_names[i]+1)); + memory_cidx[i++] = color_tag(scratchbuf); + } + + /* color tags for swap */ + if (num_swap > 0) + { + swap_cidx = (int *)malloc(num_swap * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "swap."); + while (i < num_swap) + { + strcpy(p, homogenize(swap_names[i]+1)); + swap_cidx[i++] = color_tag(scratchbuf); + } + } +#endif + + /* return number of lines available (or error) */ + return(top_lines); } -i_loadave(mpid, avenrun) +static void +pr_loadavg(double avg, int i) -int mpid; -double *avenrun; +{ + int color = 0; +#ifdef ENABLE_COLOR + color = color_test(load_cidx[i], (int)(avg * 100)); +#endif + display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0, + avg < 10.0 ? " %5.2f" : " %5.1f", avg); + display_write(-1, -1, 0, 0, (i < 2 ? "," : ";")); +} + +void +i_loadave(int mpid, double *avenrun) + { register int i; - /* i_loadave also clears the screen, since it is first */ - clear(); - /* mpid == -1 implies this system doesn't have an _mpid */ if (mpid != -1) { - printf("last pid: %5d; ", mpid); + display_fmt(0, 0, 0, 0, + "last pid: %5d; load avg:", mpid); + x_loadave = X_LOADAVE; } - - printf("load averages"); - + else + { + display_write(0, 0, 0, 0, "load averages:"); + x_loadave = X_LOADAVE - X_LASTPIDWIDTH; + } for (i = 0; i < 3; i++) { - printf("%c %5.2f", - i == 0 ? ':' : ',', - avenrun[i]); + pr_loadavg(avenrun[i], i); } + lmpid = mpid; } -u_loadave(mpid, avenrun) +void +u_loadave(int mpid, double *avenrun) -int mpid; -double *avenrun; - { register int i; @@ -250,136 +954,151 @@ /* change screen only when value has really changed */ if (mpid != lmpid) { - Move_to(x_lastpid, y_lastpid); - printf("%5d", mpid); + display_fmt(x_lastpid, y_lastpid, 0, 0, + "%5d", mpid); lmpid = mpid; } - - /* i remembers x coordinate to move to */ - i = x_loadave; } - else - { - i = x_loadave_nompid; - } - /* move into position for load averages */ - Move_to(i, y_loadave); - /* display new load averages */ - /* we should optimize this and only display changes */ for (i = 0; i < 3; i++) { - printf("%s%5.2f", - i == 0 ? "" : ", ", - avenrun[i]); + pr_loadavg(avenrun[i], i); } } -i_timeofday(tod) +static char minibar_buffer[64]; +#define MINIBAR_WIDTH 20 -time_t *tod; +void +i_minibar(int (*formatter)(char *, int)) +{ + (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); + display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); +} + +void +u_minibar(int (*formatter)(char *, int)) { - /* - * Display the current time. - * "ctime" always returns a string that looks like this: - * - * Sun Sep 16 01:03:52 1973 - * 012345678901234567890123 - * 1 2 - * - * We want indices 11 thru 18 (length 8). - */ + (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); - if (smart_terminal) + display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); +} + +void +i_uptime(time_t *bt, time_t *tod) + +{ + int uptime_days; + time_t uptime; + char t[10]; + struct tm *tm; + + if (*bt != -1) { - Move_to(screen_width - 8, 0); + uptime = *tod - *bt; + uptime += 30; + uptime_days = uptime / 86400; + + tm = gmtime(&uptime); + strftime(t, sizeof(t), "%H:%M:%S", tm); + + /* + * Display the uptime. + */ + + x_uptime = (smart_terminal ? screen_width : 79) - 29; + display_fmt(x_uptime, y_uptime, 0, 0, + " up %d+%s", + uptime_days, t); } - else +} + +void +u_uptime(time_t *bt, time_t *tod) + +{ + i_uptime(bt, tod); +} + + +void +i_timeofday(time_t *tod) + +{ + int x; + char t[10]; + struct tm *tm; + + /* where on the screen do we start? */ + x = (smart_terminal ? screen_width : 79) - 8; + + /* but don't bump in to uptime */ + if (x < x_uptime + 19) { - fputs(" ", stdout); + x = x_uptime + 19; } -#ifdef DEBUG - { - char *foo; - foo = ctime(tod); - fputs(foo, stdout); - } -#endif - printf("%-8.8s\n", &(ctime(tod)[11])); - lastline = 1; + + /* display it */ + tm = localtime(tod); + strftime(t, sizeof(t), "%H:%M:%S", tm); + display_write(x, 0, 0, 1, t); } static int ltotal = 0; -static char procstates_buffer[MAX_COLS]; +static int lthreads = 0; /* * *_procstates(total, brkdn, names) - print the process summary line - * - * Assumptions: cursor is at the beginning of the line on entry - * lastline is valid */ -i_procstates(total, brkdn) -int total; -int *brkdn; +void +i_procstates(int total, int *brkdn, int threads) { - register int i; - /* write current number of processes and remember the value */ - printf("%d processes:", total); + display_fmt(0, y_procstate, 0, 0, + "%d %s: ", total, threads ? "threads" : "processes"); ltotal = total; - /* put out enough spaces to get to column 15 */ - i = digits(total); - while (i++ < 4) + /* remember where the summary starts */ + x_procstate = virt_x; + + if (total > 0) { - putchar(' '); + /* format and print the process state summary */ + summary_format(-1, -1, brkdn, procstate_names, NULL); + + /* save the numbers for next time */ + memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); + lthreads = threads; } - - /* format and print the process state summary */ - summary_format(procstates_buffer, brkdn, procstate_names); - fputs(procstates_buffer, stdout); - - /* save the numbers for next time */ - memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); } -u_procstates(total, brkdn) +void +u_procstates(int total, int *brkdn, int threads) -int total; -int *brkdn; - { - static char new[MAX_COLS]; - register int i; + /* if threads state has changed, do a full update */ + if (lthreads != threads) + { + i_procstates(total, brkdn, threads); + return; + } /* update number of processes only if it has changed */ if (ltotal != total) { - /* move and overwrite */ -#if (x_procstate == 0) - Move_to(x_procstate, y_procstate); -#else - /* cursor is already there...no motion needed */ - /* assert(lastline == 1); */ -#endif - printf("%d", total); + display_fmt(0, y_procstate, 0, 0, + "%d", total); /* if number of digits differs, rewrite the label */ if (digits(total) != digits(ltotal)) { - fputs(" processes:", stdout); - /* put out enough spaces to get to column 15 */ - i = digits(total); - while (i++ < 4) - { - putchar(' '); - } - /* cursor may end up right where we want it!!! */ + display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes"); + x_procstate = virt_x; } /* save new total */ @@ -387,11 +1106,10 @@ } /* see if any of the state numbers has changed */ - if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) + if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) { /* format and update the line */ - summary_format(new, brkdn, procstate_names); - line_update(procstates_buffer, new, x_brkdn, y_brkdn); + summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL); memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); } } @@ -399,13 +1117,12 @@ #ifdef no_more /* * *_cpustates(states, names) - print the cpu state percentages - * - * Assumptions: cursor is on the PREVIOUS line */ /* cpustates_tag() calculates the correct tag to use to label the line */ -char *cpustates_tag() +char * +cpustates_tag() { register char *use; @@ -424,32 +1141,39 @@ use = long_tag; } - /* set cpustates_column accordingly then return result */ - cpustates_column = strlen(use); + /* set x_cpustates accordingly then return result */ + x_cpustates = strlen(use); return(use); } #endif -i_cpustates(states) +void +i_cpustates(int *states) -register int *states; - { - register int i = 0; - register int value; - register char **names; - register char *thisname; + int value; + char **names; + char *thisname; + int *colp; + int color = 0; int cpu; + char scpu[10]; +#ifdef ENABLE_COLOR + int *cidx = cpustate_cidx; +#endif for (cpu = 0; cpu < num_cpus; cpu++) { + /* initialize */ names = cpustate_names; + colp = cpustate_columns; - /* print tag and bump lastline */ + /* print tag */ if (num_cpus == 1) - printf("\nCPU: "); + strcpy(scpu, "CPU: "); else - printf("\nCPU %d: ", cpu); - lastline++; + sprintf(scpu, "CPU %d: ", cpu); + display_write(0, y_cpustates + cpu, 0, 0, scpu); + x_cpustates = strlen(scpu) + 1; /* now walk thru the names and print the line */ while ((thisname = *names++) != NULL) @@ -457,14 +1181,26 @@ if (*thisname != '\0') { /* retrieve the value and remember it */ - value = *states++; + value = *states; +#ifdef ENABLE_COLOR + /* determine color number to use */ + color = color_test(*cidx++, value/10); +#endif + /* if percentage is >= 1000, print it as 100% */ - printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), - (i++ % num_cpustates) == 0 ? "" : ", ", - ((float)value)/10., - thisname); + display_fmt( + x_cpustates + *colp, y_cpustates + cpu, + color, 0, + (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"), + ((float)value)/10., + thisname, + *names != NULL ? ", " : ""); + } + /* increment */ + colp++; + states++; } } @@ -472,26 +1208,26 @@ memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus); } -u_cpustates(states) +void +u_cpustates(int *states) -register int *states; - { - register int value; - register char **names; - register char *thisname; - register int *lp; - register int *colp; + int value; + char **names; + char *thisname; + int *lp; + int *colp; + int color = 0; int cpu; +#ifdef ENABLE_COLOR + int *cidx = cpustate_cidx; +#endif for (cpu = 0; cpu < num_cpus; cpu++) { + lp = lcpustates; + colp = cpustate_columns; names = cpustate_names; - Move_to(cpustates_column, y_cpustates + cpu); - lastline = y_cpustates + cpu; - lp = lcpustates + (cpu * num_cpustates); - colp = cpustate_columns; - /* we could be much more optimal about this */ while ((thisname = *names++) != NULL) { @@ -500,20 +1236,26 @@ /* did the value change since last time? */ if (*lp != *states) { - /* yes, move and change */ - Move_to(cpustates_column + *colp, y_cpustates + cpu); - lastline = y_cpustates + cpu; - + /* yes, change it */ /* retrieve value and remember it */ value = *states; +#ifdef ENABLE_COLOR + /* determine color number to use */ + color = color_test(*cidx, value/10); +#endif + /* if percentage is >= 1000, print it as 100% */ - printf((value >= 1000 ? "%4.0f" : "%4.1f"), - ((double)value)/10.); + display_fmt(x_cpustates + *colp, y_cpustates + cpu, color, 0, + (value >= 1000 ? "%4.0f" : "%4.1f"), + ((double)value)/10.); /* remember it for next time */ *lp = value; } +#ifdef ENABLE_COLOR + cidx++; +#endif } /* increment and move on */ @@ -524,6 +1266,7 @@ } } +void z_cpustates() { @@ -532,22 +1275,25 @@ register char *thisname; register int *lp; int cpu; + char scpu[10]; for (cpu = 0; cpu < num_cpus; cpu++) { names = cpustate_names; - /* show tag and bump lastline */ + /* print tag */ if (num_cpus == 1) - printf("\nCPU: "); + strcpy(scpu, "CPU: "); else - printf("\nCPU %d: ", cpu); - lastline++; + sprintf(scpu, "CPU %d: ", cpu); + display_write(0, y_cpustates + cpu, 0, 0, scpu); + x_cpustates = strlen(scpu) + 1; while ((thisname = *names++) != NULL) { if (*thisname != '\0') { - printf("%s %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname); + display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ", + thisname); } } } @@ -562,71 +1308,90 @@ } /* - * *_memory(stats) - print "Memory: " followed by the memory summary string + * *_kernel(stats) - print "Kernel: " followed by the kernel summary string * - * Assumptions: cursor is on "lastline" - * for i_memory ONLY: cursor is on the previous line + * Assumptions: cursor is on "lastline", the previous line */ -char memory_buffer[MAX_COLS]; +void +i_kernel(int *stats) -i_memory(stats) +{ + if (num_kernel > 0) + { + display_write(0, y_kernel, 0, 0, "Kernel: "); -int *stats; + /* format and print the kernel summary */ + summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); + } +} +void +u_kernel(int *stats) + { - fputs("\nMem: ", stdout); - lastline++; + if (num_kernel > 0) + { + /* format the new line */ + summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); + } +} +/* + * *_memory(stats) - print "Memory: " followed by the memory summary string + * + * Assumptions: cursor is on "lastline", the previous line + */ + +void +i_memory(long *stats) + +{ + display_write(0, y_mem, 0, 0, "Mem: "); + /* format and print the memory summary */ - summary_format(memory_buffer, stats, memory_names); - fputs(memory_buffer, stdout); + summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); } -u_memory(stats) +void +u_memory(long *stats) -int *stats; - { - static char new[MAX_COLS]; - /* format the new line */ - summary_format(new, stats, memory_names); - line_update(memory_buffer, new, x_mem, y_mem); + summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); } /* * *_swap(stats) - print "Swap: " followed by the swap summary string * - * Assumptions: cursor is on "lastline" - * for i_swap ONLY: cursor is on the previous line + * Assumptions: cursor is on "lastline", the previous line + * + * These functions only print something when num_swap > 0 */ -char swap_buffer[MAX_COLS]; +void +i_swap(long *stats) -i_swap(stats) - -int *stats; - { - fputs("\nSwap: ", stdout); - lastline++; + if (num_swap > 0) + { + /* print the tag */ + display_write(0, y_swap, 0, 0, "Swap: "); - /* format and print the swap summary */ - summary_format(swap_buffer, stats, swap_names); - fputs(swap_buffer, stdout); + /* format and print the swap summary */ + summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); + } } -u_swap(stats) +void +u_swap(long *stats) -int *stats; - { - static char new[MAX_COLS]; - - /* format the new line */ - summary_format(new, stats, swap_names); - line_update(swap_buffer, new, x_swap, y_swap); + if (num_swap > 0) + { + /* format the new line */ + summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); + } } /* @@ -640,39 +1405,89 @@ /* * i_message is funny because it gets its message asynchronously (with - * respect to screen updates). + * respect to screen updates). Messages are taken out of the + * circular message_buf and displayed one at a time. */ -static char next_msg[MAX_COLS + 5]; -static int msglen = 0; -/* Invariant: msglen is always the length of the message currently displayed - on the screen (even when next_msg doesn't contain that message). */ +void +i_message(struct timeval *now) -i_message() +{ + struct timeval my_now; + int i = 0; -{ - while (lastline < y_message) + dprintf("i_message(%08x)\n", now); + + /* if now is NULL we have to get it ourselves */ + if (now == NULL) { - fputc('\n', stdout); - lastline++; + time_get(&my_now); + now = &my_now; } - if (next_msg[0] != '\0') + + /* now that we have been called, messages no longer need to be held */ + message_hold = 0; + + dprintf("i_message: now %d, message_time %d\n", + now->tv_sec, message_time.tv_sec); + + if (smart_terminal) { - standout(next_msg); - msglen = strlen(next_msg); - next_msg[0] = '\0'; + /* is it time to change the message? */ + if (timercmp(now, &message_time, > )) + { + /* yes, free the current message */ + dprintf("i_message: timer expired\n"); + if (message_current != NULL) + { + free(message_current); + message_current = NULL; + } + + /* is there a new message to be displayed? */ + if (message_first != message_last) + { + /* move index to next message */ + if (++message_first == MAX_MESSAGES) message_first = 0; + + /* make the next message the current one */ + message_current = message_buf[message_first]; + + /* show it */ + dprintf("i_message: showing \"%s\"\n", message_current); + display_move(0, y_message); + screen_standout(message_current); + i = strlen(message_current); + + /* set the expiration timer */ + message_time = *now; + message_time.tv_sec += MESSAGE_DISPLAY_TIME; + + /* clear the rest of the line */ + screen_cleareol(message_length - i); + putchar('\r'); + message_length = i; + } + else + { + /* just clear what was there before, if anything */ + if (message_length > 0) + { + display_move(0, y_message); + screen_cleareol(message_length); + putchar('\r'); + message_length = 0; + } + } + } } - else if (msglen > 0) - { - (void) clear_eol(msglen); - msglen = 0; - } } -u_message() +void +u_message(struct timeval *now) { - i_message(); + i_message(now); } static int header_length; @@ -710,44 +1525,39 @@ * Assumptions: cursor is on the previous line and lastline is consistent */ -i_header(text) +void +i_header(char *text) -char *text; - { char *s; + int header_color = 0; +#ifdef ENABLE_COLOR + header_color = color_test(header_cidx, 0); +#endif s = trim_header(text); if (s != NULL) text = s; - - if (header_status == ON) + header_length = strlen(text); + if (header_status) { - putchar('\n'); - fputs(text, stdout); - lastline++; + display_write(x_header, y_header, header_color, 1, text); } - else if (header_status == ERASE) - { - header_status = OFF; - } free(s); } /*ARGSUSED*/ -u_header(text) +void +u_header(char *text) -char *text; /* ignored */ - { + int header_color = 0; - if (header_status == ERASE) - { - putchar('\n'); - lastline++; - clear_eol(header_length); - header_status = OFF; - } +#ifdef ENABLE_COLOR + header_color = color_test(header_cidx, 0); +#endif + display_write(x_header, y_header, header_color, 1, + header_status ? text : ""); } /* @@ -756,135 +1566,53 @@ * Assumptions: lastline is consistent */ -i_process(line, thisline) +void +i_process(int line, char *thisline) -int line; -char *thisline; - { - register char *p; - register char *base; - - /* make sure we are on the correct line */ - while (lastline < y_procs + line) - { - putchar('\n'); - lastline++; - } - /* truncate the line to conform to our current screen width */ thisline[display_width] = '\0'; /* write the line out */ - fputs(thisline, stdout); + display_write(0, y_procs + line, 0, 1, thisline); +} - /* copy it in to our buffer */ - base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; - p = strecpy(base, thisline); +void +u_process(int line, char *new_line) - /* zero fill the rest of it */ - memzero(p, display_width - (p - base)); +{ + i_process(line, new_line); } -u_process(line, newline) +void +i_endscreen() -int line; -char *newline; - { - register char *optr; - register int screen_line = line + Header_lines; - register char *bufferline; - - /* remember a pointer to the current line in the screen buffer */ - bufferline = &screenbuf[lineindex(line)]; - - /* truncate the line to conform to our current screen width */ - newline[display_width] = '\0'; - - /* is line higher than we went on the last display? */ - if (line >= last_hi) + if (smart_terminal) { - /* yes, just ignore screenbuf and write it out directly */ - /* get positioned on the correct line */ - if (screen_line - lastline == 1) - { - putchar('\n'); - lastline++; - } - else - { - Move_to(0, screen_line); - lastline = screen_line; - } - - /* now write the line */ - fputs(newline, stdout); - - /* copy it in to the buffer */ - optr = strecpy(bufferline, newline); - - /* zero fill the rest of it */ - memzero(optr, display_width - (optr - bufferline)); + /* move the cursor to a pleasant place */ + display_move(x_idlecursor, y_idlecursor); } else { - line_update(bufferline, newline, 0, line + Header_lines); + /* separate this display from the next with some vertical room */ + fputs("\n\n", stdout); } + fflush(stdout); } -u_endscreen(hi) +void +u_endscreen() -register int hi; - { - register int screen_line = hi + Header_lines; - register int i; - if (smart_terminal) { - if (hi < last_hi) - { - /* need to blank the remainder of the screen */ - /* but only if there is any screen left below this line */ - if (lastline + 1 < screen_length) - { - /* efficiently move to the end of currently displayed info */ - if (screen_line - lastline < 5) - { - while (lastline < screen_line) - { - putchar('\n'); - lastline++; - } - } - else - { - Move_to(0, screen_line); - lastline = screen_line; - } + /* clear-to-end the display */ + display_cte(); - if (clear_to_end) - { - /* we can do this the easy way */ - putcap(clear_to_end); - } - else - { - /* use clear_eol on each line */ - i = hi; - while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) - { - putchar('\n'); - } - } - } - } - last_hi = hi; - /* move the cursor to a pleasant place */ - Move_to(x_idlecursor, y_idlecursor); - lastline = y_idlecursor; + display_move(x_idlecursor, y_idlecursor); + fflush(stdout); } else { @@ -893,82 +1621,244 @@ } } -display_header(t) +void +display_header(int t) -int t; +{ + header_status = t != 0; +} +void +message_mark() + { - if (t) - { - header_status = ON; - } - else if (header_status == ON) - { - header_status = ERASE; - } + message_barrier = Yes; } -/*VARARGS2*/ -new_message(type, msgfmt, a1, a2, a3) +void +message_expire() -int type; -char *msgfmt; -caddr_t a1, a2, a3; +{ + message_time.tv_sec = 0; + message_time.tv_usec = 0; +} +void +message_flush() + { - register int i; + message_first = message_last; + message_time.tv_sec = 0; + message_time.tv_usec = 0; +} +/* + * void new_message_v(char *msgfmt, va_list ap) + * + * Display a message in the message area. This function takes a va_list for + * the arguments. Safe to call before display_init. This function only + * queues a message for display, and allowed for multiple messages to be + * queued. The i_message function drains the queue and actually writes the + * messages on the display. + */ + + +void +new_message_v(char *msgfmt, va_list ap) + +{ + int i; + int empty; + char msg[MAX_COLS]; + + /* if message_barrier is active, remove all pending messages */ + if (message_barrier) + { + message_flush(); + message_barrier = No; + } + /* first, format the message */ - (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3); + (void) vsnprintf(msg, sizeof(msg), msgfmt, ap); - if (msglen > 0) + /* where in the buffer will it go? */ + i = message_last + 1; + if (i >= MAX_MESSAGES) i = 0; + + /* make sure the buffer is not full */ + if (i != message_first) { - /* message there already -- can we clear it? */ - if (!overstrike) + /* insert it in to message_buf */ + message_buf[i] = strdup(msg); + dprintf("new_message_v: new message inserted in slot %d\n", i); + + /* remember if the buffer is empty and set the index */ + empty = message_last == message_first; + message_last = i; + + /* is message_buf otherwise empty and have we started displaying? */ + if (empty && !message_hold) { - /* yes -- write it and clear to end */ - i = strlen(next_msg); - if ((type & MT_delayed) == 0) - { - type & MT_standout ? standout(next_msg) : - fputs(next_msg, stdout); - (void) clear_eol(msglen - i); - msglen = i; - next_msg[0] = '\0'; - } + /* we can display the message now */ + i_message(NULL); } } - else +} + +/* + * void new_message(int type, char *msgfmt, ...) + * + * Display a message in the message area. It is safe to call this function + * before display_init. Messages logged before the display is drawn will be + * held and displayed later. + */ + +void +new_message(char *msgfmt, ...) + +{ + va_list ap; + + va_start(ap, msgfmt); + new_message_v(msgfmt, ap); + va_end(ap); +} + +/* + * void message_error(char *msgfmt, ...) + * + * Put an error message in the message area. It is safe to call this function + * before display_init. Messages logged before the display is drawn will be + * held and displayed later. + */ + +void +message_error(char *msgfmt, ...) + +{ + va_list ap; + + va_start(ap, msgfmt); + new_message_v(msgfmt, ap); + fflush(stdout); + va_end(ap); +} + +/* + * void message_clear() + * + * Clear message area and flush all pending messages. + */ + +void +message_clear() + +{ + /* remove any existing message */ + if (message_current != NULL) { - if ((type & MT_delayed) == 0) - { - type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); - msglen = strlen(next_msg); - next_msg[0] = '\0'; - } + display_move(0, y_message); + screen_cleareol(message_length); + free(message_current); + message_current = 0; } + + /* flush all pending messages */ + message_flush(); } -clear_message() +/* + * void message_prompt_v(int so, char *msgfmt, va_list ap) + * + * Place a prompt in the message area. A prompt is different from a + * message as follows: it is displayed immediately, overwriting any + * message that may already be there, it may be highlighted in standout + * mode (if "so" is true), the cursor is left to rest at the end of the + * prompt. This call causes all pending messages to be flushed. + */ +void +message_prompt_v(int so, char *msgfmt, va_list ap) + { - if (clear_eol(msglen) == 1) + char msg[MAX_COLS]; + int i; + + /* clear out the message buffer */ + message_flush(); + + /* format the message */ + i = vsnprintf(msg, sizeof(msg), msgfmt, ap); + + /* this goes over any existing message */ + display_move(0, y_message); + + /* clear the entire line */ + screen_cleareol(message_length); + + /* show the prompt */ + if (so) { - putchar('\r'); + screen_standout(msg); } + else + { + fputs(msg, stdout); + } + + /* make it all visible */ + fflush(stdout); + + /* even though we dont keep a copy of the prompt, track its length */ + message_length = i < MAX_COLS ? i : MAX_COLS; } -readline(buffer, size, numeric) +/* + * void message_prompt(char *msgfmt, ...) + * + * Place a prompt in the message area (see message_prompt_v). + */ -char *buffer; -int size; -int numeric; +void +message_prompt(char *msgfmt, ...) { + va_list ap; + + va_start(ap, msgfmt); + message_prompt_v(Yes, msgfmt, ap); + va_end(ap); +} + +void +message_prompt_plain(char *msgfmt, ...) + +{ + va_list ap; + + va_start(ap, msgfmt); + message_prompt_v(No, msgfmt, ap); + va_end(ap); +} + +/* + * int readline(char *buffer, int size, int numeric) + * + * Read a line of input from the terminal. The line is placed in + * "buffer" not to exceed "size". If "numeric" is true then the input + * can only consist of digits. This routine handles all character + * editing while keeping the terminal in cbreak mode. If "numeric" + * is true then the number entered is returned. Otherwise the number + * of character read in to "buffer" is returned. + */ + +int +readline(char *buffer, int size, int numeric) + +{ register char *ptr = buffer; register char ch; register char cnt = 0; - register char maxcnt = 0; /* allow room for null terminator */ size -= 1; @@ -976,7 +1866,7 @@ /* read loop */ while ((fflush(stdout), read(0, ptr, 1) > 0)) { - /* newline means we are done */ + /* newline or return means we are done */ if ((ch = *ptr) == '\n' || ch == '\r') { break; @@ -985,17 +1875,39 @@ /* handle special editing characters */ if (ch == ch_kill) { - /* kill line -- account for overstriking */ - if (overstrike) - { - msglen += maxcnt; - } - /* return null string */ *buffer = '\0'; putchar('\r'); return(-1); } + else if (ch == ch_werase) + { + /* erase previous word */ + if (cnt <= 0) + { + /* none to erase! */ + putchar('\7'); + } + else + { + /* + * First: remove all spaces till the first-non-space + * Second: remove all non-spaces till the first-space + */ + while(cnt > 0 && ptr[-1] == ' ') + { + fputs("\b \b", stdout); + ptr--; + cnt--; + } + while(cnt > 0 && ptr[-1] != ' ') + { + fputs("\b \b", stdout); + ptr--; + cnt--; + } + } + } else if (ch == ch_erase) { /* erase previous character */ @@ -1012,8 +1924,8 @@ } } /* check for character validity and buffer overflow */ - else if (cnt == size || (numeric && !isdigit(ch)) || - !isprint(ch)) + else if (cnt == size || (numeric && !isdigit((int)ch)) || + !isprint((int)ch)) { /* not legal */ putchar('\7'); @@ -1024,273 +1936,80 @@ putchar(ch); ptr++; cnt++; - if (cnt > maxcnt) - { - maxcnt = cnt; - } } } /* all done -- null terminate the string */ *ptr = '\0'; - /* account for the extra characters in the message area */ - /* (if terminal overstrikes, remember the furthest they went) */ - msglen += overstrike ? maxcnt : cnt; + /* add response length to message_length */ + message_length += cnt; /* return either inputted number or string length */ putchar('\r'); return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); } -/* internal support routines */ +void +display_pagerstart() -static int string_count(pp) - -register char **pp; - { - register int cnt; - - cnt = 0; - while (*pp++ != NULL) - { - cnt++; - } - return(cnt); + display_clear(); } -static void summary_format(str, numbers, names) +void +display_pagerend() -char *str; -int *numbers; -register char **names; - { - register char *p; - register int num; - register char *thisname; - register int useM = No; + char ch; - /* format each number followed by its string */ - p = str; - while ((thisname = *names++) != NULL) - { - /* get the number to format */ - num = *numbers++; - - /* display only non-zero numbers */ - if (num > 0) - { - /* is this number in kilobytes? */ - if (thisname[0] == 'K') - { - /* yes: format it as a memory value */ - p = strecpy(p, format_k(num)); - - /* skip over the K, since it was included by format_k */ - p = strecpy(p, thisname+1); - } - else - { - p = strecpy(p, itoa(num)); - p = strecpy(p, thisname); - } - } - - /* ignore negative numbers, but display corresponding string */ - else if (num < 0) - { - p = strecpy(p, thisname); - } - } - - /* if the last two characters in the string are ", ", delete them */ - p -= 2; - if (p >= str && p[0] == ',' && p[1] == ' ') - { - *p = '\0'; - } + screen_standout("Hit any key to continue: "); + fflush(stdout); + (void) read(0, &ch, 1); } -static void line_update(old, new, start, line) +void +display_pager(char *fmt, ...) -register char *old; -register char *new; -int start; -int line; - { - register int ch; - register int diff; - register int newcol = start + 1; - register int lastcol = start; - char cursor_on_line = No; - char *current; + va_list ap; - /* compare the two strings and only rewrite what has changed */ - current = old; -#ifdef DEBUG - fprintf(debug, "line_update, starting at %d\n", start); - fputs(old, debug); - fputc('\n', debug); - fputs(new, debug); - fputs("\n-\n", debug); -#endif + int ch; + char readch; + char buffer[MAX_COLS]; + char *data; - /* start things off on the right foot */ - /* this is to make sure the invariants get set up right */ - if ((ch = *new++) != *old) + /* format into buffer */ + va_start(ap, fmt); + (void) vsnprintf(buffer, MAX_COLS, fmt, ap); + va_end(ap); + data = buffer; + + while ((ch = *data++) != '\0') { - if (line - lastline == 1 && start == 0) - { - putchar('\n'); - } - else - { - Move_to(start, line); - } - cursor_on_line = Yes; putchar(ch); - *old = ch; - lastcol = 1; - } - old++; - - /* - * main loop -- check each character. If the old and new aren't the - * same, then update the display. When the distance from the - * current cursor position to the new change is small enough, - * the characters that belong there are written to move the - * cursor over. - * - * Invariants: - * lastcol is the column where the cursor currently is sitting - * (always one beyond the end of the last mismatch). - */ - do /* yes, a do...while */ - { - if ((ch = *new++) != *old) + if (ch == '\n') { - /* new character is different from old */ - /* make sure the cursor is on top of this character */ - diff = newcol - lastcol; - if (diff > 0) + if (++curr_y >= screen_length - 1) { - /* some motion is required--figure out which is shorter */ - if (diff < 6 && cursor_on_line) + screen_standout("...More..."); + fflush(stdout); + (void) read(0, &readch, 1); + putchar('\r'); + switch(readch) { - /* overwrite old stuff--get it out of the old buffer */ - printf("%.*s", diff, ¤t[lastcol-start]); + case '\r': + case '\n': + curr_y--; + break; + + case 'q': + return; + + default: + curr_y = 0; } - else - { - /* use cursor addressing */ - Move_to(newcol, line); - cursor_on_line = Yes; - } - /* remember where the cursor is */ - lastcol = newcol + 1; } - else - { - /* already there, update position */ - lastcol++; - } - - /* write what we need to */ - if (ch == '\0') - { - /* at the end--terminate with a clear-to-end-of-line */ - (void) clear_eol(strlen(old)); - } - else - { - /* write the new character */ - putchar(ch); - } - /* put the new character in the screen buffer */ - *old = ch; } - - /* update working column and screen buffer pointer */ - newcol++; - old++; - - } while (ch != '\0'); - - /* zero out the rest of the line buffer -- MUST BE DONE! */ - diff = display_width - newcol; - if (diff > 0) - { - memzero(old, diff); } - - /* remember where the current line is */ - if (cursor_on_line) - { - lastline = line; - } } - -/* - * printable(str) - make the string pointed to by "str" into one that is - * printable (i.e.: all ascii), by converting all non-printable - * characters into '?'. Replacements are done in place and a pointer - * to the original buffer is returned. - */ - -char *printable(str) - -char *str; - -{ - register char *ptr; - register char ch; - - ptr = str; - while ((ch = *ptr) != '\0') - { - if (!isprint(ch)) - { - *ptr = '?'; - } - ptr++; - } - return(str); -} - -i_uptime(bt, tod) - -struct timeval* bt; -time_t *tod; - -{ - time_t uptime; - int days, hrs, mins, secs; - - if (bt->tv_sec != -1) { - uptime = *tod - bt->tv_sec; - uptime += 30; - days = uptime / 86400; - uptime %= 86400; - hrs = uptime / 3600; - uptime %= 3600; - mins = uptime / 60; - secs = uptime % 60; - - /* - * Display the uptime. - */ - - if (smart_terminal) - { - Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0); - } - else - { - fputs(" ", stdout); - } - printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs); - } -} Index: utils.h =================================================================== --- utils.h (revision 183246) +++ utils.h (working copy) @@ -1,24 +1,78 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Top users/processes display for Unix - * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University */ /* prototypes for functions found in utils.c */ -int atoiwi(); -char *itoa(); -char *itoa7(); -int digits(); -char *strecpy(); -char **argparse(); -long percentages(); -char *errmsg(); -char *format_time(); -char *format_k(); -char *format_k2(); +#ifndef _UTILS_H +#define _UTILS_H + +int atoiwi(char *); +char *itoa(int); +char *itoa_w(int, int); +char *itoa7(int); +int digits(int); +char *printable(char *); +char *strcpyend(char *, char *); +char *homogenize(char *); +int string_index(char *, char **); +char **argparse(char *, int *); +long percentages(int, int *, long *, long *, long *); +char *errmsg(int); +char *format_percent(double); +char *format_time(long); +char *format_k(long); +char *string_list(char **); +void time_get(struct timeval *); +void time_mark(struct timeval *); +unsigned int time_elapsed(); +unsigned int diff_per_second(unsigned int, unsigned int); +void debug_set(int); +#ifdef DEBUG +#define dprintf xdprintf +void xdprintf(char *fmt, ...); +#else +#ifdef HAVE_C99_VARIADIC_MACROS +#define dprintf(...) +#else +#ifdef HAVE_GNU_VARIADIC_MACROS +#define dprintf(x...) +#else +#define dprintf if (0) +#endif +#endif +#endif + +#endif Index: top.h =================================================================== --- top.h (revision 183246) +++ top.h (working copy) @@ -1,29 +1,56 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * $FreeBSD$ */ + /* * Top - a top users display for Berkeley Unix * * General (global) definitions */ -/* Current major version number */ -#define VERSION 3 +#ifndef _TOP_H_ +#define _TOP_H_ -/* Number of lines of header information on the standard screen */ -extern int Header_lines; /* 7 */ +#include <sys/time.h> /* Maximum number of columns allowed for display */ -#define MAX_COLS 128 +#define MAX_COLS 255 /* Log base 2 of 1024 is 10 (2^10 == 1024) */ #define LOG1024 10 -char *itoa(); -char *itoa7(); - -char *version_string(); - /* Special atoi routine returns either a non-negative number or one of: */ #define Infinity -1 #define Invalid -2 @@ -37,13 +64,25 @@ #define NUM_AVERAGES 3 -enum displaymodes { DISP_CPU = 0, DISP_IO, DISP_MAX }; +struct ext_decl { + int (*f_minibar)(char *, int); + int (*f_display)(char *, int); +}; /* - * Format modifiers + * "Table_size" defines the size of the hash tables used to map uid to + * username. Things will work best if the number is a prime number. + * We use a number that should be suitable for most installations. */ -#define FMT_SHOWARGS 0x00000001 +#ifndef Table_size +#define Table_size 8191 +#endif -extern enum displaymodes displaymode; +void gettime(struct timeval *); +void quit(int); extern int pcpu_stats; + +enum displaymodes { DISP_CPU = 0, DISP_IO, DISP_MAX }; + +#endif /* _TOP_H_ */ Index: sigconv.awk =================================================================== --- sigconv.awk (revision 183246) +++ sigconv.awk (working copy) @@ -1,5 +1,40 @@ +# Copyright (c) 1984 through 2008, William LeFebvre +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of William LeFebvre nor the names of other +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + # $FreeBSD$ +# +# Awk script converts an include file with definitions for signal names +# in to a predefined array that associates the signal numbers to the names. +# + BEGIN { nsig = 0; j = 0; @@ -15,41 +50,44 @@ /^#define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/ { j = sprintf("%d", $3); + if (siglist[j] != "") next; str = $2; if (nsig < j) nsig = j; - siglist[j] = sprintf("{ \"%s\",\t%2d },", \ + siglist[j] = sprintf("\"%s\",\t%2d", \ substr(str, 4), j); } /^#[ \t]*define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/ { j = sprintf("%d", $4); + if (siglist[j] != "") next; str = $3; if (nsig < j) nsig = j; - siglist[j] = sprintf("{ \"%s\",\t%2d },", \ + siglist[j] = sprintf("\"%s\",\t%2d,", \ substr(str, 4), j); } /^#[ \t]*define[ \t][ \t]*_SIG[A-Z]+[0-9]*[ \t]/ { j = sprintf("%d", $4); + if (siglist[j] != "") next; str = $3; if (nsig < j) nsig = j; - siglist[j] = sprintf("{ \"%s\",\t%2d },", \ + siglist[j] = sprintf("\"%s\",\t%2d", \ substr(str, 5), j); } END { for (n = 1; n <= nsig; n++) if (siglist[n] != "") - printf(" %s\n", siglist[n]); + printf(" { %s },\n", siglist[n]); printf(" { NULL,\t 0 }\n};\n"); } Index: machine.h =================================================================== --- machine.h (revision 183246) +++ machine.h (working copy) @@ -1,4 +1,36 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * $FreeBSD$ */ @@ -8,19 +40,34 @@ * for any specific machine. */ +#ifndef _MACHINE_H_ +#define _MACHINE_H_ + +#include "top.h" + /* - * the statics struct is filled in by machine_init + * The statics struct is filled in by machine_init. Fields marked as + * "optional" are not filled in by every module. */ struct statics { char **procstate_names; char **cpustate_names; char **memory_names; - char **swap_names; -#ifdef ORDER - char **order_names; -#endif + char **swap_names; /* optional */ + char **order_names; /* optional */ + char **top_color_names; /* optional */ + char **kernel_names; /* optional */ + time_t boottime; /* optional */ + int modemax; /* optional */ int ncpus; + int maxcpus; + struct { + unsigned int fullcmds : 1; + unsigned int idle : 1; + unsigned int warmup : 1; + unsigned int threads : 1; + } flags; }; /* @@ -41,8 +88,9 @@ int P_ACTIVE; /* number of procs considered "active" */ int *procstates; int *cpustates; - int *memory; - int *swap; + int *kernel; + long *memory; + long *swap; struct timeval boottime; int ncpus; }; @@ -61,17 +109,25 @@ int idle; /* show idle processes */ int self; /* show self */ int system; /* show system processes */ - int thread; /* show threads */ + int fullcmd; /* show full command */ + int usernames; /* show usernames */ int uid; /* only this uid (unless uid == -1) */ + char *command; /* only this command (unless == NULL) */ + int mode; /* select display mode (0 is default) */ + int threads; /* show threads separately */ int wcpu; /* show weighted cpu */ int jail; /* show jail ID */ - char *command; /* only this command (unless == NULL) */ }; /* routines defined by the machine dependent module */ +int machine_init(struct statics *); +void get_system_info(struct system_info *); +caddr_t get_process_info(struct system_info *, struct process_select *, int); +char *format_header(char *); +char *format_next_process(struct process_select *sel, caddr_t, char *(*)(int)); +int proc_owner(int); +#ifdef HAVE_FORMAT_PROCESS_HEADER -char *format_header(); -char *format_next_process(); - -/* non-int routines typically used by the machine dependent module */ -char *printable(); +#endif /* _MACHINE_H_ */ +char *format_process_header(struct process_select *sel, caddr_t handle, int count); +#endif Index: display.h =================================================================== --- display.h (revision 183246) +++ display.h (working copy) @@ -1,7 +1,84 @@ -/* constants needed for display.c */ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ -/* "type" argument for new_message function */ +/* interface declaration for display.c */ -#define MT_standout 1 -#define MT_delayed 2 +#ifndef _DISPLAY_H +#define _DISPLAY_H +#include "globalstate.h" + +void display_clear(); +int display_resize(); +int display_lines(); +int display_columns(); +int display_init(struct statics *statics); +void i_loadave(int mpid, double *avenrun); +void u_loadave(int mpid, double *avenrun); +void i_minibar(int (*formatter)(char *, int)); +void u_minibar(int (*formatter)(char *, int)); +void i_uptime(time_t *bt, time_t *tod); +void u_uptime(time_t *bt, time_t *tod); +void i_timeofday(time_t *tod); +void i_procstates(int total, int *brkdn, int threads); +void u_procstates(int total, int *brkdn, int threads); +void i_cpustates(int *states); +void u_cpustates(int *states); +void z_cpustates(); +void i_kernel(int *stats); +void u_kernel(int *stats); +void i_memory(long *stats); +void u_memory(long *stats); +void i_swap(long *stats); +void u_swap(long *stats); +void i_message(struct timeval *now); +void u_message(struct timeval *now); +void i_header(char *text); +void u_header(char *text); +void i_process(int line, char *thisline); +void u_process(int, char *); +void i_endscreen(); +void u_endscreen(); +void display_header(int t); +void new_message(char *msgfmt, ...); +void message_error(char *msgfmt, ...); +void message_mark(); +void message_clear(); +void message_expire(); +void message_prompt(char *msgfmt, ...); +void message_prompt_plain(char *msgfmt, ...); +int readline(char *buffer, int size, int numeric); +void display_pagerstart(); +void display_pagerend(); +void display_pager(char *fmt, ...); + +#endif Index: screen.c =================================================================== --- screen.c (revision 183246) +++ screen.c (working copy) @@ -1,14 +1,42 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * - * $FreeBSD$ */ /* This file contains the routines that interface to termcap and stty/gtty. @@ -23,20 +51,52 @@ #include "os.h" #include "top.h" +#if HAVE_CURSES_H && HAVE_TERM_H +#include <curses.h> +#include <term.h> +#else +#if HAVE_TERMCAP_H +#include <termcap.h> +#else +#if HAVE_CURSES_H +#include <curses.h> +#endif +#endif +#endif + +#if !HAVE_DECL_TPUTS +int tputs(const char *, int, int (*)(int)); +#endif +#if !HAVE_DECL_TGOTO +char *tgoto(const char *, int, int); +#endif +#if !HAVE_DECL_TGETENT +int tgetent(const char *, char *); +#endif +#if !HAVE_DECL_TGETFLAG +int tgetflag(const char *); +#endif +#if !HAVE_DECL_TGETNUM +int tgetnum(const char *); +#endif +#if !HAVE_DECL_TGETSTR +char *tgetstr(const char *, char **); +#endif + #include <sys/ioctl.h> #ifdef CBREAK # include <sgtty.h> -# define SGTTY +# define USE_SGTTY #else # ifdef TCGETA -# define TERMIO +# define USE_TERMIO # include <termio.h> # else -# define TERMIOS +# define USE_TERMIOS # include <termios.h> # endif #endif -#if defined(TERMIO) || defined(TERMIOS) +#if defined(USE_TERMIO) || defined(USE_TERMIOS) # ifndef TAB3 # ifdef OXTABS # define TAB3 OXTABS @@ -45,44 +105,46 @@ # endif # endif #endif + #include "screen.h" #include "boolean.h" +#define putcap(str) (void)((str) != NULL ? tputs(str, 1, putstdout) : 0) + extern char *myname; -int putstdout(); - -int overstrike; -int screen_length; -int screen_width; char ch_erase; char ch_kill; +char ch_werase; char smart_terminal; +int screen_length; +int screen_width; + char PC; -char *tgetstr(); -char *tgoto(); -char termcap_buf[1024]; -char string_buffer[1024]; -char home[15]; -char lower_left[15]; -char *clear_line; -char *clear_screen; -char *clear_to_end; -char *cursor_motion; -char *start_standout; -char *end_standout; -char *terminal_init; -char *terminal_end; -#ifdef SGTTY +static int tc_overstrike; +static char termcap_buf[1024]; +static char string_buffer[1024]; +static char home[15]; +static char lower_left[15]; +static char *tc_clear_line; +static char *tc_clear_screen; +static char *tc_clear_to_end; +static char *tc_cursor_motion; +static char *tc_start_standout; +static char *tc_end_standout; +static char *terminal_init; +static char *terminal_end; + +#ifdef USE_SGTTY static struct sgttyb old_settings; static struct sgttyb new_settings; #endif -#ifdef TERMIO +#ifdef USE_TERMIO static struct termio old_settings; static struct termio new_settings; #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS static struct termios old_settings; static struct termios new_settings; #endif @@ -96,11 +158,65 @@ #define STDOUT 1 #define STDERR 2 -init_termcap(interactive) +/* This has to be defined as a subroutine for tputs (instead of a macro) */ -int interactive; +static int +putstdout(TPUTS_PUTC_ARGTYPE ch) { + return putchar((int)ch); +} + +void +screen_getsize() + +{ + +#ifdef TIOCGWINSZ + + struct winsize ws; + + if (ioctl (1, TIOCGWINSZ, &ws) != -1) + { + if (ws.ws_row != 0) + { + screen_length = ws.ws_row; + } + if (ws.ws_col != 0) + { + screen_width = ws.ws_col - 1; + } + } + +#else +#ifdef TIOCGSIZE + + struct ttysize ts; + + if (ioctl (1, TIOCGSIZE, &ts) != -1) + { + if (ts.ts_lines != 0) + { + screen_length = ts.ts_lines; + } + if (ts.ts_cols != 0) + { + screen_width = ts.ts_cols - 1; + } + } + +#endif /* TIOCGSIZE */ +#endif /* TIOCGWINSZ */ + + (void) strncpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1), + sizeof(lower_left) - 1); + lower_left[sizeof(lower_left) - 1] = '\0'; +} + +int +screen_readtermcap(int interactive) + +{ char *bufptr; char *PCptr; char *term_name; @@ -111,11 +227,11 @@ screen_width = MAX_COLS; screen_length = 0; - if (!interactive) + if (interactive == No) { /* pretend we have a dumb terminal */ smart_terminal = No; - return; + return No; } /* assume we have a smart terminal until proven otherwise */ @@ -129,7 +245,7 @@ if (term_name == NULL) { smart_terminal = No; - return; + return No; } /* now get the termcap entry */ @@ -147,21 +263,21 @@ /* pretend it's dumb and proceed */ smart_terminal = No; - return; + return No; } /* "hardcopy" immediately indicates a very stupid terminal */ if (tgetflag("hc")) { smart_terminal = No; - return; + return No; } /* set up common terminal capabilities */ if ((screen_length = tgetnum("li")) <= 0) { screen_length = smart_terminal = 0; - return; + return No; } /* screen_width is a little different */ @@ -175,71 +291,74 @@ } /* terminals that overstrike need special attention */ - overstrike = tgetflag("os"); + tc_overstrike = tgetflag("os"); /* initialize the pointer into the termcap string buffer */ bufptr = string_buffer; /* get "ce", clear to end */ - if (!overstrike) + if (!tc_overstrike) { - clear_line = tgetstr("ce", &bufptr); + tc_clear_line = tgetstr("ce", &bufptr); } /* get necessary capabilities */ - if ((clear_screen = tgetstr("cl", &bufptr)) == NULL || - (cursor_motion = tgetstr("cm", &bufptr)) == NULL) + if ((tc_clear_screen = tgetstr("cl", &bufptr)) == NULL || + (tc_cursor_motion = tgetstr("cm", &bufptr)) == NULL) { smart_terminal = No; - return; + return No; } /* get some more sophisticated stuff -- these are optional */ - clear_to_end = tgetstr("cd", &bufptr); + tc_clear_to_end = tgetstr("cd", &bufptr); terminal_init = tgetstr("ti", &bufptr); terminal_end = tgetstr("te", &bufptr); - start_standout = tgetstr("so", &bufptr); - end_standout = tgetstr("se", &bufptr); + tc_start_standout = tgetstr("so", &bufptr); + tc_end_standout = tgetstr("se", &bufptr); /* pad character */ PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0; /* set convenience strings */ - (void) strncpy(home, tgoto(cursor_motion, 0, 0), sizeof(home) - 1); + (void) strncpy(home, tgoto(tc_cursor_motion, 0, 0), sizeof(home) - 1); home[sizeof(home) - 1] = '\0'; - /* (lower_left is set in get_screensize) */ + /* (lower_left is set in screen_getsize) */ /* get the actual screen size with an ioctl, if needed */ /* This may change screen_width and screen_length, and it always sets lower_left. */ - get_screensize(); + screen_getsize(); /* if stdout is not a terminal, pretend we are a dumb terminal */ -#ifdef SGTTY +#ifdef USE_SGTTY if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1) { smart_terminal = No; } #endif -#ifdef TERMIO +#ifdef USE_TERMIO if (ioctl(STDOUT, TCGETA, &old_settings) == -1) { smart_terminal = No; } #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS if (tcgetattr(STDOUT, &old_settings) == -1) { smart_terminal = No; } #endif + + return smart_terminal; } -init_screen() +void +screen_init() { /* get the old settings for safe keeping */ -#ifdef SGTTY +#ifdef USE_SGTTY if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1) { /* copy the settings so we can modify them */ @@ -253,6 +372,7 @@ /* remember the erase and kill characters */ ch_erase = old_settings.sg_erase; ch_kill = old_settings.sg_kill; + ch_werase = old_settings.sg_werase; #ifdef TOStop /* get the local mode word */ @@ -269,7 +389,7 @@ putcap(terminal_init); } #endif -#ifdef TERMIO +#ifdef USE_TERMIO if (ioctl(STDOUT, TCGETA, &old_settings) != -1) { /* copy the settings so we can modify them */ @@ -283,8 +403,9 @@ (void) ioctl(STDOUT, TCSETA, &new_settings); /* remember the erase and kill characters */ - ch_erase = old_settings.c_cc[VERASE]; - ch_kill = old_settings.c_cc[VKILL]; + ch_erase = old_settings.c_cc[VERASE]; + ch_kill = old_settings.c_cc[VKILL]; + ch_werase = old_settings.c_cc[VWERASE]; /* remember that it really is a terminal */ is_a_terminal = Yes; @@ -293,7 +414,7 @@ putcap(terminal_init); } #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS if (tcgetattr(STDOUT, &old_settings) != -1) { /* copy the settings so we can modify them */ @@ -307,8 +428,9 @@ (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings); /* remember the erase and kill characters */ - ch_erase = old_settings.c_cc[VERASE]; - ch_kill = old_settings.c_cc[VKILL]; + ch_erase = old_settings.c_cc[VERASE]; + ch_kill = old_settings.c_cc[VKILL]; + ch_werase = old_settings.c_cc[VWERASE]; /* remember that it really is a terminal */ is_a_terminal = Yes; @@ -325,14 +447,15 @@ } } -end_screen() +void +screen_end() { /* move to the lower left, clear the line and send "te" */ if (smart_terminal) { putcap(lower_left); - putcap(clear_line); + putcap(tc_clear_line); fflush(stdout); putcap(terminal_end); } @@ -340,37 +463,38 @@ /* if we have settings to reset, then do so */ if (is_a_terminal) { -#ifdef SGTTY +#ifdef USE_SGTTY (void) ioctl(STDOUT, TIOCSETP, &old_settings); #ifdef TOStop (void) ioctl(STDOUT, TIOCLSET, &old_lword); #endif #endif -#ifdef TERMIO +#ifdef USE_TERMIO (void) ioctl(STDOUT, TCSETA, &old_settings); #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS (void) tcsetattr(STDOUT, TCSADRAIN, &old_settings); #endif } } -reinit_screen() +void +screen_reinit() { /* install our settings if it is a terminal */ if (is_a_terminal) { -#ifdef SGTTY +#ifdef USE_SGTTY (void) ioctl(STDOUT, TIOCSETP, &new_settings); #ifdef TOStop (void) ioctl(STDOUT, TIOCLSET, &new_lword); #endif #endif -#ifdef TERMIO +#ifdef USE_TERMIO (void) ioctl(STDOUT, TCSETA, &new_settings); #endif -#ifdef TERMIOS +#ifdef USE_TERMIOS (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings); #endif } @@ -382,102 +506,87 @@ } } -get_screensize() +void +screen_move(int x, int y) { + tputs(tgoto(tc_cursor_motion, x, y), 1, putstdout); +} -#ifdef TIOCGWINSZ +void +screen_standout(char *msg) - struct winsize ws; - - if (ioctl (1, TIOCGWINSZ, &ws) != -1) +{ + if (smart_terminal) { - if (ws.ws_row != 0) - { - screen_length = ws.ws_row; - } - if (ws.ws_col != 0) - { - screen_width = ws.ws_col - 1; - } + putcap(tc_start_standout); + fputs(msg, stdout); + putcap(tc_end_standout); } - -#else -#ifdef TIOCGSIZE - - struct ttysize ts; - - if (ioctl (1, TIOCGSIZE, &ts) != -1) + else { - if (ts.ts_lines != 0) - { - screen_length = ts.ts_lines; - } - if (ts.ts_cols != 0) - { - screen_width = ts.ts_cols - 1; - } + fputs(msg, stdout); } - -#endif /* TIOCGSIZE */ -#endif /* TIOCGWINSZ */ - - (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1), - sizeof(lower_left) - 1); - lower_left[sizeof(lower_left) - 1] = '\0'; } -standout(msg) +void +screen_clear() -char *msg; - { if (smart_terminal) { - putcap(start_standout); - fputs(msg, stdout); - putcap(end_standout); + putcap(tc_clear_screen); } - else - { - fputs(msg, stdout); - } } -clear() +int +screen_cte() { if (smart_terminal) { - putcap(clear_screen); + if (tc_clear_to_end) + { + putcap(tc_clear_to_end); + return(Yes); + } } + return(No); } -clear_eol(len) +void +screen_cleareol(int len) -int len; +{ + int i; -{ - if (smart_terminal && !overstrike && len > 0) + if (smart_terminal && !tc_overstrike && len > 0) { - if (clear_line) + if (tc_clear_line) { - putcap(clear_line); - return(0); + putcap(tc_clear_line); + return; } else { - while (len-- > 0) + i = 0; + while (i++ < 0) { putchar(' '); } - return(1); + i = 0; + while (i++ < 0) + { + putchar('\b'); + } + return; } } - return(-1); + return; } -go_home() +void +screen_home() { if (smart_terminal) @@ -486,13 +595,4 @@ } } -/* This has to be defined as a subroutine for tputs (instead of a macro) */ -putstdout(ch) - -char ch; - -{ - putchar(ch); -} - Index: username.c =================================================================== --- username.c (revision 183246) +++ username.c (working copy) @@ -1,189 +1,156 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University - * - * $FreeBSD$ */ /* * Username translation code for top. * - * These routines handle uid to username mapping. - * They use a hashing table scheme to reduce reading overhead. - * For the time being, these are very straightforward hashing routines. - * Maybe someday I'll put in something better. But with the advent of - * "random access" password files, it might not be worth the effort. + * These routines handle uid to username mapping. They use a hash table to + * reduce reading overhead. Entries are refreshed every EXPIRETIME seconds. * - * 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. + * The old ad-hoc hash functions have been replaced with something a little + * more formal and (hopefully) more robust (found in hash.c) */ +#include "os.h" + #include <sys/types.h> -#include <stdio.h> #include <pwd.h> #include <utmp.h> -#include "top.local.h" +#include "top.h" #include "utils.h" +#include "hash.h" -struct hash_el { - int uid; - char name[UT_NAMESIZE + 1]; -}; +#define EXPIRETIME (60 * 5) -#define is_empty_hash(x) (hash_table[x].name[0] == 0) +/* we need some sort of idea how long usernames can be */ +#ifndef MAXLOGNAME +#ifdef _POSIX_LOGIN_NAME_MAX +#define MAXLOGNAME _POSIX_LOGIN_NAME_MAX +#else +#define MAXLOGNAME 9 +#endif +#endif -/* simple minded hashing function */ -/* Uid "nobody" is -2 results in hashit(-2) = -2 which is out of bounds for - the hash_table. Applied abs() function to fix. 2/16/96 tpugh -*/ -#define hashit(i) (abs(i) % Table_size) +struct hash_data { + int uid; + char name[UT_NAMESIZE + 1]; /* big enough? */ + time_t expire; +}; -/* 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]; +hash_table *userhash; -init_hash() +void +init_username() + { - /* - * There used to be some steps we had to take to initialize things. - * We don't need to do that anymore, but we will leave this stub in - * just in case future changes require initialization steps. - */ + userhash = hash_create(211); } -char *username(uid) +char * +username(int uid) -register int uid; - { - register int hashindex; + struct hash_data *data; + struct passwd *pw; + time_t now; - hashindex = hashit(uid); - if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid)) - { - /* not here or not right -- get it out of passwd */ - hashindex = get_user(uid); - } - return(hash_table[hashindex].name); -} + /* what time is it? */ + now = time(NULL); -int userid(username) + /* get whatever is in the cache */ + data = hash_lookup_uint(userhash, (unsigned int)uid); -char *username; + /* if we had a cache miss, then create space for a new entry */ + if (data == NULL) + { + /* make space */ + data = (struct hash_data *)malloc(sizeof(struct hash_data)); -{ - struct passwd *pwd; + /* fill in some data, including an already expired time */ + data->uid = uid; + data->expire = (time_t)0; - /* 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); + /* add it to the hash: the rest gets filled in later */ + hash_add_uint(userhash, uid, data); } - /* 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)) + /* Now data points to the correct hash entry for "uid". If this is + a new entry, then expire is 0 and the next test will be true. */ + if (data->expire <= now) { - if (!wecare) - return 0; /* Don't clobber a slot for trash */ - if (hash_table[hashindex].uid == uid) - return(hashindex); /* Fortuitous find */ + if ((pw = getpwuid(uid)) != NULL) + { + strncpy(data->name, pw->pw_name, UT_NAMESIZE); + data->expire = now + EXPIRETIME; + dprintf("username: updating %d with %s, expires %d\n", + data->uid, data->name, data->expire); + } + else + { + /* username doesnt exist ... so invent one */ + snprintf(data->name, sizeof(data->name), "%d", uid); + data->expire = now + EXPIRETIME; + dprintf("username: updating %d with %s, expires %d\n", + data->uid, data->name, data->expire); + } } - /* empty or wrong slot -- fill it with new value */ - hash_table[hashindex].uid = uid; - (void) strncpy(hash_table[hashindex].name, name, UT_NAMESIZE); - return(hashindex); + /* return what we have */ + return data->name; } -/* - * Get a userid->name mapping from the system. - * If the passwd database is hashed (#define RANDOM_PW), we - * just handle this uid. Otherwise we scan the passwd file - * and cache any entries we pass over while looking. - */ +int +userid(char *username) -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) + if ((pwd = getpwnam(username)) == NULL) { - return(enter_user(pwd->pw_uid, pwd->pw_name, 1)); + return(-1); } -#else - int from_start = 0; - - /* - * If we just called getpwuid each time, things would be very slow - * since that just iterates through the passwd file each time. So, - * we walk through the file instead (using getpwent) and cache each - * entry as we go. Once the right record is found, we cache it and - * return immediately. The next time we come in, getpwent will get - * the next record. In theory, we never have to read the passwd file - * a second time (because we cache everything we read). But in - * practice, the cache may not be large enough, so if we don't find - * it the first time we have to scan the file a second time. This - * is not very efficient, but it will do for now. - */ - - while (from_start++ < 2) - { - while ((pwd = getpwent()) != NULL) - { - if (pwd->pw_uid == uid) - { - return(enter_user(pwd->pw_uid, pwd->pw_name, 1)); - } - (void) enter_user(pwd->pw_uid, pwd->pw_name, 0); - } - /* try again */ - setpwent(); - } -#endif - /* if we can't find the name at all, then use the uid as the name */ - return(enter_user(uid, itoa7(uid), 1)); + /* return our result */ + return(pwd->pw_uid); } + Index: loadavg.h =================================================================== --- loadavg.h (revision 183246) +++ loadavg.h (working copy) @@ -1,4 +1,36 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Top - a top users display for Berkeley Unix * * Defines required to access load average figures. Index: screen.h =================================================================== --- screen.h (revision 183246) +++ screen.h (working copy) @@ -1,31 +1,65 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * top - a top users display for Unix 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)) +#ifndef _SCREEN_H_ +#define _SCREEN_H_ -/* 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 ch_kill; /* set to the user's kill character */ +extern char ch_werase; /* set to the user's werase 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(); +void screen_getsize(); +int screen_readtermcap(int interactive); +void screen_init(); +void screen_end(); +void screen_reinit(); +void screen_move(int x, int y); +void screen_standout(char *msg); +void screen_clear(); +int screen_cte(); +void screen_cleareol(int len); +void screen_home(); + +#endif /* _SCREEN_H_ */ Index: Y2K =================================================================== --- Y2K (revision 183246) +++ Y2K (working copy) @@ -22,5 +22,5 @@ THERE IS ABSOLUTELY NO WARRANTY PROVIDED WITH THIS SOFTWARE. -Please see the contents of the file "DISCLAIMER" for further +Please see the contents of the file "LICENSE" for further information. Index: INSTALL =================================================================== --- INSTALL (revision 183246) +++ INSTALL (working copy) @@ -1,166 +1,54 @@ TOP - Version 3.5 + Version 3.8beta1 William LeFebvre and a cast of many INSTALLATION -Configuration and installation of top is very straightforward. After -unpacking the sources, run the script "Configure". It will present you -with a series of questions, all of which should be explained in the -presentation. After you have answered all the questions, "Configure" will -perform all the necessary configuration. Once this is finished, type -"make install". Make will compile the sources then install the resulting -executable and manual page in the appropriate places. +Configuration and installation of top is easy. Top version 3.6 +comes with a configure script generated by gnu autoconf. After +unpacking the tar file, simply run "./configure". The script will +automatically perform a series of checks on the system and determine +what settings are appropriate for the Makefile and certain include +files. Once configure completes, simply type "make install" and +top will be compiled and installed. By default, the installation +location is /usr/local/bin. You can change the destination location +with the --prefix option to configure. -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. +In addition to the standard options, top's configure script supports +the following: -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. + --with-module=name -HANDLING MULTIPLE ARCHITECTURES + Force the use of a particular module. Modules are located + in the subdirectory "machine". A module's name is derived + from the file's basename without the leading "m_". -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. + --with-ext=name -HANDLING MULTIPLE OS VERSIONS + Compile with the extension "name", found in the subdirectory + "ext". At the present time, there are no extensions in the + standard distribution. -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". + --enable-debug + --disable-debug -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. + Default off. Include debugging output in the compilation, + which can be seen with the -D switch. -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. + --enable-color + --disable-color -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: + Default on. Include code that allows for the use of color + in the output display. Use --disable-color if you do not + want this feature compiled in to the code. The configure + script also recognizes the spelling "colour". - exec top-`uname -m`-`uname -r` "$@" + --enable-kill + --disable-kill -So when you run this script, it execs a filename that is unique to your -specific machine architecture and your OS revision number. - -To use "metatop", do the following: - - . on any machine, run Configure and choose the module that is - appropriate for the machine - . for all machines which use the same module: - . group machines according to machine architecture AND OS - revision number (i.e.: sun4-4.1.1, sun4c-4.1.1, sun4c-4.1.2, - sun4-4.1.3, sun4c-4.1.3, sun4m-4.1.3, ...) - . for each group, choose one machine from that group and on it - run "make clean; make installmeta". - - -The "installmeta" rule in the makefile will insure that top is compiled, -install the shell file "metatop" as "top", then install the executable -"top" with a name appropriate to the machine architecture and OS revision. - - -HINTS FOR CHOOSING THE CORRECT MODULE: - -SOLARIS 2.x - -All versions of Solaris will now work with the module sunos5. Version -specific modules (such as sunos54) no longer exist. - - -SUNOS 4.x AND MULTIPROCESSOR ARCHITECTURES - -First, we need to be speaking the same language: - -sun4 a regular sparc sun 4 architecture machine (sparc station 1, - sparc station 2, IPC, SLC, etc.) - -sun4m a multiprocessor sparc (Sparc 10, 4/670, 4/690) - -I intended to write the sunos4 module so that an executable compiled on a -sun4m machine would work correctly on a sun4 machine. Unfortunately my -experiments indicate that this cannot be done. It turns out that the user -structure is so different between these two architectures that nothing -short of a serious hack will make the same executable work correctly on -both machines. I recommend that you use the separate module "sunos4mp" -when making an executable for a sun4m architecture, and use "sunos4" when -making an executable for sun4 or sun4c architectures. - -DIGITAL UNIX V4.0 - -This is the successor to DECOSF/1. Use the module decosf1. - -SOLBOURNE OPERATING SYSTEM (OS/MP) - -If you are running OS/MP version 4.1A, then use the module "osmp4.1a". - -If you are running a version of OS/MP OLDER than 4.1A (that is, one -of its predecessors), use the module "sunos4". - -If you are running OS/MP 4.1B or LATER, use the module "sunos4mp". - -HP/UX OPERATING SYSTEM - -The module hpux8 works on all version 8 systems. Some say that it works -with version 9 as well, but one user did send me a separate module for -version 9. This module has only been tested on series 800 machines. I -would recommend the following for those running version 9: try hpux9 and -if it doesn't work then try hpux8. If neither work, then send mail to me -and/or the modules' authors. Another note: we have a model 730 supposedly -running version 9.01. The module hpux9 did not compile successfully, but -the module hpux8 worked fine. The module hpux10 works on all revisions of -HP/UX 10 except 10.10, where HP removed the definition of the proc structure -from the system include files. - -NET/2 386BSD SYSTEMS - -If your version of the operating system has patchkit 2.4 installed, -then you will need to modify machine/m_386bsd.c and uncomment the -definition of PATCHED_KVM. This patchkit makes what more than a few -people believe to be a wholly unnecessary patch to the way the kvm -routines work. - -A/UX SYSTEMS - -There is a module for A/UX 3.0 and 3.1. Whether or not it works for -any other version is not known. Proceed at your own risk. - -Although AUX does not generally have a renice systemcall, it can be -implemented by tweeking kernel memory. The flag IMPLEMENT_SETPRIORITY -controls the inclusion of this code. It is off be default. While -such a simple hack should not be difficult to get right, USE THIS -FEATURE AT YOUR OWN RISK! - + Default on. Include code that allows for renicing and sending + signals to processes from within top (the 'kill' and 'renice' + commands). Use --disable-kill if you do not want this feature + compiled in to the code. Index: os.h =================================================================== --- os.h (revision 183246) +++ os.h (working copy) @@ -1,38 +1,143 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + #include <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; +#include <sys/param.h> +#include <stdio.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#if TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> #else - typedef int sigret_t; +# if HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif #endif -/* system routines that don't return int */ +#if STDC_HEADERS +#include <string.h> +#include <stdlib.h> +#define setbuffer(f, b, s) setvbuf((f), (b), (b) ? _IOFBF : _IONBF, (s)) +#define memzero(a, b) memset((a), 0, (b)) +#else /* !STDC_HEADERS */ +#ifndef HAVE_STRCHR +#define strchr(a, b) index((a), (b)) +#define strrchr(a, b) rindex((a), (b)) +#endif /* HAVE_STRCHR */ +#ifdef HAVE_MEMCPY +#define memzero(a, b) memset((a), 0, (b)) +#else +#define memcpy(a, b, c) bcopy((b), (a), (c)) +#define memzero(a, b) bzero((a), (b)) +#define memcmp(a, b, c) bcmp((a), (b), (c)) +#endif /* HAVE_MEMCPY */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#else +#ifdef HAVE_STRING_H +#include <string.h> +#endif +#endif char *getenv(); caddr_t malloc(); +#endif /* STDC_HEADERS */ -#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; +/* If snprintf or vsnprintf aren't available, we substitute our own. + But we have to include stdarg in order to be able to define them. +*/ +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#ifndef HAVE_SNPRINTF +int ap_snprintf(char *buf, size_t len, const char *format,...); +#define snprintf ap_snprintf #endif +#ifndef HAVE_VSNPRINTF +int ap_vsnprintf(char *buf, size_t len, const char *format,va_list ap); +#define vsnprintf ap_vsnprintf +#endif +#endif -/* some systems declare sys_errlist in stdio.h! */ -#if defined(__NetBSD__) || defined(__FreeBSD__) -#if !defined(__m68k__) -# if !defined(__NetBSD132__) -#define SYS_ERRLIST_DECLARED -# endif /* __NetBSD132__ */ +#if !HAVE_PID_T +typedef long pid_t; #endif +#if !HAVE_TIME_T +typedef long time_t; #endif +#if !HAVE_UID_T +typedef long uid_t; +#endif + +#ifndef INT_MAX +#define INT_MAX (0x7fffffff) +#endif + +#ifndef UINT_MAX +#define UINT_MAX (0xffffffffU) +#endif + +/* we must have both sighold and sigrelse to use them */ +#if defined(HAVE_SIGHOLD) && !defined(HAVE_SIGRELSE) +#undef HAVE_SIGHOLD +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef HAVE_SYSEXITS_H +#include <sysexits.h> +#else +#define EX_OK 0 /* successful termination */ +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ +#endif Index: Changes =================================================================== --- Changes (revision 183246) +++ Changes (working copy) @@ -1,3 +1,320 @@ +Tue May 6 2008 - wnl (3.8beta1) + Main code: fixed bugs in screen_cleareol and in display code. Fixed + bug in i_swap when all data is 0. Added ^W patch (from thaquis). + Fixed bug in xdprintf. Added command line options for the "t" and + "m" commands. + SunOS 5 changes: Support for showing individual threads. Redid + allocation of prpsinfo structures. Added a pidthr hash that uses + both pid and thread id for a key. Changed format_process_header + and format_next_process to use a table-driven method for generating + the columns. Status files from /proc (psinfo and lpsinfo) are now + cached to avoid repeatedly reopening them. Column showing number of + LWPs is now called "NLWP" and column showing lwpid is "LWP". + FreeBSD changes: Runtime check to ensure binary is running on + the same machine type it was compiled for. Lots of cleanup and + changed nearly everything to use sysctl rather than kvm, and + inability to open kvm is no longer fatal. Improved thread reporting: + disabled for 7.x and lower. Added lwpid hash for proper tracking + of threads. Changed format_process_header and format_next_process + to use a table-driven method for generating the columns. + Dec Alpha: configure uses compile-time options to properly trap + and handle exceptions from the Alpha FPU (from Brian Maly). + +Tue Feb 26 2008 - wnl (3.7) + Prepare for version 3.7 release. + +Fri Feb 1 2008 - wnl (3.7beta4) + Using the $ notation in printf formats for freebsd apparently was + causing problems on 64-bit systems. All such usage has been + removed and the process line is formatted piecemeal. + +Thu Dec 27 2007 - wnl (3.7beta3) + Improved function comments in display.c for message_error functions. + Changed some of the error messages in top.c to be more succint. + +Fri Dec 7 2007 - wnl (3.7beta3) + Changes to freebsd port: moved some functions up front to + eliminate forward references. Use sysctl to get all vm stats + information, as some of this isn't updated in the struct + vmmeter under FreeBSD 7.0. Added routines to support large-scale + sysctl access. + +Wed Nov 28 2007 - wnl (3.7beta3) + Changes to documentation: FAQ, README, man page. + +Tue Nov 27 2007 - wnl (3.7beta3) + For freebsd, added page faults, pageins, pageouts, and pages + freed to the kernel display line. These numbers reflect the + values presented in vmstat. For sunos5, added page faults, + pageins and pageouts to the kernel display line. + +Fri Nov 2 2007 - wnl (3.7beta3) + Added copyright notices to the top of every source and include file. + Added copyright information to the man page. + Removed a few outdated things from the manifest. + Minor changes to sigconv.awk. + +Sat Oct 27 2007 - wnl (3.7beta3) + Added check for sys_signame at configure time and if it is + present then it is used in commands.c to translate signal names + in to numbers. + Added alternate snprintf and vsnprintf functions from apache (in + ap_snprintf.c). Added configure magic to define and compile them in + where needed. Added check to configure for variadic macros. + Preprocessor defintion of dprintf (in utils.h) now depends on + support for variadic macros. Cleaned up m_linux code. + +Wed Oct 3 2007 - wnl (3.7beta3) + Lots of changes, thanks to Mark Wong. Most changes were to + clean the code up so that it would compile cleanly with -Wall + (all warnings). Changed function names in screen.c so that + they all start with "screen_". Isolated all interaction with + termcap to screen.c by adding a real function for cursor + addressing (in the past it was just a macro). Only screen.c + now needs to worry about defining templates for the termcap + functions. Added configure and preprocessor magic to ensure + that all the termcap functions used in the code are defined + with templates. Changed names of some other functions and + global variables to avoid name conflicts with functions in + curses and other well established libraries. Changed dprintf + macro to use variadic arguments so that the preprocessor can + gobble up the entire call when compiling without debugging + (this will have to be made more portable). All include files + are surrounded by #ifndef statements to accomodate multiple + inclusions. Platform module is now compiled with + -fno-strict-aliasing as some of the modules do type punning + that can confuse the optimizer. + +Wed Sep 26 2007 - wnl (3.7beta3) + For freebsd, priority is no longer normalized by PZERO. This + contradicts the behavior used by ps when it displays priority. + But normalizing by PZERO has become a bit of an anachronism + and it actually obscures the meaning of the priority without + adding any real value. + +Wed Sep 19 2007 - wnl (3.7beta3) + Many changes to improve the display of threads. Changed + process summary line to use the word "threads" when showing + individual threads. Added the system command to toggle the + display of system processes. Fixed bug in hash.c remove_pos. + For freebsd: count threads correctly when they are being + displayed, nice column is more closely in line with ps + (nothing fancy for real time processes), add two more process + states that didn't exist in older releases of freebsd (wait + and lock). + For linux: Threads done right. Now track individual threads + of multi-threaded processes separately so that we always know + their %cpu. Switch to format_process_header so that we can + change the column headings and remove the THR column when + displaying individual threads. Switched process (and thread) + tracking over to use generic hash table functions included + with the new version of top. Process states and total now + include threads when they are being shown. Added "SHR" column + to show the amount of shared memory per process. Improved + calculation of elapsed time and percent cpu to avoid + overflows. Remove weighted cpu calculations entirely as it is + an anachronism. + For Solaris: Moved check for libelf to accomodate older systems. + +Sun Sep 9 2007 - wnl (3.7beta2) + Documentation changes. Fixes to sunos5 port. Added display of + thread count and selection by command name to linux port. Removed + the use of inline functions from hash.c as that doesn't appear to + be very portable. + +Wed Sep 5 2007 - wnl (3.7beta1) + Fixed freebsd and linux configuration bugs. Added configuration + options for tweaking program defaults. Rewrote top level code + (top.c) from scratch, including command handling so that adding + new commands is much easier. Changed message-line handling to + ensure that the message is displayed for at least 5 seconds + regardless of the update frequency. Added a "miniupdate" that + occurs one second after the initial screen on systems that don't + already delay the first screen. The mini-update shows cpu state + percentages. Added ability to select output by command name on + some systems. Fixed color toggling via the "C" command. Added + long options via getopt_long to complement the existing single + character options. Added the freebsd "m" command to chose + alternate display modes. On freebsd this gives a process i/o + display. Added the freebsd "H" command to select the display of + individual threads. Added "-a" option ("all") to set number of + displays and number of processes to infinity (equivalent to + "-d all all"). Added dual architecture compilation for Solaris + to generate both a 32-bit and a 64-bit binary. This is on by + default when compiling on a 64-bit system and can be explicitly + set via "configure --enable-dualarch". Added uniform hashing + functions that use bucket hash for uint, pid, and string. Changed + username.c and the sunos and freebsd modules to use these functions. + Added the "kernel" information line to the display to show + statistics on what the kernel is doing (context switches, forks, + traps, etc.). This requires explicit support by the platform + module, currently only freebsd, linux, and sunos. + +Wed Apr 18 2007 - wnl (3.6.1) + Fixed a few bugs in sigconv.awk that were causing incorrect + results on FreeBSD. Changed configure.ac to fix a few linux + problems: signal include file and /proc/1/stat. + +Fri Apr 13 2007 - wnl (3.6.1) + Removed the use of VPATH for compiling the system module and used + an explicit dependency in the Makefile instead. VPATH is now set + to just srcdir to ensure that top will compile correctly when + configured from a different directory. On systems without VPATH + support, top will still configure and compile, but only + from within the source directory. This fixes bug 1699526. + +Fri Feb 2 2007 - wnl (3.6.1) + Revised the way that configure figures out owner, group, and mode. + For systems that don't use the kernel, it tries to match install + settings to allow access to stuff in /proc. More importantly, if + mode is 755 then neither owner nor group are set. This fixes bug + 1631136. Added patch from haanjdj@xs4all.nl to fix an occasional + core dump in m_decosf1.c. This checks return code from task_threads. + Made sure all get_system_info functions are declared void. Fixed + string termination bug. Cleaned up documetation for sunos5. + +Tue Aug 8 2006 - wnl (3.6.1) + For Solaris, changed the tag "swap" to "total swap" to clarify + what is beign displayed. Note that the calculations are still the + same: the display is just showing total rather than total - free. + +Thu Apr 27 2006 - wnl (3.6) + Added patches for linux-style sort shortcuts and for Unixware + support in configure (patch 1474427). Fixed sunos5 to do slow start + and to ensure cpucount is set (patch 1477386). Added pagination + routines to display.c and modified show_help to use it, since the + help screen is now longer than 24 lines. Applied patch for unixware + support that adds check for mas library (patch #1474423). Solaris + cpu percent now reflects a percentage of the entire server, rather + than a single cpu (bug 1478138). + +Mon Mar 27 2006 - wnl (3.6) + The production release of version 3.6. Fixed a minor scaling + bug in the decosf1 module. Support for MacOS X is officially + withdrawn although the macosx module is still part of the + distribution. Hopefully this is a temporary situation. + Documentation updated. + + +Wed Feb 15 2006 - wnl (3.6beta5) + Minor changes to eliminate warnings from the Sun Studio compiler. + These were mostly sloppy argument declarations. I also added + message.h to provide an interface file for just the message + related functions of display.c. + +Mon Dec 26 2005 - wnl (3.6beta4) + Added new netbsd module, courtesy of Simon Burge. + Fixed a few bugs in SVR4 module and added its use to + configure.ac, thanks to Sanchet Dighe. Also ensured that the + novpath Makefile was in the distribution. + Fixed portability problem in display.c + + +Mon Oct 24 2005 - wnl (3.6beta3) + Set up a color tagging mechanism in color.c to allow for the + dynamic creation of tag names to contol color highlighting. + These names are partially derived from the tags used to label + memory and swap information on the screen, thus are driven by + the machine module itself. Added -T option to list color + highlighting information. Help screen now includes the actual + list of sort order names. Incorporated some minor fixes to + the main code from the Freebsd source tree. Fixed bug #1324582. + Freebsd 5: removed WCPU column and added THR column. Display + for freebsd 4 and earlier unchanged since they don't track + threads in the kernel. Added LICENSE file to distribution. + +Wed Oct 12 2005 - wnl (3.6beta2) + Major overhaul to display.c. All lines of the display are + directly tracked and controlled via display_write and its + companion display_fmt. Added support for complete control + of ANSI color on the screen: this will be used in the future + to allow for full use of color everywhere on the screen. + Signal handling code now uses sigaction on all systems that + support it. Restored the freebsd module and did away with + freebsd4, and upgraded freebsd module to support 5.x. + Fix bug #1306099 (wio(wait) timer ignored on OSF1). + +Fri Sep 23 2005 - wnl (3.6beta1) + Fixed bugs #1266341 (compilation errors with gcc 4.x), + #1156464 (cpu% field for sunos), #1156243 (compilation + errors on AIX). Applied patches #1217855 (Solaris 10 + xarch flag). Overhaul of sunos5 module, making code more + efficient and easier to follow. Got rid of need for MEMTYPE + type in utils.h. Changed all memory statistics data in the + module specification from an int to a long to better support + 64-bit systems with lots of memory. Moved all unused modules + out of the distribution (I will add them back in as needed). + Moved freebsd module to freebsd4 as it won't work with 5.x + (a new module will be necessary). Added support to configure + for makes that don't understand VPATH. Updated documentation: + man page, FAQ, README, INSTALL. + +Mon Jan 24 2005 - wnl (3.6alpha10) + Updated aix43 module with ANSI function declarations and fixed + declaration of get_system_info. Configure now uses irixsgi + module for irix6* systems. Updates to the following modules: + irixsgi, sunos5. Fixed null pointer bug in color.c. Removed + some useless code and definitions in display.c + + +Sun Nov 28 2004 - wnl (3.6alpha9) + Replace AIX 5 module with alternate (bug 1056565). + Fixed vulnerability in use of snprintf. + +Fri Oct 22 2004 - wnl (3.6alpha8) + Support for linux 2.6, added more stuff to memory and swap lines. + Updated linuxthr module, which is only useful on 2.4 and earlier. + Added some color support back in (feature request 930588), but + still need to add it back to the per-process display. Added + OSF 5 support (untested). + Fixed bug 1017951 (invalid process count argument not caught) + +Tue Apr 20 2004 - wnl (3.6alpha7) + Added 64 bit support for AIX. + +Thu Apr 15 2004 - wnl (3.6alpha6) + Included fixes for decosf1 pid size and updated module. Also + added osf1 to list of recognized operating systems in configure.ac. + +Tue Mar 30 2004 - wnl (3.6alpha5) + Minor bug fixes and some code rearrangement. Changes to install + rule. Added several more platforms including: aix 4.2 thru 5, + MacOS 10, Dec OSF, HPUX 7 thru 11. Fixed the core dumping bug + in linux. Code cleanup, including sigdesc.h (by changing + sigconv.awk). Startup error messages are displayed on the + first screen rather than beforehand (no more pause). Cleaned + up interrupt handling to avoid a race condition. Eliminated + top.local.h. REMOVED Configure!!! + +Mon Mar 22 2004 - wnl (3.6alpha1) + Now using gnu autoconf. Eliminated the need for CFLAGS and LIBS + tags in the module source files. Autoconf tries to figure all + that out now. Machine module interface now uses flags to determine + if module supports sorting, selective display of idle processes, + viewing full commands. Added display of uptime for modules that + support it. Added display of full command lines for modules that + support it. 3.5 modules must be changed a bit to work for 3.6: + ORDER is no longer defined, and the module must fill in the + appropriate fields in struct statics to get the extra features. + Added a extenstion interface to allow for putting extra stuff + on the screen -- this is still half baked and not documented. + +Mon Feb 23 2004 - wnl (3.5) + Turned rc1 in to version 3.5. Only changes were to the FAQ. + +Mon Feb 2 2004 - wnl (3.5rc1) + Changed format_k (utils.c) to use MEMTYPE for its parameter. + On most systems this is a long, but if the module defines + USE_SIZE_T, this is set to be a size_t. The sunos5 module + now defines it, so that it will work correctly on 64-bit + machines. New "linuxthr" module for rolling up processes + that are really threads. Configure autodetects when running + on a 64-bit Solaris machine. + +Tue Dec 16 2003 - wnl (3.5beta13) + Improved linux module. For Solaris, changed "THR" column + heading to "LWP" since that's what they really are. + Thu Mar 30 2000 - wnl (3.5beta12) Updated modules: m_aix41.c, m_aix43.c, m_mtxinu.c, m_sco5.c, and m_ultrix4.c. Index: README =================================================================== --- README (revision 183246) +++ README (working copy) @@ -1,5 +1,5 @@ TOP - Version 3.5 + Version 3.8beta1 William LeFebvre and a cast of dozens @@ -21,46 +21,47 @@ reorganized in a major way to make it easy to port to other platforms. All system dependent code is now contained in one file. -Top now includes a configuration script called "Configure". It helps -the installer choose the correct parameters for this particular -installation. This script MUST be run before attempting to compile top. +Starting with version 3.6, top includes a "configure" script generated +by Gnu's autoconf. This script MUST be run before attempting to +compile top. It will explore the system and generate approriate +contents for Makefile, config.h, and top.1. -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. +On some systems, top requires read access to the memory files +"/dev/kmem" and "/dev/mem" as well as the system's kernel image. Most +installations have these files protected from general access. These +sites would have to install this program in the same way that programs +such as "ps" are installed. On most systems with a /proc file system, +top will try to read everything it can from /proc, but may need extra +permissions to do so. The configure script will determine the +permissions needed by the top binary, and a "make install" as root +will get the binary installed correctly. Sometimes this requires that +the binary be installed with set-group-id privileges and, in rare +cases, set-user-id to root. CAVEAT: version 3 of top has internal commands that kill and renice processes. Although I have taken steps to insure that top makes appropriate checks with these commands, I cannot guarantee that these -internal commands are totally secure. IF YOU INSTALL top as a SETUID -program, you do so AT YOUR OWN RISK! I realize that some operating -systems will require top to run setuid, and I will do everything I can -to make sure that top is a secure setuid program. +internal commands are totally secure. IF YOU INSTALL top SET-USER-ID +TO ROOT, YOU DO SO AT YOUR OWN RISK! I realize that some operating +systems will require top to run setuid root, and I will do everything +I can to make sure that top is a secure setuid program. -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. These modules +are contained in the subdirectory "machine". The "configure" script +automatically determines which module is approproate. However, it may +not be able to determine what the correct module is. This can happen +either because it doesn't know about the system or there is no module +to support the system. In the former case, if you know which module +to use, you can force "configure" to choose a particular module with +the option "--with-module". For example, if you want to force the use +of the svr4 module (which appears as "machine/m_svr4.c") then use +"configure --with-module=svr4" to generate the correct Makefile. See +the file "Porting" for a description of how to write your own module. -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. @@ -69,35 +70,52 @@ answers to the most commonly asked questions about the configuration, installation, and operation of top. +COLOR +Version 3.6 incorporated the idea of using ANSI color sequences to +enhance information on the screen. By default, no color is used. But +you can configure the use of color through the environment variable +TOPCOLORS (or, for compatibility, TOPCOLOURS). The interface is +identical to the one first implemented by chris@spang.uk.eu.org, but +the implementation is entirely different. The option -C can be used +to diable the feature entirely. + +Any information at the top of the screen can be enhanced with color. +However, due to implementation difficulties, the per-process area +cannot be color-enhanced. A complete description of color support can +be found in the man page. References for ANSI color codes can be +found all over the Internet, but if you want a handy reference, look +in color.h. + + AVAILABILITY -The latest version of "top" is now being made available via anonymous -FTP from the host "ftp.groupsys.com" in the directory "/pub/top". -Additional modules will be made available in the directory -"/pub/top/m". The site "eecs.nwu.edu" will continue to house copies -of the distribution as well. +Note that top is now a sourceforge project! Its project name is +"unixtop" and you can access its project page here: -Here are HTML links for the four best "top" archive sites: +http://sourceforge.net/projects/unixtop -<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> -<A HREF="ftp://pharos.dgim.doc.ca/packages/top"> Top mirror (dgim.doc.ca)</A> -<A HREF="ftp://uiarchive.uiuc.edu/pub/packages/top/">Top mirror (uiuc.edu)</A> +On the project page you can find more information and access the +official bug and feature request trackers. If you find a bug, +want to request a feature, or need help, please submit a request +to the appropriate tracker on sourceforge. Thank you. -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. +Subversion access is also provided by Sourceforge. If Subversion is +installed on your system you can check out the project with the +following command: -Announcements about availability will be made to the mailing list -"top-announce@groupsys.com". This is an open list maintained by -majordomo. To join the list, send a message containing the word -"subscribe" to "top-announce-request@groupsys.com". Addresses of -subscribers to this list are kept confidential and will never be used -for any purpose other than as recipients of announements concerning -this software. + svn co https://svn.sourceforge.net/svnroot/unixtop unixtop +There is also a web site dedicated to the project, and it is here: +http://www.unixtop.org + +The latest version of "top" is available as a download through +sourceforge. Start here to access the downloadable files: + +http://sourceforge.net/project/showfiles.php?group_id=72892 + + KNOWN PROBLEMS: Gnu CC @@ -115,6 +133,16 @@ sending me a bug report. Look in the gcc source distribution for the shell script "fixincludes". +MacOS X + +Since I don't have full time root access to a MacOS X system I cannot +provide effective support for the platform. MacOS X uses Mach, and it +is very difficult to extract accurate system and process information +from the system. It takes a lot of trial and error, along with root +access. I have included the most up-to-date version of the macosx module +in the distribution, but I do not claim that it works. If you want to +try to use it, you can configure with "./configure --with-module=macosx". + HP/UX 10.10 In their infinite wisdom, the folks at HP have decided that mere mortals @@ -127,66 +155,37 @@ 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.Dik@Sun.COM> - 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> + Robert Boucher, Marc Cohen, David Cutter, Casper Dik, + Charles Hedrick, Andrew Herbert, Jeff Janvrin, Torsten Kasch, + Petri Kutvonen, William L. Jones, Tim Pugh, Steve Scherf, + Phillip Wu (My apologies if I missed anyone.) +LICENSE + +Top is distributed free of charge under the same terms as the BSD +license. For an official statement, please refer to the file "LICENSE" +which should be included with the source distribution. + + AUTHOR +If you wish to contact me, please send a message to the sourceforge +username "wnl". + William LeFebvre - Group sys Consulting - wnl@groupsys.com - U.S. Mail address: William LeFebvre - Group sys Consulting 11585 Jones Bridge Road - Suite 420-139 - Alpharetta, GA 30022 - (770) 813-3224 + Suite 420 PMB 139 + Alpharetta, GA 30202 Index: layout.h =================================================================== --- layout.h (revision 183246) +++ layout.h (working copy) @@ -1,31 +1,74 @@ /* - * Top - a top users display for Berkeley Unix - * - * This file defines the locations on tne screen for various parts of the - * display. These definitions are used by the routines in "display.c" for - * cursor addressing. - * + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * $FreeBSD$ */ -extern int x_lastpid; /* 10 */ -extern int y_lastpid; /* 0 */ -extern int x_loadave; /* 33 */ -extern int x_loadave_nompid; /* 15 */ -extern int y_loadave; /* 0 */ -extern int x_procstate; /* 0 */ -extern int y_procstate; /* 1 */ -extern int x_brkdn; /* 15 */ -extern int y_brkdn; /* 1 */ -extern int x_mem; /* 5 */ -extern int y_mem; /* 3 */ -extern int x_swap; /* 6 */ -extern int y_swap; /* 4 */ -extern int y_message; /* 5 */ -extern int x_header; /* 0 */ -extern int y_header; /* 6 */ -extern int x_idlecursor; /* 0 */ -extern int y_idlecursor; /* 5 */ -extern int y_procs; /* 7 */ +/* + * Top - a top users display for Unix + * + * This file defines the default locations on the screen for various parts + * of the display. These definitions are used by the routines in "display.c" + * for cursor addressing. + */ -extern int y_cpustates; /* 2 */ +#define X_LASTPID 10 +#define Y_LASTPID 0 +#define X_LASTPIDWIDTH 13 +#define X_LOADAVE 27 +#define Y_LOADAVE 0 +#define X_LOADAVE_NOMPID 15 +#define X_LOADAVEWIDTH 7 +#define X_MINIBAR 50 +#define Y_MINIBAR 0 +#define X_UPTIME 48 +#define Y_UPTIME 0 +#define X_PROCSTATE 15 +#define Y_PROCSTATE 1 +#define X_BRKDN 15 +#define Y_BRKDN 1 +#define X_CPUSTATES 0 +#define Y_CPUSTATES 2 +#define X_KERNEL 8 +#define Y_KERNEL 3 +#define X_MEM 8 +#define Y_MEM 3 +#define X_SWAP 6 +#define Y_SWAP 4 +#define Y_MESSAGE 4 +#define X_HEADER 0 +#define Y_HEADER 5 +#define X_IDLECURSOR 0 +#define Y_IDLECURSOR 4 +#define Y_PROCS 6 + Index: version.c =================================================================== --- version.c (revision 183246) +++ version.c (working copy) @@ -1,25 +1,46 @@ /* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* * Top users/processes display for Unix * Version 3 - * - * This program may be freely redistributed, - * but this entire comment MUST remain intact. - * - * Copyright (c) 1984, 1989, William LeFebvre, Rice University - * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University */ +#include "config.h" #include "top.h" -#include "patchlevel.h" -static char version[16]; +char * +version_string() -char *version_string() - { - sprintf(version, "%d.%d", VERSION, PATCHLEVEL); -#ifdef BETA - strcat(version, BETA); -#endif - return(version); + return(PACKAGE_VERSION); } Index: FAQ =================================================================== --- FAQ (revision 183246) +++ FAQ (working copy) @@ -1,264 +1,340 @@ - TOP - Version 3.5 - Beta Release 11 + TOP + Version 3.8beta1 - William LeFebvre - with much help from others + William LeFebvre + with much help from others + Frequently Asked Questions and their Answers -FREQUENTLY ASKED QUESTIONS AND THEIR ANSWERS -This FAQ is broken out in to several topics. + GENERAL -GENERAL + 1. What is top? - 1. "Where do I get the latest version of top?" + Top provies the user with a regularly updated display showing + information about the system and its top cpu-using processes. Think + of it as a full-screen "ps" output that gets updated at regular + intervals. -The official site for top is "ftp.groupsys.com" in the directory -"/pub/top". It is also available from the following mirror sites: -"pharos.dgim.doc.ca" in /packages/top, "uiarchive.uiuc.edu" in -/pub/packages/top, "sunsite.auc.dk" in /pub/unix/top. European -users should consider using the Denmark (dk) site. - - 2. "Is there a web page for top?" + 2. Where do I get the latest version of top? -Yes. Point your browser at http://www.groupsys.com/top. It includes -all documentation, a nice interactive display which describes the -various components of the output of top, web-based retrieval of the -package, year 2000 information, and pointers to the mailing list. + The official site for top is "ftp.unixtop.org" in the directory + "/pub/top". Top is also a SourceForge project, and the most recent + releases are available on any of the SourceForge mirrors. The + SourceForge project page is at + http://sourceforge.net/projects/unixtop. - 3. "Is there a mailing list for top?" + 3. Is there a web page for top? -The official list for announcements is "top-announce@groupsys.com". -This list is managed by "majordomo@groupsys.com". Announcements of -importance to all top users will be sent to this list, including new -releases, availability of beta test versions, emergency revisions and -patches, etc. Anyone is welcome to join top-announce. This is a -read-only list. The list of subscribers will not (intentionally) be -made available, and postings to the list are limited. + Yes. Point your browser at http://www.unixtop.org. It includes all + documentation, a nice interactive display which describes the various + components of the output of top, web-based retrieval of the package, + year 2000 information, and other neat stuff. -In addition, there is a top developers mailing list that is used by -beta testers and other people who help me port the program to various -machines. Membership to this list is solely at my discretion. If you -feel qualified to act as a beta tester, or if you are doing development -work on top (such as porting to a new platform), you may submit a -request by sending a message to "top-spinners-request@groupsys.com" -containing the word "subscribe". I will contact you within a few days, -as my schedule permits. + 4. Is there a mailing list or on-line bulletin board for top? - 4. "What about Year 2000 compliance"? + There is a mailing list used for general announcements regarding top, + including new releases. This mailing list is available to sourceforge + members and can be accessed from the unixtop sourceforge project + page. Visit SourceForge and search for the project "unixtop", then + click on "mailing lists". There are also on-line forums available + through SourceForge where members can post questions and comments. -Top should not experience any problems with the transition to the year -2000. A full statement concerning top and the year 2000 can be found -in the file "Y2K" included with the distribution. + 5. What about Year 2000 compliance? + Top did not experience any problems with the transition to the year + 2000. A full statement concerning top and the year 2000 can be found + in the file "Y2K" included with the distribution. - 5. "Why does it take so long for a new version of top to go through the - beta test process?" + 6. Will there be another major release of top? Will there be a top + version 4? -This is completely my fault. I have just not had the time to give top -the attention it deserves. I thank everyone for their patience, and I -hope that with the recent changes in the direction of my career that I -can spend more time on this. + I have some great ideas for the next major release of top, and I very + much want to make those ideas a reality. What I don't have much of + these days is free time. But I will keep poking at it and I hope to + have top version 4.0 ready by the fall of 2006. - 6. "Top is not written in ANSI C. Do you ever plan to change that?" + 7. Does top really support multi-processor systems? -Top predates ANSI C by about 5 years. Yeah, it'll get "fixed" eventually. -Probably in 3.6. + On platforms that support multiple processors, top is able to detect + and correctly summarize the information about those processors. What + top does not do is break down the cpu states summary (the third line + of the display) by cpu. Instead it collects the cpu state information + from all processors and combines them in to a single line. Some + vendors include a modified version of top that presents this + information for each cpu. Top 3.7 may have this functionality but it + is not present in the standard top 3.6 release. + 8. Is top under CVS control? Can I access the sources via SourceForge + CVS or Subversion? -CONFIGURING + I maintain top using subversion, not CVS. Although I utilize my own + private subversion repository, it is regularly mirrored in to the + SourceForge Subversion repository. You can access the SourceForge + repository here: https://svn.unixtop.org/unixtop/top-3. - 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. + COMPILING - 8. "Why is Configure a c-shell script? I thought c-shell scripts were - evil?" + 9. We just upgraded our operating system to a new version and top broke. + What should we do? -They are. :-) I'll probably be rewriting the Configure script for the -next release, or switching to something like Gnu configure. + 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. -COMPILING + RUNNING - 9. "We just upgraded our operating system to a new version and top broke. - What should we do?" +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? -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. + Well, you're just not done. On many operating systems today, access + to many of the kernel memory devices and other system files is + restricted to either root or a particular group. The configure script + figures this out (usually) and makes sure that the "install" rule in + the Makefile will install top so that anyone can run it successfully. + However, you have to *install* it first. Do this with the command + "make install". +11. Top is (not) displaying idle processes and I don't (do) want it to. -RUNNING + 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. -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?" +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? -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". + Most modules have been changed to use new memory formatting functions + which will display large values in terms of megabytes instead of + kilobytes. This should fix all occurences of this problem. Also note + that newer versions of top can use columns beyond 79, and understand + window resizes. So you can always make your window wider. -11. "Top is (not) displaying idle processes and I don't (do) want it to." +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? -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. + Gnu CC likes very much to use its own include files. Not being a gcc + expert, I can't explain why it does this. But I can tell you that if + you upgrade your operating system (say from Solaris 2.6 to Solaris + 2.7) after installing gcc, then the include files that gcc uses will + be incorrect, especially those found in the "sys" directory. Your + choices are: (1) rebuild and reinstall the "standard" include files + for gcc (look for scripts in the distribution called "fixincludes" + and "fixinc.svr4"), (2) compile machine.c with + "CFLAGS=-I/usr/include" then make the rest of the object files + normally, or (3) use a different compiler. -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?" +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? -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. + This can happen if you compiled with gcc using the wrong include + files. See the previous question. -13. "I tried to compile top with gcc and it doesn't work. I get - compilation errors in the include files, or I get an executable that - dumps core, or top displays incorrect numbers in some of the displays. - What's wrong?" -Gnu CC likes very much to use its own include files. Not being a gcc -expert, I can't explain why it does this. But I can tell you that if -you upgrade your operating system (say from Solaris 2.4 to Solaris -2.5) after installing gcc, then the include files that gcc uses will -be incorrect, especially those found in the "sys" directory. Your -choices are: (1) rebuild and reinstall the "standard" include files -for gcc (look for scripts in the distribution called "fixincludes" and -"fixinc.svr4"), (2) compile machine.c with "CFLAGS=-I/usr/include" -then make the rest of the object files normally, or (3) use "cc". -Solaris 2.6 users should also consult FAQ #20. + FREEBSD PROBLEMS -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?" +15. This version of top does not show individual threads with the "t" or + "H" commands. Instead it says "command not available." Why? -This can happen if you compiled with gcc using the wrong include files. -See the previous question. + Previous versions of top attempted to support the display of + individual threads under FreeBSD through the use of the "t" command. + However, the FreeBSD kernel does not supply sufficient or correct + information on the individual threads within a process. So the data + that was being displayed was incorrect and misleading. Therefore, top + version 3.8 disables the use of this command to prevent the display + of incorrect information. FreeBSD 8.0 will correctly report + per-thread information and top version 3.8 supports the use of the + "t" command for version 8.0. +16. The "f" command (to display full command lines for the processes) + does not work and instead says "command not available". Why? -SUNOS PROBLEMS + The current version of top is able to use sysctl to retrieve almost + all of the information it needs without having to open /dev/kmem. The + one piece of information not available via sysctl is the full command + line of each argument. If you run top as a regular user and it cannot + open /dev/kmem (in other words, it is not installed set-gid to the + kmem group) then it will disable the "f" command. Make sure the top + binary is installed with a group ownership of "kmem" and with the + set-gid bit on if you want the "f" command to work properly. -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. + MACOSX PROBLEMS +17. I tried to configure top on my Mac OSX system and I got an error + claiming "macosx not supported". What up? -SVR4-derived PROBLEMS + Since I don't have full time root access to a Mac OSX system I cannot + provide effective support for the platform. MacOSX uses Mach, and it + is very difficult to extract accurate system and process information + from the system. It takes a lot of trial and error, along with root + access. I have included the most up-to-date version of the macosx + module in the distribution, but I do not claim that it works. If you + want to try to use it, you can configure with "./configure + --with-module=macosx". -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. + SUNOS PROBLEMS +18. I tried compiling top under SunOS version 4.1.x and it got compile + time errors or run time errors. Is there a patch? -SOLARIS PROBLEMS + 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. -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. + SOLARIS PROBLEMS -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. + NOTE: the most common source of problems with top under Solaris is + the result of compiling it with the wrong front end. Make sure that + /usr/ucb is not on your path before attempting to compile top under + Solaris. -19. "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?" +19. Is there somewhere I can get a pre-compiled package? -No, you were not right. As distributed, top requires NO alterations -for successful compilation and operations under any release of Solaris -2. You probably compiled top with /usr/ucb/cc instead of the real C -compiler. See FAQ #10 for more details. + Yes. Although I don't provide pre-compiled binaries, you can get a + Sun-style package from www.sunfreeware.com. -20. "When I try to compile top under Solaris 2.6 using gcc I get compile - time errors. There appear to be problems with the include files, - such as 'u_rlimit has incomplete type' and/or 'u_saved_rlimit has - incomplete type'. I've already run fixinc.svr4 as per FAQ #13. - Why didn't that fix it?" +20. Under Solaris 2, when I type "make", the system says "language + optional software package not installed." What's going on? -Only top versions 3.5 and later are compatible with Solaris 2.6. Make -sure you are using the most up-to-date version. Earlier beta release -copies of version 3.5 had additional problems when compiled with gcc. -Retrieve the official version 3.5 (non-beta) release from one of the -sites listed in FAQ #1 or FAQ #2. + You tried to compile with /usr/ucb/cc. Make sure /usr/ucb is not on + your path. Furthermore, you do not have a Sun compiler installed on + your system. You need a compiler to make top. Either Sun's C compiler + or the Gnu C compiler will work fine. +21. Under Solaris 2, when I run top as root it only shows root processes, + or it only shows processes with a PID less than 1000. It refuses to + show anything else. What do I do? -SCO PROBLEMS + 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. -21. "When I try to run Configure, it complains about a syntax error." +22. Under Solaris 2, I compiled top using what I am sure is the correct + compiler but when I try to run it it complains about missing dynamic + libraries. What is wrong? -Some versions of SCO's csh do not understand the syntax "$<". Earlier -releases of top depended on this syntax to read input from the installer's -terminal during the installation process. Version 3.5 fixes this. + Check to see if you have LD_LIBRARY_PATH defined in your shell. If + you do, make sure that /usr/ucblib is not on the path anywhere. Then + try compiling top again. +23. Under Solaris 2, when I try to run top it complains that it can't + open the library "libucb.so.1". So I changed the LIBS line in + m_sunos5.c to include -R/usr/ucblib to make sure that the dynamic + linker will look there when top runs. I figured this was just an + oversight. Was I right? -SVR42 PROBLEMS + No, you were not right. As distributed, top requires no alterations + for successful compilation and operations under any release of + Solaris 2. You probably compiled top with /usr/ucb/cc instead of the + real C compiler. See FAQ 22 for more details. -22. "The memory display doesn't work right. Why?" +24. On my 64-bit system some processes show up with incorrect information + (such as zero memory). -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. + If you are running a 64-bit system, then you need to make sure that + you are running the 64-bit top binary. Top's configure script + attempts to detect 64-bit systems, and will automatically generate + both 32-bit and 64-bit binaries on such systems. If you use or + install the 32-bit binary on a 64-bit system top will still run but + will not produce the correct results. This will also happen if you + configure your distribution on a 32-bit system then compile with that + configuration on a 64-bit system. You must configure and compile on + the same system. For Sparc systems the 32-bit binary will be created + in the subdirectory "sparcv7" and the 64-bit binary will be created + in the subdirectory "sparcv9". For Intel systems the directories will + be "i386" (32-bit) and "amd64" (64-bit). In all cases a copy of + /usr/lib/isaexec is made in the main directory and called "top". This + program will choose the correct binary to run from one of these + subdirectories. See isaexec(3c) for more details. +25. Can I install both 32-bit and 64-bit binaries on a central file + server and have machines which mount it automatically use the correct + one? -STILL STUCK + Yes. If you configure and compile on a 64-bit system, top's configure + script and makefile will automatically create both 32-bit and 64-bit + binaries. The "install" rule in the makefile will install these + binaries in subdirectories of /usr/local/bin appropriate to the + architecture (sparcv7/sparcv9 or i386/amd64) then create a copy of + /usr/lib/isaexec named "top" in /usr/local/bin to ensure that the + appropriate is run when a user types "top". If you make sure that you + configure and compile on a 64-bit system, then "make install" will do + the right thing. -23. I'm still stuck. To whom do I report problems with top?" +26. This version of top show less available swap space than previous + versions. Why does it no longer match the output of the swap summary + produced with "swap -s"? -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. + Starting with version 3.6 of top, the amount of swap space reported + by top has been changed to reflect only disk-based swap space. The + swap summary produced with "swap -s" also includes memory-based swap + space. This changed was made for several reasons. It makes the + display under Solaris more like those of other operating systems. The + display is more what users expect (except those used to previous + versions of top). Most importantly, "swap -s" gets its data via an + undocumented system interface. Now that top no longer displays that + data it can use publically documented and maintained system + interfaces to retrieve its data. -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. + + SVR4-DERIVED PROBLEMS + +27. When I run top on my SVR4-derived operating system, it displays all + the system information at the top but does not display any process + information (or only displays process information for my own + processes). Yet when I run it as root, everything works fine. What's + wrong? + + Your system probably uses the pseudo file system "/proc", which is by + default only accessible by root. Top needs to be installed setuid + root on such systems if it is going to function correctly for normal + users. + + + SVR42 PROBLEMS + +28. The memory display doesn't work right. Why? + + This is a known bug with the svr42 module. The problem has been + traced down to a potential bug in the "mem" driver. The author of the + svr42 module is working on a fix. + + + STILL STUCK + +29. I'm still stuck. To whom do I report problems with top? + + The most common problems are caused by top's sensitivity to internal + kernel data structures. So make sure that you are using the right + include files, and make sure that you test out top on the same + machine where you compiled it. Sun's BSD Source Compatability Mode is + also a common culprit. Make sure you aren't using either /usr/ucb/cc + or any of the libraries in /usr/ucblib. Finally, make sure you are + using the correct module. If there does not appear to be one + appropriate for your computer, then top probably will not work on + your system. + + If after reading all of this file and checking everything you can you + are still stuck, then please use SourceForge to submit a support + request or a bug. Top is supported by the SourceForge project named + "unixtop". On SourceForge you will find defect tracking, a mailing + list, and on-line forums. You can also contact the author through + SourceForge. + And this is the diff against the stock 3.8b1 code: diff --git a/contrib/top/commands.c b/contrib/top/commands.c index e93fb54..f9f95d4 100644 --- a/contrib/top/commands.c +++ b/contrib/top/commands.c @@ -31,6 +31,10 @@ */ /* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 */ @@ -643,6 +647,36 @@ cmd_idle(globalstate *gstate) } int +cmd_weightedcpu(globalstate *gstate) + +{ + gstate->pselect.wcpu = !gstate->pselect.wcpu; + message_error(" Displaying %sCPU.", + gstate->pselect.wcpu ? "W" : ""); + return CMD_REFRESH; +} + +int +cmd_jails(globalstate *gstate) + +{ + gstate->pselect.jail = !gstate->pselect.jail; + message_error(" %sisplaying jail ID.", + gstate->pselect.jail ? "D" : "Not d"); + return CMD_REFRESH; +} + +int +cmd_thisprocess(globalstate *gstate) + +{ + gstate->pselect.self = !gstate->pselect.self; + message_error(" %sisplaying self.", + gstate->pselect.self ? "D" : "Not d"); + return CMD_REFRESH; +} + +int cmd_displays(globalstate *gstate) { @@ -884,6 +918,8 @@ cmd_system(globalstate *gstate) { gstate->pselect.system = !gstate->pselect.system; display_header(2); + message_error(" %showing system processes.", + gstate->pselect.system ? "S" : "Not s"); return CMD_REFRESH; } @@ -895,8 +931,11 @@ cmd_threads(globalstate *gstate) { gstate->pselect.threads = !gstate->pselect.threads; display_header(2); + message_error(" Displaying threads as %s.", + gstate->pselect.threads ? "seperately" : "a count"); return CMD_REFRESH; } + message_error(" Thread support not enabled."); return CMD_NA; } @@ -909,9 +948,10 @@ command command_table[] = { { ' ', cmd_update, "update screen" }, { '?', cmd_help, "help; show this text" }, { 'h', cmd_help, NULL }, - { 'C', cmd_color, "toggle the use of color" }, + { 'C', cmd_weightedcpu, "toggle the displaying of weighted CPU percentage" }, + { 'E', cmd_color, "toggle the use of color" }, { 'H', cmd_threads, "toggle the display of individual threads" }, - { 't', cmd_threads, NULL }, + { 'j', cmd_jails, "toggle the displaying of jail ID" }, { 'M', cmd_order_mem, "sort by memory usage" }, { 'N', cmd_order_pid, "sort by process id" }, { 'P', cmd_order_cpu, "sort by CPU usage" }, @@ -920,13 +960,13 @@ command command_table[] = { { 'U', cmd_useruid, "toggle the display of usernames or uids" }, { 'c', cmd_command, "display processes by command name" }, { 'd', cmd_displays, "change number of displays to show" }, - { 'f', cmd_cmdline, "toggle the display of full command paths" }, + { 'a', cmd_cmdline, "toggle the display of full command paths" }, { 'i', cmd_idle, "toggle the displaying of idle processes" }, { 'I', cmd_idle, NULL }, #ifdef ENABLE_KILL { 'k', cmd_kill, "kill processes; send a signal to a list of processes" }, #endif - { 'm', cmd_mode, "toggle between display modes" }, + { 'm', cmd_mode, "toggle between 'cpu' and 'io' modes" }, { 'n', cmd_number, "change number of processes to display" }, { '#', cmd_number, NULL }, { 'o', cmd_order, "specify sort order (see below)" }, @@ -935,6 +975,7 @@ command command_table[] = { { 'r', cmd_renice, "renice a process" }, #endif { 's', cmd_delay, "change number of seconds to delay between updates" }, + { 't', cmd_thisprocess, "toggle the display of this process" }, { 'u', cmd_user, "display processes for only one user (+ selects all users)" }, { '\0', NULL, NULL }, }; diff --git a/contrib/top/display.c b/contrib/top/display.c index 2330ca4..019d6de 100644 --- a/contrib/top/display.c +++ b/contrib/top/display.c @@ -31,6 +31,10 @@ */ /* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 */ @@ -88,6 +92,7 @@ static int display_width = MAX_COLS; static int x_lastpid = X_LASTPID; static int y_lastpid = Y_LASTPID; static int x_loadave = X_LOADAVE; +static int x_loadave_nompid = X_LOADAVE_NOMPID; static int y_loadave = Y_LOADAVE; static int x_minibar = X_MINIBAR; static int y_minibar = Y_MINIBAR; @@ -99,6 +104,8 @@ static int x_cpustates = X_CPUSTATES; static int y_cpustates = Y_CPUSTATES; static int x_kernel = X_KERNEL; static int y_kernel = Y_KERNEL; +static int x_brkdn = X_BRKDN; +static int y_brkdn = Y_BRKDN; static int x_mem = X_MEM; static int y_mem = Y_MEM; static int x_swap = X_SWAP; @@ -110,6 +117,8 @@ static int x_idlecursor = X_IDLECURSOR; static int y_idlecursor = Y_IDLECURSOR; static int y_procs = Y_PROCS; +static int Header_lines = 7; /* XXXEG */ + /* buffer and colormask that describes the content of the screen */ /* these are singly dimensioned arrays -- the row boundaries are determined on the fly. @@ -143,9 +152,13 @@ static int num_kernel; static int *lprocstates; static int *lcpustates; +static int *lmemory; +static int *lswap; +static int num_cpus; static int *cpustate_columns; static int cpustate_total_length; +static int cpustates_column; static int header_status = Yes; @@ -672,6 +685,8 @@ display_resize() /* calculate the current dimensions */ /* if operating in "dumb" mode, we only need one line */ top_lines = smart_terminal ? screen_length : 1; + if (top_lines < 0) + top_lines = 0; /* we don't want more than MAX_COLS columns, since the machine-dependent modules make static allocations based on MAX_COLS and we don't want @@ -781,9 +796,23 @@ display_init(struct statics *statics) y_idlecursor++; y_procs++; } + lswap = (int *)malloc(num_swap * sizeof(int)); /* call resize to do the dirty work */ top_lines = display_resize(); + num_cpus = statics->ncpus; + cpustates_column = 5; /* CPU: */ + if (num_cpus != 1) + cpustates_column += 2; /* CPU 0: */ + for (i = num_cpus; i > 9; i /= 10) + cpustates_column++; + y_kernel += num_cpus - 1; + y_mem += num_cpus - 1; + y_swap += num_cpus - 1; + y_header += num_cpus - 1; + y_idlecursor += num_cpus - 1; + y_message += num_cpus - 1; + y_procs += num_cpus - 1; /* only do the rest if we need to */ if (top_lines > -1) @@ -795,7 +824,7 @@ display_init(struct statics *statics) cpustate_names = statics->cpustate_names; num_cpustates = string_count(cpustate_names); - lcpustates = (int *)calloc(num_cpustates, sizeof(int)); + lcpustates = (int *)calloc(num_cpustates, sizeof(int) * num_cpus); cpustate_columns = (int *)calloc(num_cpustates, sizeof(int)); memory_names = statics->memory_names; num_memory = string_count(memory_names); @@ -957,35 +986,32 @@ u_minibar(int (*formatter)(char *, int)) display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); } -static int uptime_days; -static int uptime_hours; -static int uptime_mins; -static int uptime_secs; - void i_uptime(time_t *bt, time_t *tod) { + int uptime_days; time_t uptime; + char t[10]; + struct tm *tm; if (*bt != -1) { uptime = *tod - *bt; uptime += 30; uptime_days = uptime / 86400; - uptime %= 86400; - uptime_hours = uptime / 3600; - uptime %= 3600; - uptime_mins = uptime / 60; - uptime_secs = uptime % 60; + + tm = gmtime(&uptime); + strftime(t, sizeof(t), "%H:%M:%S", tm); /* * Display the uptime. */ + x_uptime = (smart_terminal ? screen_width : 79) - 29; display_fmt(x_uptime, y_uptime, 0, 0, - " up %d+%02d:%02d:%02d", - uptime_days, uptime_hours, uptime_mins, uptime_secs); + " up %d+%s", + uptime_days, t); } } @@ -1001,18 +1027,9 @@ void i_timeofday(time_t *tod) { - /* - * Display the current time. - * "ctime" always returns a string that looks like this: - * - * Sun Sep 16 01:03:52 1973 - * 012345678901234567890123 - * 1 2 - * - * We want indices 11 thru 18 (length 8). - */ - int x; + char t[10]; + struct tm *tm; /* where on the screen do we start? */ x = (smart_terminal ? screen_width : 79) - 8; @@ -1024,7 +1041,9 @@ i_timeofday(time_t *tod) } /* display it */ - display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11])); + tm = localtime(tod); + strftime(t, sizeof(t), "%H:%M:%S", tm); + display_write(x, 0, 0, 1, t); } static int ltotal = 0; @@ -1095,6 +1114,7 @@ u_procstates(int total, int *brkdn, int threads) } } +#ifdef no_more /* * *_cpustates(states, names) - print the cpu state percentages */ @@ -1125,6 +1145,7 @@ cpustates_tag() x_cpustates = strlen(use); return(use); } +#endif void i_cpustates(int *states) @@ -1135,16 +1156,24 @@ i_cpustates(int *states) char *thisname; int *colp; int color = 0; + int cpu; + char scpu[10]; #ifdef ENABLE_COLOR int *cidx = cpustate_cidx; #endif +for (cpu = 0; cpu < num_cpus; cpu++) { /* initialize */ names = cpustate_names; colp = cpustate_columns; /* print tag */ - display_write(0, y_cpustates, 0, 0, cpustates_tag()); + if (num_cpus == 1) + strcpy(scpu, "CPU: "); + else + sprintf(scpu, "CPU %d: ", cpu); + display_write(0, y_cpustates + cpu, 0, 0, scpu); + x_cpustates = strlen(scpu) + 1; /* now walk thru the names and print the line */ while ((thisname = *names++) != NULL) @@ -1160,7 +1189,8 @@ i_cpustates(int *states) #endif /* if percentage is >= 1000, print it as 100% */ - display_fmt(x_cpustates + *colp, y_cpustates, + display_fmt( + x_cpustates + *colp, y_cpustates + cpu, color, 0, (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"), ((float)value)/10., @@ -1172,9 +1202,10 @@ i_cpustates(int *states) colp++; states++; } +} /* copy over values into "last" array */ - memcpy(lcpustates, states, num_cpustates * sizeof(int)); + memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus); } void @@ -1182,17 +1213,20 @@ u_cpustates(int *states) { int value; - char **names = cpustate_names; + char **names; char *thisname; int *lp; int *colp; int color = 0; + int cpu; #ifdef ENABLE_COLOR int *cidx = cpustate_cidx; #endif +for (cpu = 0; cpu < num_cpus; cpu++) { lp = lcpustates; colp = cpustate_columns; + names = cpustate_names; /* we could be much more optimal about this */ while ((thisname = *names++) != NULL) @@ -1212,7 +1246,7 @@ u_cpustates(int *states) #endif /* if percentage is >= 1000, print it as 100% */ - display_fmt(x_cpustates + *colp, y_cpustates, color, 0, + display_fmt(x_cpustates + *colp, y_cpustates + cpu, color, 0, (value >= 1000 ? "%4.0f" : "%4.1f"), ((double)value)/10.); @@ -1230,18 +1264,29 @@ u_cpustates(int *states) colp++; } } +} void z_cpustates() { register int i = 0; - register char **names = cpustate_names; + register char **names; register char *thisname; register int *lp; + int cpu; + char scpu[10]; + +for (cpu = 0; cpu < num_cpus; cpu++) { + names = cpustate_names; /* print tag */ - display_write(0, y_cpustates, 0, 0, cpustates_tag()); + if (num_cpus == 1) + strcpy(scpu, "CPU: "); + else + sprintf(scpu, "CPU %d: ", cpu); + display_write(0, y_cpustates + cpu, 0, 0, scpu); + x_cpustates = strlen(scpu) + 1; while ((thisname = *names++) != NULL) { @@ -1251,10 +1296,11 @@ z_cpustates() thisname); } } +} /* fill the "last" array with all -1s, to insure correct updating */ lp = lcpustates; - i = num_cpustates; + i = num_cpustates * num_cpus; while (--i >= 0) { *lp++ = -1; @@ -1301,7 +1347,7 @@ void i_memory(long *stats) { - display_write(0, y_mem, 0, 0, "Memory: "); + display_write(0, y_mem, 0, 0, "Mem: "); /* format and print the memory summary */ summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); @@ -1447,6 +1493,33 @@ u_message(struct timeval *now) static int header_length; /* + * Trim a header string to the current display width and return a newly + * allocated area with the trimmed header. + */ + +char * +trim_header(text) + +char *text; + +{ + char *s; + int width; + + s = NULL; + width = display_width; + header_length = strlen(text); + if (header_length >= width) { + s = malloc((width + 1) * sizeof(char)); + if (s == NULL) + return (NULL); + strncpy(s, text, width); + s[width] = '\0'; + } + return (s); +} + +/* * *_header(text) - print the header for the process area * * Assumptions: cursor is on the previous line and lastline is consistent @@ -1456,16 +1529,21 @@ void i_header(char *text) { + char *s; int header_color = 0; #ifdef ENABLE_COLOR header_color = color_test(header_cidx, 0); #endif + s = trim_header(text); + if (s != NULL) + text = s; header_length = strlen(text); if (header_status) { display_write(x_header, y_header, header_color, 1, text); } + free(s); } /*ARGSUSED*/ diff --git a/contrib/top/layout.h b/contrib/top/layout.h index 46bf78e..d41bb05 100644 --- a/contrib/top/layout.h +++ b/contrib/top/layout.h @@ -31,6 +31,10 @@ */ /* + * $FreeBSD$ + */ + +/* * Top - a top users display for Unix * * This file defines the default locations on the screen for various parts @@ -43,6 +47,7 @@ #define X_LASTPIDWIDTH 13 #define X_LOADAVE 27 #define Y_LOADAVE 0 +#define X_LOADAVE_NOMPID 15 #define X_LOADAVEWIDTH 7 #define X_MINIBAR 50 #define Y_MINIBAR 0 diff --git a/contrib/top/loadavg.h b/contrib/top/loadavg.h index c73fc7d..8090520 100644 --- a/contrib/top/loadavg.h +++ b/contrib/top/loadavg.h @@ -79,7 +79,7 @@ typedef double pctcpu; #endif #ifdef FIXED_LOADAVG - typedef long load_avg; + typedef fixpt_t load_avg; # define loaddouble(la) ((double)(la) / FIXED_LOADAVG) # define intload(i) ((int)((i) * FIXED_LOADAVG)) #else diff --git a/contrib/top/machine.h b/contrib/top/machine.h index f727d3b..2745b86 100644 --- a/contrib/top/machine.h +++ b/contrib/top/machine.h @@ -31,6 +31,10 @@ */ /* + * $FreeBSD$ + */ + +/* * 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. @@ -56,6 +60,8 @@ struct statics char **kernel_names; /* optional */ time_t boottime; /* optional */ int modemax; /* optional */ + int ncpus; + int maxcpus; struct { unsigned int fullcmds : 1; unsigned int idle : 1; @@ -85,6 +91,8 @@ struct system_info int *kernel; long *memory; long *swap; + struct timeval boottime; + int ncpus; }; /* cpu_states is an array of percentages * 10. For example, @@ -99,6 +107,7 @@ struct system_info struct process_select { int idle; /* show idle processes */ + int self; /* show self */ int system; /* show system processes */ int fullcmd; /* show full command */ int usernames; /* show usernames */ @@ -106,6 +115,8 @@ struct process_select char *command; /* only this command (unless == NULL) */ int mode; /* select display mode (0 is default) */ int threads; /* show threads separately */ + int wcpu; /* show weighted cpu */ + int jail; /* show jail ID */ }; /* routines defined by the machine dependent module */ @@ -113,7 +124,7 @@ int machine_init(struct statics *); void get_system_info(struct system_info *); caddr_t get_process_info(struct system_info *, struct process_select *, int); char *format_header(char *); -char *format_next_process(caddr_t, char *(*)(int)); +char *format_next_process(struct process_select *sel, caddr_t, char *(*)(int)); int proc_owner(int); #ifdef HAVE_FORMAT_PROCESS_HEADER diff --git a/contrib/top/screen.c b/contrib/top/screen.c index 398a858..a059bf9 100644 --- a/contrib/top/screen.c +++ b/contrib/top/screen.c @@ -31,6 +31,10 @@ */ /* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 */ @@ -204,7 +208,9 @@ screen_getsize() #endif /* TIOCGSIZE */ #endif /* TIOCGWINSZ */ - (void) strcpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1)); + (void) strncpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1), + sizeof(lower_left) - 1); + lower_left[sizeof(lower_left) - 1] = '\0'; } int @@ -315,7 +321,8 @@ screen_readtermcap(int interactive) PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0; /* set convenience strings */ - (void) strcpy(home, tgoto(tc_cursor_motion, 0, 0)); + (void) strncpy(home, tgoto(tc_cursor_motion, 0, 0), sizeof(home) - 1); + home[sizeof(home) - 1] = '\0'; /* (lower_left is set in screen_getsize) */ /* get the actual screen size with an ioctl, if needed */ diff --git a/contrib/top/sigconv.awk b/contrib/top/sigconv.awk index 4f0ef60..18543cc 100644 --- a/contrib/top/sigconv.awk +++ b/contrib/top/sigconv.awk @@ -28,6 +28,8 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# $FreeBSD$ + # # Awk script converts an include file with definitions for signal names # in to a predefined array that associates the signal numbers to the names. @@ -45,7 +47,7 @@ BEGIN { print "struct sigdesc sigdesc[] = {" } -/^#define[ \t][ \t]*SIG[A-Z]/ { +/^#define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/ { j = sprintf("%d", $3); if (siglist[j] != "") next; @@ -57,7 +59,7 @@ BEGIN { siglist[j] = sprintf("\"%s\",\t%2d", \ substr(str, 4), j); } -/^#[ \t]*define[ \t][ \t]*SIG[A-Z]/ { +/^#[ \t]*define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/ { j = sprintf("%d", $4); if (siglist[j] != "") next; @@ -66,10 +68,10 @@ BEGIN { if (nsig < j) nsig = j; - siglist[j] = sprintf("\"%s\",\t%2d", \ + siglist[j] = sprintf("\"%s\",\t%2d,", \ substr(str, 4), j); } -/^#[ \t]*define[ \t][ \t]*_SIG[A-Z]/ { +/^#[ \t]*define[ \t][ \t]*_SIG[A-Z]+[0-9]*[ \t]/ { j = sprintf("%d", $4); if (siglist[j] != "") next; diff --git a/contrib/top/top.c b/contrib/top/top.c index a9dccd8..077b887 100644 --- a/contrib/top/top.c +++ b/contrib/top/top.c @@ -30,6 +30,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* + * $FreeBSD$ + */ + char *copyright = "Copyright (c) 1984 through 2008, William LeFebvre"; @@ -40,9 +44,10 @@ char *copyright = */ #include "os.h" +#include <errno.h> #include <signal.h> -#include <setjmp.h> #include <ctype.h> +#include <setjmp.h> #include <sys/types.h> #include <sys/uio.h> #include <unistd.h> @@ -113,6 +118,7 @@ static jmp_buf jmp_int; /* globals */ char *myname = "top"; +int pcpu_stats = No; void quit(int status) @@ -333,15 +339,15 @@ do_arguments(globalstate *gstate, int ac, char **av) optind = 1; #ifdef HAVE_GETOPT_LONG - while ((i = getopt_long(ac, av, "CDSITabcinqtuvs:d:U:o:m:", longopts, NULL)) != -1) + while ((i = getopt_long(ac, av, "CDEHSITabcijnqtuvs:d:U:o:m:P", longopts, NULL)) != -1) #else - while ((i = getopt(ac, av, "CDSITabcinqtuvs:d:U:o:m:")) != EOF) + while ((i = getopt(ac, av, "CDEHSITabcijnqtuvs:d:U:o:m:P")) != EOF) #endif { switch(i) { #ifdef ENABLE_COLOR - case 'C': + case 'E': gstate->use_color = !gstate->use_color; break; #endif @@ -414,8 +420,29 @@ do_arguments(globalstate *gstate, int ac, char **av) break; case 'm': - i = atoi(optarg); - gstate->pselect.mode = i; + if (strcmp(optarg, "io") == 0) { + gstate->pselect.mode = DISP_IO; + } else if (strcmp(optarg, "cpu") == 0) { + gstate->pselect.mode = DISP_CPU; + } else { + fprintf(stderr, + "%s: warning: `-m' option can only take args " + "'io' or 'cpu'\n", + myname); + exit(1); + } + break; + + case 'H': + gstate->pselect.threads = !gstate->pselect.threads; + break; + + case 'C': + gstate->pselect.wcpu = !gstate->pselect.wcpu; + break; + + case 'j': + gstate->pselect.jail = !gstate->pselect.jail; break; case 'S': @@ -437,7 +464,7 @@ do_arguments(globalstate *gstate, int ac, char **av) break; case 't': - gstate->pselect.threads = !gstate->pselect.threads; + gstate->pselect.self = !gstate->pselect.self; break; case 'q': /* be quick about it */ @@ -453,10 +480,14 @@ do_arguments(globalstate *gstate, int ac, char **av) } break; + case 'P': + pcpu_stats = Yes; + break; + default: - fprintf(stderr, "\ -Top version %s\n\ -Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n", + fprintf(stderr, +"Top version %s\n" +"Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n", version_string(), myname); exit(EX_USAGE); } @@ -552,7 +583,8 @@ do_display(globalstate *gstate) i_header(hdr); for (i = 0; i < active_procs; i++) { - i_process(i, format_next_process(processes, gstate->get_userid)); + i_process(i, + format_next_process(&(gstate->pselect), processes, gstate->get_userid)); } i_endscreen(); if (gstate->smart_terminal) @@ -563,8 +595,8 @@ do_display(globalstate *gstate) else { u_loadave(system_info.last_pid, system_info.load_avg); - u_uptime(&(gstate->statics->boottime), &curr_time); i_timeofday(&curr_time); + u_uptime(&(gstate->statics->boottime), &curr_time); u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); u_cpustates(system_info.cpustates); u_kernel(system_info.kernel); @@ -574,7 +606,8 @@ do_display(globalstate *gstate) u_header(hdr); for (i = 0; i < active_procs; i++) { - u_process(i, format_next_process(processes, gstate->get_userid)); + u_process(i, + format_next_process(&(gstate->pselect), processes, gstate->get_userid)); } u_endscreen(); } @@ -609,6 +642,7 @@ do_command(globalstate *gstate) struct timeval now; fd_set readfds; unsigned char ch; + int sel_ret = 0; /* calculate new refresh time */ gstate->refresh = gstate->now; @@ -635,7 +669,10 @@ do_command(globalstate *gstate) FD_SET(STDIN_FILENO, &readfds); /* wait for something to read or time out */ - if (select(32, &readfds, NULL, NULL, &wait) > 0) + sel_ret = select(2, &readfds, NULL, NULL, &wait); + if (sel_ret < 0 && errno != EINTR) + quit(0); + if (sel_ret > 0) { /* read it */ if (read(STDIN_FILENO, &ch, 1) != 1) @@ -758,11 +795,15 @@ main(int argc, char *argv[]) /* preset defaults for process selection */ gstate->pselect.idle = Yes; + gstate->pselect.self = -1; + gstate->pselect.threads = No; + gstate->pselect.wcpu = 1; + gstate->pselect.jail = No; gstate->pselect.system = No; gstate->pselect.fullcmd = No; gstate->pselect.command = NULL; gstate->pselect.uid = -1; - gstate->pselect.mode = 0; + gstate->pselect.mode = DISP_CPU; /* use a large buffer for stdout */ #ifdef HAVE_SETVBUF @@ -808,6 +849,8 @@ main(int argc, char *argv[]) { exit(EX_SOFTWARE); } + if (pcpu_stats == No) + statics.ncpus = 1; /* create a helper list of sort order names */ gstate->order_namelist = string_list(statics.order_names); @@ -881,6 +924,7 @@ main(int argc, char *argv[]) } /* check for infinity and for overflowed screen */ + gstate->topn = gstate->max_topn; if (gstate->topn == Infinity) { gstate->topn = INT_MAX; @@ -920,6 +964,7 @@ main(int argc, char *argv[]) quit(EX_OSERR); /*NOTREACHED*/ } + gstate->topn = gstate->max_topn; /* set up for a full redraw, and get the current line count */ gstate->fulldraw = Yes; diff --git a/contrib/top/top.h b/contrib/top/top.h index 24b1abb..ca7799a 100644 --- a/contrib/top/top.h +++ b/contrib/top/top.h @@ -31,6 +31,10 @@ */ /* + * $FreeBSD$ + */ + +/* * Top - a top users display for Berkeley Unix * * General (global) definitions @@ -77,4 +81,8 @@ struct ext_decl { void gettime(struct timeval *); void quit(int); +extern int pcpu_stats; + +enum displaymodes { DISP_CPU = 0, DISP_IO, DISP_MAX }; + #endif /* _TOP_H_ */ diff --git a/contrib/top/username.c b/contrib/top/username.c index 9866bff..6a0c9a1 100644 --- a/contrib/top/username.c +++ b/contrib/top/username.c @@ -31,6 +31,10 @@ */ /* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 */ @@ -47,7 +51,9 @@ #include "os.h" +#include <sys/types.h> #include <pwd.h> +#include <utmp.h> #include "top.h" #include "utils.h" @@ -66,7 +72,7 @@ struct hash_data { int uid; - char name[MAXLOGNAME]; /* big enough? */ + char name[UT_NAMESIZE + 1]; /* big enough? */ time_t expire; }; @@ -114,7 +120,7 @@ username(int uid) { if ((pw = getpwuid(uid)) != NULL) { - strncpy(data->name, pw->pw_name, MAXLOGNAME-1); + strncpy(data->name, pw->pw_name, UT_NAMESIZE); data->expire = now + EXPIRETIME; dprintf("username: updating %d with %s, expires %d\n", data->uid, data->name, data->expire); diff --git a/contrib/top/utils.c b/contrib/top/utils.c index 856005a..0875650 100644 --- a/contrib/top/utils.c +++ b/contrib/top/utils.c @@ -31,6 +31,10 @@ */ /* + * $FreeBSD$ + */ + +/* * Top users/processes display for Unix * Version 3 */ @@ -454,9 +458,13 @@ percentages(int cnt, int *out, long *new, long *old, long *diffs) /* 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); + + /* Do not divide by 0. Causes Floating point exception */ + if (total_change != 0) { + 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 */ diff --git a/usr.bin/top/Makefile b/usr.bin/top/Makefile index f908b9e..4a317ad 100644 --- a/usr.bin/top/Makefile +++ b/usr.bin/top/Makefile @@ -7,8 +7,8 @@ PROG= top SRCS= commands.c display.c machine.c screen.c top.c \ username.c utils.c version.c color.c hash.c SRCS+= sigdesc.h config.h -CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER -CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I. +CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER -DSIGWINCH -DHAS_SHOWTHREADS +CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I. -Wall -g # # The table size should be a prime number approximately twice as @@ -45,7 +45,7 @@ config.h: config.h.in @${ECHO} Making config.h from config.h.in @sed \ -e 's/@DEFAULT_TOPN@/30/' \ - -e 's/@DEFAULT_DELAY@/5/' \ + -e 's/@DEFAULT_DELAY@/2/' \ -e 's/@HAVE_GETOPT_LONG@/1/' \ -e 's/@ENABLE_KILL@/1/' \ < config.h.in > config.h @@ -55,7 +55,7 @@ top.1.local: top.1.in @${ECHO} Making top.1.local from top.1.in @sed \ -e 's/@DEFAULT_TOPN@/30/' \ - -e 's/@DEFAULT_DELAY@/5/' \ + -e 's/@DEFAULT_DELAY@/2/' \ -e 's/@HAVE_GETOPT_LONG@/1/' \ -e 's/@ENABLE_KILL@/1/' \ < ${TOPDIR}/top.1.in > top.1.local diff --git a/usr.bin/top/machine.c b/usr.bin/top/machine.c index e26bca7..46ec54a 100644 --- a/usr.bin/top/machine.c +++ b/usr.bin/top/machine.c @@ -31,6 +31,10 @@ */ /* + * $FreeBSD$ + */ + +/* * top - a top users display for Unix * * SYNOPSIS: For FreeBSD 5.x, 6.x, 7.x, 8.x @@ -41,6 +45,7 @@ * Order support hacked in from top-3.5beta6/machine/m_aix41.c * by Monte Mitzelfelt * Ported to FreeBSD 5.x and higher by William LeFebvre + * Updates for the import in FreeBSD 7 by Edwin Groothuis. * * AUTHOR: Christos Zoulas <christos@ee.cornell.edu> * Steven Wallace <swallace@freebsd.org> @@ -59,6 +64,7 @@ #include <nlist.h> #include <math.h> #include <kvm.h> +#include <paths.h> #include <pwd.h> #include <sys/errno.h> #include <sys/sysctl.h> @@ -240,21 +246,27 @@ static struct sysctl_mib mibs[] = { #define VFS_BUFSPACE 20 { "kern.cp_time" }, #define K_CP_TIME 21 + { "kern.cp_times" }, +#define K_CP_TIMES 22 #ifdef HAS_SHOWTHREADS { "kern.proc.all" }, #else { "kern.proc.proc" }, #endif -#define K_PROC 22 +#define K_PROC 23 +#define K_LASTPID 24 + { "kern.lastpid" }, { NULL } }; +/* Local pointer */ +static struct statics *statics; /* these are for calculating cpu state percentages */ -static long cp_time[CPUSTATES]; -static long cp_old[CPUSTATES]; -static long cp_diff[CPUSTATES]; +static long *cp_time; /* num_cpus * CPUSTATES */ +static long *cp_old; /* num_cpus * CPUSTATES */ +static long *cp_diff; /* num_cpus * CPUSTATES */ /* these are for detailing the process states */ @@ -267,7 +279,7 @@ char *procstatenames[] = { /* these are for detailing the cpu states */ -int cpu_states[CPUSTATES]; +int *cpu_states; char *cpustatenames[] = { "user", "nice", "system", "interrupt", "idle", NULL }; @@ -380,12 +392,12 @@ static int show_threads = 0; /* sorting orders. first is default */ char *ordernames[] = { - "cpu", "size", "res", "time", "pri", "io", "pid", NULL + "cpu", "size", "res", "time", "pri", "io", "pid", "jid", NULL }; /* compare routines */ int proc_compare(), compare_size(), compare_res(), compare_time(), - compare_prio(), compare_io(), compare_pid(); + compare_prio(), compare_io(), compare_pid(), compare_jid(); int (*proc_compares[])() = { proc_compare, @@ -395,6 +407,7 @@ int (*proc_compares[])() = { compare_prio, compare_io, compare_pid, + compare_jid, NULL }; @@ -472,7 +485,7 @@ swap_getdata(long long *retavail, long long *retfree) * "size" is the size of the buffer (and the object to retrieve). * Return 0 on success, -1 on any kind of failure. */ - +#ifdef notmore static int getkval(unsigned long offset, int *ptr, int size) @@ -486,6 +499,7 @@ getkval(unsigned long offset, int *ptr, int size) } return(-1); } +#endif int get_sysctl_mibs() @@ -548,6 +562,13 @@ fmt_pid(char *buf, int sz, struct kinfo_proc *pp) { return snprintf(buf, sz, "%6d", PP(pp, pid)); } + +int +fmt_jid(char *buf, int sz, struct kinfo_proc *pp) + +{ + return snprintf(buf, sz, "%3d", pp->ki_jid); +} int fmt_username(char *buf, int sz, struct kinfo_proc *pp) @@ -699,8 +720,8 @@ fmt_command(char *buf, int sz, struct kinfo_proc *pp) int inmem; char cmd[MAX_COLS]; char *bufp; - struct pargs pargs; - int len; + char **args; + int argc; #if OSMAJOR <= 4 inmem = (PP(pp, flag) & P_INMEM); @@ -711,37 +732,21 @@ fmt_command(char *buf, int sz, struct kinfo_proc *pp) if (show_fullcmd && inmem) { /* get the pargs structure */ - if (getkval((unsigned long)PP(pp, args), (int *)&pargs, sizeof(pargs)) != -1) + if ((args = kvm_getargv(kd, pp, sz)) != NULL) { - /* determine workable length */ - if ((len = pargs.ar_length) >= MAX_COLS) - { - len = MAX_COLS - 1; - } - - /* get the string from that */ - if (len > 0 && getkval((unsigned long)PP(pp, args) + - sizeof(pargs.ar_ref) + - sizeof(pargs.ar_length), - (int *)cmd, len) != -1) - { - /* successfull retrieval: now convert nulls in to spaces */ - bufp = cmd; - while (len-- > 0) - { - if (*bufp == '\0') - { - *bufp = ' '; - } - bufp++; - } - - /* null terminate cmd */ - *--bufp = '\0'; - - /* format cmd as our answer */ - return snprintf(buf, sz, "%s", cmd); - } + /* successfull retrieval: now convert nulls in to spaces */ + bufp = cmd; + cmd[0] = '\0'; + argc = 0; + while (args[argc] != NULL) + { + if (cmd[0] != '\0') + strcat(cmd, " "); + strcat(cmd, args[argc++]); + } + + /* format cmd as our answer */ + return snprintf(buf, sz, "%s", cmd); } } @@ -795,16 +800,29 @@ int fmt_iopct(char *buf, int sz, struct kinfo_proc *pp) { - return snprintf(buf, sz, "%6.2f", (SP(pp, totalio) * 100.) / total_io); + return snprintf(buf, sz, "%6.2f%%", (SP(pp, totalio) * 100.) / total_io); } -struct proc_field proc_field[] = { +/* + * The order of proc_fields and proc_field must match! + */ + +enum proc_fields { + FIELD_PID = 0, FIELD_JID, FIELD_USERNAME, FIELD_UID, + FIELD_THR, FIELD_PRI, FIELD_NICE, FIELD_SIZE, FIELD_RES, + FIELD_STATE, FIELD_FLG, FIELD_C, FIELD_TIME, FIELD_CPU, + FIELD_WCPU, FIELD_COMMAND, FIELD_VCSW, FIELD_IVCSW, FIELD_READ, + FIELD_WRITE, FIELD_FAULT, FIELD_TOTAL, FIELD_PERCENT + +}; + +#define MAX_FIELDS 25 +struct proc_field proc_field[MAX_FIELDS] = { { "PID", 6, 1, 0, fmt_pid }, + { "JID", 3, 1, 0, fmt_jid }, { "USERNAME", 8, 0, 0, fmt_username }, -#define FIELD_USERNAME 1 { "UID", 6, 1, 0, fmt_uid }, -#define FIELD_UID 2 { "THR", 3, 1, 0, fmt_thr }, { "PRI", 3, 1, 0, fmt_pri }, { "NICE", 4, 1, 0, fmt_nice }, @@ -815,6 +833,7 @@ struct proc_field proc_field[] = { { "C", 1, 0, 0, fmt_c }, { "TIME", 6, 1, 0, fmt_time }, { "CPU", 6, 1, 0, fmt_cpu }, + { "WCPU", 6, 1, 0, fmt_cpu }, { "COMMAND", 7, 0, 0, fmt_command }, { "VCSW", 6, 1, 0, fmt_vcsw }, { "IVCSW", 6, 1, 0, fmt_ivcsw }, @@ -825,10 +844,8 @@ struct proc_field proc_field[] = { { "PERCENT", 7, 1, 0, fmt_iopct }, { NULL, 0, 0, 0, NULL } }; -#define MAX_FIELDS 24 static int mode0_display[MAX_FIELDS]; -static int mode0thr_display[MAX_FIELDS]; static int mode1_display[MAX_FIELDS]; int @@ -867,26 +884,35 @@ field_subst(int *fp, int old, int new) } int -machine_init(struct statics *statics) +machine_init(struct statics *_statics) { int i = 0; size_t len; int *ip; + statics = _statics; /* Keep a local copy */ + struct timeval boottime; - len = sizeof(smpmode); - if ((sysctlbyname("machdep.smp_active", &smpmode, &len, NULL, 0) < 0 && - sysctlbyname("smp.smp_active", &smpmode, &len, NULL, 0) < 0) || - len != sizeof(smpmode)) - { - smpmode = 0; - } - smpmode = smpmode != 0; + len = sizeof(statics->ncpus); + if (sysctlbyname("kern.smp.cpus", &(statics->ncpus), &len, NULL, 0) < 0 || + len != sizeof(statics->ncpus)) + statics->ncpus = 1; + smpmode = statics->ncpus != 1; + + len = sizeof(statics->maxcpus); + if (sysctlbyname("kern.smp.maxcpus", &(statics->maxcpus), &len, NULL, 0) < 0 || + len != sizeof(statics->maxcpus)) + statics->maxcpus = 1; + + cp_time = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long)); + cp_old = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long)); + cp_diff = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long)); + cpu_states = (int *)calloc(statics->maxcpus * CPUSTATES, sizeof(int)); /* kvm_open the active kernel: its okay if this fails */ - kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL); + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL); /* get boot time */ len = sizeof(boottime); @@ -928,7 +954,7 @@ machine_init(struct statics *statics) statics->boottime = boottime.tv_sec; statics->swap_names = swapnames; statics->order_names = ordernames; - statics->flags.warmup = 1; + statics->flags.warmup = 0; statics->modemax = 2; #ifdef HAS_SHOWTHREADS statics->flags.threads = 1; @@ -937,9 +963,10 @@ machine_init(struct statics *statics) /* we need kvm descriptor in order to show full commands */ statics->flags.fullcmds = kd != NULL; - /* set up the display indices for mode0 */ + /* set up the display indices for mode0 (DISP_CPU) */ ip = mode0_display; *ip++ = field_index("PID"); + *ip++ = field_index("JID"); *ip++ = field_index("USERNAME"); #ifdef HAS_THREADS *ip++ = field_index("THR"); @@ -950,35 +977,16 @@ machine_init(struct statics *statics) *ip++ = field_index("RES"); *ip++ = field_index("STATE"); *ip++ = field_index("FLG"); - if (smpmode) - *ip++ = field_index("C"); - *ip++ = field_index("TIME"); - *ip++ = field_index("CPU"); - *ip++ = field_index("COMMAND"); - *ip = -1; - -#ifdef HAS_SHOWTHREADS - /* set up the display indices for mode0 showing threads */ - ip = mode0thr_display; - *ip++ = field_index("PID"); - *ip++ = field_index("USERNAME"); - *ip++ = field_index("PRI"); - *ip++ = field_index("NICE"); - *ip++ = field_index("SIZE"); - *ip++ = field_index("RES"); - *ip++ = field_index("STATE"); - *ip++ = field_index("FLG"); - if (smpmode) - *ip++ = field_index("C"); + *ip++ = field_index("C"); *ip++ = field_index("TIME"); *ip++ = field_index("CPU"); *ip++ = field_index("COMMAND"); *ip = -1; -#endif - /* set up the display indices for mode1 */ + /* set up the display indices for mode1 (DISP_IO) */ ip = mode1_display; *ip++ = field_index("PID"); + *ip++ = field_index("JID"); *ip++ = field_index("USERNAME"); *ip++ = field_index("VCSW"); *ip++ = field_index("IVCSW"); @@ -1032,9 +1040,9 @@ void get_system_info(struct system_info *si) { - long total; struct timeval thistime; struct timeval timediff; + int i; /* timestamp and time difference */ gettimeofday(&thistime, 0); @@ -1049,11 +1057,19 @@ get_system_info(struct system_info *si) (void) memset(si->load_avg, 0, sizeof(si->load_avg)); } - /* get the cp_time array */ - (void)get_sysctl(K_CP_TIME, &cp_time, sizeof(cp_time)); + /* get the cp_times array ... */ + if (statics->ncpus == 1) { + (void)get_sysctl(K_CP_TIME, cp_time, sizeof(long) * CPUSTATES); + } else { + (void)get_sysctl(K_CP_TIMES, cp_time, sizeof(long) * CPUSTATES * statics->maxcpus); + } - /* convert cp_time counts to percentages */ - total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); + /* ... and convert cp_time counts to percentages */ + for (i = 0; i < statics->ncpus; i++) { + percentages(CPUSTATES, + &cpu_states[i * CPUSTATES], &cp_time[i * CPUSTATES], + &cp_old[i * CPUSTATES], &cp_diff[i * CPUSTATES]); + } /* sum memory & swap statistics */ { @@ -1136,8 +1152,7 @@ get_system_info(struct system_info *si) si->memory = memory_stats; si->swap = swap_stats; - si->last_pid = -1; - + get_sysctl(K_LASTPID, &si->last_pid, sizeof(si->last_pid)); lasttime = thistime; } @@ -1399,10 +1414,17 @@ format_process_header(struct process_select *sel, caddr_t handle, int count) /* mode & threads dictate format */ fi = display_fields = - sel->mode == 0 ? - (sel->threads == 0 ? mode0_display : mode0thr_display) : + sel->mode == DISP_CPU ? + mode0_display : mode1_display; + /* set CPU vs Weighted CPU */ + if (sel->wcpu) { + field_subst(fi, FIELD_CPU, FIELD_WCPU); + } else { + field_subst(fi, FIELD_WCPU, FIELD_CPU); + } + /* set username field correctly */ if (!sel->usernames) { @@ -1444,7 +1466,15 @@ format_process_header(struct process_select *sel, caddr_t handle, int count) p = p_header; while (*fi != -1) { + if ((*fi == FIELD_JID && sel->jail == 0) || + (*fi == FIELD_C && smpmode == 0) || + (*fi == FIELD_THR && sel->threads != 0)) { + fi++; + continue; + } + fp = &(proc_field[*fi++]); + if (fp->min_screenwidth <= cols) { p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name); @@ -1459,14 +1489,13 @@ format_process_header(struct process_select *sel, caddr_t handle, int count) static char fmt[MAX_COLS]; /* static area where result is built */ char * -format_next_process(caddr_t handle, char *(*get_userid)(int)) +format_next_process(struct process_select *sel, caddr_t handle, char *(*get_userid)(int)) { struct kinfo_proc *pp; struct handle *hp; struct proc_field *fp; int *fi; - int i; int cols; char *p; int len; @@ -1486,9 +1515,16 @@ format_next_process(caddr_t handle, char *(*get_userid)(int)) /* build output by field */ p = fmt; len = MAX_COLS; - while ((i = *fi++) != -1) + while (*fi != -1) { - fp = &(proc_field[i]); + if ((*fi == FIELD_JID && sel->jail == 0) || + (*fi == FIELD_C && smpmode == 0) || + (*fi == FIELD_THR && sel->threads != 0)) { + fi++; + continue; + } + + fp = &(proc_field[*fi++]); if (len > 0 && fp->min_screenwidth <= cols) { x = (*(fp->format))(p, len, pp); @@ -1570,6 +1606,9 @@ static unsigned char sorted_state[] = #define ORDERKEY_PID \ if ( (result = PP(p1, pid) - PP(p2, pid)) == 0) +#define ORDERKEY_JID \ + if ( (result = PP(p2, jid) - PP(p1, jid)) == 0) + /* compare_cpu - the comparison function for sorting by cpu percentage */ int @@ -1747,6 +1786,26 @@ compare_pid(struct proc **pp1, struct proc **pp2) return(result); } +/* compare_jid - the comparison function for sorting by jail id */ + +int +compare_jid(struct proc **pp1, struct proc **pp2) + +{ + struct kinfo_proc *p1; + struct kinfo_proc *p2; + int result; + + /* remove one level of indirection */ + p1 = *(struct kinfo_proc **) pp1; + p2 = *(struct kinfo_proc **) pp2; + + ORDERKEY_JID + ; + + return(result); +} + /* * proc_owner(pid) - returns the uid that owns process "pid", or -1 if * the process does not exist. >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20080925152140.2C5FA6BE>