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