1 /* 2 * Copyright (c) 1984 through 2008, William LeFebvre 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * * Neither the name of William LeFebvre nor the names of other 17 * contributors may be used to endorse or promote products derived from 18 * this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Top users/processes display for Unix 35 * Version 3 36 */ 37 38 /* 39 * This file contains the routines that implement some of the interactive 40 * mode commands. Note that some of the commands are implemented in-line 41 * in "main". This is necessary because they change the global state of 42 * "top" (i.e.: changing the number of processes to display). 43 */ 44 45 #include "os.h" 46 #include <ctype.h> 47 #include <signal.h> 48 #include <stdarg.h> 49 #include <unistd.h> 50 #include <color.h> 51 #include <errno.h> 52 #ifdef HAVE_SYS_RESOURCE_H 53 #include <sys/resource.h> 54 #endif 55 56 #if defined(HAVE_DECL_SYS_SIGLIST) & defined(HAVE_STRCASECMP) 57 #define USE_SYS_SIGLIST 58 #endif 59 60 #ifdef USE_SYS_SIGLIST 61 extern const char * const sys_siglist[]; 62 extern const char * const sys_signame[]; 63 #else 64 #include "sigdesc.h" /* generated automatically */ 65 #endif 66 #include "top.h" 67 #include "machine.h" 68 #include "globalstate.h" 69 #include "boolean.h" 70 #include "color.h" 71 #include "commands.h" 72 #include "display.h" 73 #include "screen.h" 74 #include "username.h" 75 #include "utils.h" 76 #include "version.h" 77 78 extern char *copyright; 79 80 typedef struct command { 81 int ch; 82 int (*cmd_func)(globalstate *); 83 const char *help; 84 } command; 85 86 /* 87 * Some of the commands make system calls that could generate errors. 88 * These errors are collected up in an array of structures for later 89 * contemplation and display. Such routines return a string containing an 90 * error message, or NULL if no errors occurred. We need an upper limit on 91 * the number of errors, so we arbitrarily choose 20. 92 */ 93 94 #define ERRMAX 20 95 96 struct errs /* structure for a system-call error */ 97 { 98 int errnum; /* value of errno (that is, the actual error) */ 99 char *arg; /* argument that caused the error */ 100 }; 101 102 static struct errs errs[ERRMAX]; 103 static int errcnt; 104 105 /* These macros get used to reset and log the errors */ 106 #define ERR_RESET errcnt = 0 107 #define ERROR(p, e) if (errcnt < ERRMAX) \ 108 { \ 109 errs[errcnt].arg = (p); \ 110 errs[errcnt++].errnum = (e); \ 111 } 112 113 /* 114 * err_compar(p1, p2) - comparison routine used by "qsort" 115 * for sorting errors. 116 */ 117 118 static int 119 err_compar(const void *p1, const void *p2) 120 121 { 122 register int result; 123 124 if ((result = ((const struct errs *)p1)->errnum - 125 ((const struct errs *)p2)->errnum) == 0) 126 { 127 return(strcmp(((const struct errs *)p1)->arg, 128 ((const struct errs *)p2)->arg)); 129 } 130 return(result); 131 } 132 133 /* 134 * str_adderr(str, len, err) - add an explanation of error "err" to 135 * the string "str" without overflowing length "len". return 136 * number of characters remaining in str, or 0 if overflowed. 137 */ 138 139 static int 140 str_adderr(char *str, int len, int err) 141 142 { 143 register const char *msg; 144 register int msglen; 145 146 msg = err == 0 ? "Not a number" : errmsg(err); 147 msglen = strlen(msg) + 2; 148 if (len <= msglen) 149 { 150 return(0); 151 } 152 (void) strcat(str, ": "); 153 (void) strcat(str, msg); 154 return(len - msglen); 155 } 156 157 /* 158 * str_addarg(str, len, arg, first) - add the string argument "arg" to 159 * the string "str" without overflowing length "len". This is the 160 * first in the group when "first" is set (indicating that a comma 161 * should NOT be added to the front). Return number of characters 162 * remaining in str, or 0 if overflowed. 163 */ 164 165 static int 166 str_addarg(char *str, int len, char *arg, int first) 167 168 { 169 register int arglen; 170 171 arglen = strlen(arg); 172 if (!first) 173 { 174 arglen += 2; 175 } 176 if (len <= arglen) 177 { 178 return(0); 179 } 180 if (!first) 181 { 182 (void) strcat(str, ", "); 183 } 184 (void) strcat(str, arg); 185 return(len - arglen); 186 } 187 188 /* 189 * void err_string() 190 * 191 * Use message_error to log errors in the errs array. This function 192 * will combine identical errors to make the message short, but if 193 * there is more than one type of error it will call message_error 194 * for each one. 195 */ 196 197 #define STRMAX 80 198 199 static void 200 err_string(void) 201 202 { 203 register struct errs *errp; 204 register int cnt = 0; 205 register int first = Yes; 206 register int currerr = -1; 207 int stringlen = 0; /* characters still available in "string" */ 208 char string[STRMAX]; 209 210 /* if there are no errors, our job is easy */ 211 if (errcnt == 0) 212 { 213 return; 214 } 215 216 /* sort the errors */ 217 qsort((char *)errs, errcnt, sizeof(struct errs), err_compar); 218 219 /* initialize the buffer (probably not necessary) */ 220 string[0] = '\0'; 221 stringlen = STRMAX - 1; 222 223 /* loop thru the sorted list, logging errors */ 224 while (cnt < errcnt) 225 { 226 /* point to the current error */ 227 errp = &(errs[cnt++]); 228 229 /* note that on overflow "stringlen" will become 0 and all 230 subsequent calls to str_addarg or str_adderr will return 0 */ 231 232 /* if the error number is different then add the error string */ 233 if (errp->errnum != currerr) 234 { 235 if (currerr != -1) 236 { 237 /* add error string and log the error */ 238 stringlen = str_adderr(string, stringlen, currerr); 239 message_error(" %s", string); 240 241 } 242 /* reset the buffer */ 243 string[0] = '\0'; 244 stringlen = STRMAX - 1; 245 246 /* move to next error num */ 247 currerr = errp->errnum; 248 first = Yes; 249 } 250 251 /* add this arg */ 252 stringlen = str_addarg(string, stringlen, errp->arg, first); 253 first = No; 254 } 255 256 /* add final message */ 257 stringlen = str_adderr(string, stringlen, currerr); 258 259 /* write the error string */ 260 message_error(" %s", string); 261 } 262 263 /* 264 * Utility routines that help with some of the commands. 265 */ 266 267 static char * 268 next_field(char *str) 269 270 271 { 272 if ((str = strchr(str, ' ')) == NULL) 273 { 274 return(NULL); 275 } 276 *str = '\0'; 277 while (*++str == ' ') /* loop */; 278 279 /* if there is nothing left of the string, return NULL */ 280 /* This fix is dedicated to Greg Earle */ 281 return(*str == '\0' ? NULL : str); 282 } 283 284 static int 285 scanint(char *str, int *intp) 286 287 { 288 register int val = 0; 289 register int ch; 290 291 /* if there is nothing left of the string, flag it as an error */ 292 /* This fix is dedicated to Greg Earle */ 293 if (*str == '\0') 294 { 295 return(-1); 296 } 297 298 while ((ch = *str++) != '\0') 299 { 300 if (isdigit(ch)) 301 { 302 val = val * 10 + (ch - '0'); 303 } 304 else if (isspace(ch)) 305 { 306 break; 307 } 308 else 309 { 310 return(-1); 311 } 312 } 313 *intp = val; 314 return(0); 315 } 316 317 #ifdef notdef 318 /* 319 * error_count() - return the number of errors currently logged. 320 */ 321 322 static int 323 error_count(void) 324 325 { 326 return(errcnt); 327 } 328 329 /* 330 * show_errors() - display on stdout the current log of errors. 331 */ 332 333 static void 334 show_errors(void) 335 336 { 337 register int cnt = 0; 338 register struct errs *errp = errs; 339 340 printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); 341 while (cnt++ < errcnt) 342 { 343 printf("%5s: %s\n", errp->arg, 344 errp->errnum == 0 ? "Not a number" : errmsg(errp->errnum)); 345 errp++; 346 } 347 } 348 #endif 349 350 /* 351 * kill_procs(str) - send signals to processes, much like the "kill" 352 * command does; invoked in response to 'k'. 353 */ 354 355 static void 356 kill_procs(char *str) 357 358 { 359 register char *nptr; 360 int signum = SIGTERM; /* default */ 361 int procnum; 362 int uid; 363 int owner; 364 #ifndef USE_SYS_SIGLIST 365 struct sigdesc *sigp; 366 #endif 367 368 /* reset error array */ 369 ERR_RESET; 370 371 /* remember our uid */ 372 uid = getuid(); 373 374 /* skip over leading white space */ 375 while (isspace((int)*str)) str++; 376 377 if (str[0] == '-') 378 { 379 /* explicit signal specified */ 380 if ((nptr = next_field(str)) == NULL) 381 { 382 message_error(" kill: no processes specified"); 383 return; 384 } 385 386 str++; 387 if (isdigit((int)str[0])) 388 { 389 (void) scanint(str, &signum); 390 if (signum <= 0 || signum >= NSIG) 391 { 392 message_error(" kill: invalid signal number"); 393 return; 394 } 395 } 396 else 397 { 398 /* translate the name into a number */ 399 #ifdef USE_SYS_SIGLIST 400 for (signum = 1; signum < NSIG; signum++) 401 { 402 if (strcasecmp(sys_signame[signum], str) == 0) 403 { 404 break; 405 } 406 } 407 if (signum == NSIG) 408 { 409 message_error(" kill: bad signal name"); 410 return; 411 } 412 #else 413 for (sigp = sigdesc; sigp->name != NULL; sigp++) 414 { 415 #ifdef HAVE_STRCASECMP 416 if (strcasecmp(sigp->name, str) == 0) 417 #else 418 if (strcmp(sigp->name, str) == 0) 419 #endif 420 { 421 signum = sigp->number; 422 break; 423 } 424 } 425 426 /* was it ever found */ 427 if (sigp->name == NULL) 428 { 429 message_error(" kill: bad signal name"); 430 return; 431 } 432 #endif 433 } 434 /* put the new pointer in place */ 435 str = nptr; 436 } 437 438 /* loop thru the string, killing processes */ 439 do 440 { 441 if (scanint(str, &procnum) == -1) 442 { 443 ERROR(str, 0); 444 } 445 else 446 { 447 /* check process owner if we're not root */ 448 owner = proc_owner(procnum); 449 if (uid && (uid != owner)) 450 { 451 ERROR(str, owner == -1 ? ESRCH : EACCES); 452 } 453 /* go in for the kill */ 454 else if (kill(procnum, signum) == -1) 455 { 456 /* chalk up an error */ 457 ERROR(str, errno); 458 } 459 } 460 } while ((str = next_field(str)) != NULL); 461 462 /* process errors */ 463 err_string(); 464 } 465 466 /* 467 * renice_procs(str) - change the "nice" of processes, much like the 468 * "renice" command does; invoked in response to 'r'. 469 */ 470 471 static void 472 renice_procs(char *str) 473 474 { 475 register char negate; 476 int prio; 477 int procnum; 478 int uid; 479 480 #if defined(__minix) 481 /* LSC: -Werror=maybe-uninitialized while compiling with -O3. */ 482 prio = 0; 483 #endif /* defined(__minix) */ 484 ERR_RESET; 485 uid = getuid(); 486 487 /* allow for negative priority values */ 488 if ((negate = (*str == '-')) != 0) 489 { 490 /* move past the minus sign */ 491 str++; 492 } 493 494 /* use procnum as a temporary holding place and get the number */ 495 procnum = scanint(str, &prio); 496 497 /* negate if necessary */ 498 if (negate) 499 { 500 prio = -prio; 501 } 502 503 #if defined(PRIO_MIN) && defined(PRIO_MAX) 504 /* check for validity */ 505 if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX) 506 { 507 message_error(" renice: bad priority value"); 508 return; 509 } 510 #endif 511 512 /* move to the first process number */ 513 if ((str = next_field(str)) == NULL) 514 { 515 message_error(" remice: no processes specified"); 516 return; 517 } 518 519 #ifdef HAVE_SETPRIORITY 520 /* loop thru the process numbers, renicing each one */ 521 do 522 { 523 if (scanint(str, &procnum) == -1) 524 { 525 ERROR(str, 0); 526 } 527 528 /* check process owner if we're not root */ 529 else if (uid && (uid != proc_owner(procnum))) 530 { 531 ERROR(str, EACCES); 532 } 533 else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) 534 { 535 ERROR(str, errno); 536 } 537 } while ((str = next_field(str)) != NULL); 538 err_string(); 539 #else 540 message_error(" renice operation not supported"); 541 #endif 542 } 543 544 /* COMMAND ROUTINES */ 545 546 /* 547 * Each command routine is called by command_process and is passed a 548 * pointer to the current global state. Command routines are free 549 * to change anything in the global state, although changes to the 550 * statics structure are discouraged. Whatever a command routine 551 * returns will be returned by command_process. 552 */ 553 554 static void 555 cmd_quit(globalstate *gstate) 556 557 { 558 quit(EX_OK); 559 /*NOTREACHED*/ 560 } 561 562 static int 563 cmd_update(globalstate *gstate) 564 565 { 566 /* go home for visual feedback */ 567 screen_home(); 568 fflush(stdout); 569 message_expire(); 570 return CMD_REFRESH; 571 } 572 573 static int 574 cmd_redraw(globalstate *gstate) 575 576 { 577 gstate->fulldraw = Yes; 578 return CMD_REFRESH; 579 } 580 581 static int 582 cmd_color(globalstate *gstate) 583 584 { 585 gstate->use_color = color_activate(-1); 586 gstate->fulldraw = Yes; 587 return CMD_REFRESH; 588 } 589 590 static int 591 cmd_number(globalstate *gstate) 592 593 { 594 int newval; 595 char tmpbuf[20]; 596 597 message_prompt("Number of processes to show: "); 598 newval = readline(tmpbuf, 8, Yes); 599 if (newval > -1) 600 { 601 if (newval > gstate->max_topn) 602 { 603 message_error(" This terminal can only display %d processes", 604 gstate->max_topn); 605 } 606 607 if (newval == 0) 608 { 609 /* inhibit the header */ 610 display_header(No); 611 } 612 613 else if (gstate->topn == 0) 614 { 615 display_header(Yes); 616 } 617 618 gstate->topn = newval; 619 } 620 return CMD_REFRESH; 621 } 622 623 static int 624 cmd_delay(globalstate *gstate) 625 626 { 627 double newval; 628 char tmpbuf[20]; 629 630 message_prompt("Seconds to delay: "); 631 if (readline(tmpbuf, 8, No) > 0) 632 { 633 newval = atof(tmpbuf); 634 if (newval == 0 && getuid() != 0) 635 { 636 gstate->delay = 1; 637 } 638 else 639 { 640 gstate->delay = newval; 641 } 642 } 643 return CMD_REFRESH; 644 } 645 646 static int 647 cmd_idle(globalstate *gstate) 648 649 { 650 gstate->pselect.idle = !gstate->pselect.idle; 651 message_error(" %sisplaying idle processes.", 652 gstate->pselect.idle ? "D" : "Not d"); 653 return CMD_REFRESH; 654 } 655 656 static int 657 cmd_displays(globalstate *gstate) 658 659 { 660 int i; 661 char tmpbuf[20]; 662 663 message_prompt("Displays to show (currently %s): ", 664 gstate->displays == -1 ? "infinite" : 665 itoa(gstate->displays)); 666 667 if ((i = readline(tmpbuf, 10, Yes)) > 0) 668 { 669 gstate->displays = i; 670 } 671 else if (i == 0) 672 { 673 quit(EX_OK); 674 /*NOTREACHED*/ 675 } 676 return CMD_OK; 677 } 678 679 static int 680 cmd_cmdline(globalstate *gstate) 681 682 { 683 if (gstate->statics->flags.fullcmds) 684 { 685 gstate->pselect.fullcmd = !gstate->pselect.fullcmd; 686 message_error(" %sisplaying full command lines.", 687 gstate->pselect.fullcmd ? "D" : "Not d"); 688 return CMD_REFRESH; 689 } 690 message_error(" Full command display not supported."); 691 return CMD_OK; 692 } 693 694 static int 695 cmd_order(globalstate *gstate) 696 697 { 698 char tmpbuf[MAX_COLS]; 699 int i; 700 701 if (gstate->statics->order_names != NULL) 702 { 703 message_prompt("Column to sort: "); 704 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 705 { 706 if ((i = string_index(tmpbuf, gstate->statics->order_names)) == -1) 707 { 708 message_error(" Sort order \"%s\" not recognized", tmpbuf); 709 } 710 else 711 { 712 gstate->order_index = i; 713 return CMD_REFRESH; 714 } 715 } 716 } 717 return CMD_OK; 718 } 719 720 static int 721 cmd_order_x(globalstate *gstate, const char *name, ...) 722 723 { 724 va_list ap; 725 char *p; 726 const char **names; 727 int i; 728 729 names = gstate->statics->order_names; 730 if (names != NULL) 731 { 732 if ((i = string_index(name, names)) == -1) 733 { 734 /* check the alternate list */ 735 va_start(ap, name); 736 p = va_arg(ap, char *); 737 while (p != NULL) 738 { 739 if ((i = string_index(p, names)) != -1) 740 { 741 gstate->order_index = i; 742 return CMD_REFRESH; 743 } 744 p = va_arg(ap, char *); 745 } 746 message_error(" Sort order not recognized"); 747 } 748 else 749 { 750 gstate->order_index = i; 751 return CMD_REFRESH; 752 } 753 } 754 return CMD_OK; 755 } 756 757 static int 758 cmd_order_cpu(globalstate *gstate) 759 760 { 761 return cmd_order_x(gstate, "cpu", NULL); 762 } 763 764 static int 765 cmd_order_pid(globalstate *gstate) 766 767 { 768 return cmd_order_x(gstate, "pid", NULL); 769 } 770 771 static int 772 cmd_order_mem(globalstate *gstate) 773 774 { 775 return cmd_order_x(gstate, "mem", "size", NULL); 776 } 777 778 static int 779 cmd_order_time(globalstate *gstate) 780 781 { 782 return cmd_order_x(gstate, "time"); 783 } 784 785 #ifdef ENABLE_KILL 786 787 static int 788 cmd_kill(globalstate *gstate) 789 790 { 791 char tmpbuf[MAX_COLS]; 792 793 message_prompt_plain("kill "); 794 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 795 { 796 kill_procs(tmpbuf); 797 } 798 return CMD_OK; 799 } 800 801 static int 802 cmd_renice(globalstate *gstate) 803 804 { 805 char tmpbuf[MAX_COLS]; 806 807 message_prompt_plain("renice "); 808 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 809 { 810 renice_procs(tmpbuf); 811 } 812 return CMD_OK; 813 } 814 815 #endif 816 817 static int 818 cmd_pid(globalstate *gstate) 819 820 { 821 char tmpbuf[MAX_COLS]; 822 823 message_prompt_plain("select pid "); 824 gstate->pselect.pid = -1; 825 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 826 { 827 int pid; 828 if (scanint(tmpbuf, &pid) == 0) 829 gstate->pselect.pid = pid; 830 } 831 return CMD_OK; 832 } 833 834 static int 835 cmd_user(globalstate *gstate) 836 837 { 838 char linebuf[MAX_COLS]; 839 int i; 840 int ret = CMD_OK; 841 842 message_prompt("Username to show: "); 843 if (readline(linebuf, sizeof(linebuf), No) > 0) 844 { 845 if (linebuf[0] == '+' && 846 linebuf[1] == '\0') 847 { 848 gstate->pselect.uid = -1; 849 ret = CMD_REFRESH; 850 } 851 else if ((i = userid(linebuf)) == -1) 852 { 853 message_error(" %s: unknown user", linebuf); 854 } 855 else 856 { 857 gstate->pselect.uid = i; 858 ret = CMD_REFRESH; 859 } 860 } 861 return ret; 862 } 863 864 static int 865 cmd_command(globalstate *gstate) 866 867 { 868 char linebuf[MAX_COLS]; 869 870 if (gstate->pselect.command != NULL) 871 { 872 free(gstate->pselect.command); 873 gstate->pselect.command = NULL; 874 } 875 876 message_prompt("Command to show: "); 877 if (readline(linebuf, sizeof(linebuf), No) > 0) 878 { 879 if (linebuf[0] != '\0') 880 { 881 gstate->pselect.command = estrdup(linebuf); 882 } 883 } 884 return CMD_REFRESH; 885 } 886 887 static int 888 cmd_useruid(globalstate *gstate) 889 890 { 891 gstate->pselect.usernames = !gstate->pselect.usernames; 892 display_header(2); 893 return CMD_REFRESH; 894 } 895 896 static int 897 cmd_mode(globalstate *gstate) 898 899 { 900 if (gstate->statics->modemax <= 1) 901 { 902 return CMD_NA; 903 } 904 gstate->pselect.mode = (gstate->pselect.mode + 1) % gstate->statics->modemax; 905 display_header(2); 906 return CMD_REFRESH; 907 } 908 909 static int 910 cmd_system(globalstate *gstate) 911 912 { 913 gstate->pselect.system = !gstate->pselect.system; 914 display_header(2); 915 return CMD_REFRESH; 916 } 917 918 static int 919 cmd_threads(globalstate *gstate) 920 921 { 922 if (gstate->statics->flags.threads) 923 { 924 gstate->pselect.threads = !gstate->pselect.threads; 925 display_header(2); 926 return CMD_REFRESH; 927 } 928 return CMD_NA; 929 } 930 931 static int 932 cmd_percpustates(globalstate *gstate) 933 { 934 gstate->percpustates = !gstate->percpustates; 935 gstate->fulldraw = Yes; 936 gstate->max_topn += display_setmulti(gstate->percpustates); 937 return CMD_REFRESH; 938 } 939 940 941 /* forward reference for cmd_help, as it needs to see the command_table */ 942 int cmd_help(globalstate *gstate); 943 944 /* command table */ 945 command command_table[] = { 946 { '\014', cmd_redraw, "redraw screen" }, 947 { ' ', cmd_update, "update screen" }, 948 { '?', cmd_help, "help; show this text" }, 949 { 'h', cmd_help, NULL }, 950 { '1', cmd_percpustates, "toggle the display of cpu states per cpu" }, 951 { 'C', cmd_color, "toggle the use of color" }, 952 { 'H', cmd_threads, "toggle the display of individual threads" }, 953 { 't', cmd_threads, NULL }, 954 { 'M', cmd_order_mem, "sort by memory usage" }, 955 { 'N', cmd_order_pid, "sort by process id" }, 956 { 'P', cmd_order_cpu, "sort by CPU usage" }, 957 { 'S', cmd_system, "toggle the display of system processes" }, 958 { 'T', cmd_order_time, "sort by CPU time" }, 959 { 'U', cmd_useruid, "toggle the display of usernames or uids" }, 960 { 'c', cmd_command, "display processes by command name" }, 961 { 'd', cmd_displays, "change number of displays to show" }, 962 { 'f', cmd_cmdline, "toggle the display of full command paths" }, 963 { 'i', cmd_idle, "toggle the displaying of idle processes" }, 964 { 'I', cmd_idle, NULL }, 965 #ifdef ENABLE_KILL 966 { 'k', cmd_kill, "kill processes; send a signal to a list of processes" }, 967 #endif 968 { 'm', cmd_mode, "toggle between display modes" }, 969 { 'n', cmd_number, "change number of processes to display" }, 970 { '#', cmd_number, NULL }, 971 { 'o', cmd_order, "specify sort order (see below)" }, 972 { 'p', cmd_pid, "select a single pid" }, 973 { 'q', (int (*)(globalstate *))cmd_quit, "quit" }, 974 #ifdef ENABLE_KILL 975 { 'r', cmd_renice, "renice a process" }, 976 #endif 977 { 's', cmd_delay, "change number of seconds to delay between updates" }, 978 { 'u', cmd_user, "display processes for only one user (+ selects all users)" }, 979 { '\0', NULL, NULL }, 980 }; 981 982 int 983 cmd_help(globalstate *gstate) 984 985 { 986 command *c; 987 char buf[12]; 988 char *p; 989 const char *help; 990 991 display_pagerstart(); 992 993 display_pager("Top version %s, %s\n", version_string(), copyright); 994 display_pager("Platform module: %s\n\n", MODULE); 995 display_pager("A top users display for Unix\n\n"); 996 display_pager("These single-character commands are available:\n\n"); 997 998 c = command_table; 999 while (c->cmd_func != NULL) 1000 { 1001 /* skip null help strings */ 1002 if ((help = c->help) == NULL) 1003 { 1004 continue; 1005 } 1006 1007 /* translate character in to something readable */ 1008 if (c->ch < ' ') 1009 { 1010 buf[0] = '^'; 1011 buf[1] = c->ch + '@'; 1012 buf[2] = '\0'; 1013 } 1014 else if (c->ch == ' ') 1015 { 1016 strcpy(buf, "<sp>"); 1017 } 1018 else 1019 { 1020 buf[0] = c->ch; 1021 buf[1] = '\0'; 1022 } 1023 1024 /* if the next command is the same, fold them onto one line */ 1025 if ((c+1)->cmd_func == c->cmd_func) 1026 { 1027 strcat(buf, " or "); 1028 p = buf + strlen(buf); 1029 *p++ = (c+1)->ch; 1030 *p = '\0'; 1031 c++; 1032 } 1033 1034 display_pager("%-7s - %s\n", buf, help); 1035 c++; 1036 } 1037 1038 display_pager("\nNot all commands are available on all systems.\n\n"); 1039 display_pager("Available sort orders: %s\n", gstate->order_namelist); 1040 display_pagerend(); 1041 gstate->fulldraw = Yes; 1042 return CMD_REFRESH; 1043 } 1044 1045 /* 1046 * int command_process(globalstate *gstate, int cmd) 1047 * 1048 * Process the single-character command "cmd". The global state may 1049 * be modified by the command to alter the output. Returns CMD_ERROR 1050 * if there was a serious error that requires an immediate exit, CMD_OK 1051 * to indicate success, CMD_REFRESH to indicate that the screen needs 1052 * to be refreshed immediately, CMD_UNKNOWN when the command is not known, 1053 * and CMD_NA when the command is not available. Error messages for 1054 * CMD_NA and CMD_UNKNOWN must be handled by the caller. 1055 */ 1056 1057 int 1058 command_process(globalstate *gstate, int cmd) 1059 1060 { 1061 command *c; 1062 1063 c = command_table; 1064 while (c->cmd_func != NULL) 1065 { 1066 if (c->ch == cmd) 1067 { 1068 return (c->cmd_func)(gstate); 1069 } 1070 c++; 1071 } 1072 1073 return CMD_UNKNOWN; 1074 } 1075