xref: /openbsd/usr.bin/top/top.c (revision 5b133f3f)
1 /*	$OpenBSD: top.c,v 1.109 2023/03/08 04:43:12 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 <sys/socket.h>
33 #include <curses.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <signal.h>
38 #include <string.h>
39 #include <poll.h>
40 #include <pwd.h>
41 #include <stdlib.h>
42 #include <limits.h>
43 #include <unistd.h>
44 #include <stdbool.h>
45 
46 /* includes specific to top */
47 #include "display.h"		/* interface to display package */
48 #include "screen.h"		/* interface to screen package */
49 #include "top.h"
50 #include "top.local.h"
51 #include "machine.h"
52 #include "utils.h"
53 
54 /* Size of the stdio buffer given to stdout */
55 #define BUFFERSIZE	2048
56 
57 /* The buffer that stdio will use */
58 char		stdoutbuf[BUFFERSIZE];
59 
60 /* signal handling routines */
61 static void	leave(int);
62 static void	onalrm(int);
63 static void	tstop(int);
64 static void	sigwinch(int);
65 
66 volatile sig_atomic_t leaveflag, tstopflag, winchflag;
67 
68 static void	reset_display(void);
69 int		rundisplay(void);
70 
71 static int	max_topn;	/* maximum displayable processes */
72 static int	skip;		/* how many processes to skip (scroll) */
73 
74 extern int ncpu;
75 extern int ncpuonline;
76 
77 extern int	(*proc_compares[])(const void *, const void *);
78 int order_index;
79 int rev_order;
80 
81 int displays = 0;	/* indicates unspecified */
82 int do_unames = true;
83 struct process_select ps;
84 int interactive = -1;	/* indicates undefined */
85 double delay = Default_DELAY;
86 char *order_name = NULL;
87 int topn = Default_TOPN;
88 int no_command = true;
89 int old_system = false;
90 int old_threads = false;
91 int show_args = false;
92 pid_t hlpid = (pid_t)-1;
93 int combine_cpus = 0;
94 
95 #if Default_TOPN == Infinity
96 int topn_specified = false;
97 #endif
98 
99 struct system_info system_info;
100 struct statics  statics;
101 
102 /*
103  * these defines enumerate the "strchr"s of the commands in
104  * command_chars
105  */
106 #define CMD_redraw	0
107 #define CMD_update	1
108 #define CMD_quit	2
109 #define CMD_help1	3
110 #define CMD_help2	4
111 #define CMD_OSLIMIT	4	/* terminals with OS can only handle commands */
112 #define CMD_errors	5	/* less than or equal to CMD_OSLIMIT	   */
113 #define CMD_number1	6
114 #define CMD_number2	7
115 #define CMD_delay	8
116 #define CMD_displays	9
117 #define CMD_kill	10
118 #define CMD_renice	11
119 #define CMD_idletog	12
120 #define CMD_idletog2	13
121 #define CMD_user	14
122 #define CMD_system	15
123 #define CMD_order	16
124 #define CMD_pid		17
125 #define CMD_command	18
126 #define CMD_threads	19
127 #define CMD_grep	20
128 #define CMD_add		21
129 #define CMD_hl		22
130 #define CMD_cpus	23
131 #define CMD_down	24
132 #define CMD_up		25
133 #define CMD_pagedown	26
134 #define CMD_pageup	27
135 #define CMD_grep2	28
136 #define CMD_rtableid	29
137 #define CMD_rtable	30
138 
139 static void
usage(void)140 usage(void)
141 {
142 	extern char *__progname;
143 
144 	fprintf(stderr,
145 	    "usage: %s [-1bCHIinqStu] [-d count] [-g string] [-o [-]field] "
146 	    "[-p pid] [-s time]\n\t[-T [-]rtable] [-U [-]user] [number]\n",
147 	    __progname);
148 }
149 
150 static int
getorder(char * field)151 getorder(char *field)
152 {
153 	int i, r = field[0] == '-';
154 
155 	i = string_index(r ? field + 1 : field, statics.order_names);
156 	if (i != -1)
157 		rev_order = r;
158 
159 	return i;
160 }
161 
162 static int
filteruser(char buf[])163 filteruser(char buf[])
164 {
165 	const char *errstr;
166 	char *bufp = buf;
167 	uid_t *uidp;
168 	uid_t uid;
169 
170 	if (bufp[0] == '-') {
171 		bufp++;
172 		uidp = &ps.huid;
173 		ps.uid = (pid_t)-1;
174 	} else {
175 		uidp = &ps.uid;
176 		ps.huid = (pid_t)-1;
177 	}
178 
179 	if (uid_from_user(bufp, uidp) == 0)
180 		return 0;
181 
182 	uid = strtonum(bufp, 0, UID_MAX, &errstr);
183 	if (errstr == NULL && user_from_uid(uid, 1) != NULL) {
184 		*uidp = uid;
185 		return 0;
186 	}
187 
188 	return -1;
189 }
190 
191 static int
filterpid(char buf[],int hl)192 filterpid(char buf[], int hl)
193 {
194 	const char *errstr;
195 	int pid;
196 
197 	pid = strtonum(buf, 0, INT_MAX, &errstr);
198 	if (errstr != NULL || !find_pid(pid))
199 		return -1;
200 
201 	if (hl)
202 		hlpid = (pid_t)pid;
203 	else {
204 		if (!ps.system)
205 			old_system = false;
206 		ps.pid = (pid_t)pid;
207 		ps.system = true;
208 	}
209 
210 	return 0;
211 }
212 
213 static int
filterrtable(char buf[])214 filterrtable(char buf[])
215 {
216 	const char *errstr;
217 	char *bufp = buf;
218 	uint32_t *rtableidp;
219 	uint32_t rtableid;
220 
221 	if (bufp[0] == '-') {
222 		bufp++;
223 		rtableidp = &ps.hrtableid;
224 		ps.rtableid = -1;
225 	} else {
226 		rtableidp = &ps.rtableid;
227 		ps.hrtableid = -1;
228 	}
229 
230 	rtableid = strtonum(bufp, 0, RT_TABLEID_MAX, &errstr);
231 	if (errstr == NULL) {
232 		*rtableidp = rtableid;
233 		return 0;
234 	}
235 
236 	return -1;
237 }
238 
239 static void
parseargs(int ac,char ** av)240 parseargs(int ac, char **av)
241 {
242 	char *endp;
243 	int i;
244 
245 	while ((i = getopt(ac, av, "1SHICbinqus:d:p:U:o:g:T:t")) != -1) {
246 		switch (i) {
247 		case '1':
248 			combine_cpus = 1;
249 			break;
250 		case 'C':
251 			show_args = true;
252 			break;
253 		case 'u':	/* toggle uid/username display */
254 			do_unames = !do_unames;
255 			break;
256 
257 		case 'U':	/* display only username's processes */
258 			if (filteruser(optarg) == -1)
259 				new_message(MT_delayed, "%s: unknown user",
260 				    optarg);
261 			break;
262 
263 		case 'p':	/* display only process id */
264 			if (filterpid(optarg, false) == -1)
265 				new_message(MT_delayed, "%s: unknown pid",
266 				    optarg);
267 			break;
268 
269 		case 'S':	/* show system processes */
270 			ps.system = !ps.system;
271 			old_system = !old_system;
272 			break;
273 
274 		case 'H':	/* show threads */
275 			ps.threads = true;
276 			old_threads = true;
277 			break;
278 
279 		case 'I':	/* show idle processes */
280 			ps.idle = !ps.idle;
281 			break;
282 
283 		case 'i':	/* go interactive regardless */
284 			interactive = true;
285 			break;
286 
287 		case 'n':	/* batch, or non-interactive */
288 		case 'b':
289 			interactive = false;
290 			break;
291 
292 		case 'd':	/* number of displays to show */
293 			if ((i = atoiwi(optarg)) != Invalid && i != 0) {
294 				displays = i;
295 				if (displays == 1)
296 					interactive = false;
297 				break;
298 			}
299 			new_message(MT_delayed,
300 			    "warning: display count should be positive "
301 			    "-- option ignored");
302 			break;
303 
304 		case 's':
305 			delay = strtod(optarg, &endp);
306 
307 			if (delay >= 0 && delay <= 1000000 && *endp == '\0')
308 				break;
309 
310 			new_message(MT_delayed,
311 			    "warning: delay should be a non-negative number"
312 			    " -- using default");
313 			delay = Default_DELAY;
314 			break;
315 
316 		case 'q':	/* be quick about it */
317 			/* only allow this if user is really root */
318 			if (getuid() == 0) {
319 				/* be very un-nice! */
320 				(void) nice(-20);
321 				break;
322 			}
323 			new_message(MT_delayed,
324 			    "warning: `-q' option can only be used by root");
325 			break;
326 
327 		case 'o':	/* select sort order */
328 			order_name = optarg;
329 			break;
330 
331 		case 'g':	/* grep command name */
332 			free(ps.command);
333 			if ((ps.command = strdup(optarg)) == NULL)
334 				err(1, NULL);
335 			break;
336 
337 		case 'T':
338 			if (filterrtable(optarg) == -1)
339 				new_message(MT_delayed,
340 				    "%s: invalid routing table", optarg);
341 			break;
342 
343 		case 't':
344 			ps.rtable = true;
345 			break;
346 
347 		default:
348 			usage();
349 			exit(1);
350 		}
351 	}
352 
353 	i = getncpuonline();
354 	if (i == -1)
355 		err(1, NULL);
356 
357 	if (i > 8)
358 		combine_cpus = 1;
359 
360 	/* get count of top processes to display (if any) */
361 	if (optind < ac) {
362 		if ((topn = atoiwi(av[optind])) == Invalid) {
363 			new_message(MT_delayed,
364 			    "warning: process count should "
365 			    "be a non-negative number -- using default");
366 			topn = Infinity;
367 		}
368 #if Default_TOPN == Infinity
369 		else
370 			topn_specified = true;
371 #endif
372 	}
373 }
374 
375 int
main(int argc,char * argv[])376 main(int argc, char *argv[])
377 {
378 	char *header_text, *env_top;
379 	char *uname_field = "USERNAME", *thread_field = "     TID";
380 	char *wait_field = "WAIT   ", *rtable_field = " RTABLE";
381 	const char *(*get_userid)(uid_t, int) = user_from_uid;
382 	char **preset_argv = NULL, **av = argv;
383 	int preset_argc = 0, ac = argc, active_procs, i, ncpuonline_now;
384 	sigset_t mask, oldmask;
385 	time_t curr_time;
386 	struct handle *processes;
387 
388 	/* set the buffer for stdout */
389 #ifdef DEBUG
390 	setvbuf(stdout, NULL, _IONBUF, 0);
391 #else
392 	setvbuf(stdout, stdoutbuf, _IOFBF, sizeof stdoutbuf);
393 #endif
394 
395 	/* initialize some selection options */
396 	ps.idle = true;
397 	ps.system = false;
398 	ps.uid = (uid_t)-1;
399 	ps.huid = (uid_t)-1;
400 	ps.pid = (pid_t)-1;
401 	ps.rtable = false;
402 	ps.rtableid = -1;
403 	ps.hrtableid = -1;
404 	ps.command = NULL;
405 
406 	/* get preset options from the environment */
407 	if ((env_top = getenv("TOP")) != NULL) {
408 		av = preset_argv = argparse(env_top, &preset_argc);
409 		ac = preset_argc;
410 
411 		/*
412 		 * set the dummy argument to an explanatory message, in case
413 		 * getopt encounters a bad argument
414 		 */
415 		preset_argv[0] = "while processing environment";
416 	}
417 	/* process options */
418 	do {
419 		/*
420 		 * if we're done doing the presets, then process the real
421 		 * arguments
422 		 */
423 		if (preset_argc == 0) {
424 			ac = argc;
425 			av = argv;
426 			optind = 1;
427 		}
428 		parseargs(ac, av);
429 		i = preset_argc;
430 		preset_argc = 0;
431 	} while (i != 0);
432 
433 	if (pledge("stdio rpath getpw tty proc ps vminfo", NULL) == -1)
434 		err(1, "pledge");
435 
436 	/* set constants for username/uid display correctly */
437 	if (!do_unames) {
438 		uname_field = "   UID  ";
439 		get_userid = format_uid;
440 	}
441 	/* initialize the kernel memory interface */
442 	if (machine_init(&statics) == -1)
443 		exit(1);
444 
445 	/* determine sorting order index, if necessary */
446 	if (order_name != NULL) {
447 		if ((order_index = getorder(order_name)) == -1) {
448 			new_message(MT_delayed,
449 			    " %s: unrecognized sorting order", order_name);
450 			order_index = 0;
451 		}
452 	}
453 
454 	/* initialize termcap */
455 	init_termcap(interactive);
456 
457 	/* initialize display interface */
458 	max_topn = display_init(&statics);
459 
460 	/* print warning if user requested more processes than we can display */
461 	if (topn > max_topn)
462 		new_message(MT_delayed,
463 		    "warning: this terminal can only display %d processes",
464 		    max_topn);
465 	/* adjust for topn == Infinity */
466 	if (topn == Infinity) {
467 		/*
468 		 *  For smart terminals, infinity really means everything that can
469 		 *  be displayed, or Largest.
470 		 *  On dumb terminals, infinity means every process in the system!
471 		 *  We only really want to do that if it was explicitly specified.
472 		 *  This is always the case when "Default_TOPN != Infinity".  But if
473 		 *  topn wasn't explicitly specified and we are on a dumb terminal
474 		 *  and the default is Infinity, then (and only then) we use
475 		 *  "Nominal_TOPN" instead.
476 		 */
477 #if Default_TOPN == Infinity
478 		topn = smart_terminal ? Largest :
479 		    (topn_specified ? Largest : Nominal_TOPN);
480 #else
481 		topn = Largest;
482 #endif
483 	}
484 	/* set header display accordingly */
485 	display_header(topn > 0);
486 
487 	/* determine interactive state */
488 	if (interactive == -1)
489 		interactive = smart_terminal;
490 
491 	/* if # of displays not specified, fill it in */
492 	if (displays == 0)
493 		displays = smart_terminal ? Infinity : 1;
494 
495 	/*
496 	 * block interrupt signals while setting up the screen and the
497 	 * handlers
498 	 */
499 	sigemptyset(&mask);
500 	sigaddset(&mask, SIGINT);
501 	sigaddset(&mask, SIGQUIT);
502 	sigaddset(&mask, SIGTSTP);
503 	sigprocmask(SIG_BLOCK, &mask, &oldmask);
504 	if (interactive)
505 		init_screen();
506 	if (pledge("stdio getpw tty proc ps vminfo", NULL) == -1)
507 		err(1, "pledge");
508 	(void) signal(SIGINT, leave);
509 	siginterrupt(SIGINT, 1);
510 	(void) signal(SIGQUIT, leave);
511 	(void) signal(SIGTSTP, tstop);
512 	if (smart_terminal)
513 		(void) signal(SIGWINCH, sigwinch);
514 	sigprocmask(SIG_SETMASK, &oldmask, NULL);
515 restart:
516 
517 	/*
518 	 *  main loop -- repeat while display count is positive or while it
519 	 *		indicates infinity (by being -1)
520 	 */
521 	while ((displays == -1) || (displays-- > 0)) {
522 		if (winchflag) {
523 			/*
524 			 * reascertain the screen
525 			 * dimensions
526 			 */
527 			get_screensize();
528 			resizeterm(screen_length, screen_width + 1);
529 
530 			/* tell display to resize */
531 			max_topn = display_resize();
532 
533 			/* reset the signal handler */
534 			(void) signal(SIGWINCH, sigwinch);
535 
536 			reset_display();
537 			winchflag = 0;
538 		}
539 
540 		/* get the current stats */
541 		get_system_info(&system_info);
542 
543 		/*
544 		 * don't display stats for offline CPUs: resize if we're
545 		 * interactive and CPUs have toggled on or offline
546 		 */
547 		if (interactive) {
548 			for (i = ncpuonline_now = 0; i < ncpu; i++)
549 				if (system_info.cpuonline[i])
550 					ncpuonline_now++;
551 			if (ncpuonline_now != ncpuonline) {
552 				max_topn = display_resize();
553 				reset_display();
554 				continue;
555 			}
556 		}
557 
558 		/* get the current set of processes */
559 		processes = get_process_info(&system_info, &ps,
560 		    proc_compares[order_index]);
561 
562 		/* display the load averages */
563 		i_loadave(system_info.load_avg);
564 
565 		/* display the current time */
566 		/* this method of getting the time SHOULD be fairly portable */
567 		time(&curr_time);
568 		i_timeofday(&curr_time);
569 
570 		/* display process/threads state breakdown */
571 		i_procstates(system_info.p_total, system_info.procstates,
572 		    ps.threads);
573 
574 		/* display the cpu state percentage breakdown */
575 		i_cpustates(system_info.cpustates, system_info.cpuonline);
576 
577 		/* display memory stats */
578 		i_memory(system_info.memory);
579 
580 		/* handle message area */
581 		i_message();
582 
583 		/* get the string to use for the process area header */
584 		header_text = format_header(
585 		    ps.threads ? thread_field : uname_field,
586 		    ps.rtable ? rtable_field : wait_field);
587 
588 		/* update the header area */
589 		i_header(header_text);
590 
591 		if (topn == Infinity) {
592 #if Default_TOPN == Infinity
593 			topn = smart_terminal ? Largest :
594 			    (topn_specified ? Largest : Nominal_TOPN);
595 #else
596 			topn = Largest;
597 #endif
598 		}
599 
600 		if (topn > 0) {
601 			/* determine number of processes to actually display */
602 			/*
603 			 * this number will be the smallest of:  active
604 			 * processes, number user requested, number current
605 			 * screen accommodates
606 			 */
607 			active_procs = system_info.p_active;
608 			if (active_procs > topn)
609 				active_procs = topn;
610 			if (active_procs > max_topn)
611 				active_procs = max_topn;
612 			/* determine how many process to skip, if asked to */
613 			/*
614 			 * this number is tweaked by user, but gets shrinked
615 			 * when number of active processes lowers too much
616 			 */
617 			if (skip + active_procs > system_info.p_active)
618 				skip = system_info.p_active - active_procs;
619 			skip_processes(processes, skip);
620 			/* now show the top "n" processes. */
621 			for (i = 0; i < active_procs; i++) {
622 				pid_t pid;
623 				char * s;
624 
625 				s = format_next_process(processes,
626 				    ps.threads ? NULL : get_userid, ps.rtable,
627 				    &pid);
628 				i_process(i, s, pid == hlpid);
629 			}
630 		}
631 
632 		/* do end-screen processing */
633 		u_endscreen();
634 
635 		/* now, flush the output buffer */
636 		fflush(stdout);
637 
638 		if (smart_terminal)
639 			refresh();
640 
641 		/* only do the rest if we have more displays to show */
642 		if (displays) {
643 			/* switch out for new display on smart terminals */
644 			no_command = true;
645 			if (!interactive) {
646 				/* set up alarm */
647 				(void) signal(SIGALRM, onalrm);
648 				(void) alarm((unsigned) delay);
649 
650 				/* wait for the rest of it .... */
651 				pause();
652 				if (leaveflag)
653 					exit(0);
654 				if (tstopflag) {
655 					(void) signal(SIGTSTP, SIG_DFL);
656 					(void) kill(0, SIGTSTP);
657 					/* reset the signal handler */
658 					(void) signal(SIGTSTP, tstop);
659 					tstopflag = 0;
660 				}
661 			} else {
662 				while (no_command)
663 					if (rundisplay())
664 						goto restart;
665 			}
666 		}
667 	}
668 
669 	quit(0);
670 	/* NOTREACHED */
671 	return (0);
672 }
673 
674 int
rundisplay(void)675 rundisplay(void)
676 {
677 	static char tempbuf[TEMPBUFSIZE];
678 	sigset_t mask;
679 	char ch, *iptr;
680 	int change, i;
681 	struct pollfd pfd[1];
682 	static char command_chars[] = "\f qh?en#sdkriIuSopCHg+P109)(/Tt";
683 
684 	/*
685 	 * assume valid command unless told
686 	 * otherwise
687 	 */
688 	no_command = false;
689 
690 	/*
691 	 * set up arguments for select with
692 	 * timeout
693 	 */
694 	pfd[0].fd = STDIN_FILENO;
695 	pfd[0].events = POLLIN;
696 
697 	if (leaveflag)
698 		quit(0);
699 	if (tstopflag) {
700 		/* move to the lower left */
701 		end_screen();
702 		fflush(stdout);
703 
704 		/*
705 		 * default the signal handler
706 		 * action
707 		 */
708 		(void) signal(SIGTSTP, SIG_DFL);
709 
710 		/*
711 		 * unblock the signal and
712 		 * send ourselves one
713 		 */
714 		sigemptyset(&mask);
715 		sigaddset(&mask, SIGTSTP);
716 		sigprocmask(SIG_UNBLOCK, &mask, NULL);
717 		(void) kill(0, SIGTSTP);
718 
719 		/* reset the signal handler */
720 		(void) signal(SIGTSTP, tstop);
721 
722 		/* reinit screen */
723 		reinit_screen();
724 		reset_display();
725 		tstopflag = 0;
726 		return 1;
727 	}
728 	/*
729 	 * wait for either input or the end
730 	 * of the delay period
731 	 */
732 	if (poll(pfd, 1, (int)(delay * 1000)) > 0) {
733 		char *errmsg;
734 		ssize_t len;
735 
736 		if ((pfd[0].revents & (POLLERR|POLLHUP|POLLNVAL)))
737 			exit(1);
738 
739 		clear_message();
740 
741 		/*
742 		 * now read it and convert to
743 		 * command strchr
744 		 */
745 		while (1) {
746 			len = read(STDIN_FILENO, &ch, 1);
747 			if (len == -1 && errno == EINTR)
748 				continue;
749 			if (len == 0)
750 				exit(1);
751 			break;
752 		}
753 		if ((iptr = strchr(command_chars, ch)) == NULL) {
754 			/* illegal command */
755 			new_message(MT_standout, " Command not understood");
756 			putr();
757 			no_command = true;
758 			fflush(stdout);
759 			return (0);
760 		}
761 
762 		change = iptr - command_chars;
763 
764 		switch (change) {
765 		case CMD_redraw:	/* redraw screen */
766 			reset_display();
767 			break;
768 
769 		case CMD_update:	/* merely update display */
770 			/*
771 			 * is the load average high?
772 			 */
773 			if (system_info.load_avg[0] > LoadMax) {
774 				/* yes, go home for visual feedback */
775 				go_home();
776 				fflush(stdout);
777 			}
778 			break;
779 
780 		case CMD_quit:	/* quit */
781 			quit(0);
782 			break;
783 
784 		case CMD_help1:	/* help */
785 		case CMD_help2:
786 			clear();
787 			show_help();
788 			anykey();
789 			clear();
790 			break;
791 
792 		case CMD_errors:	/* show errors */
793 			if (error_count() == 0) {
794 				new_message(MT_standout,
795 				    " Currently no errors to report.");
796 				putr();
797 				no_command = true;
798 			} else {
799 				clear();
800 				show_errors();
801 				anykey();
802 				clear();
803 			}
804 			break;
805 
806 		case CMD_number1:	/* new number */
807 		case CMD_number2:
808 			new_message(MT_standout,
809 			    "Number of processes to show: ");
810 
811 			if (readline(tempbuf, 8) > 0) {
812 				if ((i = atoiwi(tempbuf)) != Invalid) {
813 					if (i > max_topn) {
814 						new_message(MT_standout |
815 						    MT_delayed,
816 						    " This terminal can only "
817 						    "display %d processes.",
818 						    max_topn);
819 						putr();
820 					}
821 					if ((i > topn || i == Infinity)
822 					    && topn == 0) {
823 						/* redraw the header */
824 						display_header(true);
825 					} else if (i == 0)
826 						display_header(false);
827 					topn = i;
828 				} else {
829 					new_message(MT_standout,
830 					    "Processes should be a "
831 					    "non-negative number");
832 					putr();
833 					no_command = true;
834 				}
835 			} else
836 				clear_message();
837 			break;
838 
839 		case CMD_delay:	/* new seconds delay */
840 			new_message(MT_standout, "Seconds to delay: ");
841 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
842 				char *endp;
843 				double newdelay = strtod(tempbuf, &endp);
844 
845 				if (newdelay >= 0 && newdelay <= 1000000 &&
846 				    *endp == '\0') {
847 					delay = newdelay;
848 				} else {
849 					new_message(MT_standout,
850 					    "Delay should be a non-negative number");
851 					putr();
852 					no_command = true;
853 				}
854 
855 			} else
856 				clear_message();
857 			break;
858 
859 		case CMD_displays:	/* change display count */
860 			new_message(MT_standout,
861 			    "Displays to show (currently %s): ",
862 			    displays == -1 ? "infinite" :
863 			    itoa(displays));
864 
865 			if (readline(tempbuf, 10) > 0) {
866 				if ((i = atoiwi(tempbuf)) != Invalid) {
867 					if (i == 0)
868 						quit(0);
869 					displays = i;
870 				} else {
871 					new_message(MT_standout,
872 					    "Displays should be a non-negative number");
873 					putr();
874 					no_command = true;
875 				}
876 			} else
877 				clear_message();
878 			break;
879 
880 		case CMD_kill:	/* kill program */
881 			new_message(0, "kill ");
882 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
883 				if ((errmsg = kill_procs(tempbuf)) != NULL) {
884 					new_message(MT_standout, "%s", errmsg);
885 					putr();
886 					no_command = true;
887 				}
888 			} else
889 				clear_message();
890 			break;
891 
892 		case CMD_renice:	/* renice program */
893 			new_message(0, "renice ");
894 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
895 				if ((errmsg = renice_procs(tempbuf)) != NULL) {
896 					new_message(MT_standout, "%s", errmsg);
897 					putr();
898 					no_command = true;
899 				}
900 			} else
901 				clear_message();
902 			break;
903 
904 		case CMD_idletog:
905 		case CMD_idletog2:
906 			ps.idle = !ps.idle;
907 			new_message(MT_standout | MT_delayed,
908 			    " %sisplaying idle processes.",
909 			    ps.idle ? "D" : "Not d");
910 			putr();
911 			break;
912 
913 		case CMD_user:
914 			new_message(MT_standout,
915 			    "Username to show: ");
916 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
917 				if ((tempbuf[0] == '+' || tempbuf[0] == '-') &&
918 				    tempbuf[1] == '\0') {
919 					ps.uid = (uid_t)-1;
920 					ps.huid = (uid_t)-1;
921 				} else if (filteruser(tempbuf) == -1) {
922 					new_message(MT_standout,
923 					    " %s: unknown user",
924 					    tempbuf[0] == '-' ? tempbuf + 1 :
925 					    tempbuf);
926 					no_command = true;
927 				}
928 				putr();
929 			} else
930 				clear_message();
931 			break;
932 
933 		case CMD_system:
934 			ps.system = !ps.system;
935 			old_system = ps.system;
936 			new_message(MT_standout | MT_delayed,
937 			    " %sisplaying system processes.",
938 			    ps.system ? "D" : "Not d");
939 			break;
940 
941 		case CMD_order:
942 			new_message(MT_standout,
943 			    "Order to sort: ");
944 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
945 				if ((i = getorder(tempbuf)) == -1) {
946 					new_message(MT_standout,
947 					    " %s: unrecognized sorting order",
948 					    tempbuf[0] == '-' ? tempbuf + 1 :
949 					    tempbuf);
950 					no_command = true;
951 				} else
952 					order_index = i;
953 				putr();
954 			} else
955 				clear_message();
956 			break;
957 
958 		case CMD_pid:
959 			new_message(MT_standout, "Process ID to show: ");
960 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
961 				if (tempbuf[0] == '+' &&
962 				    tempbuf[1] == '\0') {
963 					ps.pid = (pid_t)-1;
964 					ps.system = old_system;
965 				} else if (filterpid(tempbuf, 0) == -1) {
966 					new_message(MT_standout,
967 					    " %s: unknown pid", tempbuf);
968 					no_command = true;
969 				}
970 				putr();
971 			} else
972 				clear_message();
973 			break;
974 
975 		case CMD_command:
976 			show_args = !show_args;
977 			break;
978 
979 		case CMD_threads:
980 			ps.threads = !ps.threads;
981 			old_threads = ps.threads;
982 			new_message(MT_standout | MT_delayed,
983 			    " %sisplaying threads.",
984 			    ps.threads ? "D" : "Not d");
985 			break;
986 
987 		case CMD_grep:
988 		case CMD_grep2:
989 			new_message(MT_standout,
990 			    "Grep command name: ");
991 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
992 				free(ps.command);
993 				if (tempbuf[0] == '+' &&
994 				    tempbuf[1] == '\0')
995 					ps.command = NULL;
996 				else if ((ps.command = strdup(tempbuf)) == NULL)
997 					err(1, NULL);
998 				putr();
999 			} else
1000 				clear_message();
1001 			break;
1002 
1003 		case CMD_hl:
1004 			new_message(MT_standout, "Process ID to highlight: ");
1005 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
1006 				if (tempbuf[0] == '+' &&
1007 				    tempbuf[1] == '\0') {
1008 					hlpid = (pid_t)-1;
1009 				} else if (filterpid(tempbuf, true) == -1) {
1010 					new_message(MT_standout,
1011 					    " %s: unknown pid", tempbuf);
1012 					no_command = true;
1013 				}
1014 				putr();
1015 			} else
1016 				clear_message();
1017 			break;
1018 
1019 		case CMD_add:
1020 			ps.uid = (uid_t)-1;	/* uid */
1021 			ps.huid = (uid_t)-1;
1022 			ps.pid = (pid_t)-1;	/* pid */
1023 			ps.rtableid = -1;	/* rtableid */
1024 			ps.hrtableid = -1;
1025 			ps.system = old_system;
1026 			ps.command = NULL;	/* grep */
1027 			hlpid = (pid_t)-1;
1028 			break;
1029 		case CMD_cpus:
1030 			combine_cpus = !combine_cpus;
1031 			max_topn = display_resize();
1032 			reset_display();
1033 			break;
1034 		case CMD_down:
1035 			skip++;
1036 			break;
1037 		case CMD_up:
1038 			if (skip > 0)
1039 				skip--;
1040 			break;
1041 		case CMD_pagedown:
1042 			skip += max_topn / 2;
1043 			break;
1044 		case CMD_pageup:
1045 			skip -= max_topn / 2;
1046 			if (skip < 0)
1047 				skip = 0;
1048 			break;
1049 		case CMD_rtableid:
1050 			new_message(MT_standout,
1051 			    "Routing table: ");
1052 			if (readline(tempbuf, sizeof(tempbuf)) > 0) {
1053 				if (tempbuf[0] == '+' && tempbuf[1] == '\0') {
1054 					ps.rtableid = -1;
1055 					ps.hrtableid = -1;
1056 				} else if (filterrtable(tempbuf) == -1) {
1057 					new_message(MT_standout,
1058 					    " %s: invalid routing table",
1059 					    tempbuf[0] == '-' ? tempbuf + 1 :
1060 					    tempbuf);
1061 					no_command = true;
1062 				}
1063 				putr();
1064 			} else
1065 				clear_message();
1066 			break;
1067 		case CMD_rtable:
1068 			ps.rtable = !ps.rtable;
1069 			new_message(MT_standout | MT_delayed,
1070 			    " %sisplaying routing tables.",
1071 			    ps.rtable ? "D" : "Not d");
1072 			break;
1073 		default:
1074 			new_message(MT_standout, " BAD CASE IN SWITCH!");
1075 			putr();
1076 		}
1077 	}
1078 
1079 	/* flush out stuff that may have been written */
1080 	fflush(stdout);
1081 	return 0;
1082 }
1083 
1084 
1085 /*
1086  *  reset_display() - reset all the display routine pointers so that entire
1087  *	screen will get redrawn.
1088  */
1089 static void
reset_display(void)1090 reset_display(void)
1091 {
1092 	if (smart_terminal) {
1093 		clear();
1094 		refresh();
1095 	}
1096 }
1097 
1098 void
leave(int signo)1099 leave(int signo)
1100 {
1101 	leaveflag = 1;
1102 }
1103 
1104 void
tstop(int signo)1105 tstop(int signo)
1106 {
1107 	tstopflag = 1;
1108 }
1109 
1110 void
sigwinch(int signo)1111 sigwinch(int signo)
1112 {
1113 	winchflag = 1;
1114 }
1115 
1116 void
onalrm(int signo)1117 onalrm(int signo)
1118 {
1119 }
1120 
1121 void
quit(int ret)1122 quit(int ret)
1123 {
1124 	end_screen();
1125 	exit(ret);
1126 }
1127