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