bin/127633: [patch] Update top(1) to 3.8b1
Edwin Groothuis
edwin at mavetju.org
Thu Sep 25 15:30:04 UTC 2008
>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 at 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 at ee.cornell.edu>
* Steven Wallace <swallace at freebsd.org>
* Wolfram Schneider <wosch at FreeBSD.org>
- * Thomas Moestl <tmoestl at 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 at 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 at 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 at 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 at 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 at 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 at 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 at sofkin.ca>
- Marc Cohen <marc at aai.com>
- David Cutter <dpc at grail.com>
- Casper Dik <Casper.Dik at Sun.COM>
- Charles Hedrick <hedrick at geneva.rutgers.edu>
- Andrew Herbert <andrew at werple.apana.org.au>
- Jeff Janvrin <jeff.janvrin at columbiasc.ncr.com>
- Torsten Kasch <torsten at techfak.uni-bielefeld.de>
- Petri Kutvonen <kutvonen at cs.helsinki.fi>
- William L. Jones <jones at chpc>
- Tim Pugh <tpugh at oce.orst.edu>
- Steve Scherf <scherf at swdc.stratus.com>
- Phillip Wu <pwu01 at 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 at 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 at groupsys.com".
-This list is managed by "majordomo at 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 at 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 at 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 at 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 at ee.cornell.edu>
* Steven Wallace <swallace at 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:
More information about the freebsd-bugs
mailing list