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