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