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