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