xref: /openbsd-src/usr.bin/top/top.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: top.c,v 1.80 2013/01/14 21:33:59 guenther 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 	    !(pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL))) {
606 		char *errmsg;
607 		ssize_t len;
608 
609 		clear_message();
610 
611 		/*
612 		 * now read it and convert to
613 		 * command strchr
614 		 */
615 		while (1) {
616 			len = read(STDIN_FILENO, &ch, 1);
617 			if (len == -1 && errno == EINTR)
618 				continue;
619 			if (len == 0)
620 				exit(1);
621 			break;
622 		}
623 		if ((iptr = strchr(command_chars, ch)) == NULL) {
624 			/* illegal command */
625 			new_message(MT_standout, " Command not understood");
626 			putr();
627 			no_command = Yes;
628 			fflush(stdout);
629 			return (0);
630 		}
631 
632 		change = iptr - command_chars;
633 
634 		switch (change) {
635 		case CMD_redraw:	/* redraw screen */
636 			reset_display();
637 			break;
638 
639 		case CMD_update:	/* merely update display */
640 			/*
641 			 * is the load average high?
642 			 */
643 			if (system_info.load_avg[0] > LoadMax) {
644 				/* yes, go home for visual feedback */
645 				go_home();
646 				fflush(stdout);
647 			}
648 			break;
649 
650 		case CMD_quit:	/* quit */
651 			quit(0);
652 			break;
653 
654 		case CMD_help1:	/* help */
655 		case CMD_help2:
656 			clear();
657 			show_help();
658 			anykey();
659 			clear();
660 			break;
661 
662 		case CMD_errors:	/* show errors */
663 			if (error_count() == 0) {
664 				new_message(MT_standout,
665 				    " Currently no errors to report.");
666 				putr();
667 				no_command = Yes;
668 			} else {
669 				clear();
670 				show_errors();
671 				anykey();
672 				clear();
673 			}
674 			break;
675 
676 		case CMD_number1:	/* new number */
677 		case CMD_number2:
678 			new_message(MT_standout,
679 			    "Number of processes to show: ");
680 
681 			if (readline(tempbuf, 8) > 0) {
682 				if ((i = atoiwi(tempbuf)) != Invalid) {
683 					if (i > max_topn) {
684 						new_message(MT_standout |
685 						    MT_delayed,
686 						    " This terminal can only "
687 						    "display %d processes.",
688 						    max_topn);
689 						putr();
690 					}
691 					if ((i > topn || i == Infinity)
692 					    && topn == 0) {
693 						/* redraw the header */
694 						display_header(Yes);
695 					} else if (i == 0)
696 						display_header(No);
697 					topn = i;
698 				} else {
699 					new_message(MT_standout,
700 					    "Processes should be a "
701 					    "non-negative number");
702  					putr();
703 					no_command = Yes;
704 				}
705 			} else
706 				clear_message();
707 			break;
708 
709 		case CMD_delay:	/* new seconds delay */
710 			new_message(MT_standout, "Seconds to delay: ");
711 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
712 				char *endp;
713 				double newdelay = strtod(tempbuf, &endp);
714 
715 				if (newdelay >= 0 && newdelay <= 1000000 &&
716 				    *endp == '\0') {
717 					delay = newdelay;
718 				} else {
719 					new_message(MT_standout,
720 					    "Delay should be a non-negative number");
721 					putr();
722 					no_command = Yes;
723 				}
724 
725 			} else
726 				clear_message();
727 			break;
728 
729 		case CMD_displays:	/* change display count */
730 			new_message(MT_standout,
731 			    "Displays to show (currently %s): ",
732 			    displays == -1 ? "infinite" :
733 			    itoa(displays));
734 
735 			if (readline(tempbuf, 10) > 0) {
736 				if ((i = atoiwi(tempbuf)) != Invalid) {
737 					if (i == 0)
738 						quit(0);
739 					displays = i;
740 				} else {
741 					new_message(MT_standout,
742 					    "Displays should be a non-negative number");
743 					putr();
744 					no_command = Yes;
745 				}
746 			} else
747 				clear_message();
748 			break;
749 
750 		case CMD_kill:	/* kill program */
751 			new_message(0, "kill ");
752 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
753 				if ((errmsg = kill_procs(tempbuf)) != NULL) {
754 					new_message(MT_standout, "%s", errmsg);
755 					putr();
756 					no_command = Yes;
757 				}
758 			} else
759 				clear_message();
760 			break;
761 
762 		case CMD_renice:	/* renice program */
763 			new_message(0, "renice ");
764 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
765 				if ((errmsg = renice_procs(tempbuf)) != NULL) {
766 					new_message(MT_standout, "%s", errmsg);
767 					putr();
768 					no_command = Yes;
769 				}
770 			} else
771 				clear_message();
772 			break;
773 
774 		case CMD_idletog:
775 		case CMD_idletog2:
776 			ps.idle = !ps.idle;
777 			new_message(MT_standout | MT_delayed,
778 			    " %sisplaying idle processes.",
779 			    ps.idle ? "D" : "Not d");
780 			putr();
781 			break;
782 
783 		case CMD_user:
784 			new_message(MT_standout,
785 			    "Username to show: ");
786 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
787 				if ((tempbuf[0] == '+' || tempbuf[0] == '-') &&
788 				    tempbuf[1] == '\0') {
789 					ps.uid = (uid_t)-1;
790 					ps.huid = (uid_t)-1;
791 				} else if (tempbuf[0] == '-') {
792 					if ((huid = userid(tempbuf+1)) == (uid_t)-1) {
793 						new_message(MT_standout,
794 						    " %s: unknown user", tempbuf+1);
795 						no_command = Yes;
796 					} else {
797 						ps.huid = huid;
798 						ps.uid = (uid_t)-1;
799 					}
800 				} else if ((uid = userid(tempbuf)) == (uid_t)-1) {
801 						new_message(MT_standout,
802 					    	    " %s: unknown user", tempbuf);
803 						no_command = Yes;
804 				} else {
805 					ps.uid = uid;
806 					ps.huid = (uid_t)-1;
807 				}
808 				putr();
809 			} else
810 				clear_message();
811 			break;
812 
813 		case CMD_system:
814 			ps.system = !ps.system;
815 			old_system = ps.system;
816 			new_message(MT_standout | MT_delayed,
817 			    " %sisplaying system processes.",
818 			    ps.system ? "D" : "Not d");
819 			break;
820 
821 		case CMD_order:
822 			new_message(MT_standout,
823 			    "Order to sort: ");
824 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
825 				if ((i = string_index(tempbuf,
826 				    statics.order_names)) == -1) {
827 					new_message(MT_standout,
828 					    " %s: unrecognized sorting order",
829 					    tempbuf);
830 					no_command = Yes;
831 				} else
832 					order_index = i;
833 				putr();
834 			} else
835 				clear_message();
836 			break;
837 
838 		case CMD_pid:
839 			new_message(MT_standout, "Process ID to show: ");
840 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
841 				if (tempbuf[0] == '+' &&
842 				    tempbuf[1] == '\0') {
843 					ps.pid = (pid_t)-1;
844 					ps.system = old_system;
845 				} else {
846 					unsigned long long num;
847 					const char *errstr;
848 
849 					num = strtonum(tempbuf, 0, INT_MAX,
850 					    &errstr);
851 					if (errstr != NULL || !find_pid(num)) {
852 						new_message(MT_standout,
853 						    " %s: unknown pid",
854 						    tempbuf);
855 						no_command = Yes;
856 					} else {
857 						if (ps.system == No)
858 							old_system = No;
859 						ps.pid = (pid_t)num;
860 						ps.system = Yes;
861 					}
862 				}
863 				putr();
864 			} else
865 				clear_message();
866 			break;
867 
868 		case CMD_command:
869 			show_args = (show_args == No) ? Yes : No;
870 			break;
871 
872 		case CMD_threads:
873 			ps.threads = !ps.threads;
874 			old_threads = ps.threads;
875 			new_message(MT_standout | MT_delayed,
876 			    " %sisplaying threads.",
877 			    ps.threads ? "D" : "Not d");
878 			break;
879 
880 		case CMD_grep:
881 			new_message(MT_standout,
882 			    "Grep command name: ");
883 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
884 				free(ps.command);
885 				if (tempbuf[0] == '+' &&
886 				    tempbuf[1] == '\0')
887 					ps.command = NULL;
888 				else
889 					if ((ps.command = strdup(tempbuf)) ==
890 					    NULL)
891 						err(1, NULL);
892 				putr();
893 			} else
894 				clear_message();
895 			break;
896 
897 		case CMD_hl:
898 			new_message(MT_standout, "Process ID to highlight: ");
899 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
900 				if (tempbuf[0] == '+' &&
901 				    tempbuf[1] == '\0') {
902 					hlpid = -1;
903 				} else {
904 					unsigned long long num;
905 					const char *errstr;
906 
907 					num = strtonum(tempbuf, 0, INT_MAX,
908 					    &errstr);
909 					if (errstr != NULL || !find_pid(num)) {
910 						new_message(MT_standout,
911 						    " %s: unknown pid",
912 						    tempbuf);
913 						no_command = Yes;
914 					} else
915 						hlpid = (pid_t)num;
916 				}
917 				putr();
918 			} else
919 				clear_message();
920 			break;
921 
922 		case CMD_add:
923 			ps.uid = (uid_t)-1;	/* uid */
924 			ps.huid = (uid_t)-1;
925 			ps.pid = (pid_t)-1; 	/* pid */
926 			ps.system = old_system;
927 			ps.command = NULL;	/* grep */
928 			hlpid = -1;
929 			break;
930 		case CMD_cpus:
931 			combine_cpus = !combine_cpus;
932 			max_topn = display_resize();
933 			reset_display();
934 			break;
935 		default:
936 			new_message(MT_standout, " BAD CASE IN SWITCH!");
937 			putr();
938 		}
939 	}
940 
941 	/* flush out stuff that may have been written */
942 	fflush(stdout);
943 	return 0;
944 }
945 
946 
947 /*
948  *  reset_display() - reset all the display routine pointers so that entire
949  *	screen will get redrawn.
950  */
951 static void
952 reset_display(void)
953 {
954 	if (smart_terminal) {
955 		clear();
956 		refresh();
957 	}
958 }
959 
960 /* ARGSUSED */
961 void
962 leave(int signo)
963 {
964 	leaveflag = 1;
965 }
966 
967 /* ARGSUSED */
968 void
969 tstop(int signo)
970 {
971 	tstopflag = 1;
972 }
973 
974 /* ARGSUSED */
975 void
976 sigwinch(int signo)
977 {
978 	winchflag = 1;
979 }
980 
981 /* ARGSUSED */
982 void
983 onalrm(int signo)
984 {
985 }
986 
987 void
988 quit(int ret)
989 {
990 	end_screen();
991 	exit(ret);
992 }
993