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