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