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