aboutsummaryrefslogtreecommitdiff
path: root/usr.bin/top/top.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.bin/top/top.c')
-rw-r--r--usr.bin/top/top.c1229
1 files changed, 1229 insertions, 0 deletions
diff --git a/usr.bin/top/top.c b/usr.bin/top/top.c
new file mode 100644
index 000000000000..f0458a4037af
--- /dev/null
+++ b/usr.bin/top/top.c
@@ -0,0 +1,1229 @@
+/*-
+ * Top users/processes display for Unix
+ *
+ * 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
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/cdefs.h>
+#include <sys/limits.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/signal.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <jail.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "commands.h"
+#include "display.h" /* interface to display package */
+#include "screen.h" /* interface to screen package */
+#include "top.h"
+#include "machine.h"
+#include "utils.h"
+#include "username.h"
+
+/* Size of the stdio buffer given to stdout */
+#define Buffersize 2048
+
+char copyright[] =
+ "Copyright (c) 1984 through 1996, William LeFebvre";
+
+typedef void sigret_t;
+
+/* The buffer that stdio will use */
+static char stdoutbuf[Buffersize];
+
+static int fmt_flags = 0;
+int show_args = false;
+int pcpu_stats = false;
+
+/* signal handling routines */
+static sigret_t leave(int);
+static sigret_t tstop(int);
+static sigret_t top_winch(int);
+
+static volatile sig_atomic_t leaveflag;
+static volatile sig_atomic_t tstopflag;
+static volatile sig_atomic_t winchflag;
+
+/* values which need to be accessed by signal handlers */
+static int max_topn; /* maximum displayable processes */
+
+/* miscellaneous things */
+struct process_select ps;
+pid_t mypid;
+
+/* pointers to display routines */
+static void (*d_loadave)(int mpid, double *avenrun) = i_loadave;
+static void (*d_procstates)(int total, int *brkdn) = i_procstates;
+static void (*d_cpustates)(int *states) = i_cpustates;
+static void (*d_memory)(int *stats) = i_memory;
+static void (*d_arc)(int *stats) = i_arc;
+static void (*d_carc)(int *stats) = i_carc;
+static void (*d_swap)(int *stats) = i_swap;
+static void (*d_message)(void) = i_message;
+static void (*d_header)(const char *text) = i_header;
+static void (*d_process)(int line, char *thisline) = i_process;
+
+static void reset_display(void);
+
+static const struct option longopts[] = {
+ { "cpu-display-mode", no_argument, NULL, 'C' }, /* differs from orignal */
+ /* D reserved */
+ { "thread", no_argument, NULL, 'H' },
+ { "idle-procs", no_argument, NULL, 'I' },
+ { "jail", required_argument, NULL, 'J' },
+ { "per-cpu", no_argument, NULL, 'P' },
+ { "system-procs", no_argument, NULL, 'S' },
+ { "thread-id", no_argument, NULL, 'T' }, /* differs from orignal */
+ { "user", required_argument, NULL, 'U' },
+ { "all", no_argument, NULL, 'a' },
+ { "batch", no_argument, NULL, 'b' },
+ /* c reserved */
+ { "displays", required_argument, NULL, 'd' },
+ { "interactive", no_argument, NULL, 'i' },
+ { "jail-id", no_argument, NULL, 'j' },
+ { "display-mode", required_argument, NULL, 'm' },
+ /* n is identical to batch */
+ { "sort-order", required_argument, NULL, 'o' },
+ { "pid", required_argument, NULL, 'p' },
+ { "quick", no_argument, NULL, 'q' },
+ { "delay", required_argument, NULL, 's' },
+ { "threads", no_argument, NULL, 't' },
+ { "uids", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'v' },
+ { "swap", no_argument, NULL, 'w' },
+ { "system-idle-procs", no_argument, NULL, 'z' },
+ { NULL, 0, NULL, 0 }
+};
+
+static void
+reset_uids(void)
+{
+ for (size_t i = 0; i < TOP_MAX_UIDS; ++i)
+ ps.uid[i] = -1;
+}
+
+static int
+add_uid(int uid)
+{
+ size_t i = 0;
+
+ /* Add the uid if there's room */
+ for (; i < TOP_MAX_UIDS; ++i)
+ {
+ if (ps.uid[i] == -1 || ps.uid[i] == uid)
+ {
+ ps.uid[i] = uid;
+ break;
+ }
+ }
+
+ return (i == TOP_MAX_UIDS);
+}
+
+static void
+rem_uid(int uid)
+{
+ size_t i = 0;
+ size_t where = TOP_MAX_UIDS;
+
+ /* Look for the user to remove - no problem if it's not there */
+ for (; i < TOP_MAX_UIDS; ++i)
+ {
+ if (ps.uid[i] == -1)
+ break;
+ if (ps.uid[i] == uid)
+ where = i;
+ }
+
+ /* Make sure we don't leave a hole in the middle */
+ if (where != TOP_MAX_UIDS)
+ {
+ ps.uid[where] = ps.uid[i-1];
+ ps.uid[i-1] = -1;
+ }
+}
+
+static int
+handle_user(char *buf, size_t buflen)
+{
+ int rc = 0;
+ int uid = -1;
+ char *buf2 = buf;
+
+ new_message(MT_standout, "Username to show (+ for all): ");
+ if (readline(buf, buflen, false) <= 0)
+ {
+ clear_message();
+ return (rc);
+ }
+
+ if (buf[0] == '+' || buf[0] == '-')
+ {
+ if (buf[1] == '\0')
+ {
+ reset_uids();
+ goto end;
+ }
+ else
+ ++buf2;
+ }
+
+ if ((uid = userid(buf2)) == -1)
+ {
+ new_message(MT_standout, " %s: unknown user", buf2);
+ rc = 1;
+ goto end;
+ }
+
+ if (buf2 == buf)
+ {
+ reset_uids();
+ ps.uid[0] = uid;
+ goto end;
+ }
+
+ if (buf[0] == '+')
+ {
+ if (add_uid(uid))
+ {
+ new_message(MT_standout, " too many users, reset with '+'");
+ rc = 1;
+ goto end;
+ }
+ }
+ else
+ rem_uid(uid);
+
+end:
+ putchar('\r');
+ return (rc);
+}
+
+int
+main(int argc, const char *argv[])
+{
+ int i;
+ int active_procs;
+
+ struct system_info system_info;
+ struct statics statics;
+ void * processes;
+
+ static char tempbuf1[50];
+ static char tempbuf2[50];
+ sigset_t old_sigmask, new_sigmask;
+ int topn = Infinity;
+ struct timeval delay = { 2, 0 };
+ int displays = 0; /* indicates unspecified */
+ int sel_ret = 0;
+ time_t curr_time;
+ char *(*get_userid)(int) = username;
+ const char *uname_field = "USERNAME";
+ const char *header_text;
+ char *env_top;
+ const char **preset_argv;
+ int preset_argc = 0;
+ const char **av = NULL;
+ int ac = -1;
+ bool do_unames = true;
+ char interactive = 2;
+ char warnings = 0;
+ char topn_specified = false;
+ char ch;
+ char no_command = 1;
+ struct timeval timeout;
+ char *order_name = NULL;
+ const struct sort_info *sort_info = NULL;
+ fd_set readfds;
+ char *nptr;
+
+ /* set the buffer for stdout */
+#ifdef DEBUG
+ extern FILE *debug;
+ debug = fopen("debug.run", "w");
+ setbuffer(stdout, NULL, 0);
+#else
+ setbuffer(stdout, stdoutbuf, Buffersize);
+#endif
+
+ if (setlocale(LC_ALL, "") == NULL) {
+ warnx("invalid locale, check your environment");
+ sleep(2);
+ }
+
+ mypid = getpid();
+
+ /* get our name */
+ /* initialize some selection options */
+ ps.idle = true;
+ ps.self = true;
+ ps.system = false;
+ reset_uids();
+ ps.thread = false;
+ ps.wcpu = 1;
+ ps.jid = -1;
+ ps.jail = false;
+ ps.swap = false;
+ ps.kidle = true;
+ ps.pid = -1;
+ ps.command = NULL;
+ ps.thread_id = false;
+
+ /* get preset options from the environment */
+ if ((env_top = getenv("TOP")) != NULL)
+ {
+ av = preset_argv = argparse(env_top, &preset_argc);
+ ac = preset_argc;
+
+ /* set the dummy argument to an explanatory message, in case
+ getopt encounters a bad argument */
+ preset_argv[0] = "while processing environment";
+ }
+
+ /* process options */
+ do {
+ /* if we're done doing the presets, then process the real arguments */
+ if (preset_argc == 0)
+ {
+ ac = argc;
+ av = argv;
+
+ /* this should keep getopt happy... */
+ optind = 1;
+ }
+
+ while ((i = getopt_long(ac, __DECONST(char * const *, av), "CSIHPabijJ:nquvzs:d:U:m:o:p:Ttw", longopts, NULL)) != EOF)
+ {
+ switch(i)
+ {
+ case 'v': /* show version number */
+ errx(0, "version FreeBSD");
+ break;
+
+ case 'u': /* toggle uid/username display */
+ do_unames = !do_unames;
+ break;
+
+ case 'U': /* display only username's processes */
+ if ((ps.uid[0] = userid(optarg)) == -1)
+ {
+ errx(1, "%s: unknown user\n", optarg);
+ }
+ break;
+
+ case 'S': /* show system processes */
+ ps.system = true;
+ break;
+
+ case 'I': /* show idle processes */
+ ps.idle = !ps.idle;
+ break;
+
+ case 'i': /* go interactive regardless */
+ interactive = 1;
+ break;
+
+ case 'n': /* batch, or non-interactive */
+ case 'b':
+ interactive = 0;
+ break;
+
+ case 'a':
+ fmt_flags ^= FMT_SHOWARGS;
+ break;
+
+ case 'd': /* number of displays to show */
+ if ((i = atoiwi(optarg)) == Invalid || i == 0)
+ {
+ warnx("warning: display count should be positive -- option ignored");
+ warnings++;
+ }
+ else
+ {
+ displays = i;
+ }
+ break;
+ case 'p': {
+ unsigned long long num;
+ const char *errstr;
+
+ num = strtonum(optarg, 0, INT_MAX, &errstr);
+ if (errstr != NULL || !find_pid(num)) {
+ fprintf(stderr, "%s: unknown pid\n", optarg);
+ exit(1);
+ }
+ ps.pid = (pid_t)num;
+ ps.system = true;
+ break;
+ }
+
+ case 's':
+ {
+ double delay_d = strtod(optarg, &nptr);
+ if (nptr == optarg)
+ {
+ warnx("warning: invalid delay");
+ warnings++;
+ }
+ else if (delay_d <= 0)
+ {
+ warnx("warning: seconds delay should be positive -- using default");
+ warnings++;
+ }
+ else
+ {
+ delay.tv_sec = delay_d;
+ delay.tv_usec = (delay_d - delay.tv_sec) * 1e6;
+ }
+ break;
+ }
+
+ case 'q': /* be quick about it */
+ errno = 0;
+ i = setpriority(PRIO_PROCESS, 0, PRIO_MIN);
+ if (i == -1 && errno != 0) {
+ warnx("warning: `-q' option failed (%m)");
+ 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 {
+ errx(1, "warning: `-m' option can only take args 'io' or 'cpu'");
+ }
+ break;
+
+ case 'o': /* select sort order */
+ order_name = optarg;
+ break;
+
+ case 't':
+ ps.self = !ps.self;
+ break;
+
+ case 'C':
+ ps.wcpu = !ps.wcpu;
+ break;
+
+ case 'H':
+ ps.thread = !ps.thread;
+ break;
+
+ case 'T':
+ ps.thread_id = !ps.thread_id;
+ break;
+
+ case 'j':
+ ps.jail = !ps.jail;
+ break;
+
+ case 'J': /* display only jail's processes */
+ if ((ps.jid = jail_getid(optarg)) == -1)
+ {
+ fprintf(stderr, "%s: unknown jail\n", optarg);
+ exit(1);
+ }
+ ps.jail = 1;
+ break;
+
+ case 'P':
+ pcpu_stats = !pcpu_stats;
+ break;
+
+ case 'w':
+ ps.swap = 1;
+ break;
+
+ case 'z':
+ ps.kidle = !ps.kidle;
+ break;
+
+ default:
+ errx(1,
+"[-abCHIijnPqSTtuvwz] [-d count] [-J jail] [-m cpu | io] [-o field]\n"
+" [-p pid] [-s time] [-U username] [number]");
+ }
+ }
+
+ /* get count of top processes to display (if any) */
+ if (optind < ac)
+ {
+ if ((topn = atoiwi(av[optind])) == Invalid)
+ {
+ warnx("warning: process display count should be non-negative -- using default");
+ warnings++;
+ }
+ else
+ {
+ topn_specified = true;
+ }
+ }
+
+ /* tricky: remember old value of preset_argc & set preset_argc = 0 */
+ i = preset_argc;
+ preset_argc = 0;
+
+ /* repeat only if we really did the preset arguments */
+ } while (i != 0);
+
+ /* set constants for username/uid display correctly */
+ if (!do_unames)
+ {
+ uname_field = " UID ";
+ get_userid = itoa7;
+ }
+
+ /* initialize the kernel memory interface */
+ if (machine_init(&statics) == -1)
+ {
+ exit(1);
+ }
+
+ /* determine sorting order index, if necessary */
+ if (order_name != NULL)
+ {
+ if ((sort_info = get_sort_info(order_name)) == NULL) {
+ warnx("'%s' is not a recognized sorting order.", order_name);
+ fprintf(stderr, "\tTry one of these:");
+ dump_sort_names(stderr);
+ fputc('\n', stderr);
+ exit(1);
+ }
+ }
+ else
+ {
+ sort_info = get_sort_info(NULL);
+ }
+
+ /* initialize termcap */
+ init_termcap(interactive);
+
+ /* get the string to use for the process area header */
+ header_text = format_header(uname_field);
+
+ /* initialize display interface */
+ if ((max_topn = display_init(&statics)) == -1)
+ {
+ errx(4, "can't allocate sufficient memory");
+ }
+
+ /* print warning if user requested more processes than we can display */
+ if (topn > max_topn)
+ {
+ warnx("warning: this terminal can only display %d processes.", max_topn);
+ warnings++;
+ }
+
+ /* adjust for topn == Infinity */
+ if (topn == Infinity)
+ {
+ /*
+ * For smart terminals, infinity really means everything that can
+ * be displayed, or Largest.
+ * On dumb terminals, infinity means every process in the system!
+ * We only really want to do that if it was explicitly specified.
+ * This is always the case when "Default_TOPN != Infinity". But if
+ * topn wasn't explicitly specified and we are on a dumb terminal
+ * and the default is Infinity, then (and only then) we use
+ * "Nominal_TOPN" instead.
+ */
+ topn = smart_terminal ? Largest :
+ (topn_specified ? Largest : Nominal_TOPN);
+ }
+
+ /* set header display accordingly */
+ display_header(topn > 0);
+
+ /* determine interactive state */
+ if (interactive == 2)
+ {
+ interactive = smart_terminal;
+ }
+
+ /* if # of displays not specified, fill it in */
+ if (displays == 0)
+ {
+ displays = smart_terminal ? Infinity : 1;
+ }
+
+ /* hold interrupt signals while setting up the screen and the handlers */
+
+ sigemptyset(&new_sigmask);
+ sigaddset(&new_sigmask, SIGINT);
+ sigaddset(&new_sigmask, SIGQUIT);
+ sigaddset(&new_sigmask, SIGTSTP);
+ sigprocmask(SIG_BLOCK, &new_sigmask, &old_sigmask);
+ init_screen();
+ signal(SIGINT, leave);
+ signal(SIGQUIT, leave);
+ signal(SIGTSTP, tstop);
+ signal(SIGWINCH, top_winch);
+ sigprocmask(SIG_SETMASK, &old_sigmask, NULL);
+ if (warnings)
+ {
+ fputs("....", stderr);
+ fflush(stderr);
+ sleep(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))
+ {
+
+ /* get the current stats */
+ get_system_info(&system_info);
+
+ /* get the current set of processes */
+ processes =
+ get_process_info(&system_info, &ps, sort_info);
+
+ /* display the load averages */
+ (*d_loadave)(system_info.last_pid,
+ system_info.load_avg);
+
+ /* display the battery info (if any) */
+ i_battery(statics.nbatteries, system_info.battery);
+
+ /* display the current time */
+ /* this method of getting the time SHOULD be fairly portable */
+ time(&curr_time);
+ i_uptime(&system_info.boottime, &curr_time);
+ i_timeofday(&curr_time);
+
+ /* display process state breakdown */
+ (*d_procstates)(system_info.p_total,
+ system_info.procstates);
+ (*d_cpustates)(system_info.cpustates);
+
+ /* display memory stats */
+ (*d_memory)(system_info.memory);
+ (*d_arc)(system_info.arc);
+ (*d_carc)(system_info.carc);
+
+ /* display swap stats */
+ (*d_swap)(system_info.swap);
+
+ /* handle message area */
+ (*d_message)();
+
+ /* update the header area */
+ (*d_header)(header_text);
+
+ if (topn > 0)
+ {
+ /* determine number of processes to actually display */
+ /* this number will be the smallest of: active processes,
+ number user requested, number current screen accommodates */
+ active_procs = system_info.p_pactive;
+ if (active_procs > topn)
+ {
+ active_procs = topn;
+ }
+ if (active_procs > max_topn)
+ {
+ active_procs = max_topn;
+ }
+
+ /* now show the top "n" processes. */
+ for (i = 0; i < active_procs; i++)
+ {
+ (*d_process)(i, format_next_process(processes, get_userid,
+ fmt_flags));
+ }
+ }
+ else
+ {
+ i = 0;
+ }
+
+ /* do end-screen processing */
+ u_endscreen(i);
+
+ /* now, flush the output buffer */
+ if (fflush(stdout) != 0)
+ {
+ new_message(MT_standout, " Write error on stdout");
+ putchar('\r');
+ quit(1);
+ }
+
+ /* only do the rest if we have more displays to show */
+ if (displays)
+ {
+ /* switch out for new display on smart terminals */
+ if (smart_terminal)
+ {
+ if (overstrike)
+ {
+ reset_display();
+ }
+ else
+ {
+ d_loadave = u_loadave;
+ d_procstates = u_procstates;
+ d_cpustates = u_cpustates;
+ d_memory = u_memory;
+ d_arc = u_arc;
+ d_carc = u_carc;
+ d_swap = u_swap;
+ d_message = u_message;
+ d_header = u_header;
+ d_process = u_process;
+ }
+ }
+
+ no_command = true;
+ if (!interactive)
+ {
+ timeout = delay;
+ select(0, NULL, NULL, NULL, &timeout);
+ if (leaveflag) {
+ end_screen();
+ exit(0);
+ }
+ }
+ else while (no_command)
+ {
+ /* assume valid command unless told otherwise */
+ no_command = false;
+
+ /* set up arguments for select with timeout */
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds); /* for standard input */
+ timeout = delay;
+
+ if (leaveflag) {
+ end_screen();
+ exit(0);
+ }
+
+ if (tstopflag) {
+ /* move to the lower left */
+ end_screen();
+ fflush(stdout);
+
+ /* default the signal handler action */
+ signal(SIGTSTP, SIG_DFL);
+
+ /* unblock the signal and send ourselves one */
+ sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
+ kill(0, SIGTSTP);
+
+ /* reset the signal handler */
+ signal(SIGTSTP, tstop);
+
+ /* reinit screen */
+ reinit_screen();
+ reset_display();
+ tstopflag = 0;
+ goto restart;
+ }
+
+ if (winchflag) {
+ /* reascertain the screen dimensions */
+ get_screensize();
+
+ /* tell display to resize */
+ max_topn = display_resize();
+
+ /* reset the signal handler */
+ signal(SIGWINCH, top_winch);
+
+ reset_display();
+ winchflag = 0;
+ goto restart;
+ }
+
+ /* wait for either input or the end of the delay period */
+ sel_ret = select(2, &readfds, NULL, NULL, &timeout);
+ if (sel_ret < 0 && errno != EINTR)
+ quit(0);
+ if (sel_ret > 0)
+ {
+ int newval;
+ const char *errmsg;
+ const struct command *cptr;
+
+ /* something to read -- clear the message area first */
+ clear_message();
+
+ /* now read it and convert to command strchr */
+ /* (use "change" as a temporary to hold strchr) */
+ if (read(0, &ch, 1) != 1)
+ {
+ /* read error: either 0 or -1 */
+ new_message(MT_standout, " Read error on stdin");
+ putchar('\r');
+ quit(1);
+ }
+ if (ch == '\r' || ch == '\n') {
+ continue;
+ }
+ cptr = all_commands;
+ while (cptr->c != '\0') {
+ if (cptr->c == ch) {
+ break;
+ }
+ cptr++;
+ }
+ if (cptr->c == '\0') {
+ new_message(MT_standout, " Command not understood");
+ putchar('\r');
+ no_command = true;
+ }
+ if (overstrike && !cptr->available_to_dumb)
+ {
+ new_message(MT_standout,
+ " Command cannot be handled by this terminal");
+ putchar('\r');
+ no_command = true;
+ }
+ if (!no_command) {
+ switch(cptr->id)
+ {
+ case CMD_redraw: /* redraw screen */
+ reset_display();
+ break;
+
+ case CMD_update: /* merely update display */
+ break;
+
+ case CMD_quit:
+ quit(0);
+ break;
+
+ case CMD_help:
+ reset_display();
+ top_clear();
+ show_help();
+ top_standout("Hit any key to continue: ");
+ fflush(stdout);
+ 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 = true;
+ }
+ else
+ {
+ reset_display();
+ top_clear();
+ show_errors();
+ top_standout("Hit any key to continue: ");
+ fflush(stdout);
+ read(0, &ch, 1);
+ }
+ break;
+
+ case CMD_number:
+ new_message(MT_standout,
+ "Number of processes to show: ");
+ newval = readline(tempbuf1, 8, true);
+ if (newval > -1)
+ {
+ if (newval > max_topn)
+ {
+ new_message(MT_standout | MT_delayed,
+ " This terminal can only display %d processes.",
+ max_topn);
+ putchar('\r');
+ }
+
+ if (newval == 0)
+ {
+ /* inhibit the header */
+ display_header(false);
+ }
+ else if (newval > topn && topn == 0)
+ {
+ /* redraw the header */
+ display_header(true);
+ 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, false)) > 0)
+ {
+ double delay_d = strtod(tempbuf1, &nptr);
+ if (nptr == tempbuf1 || delay_d <= 0)
+ {
+ new_message(MT_standout, " Invalid delay");
+ putchar('\r');
+ no_command = true;
+ }
+ else
+ {
+ delay.tv_sec = delay_d;
+ delay.tv_usec = (delay_d - delay.tv_sec) * 1e6;
+ clear_message();
+ }
+ }
+ break;
+
+ case CMD_grep: /* grep command name */
+ new_message(MT_standout,
+ "Grep command name (+ for all): ");
+ if (readline(tempbuf1, sizeof(tempbuf1), false) > 0) {
+ free(ps.command);
+ if (tempbuf1[0] == '+' && tempbuf1[1] == '\0') {
+ ps.command = NULL;
+ } else if ((ps.command = strdup(tempbuf1)) == NULL)
+ quit(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, true)) > 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), false) > 0)
+ {
+ if ((errmsg = kill_procs(tempbuf2)) != NULL)
+ {
+ new_message(MT_standout, "%s", errmsg);
+ putchar('\r');
+ no_command = true;
+ }
+ }
+ else
+ {
+ clear_message();
+ }
+ break;
+
+ case CMD_renice: /* renice program */
+ new_message(0, "renice ");
+ if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
+ {
+ if ((errmsg = renice_procs(tempbuf2)) != NULL)
+ {
+ new_message(MT_standout, "%s", errmsg);
+ putchar('\r');
+ no_command = true;
+ }
+ }
+ else
+ {
+ clear_message();
+ }
+ break;
+
+ case CMD_idletog:
+ ps.idle = !ps.idle;
+ new_message(MT_standout | MT_delayed,
+ " %sisplaying idle processes.",
+ ps.idle ? "D" : "Not d");
+ putchar('\r');
+ break;
+
+ case CMD_selftog:
+ ps.self = !ps.self;
+ new_message(MT_standout | MT_delayed,
+ " %sisplaying self.",
+ (ps.self) ? "D" : "Not d");
+ putchar('\r');
+ break;
+
+ case CMD_user:
+ if (handle_user(tempbuf2, sizeof(tempbuf2)))
+ no_command = true;
+ 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_toggletid:
+ ps.thread_id = !ps.thread_id;
+ new_message(MT_standout | MT_delayed,
+ " Displaying %s",
+ ps.thread_id ? "tid" : "pid");
+ header_text = format_header(uname_field);
+ reset_display();
+ putchar('\r');
+ break;
+
+ case CMD_wcputog:
+ ps.wcpu = !ps.wcpu;
+ new_message(MT_standout | MT_delayed,
+ " Displaying %s CPU",
+ ps.wcpu ? "weighted" : "raw");
+ header_text = format_header(uname_field);
+ reset_display();
+ putchar('\r');
+ break;
+ case CMD_viewtog:
+ displaymode = displaymode == DISP_IO ? DISP_CPU : DISP_IO;
+ new_message(MT_standout | MT_delayed,
+ " Displaying %s statistics.",
+ displaymode == DISP_IO ? "IO" : "CPU");
+ header_text = format_header(uname_field);
+ display_header(true);
+ d_header = i_header;
+ reset_display();
+ break;
+ case CMD_viewsys:
+ ps.system = !ps.system;
+ new_message(MT_standout | MT_delayed,
+ " %sisplaying system processes.",
+ ps.system ? "D" : "Not d");
+ break;
+ case CMD_showargs:
+ fmt_flags ^= FMT_SHOWARGS;
+ show_args = fmt_flags & FMT_SHOWARGS;
+ new_message(MT_standout | MT_delayed,
+ " %sisplaying process arguments.",
+ fmt_flags & FMT_SHOWARGS ? "D" : "Not d");
+ break;
+ case CMD_order:
+ new_message(MT_standout,
+ "Order to sort: ");
+ if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
+ {
+ const struct sort_info *new_sort_info;
+
+ if ((new_sort_info = get_sort_info(tempbuf2)) == NULL)
+ {
+ new_message(MT_standout,
+ " %s: unrecognized sorting order", tempbuf2);
+ no_command = true;
+ }
+ else
+ {
+ sort_info = new_sort_info;
+ }
+ putchar('\r');
+ }
+ else
+ {
+ clear_message();
+ }
+ break;
+ case CMD_jidtog:
+ ps.jail = !ps.jail;
+ new_message(MT_standout | MT_delayed,
+ " %sisplaying jail ID.",
+ ps.jail ? "D" : "Not d");
+ header_text = format_header(uname_field);
+ reset_display();
+ putchar('\r');
+ break;
+
+ case CMD_jail:
+ new_message(MT_standout,
+ "Jail to show (+ for all): ");
+ if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
+ {
+ if (tempbuf2[0] == '+' &&
+ tempbuf2[1] == '\0')
+ {
+ ps.jid = -1;
+ }
+ else if ((i = jail_getid(tempbuf2)) == -1)
+ {
+ new_message(MT_standout,
+ " %s: unknown jail", tempbuf2);
+ no_command = true;
+ }
+ else
+ {
+ ps.jid = i;
+ }
+ if (ps.jail == 0) {
+ ps.jail = 1;
+ new_message(MT_standout |
+ MT_delayed, " Displaying jail "
+ "ID.");
+ header_text =
+ format_header(uname_field);
+ reset_display();
+ }
+ putchar('\r');
+ }
+ else
+ {
+ clear_message();
+ }
+ break;
+
+ case CMD_kidletog:
+ ps.kidle = !ps.kidle;
+ new_message(MT_standout | MT_delayed,
+ " %sisplaying system idle process.",
+ ps.kidle ? "D" : "Not d");
+ putchar('\r');
+ break;
+ case CMD_pcputog:
+ pcpu_stats = !pcpu_stats;
+ new_message(MT_standout | MT_delayed,
+ " Displaying %sCPU statistics.",
+ pcpu_stats ? "per-" : "global ");
+ toggle_pcpustats();
+ max_topn = display_updatecpus(&statics);
+ reset_display();
+ putchar('\r');
+ break;
+ case CMD_swaptog:
+ ps.swap = !ps.swap;
+ new_message(MT_standout | MT_delayed,
+ " %sisplaying per-process swap usage.",
+ ps.swap ? "D" : "Not d");
+ header_text = format_header(uname_field);
+ reset_display();
+ putchar('\r');
+ break;
+ case CMD_pid:
+ new_message(MT_standout,
+ "Process id to show (+ for all): ");
+ if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) {
+ if (tempbuf2[0] == '+' &&
+ tempbuf2[1] == '\0') {
+ ps.pid = (pid_t)-1;
+ } else {
+ unsigned long long num;
+ const char *errstr;
+
+ num = strtonum(tempbuf2, 0, INT_MAX,
+ &errstr);
+ if (errstr != NULL || !find_pid(num)) {
+ new_message(MT_standout,
+ " %s: unknown pid",
+ tempbuf2);
+ no_command = true;
+ } else {
+ ps.pid = (pid_t)num;
+ }
+ }
+ putchar('\r');
+ } else
+ clear_message();
+ break;
+ case CMD_NONE:
+ assert(false && "reached switch without command");
+ }
+ }
+ }
+
+ /* flush out stuff that may have been written */
+ fflush(stdout);
+ }
+ }
+ }
+
+#ifdef DEBUG
+ fclose(debug);
+#endif
+ quit(0);
+}
+
+/*
+ * reset_display() - reset all the display routine pointers so that entire
+ * screen will get redrawn.
+ */
+
+static void
+reset_display(void)
+{
+ d_loadave = i_loadave;
+ d_procstates = i_procstates;
+ d_cpustates = i_cpustates;
+ d_memory = i_memory;
+ d_arc = i_arc;
+ d_carc = i_carc;
+ d_swap = i_swap;
+ d_message = i_message;
+ d_header = i_header;
+ d_process = i_process;
+}
+
+/*
+ * signal handlers
+ */
+
+static sigret_t
+leave(int i __unused) /* exit under normal conditions -- INT handler */
+{
+
+ leaveflag = 1;
+}
+
+static sigret_t
+tstop(int i __unused) /* SIGTSTP handler */
+{
+
+ tstopflag = 1;
+}
+
+static sigret_t
+top_winch(int i __unused) /* SIGWINCH handler */
+{
+
+ winchflag = 1;
+}
+
+void __dead2
+quit(int status) /* exit under duress */
+{
+ end_screen();
+ exit(status);
+}