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 ERR_RESET; 481 uid = getuid(); 482 483 /* allow for negative priority values */ 484 if ((negate = (*str == '-')) != 0) 485 { 486 /* move past the minus sign */ 487 str++; 488 } 489 490 /* use procnum as a temporary holding place and get the number */ 491 procnum = scanint(str, &prio); 492 493 /* negate if necessary */ 494 if (negate) 495 { 496 prio = -prio; 497 } 498 499 #if defined(PRIO_MIN) && defined(PRIO_MAX) 500 /* check for validity */ 501 if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX) 502 { 503 message_error(" renice: bad priority value"); 504 return; 505 } 506 #endif 507 508 /* move to the first process number */ 509 if ((str = next_field(str)) == NULL) 510 { 511 message_error(" remice: no processes specified"); 512 return; 513 } 514 515 #ifdef HAVE_SETPRIORITY 516 /* loop thru the process numbers, renicing each one */ 517 do 518 { 519 if (scanint(str, &procnum) == -1) 520 { 521 ERROR(str, 0); 522 } 523 524 /* check process owner if we're not root */ 525 else if (uid && (uid != proc_owner(procnum))) 526 { 527 ERROR(str, EACCES); 528 } 529 else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) 530 { 531 ERROR(str, errno); 532 } 533 } while ((str = next_field(str)) != NULL); 534 err_string(); 535 #else 536 message_error(" renice operation not supported"); 537 #endif 538 } 539 540 /* COMMAND ROUTINES */ 541 542 /* 543 * Each command routine is called by command_process and is passed a 544 * pointer to the current global state. Command routines are free 545 * to change anything in the global state, although changes to the 546 * statics structure are discouraged. Whatever a command routine 547 * returns will be returned by command_process. 548 */ 549 550 static void 551 cmd_quit(globalstate *gstate) 552 553 { 554 quit(EX_OK); 555 /*NOTREACHED*/ 556 } 557 558 static int 559 cmd_update(globalstate *gstate) 560 561 { 562 /* go home for visual feedback */ 563 screen_home(); 564 fflush(stdout); 565 message_expire(); 566 return CMD_REFRESH; 567 } 568 569 static int 570 cmd_redraw(globalstate *gstate) 571 572 { 573 gstate->fulldraw = Yes; 574 return CMD_REFRESH; 575 } 576 577 static int 578 cmd_color(globalstate *gstate) 579 580 { 581 gstate->use_color = color_activate(-1); 582 gstate->fulldraw = Yes; 583 return CMD_REFRESH; 584 } 585 586 static int 587 cmd_number(globalstate *gstate) 588 589 { 590 int newval; 591 char tmpbuf[20]; 592 593 message_prompt("Number of processes to show: "); 594 newval = readline(tmpbuf, 8, Yes); 595 if (newval > -1) 596 { 597 if (newval > gstate->max_topn) 598 { 599 message_error(" This terminal can only display %d processes", 600 gstate->max_topn); 601 } 602 603 if (newval == 0) 604 { 605 /* inhibit the header */ 606 display_header(No); 607 } 608 609 else if (gstate->topn == 0) 610 { 611 display_header(Yes); 612 } 613 614 gstate->topn = newval; 615 } 616 return CMD_REFRESH; 617 } 618 619 static int 620 cmd_delay(globalstate *gstate) 621 622 { 623 double newval; 624 char tmpbuf[20]; 625 626 message_prompt("Seconds to delay: "); 627 if (readline(tmpbuf, 8, No) > 0) 628 { 629 newval = atof(tmpbuf); 630 if (newval == 0 && getuid() != 0) 631 { 632 gstate->delay = 1; 633 } 634 else 635 { 636 gstate->delay = newval; 637 } 638 } 639 return CMD_REFRESH; 640 } 641 642 static int 643 cmd_idle(globalstate *gstate) 644 645 { 646 gstate->pselect.idle = !gstate->pselect.idle; 647 message_error(" %sisplaying idle processes.", 648 gstate->pselect.idle ? "D" : "Not d"); 649 return CMD_REFRESH; 650 } 651 652 static int 653 cmd_displays(globalstate *gstate) 654 655 { 656 int i; 657 char tmpbuf[20]; 658 659 message_prompt("Displays to show (currently %s): ", 660 gstate->displays == -1 ? "infinite" : 661 itoa(gstate->displays)); 662 663 if ((i = readline(tmpbuf, 10, Yes)) > 0) 664 { 665 gstate->displays = i; 666 } 667 else if (i == 0) 668 { 669 quit(EX_OK); 670 /*NOTREACHED*/ 671 } 672 return CMD_OK; 673 } 674 675 static int 676 cmd_cmdline(globalstate *gstate) 677 678 { 679 if (gstate->statics->flags.fullcmds) 680 { 681 gstate->pselect.fullcmd = !gstate->pselect.fullcmd; 682 message_error(" %sisplaying full command lines.", 683 gstate->pselect.fullcmd ? "D" : "Not d"); 684 return CMD_REFRESH; 685 } 686 message_error(" Full command display not supported."); 687 return CMD_OK; 688 } 689 690 static int 691 cmd_order(globalstate *gstate) 692 693 { 694 char tmpbuf[MAX_COLS]; 695 int i; 696 697 if (gstate->statics->order_names != NULL) 698 { 699 message_prompt("Column to sort: "); 700 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 701 { 702 if ((i = string_index(tmpbuf, gstate->statics->order_names)) == -1) 703 { 704 message_error(" Sort order \"%s\" not recognized", tmpbuf); 705 } 706 else 707 { 708 gstate->order_index = i; 709 return CMD_REFRESH; 710 } 711 } 712 } 713 return CMD_OK; 714 } 715 716 static int 717 cmd_order_x(globalstate *gstate, const char *name, ...) 718 719 { 720 va_list ap; 721 char *p; 722 const char **names; 723 int i; 724 725 names = gstate->statics->order_names; 726 if (names != NULL) 727 { 728 if ((i = string_index(name, names)) == -1) 729 { 730 /* check the alternate list */ 731 va_start(ap, name); 732 p = va_arg(ap, char *); 733 while (p != NULL) 734 { 735 if ((i = string_index(p, names)) != -1) 736 { 737 gstate->order_index = i; 738 return CMD_REFRESH; 739 } 740 p = va_arg(ap, char *); 741 } 742 message_error(" Sort order not recognized"); 743 } 744 else 745 { 746 gstate->order_index = i; 747 return CMD_REFRESH; 748 } 749 } 750 return CMD_OK; 751 } 752 753 static int 754 cmd_order_cpu(globalstate *gstate) 755 756 { 757 return cmd_order_x(gstate, "cpu", NULL); 758 } 759 760 static int 761 cmd_order_pid(globalstate *gstate) 762 763 { 764 return cmd_order_x(gstate, "pid", NULL); 765 } 766 767 static int 768 cmd_order_mem(globalstate *gstate) 769 770 { 771 return cmd_order_x(gstate, "mem", "size", NULL); 772 } 773 774 static int 775 cmd_order_time(globalstate *gstate) 776 777 { 778 return cmd_order_x(gstate, "time"); 779 } 780 781 #ifdef ENABLE_KILL 782 783 static int 784 cmd_kill(globalstate *gstate) 785 786 { 787 char tmpbuf[MAX_COLS]; 788 789 message_prompt_plain("kill "); 790 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 791 { 792 kill_procs(tmpbuf); 793 } 794 return CMD_OK; 795 } 796 797 static int 798 cmd_renice(globalstate *gstate) 799 800 { 801 char tmpbuf[MAX_COLS]; 802 803 message_prompt_plain("renice "); 804 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 805 { 806 renice_procs(tmpbuf); 807 } 808 return CMD_OK; 809 } 810 811 #endif 812 813 static int 814 cmd_pid(globalstate *gstate) 815 816 { 817 char tmpbuf[MAX_COLS]; 818 819 message_prompt_plain("select pid "); 820 gstate->pselect.pid = -1; 821 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 822 { 823 int pid; 824 if (scanint(tmpbuf, &pid) == 0) 825 gstate->pselect.pid = pid; 826 } 827 return CMD_OK; 828 } 829 830 static int 831 cmd_user(globalstate *gstate) 832 833 { 834 char linebuf[MAX_COLS]; 835 int i; 836 int ret = CMD_OK; 837 838 message_prompt("Username to show: "); 839 if (readline(linebuf, sizeof(linebuf), No) > 0) 840 { 841 if (linebuf[0] == '+' && 842 linebuf[1] == '\0') 843 { 844 gstate->pselect.uid = -1; 845 ret = CMD_REFRESH; 846 } 847 else if ((i = userid(linebuf)) == -1) 848 { 849 message_error(" %s: unknown user", linebuf); 850 } 851 else 852 { 853 gstate->pselect.uid = i; 854 ret = CMD_REFRESH; 855 } 856 } 857 return ret; 858 } 859 860 static int 861 cmd_command(globalstate *gstate) 862 863 { 864 char linebuf[MAX_COLS]; 865 866 if (gstate->pselect.command != NULL) 867 { 868 free(gstate->pselect.command); 869 gstate->pselect.command = NULL; 870 } 871 872 message_prompt("Command to show: "); 873 if (readline(linebuf, sizeof(linebuf), No) > 0) 874 { 875 if (linebuf[0] != '\0') 876 { 877 gstate->pselect.command = estrdup(linebuf); 878 } 879 } 880 return CMD_REFRESH; 881 } 882 883 static int 884 cmd_useruid(globalstate *gstate) 885 886 { 887 gstate->pselect.usernames = !gstate->pselect.usernames; 888 display_header(2); 889 return CMD_REFRESH; 890 } 891 892 static int 893 cmd_mode(globalstate *gstate) 894 895 { 896 if (gstate->statics->modemax <= 1) 897 { 898 return CMD_NA; 899 } 900 gstate->pselect.mode = (gstate->pselect.mode + 1) % gstate->statics->modemax; 901 display_header(2); 902 return CMD_REFRESH; 903 } 904 905 static int 906 cmd_system(globalstate *gstate) 907 908 { 909 gstate->pselect.system = !gstate->pselect.system; 910 display_header(2); 911 return CMD_REFRESH; 912 } 913 914 static int 915 cmd_threads(globalstate *gstate) 916 917 { 918 if (gstate->statics->flags.threads) 919 { 920 gstate->pselect.threads = !gstate->pselect.threads; 921 display_header(2); 922 return CMD_REFRESH; 923 } 924 return CMD_NA; 925 } 926 927 static int 928 cmd_percpustates(globalstate *gstate) 929 { 930 gstate->percpustates = !gstate->percpustates; 931 gstate->fulldraw = Yes; 932 gstate->max_topn += display_setmulti(gstate->percpustates); 933 return CMD_REFRESH; 934 } 935 936 937 /* forward reference for cmd_help, as it needs to see the command_table */ 938 int cmd_help(globalstate *gstate); 939 940 /* command table */ 941 command command_table[] = { 942 { '\014', cmd_redraw, "redraw screen" }, 943 { ' ', cmd_update, "update screen" }, 944 { '?', cmd_help, "help; show this text" }, 945 { 'h', cmd_help, NULL }, 946 { '1', cmd_percpustates, "toggle the detail per cpu of cpustates" }, 947 { 'C', cmd_color, "toggle the use of color" }, 948 { 'H', cmd_threads, "toggle the display of individual threads" }, 949 { 't', cmd_threads, NULL }, 950 { 'M', cmd_order_mem, "sort by memory usage" }, 951 { 'N', cmd_order_pid, "sort by process id" }, 952 { 'P', cmd_order_cpu, "sort by CPU usage" }, 953 { 'S', cmd_system, "toggle the display of system processes" }, 954 { 'T', cmd_order_time, "sort by CPU time" }, 955 { 'U', cmd_useruid, "toggle the display of usernames or uids" }, 956 { 'c', cmd_command, "display processes by command name" }, 957 { 'd', cmd_displays, "change number of displays to show" }, 958 { 'f', cmd_cmdline, "toggle the display of full command paths" }, 959 { 'i', cmd_idle, "toggle the displaying of idle processes" }, 960 { 'I', cmd_idle, NULL }, 961 #ifdef ENABLE_KILL 962 { 'k', cmd_kill, "kill processes; send a signal to a list of processes" }, 963 #endif 964 { 'm', cmd_mode, "toggle between display modes" }, 965 { 'n', cmd_number, "change number of processes to display" }, 966 { '#', cmd_number, NULL }, 967 { 'o', cmd_order, "specify sort order (see below)" }, 968 { 'p', cmd_pid, "select a single pid" }, 969 { 'q', (int (*)(globalstate *))cmd_quit, "quit" }, 970 #ifdef ENABLE_KILL 971 { 'r', cmd_renice, "renice a process" }, 972 #endif 973 { 's', cmd_delay, "change number of seconds to delay between updates" }, 974 { 'u', cmd_user, "display processes for only one user (+ selects all users)" }, 975 { '\0', NULL, NULL }, 976 }; 977 978 int 979 cmd_help(globalstate *gstate) 980 981 { 982 command *c; 983 char buf[12]; 984 char *p; 985 const char *help; 986 987 display_pagerstart(); 988 989 display_pager("Top version %s, %s\n", version_string(), copyright); 990 display_pager("Platform module: %s\n\n", MODULE); 991 display_pager("A top users display for Unix\n\n"); 992 display_pager("These single-character commands are available:\n\n"); 993 994 c = command_table; 995 while (c->cmd_func != NULL) 996 { 997 /* skip null help strings */ 998 if ((help = c->help) == NULL) 999 { 1000 continue; 1001 } 1002 1003 /* translate character in to something readable */ 1004 if (c->ch < ' ') 1005 { 1006 buf[0] = '^'; 1007 buf[1] = c->ch + '@'; 1008 buf[2] = '\0'; 1009 } 1010 else if (c->ch == ' ') 1011 { 1012 strcpy(buf, "<sp>"); 1013 } 1014 else 1015 { 1016 buf[0] = c->ch; 1017 buf[1] = '\0'; 1018 } 1019 1020 /* if the next command is the same, fold them onto one line */ 1021 if ((c+1)->cmd_func == c->cmd_func) 1022 { 1023 strcat(buf, " or "); 1024 p = buf + strlen(buf); 1025 *p++ = (c+1)->ch; 1026 *p = '\0'; 1027 c++; 1028 } 1029 1030 display_pager("%-7s - %s\n", buf, help); 1031 c++; 1032 } 1033 1034 display_pager("\nNot all commands are available on all systems.\n\n"); 1035 display_pager("Available sort orders: %s\n", gstate->order_namelist); 1036 display_pagerend(); 1037 gstate->fulldraw = Yes; 1038 return CMD_REFRESH; 1039 } 1040 1041 /* 1042 * int command_process(globalstate *gstate, int cmd) 1043 * 1044 * Process the single-character command "cmd". The global state may 1045 * be modified by the command to alter the output. Returns CMD_ERROR 1046 * if there was a serious error that requires an immediate exit, CMD_OK 1047 * to indicate success, CMD_REFRESH to indicate that the screen needs 1048 * to be refreshed immediately, CMD_UNKNOWN when the command is not known, 1049 * and CMD_NA when the command is not available. Error messages for 1050 * CMD_NA and CMD_UNKNOWN must be handled by the caller. 1051 */ 1052 1053 int 1054 command_process(globalstate *gstate, int cmd) 1055 1056 { 1057 command *c; 1058 1059 c = command_table; 1060 while (c->cmd_func != NULL) 1061 { 1062 if (c->ch == cmd) 1063 { 1064 return (c->cmd_func)(gstate); 1065 } 1066 c++; 1067 } 1068 1069 return CMD_UNKNOWN; 1070 } 1071