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