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 battery info (if any) */ 623 i_battery(statics.nbatteries, system_info.battery); 624 625 /* display the current time */ 626 /* this method of getting the time SHOULD be fairly portable */ 627 time(&curr_time); 628 i_uptime(&system_info.boottime, &curr_time); 629 i_timeofday(&curr_time); 630 631 /* display process state breakdown */ 632 (*d_procstates)(system_info.p_total, 633 system_info.procstates); 634 (*d_cpustates)(system_info.cpustates); 635 636 /* display memory stats */ 637 (*d_memory)(system_info.memory); 638 (*d_arc)(system_info.arc); 639 (*d_carc)(system_info.carc); 640 641 /* display swap stats */ 642 (*d_swap)(system_info.swap); 643 644 /* handle message area */ 645 (*d_message)(); 646 647 /* update the header area */ 648 (*d_header)(header_text); 649 650 if (topn > 0) 651 { 652 /* determine number of processes to actually display */ 653 /* this number will be the smallest of: active processes, 654 number user requested, number current screen accomodates */ 655 active_procs = system_info.p_pactive; 656 if (active_procs > topn) 657 { 658 active_procs = topn; 659 } 660 if (active_procs > max_topn) 661 { 662 active_procs = max_topn; 663 } 664 665 /* now show the top "n" processes. */ 666 for (i = 0; i < active_procs; i++) 667 { 668 (*d_process)(i, format_next_process(processes, get_userid, 669 fmt_flags)); 670 } 671 } 672 else 673 { 674 i = 0; 675 } 676 677 /* do end-screen processing */ 678 u_endscreen(i); 679 680 /* now, flush the output buffer */ 681 if (fflush(stdout) != 0) 682 { 683 new_message(MT_standout, " Write error on stdout"); 684 putchar('\r'); 685 quit(1); 686 } 687 688 /* only do the rest if we have more displays to show */ 689 if (displays) 690 { 691 /* switch out for new display on smart terminals */ 692 if (smart_terminal) 693 { 694 if (overstrike) 695 { 696 reset_display(); 697 } 698 else 699 { 700 d_loadave = u_loadave; 701 d_procstates = u_procstates; 702 d_cpustates = u_cpustates; 703 d_memory = u_memory; 704 d_arc = u_arc; 705 d_carc = u_carc; 706 d_swap = u_swap; 707 d_message = u_message; 708 d_header = u_header; 709 d_process = u_process; 710 } 711 } 712 713 no_command = true; 714 if (!interactive) 715 { 716 timeout = delay; 717 select(0, NULL, NULL, NULL, &timeout); 718 if (leaveflag) { 719 end_screen(); 720 exit(0); 721 } 722 } 723 else while (no_command) 724 { 725 /* assume valid command unless told otherwise */ 726 no_command = false; 727 728 /* set up arguments for select with timeout */ 729 FD_ZERO(&readfds); 730 FD_SET(0, &readfds); /* for standard input */ 731 timeout = delay; 732 733 if (leaveflag) { 734 end_screen(); 735 exit(0); 736 } 737 738 if (tstopflag) { 739 /* move to the lower left */ 740 end_screen(); 741 fflush(stdout); 742 743 /* default the signal handler action */ 744 signal(SIGTSTP, SIG_DFL); 745 746 /* unblock the signal and send ourselves one */ 747 sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1))); 748 kill(0, SIGTSTP); 749 750 /* reset the signal handler */ 751 signal(SIGTSTP, tstop); 752 753 /* reinit screen */ 754 reinit_screen(); 755 reset_display(); 756 tstopflag = 0; 757 goto restart; 758 } 759 760 if (winchflag) { 761 /* reascertain the screen dimensions */ 762 get_screensize(); 763 764 /* tell display to resize */ 765 max_topn = display_resize(); 766 767 /* reset the signal handler */ 768 signal(SIGWINCH, top_winch); 769 770 reset_display(); 771 winchflag = 0; 772 goto restart; 773 } 774 775 /* wait for either input or the end of the delay period */ 776 sel_ret = select(2, &readfds, NULL, NULL, &timeout); 777 if (sel_ret < 0 && errno != EINTR) 778 quit(0); 779 if (sel_ret > 0) 780 { 781 int newval; 782 const char *errmsg; 783 const struct command *cptr; 784 785 /* something to read -- clear the message area first */ 786 clear_message(); 787 788 /* now read it and convert to command strchr */ 789 /* (use "change" as a temporary to hold strchr) */ 790 if (read(0, &ch, 1) != 1) 791 { 792 /* read error: either 0 or -1 */ 793 new_message(MT_standout, " Read error on stdin"); 794 putchar('\r'); 795 quit(1); 796 } 797 if (ch == '\r' || ch == '\n') { 798 continue; 799 } 800 cptr = all_commands; 801 while (cptr->c != '\0') { 802 if (cptr->c == ch) { 803 break; 804 } 805 cptr++; 806 } 807 if (cptr->c == '\0') { 808 new_message(MT_standout, " Command not understood"); 809 putchar('\r'); 810 no_command = true; 811 } 812 if (overstrike && !cptr->available_to_dumb) 813 { 814 new_message(MT_standout, 815 " Command cannot be handled by this terminal"); 816 putchar('\r'); 817 no_command = true; 818 } 819 if (!no_command) { 820 switch(cptr->id) 821 { 822 case CMD_redraw: /* redraw screen */ 823 reset_display(); 824 break; 825 826 case CMD_update: /* merely update display */ 827 break; 828 829 case CMD_quit: 830 quit(0); 831 break; 832 833 case CMD_help: 834 reset_display(); 835 top_clear(); 836 show_help(); 837 top_standout("Hit any key to continue: "); 838 fflush(stdout); 839 read(0, &ch, 1); 840 break; 841 842 case CMD_errors: /* show errors */ 843 if (error_count() == 0) 844 { 845 new_message(MT_standout, 846 " Currently no errors to report."); 847 putchar('\r'); 848 no_command = true; 849 } 850 else 851 { 852 reset_display(); 853 top_clear(); 854 show_errors(); 855 top_standout("Hit any key to continue: "); 856 fflush(stdout); 857 read(0, &ch, 1); 858 } 859 break; 860 861 case CMD_number: 862 new_message(MT_standout, 863 "Number of processes to show: "); 864 newval = readline(tempbuf1, 8, true); 865 if (newval > -1) 866 { 867 if (newval > max_topn) 868 { 869 new_message(MT_standout | MT_delayed, 870 " This terminal can only display %d processes.", 871 max_topn); 872 putchar('\r'); 873 } 874 875 if (newval == 0) 876 { 877 /* inhibit the header */ 878 display_header(false); 879 } 880 else if (newval > topn && topn == 0) 881 { 882 /* redraw the header */ 883 display_header(true); 884 d_header = i_header; 885 } 886 topn = newval; 887 } 888 break; 889 890 case CMD_delay: /* new seconds delay */ 891 new_message(MT_standout, "Seconds to delay: "); 892 if ((i = readline(tempbuf1, 8, false)) > 0) 893 { 894 double delay_d = strtod(tempbuf1, &nptr); 895 if (nptr == tempbuf1 || delay_d <= 0) 896 { 897 new_message(MT_standout, " Invalid delay"); 898 putchar('\r'); 899 no_command = true; 900 } 901 else 902 { 903 delay.tv_sec = delay_d; 904 delay.tv_usec = (delay_d - delay.tv_sec) * 1e6; 905 clear_message(); 906 } 907 } 908 break; 909 910 case CMD_displays: /* change display count */ 911 new_message(MT_standout, 912 "Displays to show (currently %s): ", 913 displays == -1 ? "infinite" : 914 itoa(displays)); 915 if ((i = readline(tempbuf1, 10, true)) > 0) 916 { 917 displays = i; 918 } 919 else if (i == 0) 920 { 921 quit(0); 922 } 923 clear_message(); 924 break; 925 926 case CMD_kill: /* kill program */ 927 new_message(0, "kill "); 928 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 929 { 930 if ((errmsg = kill_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_renice: /* renice program */ 944 new_message(0, "renice "); 945 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 946 { 947 if ((errmsg = renice_procs(tempbuf2)) != NULL) 948 { 949 new_message(MT_standout, "%s", errmsg); 950 putchar('\r'); 951 no_command = true; 952 } 953 } 954 else 955 { 956 clear_message(); 957 } 958 break; 959 960 case CMD_idletog: 961 ps.idle = !ps.idle; 962 new_message(MT_standout | MT_delayed, 963 " %sisplaying idle processes.", 964 ps.idle ? "D" : "Not d"); 965 putchar('\r'); 966 break; 967 968 case CMD_selftog: 969 ps.self = !ps.self; 970 new_message(MT_standout | MT_delayed, 971 " %sisplaying self.", 972 (ps.self) ? "D" : "Not d"); 973 putchar('\r'); 974 break; 975 976 case CMD_user: 977 if (handle_user(tempbuf2, sizeof(tempbuf2))) 978 no_command = true; 979 break; 980 981 case CMD_thrtog: 982 ps.thread = !ps.thread; 983 new_message(MT_standout | MT_delayed, 984 " Displaying threads %s", 985 ps.thread ? "separately" : "as a count"); 986 header_text = format_header(uname_field); 987 reset_display(); 988 putchar('\r'); 989 break; 990 991 case CMD_toggletid: 992 ps.thread_id = !ps.thread_id; 993 new_message(MT_standout | MT_delayed, 994 " Displaying %s", 995 ps.thread_id ? "tid" : "pid"); 996 header_text = format_header(uname_field); 997 reset_display(); 998 putchar('\r'); 999 break; 1000 1001 case CMD_wcputog: 1002 ps.wcpu = !ps.wcpu; 1003 new_message(MT_standout | MT_delayed, 1004 " Displaying %s CPU", 1005 ps.wcpu ? "weighted" : "raw"); 1006 header_text = format_header(uname_field); 1007 reset_display(); 1008 putchar('\r'); 1009 break; 1010 case CMD_viewtog: 1011 displaymode = displaymode == DISP_IO ? DISP_CPU : DISP_IO; 1012 new_message(MT_standout | MT_delayed, 1013 " Displaying %s statistics.", 1014 displaymode == DISP_IO ? "IO" : "CPU"); 1015 header_text = format_header(uname_field); 1016 display_header(true); 1017 d_header = i_header; 1018 reset_display(); 1019 break; 1020 case CMD_viewsys: 1021 ps.system = !ps.system; 1022 new_message(MT_standout | MT_delayed, 1023 " %sisplaying system processes.", 1024 ps.system ? "D" : "Not d"); 1025 break; 1026 case CMD_showargs: 1027 fmt_flags ^= FMT_SHOWARGS; 1028 new_message(MT_standout | MT_delayed, 1029 " %sisplaying process arguments.", 1030 fmt_flags & FMT_SHOWARGS ? "D" : "Not d"); 1031 break; 1032 case CMD_order: 1033 new_message(MT_standout, 1034 "Order to sort: "); 1035 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 1036 { 1037 if ((i = string_index(tempbuf2, statics.order_names)) == -1) 1038 { 1039 new_message(MT_standout, 1040 " %s: unrecognized sorting order", tempbuf2); 1041 no_command = true; 1042 } 1043 else 1044 { 1045 order_index = i; 1046 } 1047 putchar('\r'); 1048 } 1049 else 1050 { 1051 clear_message(); 1052 } 1053 break; 1054 case CMD_jidtog: 1055 ps.jail = !ps.jail; 1056 new_message(MT_standout | MT_delayed, 1057 " %sisplaying jail ID.", 1058 ps.jail ? "D" : "Not d"); 1059 header_text = format_header(uname_field); 1060 reset_display(); 1061 putchar('\r'); 1062 break; 1063 1064 case CMD_jail: 1065 new_message(MT_standout, 1066 "Jail to show (+ for all): "); 1067 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) 1068 { 1069 if (tempbuf2[0] == '+' && 1070 tempbuf2[1] == '\0') 1071 { 1072 ps.jid = -1; 1073 } 1074 else if ((i = jail_getid(tempbuf2)) == -1) 1075 { 1076 new_message(MT_standout, 1077 " %s: unknown jail", tempbuf2); 1078 no_command = true; 1079 } 1080 else 1081 { 1082 ps.jid = i; 1083 } 1084 if (ps.jail == 0) { 1085 ps.jail = 1; 1086 new_message(MT_standout | 1087 MT_delayed, " Displaying jail " 1088 "ID."); 1089 header_text = 1090 format_header(uname_field); 1091 reset_display(); 1092 } 1093 putchar('\r'); 1094 } 1095 else 1096 { 1097 clear_message(); 1098 } 1099 break; 1100 1101 case CMD_kidletog: 1102 ps.kidle = !ps.kidle; 1103 new_message(MT_standout | MT_delayed, 1104 " %sisplaying system idle process.", 1105 ps.kidle ? "D" : "Not d"); 1106 putchar('\r'); 1107 break; 1108 case CMD_pcputog: 1109 pcpu_stats = !pcpu_stats; 1110 new_message(MT_standout | MT_delayed, 1111 " Displaying %sCPU statistics.", 1112 pcpu_stats ? "per-" : "global "); 1113 toggle_pcpustats(); 1114 max_topn = display_updatecpus(&statics); 1115 reset_display(); 1116 putchar('\r'); 1117 break; 1118 case CMD_swaptog: 1119 ps.swap = !ps.swap; 1120 new_message(MT_standout | MT_delayed, 1121 " %sisplaying per-process swap usage.", 1122 ps.swap ? "D" : "Not d"); 1123 header_text = format_header(uname_field); 1124 reset_display(); 1125 putchar('\r'); 1126 break; 1127 case CMD_pid: 1128 new_message(MT_standout, 1129 "Process id to show (+ for all): "); 1130 if (readline(tempbuf2, sizeof(tempbuf2), false) > 0) { 1131 if (tempbuf2[0] == '+' && 1132 tempbuf2[1] == '\0') { 1133 ps.pid = (pid_t)-1; 1134 } else { 1135 unsigned long long num; 1136 const char *errstr; 1137 1138 num = strtonum(tempbuf2, 0, INT_MAX, 1139 &errstr); 1140 if (errstr != NULL || !find_pid(num)) { 1141 new_message(MT_standout, 1142 " %s: unknown pid", 1143 tempbuf2); 1144 no_command = true; 1145 } else { 1146 ps.pid = (pid_t)num; 1147 } 1148 } 1149 putchar('\r'); 1150 } else 1151 clear_message(); 1152 break; 1153 case CMD_NONE: 1154 assert(false && "reached switch without command"); 1155 } 1156 } 1157 } 1158 1159 /* flush out stuff that may have been written */ 1160 fflush(stdout); 1161 } 1162 } 1163 } 1164 1165 #ifdef DEBUG 1166 fclose(debug); 1167 #endif 1168 quit(0); 1169 } 1170 1171 /* 1172 * reset_display() - reset all the display routine pointers so that entire 1173 * screen will get redrawn. 1174 */ 1175 1176 static void 1177 reset_display(void) 1178 { 1179 d_loadave = i_loadave; 1180 d_procstates = i_procstates; 1181 d_cpustates = i_cpustates; 1182 d_memory = i_memory; 1183 d_arc = i_arc; 1184 d_carc = i_carc; 1185 d_swap = i_swap; 1186 d_message = i_message; 1187 d_header = i_header; 1188 d_process = i_process; 1189 } 1190 1191 /* 1192 * signal handlers 1193 */ 1194 1195 static sigret_t 1196 leave(int i __unused) /* exit under normal conditions -- INT handler */ 1197 { 1198 1199 leaveflag = 1; 1200 } 1201 1202 static sigret_t 1203 tstop(int i __unused) /* SIGTSTP handler */ 1204 { 1205 1206 tstopflag = 1; 1207 } 1208 1209 static sigret_t 1210 top_winch(int i __unused) /* SIGWINCH handler */ 1211 { 1212 1213 winchflag = 1; 1214 } 1215 1216 void __dead2 1217 quit(int status) /* exit under duress */ 1218 { 1219 end_screen(); 1220 exit(status); 1221 } 1222