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