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