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