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