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