xref: /dragonfly/usr.bin/top/top.c (revision da0d35cf5f1c213e98d1d2ab31bf1d7794d56691)
1 /*
2  * Copyright (c) 1984 through 2008, William LeFebvre
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  *     * Neither the name of William LeFebvre nor the names of other
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 char *copyright =
34     "Copyright (c) 1984 through 2008, William LeFebvre";
35 
36 /*
37  * Changes to other files that we can do at the same time:
38  * screen.c:init_termcap: get rid of the "interactive" argument and have it
39  *      pass back something meaningful (such as success/failure/error).
40  */
41 
42 #include "os.h"
43 #include <signal.h>
44 #include <setjmp.h>
45 #include <ctype.h>
46 #include <sys/types.h>
47 #include <sys/uio.h>
48 #include <unistd.h>
49 
50 #ifdef HAVE_SYS_UTSNAME_H
51 #include <sys/utsname.h>
52 #endif
53 
54 #ifdef HAVE_GETOPT_H
55 #include <getopt.h>
56 #endif
57 
58 /* definitions */
59 #ifndef STDIN_FILENO
60 #define STDIN_FILENO 0
61 #endif
62 
63 /* determine which type of signal functions to use */
64 /* cant have sigaction without sigprocmask */
65 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK)
66 #undef HAVE_SIGACTION
67 #endif
68 /* always use sigaction when it is available */
69 #ifdef HAVE_SIGACTION
70 #undef HAVE_SIGHOLD
71 #else
72 /* use sighold/sigrelse, otherwise use old fashioned BSD signals */
73 #if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
74 #define BSD_SIGNALS
75 #endif
76 #endif
77 
78 /* if FD_SET and friends aren't present, then fake something up */
79 #ifndef FD_SET
80 typedef int fd_set;
81 #define FD_ZERO(x)     (*(x) = 0)
82 #define FD_SET(f, x)   (*(x) = 1<<f)
83 #endif
84 
85 /* includes specific to top */
86 
87 #include "top.h"
88 #include "machine.h"
89 #include "globalstate.h"
90 #include "commands.h"
91 #include "display.h"
92 #include "screen.h"
93 #include "boolean.h"
94 #include "username.h"
95 #include "utils.h"
96 #include "version.h"
97 #ifdef ENABLE_COLOR
98 #include "color.h"
99 #endif
100 
101 /* definitions */
102 #define BUFFERSIZE 4096
103 #define JMP_RESUME 1
104 #define JMP_RESIZE 2
105 
106 /* externs for getopt: */
107 extern int  optind;
108 extern char *optarg;
109 
110 /* statics */
111 static char stdoutbuf[BUFFERSIZE];
112 static jmp_buf jmp_int;
113 
114 /* globals */
115 char *myname = "top";
116 
117 void
quit(int status)118 quit(int status)
119 
120 {
121     screen_end();
122     chdir("/tmp");
123     exit(status);
124     /* NOTREACHED */
125 }
126 
127 /*
128  *  signal handlers
129  */
130 
131 void
set_signal(int sig,RETSIGTYPE (* handler)(int))132 set_signal(int sig, RETSIGTYPE (*handler)(int))
133 
134 {
135 #ifdef HAVE_SIGACTION
136     struct sigaction action;
137 
138     action.sa_handler = handler;
139     action.sa_flags = 0;
140     (void) sigaction(sig, &action, NULL);
141 #else
142     (void) signal(sig, handler);
143 #endif
144 }
145 
146 void
release_signal(int sig)147 release_signal(int sig)
148 
149 {
150 #ifdef HAVE_SIGACTION
151     sigset_t set;
152     sigemptyset(&set);
153     sigaddset(&set, sig);
154     sigprocmask(SIG_UNBLOCK, &set, NULL);
155 #endif
156 
157 #ifdef HAVE_SIGHOLD
158     sigrelse(sig);
159 #endif
160 
161 #ifdef BSD_SIGNALS
162     (void) sigsetmask(sigblock(0) & ~(sigmask(sig)));
163 #endif
164 }
165 
166 RETSIGTYPE
sig_leave(int i)167 sig_leave(int i)    /* exit under normal conditions -- INT handler */
168 
169 {
170     screen_end();
171     exit(EX_OK);
172 }
173 
174 RETSIGTYPE
sig_tstop(int i)175 sig_tstop(int i)    /* SIGTSTP handler */
176 
177 {
178     /* move to the lower left */
179     screen_end();
180     fflush(stdout);
181 
182     /* default the signal handler action */
183     set_signal(SIGTSTP, SIG_DFL);
184 
185     /* unblock the TSTP signal */
186     release_signal(SIGTSTP);
187 
188     /* send ourselves a TSTP to stop the process */
189     (void) kill(0, SIGTSTP);
190 
191     /* reset the signal handler */
192     set_signal(SIGTSTP, sig_tstop);
193 
194     /* reinit screen */
195     screen_reinit();
196 
197     /* jump back to a known place in the main loop */
198     longjmp(jmp_int, JMP_RESUME);
199 
200     /* NOTREACHED */
201 }
202 
203 #ifdef SIGWINCH
204 RETSIGTYPE
sig_winch(int i)205 sig_winch(int i)              /* SIGWINCH handler */
206 
207 {
208     /* reascertain the screen dimensions */
209     screen_getsize();
210 
211     /* jump back to a known place in the main loop */
212     longjmp(jmp_int, JMP_RESIZE);
213 }
214 #endif
215 
216 #ifdef HAVE_SIGACTION
217 static sigset_t signalset;
218 #endif
219 
220 void *
hold_signals()221 hold_signals()
222 
223 {
224 #ifdef HAVE_SIGACTION
225     sigemptyset(&signalset);
226     sigaddset(&signalset, SIGINT);
227     sigaddset(&signalset, SIGQUIT);
228     sigaddset(&signalset, SIGTSTP);
229 #ifdef SIGWINCH
230     sigaddset(&signalset, SIGWINCH);
231 #endif
232     sigprocmask(SIG_BLOCK, &signalset, NULL);
233     return (void *)(&signalset);
234 #endif
235 
236 #ifdef HAVE_SIGHOLD
237     sighold(SIGINT);
238     sighold(SIGQUIT);
239     sighold(SIGTSTP);
240 #ifdef SIGWINCH
241     sighold(SIGWINCH);
242     return NULL;
243 #endif
244 #endif
245 
246 #ifdef BSD_SIGNALS
247     int mask;
248 #ifdef SIGWINCH
249     mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
250                         sigmask(SIGTSTP) | sigmask(SIGWINCH));
251 #else
252     mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTSTP));
253     return (void *)mask;
254 #endif
255 #endif
256 
257 }
258 
259 void
set_signals()260 set_signals()
261 
262 {
263     (void) set_signal(SIGINT, sig_leave);
264     (void) set_signal(SIGQUIT, sig_leave);
265     (void) set_signal(SIGTSTP, sig_tstop);
266 #ifdef SIGWINCH
267     (void) set_signal(SIGWINCH, sig_winch);
268 #endif
269 }
270 
271 void
release_signals(void * parm)272 release_signals(void *parm)
273 
274 {
275 #ifdef HAVE_SIGACTION
276     sigprocmask(SIG_UNBLOCK, (sigset_t *)parm, NULL);
277 #endif
278 
279 #ifdef HAVE_SIGHOLD
280     sigrelse(SIGINT);
281     sigrelse(SIGQUIT);
282     sigrelse(SIGTSTP);
283 #ifdef SIGWINCH
284     sigrelse(SIGWINCH);
285 #endif
286 #endif
287 
288 #ifdef BSD_SIGNALS
289     (void) sigsetmask((int)parm);
290 #endif
291 }
292 
293 /*
294  * void do_arguments(globalstate *gstate, int ac, char **av)
295  *
296  * Arguments processing.  gstate points to the global state,
297  * ac and av are the arguments to process.  This can be called
298  * multiple times with different sets of arguments.
299  */
300 
301 #ifdef HAVE_GETOPT_LONG
302 static struct option longopts[] = {
303     { "color", no_argument, NULL, 'C' },
304     { "debug", no_argument, NULL, 'D' },
305     { "system-procs", no_argument, NULL, 'S' },
306     { "idle-procs", no_argument, NULL, 'I' },
307     { "tag-names", no_argument, NULL, 'T' },
308     { "all", no_argument, NULL, 'a' },
309     { "batch", no_argument, NULL, 'b' },
310     { "full-commands", no_argument, NULL, 'c' },
311     { "interactive", no_argument, NULL, 'i' },
312     { "quick", no_argument, NULL, 'q' },
313     { "threads", no_argument, NULL, 't' },
314     { "uids", no_argument, NULL, 'u' },
315     { "version", no_argument, NULL, 'v' },
316     { "delay", required_argument, NULL, 's' },
317     { "displays", required_argument, NULL, 'd' },
318     { "user", required_argument, NULL, 'U' },
319     { "sort-order", required_argument, NULL, 'o' },
320     { "display-mode", required_argument, NULL, 'm' },
321     { NULL, 0, NULL, 0 },
322 };
323 #endif
324 
325 
326 void
do_arguments(globalstate * gstate,int ac,char ** av)327 do_arguments(globalstate *gstate, int ac, char **av)
328 
329 {
330     int i;
331 
332     /* this appears to keep getopt happy */
333     optind = 1;
334 
335 #ifdef HAVE_GETOPT_LONG
336     while ((i = getopt_long(ac, av, "CDSIMTabcinqtuvs:d:U:o:m:", longopts, NULL)) != -1)
337 #else
338     while ((i = getopt(ac, av, "CDSIMTabcinqtuvs:d:U:o:m:")) != EOF)
339 #endif
340     {
341           switch(i)
342           {
343 #ifdef ENABLE_COLOR
344           case 'C':
345               gstate->use_color = !gstate->use_color;
346               break;
347 #endif
348 
349           case 'D':
350               debug_set(1);
351               break;
352 
353           case 'v':
354               fprintf(stderr, "%s: version %s\n", myname, version_string());
355               exit(EX_OK);
356               break;
357 
358           case 'b':
359           case 'n':
360               gstate->interactive = No;
361               break;
362 
363           case 'a':
364               gstate->displays = Infinity;
365               gstate->topn = Infinity;
366               break;
367 
368           case 'i':
369               gstate->interactive = Yes;
370               break;
371 
372           case 'o':
373               gstate->order_name = optarg;
374               break;
375 
376           case 'd':
377               i = atoiwi(optarg);
378               if (i == Invalid || i == 0)
379               {
380                     message_error(" Bad display count");
381               }
382               else
383               {
384                     gstate->displays = i;
385               }
386               break;
387 
388           case 's':
389               i = atoi(optarg);
390               if (i < 0 || (i == 0 && getuid() != 0))
391               {
392                     message_error(" Bad seconds delay");
393               }
394               else
395               {
396                     gstate->delay = i;
397               }
398               break;
399 
400           case 'u':
401               gstate->show_usernames = !gstate->show_usernames;
402               break;
403 
404           case 'U':
405               i = userid(optarg);
406               if (i == -1)
407               {
408                     message_error(" Unknown user '%s'", optarg);
409               }
410               else
411               {
412                     gstate->pselect.uid = i;
413               }
414               break;
415 
416           case 'm':
417               i = atoi(optarg);
418               gstate->pselect.mode = i;
419               break;
420 
421           case 'S':
422               gstate->pselect.system = !gstate->pselect.system;
423               break;
424 
425           case 'I':
426               gstate->pselect.idle = !gstate->pselect.idle;
427               break;
428 
429         case 'M':
430               enable_ncpus = 1;
431             break;
432 
433 
434 #ifdef ENABLE_COLOR
435           case 'T':
436               gstate->show_tags = 1;
437               break;
438 #endif
439 
440           case 'c':
441               gstate->pselect.fullcmd = !gstate->pselect.fullcmd;
442               break;
443 
444           case 't':
445               gstate->pselect.threads = !gstate->pselect.threads;
446               break;
447 
448           case 'q':           /* be quick about it */
449               /* only allow this if user is really root */
450               if (getuid() == 0)
451               {
452                     /* be very un-nice! */
453                     (void) nice(-20);
454               }
455               else
456               {
457                     message_error(" Option -q can only be used by root");
458               }
459               break;
460 
461           default:
462               fprintf(stderr, "\
463 Top version %s\n\
464 Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
465                         version_string(), myname);
466               exit(EX_USAGE);
467           }
468     }
469 
470     /* get count of top processes to display */
471     if (optind < ac && *av[optind])
472     {
473           if ((i = atoiwi(av[optind])) == Invalid)
474           {
475               message_error(" Process count not a number");
476           }
477           else
478           {
479               gstate->topn = i;
480           }
481     }
482 }
483 
484 void
do_display(globalstate * gstate)485 do_display(globalstate *gstate)
486 
487 {
488     int active_procs;
489     int i;
490     time_t curr_time;
491     caddr_t processes;
492     struct system_info system_info;
493     char *hdr;
494 
495     /* get the time */
496     time_mark(&(gstate->now));
497     curr_time = (time_t)(gstate->now.tv_sec);
498 
499     /* get the current stats */
500     get_system_info(&system_info);
501 
502     /* get the current processes */
503     processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index);
504 
505     /* determine number of processes to actually display */
506     if (gstate->topn > 0)
507     {
508           /* this number will be the smallest of:  active processes,
509              number user requested, number current screen accomodates */
510           active_procs = system_info.P_ACTIVE;
511           if (active_procs > gstate->topn)
512           {
513               active_procs = gstate->topn;
514           }
515           if (active_procs > gstate->max_topn)
516           {
517               active_procs = gstate->max_topn;
518           }
519     }
520     else
521     {
522           /* dont show any */
523           active_procs = 0;
524     }
525 
526     hdr = gstate->header_text;
527 
528     /* full screen or update? */
529     if (gstate->fulldraw)
530     {
531           struct timespec uts;
532 
533           clock_gettime(CLOCK_UPTIME, &uts);
534           display_clear();
535           i_loadave(system_info.last_pid, system_info.load_avg);
536           i_uptime(uts.tv_sec);
537           i_timeofday(&curr_time);
538           i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
539           if (gstate->show_cpustates)
540           {
541               i_cpustates(system_info.cpustates);
542           }
543           else
544           {
545               if (smart_terminal)
546               {
547                     z_cpustates();
548               }
549               gstate->show_cpustates = Yes;
550           }
551           i_kernel(system_info.kernel);
552           i_memory(system_info.memory);
553           i_swap(system_info.swap);
554           i_message(&(gstate->now));
555           i_header(hdr);
556           for (i = 0; i < active_procs; i++)
557           {
558               i_process(i, format_next_process(processes, gstate->get_userid));
559           }
560           i_endscreen();
561           if (gstate->smart_terminal)
562           {
563               gstate->fulldraw = No;
564           }
565     }
566     else
567     {
568           struct timespec uts;
569 
570           clock_gettime(CLOCK_UPTIME, &uts);
571           u_loadave(system_info.last_pid, system_info.load_avg);
572           u_uptime(uts.tv_sec);
573           i_timeofday(&curr_time);
574           u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
575           u_cpustates(system_info.cpustates);
576           u_kernel(system_info.kernel);
577           u_memory(system_info.memory);
578           u_swap(system_info.swap);
579           u_message(&(gstate->now));
580           u_header(hdr);
581           for (i = 0; i < active_procs; i++)
582           {
583               u_process(i, format_next_process(processes, gstate->get_userid));
584           }
585           u_endscreen();
586     }
587 }
588 
589 #ifdef DEBUG
590 void
timeval_xdprint(char * s,struct timeval tv)591 timeval_xdprint(char *s, struct timeval tv)
592 
593 {
594     xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec);
595 }
596 #endif
597 
598 void
do_wait(globalstate * gstate)599 do_wait(globalstate *gstate)
600 
601 {
602     struct timeval wait;
603 
604     wait.tv_sec = gstate->delay;
605     wait.tv_usec = 0;
606     select(0, NULL, NULL, NULL, &wait);
607 }
608 
609 void
do_command(globalstate * gstate)610 do_command(globalstate *gstate)
611 
612 {
613     int status;
614     struct timeval wait = {0, 0};
615     struct timeval now;
616     fd_set readfds;
617     unsigned char ch;
618 
619     /* calculate new refresh time */
620     gstate->refresh = gstate->now;
621     gstate->refresh.tv_sec += gstate->delay;
622     time_get(&now);
623 
624     /* loop waiting for time to expire */
625     do {
626           /* calculate time to wait */
627           if (gstate->delay > 0)
628           {
629               wait = gstate->refresh;
630               wait.tv_usec -= now.tv_usec;
631               if (wait.tv_usec < 0)
632               {
633                     wait.tv_usec += 1000000;
634                     wait.tv_sec--;
635               }
636               wait.tv_sec -= now.tv_sec;
637           }
638 
639           /* set up arguments for select on stdin (0) */
640           FD_ZERO(&readfds);
641           FD_SET(STDIN_FILENO, &readfds);
642 
643           /* wait for something to read or time out */
644           if (select(32, &readfds, NULL, NULL, &wait) > 0)
645           {
646               /* read it */
647               if (read(STDIN_FILENO, &ch, 1) != 1)
648               {
649                     /* read error */
650                     message_error(" Read error on stdin");
651                     quit(EX_DATAERR);
652                     /*NOTREACHED*/
653               }
654 
655               /* mark pending messages as old */
656               message_mark();
657 
658               /* dispatch */
659               status = command_process(gstate, (int)ch);
660               switch(status)
661               {
662               case CMD_ERROR:
663                     quit(EX_SOFTWARE);
664                     /*NOTREACHED*/
665 
666               case CMD_REFRESH:
667                     return;
668 
669               case CMD_UNKNOWN:
670                     message_error(" Unknown command");
671                     break;
672 
673               case CMD_NA:
674                     message_error(" Command not available");
675               }
676           }
677 
678           /* get new time */
679           time_get(&now);
680     } while (timercmp(&now, &(gstate->refresh), < ));
681 }
682 
683 void
do_minidisplay(globalstate * gstate)684 do_minidisplay(globalstate *gstate)
685 
686 {
687     int real_delay;
688     struct system_info si;
689 
690     /* save the real delay and substitute 1 second */
691     real_delay = gstate->delay;
692     gstate->delay = 1;
693 
694     /* wait 1 second for a command */
695     time_mark(&(gstate->now));
696     do_command(gstate);
697 
698     /* do a mini update that only updates the cpustates */
699     get_system_info(&si);
700     u_cpustates(si.cpustates);
701 
702     /* restore the delay time */
703     gstate->delay = real_delay;
704 
705     /* done */
706     i_endscreen();
707 }
708 
709 int
main(int argc,char * argv[])710 main(int argc, char *argv[])
711 
712 {
713     char *env_top;
714     char **preset_argv;
715     int preset_argc = 0;
716     void *mask;
717     int need_mini = 1;
718 
719     struct statics statics;
720     globalstate *gstate;
721 
722     /* get our name */
723     if (argc > 0)
724     {
725           if ((myname = strrchr(argv[0], '/')) == NULL)
726           {
727               myname = argv[0];
728           }
729           else
730           {
731               myname++;
732           }
733     }
734 
735     /* binary compatibility check */
736 #ifdef HAVE_UNAME
737     {
738           struct utsname uts;
739 
740           if (uname(&uts) == 0)
741           {
742               if (strcmp(uts.machine, UNAME_HARDWARE) != 0)
743               {
744                     fprintf(stderr, "%s: incompatible hardware platform\n",
745                               myname);
746                     exit(EX_UNAVAILABLE);
747               }
748           }
749     }
750 #endif
751 
752     /* initialization */
753     gstate = (globalstate *)calloc(1, sizeof(globalstate));
754     gstate->statics = &statics;
755     time_mark(NULL);
756 
757     /* preset defaults for various options */
758     gstate->show_usernames = Yes;
759     gstate->topn = DEFAULT_TOPN;
760     gstate->delay = DEFAULT_DELAY;
761     gstate->fulldraw = Yes;
762     gstate->use_color = Yes;
763     gstate->interactive = Maybe;
764 
765     /* preset defaults for process selection */
766     gstate->pselect.idle = Yes;
767     gstate->pselect.system = No;
768     gstate->pselect.fullcmd = No;
769     gstate->pselect.command = NULL;
770     gstate->pselect.uid = -1;
771     gstate->pselect.mode = 0;
772 
773     /* use a large buffer for stdout */
774 #ifdef HAVE_SETVBUF
775     setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE);
776 #else
777 #ifdef HAVE_SETBUFFER
778     setbuffer(stdout, stdoutbuf, BUFFERSIZE);
779 #endif
780 #endif
781 
782     /* get preset options from the environment */
783     if ((env_top = getenv("TOP")) != NULL)
784     {
785           preset_argv = argparse(env_top, &preset_argc);
786           preset_argv[0] = myname;
787           do_arguments(gstate, preset_argc, preset_argv);
788     }
789 
790     /* process arguments */
791     do_arguments(gstate, argc, argv);
792 
793 #ifdef ENABLE_COLOR
794     /* If colour has been turned on read in the settings. */
795     env_top = getenv("TOPCOLOURS");
796     if (!env_top)
797     {
798           env_top = getenv("TOPCOLORS");
799     }
800     /* must do something about error messages */
801     color_env_parse(env_top);
802     color_activate(gstate->use_color);
803 #endif
804 
805     /* in order to support forward compatability, we have to ensure that
806        the entire statics structure is set to a known value before we call
807        machine_init.  This way fields that a module does not know about
808        will retain their default values */
809     memzero((void *)&statics, sizeof(statics));
810 
811     /* call the platform-specific init */
812     if (machine_init(&statics) == -1)
813     {
814           exit(EX_SOFTWARE);
815     }
816 
817     /* create a helper list of sort order names */
818     gstate->order_namelist = string_list(statics.order_names);
819 
820     /* look up chosen sorting order */
821     if (gstate->order_name != NULL)
822     {
823           int i;
824 
825           if (statics.order_names == NULL)
826           {
827               message_error(" This platform does not support arbitrary ordering");
828           }
829           else if ((i = string_index(gstate->order_name,
830                                            statics.order_names)) == -1)
831           {
832               message_error(" Sort order `%s' not recognized", gstate->order_name);
833               message_error(" Recognized sort orders: %s", gstate->order_namelist);
834           }
835           else
836           {
837               gstate->order_index = i;
838           }
839     }
840 
841     /* initialize extensions */
842     init_username();
843 
844     /* initialize termcap */
845     gstate->smart_terminal = screen_readtermcap(gstate->interactive);
846 
847     /* determine interactive state */
848     if (gstate->interactive == Maybe)
849     {
850           gstate->interactive = smart_terminal;
851     }
852 
853     /* if displays were not specified, choose an appropriate default */
854     if (gstate->displays == 0)
855     {
856           gstate->displays = gstate->smart_terminal ? Infinity: 1;
857     }
858 
859     /* we don't need a mini display when delay is less than 2
860        seconds or when we are not on a smart terminal */
861     if (gstate->delay <= 1 || !smart_terminal)
862     {
863           need_mini = 0;
864     }
865 
866     /* set constants for username/uid display */
867     if (gstate->show_usernames)
868     {
869           gstate->header_text = format_header("USERNAME");
870           gstate->get_userid = username;
871     }
872     else
873     {
874           gstate->header_text = format_header("   UID  ");
875           gstate->get_userid = itoa7;
876     }
877     gstate->pselect.usernames = gstate->show_usernames;
878 
879     /* initialize display */
880     if ((gstate->max_topn = display_init(&statics)) == -1)
881     {
882           fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
883           exit(EX_OSERR);
884     }
885 
886     /* check for infinity and for overflowed screen */
887     if (gstate->topn == Infinity)
888     {
889           gstate->topn = INT_MAX;
890     }
891     else if (gstate->topn > gstate->max_topn)
892     {
893 #if 0
894           message_error(" This terminal can only display %d processes",
895                           gstate->max_topn);
896 #endif
897     }
898 
899 #ifdef ENABLE_COLOR
900     /* producing a list of color tags is easy */
901     if (gstate->show_tags)
902     {
903           color_dump(stdout);
904           exit(EX_OK);
905     }
906 #endif
907 
908     /* hold all signals while we initialize the screen */
909     mask = hold_signals();
910     screen_init();
911 
912     /* set the signal handlers */
913     set_signals();
914 
915     /* longjmp re-entry point */
916     /* set the jump buffer for long jumps out of signal handlers */
917     if (setjmp(jmp_int) != 0)
918     {
919           /* this is where we end up after processing sigwinch or sigtstp */
920 
921           /* tell display to resize its buffers, and get the new length */
922           if ((gstate->max_topn = display_resize()) == -1)
923           {
924               /* thats bad */
925               quit(EX_OSERR);
926               /*NOTREACHED*/
927           }
928 
929           /* set up for a full redraw, and get the current line count */
930           gstate->fulldraw = Yes;
931 
932           /* safe to release the signals now */
933           release_signals(mask);
934     }
935     else
936     {
937           /* release the signals */
938           release_signals(mask);
939 
940           /* some systems require a warmup */
941           /* always do a warmup for batch mode */
942           if (gstate->interactive == 0 || statics.flags.warmup)
943           {
944               struct system_info system_info;
945               struct timeval timeout;
946 
947               time_mark(&(gstate->now));
948               get_system_info(&system_info);
949               (void)get_process_info(&system_info, &gstate->pselect, 0);
950               timeout.tv_sec = 1;
951               timeout.tv_usec = 0;
952               select(0, NULL, NULL, NULL, &timeout);
953 
954               /* if we've warmed up, then we can show good states too */
955               gstate->show_cpustates = Yes;
956               need_mini = 0;
957           }
958     }
959 
960     /* main loop */
961     while ((gstate->displays == -1) || (--gstate->displays > 0))
962     {
963           do_display(gstate);
964           if (gstate->interactive)
965           {
966               if (need_mini)
967               {
968                     do_minidisplay(gstate);
969                     need_mini = 0;
970               }
971               do_command(gstate);
972           }
973           else
974           {
975               do_wait(gstate);
976           }
977     }
978 
979     /* do one last display */
980     do_display(gstate);
981 
982     quit(EX_OK);
983     /* NOTREACHED */
984     return 1; /* Keep compiler quiet. */
985 }
986