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