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
33const 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
80typedef 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: */
107extern int  optind;
108extern char *optarg;
109
110/* statics */
111static char stdoutbuf[BUFFERSIZE];
112static jmp_buf jmp_int;
113
114/* globals */
115char *myname;
116
117void
118quit(int status)
119
120{
121    screen_end();
122    chdir("/tmp");
123    exit(status);
124    /* NOTREACHED */
125}
126
127/*
128 *  signal handlers
129 */
130
131static void
132set_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
146static void
147release_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
166static RETSIGTYPE
167sig_leave(int i)	/* exit under normal conditions -- INT handler */
168
169{
170    screen_end();
171    exit(EX_OK);
172}
173
174static RETSIGTYPE
175sig_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
204static RETSIGTYPE
205sig_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
217static sigset_t signalset;
218#endif
219
220static void *
221hold_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
259static void
260set_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
271static void
272release_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
302static 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
328static void
329do_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, "\
470Top version %s\n\
471Usage: %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
492static void
493do_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
597void
598timeval_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
605static void
606do_wait(globalstate *gstate)
607
608{
609    struct timeval wait;
610
611    double2tv(&wait, gstate->delay);
612    select(0, NULL, NULL, NULL, &wait);
613}
614
615static void
616do_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
690static void
691do_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
716int
717main(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