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