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/param.h> 17 #include <sys/jail.h> 18 #include <sys/time.h> 19 20 #include <ctype.h> 21 #include <curses.h> 22 #include <errno.h> 23 #include <jail.h> 24 #include <setjmp.h> 25 #include <stdlib.h> 26 #include <signal.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "commands.h" 31 #include "display.h" /* interface to display package */ 32 #include "screen.h" /* interface to screen package */ 33 #include "top.h" 34 #include "machine.h" 35 #include "utils.h" 36 #include "username.h" 37 38 /* Size of the stdio buffer given to stdout */ 39 #define Buffersize 2048 40 41 char copyright[] = 42 "Copyright (c) 1984 through 1996, William LeFebvre"; 43 44 typedef void sigret_t; 45 46 /* The buffer that stdio will use */ 47 static char stdoutbuf[Buffersize]; 48 49 /* build Signal masks */ 50 #define Smask(s) (1 << ((s) - 1)) 51 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 72 /* pointers to display routines */ 73 static void (*d_loadave)(int mpid, double *avenrun) = i_loadave; 74 static void (*d_procstates)(int total, int *brkdn) = i_procstates; 75 static void (*d_cpustates)(int *states) = i_cpustates; 76 static void (*d_memory)(int *stats) = i_memory; 77 static void (*d_arc)(int *stats) = i_arc; 78 static void (*d_carc)(int *stats) = i_carc; 79 static void (*d_swap)(int *stats) = i_swap; 80 static void (*d_message)(void) = i_message; 81 static void (*d_header)(const char *text) = i_header; 82 static void (*d_process)(int line, char *thisline) = i_process; 83 84 static void reset_display(void); 85 86 static void 87 reset_uids(void) 88 { 89 for (size_t i = 0; i < TOP_MAX_UIDS; ++i) 90 ps.uid[i] = -1; 91 } 92 93 static int 94 add_uid(int uid) 95 { 96 size_t i = 0; 97 98 /* Add the uid if there's room */ 99 for (; i < TOP_MAX_UIDS; ++i) 100 { 101 if (ps.uid[i] == -1 || ps.uid[i] == uid) 102 { 103 ps.uid[i] = uid; 104 break; 105 } 106 } 107 108 return (i == TOP_MAX_UIDS); 109 } 110 111 static void 112 rem_uid(int uid) 113 { 114 size_t i = 0; 115 size_t where = TOP_MAX_UIDS; 116 117 /* Look for the user to remove - no problem if it's not there */ 118 for (; i < TOP_MAX_UIDS; ++i) 119 { 120 if (ps.uid[i] == -1) 121 break; 122 if (ps.uid[i] == uid) 123 where = i; 124 } 125 126 /* Make sure we don't leave a hole in the middle */ 127 if (where != TOP_MAX_UIDS) 128 { 129 ps.uid[where] = ps.uid[i-1]; 130 ps.uid[i-1] = -1; 131 } 132 } 133 134 static int 135 handle_user(char *buf, size_t buflen) 136 { 137 int rc = 0; 138 int uid = -1; 139 char *buf2 = buf; 140 141 new_message(MT_standout, "Username to show (+ for all): "); 142 if (readline(buf, buflen, false) <= 0) 143 { 144 clear_message(); 145 return rc; 146 } 147 148 if (buf[0] == '+' || buf[0] == '-') 149 { 150 if (buf[1] == '\0') 151 { 152 reset_uids(); 153 goto end; 154 } 155 else 156 ++buf2; 157 } 158 159 if ((uid = userid(buf2)) == -1) 160 { 161 new_message(MT_standout, " %s: unknown user", buf2); 162 rc = 1; 163 goto end; 164 } 165 166 if (buf2 == buf) 167 { 168 reset_uids(); 169 ps.uid[0] = uid; 170 goto end; 171 } 172 173 if (buf[0] == '+') 174 { 175 if (add_uid(uid)) 176 { 177 new_message(MT_standout, " too many users, reset with '+'"); 178 rc = 1; 179 goto end; 180 } 181 } 182 else 183 rem_uid(uid); 184 185 end: 186 putchar('\r'); 187 return rc; 188 } 189 190 int 191 main(int argc, char *argv[]) 192 { 193 int i; 194 int active_procs; 195 int change; 196 197 struct system_info system_info; 198 struct statics statics; 199 void * processes; 200 201 static char tempbuf1[50]; 202 static char tempbuf2[50]; 203 int old_sigmask; /* only used for BSD-style signals */ 204 int topn = Infinity; 205 int delay = Default_DELAY; 206 int displays = 0; /* indicates unspecified */ 207 int sel_ret = 0; 208 time_t curr_time; 209 char *(*get_userid)(int) = username; 210 const char *uname_field = "USERNAME"; 211 const char *header_text; 212 char *env_top; 213 const char **preset_argv; 214 int preset_argc = 0; 215 char **av; 216 int ac; 217 bool dostates = false; 218 bool do_unames = true; 219 char interactive = 2; 220 char warnings = 0; 221 char topn_specified = false; 222 char ch; 223 char *iptr; 224 char no_command = 1; 225 struct timeval timeout; 226 char *order_name = NULL; 227 int order_index = 0; 228 fd_set readfds; 229 char old_system = false; 230 231 static const char command_chars[] = "\f qh?en#sdkriIutHmSCajzPJwop"; 232 /* these defines enumerate the "strchr"s of the commands in command_chars */ 233 #define CMD_redraw 0 234 #define CMD_update 1 235 #define CMD_quit 2 236 #define CMD_help1 3 237 #define CMD_help2 4 238 #define CMD_OSLIMIT 4 /* terminals with OS can only handle commands */ 239 #define CMD_errors 5 /* less than or equal to CMD_OSLIMIT */ 240 #define CMD_number1 6 241 #define CMD_number2 7 242 #define CMD_delay 8 243 #define CMD_displays 9 244 #define CMD_kill 10 245 #define CMD_renice 11 246 #define CMD_idletog 12 247 #define CMD_idletog2 13 248 #define CMD_user 14 249 #define CMD_selftog 15 250 #define CMD_thrtog 16 251 #define CMD_viewtog 17 252 #define CMD_viewsys 18 253 #define CMD_wcputog 19 254 #define CMD_showargs 20 255 #define CMD_jidtog 21 256 #define CMD_kidletog 22 257 #define CMD_pcputog 23 258 #define CMD_jail 24 259 #define CMD_swaptog 25 260 #define CMD_order 26 261 #define CMD_pid 27 262 263 /* set the buffer for stdout */ 264 #ifdef DEBUG 265 extern FILE *debug; 266 debug = fopen("debug.run", "w"); 267 setbuffer(stdout, NULL, 0); 268 #else 269 setbuffer(stdout, stdoutbuf, Buffersize); 270 #endif 271 272 /* get our name */ 273 if (argc > 0) 274 { 275 if ((myname = strrchr(argv[0], '/')) == 0) 276 { 277 myname = argv[0]; 278 } 279 else 280 { 281 myname++; 282 } 283 } 284 285 /* initialize some selection options */ 286 ps.idle = true; 287 ps.self = -1; 288 ps.system = false; 289 reset_uids(); 290 ps.thread = false; 291 ps.wcpu = 1; 292 ps.jid = -1; 293 ps.jail = false; 294 ps.swap = false; 295 ps.kidle = true; 296 ps.pid = -1; 297 ps.command = NULL; 298 299 /* get preset options from the environment */ 300 if ((env_top = getenv("TOP")) != NULL) 301 { 302 av = preset_argv = argparse(env_top, &preset_argc); 303 ac = preset_argc; 304 305 /* set the dummy argument to an explanatory message, in case 306 getopt encounters a bad argument */ 307 preset_argv[0] = "while processing environment"; 308 } 309 310 /* process options */ 311 do { 312 /* if we're done doing the presets, then process the real arguments */ 313 if (preset_argc == 0) 314 { 315 ac = argc; 316 av = argv; 317 318 /* this should keep getopt happy... */ 319 optind = 1; 320 } 321 322 while ((i = getopt(ac, av, "CSIHPabijJ:nquvzs:d:U:m:o:p:tw")) != EOF) 323 { 324 switch(i) 325 { 326 case 'v': /* show version number */ 327 fprintf(stderr, "%s: version FreeBSD\n", myname); 328 exit(1); 329 break; 330 331 case 'u': /* toggle uid/username display */ 332 do_unames = !do_unames; 333 break; 334 335 case 'U': /* display only username's processes */ 336 if ((ps.uid[0] = userid(optarg)) == -1) 337 { 338 fprintf(stderr, "%s: unknown user\n", optarg); 339 exit(1); 340 } 341 break; 342 343 case 'S': /* show system processes */ 344 ps.system = true; 345 old_system = true; 346 break; 347 348 case 'I': /* show idle processes */ 349 ps.idle = !ps.idle; 350 break; 351 352 case 'i': /* go interactive regardless */ 353 interactive = 1; 354 break; 355 356 case 'n': /* batch, or non-interactive */ 357 case 'b': 358 interactive = 0; 359 break; 360 361 case 'a': 362 fmt_flags ^= FMT_SHOWARGS; 363 break; 364 365 case 'd': /* number of displays to show */ 366 if ((i = atoiwi(optarg)) == Invalid || i == 0) 367 { 368 fprintf(stderr, 369 "%s: warning: display count should be positive -- option ignored\n", 370 myname); 371 warnings++; 372 } 373 else 374 { 375 displays = i; 376 } 377 break; 378 case 'p': { 379 unsigned long long num; 380 const char *errstr; 381 382 num = strtonum(optarg, 0, INT_MAX, &errstr); 383 if (errstr != NULL || !find_pid(num)) { 384 fprintf(stderr, "%s: unknown pid\n", optarg); 385 exit(1); 386 } 387 ps.pid = (pid_t)num; 388 ps.system = true; 389 break; 390 } 391 392 case 's': 393 if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0)) 394 { 395 fprintf(stderr, 396 "%s: warning: seconds delay should be positive -- using default\n", 397 myname); 398 delay = Default_DELAY; 399 warnings++; 400 } 401 break; 402 403 case 'q': /* be quick about it */ 404 /* only allow this if user is really root */ 405 if (getuid() == 0) 406 { 407 /* be very un-nice! */ 408 nice(-20); 409 } 410 else 411 { 412 fprintf(stderr, 413 "%s: warning: `-q' option can only be used by root\n", 414 myname); 415 warnings++; 416 } 417 break; 418 419 case 'm': /* select display mode */ 420 if (strcmp(optarg, "io") == 0) { 421 displaymode = DISP_IO; 422 } else if (strcmp(optarg, "cpu") == 0) { 423 displaymode = DISP_CPU; 424 } else { 425 fprintf(stderr, 426 "%s: warning: `-m' option can only take args " 427 "'io' or 'cpu'\n", 428 myname); 429 exit(1); 430 } 431 break; 432 433 case 'o': /* select sort order */ 434 order_name = optarg; 435 break; 436 437 case 't': 438 ps.self = (ps.self == -1) ? getpid() : -1; 439 break; 440 441 case 'C': 442 ps.wcpu = !ps.wcpu; 443 break; 444 445 case 'H': 446 ps.thread = !ps.thread; 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 char **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); /* why must I do this? */ 605 sleep((unsigned)(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 /*NOTREACHED*/ 714 } 715 716 /* only do the rest if we have more displays to show */ 717 if (displays) 718 { 719 /* switch out for new display on smart terminals */ 720 if (smart_terminal) 721 { 722 if (overstrike) 723 { 724 reset_display(); 725 } 726 else 727 { 728 d_loadave = u_loadave; 729 d_procstates = u_procstates; 730 d_cpustates = u_cpustates; 731 d_memory = u_memory; 732 d_arc = u_arc; 733 d_carc = u_carc; 734 d_swap = u_swap; 735 d_message = u_message; 736 d_header = u_header; 737 d_process = u_process; 738 } 739 } 740 741 no_command = true; 742 if (!interactive) 743 { 744 sleep(delay); 745 if (leaveflag) { 746 end_screen(); 747 exit(0); 748 } 749 } 750 else while (no_command) 751 { 752 /* assume valid command unless told otherwise */ 753 no_command = false; 754 755 /* set up arguments for select with timeout */ 756 FD_ZERO(&readfds); 757 FD_SET(0, &readfds); /* for standard input */ 758 timeout.tv_sec = delay; 759 timeout.tv_usec = 0; 760 761 if (leaveflag) { 762 end_screen(); 763 exit(0); 764 } 765 766 if (tstopflag) { 767 /* move to the lower left */ 768 end_screen(); 769 fflush(stdout); 770 771 /* default the signal handler action */ 772 signal(SIGTSTP, SIG_DFL); 773 774 /* unblock the signal and send ourselves one */ 775 sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); 776 kill(0, SIGTSTP); 777 778 /* reset the signal handler */ 779 signal(SIGTSTP, tstop); 780 781 /* reinit screen */ 782 reinit_screen(); 783 reset_display(); 784 tstopflag = 0; 785 goto restart; 786 } 787 788 if (winchflag) { 789 /* reascertain the screen dimensions */ 790 get_screensize(); 791 792 /* tell display to resize */ 793 max_topn = display_resize(); 794 795 /* reset the signal handler */ 796 signal(SIGWINCH, top_winch); 797 798 reset_display(); 799 winchflag = 0; 800 goto restart; 801 } 802 803 /* wait for either input or the end of the delay period */ 804 sel_ret = select(2, &readfds, NULL, NULL, &timeout); 805 if (sel_ret < 0 && errno != EINTR) 806 quit(0); 807 if (sel_ret > 0) 808 { 809 int newval; 810 char *errmsg; 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 /*NOTREACHED*/ 824 } 825 if ((iptr = strchr(command_chars, ch)) == NULL) 826 { 827 if (ch != '\r' && ch != '\n') 828 { 829 /* illegal command */ 830 new_message(MT_standout, " Command not understood"); 831 } 832 putchar('\r'); 833 no_command = true; 834 } 835 else 836 { 837 change = iptr - command_chars; 838 if (overstrike && change > CMD_OSLIMIT) 839 { 840 /* error */ 841 new_message(MT_standout, 842 " Command cannot be handled by this terminal"); 843 putchar('\r'); 844 no_command = true; 845 } 846 else switch(change) 847 { 848 case CMD_redraw: /* redraw screen */ 849 reset_display(); 850 break; 851 852 case CMD_update: /* merely update display */ 853 /* is the load average high? */ 854 if (system_info.load_avg[0] > LoadMax) 855 { 856 /* yes, go home for visual feedback */ 857 go_home(); 858 fflush(stdout); 859 } 860 break; 861 862 case CMD_quit: /* quit */ 863 quit(0); 864 /*NOTREACHED*/ 865 break; 866 867 case CMD_help1: /* help */ 868 case CMD_help2: 869 reset_display(); 870 top_clear(); 871 show_help(); 872 top_standout("Hit any key to continue: "); 873 fflush(stdout); 874 read(0, &ch, 1); 875 break; 876 877 case CMD_errors: /* show errors */ 878 if (error_count() == 0) 879 { 880 new_message(MT_standout, 881 " Currently no errors to report."); 882 putchar('\r'); 883 no_command = true; 884 } 885 else 886 { 887 reset_display(); 888 top_clear(); 889 show_errors(); 890 top_standout("Hit any key to continue: "); 891 fflush(stdout); 892 read(0, &ch, 1); 893 } 894 break; 895 896 case CMD_number1: /* new number */ 897 case CMD_number2: 898 new_message(MT_standout, 899 "Number of processes to show: "); 900 newval = readline(tempbuf1, 8, true); 901 if (newval > -1) 902 { 903 if (newval > max_topn) 904 { 905 new_message(MT_standout | MT_delayed, 906 " This terminal can only display %d processes.", 907 max_topn); 908 putchar('\r'); 909 } 910 911 if (newval == 0) 912 { 913 /* inhibit the header */ 914 display_header(false); 915 } 916 else if (newval > topn && topn == 0) 917 { 918 /* redraw the header */ 919 display_header(true); 920 d_header = i_header; 921 } 922 topn = newval; 923 } 924 break; 925 926 case CMD_delay: /* new seconds delay */ 927 new_message(MT_standout, "Seconds to delay: "); 928 if ((i = readline(tempbuf1, 8, true)) > -1) 929 { 930 if ((delay = i) == 0 && getuid() != 0) 931 { 932 delay = 1; 933 } 934 } 935 clear_message(); 936 break; 937 938 case CMD_displays: /* change display count */ 939 new_message(MT_standout, 940 "Displays to show (currently %s): ", 941 displays == -1 ? "infinite" : 942 itoa(displays)); 943 if ((i = readline(tempbuf1, 10, true)) > 0) 944 { 945 displays = i; 946 } 947 else if (i == 0) 948 { 949 quit(0); 950 } 951 clear_message(); 952 break; 953 954 case CMD_kill: /* kill program */ 955 new_message(0, "kill "); 956 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 957 { 958 if ((errmsg = kill_procs(tempbuf2)) != NULL) 959 { 960 new_message(MT_standout, "%s", errmsg); 961 putchar('\r'); 962 no_command = true; 963 } 964 } 965 else 966 { 967 clear_message(); 968 } 969 break; 970 971 case CMD_renice: /* renice program */ 972 new_message(0, "renice "); 973 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 974 { 975 if ((errmsg = renice_procs(tempbuf2)) != NULL) 976 { 977 new_message(MT_standout, "%s", errmsg); 978 putchar('\r'); 979 no_command = true; 980 } 981 } 982 else 983 { 984 clear_message(); 985 } 986 break; 987 988 case CMD_idletog: 989 case CMD_idletog2: 990 ps.idle = !ps.idle; 991 new_message(MT_standout | MT_delayed, 992 " %sisplaying idle processes.", 993 ps.idle ? "D" : "Not d"); 994 putchar('\r'); 995 break; 996 997 case CMD_selftog: 998 ps.self = (ps.self == -1) ? getpid() : -1; 999 new_message(MT_standout | MT_delayed, 1000 " %sisplaying self.", 1001 (ps.self == -1) ? "D" : "Not d"); 1002 putchar('\r'); 1003 break; 1004 1005 case CMD_user: 1006 if (handle_user(tempbuf2, sizeof(tempbuf2))) 1007 no_command = true; 1008 break; 1009 1010 case CMD_thrtog: 1011 ps.thread = !ps.thread; 1012 new_message(MT_standout | MT_delayed, 1013 " Displaying threads %s", 1014 ps.thread ? "separately" : "as a count"); 1015 header_text = format_header(uname_field); 1016 reset_display(); 1017 putchar('\r'); 1018 break; 1019 case CMD_wcputog: 1020 ps.wcpu = !ps.wcpu; 1021 new_message(MT_standout | MT_delayed, 1022 " Displaying %s CPU", 1023 ps.wcpu ? "weighted" : "raw"); 1024 header_text = format_header(uname_field); 1025 reset_display(); 1026 putchar('\r'); 1027 break; 1028 case CMD_viewtog: 1029 if (++displaymode == DISP_MAX) 1030 displaymode = 0; 1031 header_text = format_header(uname_field); 1032 display_header(true); 1033 d_header = i_header; 1034 reset_display(); 1035 break; 1036 case CMD_viewsys: 1037 ps.system = !ps.system; 1038 old_system = ps.system; 1039 break; 1040 case CMD_showargs: 1041 fmt_flags ^= FMT_SHOWARGS; 1042 break; 1043 case CMD_order: 1044 new_message(MT_standout, 1045 "Order to sort: "); 1046 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 1047 { 1048 if ((i = string_index(tempbuf2, statics.order_names)) == -1) 1049 { 1050 new_message(MT_standout, 1051 " %s: unrecognized sorting order", tempbuf2); 1052 no_command = true; 1053 } 1054 else 1055 { 1056 order_index = i; 1057 } 1058 putchar('\r'); 1059 } 1060 else 1061 { 1062 clear_message(); 1063 } 1064 break; 1065 case CMD_jidtog: 1066 ps.jail = !ps.jail; 1067 new_message(MT_standout | MT_delayed, 1068 " %sisplaying jail ID.", 1069 ps.jail ? "D" : "Not d"); 1070 header_text = format_header(uname_field); 1071 reset_display(); 1072 putchar('\r'); 1073 break; 1074 1075 case CMD_jail: 1076 new_message(MT_standout, 1077 "Jail to show (+ for all): "); 1078 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 1079 { 1080 if (tempbuf2[0] == '+' && 1081 tempbuf2[1] == '\0') 1082 { 1083 ps.jid = -1; 1084 } 1085 else if ((i = jail_getid(tempbuf2)) == -1) 1086 { 1087 new_message(MT_standout, 1088 " %s: unknown jail", tempbuf2); 1089 no_command = true; 1090 } 1091 else 1092 { 1093 ps.jid = i; 1094 } 1095 if (ps.jail == 0) { 1096 ps.jail = 1; 1097 new_message(MT_standout | 1098 MT_delayed, " Displaying jail " 1099 "ID."); 1100 header_text = 1101 format_header(uname_field); 1102 reset_display(); 1103 } 1104 putchar('\r'); 1105 } 1106 else 1107 { 1108 clear_message(); 1109 } 1110 break; 1111 1112 case CMD_kidletog: 1113 ps.kidle = !ps.kidle; 1114 new_message(MT_standout | MT_delayed, 1115 " %sisplaying system idle process.", 1116 ps.kidle ? "D" : "Not d"); 1117 putchar('\r'); 1118 break; 1119 case CMD_pcputog: 1120 pcpu_stats = !pcpu_stats; 1121 new_message(MT_standout | MT_delayed, 1122 " Displaying %sCPU statistics.", 1123 pcpu_stats ? "per-" : "global "); 1124 toggle_pcpustats(); 1125 max_topn = display_updatecpus(&statics); 1126 reset_display(); 1127 putchar('\r'); 1128 break; 1129 case CMD_swaptog: 1130 ps.swap = !ps.swap; 1131 new_message(MT_standout | MT_delayed, 1132 " %sisplaying per-process swap usage.", 1133 ps.swap ? "D" : "Not d"); 1134 header_text = format_header(uname_field); 1135 reset_display(); 1136 putchar('\r'); 1137 break; 1138 case CMD_pid: 1139 new_message(MT_standout, 1140 "Process id to show (+ for all): "); 1141 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) { 1142 if (tempbuf2[0] == '+' && 1143 tempbuf2[1] == '\0') { 1144 ps.pid = (pid_t)-1; 1145 ps.system = old_system; 1146 } else { 1147 unsigned long long num; 1148 const char *errstr; 1149 1150 num = strtonum(tempbuf2, 0, INT_MAX, 1151 &errstr); 1152 if (errstr != NULL || !find_pid(num)) { 1153 new_message(MT_standout, 1154 " %s: unknown pid", 1155 tempbuf2); 1156 no_command = true; 1157 } else { 1158 if (ps.system == false) 1159 old_system = false; 1160 ps.pid = (pid_t)num; 1161 ps.system = true; 1162 } 1163 } 1164 putchar('\r'); 1165 } else 1166 clear_message(); 1167 break; 1168 default: 1169 new_message(MT_standout, " BAD CASE IN SWITCH!"); 1170 putchar('\r'); 1171 } 1172 } 1173 1174 /* flush out stuff that may have been written */ 1175 fflush(stdout); 1176 } 1177 } 1178 } 1179 } 1180 1181 #ifdef DEBUG 1182 fclose(debug); 1183 #endif 1184 quit(0); 1185 /*NOTREACHED*/ 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 1234 quit(int status) /* exit under duress */ 1235 { 1236 end_screen(); 1237 exit(status); 1238 } 1239