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