top.c revision 301836
1char *copyright =
2    "Copyright (c) 1984 through 1996, William LeFebvre";
3
4/*
5 *  Top users/processes display for Unix
6 *  Version 3
7 *
8 *  This program may be freely redistributed,
9 *  but this entire comment MUST remain intact.
10 *
11 *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
12 *  Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
13 *  Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
14 *  Copyright (c) 1996, William LeFebvre, Group sys Consulting
15 *
16 * $FreeBSD: stable/10/contrib/top/top.c 301836 2016-06-12 05:57:42Z ngie $
17 */
18
19/*
20 *  See the file "Changes" for information on version-to-version changes.
21 */
22
23/*
24 *  This file contains "main" and other high-level routines.
25 */
26
27/*
28 * The following preprocessor variables, when defined, are used to
29 * distinguish between different Unix implementations:
30 *
31 *	SIGHOLD  - use SVR4 sighold function when defined
32 *	SIGRELSE - use SVR4 sigrelse function when defined
33 *	FD_SET   - macros FD_SET and FD_ZERO are used when defined
34 */
35
36#include "os.h"
37
38#include <sys/jail.h>
39#include <sys/time.h>
40
41#include <ctype.h>
42#include <errno.h>
43#include <jail.h>
44#include <setjmp.h>
45#include <signal.h>
46#include <unistd.h>
47
48/* includes specific to top */
49#include "commands.h"
50#include "display.h"		/* interface to display package */
51#include "screen.h"		/* interface to screen package */
52#include "top.h"
53#include "top.local.h"
54#include "boolean.h"
55#include "machine.h"
56#include "utils.h"
57#include "username.h"
58
59/* Size of the stdio buffer given to stdout */
60#define Buffersize	2048
61
62/* The buffer that stdio will use */
63char stdoutbuf[Buffersize];
64
65/* build Signal masks */
66#define Smask(s)	(1 << ((s) - 1))
67
68/* for getopt: */
69extern int  optind;
70extern char *optarg;
71
72/* imported from screen.c */
73extern int overstrike;
74
75static int fmt_flags = 0;
76int pcpu_stats = No;
77
78/* signal handling routines */
79sigret_t leave();
80sigret_t tstop();
81#ifdef SIGWINCH
82sigret_t winch();
83#endif
84
85volatile sig_atomic_t leaveflag;
86volatile sig_atomic_t tstopflag;
87volatile sig_atomic_t winchflag;
88
89/* internal routines */
90void quit();
91
92/* values which need to be accessed by signal handlers */
93static int max_topn;		/* maximum displayable processes */
94
95/* miscellaneous things */
96struct process_select ps;
97char *myname = "top";
98jmp_buf jmp_int;
99
100/* routines that don't return int */
101
102char *username();
103char *ctime();
104char *kill_procs();
105char *renice_procs();
106
107#ifdef ORDER
108extern int (*compares[])();
109#else
110extern int proc_compare();
111extern int io_compare();
112#endif
113time_t time();
114
115caddr_t get_process_info();
116
117/* different routines for displaying the user's identification */
118/* (values assigned to get_userid) */
119char *username();
120char *itoa7();
121
122/* pointers to display routines */
123void (*d_loadave)() = i_loadave;
124void (*d_procstates)() = i_procstates;
125void (*d_cpustates)() = i_cpustates;
126void (*d_memory)() = i_memory;
127void (*d_arc)() = i_arc;
128void (*d_swap)() = i_swap;
129void (*d_message)() = i_message;
130void (*d_header)() = i_header;
131void (*d_process)() = i_process;
132
133void reset_display(void);
134
135
136int
137main(argc, argv)
138
139int  argc;
140char *argv[];
141
142{
143    register int i;
144    register int active_procs;
145    register int change;
146
147    struct system_info system_info;
148    struct statics statics;
149    caddr_t processes;
150
151    static char tempbuf1[50];
152    static char tempbuf2[50];
153    int old_sigmask;		/* only used for BSD-style signals */
154    int topn = Default_TOPN;
155    int delay = Default_DELAY;
156    int displays = 0;		/* indicates unspecified */
157    int sel_ret = 0;
158    time_t curr_time;
159    char *(*get_userid)() = username;
160    char *uname_field = "USERNAME";
161    char *header_text;
162    char *env_top;
163    char **preset_argv;
164    int  preset_argc = 0;
165    char **av;
166    int  ac;
167    char dostates = No;
168    char do_unames = Yes;
169    char interactive = Maybe;
170    char warnings = 0;
171#if Default_TOPN == Infinity
172    char topn_specified = No;
173#endif
174    char ch;
175    char *iptr;
176    char no_command = 1;
177    struct timeval timeout;
178#ifdef ORDER
179    char *order_name = NULL;
180    int order_index = 0;
181#endif
182#ifndef FD_SET
183    /* FD_SET and friends are not present:  fake it */
184    typedef int fd_set;
185#define FD_ZERO(x)     (*(x) = 0)
186#define FD_SET(f, x)   (*(x) = 1<<f)
187#endif
188    fd_set readfds;
189
190#ifdef ORDER
191    static char command_chars[] = "\f qh?en#sdkriIutHmSCajzPJo";
192#else
193    static char command_chars[] = "\f qh?en#sdkriIutHmSCajzPJ";
194#endif
195/* these defines enumerate the "strchr"s of the commands in command_chars */
196#define CMD_redraw	0
197#define CMD_update	1
198#define CMD_quit	2
199#define CMD_help1	3
200#define CMD_help2	4
201#define CMD_OSLIMIT	4    /* terminals with OS can only handle commands */
202#define CMD_errors	5    /* less than or equal to CMD_OSLIMIT	   */
203#define CMD_number1	6
204#define CMD_number2	7
205#define CMD_delay	8
206#define CMD_displays	9
207#define CMD_kill	10
208#define CMD_renice	11
209#define CMD_idletog     12
210#define CMD_idletog2    13
211#define CMD_user	14
212#define CMD_selftog	15
213#define CMD_thrtog	16
214#define CMD_viewtog	17
215#define CMD_viewsys	18
216#define	CMD_wcputog	19
217#define	CMD_showargs	20
218#define	CMD_jidtog	21
219#define CMD_kidletog	22
220#define CMD_pcputog	23
221#define CMD_jail	24
222#ifdef ORDER
223#define CMD_order       25
224#endif
225
226    /* set the buffer for stdout */
227#ifdef DEBUG
228    extern FILE *debug;
229    debug = fopen("debug.run", "w");
230    setbuffer(stdout, NULL, 0);
231#else
232    setbuffer(stdout, stdoutbuf, Buffersize);
233#endif
234
235    /* get our name */
236    if (argc > 0)
237    {
238	if ((myname = strrchr(argv[0], '/')) == 0)
239	{
240	    myname = argv[0];
241	}
242	else
243	{
244	    myname++;
245	}
246    }
247
248    /* initialize some selection options */
249    ps.idle    = Yes;
250    ps.self    = -1;
251    ps.system  = No;
252    ps.uid     = -1;
253    ps.thread  = No;
254    ps.wcpu    = 1;
255    ps.jid     = -1;
256    ps.jail    = No;
257    ps.kidle   = Yes;
258    ps.command = NULL;
259
260    /* get preset options from the environment */
261    if ((env_top = getenv("TOP")) != NULL)
262    {
263	av = preset_argv = argparse(env_top, &preset_argc);
264	ac = preset_argc;
265
266	/* set the dummy argument to an explanatory message, in case
267	   getopt encounters a bad argument */
268	preset_argv[0] = "while processing environment";
269    }
270
271    /* process options */
272    do {
273	/* if we're done doing the presets, then process the real arguments */
274	if (preset_argc == 0)
275	{
276	    ac = argc;
277	    av = argv;
278
279	    /* this should keep getopt happy... */
280	    optind = 1;
281	}
282
283	while ((i = getopt(ac, av, "CSIHPabijJ:nquvzs:d:U:m:o:t")) != EOF)
284	{
285	    switch(i)
286	    {
287	      case 'v':			/* show version number */
288		fprintf(stderr, "%s: version %s\n",
289			myname, version_string());
290		exit(1);
291		break;
292
293	      case 'u':			/* toggle uid/username display */
294		do_unames = !do_unames;
295		break;
296
297	      case 'U':			/* display only username's processes */
298		if ((ps.uid = userid(optarg)) == -1)
299		{
300		    fprintf(stderr, "%s: unknown user\n", optarg);
301		    exit(1);
302		}
303		break;
304
305	      case 'S':			/* show system processes */
306		ps.system = !ps.system;
307		break;
308
309	      case 'I':                   /* show idle processes */
310		ps.idle = !ps.idle;
311		break;
312
313	      case 'i':			/* go interactive regardless */
314		interactive = Yes;
315		break;
316
317	      case 'n':			/* batch, or non-interactive */
318	      case 'b':
319		interactive = No;
320		break;
321
322	      case 'a':
323		fmt_flags ^= FMT_SHOWARGS;
324		break;
325
326	      case 'd':			/* number of displays to show */
327		if ((i = atoiwi(optarg)) == Invalid || i == 0)
328		{
329		    fprintf(stderr,
330			"%s: warning: display count should be positive -- option ignored\n",
331			myname);
332		    warnings++;
333		}
334		else
335		{
336		    displays = i;
337		}
338		break;
339
340	      case 's':
341		if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0))
342		{
343		    fprintf(stderr,
344			"%s: warning: seconds delay should be positive -- using default\n",
345			myname);
346		    delay = Default_DELAY;
347		    warnings++;
348		}
349		break;
350
351	      case 'q':		/* be quick about it */
352		/* only allow this if user is really root */
353		if (getuid() == 0)
354		{
355		    /* be very un-nice! */
356		    (void) nice(-20);
357		}
358		else
359		{
360		    fprintf(stderr,
361			"%s: warning: `-q' option can only be used by root\n",
362			myname);
363		    warnings++;
364		}
365		break;
366
367	      case 'm':		/* select display mode */
368		if (strcmp(optarg, "io") == 0) {
369			displaymode = DISP_IO;
370		} else if (strcmp(optarg, "cpu") == 0) {
371			displaymode = DISP_CPU;
372		} else {
373			fprintf(stderr,
374			"%s: warning: `-m' option can only take args "
375			"'io' or 'cpu'\n",
376			myname);
377			exit(1);
378		}
379		break;
380
381	      case 'o':		/* select sort order */
382#ifdef ORDER
383		order_name = optarg;
384#else
385		fprintf(stderr,
386			"%s: this platform does not support arbitrary ordering.  Sorry.\n",
387			myname);
388		warnings++;
389#endif
390		break;
391
392	      case 't':
393		ps.self = (ps.self == -1) ? getpid() : -1;
394		break;
395
396	      case 'C':
397		ps.wcpu = !ps.wcpu;
398		break;
399
400	      case 'H':
401		ps.thread = !ps.thread;
402		break;
403
404	      case 'j':
405		ps.jail = !ps.jail;
406		break;
407
408	      case 'J':			/* display only jail's processes */
409		if ((ps.jid = jail_getid(optarg)) == -1)
410		{
411		    fprintf(stderr, "%s: unknown jail\n", optarg);
412		    exit(1);
413		}
414		ps.jail = 1;
415		break;
416
417	      case 'P':
418		pcpu_stats = !pcpu_stats;
419		break;
420
421	      case 'z':
422		ps.kidle = !ps.kidle;
423		break;
424
425	      default:
426		fprintf(stderr,
427"Top version %s\n"
428"Usage: %s [-abCHIijnPqStuvz] [-d count] [-m io | cpu] [-o field] [-s time]\n"
429"       [-J jail] [-U username] [number]\n",
430			version_string(), myname);
431		exit(1);
432	    }
433	}
434
435	/* get count of top processes to display (if any) */
436	if (optind < ac)
437	{
438	    if ((topn = atoiwi(av[optind])) == Invalid)
439	    {
440		fprintf(stderr,
441			"%s: warning: process display count should be non-negative -- using default\n",
442			myname);
443		warnings++;
444	    }
445#if Default_TOPN == Infinity
446            else
447	    {
448		topn_specified = Yes;
449	    }
450#endif
451	}
452
453	/* tricky:  remember old value of preset_argc & set preset_argc = 0 */
454	i = preset_argc;
455	preset_argc = 0;
456
457    /* repeat only if we really did the preset arguments */
458    } while (i != 0);
459
460    /* set constants for username/uid display correctly */
461    if (!do_unames)
462    {
463	uname_field = "   UID  ";
464	get_userid = itoa7;
465    }
466
467    /* initialize the kernel memory interface */
468    if (machine_init(&statics, do_unames) == -1)
469    {
470	exit(1);
471    }
472
473#ifdef ORDER
474    /* determine sorting order index, if necessary */
475    if (order_name != NULL)
476    {
477	if ((order_index = string_index(order_name, statics.order_names)) == -1)
478	{
479	    char **pp;
480
481	    fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n",
482		    myname, order_name);
483	    fprintf(stderr, "\tTry one of these:");
484	    pp = statics.order_names;
485	    while (*pp != NULL)
486	    {
487		fprintf(stderr, " %s", *pp++);
488	    }
489	    fputc('\n', stderr);
490	    exit(1);
491	}
492    }
493#endif
494
495#ifdef no_initialization_needed
496    /* initialize the hashing stuff */
497    if (do_unames)
498    {
499	init_hash();
500    }
501#endif
502
503    /* initialize termcap */
504    init_termcap(interactive);
505
506    /* get the string to use for the process area header */
507    header_text = format_header(uname_field);
508
509    /* initialize display interface */
510    if ((max_topn = display_init(&statics)) == -1)
511    {
512	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
513	exit(4);
514    }
515
516    /* print warning if user requested more processes than we can display */
517    if (topn > max_topn)
518    {
519	fprintf(stderr,
520		"%s: warning: this terminal can only display %d processes.\n",
521		myname, max_topn);
522	warnings++;
523    }
524
525    /* adjust for topn == Infinity */
526    if (topn == Infinity)
527    {
528	/*
529	 *  For smart terminals, infinity really means everything that can
530	 *  be displayed, or Largest.
531	 *  On dumb terminals, infinity means every process in the system!
532	 *  We only really want to do that if it was explicitly specified.
533	 *  This is always the case when "Default_TOPN != Infinity".  But if
534	 *  topn wasn't explicitly specified and we are on a dumb terminal
535	 *  and the default is Infinity, then (and only then) we use
536	 *  "Nominal_TOPN" instead.
537	 */
538#if Default_TOPN == Infinity
539	topn = smart_terminal ? Largest :
540		    (topn_specified ? Largest : Nominal_TOPN);
541#else
542	topn = Largest;
543#endif
544    }
545
546    /* set header display accordingly */
547    display_header(topn > 0);
548
549    /* determine interactive state */
550    if (interactive == Maybe)
551    {
552	interactive = smart_terminal;
553    }
554
555    /* if # of displays not specified, fill it in */
556    if (displays == 0)
557    {
558	displays = smart_terminal ? Infinity : 1;
559    }
560
561    /* hold interrupt signals while setting up the screen and the handlers */
562#ifdef SIGHOLD
563    sighold(SIGINT);
564    sighold(SIGQUIT);
565    sighold(SIGTSTP);
566#else
567    old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
568#endif
569    init_screen();
570    (void) signal(SIGINT, leave);
571    (void) signal(SIGQUIT, leave);
572    (void) signal(SIGTSTP, tstop);
573#ifdef SIGWINCH
574    (void) signal(SIGWINCH, winch);
575#endif
576#ifdef SIGRELSE
577    sigrelse(SIGINT);
578    sigrelse(SIGQUIT);
579    sigrelse(SIGTSTP);
580#else
581    (void) sigsetmask(old_sigmask);
582#endif
583    if (warnings)
584    {
585	fputs("....", stderr);
586	fflush(stderr);			/* why must I do this? */
587	sleep((unsigned)(3 * warnings));
588	fputc('\n', stderr);
589    }
590
591restart:
592
593    /*
594     *  main loop -- repeat while display count is positive or while it
595     *		indicates infinity (by being -1)
596     */
597
598    while ((displays == -1) || (displays-- > 0))
599    {
600	int (*compare)();
601
602
603	/* get the current stats */
604	get_system_info(&system_info);
605
606#ifdef ORDER
607	compare = compares[order_index];
608#else
609	if (displaymode == DISP_CPU)
610		compare = proc_compare;
611	else
612		compare = io_compare;
613#endif
614
615	/* get the current set of processes */
616	processes =
617		get_process_info(&system_info, &ps, compare);
618
619	/* display the load averages */
620	(*d_loadave)(system_info.last_pid,
621		     system_info.load_avg);
622
623	/* display the current time */
624	/* this method of getting the time SHOULD be fairly portable */
625	time(&curr_time);
626	i_uptime(&system_info.boottime, &curr_time);
627	i_timeofday(&curr_time);
628
629	/* display process state breakdown */
630	(*d_procstates)(system_info.p_total,
631			system_info.procstates);
632
633	/* display the cpu state percentage breakdown */
634	if (dostates)	/* but not the first time */
635	{
636	    (*d_cpustates)(system_info.cpustates);
637	}
638	else
639	{
640	    /* we'll do it next time */
641	    if (smart_terminal)
642	    {
643		z_cpustates();
644	    }
645	    else
646	    {
647		putchar('\n');
648	    }
649	    dostates = Yes;
650	}
651
652	/* display memory stats */
653	(*d_memory)(system_info.memory);
654	(*d_arc)(system_info.arc);
655
656	/* display swap stats */
657	(*d_swap)(system_info.swap);
658
659	/* handle message area */
660	(*d_message)();
661
662	/* update the header area */
663	(*d_header)(header_text);
664
665	if (topn > 0)
666	{
667	    /* determine number of processes to actually display */
668	    /* this number will be the smallest of:  active processes,
669	       number user requested, number current screen accomodates */
670	    active_procs = system_info.P_ACTIVE;
671	    if (active_procs > topn)
672	    {
673		active_procs = topn;
674	    }
675	    if (active_procs > max_topn)
676	    {
677		active_procs = max_topn;
678	    }
679
680	    /* now show the top "n" processes. */
681	    for (i = 0; i < active_procs; i++)
682	    {
683		(*d_process)(i, format_next_process(processes, get_userid,
684			     fmt_flags));
685	    }
686	}
687	else
688	{
689	    i = 0;
690	}
691
692	/* do end-screen processing */
693	u_endscreen(i);
694
695	/* now, flush the output buffer */
696	if (fflush(stdout) != 0)
697	{
698	    new_message(MT_standout, " Write error on stdout");
699	    putchar('\r');
700	    quit(1);
701	    /*NOTREACHED*/
702	}
703
704	/* only do the rest if we have more displays to show */
705	if (displays)
706	{
707	    /* switch out for new display on smart terminals */
708	    if (smart_terminal)
709	    {
710		if (overstrike)
711		{
712		    reset_display();
713		}
714		else
715		{
716		    d_loadave = u_loadave;
717		    d_procstates = u_procstates;
718		    d_cpustates = u_cpustates;
719		    d_memory = u_memory;
720		    d_arc = u_arc;
721		    d_swap = u_swap;
722		    d_message = u_message;
723		    d_header = u_header;
724		    d_process = u_process;
725		}
726	    }
727
728	    no_command = Yes;
729	    if (!interactive)
730	    {
731		sleep(delay);
732		if (leaveflag) {
733		    end_screen();
734		    exit(0);
735		}
736	    }
737	    else while (no_command)
738	    {
739		/* assume valid command unless told otherwise */
740		no_command = No;
741
742		/* set up arguments for select with timeout */
743		FD_ZERO(&readfds);
744		FD_SET(0, &readfds);		/* for standard input */
745		timeout.tv_sec  = delay;
746		timeout.tv_usec = 0;
747
748		if (leaveflag) {
749		    end_screen();
750		    exit(0);
751		}
752
753		if (tstopflag) {
754		    /* move to the lower left */
755		    end_screen();
756		    fflush(stdout);
757
758		    /* default the signal handler action */
759		    (void) signal(SIGTSTP, SIG_DFL);
760
761		    /* unblock the signal and send ourselves one */
762#ifdef SIGRELSE
763		    sigrelse(SIGTSTP);
764#else
765		    (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
766#endif
767		    (void) kill(0, SIGTSTP);
768
769		    /* reset the signal handler */
770		    (void) signal(SIGTSTP, tstop);
771
772		    /* reinit screen */
773		    reinit_screen();
774		    reset_display();
775		    tstopflag = 0;
776		    goto restart;
777		}
778
779		if (winchflag) {
780		    /* reascertain the screen dimensions */
781		    get_screensize();
782
783		    /* tell display to resize */
784		    max_topn = display_resize();
785
786		    /* reset the signal handler */
787		    (void) signal(SIGWINCH, winch);
788
789		    reset_display();
790		    winchflag = 0;
791		    goto restart;
792		}
793
794		/* wait for either input or the end of the delay period */
795		sel_ret = select(2, &readfds, NULL, NULL, &timeout);
796		if (sel_ret < 0 && errno != EINTR)
797		    quit(0);
798		if (sel_ret > 0)
799		{
800		    int newval;
801		    char *errmsg;
802
803		    /* something to read -- clear the message area first */
804		    clear_message();
805
806		    /* now read it and convert to command strchr */
807		    /* (use "change" as a temporary to hold strchr) */
808		    if (read(0, &ch, 1) != 1)
809		    {
810			/* read error: either 0 or -1 */
811			new_message(MT_standout, " Read error on stdin");
812			putchar('\r');
813			quit(1);
814			/*NOTREACHED*/
815		    }
816		    if ((iptr = strchr(command_chars, ch)) == NULL)
817		    {
818			if (ch != '\r' && ch != '\n')
819			{
820			    /* illegal command */
821			    new_message(MT_standout, " Command not understood");
822			}
823			putchar('\r');
824			no_command = Yes;
825		    }
826		    else
827		    {
828			change = iptr - command_chars;
829			if (overstrike && change > CMD_OSLIMIT)
830			{
831			    /* error */
832			    new_message(MT_standout,
833			    " Command cannot be handled by this terminal");
834			    putchar('\r');
835			    no_command = Yes;
836			}
837			else switch(change)
838			{
839			    case CMD_redraw:	/* redraw screen */
840				reset_display();
841				break;
842
843			    case CMD_update:	/* merely update display */
844				/* is the load average high? */
845				if (system_info.load_avg[0] > LoadMax)
846				{
847				    /* yes, go home for visual feedback */
848				    go_home();
849				    fflush(stdout);
850				}
851				break;
852
853			    case CMD_quit:	/* quit */
854				quit(0);
855				/*NOTREACHED*/
856				break;
857
858			    case CMD_help1:	/* help */
859			    case CMD_help2:
860				reset_display();
861				clear();
862				show_help();
863				standout("Hit any key to continue: ");
864				fflush(stdout);
865				(void) read(0, &ch, 1);
866				break;
867
868			    case CMD_errors:	/* show errors */
869				if (error_count() == 0)
870				{
871				    new_message(MT_standout,
872					" Currently no errors to report.");
873				    putchar('\r');
874				    no_command = Yes;
875				}
876				else
877				{
878				    reset_display();
879				    clear();
880				    show_errors();
881				    standout("Hit any key to continue: ");
882				    fflush(stdout);
883				    (void) read(0, &ch, 1);
884				}
885				break;
886
887			    case CMD_number1:	/* new number */
888			    case CMD_number2:
889				new_message(MT_standout,
890				    "Number of processes to show: ");
891				newval = readline(tempbuf1, 8, Yes);
892				if (newval > -1)
893				{
894				    if (newval > max_topn)
895				    {
896					new_message(MT_standout | MT_delayed,
897					  " This terminal can only display %d processes.",
898					  max_topn);
899					putchar('\r');
900				    }
901
902				    if (newval == 0)
903				    {
904					/* inhibit the header */
905					display_header(No);
906				    }
907				    else if (newval > topn && topn == 0)
908				    {
909					/* redraw the header */
910					display_header(Yes);
911					d_header = i_header;
912				    }
913				    topn = newval;
914				}
915				break;
916
917			    case CMD_delay:	/* new seconds delay */
918				new_message(MT_standout, "Seconds to delay: ");
919				if ((i = readline(tempbuf1, 8, Yes)) > -1)
920				{
921				    if ((delay = i) == 0 && getuid() != 0)
922				    {
923					delay = 1;
924				    }
925				}
926				clear_message();
927				break;
928
929			    case CMD_displays:	/* change display count */
930				new_message(MT_standout,
931					"Displays to show (currently %s): ",
932					displays == -1 ? "infinite" :
933							 itoa(displays));
934				if ((i = readline(tempbuf1, 10, Yes)) > 0)
935				{
936				    displays = i;
937				}
938				else if (i == 0)
939				{
940				    quit(0);
941				}
942				clear_message();
943				break;
944
945			    case CMD_kill:	/* kill program */
946				new_message(0, "kill ");
947				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
948				{
949				    if ((errmsg = kill_procs(tempbuf2)) != NULL)
950				    {
951					new_message(MT_standout, "%s", errmsg);
952					putchar('\r');
953					no_command = Yes;
954				    }
955				}
956				else
957				{
958				    clear_message();
959				}
960				break;
961
962			    case CMD_renice:	/* renice program */
963				new_message(0, "renice ");
964				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
965				{
966				    if ((errmsg = renice_procs(tempbuf2)) != NULL)
967				    {
968					new_message(MT_standout, "%s", errmsg);
969					putchar('\r');
970					no_command = Yes;
971				    }
972				}
973				else
974				{
975				    clear_message();
976				}
977				break;
978
979			    case CMD_idletog:
980			    case CMD_idletog2:
981				ps.idle = !ps.idle;
982				new_message(MT_standout | MT_delayed,
983				    " %sisplaying idle processes.",
984				    ps.idle ? "D" : "Not d");
985				putchar('\r');
986				break;
987
988			    case CMD_selftog:
989				ps.self = (ps.self == -1) ? getpid() : -1;
990				new_message(MT_standout | MT_delayed,
991				    " %sisplaying self.",
992				    (ps.self == -1) ? "D" : "Not d");
993				putchar('\r');
994				break;
995
996			    case CMD_user:
997				new_message(MT_standout,
998				    "Username to show (+ for all): ");
999				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
1000				{
1001				    if (tempbuf2[0] == '+' &&
1002					tempbuf2[1] == '\0')
1003				    {
1004					ps.uid = -1;
1005				    }
1006				    else if ((i = userid(tempbuf2)) == -1)
1007				    {
1008					new_message(MT_standout,
1009					    " %s: unknown user", tempbuf2);
1010					no_command = Yes;
1011				    }
1012				    else
1013				    {
1014					ps.uid = i;
1015				    }
1016				    putchar('\r');
1017				}
1018				else
1019				{
1020				    clear_message();
1021				}
1022				break;
1023
1024			    case CMD_thrtog:
1025				ps.thread = !ps.thread;
1026				new_message(MT_standout | MT_delayed,
1027				    " Displaying threads %s",
1028				    ps.thread ? "separately" : "as a count");
1029				header_text = format_header(uname_field);
1030				reset_display();
1031				putchar('\r');
1032				break;
1033			    case CMD_wcputog:
1034				ps.wcpu = !ps.wcpu;
1035				new_message(MT_standout | MT_delayed,
1036				    " Displaying %s CPU",
1037				    ps.wcpu ? "weighted" : "raw");
1038				header_text = format_header(uname_field);
1039				reset_display();
1040				putchar('\r');
1041				break;
1042			    case CMD_viewtog:
1043				if (++displaymode == DISP_MAX)
1044					displaymode = 0;
1045				header_text = format_header(uname_field);
1046				display_header(Yes);
1047				d_header = i_header;
1048				reset_display();
1049				break;
1050			    case CMD_viewsys:
1051				ps.system = !ps.system;
1052				break;
1053			    case CMD_showargs:
1054				fmt_flags ^= FMT_SHOWARGS;
1055				break;
1056#ifdef ORDER
1057			    case CMD_order:
1058				new_message(MT_standout,
1059				    "Order to sort: ");
1060				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
1061				{
1062				  if ((i = string_index(tempbuf2, statics.order_names)) == -1)
1063					{
1064					  new_message(MT_standout,
1065					      " %s: unrecognized sorting order", tempbuf2);
1066					  no_command = Yes;
1067				    }
1068				    else
1069				    {
1070					order_index = i;
1071				    }
1072				    putchar('\r');
1073				}
1074				else
1075				{
1076				    clear_message();
1077				}
1078				break;
1079#endif
1080			    case CMD_jidtog:
1081				ps.jail = !ps.jail;
1082				new_message(MT_standout | MT_delayed,
1083				    " %sisplaying jail ID.",
1084				    ps.jail ? "D" : "Not d");
1085				header_text = format_header(uname_field);
1086				reset_display();
1087				putchar('\r');
1088				break;
1089
1090			    case CMD_jail:
1091				new_message(MT_standout,
1092				    "Jail to show (+ for all): ");
1093				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
1094				{
1095				    if (tempbuf2[0] == '+' &&
1096					tempbuf2[1] == '\0')
1097				    {
1098					ps.jid = -1;
1099				    }
1100				    else if ((i = jail_getid(tempbuf2)) == -1)
1101				    {
1102					new_message(MT_standout,
1103					    " %s: unknown jail", tempbuf2);
1104					no_command = Yes;
1105				    }
1106				    else
1107				    {
1108					ps.jid = i;
1109				    }
1110				    if (ps.jail == 0) {
1111					    ps.jail = 1;
1112					    new_message(MT_standout |
1113						MT_delayed, " Displaying jail "
1114						"ID.");
1115					    header_text =
1116						format_header(uname_field);
1117					    reset_display();
1118				    }
1119				    putchar('\r');
1120				}
1121				else
1122				{
1123				    clear_message();
1124				}
1125				break;
1126
1127			    case CMD_kidletog:
1128				ps.kidle = !ps.kidle;
1129				new_message(MT_standout | MT_delayed,
1130				    " %sisplaying system idle process.",
1131				    ps.kidle ? "D" : "Not d");
1132				putchar('\r');
1133				break;
1134			    case CMD_pcputog:
1135				pcpu_stats = !pcpu_stats;
1136				new_message(MT_standout | MT_delayed,
1137				    " Displaying %sCPU statistics.",
1138				    pcpu_stats ? "per-" : "global ");
1139				toggle_pcpustats();
1140				max_topn = display_updatecpus(&statics);
1141				reset_display();
1142				putchar('\r');
1143				break;
1144			    default:
1145				new_message(MT_standout, " BAD CASE IN SWITCH!");
1146				putchar('\r');
1147			}
1148		    }
1149
1150		    /* flush out stuff that may have been written */
1151		    fflush(stdout);
1152		}
1153	    }
1154	}
1155    }
1156
1157#ifdef DEBUG
1158    fclose(debug);
1159#endif
1160    quit(0);
1161    /*NOTREACHED*/
1162}
1163
1164/*
1165 *  reset_display() - reset all the display routine pointers so that entire
1166 *	screen will get redrawn.
1167 */
1168
1169void
1170reset_display()
1171
1172{
1173    d_loadave    = i_loadave;
1174    d_procstates = i_procstates;
1175    d_cpustates  = i_cpustates;
1176    d_memory     = i_memory;
1177    d_arc        = i_arc;
1178    d_swap       = i_swap;
1179    d_message	 = i_message;
1180    d_header	 = i_header;
1181    d_process	 = i_process;
1182}
1183
1184/*
1185 *  signal handlers
1186 */
1187
1188sigret_t leave()	/* exit under normal conditions -- INT handler */
1189
1190{
1191    leaveflag = 1;
1192}
1193
1194sigret_t tstop(i)	/* SIGTSTP handler */
1195
1196int i;
1197
1198{
1199    tstopflag = 1;
1200}
1201
1202#ifdef SIGWINCH
1203sigret_t winch(i)		/* SIGWINCH handler */
1204
1205int i;
1206
1207{
1208    winchflag = 1;
1209}
1210#endif
1211
1212void quit(status)		/* exit under duress */
1213
1214int status;
1215
1216{
1217    end_screen();
1218    exit(status);
1219    /*NOTREACHED*/
1220}
1221