xref: /openbsd-src/usr.bin/top/top.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: top.c,v 1.75 2010/04/24 22:02:14 deraadt Exp $	*/
2 
3 /*
4  *  Top users/processes display for Unix
5  *  Version 3
6  *
7  * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8  * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/types.h>
32 #include <curses.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <stdio.h>
36 #include <signal.h>
37 #include <string.h>
38 #include <poll.h>
39 #include <stdlib.h>
40 #include <limits.h>
41 #include <unistd.h>
42 
43 /* includes specific to top */
44 #include "display.h"		/* interface to display package */
45 #include "screen.h"		/* interface to screen package */
46 #include "top.h"
47 #include "top.local.h"
48 #include "boolean.h"
49 #include "machine.h"
50 #include "utils.h"
51 
52 /* Size of the stdio buffer given to stdout */
53 #define BUFFERSIZE	2048
54 
55 /* The buffer that stdio will use */
56 char		stdoutbuf[BUFFERSIZE];
57 
58 /* signal handling routines */
59 static void	leave(int);
60 static void	onalrm(int);
61 static void	tstop(int);
62 static void	sigwinch(int);
63 
64 volatile sig_atomic_t leaveflag, tstopflag, winchflag;
65 
66 static void	reset_display(void);
67 int		rundisplay(void);
68 
69 static int	max_topn;	/* maximum displayable processes */
70 
71 extern int	(*proc_compares[])(const void *, const void *);
72 int order_index;
73 
74 int displays = 0;	/* indicates unspecified */
75 char do_unames = Yes;
76 struct process_select ps;
77 char interactive = Maybe;
78 double delay = Default_DELAY;
79 char *order_name = NULL;
80 int topn = Default_TOPN;
81 int no_command = Yes;
82 int old_system = No;
83 int old_threads = No;
84 int show_args = No;
85 pid_t hlpid = -1;
86 int combine_cpus = 0;
87 
88 #if Default_TOPN == Infinity
89 char topn_specified = No;
90 #endif
91 
92 /*
93  * these defines enumerate the "strchr"s of the commands in
94  * command_chars
95  */
96 #define CMD_redraw	0
97 #define CMD_update	1
98 #define CMD_quit	2
99 #define CMD_help1	3
100 #define CMD_help2	4
101 #define CMD_OSLIMIT	4	/* terminals with OS can only handle commands */
102 #define CMD_errors	5	/* less than or equal to CMD_OSLIMIT	   */
103 #define CMD_number1	6
104 #define CMD_number2	7
105 #define CMD_delay	8
106 #define CMD_displays	9
107 #define CMD_kill	10
108 #define CMD_renice	11
109 #define CMD_idletog	12
110 #define CMD_idletog2	13
111 #define CMD_user	14
112 #define CMD_system	15
113 #define CMD_order	16
114 #define CMD_pid		17
115 #define CMD_command	18
116 #define CMD_threads	19
117 #define CMD_grep	20
118 #define CMD_add		21
119 #define CMD_hl		22
120 #define CMD_cpus	23
121 
122 static void
123 usage(void)
124 {
125 	extern char *__progname;
126 
127 	fprintf(stderr,
128 	    "usage: %s [-1bCIinqSTu] [-d count] [-g string] [-o field] "
129 	    "[-p pid] [-s time]\n\t[-U user] [number]\n",
130 	    __progname);
131 }
132 
133 static void
134 parseargs(int ac, char **av)
135 {
136 	char *endp;
137 	int i;
138 
139 	while ((i = getopt(ac, av, "1STICbinqus:d:p:U:o:g:")) != -1) {
140 		switch (i) {
141 		case '1':
142 			combine_cpus = 1;
143 			break;
144 		case 'C':
145 			show_args = Yes;
146 			break;
147 		case 'u':	/* toggle uid/username display */
148 			do_unames = !do_unames;
149 			break;
150 
151 		case 'U':	/* display only username's processes */
152 			if ((ps.uid = userid(optarg)) == (uid_t)-1)
153 				new_message(MT_delayed, "%s: unknown user",
154 				    optarg);
155 			break;
156 
157 		case 'p': {	/* display only process id */
158 			const char *errstr;
159 
160 			i = strtonum(optarg, 0, INT_MAX, &errstr);
161 			if (errstr != NULL || !find_pid(i))
162 				new_message(MT_delayed, "%s: unknown pid",
163 				    optarg);
164 			else {
165 				ps.pid = (pid_t)i;
166 				ps.system = Yes;
167 			}
168 			break;
169 		}
170 
171 		case 'S':	/* show system processes */
172 			ps.system = !ps.system;
173 			old_system = !old_system;
174 			break;
175 
176 		case 'T':	/* show threads */
177 			ps.threads = Yes;
178 			old_threads = Yes;
179 			break;
180 
181 		case 'I':	/* show idle processes */
182 			ps.idle = !ps.idle;
183 			break;
184 
185 		case 'i':	/* go interactive regardless */
186 			interactive = Yes;
187 			break;
188 
189 		case 'n':	/* batch, or non-interactive */
190 		case 'b':
191 			interactive = No;
192 			break;
193 
194 		case 'd':	/* number of displays to show */
195 			if ((i = atoiwi(optarg)) != Invalid && i != 0) {
196 				displays = i;
197 				if (displays == 1)
198 					interactive = No;
199 				break;
200 			}
201 			new_message(MT_delayed,
202 			    "warning: display count should be positive "
203 			    "-- option ignored");
204 			break;
205 
206 		case 's':
207 			delay = strtod(optarg, &endp);
208 
209 			if (delay >= 0 && delay <= 1000000 && *endp == '\0')
210 				break;
211 
212 			new_message(MT_delayed,
213 			    "warning: delay should be a non-negative number"
214 			    " -- using default");
215 			delay = Default_DELAY;
216 			break;
217 
218 		case 'q':	/* be quick about it */
219 			/* only allow this if user is really root */
220 			if (getuid() == 0) {
221 				/* be very un-nice! */
222 				(void) nice(-20);
223 				break;
224 			}
225 			new_message(MT_delayed,
226 			    "warning: `-q' option can only be used by root");
227 			break;
228 
229 		case 'o':	/* select sort order */
230 			order_name = optarg;
231 			break;
232 
233 		case 'g':	/* grep command name */
234 			free(ps.command);
235 			if ((ps.command = strdup(optarg)) == NULL)
236 				err(1, NULL);
237 			break;
238 
239 		default:
240 			usage();
241 			exit(1);
242 		}
243 	}
244 
245 	/* get count of top processes to display (if any) */
246 	if (optind < ac) {
247 		if ((topn = atoiwi(av[optind])) == Invalid) {
248 			new_message(MT_delayed,
249 			    "warning: process count should "
250 			    "be a non-negative number -- using default");
251 			topn = Infinity;
252 		}
253 #if Default_TOPN == Infinity
254 		else
255 			topn_specified = Yes;
256 #endif
257 	}
258 }
259 
260 struct system_info system_info;
261 struct statics  statics;
262 
263 int
264 main(int argc, char *argv[])
265 {
266 	char *uname_field = "USERNAME", *header_text, *env_top;
267 	char *(*get_userid)(uid_t) = username, **preset_argv, **av;
268 	int preset_argc = 0, ac, active_procs, i;
269 	sigset_t mask, oldmask;
270 	time_t curr_time;
271 	caddr_t processes;
272 
273 	/* set the buffer for stdout */
274 #ifdef DEBUG
275 	setbuffer(stdout, NULL, 0);
276 #else
277 	setbuffer(stdout, stdoutbuf, sizeof stdoutbuf);
278 #endif
279 
280 	/* initialize some selection options */
281 	ps.idle = Yes;
282 	ps.system = No;
283 	ps.uid = (uid_t)-1;
284 	ps.pid = (pid_t)-1;
285 	ps.command = NULL;
286 
287 	/* get preset options from the environment */
288 	if ((env_top = getenv("TOP")) != NULL) {
289 		av = preset_argv = argparse(env_top, &preset_argc);
290 		ac = preset_argc;
291 
292 		/*
293 		 * set the dummy argument to an explanatory message, in case
294 		 * getopt encounters a bad argument
295 		 */
296 		preset_argv[0] = "while processing environment";
297 	}
298 	/* process options */
299 	do {
300 		/*
301 		 * if we're done doing the presets, then process the real
302 		 * arguments
303 		 */
304 		if (preset_argc == 0) {
305 			ac = argc;
306 			av = argv;
307 			optind = 1;
308 		}
309 		parseargs(ac, av);
310 		i = preset_argc;
311 		preset_argc = 0;
312 	} while (i != 0);
313 
314 	/* set constants for username/uid display correctly */
315 	if (!do_unames) {
316 		uname_field = "   UID  ";
317 		get_userid = format_uid;
318 	}
319 	/* initialize the kernel memory interface */
320 	if (machine_init(&statics) == -1)
321 		exit(1);
322 
323 	/* determine sorting order index, if necessary */
324 	if (order_name != NULL) {
325 		if ((order_index = string_index(order_name,
326 		    statics.order_names)) == -1) {
327 			char **pp, msg[512];
328 
329 			snprintf(msg, sizeof(msg),
330 			    "'%s' is not a recognized sorting order",
331 			    order_name);
332 			strlcat(msg, ". Valid are:", sizeof(msg));
333 			pp = statics.order_names;
334 			while (*pp != NULL) {
335 				strlcat(msg, " ", sizeof(msg));
336 				strlcat(msg, *pp++, sizeof(msg));
337 			}
338 			new_message(MT_delayed, msg);
339 			order_index = 0;
340 		}
341 	}
342 
343 	/* initialize termcap */
344 	init_termcap(interactive);
345 
346 	/* get the string to use for the process area header */
347 	header_text = format_header(uname_field);
348 
349 	/* initialize display interface */
350 	max_topn = display_init(&statics);
351 
352 	/* print warning if user requested more processes than we can display */
353 	if (topn > max_topn)
354 		new_message(MT_delayed,
355 		    "warning: this terminal can only display %d processes",
356 		    max_topn);
357 	/* adjust for topn == Infinity */
358 	if (topn == Infinity) {
359 		/*
360 		 *  For smart terminals, infinity really means everything that can
361 		 *  be displayed, or Largest.
362 		 *  On dumb terminals, infinity means every process in the system!
363 		 *  We only really want to do that if it was explicitly specified.
364 		 *  This is always the case when "Default_TOPN != Infinity".  But if
365 		 *  topn wasn't explicitly specified and we are on a dumb terminal
366 		 *  and the default is Infinity, then (and only then) we use
367 		 *  "Nominal_TOPN" instead.
368 		 */
369 #if Default_TOPN == Infinity
370 		topn = smart_terminal ? Largest :
371 		    (topn_specified ? Largest : Nominal_TOPN);
372 #else
373 		topn = Largest;
374 #endif
375 	}
376 	/* set header display accordingly */
377 	display_header(topn > 0);
378 
379 	/* determine interactive state */
380 	if (interactive == Maybe)
381 		interactive = smart_terminal;
382 
383 	/* if # of displays not specified, fill it in */
384 	if (displays == 0)
385 		displays = smart_terminal ? Infinity : 1;
386 
387 	/*
388 	 * block interrupt signals while setting up the screen and the
389 	 * handlers
390 	 */
391 	sigemptyset(&mask);
392 	sigaddset(&mask, SIGINT);
393 	sigaddset(&mask, SIGQUIT);
394 	sigaddset(&mask, SIGTSTP);
395 	sigprocmask(SIG_BLOCK, &mask, &oldmask);
396 	if (interactive)
397 		init_screen();
398 	(void) signal(SIGINT, leave);
399 	siginterrupt(SIGINT, 1);
400 	(void) signal(SIGQUIT, leave);
401 	(void) signal(SIGTSTP, tstop);
402 	if (smart_terminal)
403 		(void) signal(SIGWINCH, sigwinch);
404 	sigprocmask(SIG_SETMASK, &oldmask, NULL);
405 restart:
406 
407 	/*
408 	 *  main loop -- repeat while display count is positive or while it
409 	 *		indicates infinity (by being -1)
410 	 */
411 	while ((displays == -1) || (displays-- > 0)) {
412 		if (winchflag) {
413 			/*
414 			 * reascertain the screen
415 			 * dimensions
416 			 */
417 			get_screensize();
418 			resizeterm(screen_length, screen_width + 1);
419 
420 			/* tell display to resize */
421 			max_topn = display_resize();
422 
423 			/* reset the signal handler */
424 			(void) signal(SIGWINCH, sigwinch);
425 
426 			reset_display();
427 			winchflag = 0;
428 		}
429 
430 		/* get the current stats */
431 		get_system_info(&system_info);
432 
433 		/* get the current set of processes */
434 		processes = get_process_info(&system_info, &ps,
435 		    proc_compares[order_index]);
436 
437 		/* display the load averages */
438 		i_loadave(system_info.last_pid, system_info.load_avg);
439 
440 		/* display the current time */
441 		/* this method of getting the time SHOULD be fairly portable */
442 		time(&curr_time);
443 		i_timeofday(&curr_time);
444 
445 		/* display process state breakdown */
446 		i_procstates(system_info.p_total, system_info.procstates);
447 
448 		/* display the cpu state percentage breakdown */
449 		i_cpustates(system_info.cpustates);
450 
451 		/* display memory stats */
452 		i_memory(system_info.memory);
453 
454 		/* handle message area */
455 		i_message();
456 
457 		/* update the header area */
458 		i_header(header_text);
459 
460 		if (topn == Infinity) {
461 #if Default_TOPN == Infinity
462 			topn = smart_terminal ? Largest :
463 			    (topn_specified ? Largest : Nominal_TOPN);
464 #else
465 			topn = Largest;
466 #endif
467 		}
468 
469 		if (topn > 0) {
470 			/* determine number of processes to actually display */
471 			/*
472 			 * this number will be the smallest of:  active
473 			 * processes, number user requested, number current
474 			 * screen accommodates
475 			 */
476 			active_procs = system_info.p_active;
477 			if (active_procs > topn)
478 				active_procs = topn;
479 			if (active_procs > max_topn)
480 				active_procs = max_topn;
481 			/* now show the top "n" processes. */
482 			for (i = 0; i < active_procs; i++) {
483 				pid_t pid;
484 				char * s;
485 
486 				s = format_next_process(processes, get_userid,
487 				    &pid);
488 				i_process(i, s, pid == hlpid);
489 			}
490 		}
491 
492 		/* do end-screen processing */
493 		u_endscreen();
494 
495 		/* now, flush the output buffer */
496 		fflush(stdout);
497 
498 		if (smart_terminal)
499 			refresh();
500 
501 		/* only do the rest if we have more displays to show */
502 		if (displays) {
503 			/* switch out for new display on smart terminals */
504 			no_command = Yes;
505 			if (!interactive) {
506 				/* set up alarm */
507 				(void) signal(SIGALRM, onalrm);
508 				(void) alarm((unsigned) delay);
509 
510 				/* wait for the rest of it .... */
511 				pause();
512 				if (leaveflag)
513 					exit(0);
514 				if (tstopflag) {
515 					(void) signal(SIGTSTP, SIG_DFL);
516 					(void) kill(0, SIGTSTP);
517 					/* reset the signal handler */
518 					(void) signal(SIGTSTP, tstop);
519 					tstopflag = 0;
520 				}
521 			} else {
522 				while (no_command)
523 					if (rundisplay())
524 						goto restart;
525 			}
526 		}
527 	}
528 
529 	quit(0);
530 	/* NOTREACHED */
531 	return (0);
532 }
533 
534 int
535 rundisplay(void)
536 {
537 	static char tempbuf[TEMPBUFSIZE];
538 	sigset_t mask;
539 	char ch, *iptr;
540 	int change, i;
541 	struct pollfd pfd[1];
542 	uid_t uid;
543 	static char command_chars[] = "\f qh?en#sdkriIuSopCTg+P1";
544 
545 	/*
546 	 * assume valid command unless told
547 	 * otherwise
548 	 */
549 	no_command = No;
550 
551 	/*
552 	 * set up arguments for select with
553 	 * timeout
554 	 */
555 	pfd[0].fd = STDIN_FILENO;
556 	pfd[0].events = POLLIN;
557 
558 	if (leaveflag)
559 		quit(0);
560 	if (tstopflag) {
561 		/* move to the lower left */
562 		end_screen();
563 		fflush(stdout);
564 
565 		/*
566 		 * default the signal handler
567 		 * action
568 		 */
569 		(void) signal(SIGTSTP, SIG_DFL);
570 
571 		/*
572 		 * unblock the signal and
573 		 * send ourselves one
574 		 */
575 		sigemptyset(&mask);
576 		sigaddset(&mask, SIGTSTP);
577 		sigprocmask(SIG_UNBLOCK, &mask, NULL);
578 		(void) kill(0, SIGTSTP);
579 
580 		/* reset the signal handler */
581 		(void) signal(SIGTSTP, tstop);
582 
583 		/* reinit screen */
584 		reinit_screen();
585 		reset_display();
586 		tstopflag = 0;
587 		return 1;
588 	}
589 	/*
590 	 * wait for either input or the end
591 	 * of the delay period
592 	 */
593 	if (poll(pfd, 1, (int)(delay * 1000)) > 0 &&
594 	    !(pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL))) {
595 		char *errmsg;
596 		ssize_t len;
597 
598 		clear_message();
599 
600 		/*
601 		 * now read it and convert to
602 		 * command strchr
603 		 */
604 		while (1) {
605 			len = read(STDIN_FILENO, &ch, 1);
606 			if (len == -1 && errno == EINTR)
607 				continue;
608 			if (len == 0)
609 				exit(1);
610 			break;
611 		}
612 		if ((iptr = strchr(command_chars, ch)) == NULL) {
613 			/* illegal command */
614 			new_message(MT_standout, " Command not understood");
615 			putr();
616 			no_command = Yes;
617 			fflush(stdout);
618 			return (0);
619 		}
620 
621 		change = iptr - command_chars;
622 
623 		switch (change) {
624 		case CMD_redraw:	/* redraw screen */
625 			reset_display();
626 			break;
627 
628 		case CMD_update:	/* merely update display */
629 			/*
630 			 * is the load average high?
631 			 */
632 			if (system_info.load_avg[0] > LoadMax) {
633 				/* yes, go home for visual feedback */
634 				go_home();
635 				fflush(stdout);
636 			}
637 			break;
638 
639 		case CMD_quit:	/* quit */
640 			quit(0);
641 			break;
642 
643 		case CMD_help1:	/* help */
644 		case CMD_help2:
645 			clear();
646 			show_help();
647 			anykey();
648 			clear();
649 			break;
650 
651 		case CMD_errors:	/* show errors */
652 			if (error_count() == 0) {
653 				new_message(MT_standout,
654 				    " Currently no errors to report.");
655 				putr();
656 				no_command = Yes;
657 			} else {
658 				clear();
659 				show_errors();
660 				anykey();
661 				clear();
662 			}
663 			break;
664 
665 		case CMD_number1:	/* new number */
666 		case CMD_number2:
667 			new_message(MT_standout,
668 			    "Number of processes to show: ");
669 
670 			if (readline(tempbuf, 8) > 0) {
671 				if ((i = atoiwi(tempbuf)) != Invalid) {
672 					if (i > max_topn) {
673 						new_message(MT_standout |
674 						    MT_delayed,
675 						    " This terminal can only "
676 						    "display %d processes.",
677 						    max_topn);
678 						putr();
679 					}
680 					if ((i > topn || i == Infinity)
681 					    && topn == 0) {
682 						/* redraw the header */
683 						display_header(Yes);
684 					} else if (i == 0)
685 						display_header(No);
686 					topn = i;
687 				} else {
688 					new_message(MT_standout,
689 					    "Processes should be a "
690 					    "non-negative number");
691  					putr();
692 					no_command = Yes;
693 				}
694 			} else
695 				clear_message();
696 			break;
697 
698 		case CMD_delay:	/* new seconds delay */
699 			new_message(MT_standout, "Seconds to delay: ");
700 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
701 				char *endp;
702 				double newdelay = strtod(tempbuf, &endp);
703 
704 				if (newdelay >= 0 && newdelay <= 1000000 &&
705 				    *endp == '\0') {
706 					delay = newdelay;
707 				} else {
708 					new_message(MT_standout,
709 					    "Delay should be a non-negative number");
710 					putr();
711 					no_command = Yes;
712 				}
713 
714 			} else
715 				clear_message();
716 			break;
717 
718 		case CMD_displays:	/* change display count */
719 			new_message(MT_standout,
720 			    "Displays to show (currently %s): ",
721 			    displays == -1 ? "infinite" :
722 			    itoa(displays));
723 
724 			if (readline(tempbuf, 10) > 0) {
725 				if ((i = atoiwi(tempbuf)) != Invalid) {
726 					if (i == 0)
727 						quit(0);
728 					displays = i;
729 				} else {
730 					new_message(MT_standout,
731 					    "Displays should be a non-negative number");
732 					putr();
733 					no_command = Yes;
734 				}
735 			} else
736 				clear_message();
737 			break;
738 
739 		case CMD_kill:	/* kill program */
740 			new_message(0, "kill ");
741 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
742 				if ((errmsg = kill_procs(tempbuf)) != NULL) {
743 					new_message(MT_standout, "%s", errmsg);
744 					putr();
745 					no_command = Yes;
746 				}
747 			} else
748 				clear_message();
749 			break;
750 
751 		case CMD_renice:	/* renice program */
752 			new_message(0, "renice ");
753 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
754 				if ((errmsg = renice_procs(tempbuf)) != NULL) {
755 					new_message(MT_standout, "%s", errmsg);
756 					putr();
757 					no_command = Yes;
758 				}
759 			} else
760 				clear_message();
761 			break;
762 
763 		case CMD_idletog:
764 		case CMD_idletog2:
765 			ps.idle = !ps.idle;
766 			new_message(MT_standout | MT_delayed,
767 			    " %sisplaying idle processes.",
768 			    ps.idle ? "D" : "Not d");
769 			putr();
770 			break;
771 
772 		case CMD_user:
773 			new_message(MT_standout,
774 			    "Username to show: ");
775 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
776 				if (tempbuf[0] == '+' &&
777 				    tempbuf[1] == '\0') {
778 					ps.uid = (uid_t)-1;
779 				} else if ((uid = userid(tempbuf)) == (uid_t)-1) {
780 					new_message(MT_standout,
781 					    " %s: unknown user", tempbuf);
782 					no_command = Yes;
783 				} else
784 					ps.uid = uid;
785 				putr();
786 			} else
787 				clear_message();
788 			break;
789 
790 		case CMD_system:
791 			ps.system = !ps.system;
792 			old_system = ps.system;
793 			new_message(MT_standout | MT_delayed,
794 			    " %sisplaying system processes.",
795 			    ps.system ? "D" : "Not d");
796 			break;
797 
798 		case CMD_order:
799 			new_message(MT_standout,
800 			    "Order to sort: ");
801 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
802 				if ((i = string_index(tempbuf,
803 				    statics.order_names)) == -1) {
804 					new_message(MT_standout,
805 					    " %s: unrecognized sorting order",
806 					    tempbuf);
807 					no_command = Yes;
808 				} else
809 					order_index = i;
810 				putr();
811 			} else
812 				clear_message();
813 			break;
814 
815 		case CMD_pid:
816 			new_message(MT_standout, "Process ID to show: ");
817 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
818 				if (tempbuf[0] == '+' &&
819 				    tempbuf[1] == '\0') {
820 					ps.pid = (pid_t)-1;
821 					ps.system = old_system;
822 				} else {
823 					unsigned long long num;
824 					const char *errstr;
825 
826 					num = strtonum(tempbuf, 0, INT_MAX,
827 					    &errstr);
828 					if (errstr != NULL || !find_pid(num)) {
829 						new_message(MT_standout,
830 						    " %s: unknown pid",
831 						    tempbuf);
832 						no_command = Yes;
833 					} else {
834 						if (ps.system == No)
835 							old_system = No;
836 						ps.pid = (pid_t)num;
837 						ps.system = Yes;
838 					}
839 				}
840 				putr();
841 			} else
842 				clear_message();
843 			break;
844 
845 		case CMD_command:
846 			show_args = (show_args == No) ? Yes : No;
847 			break;
848 
849 		case CMD_threads:
850 			ps.threads = !ps.threads;
851 			old_threads = ps.threads;
852 			new_message(MT_standout | MT_delayed,
853 			    " %sisplaying threads.",
854 			    ps.threads ? "D" : "Not d");
855 			break;
856 
857 		case CMD_grep:
858 			new_message(MT_standout,
859 			    "Grep command name: ");
860 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
861 				free(ps.command);
862 				if (tempbuf[0] == '+' &&
863 				    tempbuf[1] == '\0')
864 					ps.command = NULL;
865 				else
866 					if ((ps.command = strdup(tempbuf)) ==
867 					    NULL)
868 						err(1, NULL);
869 				putr();
870 			} else
871 				clear_message();
872 			break;
873 
874 		case CMD_hl:
875 			new_message(MT_standout, "Process ID to highlight: ");
876 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
877 				if (tempbuf[0] == '+' &&
878 				    tempbuf[1] == '\0') {
879 					hlpid = -1;
880 				} else {
881 					unsigned long long num;
882 					const char *errstr;
883 
884 					num = strtonum(tempbuf, 0, INT_MAX,
885 					    &errstr);
886 					if (errstr != NULL || !find_pid(num)) {
887 						new_message(MT_standout,
888 						    " %s: unknown pid",
889 						    tempbuf);
890 						no_command = Yes;
891 					} else
892 						hlpid = (pid_t)num;
893 				}
894 				putr();
895 			} else
896 				clear_message();
897 			break;
898 
899 		case CMD_add:
900 			ps.uid = (uid_t)-1;	/* uid */
901 			ps.pid = (pid_t)-1; 	/* pid */
902 			ps.system = old_system;
903 			ps.command = NULL;	/* grep */
904 			hlpid = -1;
905 			break;
906 		case CMD_cpus:
907 			combine_cpus = !combine_cpus;
908 			max_topn = display_resize();
909 			reset_display();
910 			break;
911 		default:
912 			new_message(MT_standout, " BAD CASE IN SWITCH!");
913 			putr();
914 		}
915 	}
916 
917 	/* flush out stuff that may have been written */
918 	fflush(stdout);
919 	return 0;
920 }
921 
922 
923 /*
924  *  reset_display() - reset all the display routine pointers so that entire
925  *	screen will get redrawn.
926  */
927 static void
928 reset_display(void)
929 {
930 	if (smart_terminal) {
931 		clear();
932 		refresh();
933 	}
934 }
935 
936 /* ARGSUSED */
937 void
938 leave(int signo)
939 {
940 	leaveflag = 1;
941 }
942 
943 /* ARGSUSED */
944 void
945 tstop(int signo)
946 {
947 	tstopflag = 1;
948 }
949 
950 /* ARGSUSED */
951 void
952 sigwinch(int signo)
953 {
954 	winchflag = 1;
955 }
956 
957 /* ARGSUSED */
958 void
959 onalrm(int signo)
960 {
961 }
962 
963 void
964 quit(int ret)
965 {
966 	end_screen();
967 	exit(ret);
968 }
969