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