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