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