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