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