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