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 int errno; 79 80 extern char *copyright; 81 82 typedef struct command { 83 int ch; 84 int (*cmd_func)(globalstate *); 85 char *help; 86 } command; 87 88 /* 89 * Some of the commands make system calls that could generate errors. 90 * These errors are collected up in an array of structures for later 91 * contemplation and display. Such routines return a string containing an 92 * error message, or NULL if no errors occurred. We need an upper limit on 93 * the number of errors, so we arbitrarily choose 20. 94 */ 95 96 #define ERRMAX 20 97 98 struct errs /* structure for a system-call error */ 99 { 100 int errnum; /* value of errno (that is, the actual error) */ 101 char *arg; /* argument that caused the error */ 102 }; 103 104 static struct errs errs[ERRMAX]; 105 static int errcnt; 106 107 /* These macros get used to reset and log the errors */ 108 #define ERR_RESET errcnt = 0 109 #define ERROR(p, e) if (errcnt < ERRMAX) \ 110 { \ 111 errs[errcnt].arg = (p); \ 112 errs[errcnt++].errnum = (e); \ 113 } 114 115 /* 116 * err_compar(p1, p2) - comparison routine used by "qsort" 117 * for sorting errors. 118 */ 119 120 int 121 err_compar(const void *p1, const void *p2) 122 123 { 124 register int result; 125 126 if ((result = ((struct errs *)p1)->errnum - 127 ((struct errs *)p2)->errnum) == 0) 128 { 129 return(strcmp(((struct errs *)p1)->arg, 130 ((struct errs *)p2)->arg)); 131 } 132 return(result); 133 } 134 135 /* 136 * str_adderr(str, len, err) - add an explanation of error "err" to 137 * the string "str" without overflowing length "len". return 138 * number of characters remaining in str, or 0 if overflowed. 139 */ 140 141 int 142 str_adderr(char *str, int len, int err) 143 144 { 145 register char *msg; 146 register int msglen; 147 148 msg = err == 0 ? "Not a number" : errmsg(err); 149 msglen = strlen(msg) + 2; 150 if (len <= msglen) 151 { 152 return(0); 153 } 154 (void) strcat(str, ": "); 155 (void) strcat(str, msg); 156 return(len - msglen); 157 } 158 159 /* 160 * str_addarg(str, len, arg, first) - add the string argument "arg" to 161 * the string "str" without overflowing length "len". This is the 162 * first in the group when "first" is set (indicating that a comma 163 * should NOT be added to the front). Return number of characters 164 * remaining in str, or 0 if overflowed. 165 */ 166 167 int 168 str_addarg(char *str, int len, char *arg, int first) 169 170 { 171 register int arglen; 172 173 arglen = strlen(arg); 174 if (!first) 175 { 176 arglen += 2; 177 } 178 if (len <= arglen) 179 { 180 return(0); 181 } 182 if (!first) 183 { 184 (void) strcat(str, ", "); 185 } 186 (void) strcat(str, arg); 187 return(len - arglen); 188 } 189 190 /* 191 * void err_string() 192 * 193 * Use message_error to log errors in the errs array. This function 194 * will combine identical errors to make the message short, but if 195 * there is more than one type of error it will call message_error 196 * for each one. 197 */ 198 199 #define STRMAX 80 200 201 void 202 err_string() 203 204 { 205 register struct errs *errp; 206 register int cnt = 0; 207 register int first = Yes; 208 register int currerr = -1; 209 int stringlen = 0; /* characters still available in "string" */ 210 char string[STRMAX]; 211 212 /* if there are no errors, our job is easy */ 213 if (errcnt == 0) 214 { 215 return; 216 } 217 218 /* sort the errors */ 219 qsort((char *)errs, errcnt, sizeof(struct errs), err_compar); 220 221 /* initialize the buffer (probably not necessary) */ 222 string[0] = '\0'; 223 stringlen = STRMAX - 1; 224 225 /* loop thru the sorted list, logging errors */ 226 while (cnt < errcnt) 227 { 228 /* point to the current error */ 229 errp = &(errs[cnt++]); 230 231 /* note that on overflow "stringlen" will become 0 and all 232 subsequent calls to str_addarg or str_adderr will return 0 */ 233 234 /* if the error number is different then add the error string */ 235 if (errp->errnum != currerr) 236 { 237 if (currerr != -1) 238 { 239 /* add error string and log the error */ 240 stringlen = str_adderr(string, stringlen, currerr); 241 message_error(" %s", string); 242 243 } 244 /* reset the buffer */ 245 string[0] = '\0'; 246 stringlen = STRMAX - 1; 247 248 /* move to next error num */ 249 currerr = errp->errnum; 250 first = Yes; 251 } 252 253 /* add this arg */ 254 stringlen = str_addarg(string, stringlen, errp->arg, first); 255 first = No; 256 } 257 258 /* add final message */ 259 stringlen = str_adderr(string, stringlen, currerr); 260 261 /* write the error string */ 262 message_error(" %s", string); 263 } 264 265 /* 266 * Utility routines that help with some of the commands. 267 */ 268 269 char * 270 next_field(char *str) 271 272 273 { 274 if ((str = strchr(str, ' ')) == NULL) 275 { 276 return(NULL); 277 } 278 *str = '\0'; 279 while (*++str == ' ') /* loop */; 280 281 /* if there is nothing left of the string, return NULL */ 282 /* This fix is dedicated to Greg Earle */ 283 return(*str == '\0' ? NULL : str); 284 } 285 286 int 287 scanint(char *str, int *intp) 288 289 { 290 register int val = 0; 291 register int ch; 292 293 /* if there is nothing left of the string, flag it as an error */ 294 /* This fix is dedicated to Greg Earle */ 295 if (*str == '\0') 296 { 297 return(-1); 298 } 299 300 while ((ch = *str++) != '\0') 301 { 302 if (isdigit(ch)) 303 { 304 val = val * 10 + (ch - '0'); 305 } 306 else if (isspace(ch)) 307 { 308 break; 309 } 310 else 311 { 312 return(-1); 313 } 314 } 315 *intp = val; 316 return(0); 317 } 318 319 /* 320 * error_count() - return the number of errors currently logged. 321 */ 322 323 int 324 error_count() 325 326 { 327 return(errcnt); 328 } 329 330 /* 331 * show_errors() - display on stdout the current log of errors. 332 */ 333 334 void 335 show_errors() 336 337 { 338 register int cnt = 0; 339 register struct errs *errp = errs; 340 341 printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); 342 while (cnt++ < errcnt) 343 { 344 printf("%5s: %s\n", errp->arg, 345 errp->errnum == 0 ? "Not a number" : errmsg(errp->errnum)); 346 errp++; 347 } 348 } 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 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 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 } 505 #endif 506 507 /* move to the first process number */ 508 if ((str = next_field(str)) == NULL) 509 { 510 message_error(" remice: no processes specified"); 511 return; 512 } 513 514 #ifdef HAVE_SETPRIORITY 515 /* loop thru the process numbers, renicing each one */ 516 do 517 { 518 if (scanint(str, &procnum) == -1) 519 { 520 ERROR(str, 0); 521 } 522 523 /* check process owner if we're not root */ 524 else if (uid && (uid != proc_owner(procnum))) 525 { 526 ERROR(str, EACCES); 527 } 528 else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) 529 { 530 ERROR(str, errno); 531 } 532 } while ((str = next_field(str)) != NULL); 533 err_string(); 534 #else 535 message_error(" renice operation not supported"); 536 #endif 537 } 538 539 /* COMMAND ROUTINES */ 540 541 /* 542 * Each command routine is called by command_process and is passed a 543 * pointer to the current global state. Command routines are free 544 * to change anything in the global state, although changes to the 545 * statics structure are discouraged. Whatever a command routine 546 * returns will be returned by command_process. 547 */ 548 549 void 550 cmd_quit(globalstate *gstate) 551 552 { 553 quit(EX_OK); 554 /*NOTREACHED*/ 555 } 556 557 int 558 cmd_update(globalstate *gstate) 559 560 { 561 /* go home for visual feedback */ 562 screen_home(); 563 fflush(stdout); 564 message_expire(); 565 return CMD_REFRESH; 566 } 567 568 int 569 cmd_redraw(globalstate *gstate) 570 571 { 572 gstate->fulldraw = Yes; 573 return CMD_REFRESH; 574 } 575 576 int 577 cmd_color(globalstate *gstate) 578 579 { 580 gstate->use_color = color_activate(-1); 581 gstate->fulldraw = Yes; 582 return CMD_REFRESH; 583 } 584 585 int 586 cmd_number(globalstate *gstate) 587 588 { 589 int newval; 590 char tmpbuf[20]; 591 592 message_prompt("Number of processes to show: "); 593 newval = readline(tmpbuf, 8, Yes); 594 if (newval > -1) 595 { 596 #if 0 597 if (newval > gstate->max_topn) 598 { 599 message_error(" This terminal can only display %d processes", 600 gstate->max_topn); 601 } 602 #endif 603 604 if (newval == 0) 605 { 606 /* inhibit the header */ 607 display_header(No); 608 } 609 610 else if (gstate->topn == 0) 611 { 612 display_header(Yes); 613 } 614 615 gstate->topn = newval; 616 } 617 return CMD_REFRESH; 618 } 619 620 int 621 cmd_delay(globalstate *gstate) 622 623 { 624 int newval; 625 char tmpbuf[20]; 626 627 message_prompt("Seconds to delay: "); 628 if ((newval = readline(tmpbuf, 8, Yes)) > -1) 629 { 630 if ((gstate->delay = newval) == 0 && getuid() != 0) 631 { 632 gstate->delay = 1; 633 } 634 } 635 return CMD_REFRESH; 636 } 637 638 int 639 cmd_idle(globalstate *gstate) 640 641 { 642 gstate->pselect.idle = !gstate->pselect.idle; 643 message_error(" %sisplaying idle processes.", 644 gstate->pselect.idle ? "D" : "Not d"); 645 return CMD_REFRESH; 646 } 647 648 int 649 cmd_displays(globalstate *gstate) 650 651 { 652 int i; 653 char tmpbuf[20]; 654 655 message_prompt("Displays to show (currently %s): ", 656 gstate->displays == -1 ? "infinite" : 657 itoa(gstate->displays)); 658 659 if ((i = readline(tmpbuf, 10, Yes)) > 0) 660 { 661 gstate->displays = i; 662 } 663 else if (i == 0) 664 { 665 quit(EX_OK); 666 /*NOTREACHED*/ 667 } 668 return CMD_OK; 669 } 670 671 int 672 cmd_cmdline(globalstate *gstate) 673 674 { 675 if (gstate->statics->flags.fullcmds) 676 { 677 gstate->pselect.fullcmd = !gstate->pselect.fullcmd; 678 message_error(" %sisplaying full command lines.", 679 gstate->pselect.fullcmd ? "D" : "Not d"); 680 return CMD_REFRESH; 681 } 682 message_error(" Full command display not supported."); 683 return CMD_OK; 684 } 685 686 int 687 cmd_order(globalstate *gstate) 688 689 { 690 char tmpbuf[MAX_COLS]; 691 int i; 692 693 if (gstate->statics->order_names != NULL) 694 { 695 message_prompt("Column to sort: "); 696 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 697 { 698 if ((i = string_index(tmpbuf, gstate->statics->order_names)) == -1) 699 { 700 message_error(" Sort order \"%s\" not recognized", tmpbuf); 701 } 702 else 703 { 704 gstate->order_index = i; 705 return CMD_REFRESH; 706 } 707 } 708 } 709 return CMD_OK; 710 } 711 712 int 713 cmd_order_x(globalstate *gstate, char *name, ...) 714 715 { 716 va_list ap; 717 char *p; 718 char **names; 719 int i; 720 721 names = gstate->statics->order_names; 722 if (names != NULL) 723 { 724 if ((i = string_index(name, names)) == -1) 725 { 726 /* check the alternate list */ 727 va_start(ap, name); 728 p = va_arg(ap, char *); 729 while (p != NULL) 730 { 731 if ((i = string_index(p, names)) != -1) 732 { 733 gstate->order_index = i; 734 return CMD_REFRESH; 735 } 736 p = va_arg(ap, char *); 737 } 738 message_error(" Sort order not recognized"); 739 } 740 else 741 { 742 gstate->order_index = i; 743 return CMD_REFRESH; 744 } 745 } 746 return CMD_OK; 747 } 748 749 int 750 cmd_order_cpu(globalstate *gstate) 751 752 { 753 return cmd_order_x(gstate, "cpu", NULL); 754 } 755 756 int 757 cmd_order_pid(globalstate *gstate) 758 759 { 760 return cmd_order_x(gstate, "pid", NULL); 761 } 762 763 int 764 cmd_order_mem(globalstate *gstate) 765 766 { 767 return cmd_order_x(gstate, "mem", "size", NULL); 768 } 769 770 int 771 cmd_order_time(globalstate *gstate) 772 773 { 774 return cmd_order_x(gstate, "time"); 775 } 776 777 #ifdef ENABLE_KILL 778 779 int 780 cmd_kill(globalstate *gstate) 781 782 { 783 char tmpbuf[MAX_COLS]; 784 785 message_prompt_plain("kill "); 786 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 787 { 788 kill_procs(tmpbuf); 789 } 790 return CMD_OK; 791 } 792 793 int 794 cmd_renice(globalstate *gstate) 795 796 { 797 char tmpbuf[MAX_COLS]; 798 799 message_prompt_plain("renice "); 800 if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) 801 { 802 renice_procs(tmpbuf); 803 } 804 return CMD_OK; 805 } 806 807 #endif 808 809 int 810 cmd_user(globalstate *gstate) 811 812 { 813 char linebuf[MAX_COLS]; 814 int i; 815 int ret = CMD_OK; 816 817 message_prompt("Username to show: "); 818 if (readline(linebuf, sizeof(linebuf), No) > 0) 819 { 820 if (linebuf[0] == '+' && 821 linebuf[1] == '\0') 822 { 823 gstate->pselect.uid = -1; 824 ret = CMD_REFRESH; 825 } 826 else if ((i = userid(linebuf)) == -1) 827 { 828 message_error(" %s: unknown user", linebuf); 829 } 830 else 831 { 832 gstate->pselect.uid = i; 833 ret = CMD_REFRESH; 834 } 835 } 836 return ret; 837 } 838 839 int 840 cmd_command(globalstate *gstate) 841 842 { 843 char linebuf[MAX_COLS]; 844 845 if (gstate->pselect.command != NULL) 846 { 847 free(gstate->pselect.command); 848 gstate->pselect.command = NULL; 849 } 850 851 message_prompt("Command to show: "); 852 if (readline(linebuf, sizeof(linebuf), No) > 0) 853 { 854 if (linebuf[0] != '\0') 855 { 856 gstate->pselect.command = strdup(linebuf); 857 } 858 } 859 return CMD_REFRESH; 860 } 861 862 int 863 cmd_useruid(globalstate *gstate) 864 865 { 866 gstate->pselect.usernames = !gstate->pselect.usernames; 867 /* set constants for username/uid display */ 868 if (gstate->pselect.usernames) 869 { 870 gstate->header_text = format_header("USERNAME"); 871 gstate->get_userid = username; 872 } 873 else 874 { 875 gstate->header_text = format_header(" UID "); 876 gstate->get_userid = itoa7; 877 } 878 display_header(2); 879 return CMD_REFRESH; 880 } 881 882 int 883 cmd_mode(globalstate *gstate) 884 885 { 886 if (gstate->statics->modemax <= 1) 887 { 888 return CMD_NA; 889 } 890 gstate->pselect.mode = (gstate->pselect.mode + 1) % gstate->statics->modemax; 891 display_header(2); 892 return CMD_REFRESH; 893 } 894 895 int 896 cmd_system(globalstate *gstate) 897 898 { 899 gstate->pselect.system = !gstate->pselect.system; 900 display_header(2); 901 return CMD_REFRESH; 902 } 903 904 int 905 cmd_threads(globalstate *gstate) 906 907 { 908 if (gstate->statics->flags.threads) 909 { 910 gstate->pselect.threads = !gstate->pselect.threads; 911 display_header(2); 912 return CMD_REFRESH; 913 } 914 return CMD_NA; 915 } 916 917 /* forward reference for cmd_help, as it needs to see the command_table */ 918 int cmd_help(globalstate *gstate); 919 920 /* command table */ 921 command command_table[] = { 922 { '\014', cmd_redraw, "redraw screen" }, 923 { ' ', cmd_update, "update screen" }, 924 { '?', cmd_help, "help; show this text" }, 925 { 'h', cmd_help, NULL }, 926 { 'C', cmd_color, "toggle the use of color" }, 927 { 'H', cmd_threads, "toggle the display of individual threads" }, 928 { 't', cmd_threads, NULL }, 929 { 'M', cmd_order_mem, "sort by memory usage" }, 930 { 'N', cmd_order_pid, "sort by process id" }, 931 { 'P', cmd_order_cpu, "sort by CPU usage" }, 932 { 'S', cmd_system, "toggle the display of system processes" }, 933 { 'T', cmd_order_time, "sort by CPU time" }, 934 { 'U', cmd_useruid, "toggle the display of usernames or uids" }, 935 { 'c', cmd_command, "display processes by command name" }, 936 { 'd', cmd_displays, "change number of displays to show" }, 937 { 'f', cmd_cmdline, "toggle the display of full command paths" }, 938 { 'i', cmd_idle, "toggle the displaying of idle processes" }, 939 { 'I', cmd_idle, NULL }, 940 #ifdef ENABLE_KILL 941 { 'k', cmd_kill, "kill processes; send a signal to a list of processes" }, 942 #endif 943 { 'm', cmd_mode, "toggle between display modes" }, 944 { 'n', cmd_number, "change number of processes to display" }, 945 { '#', cmd_number, NULL }, 946 { 'o', cmd_order, "specify sort order (see below)" }, 947 { 'q', (int (*)(globalstate *))cmd_quit, "quit" }, 948 #ifdef ENABLE_KILL 949 { 'r', cmd_renice, "renice a process" }, 950 #endif 951 { 's', cmd_delay, "change number of seconds to delay between updates" }, 952 { 'u', cmd_user, "display processes for only one user (+ selects all users)" }, 953 { '\0', NULL, NULL }, 954 }; 955 956 int 957 cmd_help(globalstate *gstate) 958 959 { 960 command *c; 961 char buf[12]; 962 char *p; 963 char *help; 964 965 display_pagerstart(); 966 967 display_pager("Top version %s, %s\n", version_string(), copyright); 968 display_pager("Platform module: %s\n\n", MODULE); 969 display_pager("A top users display for Unix\n\n"); 970 display_pager("These single-character commands are available:\n\n"); 971 972 c = command_table; 973 while (c->cmd_func != NULL) 974 { 975 /* skip null help strings */ 976 if ((help = c->help) == NULL) 977 { 978 continue; 979 } 980 981 /* translate character in to something readable */ 982 if (c->ch < ' ') 983 { 984 buf[0] = '^'; 985 buf[1] = c->ch + '@'; 986 buf[2] = '\0'; 987 } 988 else if (c->ch == ' ') 989 { 990 strcpy(buf, "<sp>"); 991 } 992 else 993 { 994 buf[0] = c->ch; 995 buf[1] = '\0'; 996 } 997 998 /* if the next command is the same, fold them onto one line */ 999 if ((c+1)->cmd_func == c->cmd_func) 1000 { 1001 strcat(buf, " or "); 1002 p = buf + strlen(buf); 1003 *p++ = (c+1)->ch; 1004 *p = '\0'; 1005 c++; 1006 } 1007 1008 display_pager("%-7s - %s\n", buf, help); 1009 c++; 1010 } 1011 1012 display_pager("\nNot all commands are available on all systems.\n\n"); 1013 display_pager("Available sort orders: %s\n", gstate->order_namelist); 1014 display_pagerend(); 1015 gstate->fulldraw = Yes; 1016 return CMD_REFRESH; 1017 } 1018 1019 /* 1020 * int command_process(globalstate *gstate, int cmd) 1021 * 1022 * Process the single-character command "cmd". The global state may 1023 * be modified by the command to alter the output. Returns CMD_ERROR 1024 * if there was a serious error that requires an immediate exit, CMD_OK 1025 * to indicate success, CMD_REFRESH to indicate that the screen needs 1026 * to be refreshed immediately, CMD_UNKNOWN when the command is not known, 1027 * and CMD_NA when the command is not available. Error messages for 1028 * CMD_NA and CMD_UNKNOWN must be handled by the caller. 1029 */ 1030 1031 int 1032 command_process(globalstate *gstate, int cmd) 1033 1034 { 1035 command *c; 1036 1037 c = command_table; 1038 while (c->cmd_func != NULL) 1039 { 1040 if (c->ch == cmd) 1041 { 1042 return (c->cmd_func)(gstate); 1043 } 1044 c++; 1045 } 1046 1047 return CMD_UNKNOWN; 1048 } 1049