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