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    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
147static void
148release_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
167static RETSIGTYPE
168sig_leave(int i)	/* exit under normal conditions -- INT handler */
169
170{
171    screen_end();
172    exit(EX_OK);
173}
174
175static RETSIGTYPE
176sig_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
205static RETSIGTYPE
206sig_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
218static sigset_t signalset;
219#endif
220
221static void *
222hold_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
260static void
261set_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
272static void
273release_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
303static 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
329static void
330do_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, "\
469Top version %s\n\
470Usage: %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
491static void
492do_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
596void
597timeval_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
604static void
605do_wait(globalstate *gstate)
606
607{
608    struct timeval wait;
609
610    double2tv(&wait, gstate->delay);
611    select(0, NULL, NULL, NULL, &wait);
612}
613
614static void
615do_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
689static void
690do_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
715int
716main(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