1/*-
2 *  Top users/processes display for Unix
3 *
4 *  This program may be freely redistributed,
5 *  but this entire comment MUST remain intact.
6 *
7 *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
8 *  Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
9 *  Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
10 *  Copyright (c) 1996, William LeFebvre, Group sys Consulting
11 */
12
13#include <sys/types.h>
14#include <sys/time.h>
15#include <sys/cdefs.h>
16#include <sys/limits.h>
17#include <sys/resource.h>
18#include <sys/select.h>
19#include <sys/signal.h>
20
21#include <assert.h>
22#include <err.h>
23#include <errno.h>
24#include <getopt.h>
25#include <jail.h>
26#include <locale.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <signal.h>
31#include <string.h>
32#include <unistd.h>
33
34#include "commands.h"
35#include "display.h"		/* interface to display package */
36#include "screen.h"		/* interface to screen package */
37#include "top.h"
38#include "machine.h"
39#include "utils.h"
40#include "username.h"
41
42/* Size of the stdio buffer given to stdout */
43#define Buffersize	2048
44
45char copyright[] =
46    "Copyright (c) 1984 through 1996, William LeFebvre";
47
48typedef void sigret_t;
49
50/* The buffer that stdio will use */
51static char stdoutbuf[Buffersize];
52
53static int fmt_flags = 0;
54int show_args = false;
55int pcpu_stats = false;
56
57/* signal handling routines */
58static sigret_t leave(int);
59static sigret_t tstop(int);
60static sigret_t top_winch(int);
61
62static volatile sig_atomic_t leaveflag;
63static volatile sig_atomic_t tstopflag;
64static volatile sig_atomic_t winchflag;
65
66/* values which need to be accessed by signal handlers */
67static int max_topn;		/* maximum displayable processes */
68
69/* miscellaneous things */
70struct process_select ps;
71pid_t mypid;
72
73/* pointers to display routines */
74static void (*d_loadave)(int mpid, double *avenrun) = i_loadave;
75static void (*d_procstates)(int total, int *brkdn) = i_procstates;
76static void (*d_cpustates)(int *states) = i_cpustates;
77static void (*d_memory)(int *stats) = i_memory;
78static void (*d_arc)(int *stats) = i_arc;
79static void (*d_carc)(int *stats) = i_carc;
80static void (*d_swap)(int *stats) = i_swap;
81static void (*d_message)(void) = i_message;
82static void (*d_header)(const char *text) = i_header;
83static void (*d_process)(int line, char *thisline) = i_process;
84
85static void reset_display(void);
86
87static const struct option longopts[] = {
88    { "cpu-display-mode", no_argument, NULL, 'C' }, /* differs from orignal */
89    /* D reserved */
90    { "thread", no_argument, NULL, 'H' },
91    { "idle-procs", no_argument, NULL, 'I' },
92	{ "jail", required_argument, NULL, 'J' },
93	{ "per-cpu", no_argument, NULL, 'P' },
94    { "system-procs", no_argument, NULL, 'S' },
95    { "thread-id", no_argument, NULL, 'T' }, /* differs from orignal */
96    { "user", required_argument, NULL, 'U' },
97    { "all", no_argument, NULL, 'a' },
98    { "batch", no_argument, NULL, 'b' },
99    /* c reserved */
100    { "displays", required_argument, NULL, 'd' },
101    { "interactive", no_argument, NULL, 'i' },
102    { "jail-id", no_argument, NULL, 'j' },
103    { "display-mode", required_argument, NULL, 'm' },
104    /* n is identical to batch */
105    { "sort-order", required_argument, NULL, 'o' },
106    { "pid", required_argument, NULL, 'p' },
107    { "quick", no_argument, NULL, 'q' },
108    { "delay", required_argument, NULL, 's' },
109    { "threads", no_argument, NULL, 't' },
110    { "uids", no_argument, NULL, 'u' },
111    { "version", no_argument, NULL, 'v' },
112	{ "swap", no_argument, NULL, 'w' },
113	{ "system-idle-procs", no_argument, NULL, 'z' },
114	{ NULL, 0, NULL, 0 }
115};
116
117static void
118reset_uids(void)
119{
120    for (size_t i = 0; i < TOP_MAX_UIDS; ++i)
121	ps.uid[i] = -1;
122}
123
124static int
125add_uid(int uid)
126{
127    size_t i = 0;
128
129    /* Add the uid if there's room */
130    for (; i < TOP_MAX_UIDS; ++i)
131    {
132	if (ps.uid[i] == -1 || ps.uid[i] == uid)
133	{
134	    ps.uid[i] = uid;
135	    break;
136	}
137    }
138
139    return (i == TOP_MAX_UIDS);
140}
141
142static void
143rem_uid(int uid)
144{
145    size_t i = 0;
146    size_t where = TOP_MAX_UIDS;
147
148    /* Look for the user to remove - no problem if it's not there */
149    for (; i < TOP_MAX_UIDS; ++i)
150    {
151	if (ps.uid[i] == -1)
152	    break;
153	if (ps.uid[i] == uid)
154	    where = i;
155    }
156
157    /* Make sure we don't leave a hole in the middle */
158    if (where != TOP_MAX_UIDS)
159    {
160	ps.uid[where] = ps.uid[i-1];
161	ps.uid[i-1] = -1;
162    }
163}
164
165static int
166handle_user(char *buf, size_t buflen)
167{
168    int rc = 0;
169    int uid = -1;
170    char *buf2 = buf;
171
172    new_message(MT_standout, "Username to show (+ for all): ");
173    if (readline(buf, buflen, false) <= 0)
174    {
175	clear_message();
176	return (rc);
177    }
178
179    if (buf[0] == '+' || buf[0] == '-')
180    {
181	if (buf[1] == '\0')
182	{
183	    reset_uids();
184	    goto end;
185	}
186	else
187	    ++buf2;
188    }
189
190    if ((uid = userid(buf2)) == -1)
191    {
192	new_message(MT_standout, " %s: unknown user", buf2);
193	rc = 1;
194	goto end;
195    }
196
197    if (buf2 == buf)
198    {
199	reset_uids();
200	ps.uid[0] = uid;
201	goto end;
202    }
203
204    if (buf[0] == '+')
205    {
206	if (add_uid(uid))
207	{
208	    new_message(MT_standout, " too many users, reset with '+'");
209	    rc = 1;
210	    goto end;
211	}
212    }
213    else
214	rem_uid(uid);
215
216end:
217    putchar('\r');
218    return (rc);
219}
220
221int
222main(int argc, const char *argv[])
223{
224    int i;
225    int active_procs;
226
227    struct system_info system_info;
228    struct statics statics;
229    void * processes;
230
231    static char tempbuf1[50];
232    static char tempbuf2[50];
233	sigset_t old_sigmask, new_sigmask;
234    int topn = Infinity;
235    struct timeval delay = { 2, 0 };
236    int displays = 0;		/* indicates unspecified */
237    int sel_ret = 0;
238    time_t curr_time;
239    char *(*get_userid)(int) = username;
240    const char *uname_field = "USERNAME";
241    const char *header_text;
242    char *env_top;
243    const char **preset_argv;
244    int  preset_argc = 0;
245    const char **av = NULL;
246    int  ac = -1;
247    bool do_unames = true;
248    char interactive = 2;
249    char warnings = 0;
250    char topn_specified = false;
251    char ch;
252    char no_command = 1;
253    struct timeval timeout;
254    char *order_name = NULL;
255    int order_index = 0;
256    fd_set readfds;
257	char *nptr;
258
259    /* set the buffer for stdout */
260#ifdef DEBUG
261    extern FILE *debug;
262    debug = fopen("debug.run", "w");
263    setbuffer(stdout, NULL, 0);
264#else
265    setbuffer(stdout, stdoutbuf, Buffersize);
266#endif
267
268    if (setlocale(LC_ALL, "") == NULL) {
269        fprintf(stderr, "invalid locale.\n");
270	exit(1);
271    }
272
273    mypid = getpid();
274
275    /* get our name */
276    /* initialize some selection options */
277    ps.idle    = true;
278    ps.self    = true;
279    ps.system  = false;
280    reset_uids();
281    ps.thread  = false;
282    ps.wcpu    = 1;
283    ps.jid     = -1;
284    ps.jail    = false;
285    ps.swap    = false;
286    ps.kidle   = true;
287    ps.pid     = -1;
288    ps.command = NULL;
289    ps.thread_id = false;
290
291    /* get preset options from the environment */
292    if ((env_top = getenv("TOP")) != NULL)
293    {
294	av = preset_argv = argparse(env_top, &preset_argc);
295	ac = preset_argc;
296
297	/* set the dummy argument to an explanatory message, in case
298	   getopt encounters a bad argument */
299	preset_argv[0] = "while processing environment";
300    }
301
302    /* process options */
303    do {
304	/* if we're done doing the presets, then process the real arguments */
305	if (preset_argc == 0)
306	{
307	    ac = argc;
308	    av = argv;
309
310	    /* this should keep getopt happy... */
311	    optind = 1;
312	}
313
314	while ((i = getopt_long(ac, __DECONST(char * const *, av), "CSIHPabijJ:nquvzs:d:U:m:o:p:Ttw", longopts, NULL)) != EOF)
315	{
316	    switch(i)
317	    {
318	      case 'v':			/* show version number */
319			  errx(0, "version FreeBSD");
320			  break;
321
322	      case 'u':			/* toggle uid/username display */
323		do_unames = !do_unames;
324		break;
325
326	      case 'U':			/* display only username's processes */
327		if ((ps.uid[0] = userid(optarg)) == -1)
328		{
329		    errx(1, "%s: unknown user\n", optarg);
330		}
331		break;
332
333	      case 'S':			/* show system processes */
334		ps.system = true;
335		break;
336
337	      case 'I':                   /* show idle processes */
338		ps.idle = !ps.idle;
339		break;
340
341	      case 'i':			/* go interactive regardless */
342		interactive = 1;
343		break;
344
345	      case 'n':			/* batch, or non-interactive */
346	      case 'b':
347		interactive = 0;
348		break;
349
350	      case 'a':
351		fmt_flags ^= FMT_SHOWARGS;
352		break;
353
354	      case 'd':			/* number of displays to show */
355		if ((i = atoiwi(optarg)) == Invalid || i == 0)
356		{
357		    warnx("warning: display count should be positive -- option ignored");
358		    warnings++;
359		}
360		else
361		{
362		    displays = i;
363		}
364		break;
365	      case 'p': {
366		unsigned long long num;
367		const char *errstr;
368
369		num = strtonum(optarg, 0, INT_MAX, &errstr);
370		if (errstr != NULL || !find_pid(num)) {
371			fprintf(stderr, "%s: unknown pid\n", optarg);
372			exit(1);
373		}
374		ps.pid = (pid_t)num;
375		ps.system = true;
376		break;
377	      }
378
379	      case 's':
380	      {
381		  double delay_d = strtod(optarg, &nptr);
382		  if (nptr == optarg)
383		  {
384		      warnx("warning: invalid delay");
385		      warnings++;
386		  }
387		  else if (delay_d <= 0)
388		  {
389		      warnx("warning: seconds delay should be positive -- using default");
390		      warnings++;
391		  }
392		  else
393		  {
394		      delay.tv_sec = delay_d;
395		      delay.tv_usec = (delay_d - delay.tv_sec) * 1e6;
396		  }
397		  break;
398	      }
399
400	      case 'q':		/* be quick about it */
401			errno = 0;
402			i = setpriority(PRIO_PROCESS, 0, PRIO_MIN);
403			if (i == -1 && errno != 0) {
404				warnx("warning: `-q' option failed (%m)");
405				warnings++;
406			}
407		break;
408
409	      case 'm':		/* select display mode */
410		if (strcmp(optarg, "io") == 0) {
411			displaymode = DISP_IO;
412		} else if (strcmp(optarg, "cpu") == 0) {
413			displaymode = DISP_CPU;
414		} else {
415			errx(1, "warning: `-m' option can only take args 'io' or 'cpu'");
416		}
417		break;
418
419	      case 'o':		/* select sort order */
420		order_name = optarg;
421		break;
422
423	      case 't':
424		ps.self = !ps.self;
425		break;
426
427	      case 'C':
428		ps.wcpu = !ps.wcpu;
429		break;
430
431	      case 'H':
432		ps.thread = !ps.thread;
433		break;
434
435	      case 'T':
436		ps.thread_id = !ps.thread_id;
437		break;
438
439	      case 'j':
440		ps.jail = !ps.jail;
441		break;
442
443	      case 'J':			/* display only jail's processes */
444		if ((ps.jid = jail_getid(optarg)) == -1)
445		{
446		    fprintf(stderr, "%s: unknown jail\n", optarg);
447		    exit(1);
448		}
449		ps.jail = 1;
450		break;
451
452	      case 'P':
453		pcpu_stats = !pcpu_stats;
454		break;
455
456	      case 'w':
457		ps.swap = 1;
458		break;
459
460	      case 'z':
461		ps.kidle = !ps.kidle;
462		break;
463
464	      default:
465		errx(1,
466"[-abCHIijnPqStuvwz] [-d count] [-J jail] [-m cpu | io] [-o field]\n"
467"     [-p pid] [-s time] [-U username] [number]");
468	    }
469	}
470
471	/* get count of top processes to display (if any) */
472	if (optind < ac)
473	{
474	    if ((topn = atoiwi(av[optind])) == Invalid)
475	    {
476			warnx("warning: process display count should be non-negative -- using default");
477			warnings++;
478	    }
479            else
480	    {
481		topn_specified = true;
482	    }
483	}
484
485	/* tricky:  remember old value of preset_argc & set preset_argc = 0 */
486	i = preset_argc;
487	preset_argc = 0;
488
489    /* repeat only if we really did the preset arguments */
490    } while (i != 0);
491
492    /* set constants for username/uid display correctly */
493    if (!do_unames)
494    {
495	uname_field = "   UID  ";
496	get_userid = itoa7;
497    }
498
499    /* initialize the kernel memory interface */
500    if (machine_init(&statics) == -1)
501    {
502	exit(1);
503    }
504
505    /* determine sorting order index, if necessary */
506    if (order_name != NULL)
507    {
508	if ((order_index = string_index(order_name, statics.order_names)) == -1)
509	{
510	    const char * const *pp;
511
512	    warnx("'%s' is not a recognized sorting order.", order_name);
513	    fprintf(stderr, "\tTry one of these:");
514	    pp = statics.order_names;
515	    while (*pp != NULL)
516	    {
517		fprintf(stderr, " %s", *pp++);
518	    }
519	    fputc('\n', stderr);
520	    exit(1);
521	}
522    }
523
524    /* initialize termcap */
525    init_termcap(interactive);
526
527    /* get the string to use for the process area header */
528    header_text = format_header(uname_field);
529
530    /* initialize display interface */
531    if ((max_topn = display_init(&statics)) == -1)
532    {
533		errx(4, "can't allocate sufficient memory");
534    }
535
536    /* print warning if user requested more processes than we can display */
537    if (topn > max_topn)
538    {
539		warnx("warning: this terminal can only display %d processes.", max_topn);
540		warnings++;
541    }
542
543    /* adjust for topn == Infinity */
544    if (topn == Infinity)
545    {
546	/*
547	 *  For smart terminals, infinity really means everything that can
548	 *  be displayed, or Largest.
549	 *  On dumb terminals, infinity means every process in the system!
550	 *  We only really want to do that if it was explicitly specified.
551	 *  This is always the case when "Default_TOPN != Infinity".  But if
552	 *  topn wasn't explicitly specified and we are on a dumb terminal
553	 *  and the default is Infinity, then (and only then) we use
554	 *  "Nominal_TOPN" instead.
555	 */
556	topn = smart_terminal ? Largest :
557		    (topn_specified ? Largest : Nominal_TOPN);
558    }
559
560    /* set header display accordingly */
561    display_header(topn > 0);
562
563    /* determine interactive state */
564    if (interactive == 2)
565    {
566	interactive = smart_terminal;
567    }
568
569    /* if # of displays not specified, fill it in */
570    if (displays == 0)
571    {
572	displays = smart_terminal ? Infinity : 1;
573    }
574
575    /* hold interrupt signals while setting up the screen and the handlers */
576
577	sigemptyset(&new_sigmask);
578	sigaddset(&new_sigmask, SIGINT);
579	sigaddset(&new_sigmask, SIGQUIT);
580	sigaddset(&new_sigmask, SIGTSTP);
581	sigprocmask(SIG_BLOCK, &new_sigmask, &old_sigmask);
582    init_screen();
583    signal(SIGINT, leave);
584    signal(SIGQUIT, leave);
585    signal(SIGTSTP, tstop);
586    signal(SIGWINCH, top_winch);
587    sigprocmask(SIG_SETMASK, &old_sigmask, NULL);
588    if (warnings)
589    {
590	fputs("....", stderr);
591	fflush(stderr);
592	sleep(3 * warnings);
593	fputc('\n', stderr);
594    }
595
596restart:
597
598    /*
599     *  main loop -- repeat while display count is positive or while it
600     *		indicates infinity (by being -1)
601     */
602
603    while ((displays == -1) || (displays-- > 0))
604    {
605	int (*compare)(const void * const, const void * const);
606
607
608	/* get the current stats */
609	get_system_info(&system_info);
610
611	compare = compares[order_index];
612
613	/* get the current set of processes */
614	processes =
615		get_process_info(&system_info, &ps, compare);
616
617	/* display the load averages */
618	(*d_loadave)(system_info.last_pid,
619		     system_info.load_avg);
620
621	/* display the battery info (if any) */
622	i_battery(statics.nbatteries, system_info.battery);
623
624	/* display the current time */
625	/* this method of getting the time SHOULD be fairly portable */
626	time(&curr_time);
627	i_uptime(&system_info.boottime, &curr_time);
628	i_timeofday(&curr_time);
629
630	/* display process state breakdown */
631	(*d_procstates)(system_info.p_total,
632			system_info.procstates);
633	(*d_cpustates)(system_info.cpustates);
634
635	/* display memory stats */
636	(*d_memory)(system_info.memory);
637	(*d_arc)(system_info.arc);
638	(*d_carc)(system_info.carc);
639
640	/* display swap stats */
641	(*d_swap)(system_info.swap);
642
643	/* handle message area */
644	(*d_message)();
645
646	/* update the header area */
647	(*d_header)(header_text);
648
649	if (topn > 0)
650	{
651	    /* determine number of processes to actually display */
652	    /* this number will be the smallest of:  active processes,
653	       number user requested, number current screen accommodates */
654	    active_procs = system_info.p_pactive;
655	    if (active_procs > topn)
656	    {
657		active_procs = topn;
658	    }
659	    if (active_procs > max_topn)
660	    {
661		active_procs = max_topn;
662	    }
663
664	    /* now show the top "n" processes. */
665	    for (i = 0; i < active_procs; i++)
666	    {
667		(*d_process)(i, format_next_process(processes, get_userid,
668			     fmt_flags));
669	    }
670	}
671	else
672	{
673	    i = 0;
674	}
675
676	/* do end-screen processing */
677	u_endscreen(i);
678
679	/* now, flush the output buffer */
680	if (fflush(stdout) != 0)
681	{
682	    new_message(MT_standout, " Write error on stdout");
683	    putchar('\r');
684	    quit(1);
685	}
686
687	/* only do the rest if we have more displays to show */
688	if (displays)
689	{
690	    /* switch out for new display on smart terminals */
691	    if (smart_terminal)
692	    {
693		if (overstrike)
694		{
695		    reset_display();
696		}
697		else
698		{
699		    d_loadave = u_loadave;
700		    d_procstates = u_procstates;
701		    d_cpustates = u_cpustates;
702		    d_memory = u_memory;
703		    d_arc = u_arc;
704		    d_carc = u_carc;
705		    d_swap = u_swap;
706		    d_message = u_message;
707		    d_header = u_header;
708		    d_process = u_process;
709		}
710	    }
711
712	    no_command = true;
713	    if (!interactive)
714	    {
715		timeout = delay;
716		select(0, NULL, NULL, NULL, &timeout);
717		if (leaveflag) {
718		    end_screen();
719		    exit(0);
720		}
721	    }
722	    else while (no_command)
723	    {
724		/* assume valid command unless told otherwise */
725		no_command = false;
726
727		/* set up arguments for select with timeout */
728		FD_ZERO(&readfds);
729		FD_SET(0, &readfds);		/* for standard input */
730		timeout = delay;
731
732		if (leaveflag) {
733		    end_screen();
734		    exit(0);
735		}
736
737		if (tstopflag) {
738		    /* move to the lower left */
739		    end_screen();
740		    fflush(stdout);
741
742		    /* default the signal handler action */
743		    signal(SIGTSTP, SIG_DFL);
744
745		    /* unblock the signal and send ourselves one */
746		    sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
747		    kill(0, SIGTSTP);
748
749		    /* reset the signal handler */
750		    signal(SIGTSTP, tstop);
751
752		    /* reinit screen */
753		    reinit_screen();
754		    reset_display();
755		    tstopflag = 0;
756		    goto restart;
757		}
758
759		if (winchflag) {
760		    /* reascertain the screen dimensions */
761		    get_screensize();
762
763		    /* tell display to resize */
764		    max_topn = display_resize();
765
766		    /* reset the signal handler */
767		    signal(SIGWINCH, top_winch);
768
769		    reset_display();
770		    winchflag = 0;
771		    goto restart;
772		}
773
774		/* wait for either input or the end of the delay period */
775		sel_ret = select(2, &readfds, NULL, NULL, &timeout);
776		if (sel_ret < 0 && errno != EINTR)
777		    quit(0);
778		if (sel_ret > 0)
779		{
780		    int newval;
781		    const char *errmsg;
782			const struct command *cptr;
783
784		    /* something to read -- clear the message area first */
785		    clear_message();
786
787		    /* now read it and convert to command strchr */
788		    /* (use "change" as a temporary to hold strchr) */
789		    if (read(0, &ch, 1) != 1)
790		    {
791			/* read error: either 0 or -1 */
792			new_message(MT_standout, " Read error on stdin");
793			putchar('\r');
794			quit(1);
795		    }
796			if (ch == '\r' || ch == '\n') {
797				continue;
798			}
799			cptr = all_commands;
800			while (cptr->c != '\0') {
801				if (cptr->c == ch) {
802					break;
803				}
804				cptr++;
805			}
806			if (cptr->c == '\0') {
807			    new_message(MT_standout, " Command not understood");
808			    putchar('\r');
809				no_command = true;
810			}
811			if (overstrike && !cptr->available_to_dumb)
812			{
813			    new_message(MT_standout,
814			    " Command cannot be handled by this terminal");
815			    putchar('\r');
816				no_command = true;
817			}
818			if (!no_command) {
819			switch(cptr->id)
820			{
821			    case CMD_redraw:	/* redraw screen */
822				reset_display();
823				break;
824
825			    case CMD_update:	/* merely update display */
826				break;
827
828			    case CMD_quit:
829				quit(0);
830				break;
831
832			    case CMD_help:
833				reset_display();
834				top_clear();
835				show_help();
836				top_standout("Hit any key to continue: ");
837				fflush(stdout);
838				read(0, &ch, 1);
839				break;
840
841			    case CMD_errors:	/* show errors */
842				if (error_count() == 0)
843				{
844				    new_message(MT_standout,
845					" Currently no errors to report.");
846				    putchar('\r');
847				    no_command = true;
848				}
849				else
850				{
851				    reset_display();
852				    top_clear();
853				    show_errors();
854				    top_standout("Hit any key to continue: ");
855				    fflush(stdout);
856				    read(0, &ch, 1);
857				}
858				break;
859
860			    case CMD_number:
861				new_message(MT_standout,
862				    "Number of processes to show: ");
863				newval = readline(tempbuf1, 8, true);
864				if (newval > -1)
865				{
866				    if (newval > max_topn)
867				    {
868					new_message(MT_standout | MT_delayed,
869					  " This terminal can only display %d processes.",
870					  max_topn);
871					putchar('\r');
872				    }
873
874				    if (newval == 0)
875				    {
876					/* inhibit the header */
877					display_header(false);
878				    }
879				    else if (newval > topn && topn == 0)
880				    {
881					/* redraw the header */
882					display_header(true);
883					d_header = i_header;
884				    }
885				    topn = newval;
886				}
887				break;
888
889			    case CMD_delay:	/* new seconds delay */
890				new_message(MT_standout, "Seconds to delay: ");
891				if ((i = readline(tempbuf1, 8, false)) > 0)
892				{
893				    double delay_d = strtod(tempbuf1, &nptr);
894				    if (nptr == tempbuf1 || delay_d <= 0)
895				    {
896					new_message(MT_standout, " Invalid delay");
897					putchar('\r');
898					no_command = true;
899				    }
900				    else
901				    {
902					delay.tv_sec = delay_d;
903					delay.tv_usec = (delay_d - delay.tv_sec) * 1e6;
904					clear_message();
905				    }
906				}
907				break;
908
909			    case CMD_grep: /* grep command name */
910				new_message(MT_standout,
911				    "Grep command name (+ for all): ");
912				if (readline(tempbuf1, sizeof(tempbuf1), false) > 0) {
913					free(ps.command);
914					if (tempbuf1[0] == '+' && tempbuf1[1] == '\0') {
915						ps.command = NULL;
916					} else if ((ps.command = strdup(tempbuf1)) == NULL)
917						quit(1);
918				}
919				clear_message();
920				break;
921
922			    case CMD_displays:	/* change display count */
923				new_message(MT_standout,
924					"Displays to show (currently %s): ",
925					displays == -1 ? "infinite" :
926							 itoa(displays));
927				if ((i = readline(tempbuf1, 10, true)) > 0)
928				{
929				    displays = i;
930				}
931				else if (i == 0)
932				{
933				    quit(0);
934				}
935				clear_message();
936				break;
937
938			    case CMD_kill:	/* kill program */
939				new_message(0, "kill ");
940				if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
941				{
942				    if ((errmsg = kill_procs(tempbuf2)) != NULL)
943				    {
944					new_message(MT_standout, "%s", errmsg);
945					putchar('\r');
946					no_command = true;
947				    }
948				}
949				else
950				{
951				    clear_message();
952				}
953				break;
954
955			    case CMD_renice:	/* renice program */
956				new_message(0, "renice ");
957				if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
958				{
959				    if ((errmsg = renice_procs(tempbuf2)) != NULL)
960				    {
961					new_message(MT_standout, "%s", errmsg);
962					putchar('\r');
963					no_command = true;
964				    }
965				}
966				else
967				{
968				    clear_message();
969				}
970				break;
971
972			    case CMD_idletog:
973				ps.idle = !ps.idle;
974				new_message(MT_standout | MT_delayed,
975				    " %sisplaying idle processes.",
976				    ps.idle ? "D" : "Not d");
977				putchar('\r');
978				break;
979
980			    case CMD_selftog:
981				ps.self = !ps.self;
982				new_message(MT_standout | MT_delayed,
983				    " %sisplaying self.",
984				    (ps.self) ? "D" : "Not d");
985				putchar('\r');
986				break;
987
988			    case CMD_user:
989				if (handle_user(tempbuf2, sizeof(tempbuf2)))
990				    no_command = true;
991				break;
992
993			    case CMD_thrtog:
994				ps.thread = !ps.thread;
995				new_message(MT_standout | MT_delayed,
996				    " Displaying threads %s",
997				    ps.thread ? "separately" : "as a count");
998				header_text = format_header(uname_field);
999				reset_display();
1000				putchar('\r');
1001				break;
1002
1003			    case CMD_toggletid:
1004				ps.thread_id = !ps.thread_id;
1005				new_message(MT_standout | MT_delayed,
1006				    " Displaying %s",
1007				    ps.thread_id ? "tid" : "pid");
1008				header_text = format_header(uname_field);
1009				reset_display();
1010				putchar('\r');
1011				break;
1012
1013			    case CMD_wcputog:
1014				ps.wcpu = !ps.wcpu;
1015				new_message(MT_standout | MT_delayed,
1016				    " Displaying %s CPU",
1017				    ps.wcpu ? "weighted" : "raw");
1018				header_text = format_header(uname_field);
1019				reset_display();
1020				putchar('\r');
1021				break;
1022			    case CMD_viewtog:
1023				displaymode = displaymode == DISP_IO ? DISP_CPU : DISP_IO;
1024				new_message(MT_standout | MT_delayed,
1025				    " Displaying %s statistics.",
1026				    displaymode == DISP_IO ? "IO" : "CPU");
1027				header_text = format_header(uname_field);
1028				display_header(true);
1029				d_header = i_header;
1030				reset_display();
1031				break;
1032			    case CMD_viewsys:
1033				ps.system = !ps.system;
1034				new_message(MT_standout | MT_delayed,
1035				    " %sisplaying system processes.",
1036				    ps.system ? "D" : "Not d");
1037				break;
1038			    case CMD_showargs:
1039				fmt_flags ^= FMT_SHOWARGS;
1040				show_args = fmt_flags & FMT_SHOWARGS;
1041				new_message(MT_standout | MT_delayed,
1042				    " %sisplaying process arguments.",
1043				    fmt_flags & FMT_SHOWARGS ? "D" : "Not d");
1044				break;
1045			    case CMD_order:
1046				new_message(MT_standout,
1047				    "Order to sort: ");
1048				if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
1049				{
1050				  if ((i = string_index(tempbuf2, statics.order_names)) == -1)
1051					{
1052					  new_message(MT_standout,
1053					      " %s: unrecognized sorting order", tempbuf2);
1054					  no_command = true;
1055				    }
1056				    else
1057				    {
1058					order_index = i;
1059				    }
1060				    putchar('\r');
1061				}
1062				else
1063				{
1064				    clear_message();
1065				}
1066				break;
1067			    case CMD_jidtog:
1068				ps.jail = !ps.jail;
1069				new_message(MT_standout | MT_delayed,
1070				    " %sisplaying jail ID.",
1071				    ps.jail ? "D" : "Not d");
1072				header_text = format_header(uname_field);
1073				reset_display();
1074				putchar('\r');
1075				break;
1076
1077			    case CMD_jail:
1078				new_message(MT_standout,
1079				    "Jail to show (+ for all): ");
1080				if (readline(tempbuf2, sizeof(tempbuf2), false) > 0)
1081				{
1082				    if (tempbuf2[0] == '+' &&
1083					tempbuf2[1] == '\0')
1084				    {
1085					ps.jid = -1;
1086				    }
1087				    else if ((i = jail_getid(tempbuf2)) == -1)
1088				    {
1089					new_message(MT_standout,
1090					    " %s: unknown jail", tempbuf2);
1091					no_command = true;
1092				    }
1093				    else
1094				    {
1095					ps.jid = i;
1096				    }
1097				    if (ps.jail == 0) {
1098					    ps.jail = 1;
1099					    new_message(MT_standout |
1100						MT_delayed, " Displaying jail "
1101						"ID.");
1102					    header_text =
1103						format_header(uname_field);
1104					    reset_display();
1105				    }
1106				    putchar('\r');
1107				}
1108				else
1109				{
1110				    clear_message();
1111				}
1112				break;
1113
1114			    case CMD_kidletog:
1115				ps.kidle = !ps.kidle;
1116				new_message(MT_standout | MT_delayed,
1117				    " %sisplaying system idle process.",
1118				    ps.kidle ? "D" : "Not d");
1119				putchar('\r');
1120				break;
1121			    case CMD_pcputog:
1122				pcpu_stats = !pcpu_stats;
1123				new_message(MT_standout | MT_delayed,
1124				    " Displaying %sCPU statistics.",
1125				    pcpu_stats ? "per-" : "global ");
1126				toggle_pcpustats();
1127				max_topn = display_updatecpus(&statics);
1128				reset_display();
1129				putchar('\r');
1130				break;
1131			    case CMD_swaptog:
1132				ps.swap = !ps.swap;
1133				new_message(MT_standout | MT_delayed,
1134				    " %sisplaying per-process swap usage.",
1135				    ps.swap ? "D" : "Not d");
1136				header_text = format_header(uname_field);
1137				reset_display();
1138				putchar('\r');
1139				break;
1140			    case CMD_pid:
1141				new_message(MT_standout,
1142					"Process id to show (+ for all): ");
1143				if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) {
1144					if (tempbuf2[0] == '+' &&
1145                   			    tempbuf2[1] == '\0') {
1146						ps.pid = (pid_t)-1;
1147					} else {
1148						unsigned long long num;
1149						const char *errstr;
1150
1151						num = strtonum(tempbuf2, 0, INT_MAX,
1152							&errstr);
1153						if (errstr != NULL || !find_pid(num)) {
1154							new_message(MT_standout,
1155								" %s: unknown pid",
1156								tempbuf2);
1157							no_command = true;
1158						} else {
1159							ps.pid = (pid_t)num;
1160						}
1161					}
1162					putchar('\r');
1163				} else
1164					clear_message();
1165				break;
1166			    case CMD_NONE:
1167					assert(false && "reached switch without command");
1168			}
1169			}
1170		    }
1171
1172		    /* flush out stuff that may have been written */
1173		    fflush(stdout);
1174		}
1175	    }
1176    }
1177
1178#ifdef DEBUG
1179    fclose(debug);
1180#endif
1181    quit(0);
1182}
1183
1184/*
1185 *  reset_display() - reset all the display routine pointers so that entire
1186 *	screen will get redrawn.
1187 */
1188
1189static void
1190reset_display(void)
1191{
1192    d_loadave    = i_loadave;
1193    d_procstates = i_procstates;
1194    d_cpustates  = i_cpustates;
1195    d_memory     = i_memory;
1196    d_arc        = i_arc;
1197    d_carc       = i_carc;
1198    d_swap       = i_swap;
1199    d_message	 = i_message;
1200    d_header	 = i_header;
1201    d_process	 = i_process;
1202}
1203
1204/*
1205 *  signal handlers
1206 */
1207
1208static sigret_t
1209leave(int i __unused)	/* exit under normal conditions -- INT handler */
1210{
1211
1212    leaveflag = 1;
1213}
1214
1215static sigret_t
1216tstop(int i __unused)	/* SIGTSTP handler */
1217{
1218
1219    tstopflag = 1;
1220}
1221
1222static sigret_t
1223top_winch(int i __unused)		/* SIGWINCH handler */
1224{
1225
1226    winchflag = 1;
1227}
1228
1229void __dead2
1230quit(int status)		/* exit under duress */
1231{
1232    end_screen();
1233    exit(status);
1234}
1235