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