1 /*- 2 * Top users/processes display for Unix 3 * 4 * This program may be freely redistributed, 5 * but this entire comment MUST remain intact. 6 * 7 * Copyright (c) 1984, 1989, William LeFebvre, Rice University 8 * Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University 9 * Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory 10 * Copyright (c) 1996, William LeFebvre, Group sys Consulting 11 * 12 * $FreeBSD$ 13 */ 14 15 #include <sys/types.h> 16 #include <sys/time.h> 17 #include <sys/cdefs.h> 18 #include <sys/limits.h> 19 #include <sys/resource.h> 20 #include <sys/select.h> 21 #include <sys/signal.h> 22 23 #include <assert.h> 24 #include <errno.h> 25 #include <getopt.h> 26 #include <jail.h> 27 #include <stdbool.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <signal.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #include "commands.h" 35 #include "display.h" /* interface to display package */ 36 #include "screen.h" /* interface to screen package */ 37 #include "top.h" 38 #include "machine.h" 39 #include "utils.h" 40 #include "username.h" 41 42 /* Size of the stdio buffer given to stdout */ 43 #define Buffersize 2048 44 45 char copyright[] = 46 "Copyright (c) 1984 through 1996, William LeFebvre"; 47 48 typedef void sigret_t; 49 50 /* The buffer that stdio will use */ 51 static char stdoutbuf[Buffersize]; 52 53 /* build Signal masks */ 54 #define Smask(s) (1 << ((s) - 1)) 55 56 57 static int fmt_flags = 0; 58 int pcpu_stats = false; 59 60 /* signal handling routines */ 61 static sigret_t leave(int); 62 static sigret_t tstop(int); 63 static sigret_t top_winch(int); 64 65 static volatile sig_atomic_t leaveflag; 66 static volatile sig_atomic_t tstopflag; 67 static volatile sig_atomic_t winchflag; 68 69 /* values which need to be accessed by signal handlers */ 70 static int max_topn; /* maximum displayable processes */ 71 72 /* miscellaneous things */ 73 struct process_select ps; 74 const char * myname = "top"; 75 pid_t mypid; 76 77 /* pointers to display routines */ 78 static void (*d_loadave)(int mpid, double *avenrun) = i_loadave; 79 static void (*d_procstates)(int total, int *brkdn) = i_procstates; 80 static void (*d_cpustates)(int *states) = i_cpustates; 81 static void (*d_memory)(int *stats) = i_memory; 82 static void (*d_arc)(int *stats) = i_arc; 83 static void (*d_carc)(int *stats) = i_carc; 84 static void (*d_swap)(int *stats) = i_swap; 85 static void (*d_message)(void) = i_message; 86 static void (*d_header)(const char *text) = i_header; 87 static void (*d_process)(int line, char *thisline) = i_process; 88 89 static void reset_display(void); 90 91 static const struct option longopts[] = { 92 { "cpu-display-mode", no_argument, NULL, 'C' }, /* differs from orignal */ 93 /* D reserved */ 94 { "thread", no_argument, NULL, 'H' }, 95 { "idle-procs", no_argument, NULL, 'I' }, 96 { "jail", required_argument, NULL, 'J' }, 97 { "per-cpu", no_argument, NULL, 'P' }, 98 { "system-procs", no_argument, NULL, 'S' }, 99 { "thread-id", no_argument, NULL, 'T' }, /* differs from orignal */ 100 { "user", required_argument, NULL, 'U' }, 101 { "all", no_argument, NULL, 'a' }, 102 { "batch", no_argument, NULL, 'b' }, 103 /* c reserved */ 104 { "displays", required_argument, NULL, 'd' }, 105 { "interactive", no_argument, NULL, 'i' }, 106 { "jail-id", no_argument, NULL, 'j' }, 107 { "display-mode", required_argument, NULL, 'm' }, 108 /* n is identical to batch */ 109 { "sort-order", required_argument, NULL, 'o' }, 110 { "pid", required_argument, NULL, 'p' }, 111 { "quick", no_argument, NULL, 'q' }, 112 { "delay", required_argument, NULL, 's' }, 113 { "threads", no_argument, NULL, 't' }, 114 { "uids", no_argument, NULL, 'u' }, 115 { "version", no_argument, NULL, 'v' }, 116 { "swap", no_argument, NULL, 'w' }, 117 { "system-idle-procs", no_argument, NULL, 'z' } 118 }; 119 120 static void 121 reset_uids(void) 122 { 123 for (size_t i = 0; i < TOP_MAX_UIDS; ++i) 124 ps.uid[i] = -1; 125 } 126 127 static int 128 add_uid(int uid) 129 { 130 size_t i = 0; 131 132 /* Add the uid if there's room */ 133 for (; i < TOP_MAX_UIDS; ++i) 134 { 135 if (ps.uid[i] == -1 || ps.uid[i] == uid) 136 { 137 ps.uid[i] = uid; 138 break; 139 } 140 } 141 142 return (i == TOP_MAX_UIDS); 143 } 144 145 static void 146 rem_uid(int uid) 147 { 148 size_t i = 0; 149 size_t where = TOP_MAX_UIDS; 150 151 /* Look for the user to remove - no problem if it's not there */ 152 for (; i < TOP_MAX_UIDS; ++i) 153 { 154 if (ps.uid[i] == -1) 155 break; 156 if (ps.uid[i] == uid) 157 where = i; 158 } 159 160 /* Make sure we don't leave a hole in the middle */ 161 if (where != TOP_MAX_UIDS) 162 { 163 ps.uid[where] = ps.uid[i-1]; 164 ps.uid[i-1] = -1; 165 } 166 } 167 168 static int 169 handle_user(char *buf, size_t buflen) 170 { 171 int rc = 0; 172 int uid = -1; 173 char *buf2 = buf; 174 175 new_message(MT_standout, "Username to show (+ for all): "); 176 if (readline(buf, buflen, false) <= 0) 177 { 178 clear_message(); 179 return (rc); 180 } 181 182 if (buf[0] == '+' || buf[0] == '-') 183 { 184 if (buf[1] == '\0') 185 { 186 reset_uids(); 187 goto end; 188 } 189 else 190 ++buf2; 191 } 192 193 if ((uid = userid(buf2)) == -1) 194 { 195 new_message(MT_standout, " %s: unknown user", buf2); 196 rc = 1; 197 goto end; 198 } 199 200 if (buf2 == buf) 201 { 202 reset_uids(); 203 ps.uid[0] = uid; 204 goto end; 205 } 206 207 if (buf[0] == '+') 208 { 209 if (add_uid(uid)) 210 { 211 new_message(MT_standout, " too many users, reset with '+'"); 212 rc = 1; 213 goto end; 214 } 215 } 216 else 217 rem_uid(uid); 218 219 end: 220 putchar('\r'); 221 return (rc); 222 } 223 224 int 225 main(int argc, char *argv[]) 226 { 227 int i; 228 int active_procs; 229 230 struct system_info system_info; 231 struct statics statics; 232 void * processes; 233 234 static char tempbuf1[50]; 235 static char tempbuf2[50]; 236 int old_sigmask; /* only used for BSD-style signals */ 237 int topn = Infinity; 238 double delay = 2; 239 int displays = 0; /* indicates unspecified */ 240 int sel_ret = 0; 241 time_t curr_time; 242 char *(*get_userid)(int) = username; 243 const char *uname_field = "USERNAME"; 244 const char *header_text; 245 char *env_top; 246 const char **preset_argv; 247 int preset_argc = 0; 248 const char **av = NULL; 249 int ac = -1; 250 bool dostates = false; 251 bool do_unames = true; 252 char interactive = 2; 253 char warnings = 0; 254 char topn_specified = false; 255 char ch; 256 char no_command = 1; 257 struct timeval timeout; 258 char *order_name = NULL; 259 int order_index = 0; 260 fd_set readfds; 261 262 /* set the buffer for stdout */ 263 #ifdef DEBUG 264 extern FILE *debug; 265 debug = fopen("debug.run", "w"); 266 setbuffer(stdout, NULL, 0); 267 #else 268 setbuffer(stdout, stdoutbuf, Buffersize); 269 #endif 270 271 if (argc > 0) 272 { 273 if ((myname = strrchr(argv[0], '/')) == 0) 274 { 275 myname = argv[0]; 276 } 277 else 278 { 279 myname++; 280 } 281 } 282 283 mypid = getpid(); 284 285 /* get our name */ 286 /* initialize some selection options */ 287 ps.idle = true; 288 ps.self = false; 289 ps.system = false; 290 reset_uids(); 291 ps.thread = false; 292 ps.wcpu = 1; 293 ps.jid = -1; 294 ps.jail = false; 295 ps.swap = false; 296 ps.kidle = true; 297 ps.pid = -1; 298 ps.command = NULL; 299 ps.thread_id = false; 300 301 /* get preset options from the environment */ 302 if ((env_top = getenv("TOP")) != NULL) 303 { 304 av = preset_argv = argparse(env_top, &preset_argc); 305 ac = preset_argc; 306 307 /* set the dummy argument to an explanatory message, in case 308 getopt encounters a bad argument */ 309 preset_argv[0] = "while processing environment"; 310 } 311 312 /* process options */ 313 do { 314 /* if we're done doing the presets, then process the real arguments */ 315 if (preset_argc == 0) 316 { 317 ac = argc; 318 av = argv; 319 320 /* this should keep getopt happy... */ 321 optind = 1; 322 } 323 324 while ((i = getopt_long(ac, av, "CSIHPabijJ:nquvzs:d:U:m:o:p:Ttw", longopts, NULL)) != EOF) 325 { 326 switch(i) 327 { 328 case 'v': /* show version number */ 329 fprintf(stderr, "%s: version FreeBSD\n", myname); 330 exit(1); 331 break; 332 333 case 'u': /* toggle uid/username display */ 334 do_unames = !do_unames; 335 break; 336 337 case 'U': /* display only username's processes */ 338 if ((ps.uid[0] = userid(optarg)) == -1) 339 { 340 fprintf(stderr, "%s: unknown user\n", optarg); 341 exit(1); 342 } 343 break; 344 345 case 'S': /* show system processes */ 346 ps.system = true; 347 break; 348 349 case 'I': /* show idle processes */ 350 ps.idle = !ps.idle; 351 break; 352 353 case 'i': /* go interactive regardless */ 354 interactive = 1; 355 break; 356 357 case 'n': /* batch, or non-interactive */ 358 case 'b': 359 interactive = 0; 360 break; 361 362 case 'a': 363 fmt_flags ^= FMT_SHOWARGS; 364 break; 365 366 case 'd': /* number of displays to show */ 367 if ((i = atoiwi(optarg)) == Invalid || i == 0) 368 { 369 fprintf(stderr, 370 "%s: warning: display count should be positive -- option ignored\n", 371 myname); 372 warnings++; 373 } 374 else 375 { 376 displays = i; 377 } 378 break; 379 case 'p': { 380 unsigned long long num; 381 const char *errstr; 382 383 num = strtonum(optarg, 0, INT_MAX, &errstr); 384 if (errstr != NULL || !find_pid(num)) { 385 fprintf(stderr, "%s: unknown pid\n", optarg); 386 exit(1); 387 } 388 ps.pid = (pid_t)num; 389 ps.system = true; 390 break; 391 } 392 393 case 's': 394 delay = strtod(optarg, NULL); 395 if (delay < 0) { 396 fprintf(stderr, 397 "%s: warning: seconds delay should be positive -- using default\n", 398 myname); 399 delay = 2; 400 warnings++; 401 } 402 403 break; 404 405 case 'q': /* be quick about it */ 406 errno = 0; 407 i = setpriority(PRIO_PROCESS, 0, PRIO_MIN); 408 if (i == -1 && errno != 0) { 409 fprintf(stderr, 410 "%s: warning: `-q' option failed (%m)\n", myname); 411 warnings++; 412 } 413 break; 414 415 case 'm': /* select display mode */ 416 if (strcmp(optarg, "io") == 0) { 417 displaymode = DISP_IO; 418 } else if (strcmp(optarg, "cpu") == 0) { 419 displaymode = DISP_CPU; 420 } else { 421 fprintf(stderr, 422 "%s: warning: `-m' option can only take args " 423 "'io' or 'cpu'\n", 424 myname); 425 exit(1); 426 } 427 break; 428 429 case 'o': /* select sort order */ 430 order_name = optarg; 431 break; 432 433 case 't': 434 ps.self = !ps.self; 435 break; 436 437 case 'C': 438 ps.wcpu = !ps.wcpu; 439 break; 440 441 case 'H': 442 ps.thread = !ps.thread; 443 break; 444 445 case 'T': 446 ps.thread_id = !ps.thread_id; 447 break; 448 449 case 'j': 450 ps.jail = !ps.jail; 451 break; 452 453 case 'J': /* display only jail's processes */ 454 if ((ps.jid = jail_getid(optarg)) == -1) 455 { 456 fprintf(stderr, "%s: unknown jail\n", optarg); 457 exit(1); 458 } 459 ps.jail = 1; 460 break; 461 462 case 'P': 463 pcpu_stats = !pcpu_stats; 464 break; 465 466 case 'w': 467 ps.swap = 1; 468 break; 469 470 case 'z': 471 ps.kidle = !ps.kidle; 472 break; 473 474 default: 475 fprintf(stderr, 476 "Usage: %s [-abCHIijnPqStuvwz] [-d count] [-m io | cpu] [-o field] [-p pid]\n" 477 " [-s time] [-J jail] [-U username] [number]\n", 478 myname); 479 exit(1); 480 } 481 } 482 483 /* get count of top processes to display (if any) */ 484 if (optind < ac) 485 { 486 if ((topn = atoiwi(av[optind])) == Invalid) 487 { 488 fprintf(stderr, 489 "%s: warning: process display count should be non-negative -- using default\n", 490 myname); 491 warnings++; 492 } 493 else 494 { 495 topn_specified = true; 496 } 497 } 498 499 /* tricky: remember old value of preset_argc & set preset_argc = 0 */ 500 i = preset_argc; 501 preset_argc = 0; 502 503 /* repeat only if we really did the preset arguments */ 504 } while (i != 0); 505 506 /* set constants for username/uid display correctly */ 507 if (!do_unames) 508 { 509 uname_field = " UID "; 510 get_userid = itoa7; 511 } 512 513 /* initialize the kernel memory interface */ 514 if (machine_init(&statics) == -1) 515 { 516 exit(1); 517 } 518 519 /* determine sorting order index, if necessary */ 520 if (order_name != NULL) 521 { 522 if ((order_index = string_index(order_name, statics.order_names)) == -1) 523 { 524 const char * const *pp; 525 526 fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n", 527 myname, order_name); 528 fprintf(stderr, "\tTry one of these:"); 529 pp = statics.order_names; 530 while (*pp != NULL) 531 { 532 fprintf(stderr, " %s", *pp++); 533 } 534 fputc('\n', stderr); 535 exit(1); 536 } 537 } 538 539 /* initialize termcap */ 540 init_termcap(interactive); 541 542 /* get the string to use for the process area header */ 543 header_text = format_header(uname_field); 544 545 /* initialize display interface */ 546 if ((max_topn = display_init(&statics)) == -1) 547 { 548 fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); 549 exit(4); 550 } 551 552 /* print warning if user requested more processes than we can display */ 553 if (topn > max_topn) 554 { 555 fprintf(stderr, 556 "%s: warning: this terminal can only display %d processes.\n", 557 myname, max_topn); 558 warnings++; 559 } 560 561 /* adjust for topn == Infinity */ 562 if (topn == Infinity) 563 { 564 /* 565 * For smart terminals, infinity really means everything that can 566 * be displayed, or Largest. 567 * On dumb terminals, infinity means every process in the system! 568 * We only really want to do that if it was explicitly specified. 569 * This is always the case when "Default_TOPN != Infinity". But if 570 * topn wasn't explicitly specified and we are on a dumb terminal 571 * and the default is Infinity, then (and only then) we use 572 * "Nominal_TOPN" instead. 573 */ 574 topn = smart_terminal ? Largest : 575 (topn_specified ? Largest : Nominal_TOPN); 576 } 577 578 /* set header display accordingly */ 579 display_header(topn > 0); 580 581 /* determine interactive state */ 582 if (interactive == 2) 583 { 584 interactive = smart_terminal; 585 } 586 587 /* if # of displays not specified, fill it in */ 588 if (displays == 0) 589 { 590 displays = smart_terminal ? Infinity : 1; 591 } 592 593 /* hold interrupt signals while setting up the screen and the handlers */ 594 old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP)); 595 init_screen(); 596 signal(SIGINT, leave); 597 signal(SIGQUIT, leave); 598 signal(SIGTSTP, tstop); 599 signal(SIGWINCH, top_winch); 600 sigsetmask(old_sigmask); 601 if (warnings) 602 { 603 fputs("....", stderr); 604 fflush(stderr); 605 sleep(3 * warnings); 606 fputc('\n', stderr); 607 } 608 609 restart: 610 611 /* 612 * main loop -- repeat while display count is positive or while it 613 * indicates infinity (by being -1) 614 */ 615 616 while ((displays == -1) || (displays-- > 0)) 617 { 618 int (*compare)(const void * const, const void * const); 619 620 621 /* get the current stats */ 622 get_system_info(&system_info); 623 624 compare = compares[order_index]; 625 626 /* get the current set of processes */ 627 processes = 628 get_process_info(&system_info, &ps, compare); 629 630 /* display the load averages */ 631 (*d_loadave)(system_info.last_pid, 632 system_info.load_avg); 633 634 /* display the current time */ 635 /* this method of getting the time SHOULD be fairly portable */ 636 time(&curr_time); 637 i_uptime(&system_info.boottime, &curr_time); 638 i_timeofday(&curr_time); 639 640 /* display process state breakdown */ 641 (*d_procstates)(system_info.p_total, 642 system_info.procstates); 643 644 /* display the cpu state percentage breakdown */ 645 if (dostates) /* but not the first time */ 646 { 647 (*d_cpustates)(system_info.cpustates); 648 } 649 else 650 { 651 /* we'll do it next time */ 652 if (smart_terminal) 653 { 654 z_cpustates(); 655 } 656 else 657 { 658 putchar('\n'); 659 } 660 dostates = true; 661 } 662 663 /* display memory stats */ 664 (*d_memory)(system_info.memory); 665 (*d_arc)(system_info.arc); 666 (*d_carc)(system_info.carc); 667 668 /* display swap stats */ 669 (*d_swap)(system_info.swap); 670 671 /* handle message area */ 672 (*d_message)(); 673 674 /* update the header area */ 675 (*d_header)(header_text); 676 677 if (topn > 0) 678 { 679 /* determine number of processes to actually display */ 680 /* this number will be the smallest of: active processes, 681 number user requested, number current screen accomodates */ 682 active_procs = system_info.p_pactive; 683 if (active_procs > topn) 684 { 685 active_procs = topn; 686 } 687 if (active_procs > max_topn) 688 { 689 active_procs = max_topn; 690 } 691 692 /* now show the top "n" processes. */ 693 for (i = 0; i < active_procs; i++) 694 { 695 (*d_process)(i, format_next_process(processes, get_userid, 696 fmt_flags)); 697 } 698 } 699 else 700 { 701 i = 0; 702 } 703 704 /* do end-screen processing */ 705 u_endscreen(i); 706 707 /* now, flush the output buffer */ 708 if (fflush(stdout) != 0) 709 { 710 new_message(MT_standout, " Write error on stdout"); 711 putchar('\r'); 712 quit(1); 713 } 714 715 /* only do the rest if we have more displays to show */ 716 if (displays) 717 { 718 /* switch out for new display on smart terminals */ 719 if (smart_terminal) 720 { 721 if (overstrike) 722 { 723 reset_display(); 724 } 725 else 726 { 727 d_loadave = u_loadave; 728 d_procstates = u_procstates; 729 d_cpustates = u_cpustates; 730 d_memory = u_memory; 731 d_arc = u_arc; 732 d_carc = u_carc; 733 d_swap = u_swap; 734 d_message = u_message; 735 d_header = u_header; 736 d_process = u_process; 737 } 738 } 739 740 no_command = true; 741 if (!interactive) 742 { 743 usleep(delay * 1e6); 744 if (leaveflag) { 745 end_screen(); 746 exit(0); 747 } 748 } 749 else while (no_command) 750 { 751 /* assume valid command unless told otherwise */ 752 no_command = false; 753 754 /* set up arguments for select with timeout */ 755 FD_ZERO(&readfds); 756 FD_SET(0, &readfds); /* for standard input */ 757 timeout.tv_sec = delay; 758 timeout.tv_usec = 0; 759 760 if (leaveflag) { 761 end_screen(); 762 exit(0); 763 } 764 765 if (tstopflag) { 766 /* move to the lower left */ 767 end_screen(); 768 fflush(stdout); 769 770 /* default the signal handler action */ 771 signal(SIGTSTP, SIG_DFL); 772 773 /* unblock the signal and send ourselves one */ 774 sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); 775 kill(0, SIGTSTP); 776 777 /* reset the signal handler */ 778 signal(SIGTSTP, tstop); 779 780 /* reinit screen */ 781 reinit_screen(); 782 reset_display(); 783 tstopflag = 0; 784 goto restart; 785 } 786 787 if (winchflag) { 788 /* reascertain the screen dimensions */ 789 get_screensize(); 790 791 /* tell display to resize */ 792 max_topn = display_resize(); 793 794 /* reset the signal handler */ 795 signal(SIGWINCH, top_winch); 796 797 reset_display(); 798 winchflag = 0; 799 goto restart; 800 } 801 802 /* wait for either input or the end of the delay period */ 803 sel_ret = select(2, &readfds, NULL, NULL, &timeout); 804 if (sel_ret < 0 && errno != EINTR) 805 quit(0); 806 if (sel_ret > 0) 807 { 808 int newval; 809 const char *errmsg; 810 const struct command *cptr; 811 812 /* something to read -- clear the message area first */ 813 clear_message(); 814 815 /* now read it and convert to command strchr */ 816 /* (use "change" as a temporary to hold strchr) */ 817 if (read(0, &ch, 1) != 1) 818 { 819 /* read error: either 0 or -1 */ 820 new_message(MT_standout, " Read error on stdin"); 821 putchar('\r'); 822 quit(1); 823 } 824 if (ch == '\r' || ch == '\n') { 825 continue; 826 } 827 cptr = all_commands; 828 while (cptr->c != '\0') { 829 if (cptr->c == ch) { 830 break; 831 } 832 cptr++; 833 } 834 if (cptr->c == '\0') { 835 new_message(MT_standout, " Command not understood"); 836 putchar('\r'); 837 no_command = true; 838 } 839 if (overstrike && !cptr->available_to_dumb) 840 { 841 new_message(MT_standout, 842 " Command cannot be handled by this terminal"); 843 putchar('\r'); 844 no_command = true; 845 } 846 if (!no_command) { 847 switch(cptr->id) 848 { 849 case CMD_redraw: /* redraw screen */ 850 reset_display(); 851 break; 852 853 case CMD_update: /* merely update display */ 854 /* is the load average high? */ 855 if (system_info.load_avg[0] > LoadMax) 856 { 857 /* yes, go home for visual feedback */ 858 go_home(); 859 fflush(stdout); 860 } 861 break; 862 863 case CMD_quit: 864 quit(0); 865 break; 866 867 case CMD_help: 868 reset_display(); 869 top_clear(); 870 show_help(); 871 top_standout("Hit any key to continue: "); 872 fflush(stdout); 873 read(0, &ch, 1); 874 break; 875 876 case CMD_errors: /* show errors */ 877 if (error_count() == 0) 878 { 879 new_message(MT_standout, 880 " Currently no errors to report."); 881 putchar('\r'); 882 no_command = true; 883 } 884 else 885 { 886 reset_display(); 887 top_clear(); 888 show_errors(); 889 top_standout("Hit any key to continue: "); 890 fflush(stdout); 891 read(0, &ch, 1); 892 } 893 break; 894 895 case CMD_number: 896 new_message(MT_standout, 897 "Number of processes to show: "); 898 newval = readline(tempbuf1, 8, true); 899 if (newval > -1) 900 { 901 if (newval > max_topn) 902 { 903 new_message(MT_standout | MT_delayed, 904 " This terminal can only display %d processes.", 905 max_topn); 906 putchar('\r'); 907 } 908 909 if (newval == 0) 910 { 911 /* inhibit the header */ 912 display_header(false); 913 } 914 else if (newval > topn && topn == 0) 915 { 916 /* redraw the header */ 917 display_header(true); 918 d_header = i_header; 919 } 920 topn = newval; 921 } 922 break; 923 924 case CMD_delay: /* new seconds delay */ 925 new_message(MT_standout, "Seconds to delay: "); 926 if ((i = readline(tempbuf1, 8, true)) > -1) 927 { 928 if ((delay = i) == 0) 929 { 930 delay = 1; 931 } 932 } 933 clear_message(); 934 break; 935 936 case CMD_displays: /* change display count */ 937 new_message(MT_standout, 938 "Displays to show (currently %s): ", 939 displays == -1 ? "infinite" : 940 itoa(displays)); 941 if ((i = readline(tempbuf1, 10, true)) > 0) 942 { 943 displays = i; 944 } 945 else if (i == 0) 946 { 947 quit(0); 948 } 949 clear_message(); 950 break; 951 952 case CMD_kill: /* kill program */ 953 new_message(0, "kill "); 954 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 955 { 956 if ((errmsg = kill_procs(tempbuf2)) != NULL) 957 { 958 new_message(MT_standout, "%s", errmsg); 959 putchar('\r'); 960 no_command = true; 961 } 962 } 963 else 964 { 965 clear_message(); 966 } 967 break; 968 969 case CMD_renice: /* renice program */ 970 new_message(0, "renice "); 971 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 972 { 973 if ((errmsg = renice_procs(tempbuf2)) != NULL) 974 { 975 new_message(MT_standout, "%s", errmsg); 976 putchar('\r'); 977 no_command = true; 978 } 979 } 980 else 981 { 982 clear_message(); 983 } 984 break; 985 986 case CMD_idletog: 987 ps.idle = !ps.idle; 988 new_message(MT_standout | MT_delayed, 989 " %sisplaying idle processes.", 990 ps.idle ? "D" : "Not d"); 991 putchar('\r'); 992 break; 993 994 case CMD_selftog: 995 ps.self = !ps.self; 996 new_message(MT_standout | MT_delayed, 997 " %sisplaying self.", 998 (ps.self) ? "D" : "Not d"); 999 putchar('\r'); 1000 break; 1001 1002 case CMD_user: 1003 if (handle_user(tempbuf2, sizeof(tempbuf2))) 1004 no_command = true; 1005 break; 1006 1007 case CMD_thrtog: 1008 ps.thread = !ps.thread; 1009 new_message(MT_standout | MT_delayed, 1010 " Displaying threads %s", 1011 ps.thread ? "separately" : "as a count"); 1012 header_text = format_header(uname_field); 1013 reset_display(); 1014 putchar('\r'); 1015 break; 1016 1017 case CMD_toggletid: 1018 ps.thread_id = !ps.thread_id; 1019 new_message(MT_standout | MT_delayed, 1020 " Displaying %s", 1021 ps.thread_id ? "tid" : "pid"); 1022 header_text = format_header(uname_field); 1023 reset_display(); 1024 putchar('\r'); 1025 break; 1026 1027 case CMD_wcputog: 1028 ps.wcpu = !ps.wcpu; 1029 new_message(MT_standout | MT_delayed, 1030 " Displaying %s CPU", 1031 ps.wcpu ? "weighted" : "raw"); 1032 header_text = format_header(uname_field); 1033 reset_display(); 1034 putchar('\r'); 1035 break; 1036 case CMD_viewtog: 1037 displaymode = displaymode == DISP_IO ? DISP_CPU : DISP_IO; 1038 header_text = format_header(uname_field); 1039 display_header(true); 1040 d_header = i_header; 1041 reset_display(); 1042 break; 1043 case CMD_viewsys: 1044 ps.system = !ps.system; 1045 break; 1046 case CMD_showargs: 1047 fmt_flags ^= FMT_SHOWARGS; 1048 break; 1049 case CMD_order: 1050 new_message(MT_standout, 1051 "Order to sort: "); 1052 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 1053 { 1054 if ((i = string_index(tempbuf2, statics.order_names)) == -1) 1055 { 1056 new_message(MT_standout, 1057 " %s: unrecognized sorting order", tempbuf2); 1058 no_command = true; 1059 } 1060 else 1061 { 1062 order_index = i; 1063 } 1064 putchar('\r'); 1065 } 1066 else 1067 { 1068 clear_message(); 1069 } 1070 break; 1071 case CMD_jidtog: 1072 ps.jail = !ps.jail; 1073 new_message(MT_standout | MT_delayed, 1074 " %sisplaying jail ID.", 1075 ps.jail ? "D" : "Not d"); 1076 header_text = format_header(uname_field); 1077 reset_display(); 1078 putchar('\r'); 1079 break; 1080 1081 case CMD_jail: 1082 new_message(MT_standout, 1083 "Jail to show (+ for all): "); 1084 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 1085 { 1086 if (tempbuf2[0] == '+' && 1087 tempbuf2[1] == '\0') 1088 { 1089 ps.jid = -1; 1090 } 1091 else if ((i = jail_getid(tempbuf2)) == -1) 1092 { 1093 new_message(MT_standout, 1094 " %s: unknown jail", tempbuf2); 1095 no_command = true; 1096 } 1097 else 1098 { 1099 ps.jid = i; 1100 } 1101 if (ps.jail == 0) { 1102 ps.jail = 1; 1103 new_message(MT_standout | 1104 MT_delayed, " Displaying jail " 1105 "ID."); 1106 header_text = 1107 format_header(uname_field); 1108 reset_display(); 1109 } 1110 putchar('\r'); 1111 } 1112 else 1113 { 1114 clear_message(); 1115 } 1116 break; 1117 1118 case CMD_kidletog: 1119 ps.kidle = !ps.kidle; 1120 new_message(MT_standout | MT_delayed, 1121 " %sisplaying system idle process.", 1122 ps.kidle ? "D" : "Not d"); 1123 putchar('\r'); 1124 break; 1125 case CMD_pcputog: 1126 pcpu_stats = !pcpu_stats; 1127 new_message(MT_standout | MT_delayed, 1128 " Displaying %sCPU statistics.", 1129 pcpu_stats ? "per-" : "global "); 1130 toggle_pcpustats(); 1131 max_topn = display_updatecpus(&statics); 1132 reset_display(); 1133 putchar('\r'); 1134 break; 1135 case CMD_swaptog: 1136 ps.swap = !ps.swap; 1137 new_message(MT_standout | MT_delayed, 1138 " %sisplaying per-process swap usage.", 1139 ps.swap ? "D" : "Not d"); 1140 header_text = format_header(uname_field); 1141 reset_display(); 1142 putchar('\r'); 1143 break; 1144 case CMD_pid: 1145 new_message(MT_standout, 1146 "Process id to show (+ for all): "); 1147 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) { 1148 if (tempbuf2[0] == '+' && 1149 tempbuf2[1] == '\0') { 1150 ps.pid = (pid_t)-1; 1151 } else { 1152 unsigned long long num; 1153 const char *errstr; 1154 1155 num = strtonum(tempbuf2, 0, INT_MAX, 1156 &errstr); 1157 if (errstr != NULL || !find_pid(num)) { 1158 new_message(MT_standout, 1159 " %s: unknown pid", 1160 tempbuf2); 1161 no_command = true; 1162 } else { 1163 ps.pid = (pid_t)num; 1164 } 1165 } 1166 putchar('\r'); 1167 } else 1168 clear_message(); 1169 break; 1170 case CMD_NONE: 1171 assert("reached switch without command"); 1172 } 1173 } 1174 } 1175 1176 /* flush out stuff that may have been written */ 1177 fflush(stdout); 1178 } 1179 } 1180 } 1181 1182 #ifdef DEBUG 1183 fclose(debug); 1184 #endif 1185 quit(0); 1186 } 1187 1188 /* 1189 * reset_display() - reset all the display routine pointers so that entire 1190 * screen will get redrawn. 1191 */ 1192 1193 static void 1194 reset_display(void) 1195 { 1196 d_loadave = i_loadave; 1197 d_procstates = i_procstates; 1198 d_cpustates = i_cpustates; 1199 d_memory = i_memory; 1200 d_arc = i_arc; 1201 d_carc = i_carc; 1202 d_swap = i_swap; 1203 d_message = i_message; 1204 d_header = i_header; 1205 d_process = i_process; 1206 } 1207 1208 /* 1209 * signal handlers 1210 */ 1211 1212 static sigret_t 1213 leave(int i __unused) /* exit under normal conditions -- INT handler */ 1214 { 1215 1216 leaveflag = 1; 1217 } 1218 1219 static sigret_t 1220 tstop(int i __unused) /* SIGTSTP handler */ 1221 { 1222 1223 tstopflag = 1; 1224 } 1225 1226 static sigret_t 1227 top_winch(int i __unused) /* SIGWINCH handler */ 1228 { 1229 1230 winchflag = 1; 1231 } 1232 1233 void __dead2 1234 quit(int status) /* exit under duress */ 1235 { 1236 end_screen(); 1237 exit(status); 1238 } 1239