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