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