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 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 146static void 147release_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 166static RETSIGTYPE 167sig_leave(int i) /* exit under normal conditions -- INT handler */ 168 169{ 170 screen_end(); 171 exit(EX_OK); 172} 173 174static RETSIGTYPE 175sig_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 204static RETSIGTYPE 205sig_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 217static sigset_t signalset; 218#endif 219 220static void * 221hold_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 259static void 260set_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 271static void 272release_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 302static 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 328static void 329do_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 gstate->fulldraw = Yes; 349 gstate->max_topn += display_setmulti(gstate->percpustates); 350 break; 351#ifdef ENABLE_COLOR 352 case 'C': 353 gstate->use_color = !gstate->use_color; 354 break; 355#endif 356 357 case 'D': 358 debug_set(1); 359 break; 360 361 case 'v': 362 fprintf(stderr, "%s: version %s\n", myname, version_string()); 363 exit(EX_OK); 364 break; 365 366 case 'b': 367 case 'n': 368 gstate->interactive = No; 369 break; 370 371 case 'a': 372 gstate->displays = Infinity; 373 gstate->topn = Infinity; 374 break; 375 376 case 'i': 377 gstate->interactive = Yes; 378 break; 379 380 case 'o': 381 gstate->order_name = optarg; 382 break; 383 384 case 'd': 385 i = atoiwi(optarg); 386 if (i == Invalid || i == 0) 387 { 388 message_error(" Bad display count"); 389 } 390 else 391 { 392 gstate->displays = i; 393 } 394 break; 395 396 case 's': 397 f = atof(optarg); 398 if (f < 0 || (f == 0 && getuid() != 0)) 399 { 400 message_error(" Bad seconds delay"); 401 } 402 else 403 { 404 gstate->delay = f; 405 } 406 break; 407 408 case 'u': 409 gstate->show_usernames = !gstate->show_usernames; 410 break; 411 412 case 'U': 413 i = userid(optarg); 414 if (i == -1) 415 { 416 message_error(" Unknown user '%s'", optarg); 417 } 418 else 419 { 420 gstate->pselect.uid = i; 421 } 422 break; 423 424 case 'm': 425 i = atoi(optarg); 426 gstate->pselect.mode = i; 427 break; 428 429 case 'S': 430 gstate->pselect.system = !gstate->pselect.system; 431 break; 432 433 case 'I': 434 gstate->pselect.idle = !gstate->pselect.idle; 435 break; 436 437#ifdef ENABLE_COLOR 438 case 'T': 439 gstate->show_tags = 1; 440 break; 441#endif 442 443 case 'c': 444 gstate->pselect.fullcmd = !gstate->pselect.fullcmd; 445 break; 446 447 case 't': 448 gstate->pselect.threads = !gstate->pselect.threads; 449 break; 450 451 case 'p': 452 gstate->pselect.pid = atoi(optarg); 453 break; 454 455 case 'q': /* be quick about it */ 456 /* only allow this if user is really root */ 457 if (getuid() == 0) 458 { 459 /* be very un-nice! */ 460 (void) nice(-20); 461 } 462 else 463 { 464 message_error(" Option -q can only be used by root"); 465 } 466 break; 467 468 default: 469 fprintf(stderr, "\ 470Top version %s\n\ 471Usage: %s [-1CISTabcinqtuv] [-d count] [-m mode] [-o field] [-p pid]\n\ 472 [-s time] [-U username] [number]\n", 473 version_string(), myname); 474 exit(EX_USAGE); 475 } 476 } 477 478 /* get count of top processes to display */ 479 if (optind < ac && *av[optind]) 480 { 481 if ((i = atoiwi(av[optind])) == Invalid) 482 { 483 message_error(" Process count not a number"); 484 } 485 else 486 { 487 gstate->topn = i; 488 } 489 } 490} 491 492static void 493do_display(globalstate *gstate) 494 495{ 496 int active_procs; 497 int i; 498 time_t curr_time; 499 caddr_t processes; 500 struct system_info system_info; 501 char *hdr; 502 503 /* get the time */ 504 time_mark(&(gstate->now)); 505 curr_time = (time_t)(gstate->now.tv_sec); 506 507 /* get the current stats */ 508 get_system_info(&system_info); 509 510 /* get the current processes */ 511 processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index); 512 513 /* determine number of processes to actually display */ 514 if (gstate->topn > 0) 515 { 516 /* this number will be the smallest of: active processes, 517 number user requested, number current screen accomodates */ 518 active_procs = system_info.P_ACTIVE; 519 if (active_procs > gstate->topn) 520 { 521 active_procs = gstate->topn; 522 } 523 if (active_procs > gstate->max_topn) 524 { 525 active_procs = gstate->max_topn; 526 } 527 } 528 else 529 { 530 /* dont show any */ 531 active_procs = 0; 532 } 533 534#ifdef HAVE_FORMAT_PROCESS_HEADER 535 /* get the process header to use */ 536 hdr = format_process_header(&(gstate->pselect), processes, active_procs); 537#else 538 hdr = gstate->header_text; 539#endif 540 541 /* full screen or update? */ 542 if (gstate->fulldraw) 543 { 544 display_clear(); 545 i_loadave(system_info.last_pid, system_info.load_avg); 546 i_uptime(&(gstate->statics->boottime), &curr_time); 547 i_timeofday(&curr_time); 548 i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); 549 if (gstate->show_cpustates) 550 { 551 i_cpustates(system_info.cpustates); 552 } 553 else 554 { 555 if (smart_terminal) 556 { 557 z_cpustates(); 558 } 559 gstate->show_cpustates = Yes; 560 } 561 i_kernel(system_info.kernel); 562 i_memory(system_info.memory); 563 i_swap(system_info.swap); 564 i_message(&(gstate->now)); 565 i_header(hdr); 566 for (i = 0; i < active_procs; i++) 567 { 568 i_process(i, format_next_process(processes, gstate->get_userid)); 569 } 570 i_endscreen(); 571 if (gstate->smart_terminal) 572 { 573 gstate->fulldraw = No; 574 } 575 } 576 else 577 { 578 u_loadave(system_info.last_pid, system_info.load_avg); 579 u_uptime(&(gstate->statics->boottime), &curr_time); 580 i_timeofday(&curr_time); 581 u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); 582 u_cpustates(system_info.cpustates); 583 u_kernel(system_info.kernel); 584 u_memory(system_info.memory); 585 u_swap(system_info.swap); 586 u_message(&(gstate->now)); 587 u_header(hdr); 588 for (i = 0; i < active_procs; i++) 589 { 590 u_process(i, format_next_process(processes, gstate->get_userid)); 591 } 592 u_endscreen(); 593 } 594} 595 596#ifdef DEBUG 597void 598timeval_xdprint(char *s, struct timeval tv) 599 600{ 601 xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec); 602} 603#endif 604 605static void 606do_wait(globalstate *gstate) 607 608{ 609 struct timeval wait; 610 611 double2tv(&wait, gstate->delay); 612 select(0, NULL, NULL, NULL, &wait); 613} 614 615static void 616do_command(globalstate *gstate) 617 618{ 619 int status; 620 struct timeval wait = {0, 0}; 621 struct timeval now; 622 fd_set readfds; 623 unsigned char ch; 624 625 /* calculate new refresh time */ 626 gstate->refresh = gstate->now; 627 double2tv(&now, gstate->delay); 628 timeradd(&now, &gstate->refresh, &gstate->refresh); 629 time_get(&now); 630 631 /* loop waiting for time to expire */ 632 do { 633 /* calculate time to wait */ 634 if (gstate->delay > 0) 635 { 636 wait = gstate->refresh; 637 wait.tv_usec -= now.tv_usec; 638 if (wait.tv_usec < 0) 639 { 640 wait.tv_usec += 1000000; 641 wait.tv_sec--; 642 } 643 wait.tv_sec -= now.tv_sec; 644 } 645 646 /* set up arguments for select on stdin (0) */ 647 FD_ZERO(&readfds); 648 FD_SET(STDIN_FILENO, &readfds); 649 650 /* wait for something to read or time out */ 651 if (select(32, &readfds, NULL, NULL, &wait) > 0) 652 { 653 /* read it */ 654 if (read(STDIN_FILENO, &ch, 1) != 1) 655 { 656 /* read error */ 657 message_error(" Read error on stdin"); 658 quit(EX_DATAERR); 659 /*NOTREACHED*/ 660 } 661 662 /* mark pending messages as old */ 663 message_mark(); 664 665 /* dispatch */ 666 status = command_process(gstate, (int)ch); 667 switch(status) 668 { 669 case CMD_ERROR: 670 quit(EX_SOFTWARE); 671 /*NOTREACHED*/ 672 673 case CMD_REFRESH: 674 return; 675 676 case CMD_UNKNOWN: 677 message_error(" Unknown command"); 678 break; 679 680 case CMD_NA: 681 message_error(" Command not available"); 682 } 683 } 684 685 /* get new time */ 686 time_get(&now); 687 } while (timercmp(&now, &(gstate->refresh), < )); 688} 689 690static void 691do_minidisplay(globalstate *gstate) 692 693{ 694 double real_delay; 695 struct system_info si; 696 697 /* save the real delay and substitute 1 second */ 698 real_delay = gstate->delay; 699 gstate->delay = 1; 700 701 /* wait 1 second for a command */ 702 time_mark(&(gstate->now)); 703 do_command(gstate); 704 705 /* do a mini update that only updates the cpustates */ 706 get_system_info(&si); 707 u_cpustates(si.cpustates); 708 709 /* restore the delay time */ 710 gstate->delay = real_delay; 711 712 /* done */ 713 i_endscreen(); 714} 715 716int 717main(int argc, char *argv[]) 718 719{ 720 char *env_top; 721 char **preset_argv; 722 int preset_argc = 0; 723 void *mask; 724 volatile int need_mini = 1; 725 static char top[] = "top"; 726 727 struct statics statics; 728 globalstate *gstate; 729 730 /* get our name */ 731 if (argc > 0) 732 { 733 if ((myname = strrchr(argv[0], '/')) == 0) 734 { 735 myname = argv[0]; 736 } 737 else 738 { 739 myname++; 740 } 741 } else 742 myname = top; 743 744 745 /* binary compatibility check */ 746#ifdef HAVE_UNAME 747 { 748 struct utsname uts; 749 750 if (uname(&uts) == 0) 751 { 752 if (strcmp(uts.machine, UNAME_HARDWARE) != 0) 753 { 754 fprintf(stderr, "%s: incompatible hardware platform\n", 755 myname); 756 exit(EX_UNAVAILABLE); 757 } 758 } 759 } 760#endif 761 762 /* initialization */ 763 gstate = ecalloc(1, sizeof(globalstate)); 764 gstate->statics = &statics; 765 time_mark(NULL); 766 767 /* preset defaults for various options */ 768 gstate->show_usernames = Yes; 769 gstate->topn = DEFAULT_TOPN; 770 gstate->delay = DEFAULT_DELAY; 771 gstate->fulldraw = Yes; 772 gstate->use_color = Yes; 773 gstate->interactive = Maybe; 774 gstate->percpustates = Yes; 775 776 /* preset defaults for process selection */ 777 gstate->pselect.idle = Yes; 778 gstate->pselect.system = Yes; 779 gstate->pselect.fullcmd = No; 780 gstate->pselect.command = NULL; 781 gstate->pselect.uid = -1; 782 gstate->pselect.pid = -1; 783 gstate->pselect.mode = 0; 784 785 /* use a large buffer for stdout */ 786#ifdef HAVE_SETVBUF 787 setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE); 788#else 789#ifdef HAVE_SETBUFFER 790 setbuffer(stdout, stdoutbuf, BUFFERSIZE); 791#endif 792#endif 793 794 /* get preset options from the environment */ 795 if ((env_top = getenv("TOP")) != NULL) 796 { 797 preset_argv = argparse(env_top, &preset_argc); 798 preset_argv[0] = myname; 799 do_arguments(gstate, preset_argc, preset_argv); 800 } 801 802 /* process arguments */ 803 do_arguments(gstate, argc, argv); 804 805#ifdef ENABLE_COLOR 806 /* If colour has been turned on read in the settings. */ 807 env_top = getenv("TOPCOLOURS"); 808 if (!env_top) 809 { 810 env_top = getenv("TOPCOLORS"); 811 } 812 /* must do something about error messages */ 813 color_env_parse(env_top); 814 color_activate(gstate->use_color); 815#endif 816 817 /* in order to support forward compatability, we have to ensure that 818 the entire statics structure is set to a known value before we call 819 machine_init. This way fields that a module does not know about 820 will retain their default values */ 821 memzero((void *)&statics, sizeof(statics)); 822 statics.boottime = -1; 823 824 /* call the platform-specific init */ 825 if (machine_init(&statics) == -1) 826 { 827 exit(EX_SOFTWARE); 828 } 829 830 /* create a helper list of sort order names */ 831 gstate->order_namelist = string_list(statics.order_names); 832 833 /* look up chosen sorting order */ 834 if (gstate->order_name != NULL) 835 { 836 int i; 837 838 if (statics.order_names == NULL) 839 { 840 message_error(" This platform does not support arbitrary ordering"); 841 } 842 else if ((i = string_index(gstate->order_name, 843 statics.order_names)) == -1) 844 { 845 message_error(" Sort order `%s' not recognized", gstate->order_name); 846 message_error(" Recognized sort orders: %s", gstate->order_namelist); 847 } 848 else 849 { 850 gstate->order_index = i; 851 } 852 } 853 854 /* initialize extensions */ 855 init_username(); 856 857 /* initialize termcap */ 858 gstate->smart_terminal = screen_readtermcap(gstate->interactive); 859 860 /* determine interactive state */ 861 if (gstate->interactive == Maybe) 862 { 863 gstate->interactive = smart_terminal; 864 } 865 866 /* if displays were not specified, choose an appropriate default */ 867 if (gstate->displays == 0) 868 { 869 gstate->displays = gstate->smart_terminal ? Infinity: 1; 870 } 871 872 /* we don't need a mini display when delay is less than 2 873 seconds or when we are not on a smart terminal */ 874 if (gstate->delay <= 1 || !smart_terminal) 875 { 876 need_mini = 0; 877 } 878 879#ifndef HAVE_FORMAT_PROCESS_HEADER 880 /* set constants for username/uid display */ 881 if (gstate->show_usernames) 882 { 883 gstate->header_text = format_header("USERNAME"); 884 gstate->get_userid = username; 885 } 886 else 887 { 888 gstate->header_text = format_header(" UID "); 889 gstate->get_userid = itoa7; 890 } 891#endif 892 gstate->pselect.usernames = gstate->show_usernames; 893 894 /* initialize display */ 895 if ((gstate->max_topn = display_init(&statics, gstate->percpustates)) == -1) 896 { 897 fprintf(stderr, "%s: display too small\n", myname); 898 exit(EX_OSERR); 899 } 900 901 /* check for infinity and for overflowed screen */ 902 if (gstate->topn == Infinity) 903 { 904 gstate->topn = INT_MAX; 905 } 906 else if (gstate->topn > gstate->max_topn) 907 { 908 message_error(" This terminal can only display %d processes", 909 gstate->max_topn); 910 } 911 912#ifdef ENABLE_COLOR 913 /* producing a list of color tags is easy */ 914 if (gstate->show_tags) 915 { 916 color_dump(stdout); 917 exit(EX_OK); 918 } 919#endif 920 921 /* hold all signals while we initialize the screen */ 922 mask = hold_signals(); 923 screen_init(); 924 925 /* set the signal handlers */ 926 set_signals(); 927 928 /* longjmp re-entry point */ 929 /* set the jump buffer for long jumps out of signal handlers */ 930 if (setjmp(jmp_int) != 0) 931 { 932 /* this is where we end up after processing sigwinch or sigtstp */ 933 934 /* tell display to resize its buffers, and get the new length */ 935 if ((gstate->max_topn = display_resize()) == -1) 936 { 937 /* thats bad */ 938 quit(EX_OSERR); 939 /*NOTREACHED*/ 940 } 941 942 /* set up for a full redraw, and get the current line count */ 943 gstate->fulldraw = Yes; 944 945 /* safe to release the signals now */ 946 release_signals(mask); 947 } 948 else 949 { 950 /* release the signals */ 951 release_signals(mask); 952 953 /* some systems require a warmup */ 954 /* always do a warmup for batch mode */ 955 if (gstate->interactive == 0 || statics.flags.warmup) 956 { 957 struct system_info system_info; 958 struct timeval timeout; 959 960 time_mark(&(gstate->now)); 961 get_system_info(&system_info); 962 (void)get_process_info(&system_info, &gstate->pselect, 0); 963 timeout.tv_sec = 1; 964 timeout.tv_usec = 0; 965 select(0, NULL, NULL, NULL, &timeout); 966 967 /* if we've warmed up, then we can show good states too */ 968 gstate->show_cpustates = Yes; 969 need_mini = 0; 970 } 971 } 972 973 /* main loop */ 974 while ((gstate->displays == -1) || (--gstate->displays > 0)) 975 { 976 do_display(gstate); 977 if (gstate->interactive) 978 { 979 if (need_mini) 980 { 981 do_minidisplay(gstate); 982 need_mini = 0; 983 } 984 do_command(gstate); 985 } 986 else 987 { 988 do_wait(gstate); 989 } 990 } 991 992 /* do one last display */ 993 do_display(gstate); 994 995 quit(EX_OK); 996 /* NOTREACHED */ 997 return 1; /* Keep compiler quiet. */ 998} 999