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