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