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