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