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