Changeset View
Changeset View
Standalone View
Standalone View
contrib/top/commands.c
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/* | /* | ||||
* Top users/processes display for Unix | * Copyright (c) 1984 through 2008, William LeFebvre | ||||
* Version 3 | * All rights reserved. | ||||
* | * | ||||
* This program may be freely redistributed, | * Redistribution and use in source and binary forms, with or without | ||||
* but this entire comment MUST remain intact. | * modification, are permitted provided that the following conditions are met: | ||||
* | * | ||||
* Copyright (c) 1984, 1989, William LeFebvre, Rice University | * * Redistributions of source code must retain the above copyright | ||||
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University | * notice, this list of conditions and the following disclaimer. | ||||
* | * | ||||
* * Redistributions in binary form must reproduce the above | |||||
* copyright notice, this list of conditions and the following disclaimer | |||||
* in the documentation and/or other materials provided with the | |||||
* distribution. | |||||
* | |||||
* * Neither the name of William LeFebvre nor the names of other | |||||
* contributors may be used to endorse or promote products derived from | |||||
* this software without specific prior written permission. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
* | |||||
* $FreeBSD$ | * $FreeBSD$ | ||||
*/ | */ | ||||
/* | /* | ||||
* Top users/processes display for Unix | |||||
* Version 3 | |||||
*/ | |||||
/* | |||||
* This file contains the routines that implement some of the interactive | * This file contains the routines that implement some of the interactive | ||||
* mode commands. Note that some of the commands are implemented in-line | * mode commands. Note that some of the commands are implemented in-line | ||||
* in "main". This is necessary because they change the global state of | * in "main". This is necessary because they change the global state of | ||||
* "top" (i.e.: changing the number of processes to display). | * "top" (i.e.: changing the number of processes to display). | ||||
*/ | */ | ||||
#include "os.h" | #include "os.h" | ||||
#include <sys/time.h> | |||||
#include <sys/resource.h> | |||||
#include <ctype.h> | #include <ctype.h> | ||||
#include <errno.h> | |||||
#include <signal.h> | #include <signal.h> | ||||
#include <stdarg.h> | |||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <jail.h> | |||||
#include <color.h> | |||||
#include <errno.h> | |||||
#ifdef HAVE_SYS_RESOURCE_H | |||||
#include <sys/resource.h> | |||||
#endif | |||||
#include "commands.h" | #if defined(HAVE_DECL_SYS_SIGLIST) & defined(HAVE_STRCASECMP) | ||||
#define USE_SYS_SIGLIST | |||||
#endif | |||||
#ifdef USE_SYS_SIGLIST | |||||
extern const char * const sys_siglist[]; | |||||
extern const char * const sys_signame[]; | |||||
#else | |||||
#include "sigdesc.h" /* generated automatically */ | #include "sigdesc.h" /* generated automatically */ | ||||
#endif | |||||
#include "top.h" | #include "top.h" | ||||
#include "machine.h" | |||||
#include "globalstate.h" | |||||
#include "boolean.h" | #include "boolean.h" | ||||
#include "color.h" | |||||
#include "commands.h" | |||||
#include "display.h" | |||||
#include "screen.h" | |||||
#include "username.h" | |||||
#include "utils.h" | #include "utils.h" | ||||
#include "machine.h" | #include "version.h" | ||||
extern int errno; | extern int errno; | ||||
extern char *copyright; | extern char *copyright; | ||||
/* imported from screen.c */ | typedef struct command { | ||||
extern int overstrike; | int ch; | ||||
int (*cmd_func)(globalstate *); | |||||
char *help; | |||||
} command; | |||||
int err_compar(); | |||||
char *err_string(); | |||||
static int str_adderr(char *str, int len, int err); | |||||
static int str_addarg(char *str, int len, char *arg, int first); | |||||
/* | /* | ||||
* show_help() - display the help screen; invoked in response to | * Some of the commands make system calls that could generate errors. | ||||
* either 'h' or '?'. | * These errors are collected up in an array of structures for later | ||||
* contemplation and display. Such routines return a string containing an | |||||
* error message, or NULL if no errors occurred. We need an upper limit on | |||||
* the number of errors, so we arbitrarily choose 20. | |||||
*/ | */ | ||||
void | #define ERRMAX 20 | ||||
show_help() | |||||
struct errs /* structure for a system-call error */ | |||||
{ | { | ||||
printf("Top version %s, %s\n", version_string(), copyright); | int errnum; /* value of errno (that is, the actual error) */ | ||||
fputs("\n\n\ | char *arg; /* argument that caused the error */ | ||||
A top users display for Unix\n\ | }; | ||||
\n\ | |||||
These single-character commands are available:\n\ | |||||
\n\ | |||||
^L - redraw screen\n\ | |||||
q - quit\n\ | |||||
h or ? - help; show this text\n", stdout); | |||||
/* not all commands are availalbe with overstrike terminals */ | static struct errs errs[ERRMAX]; | ||||
if (overstrike) | static int errcnt; | ||||
{ | |||||
fputs("\n\ | /* These macros get used to reset and log the errors */ | ||||
Other commands are also available, but this terminal is not\n\ | #define ERR_RESET errcnt = 0 | ||||
sophisticated enough to handle those commands gracefully.\n\n", stdout); | #define ERROR(p, e) if (errcnt < ERRMAX) \ | ||||
{ \ | |||||
errs[errcnt].arg = (p); \ | |||||
errs[errcnt++].errnum = (e); \ | |||||
} | } | ||||
else | |||||
{ | |||||
fputs("\ | |||||
C - toggle the displaying of weighted CPU percentage\n\ | |||||
d - change number of displays to show\n\ | |||||
e - list errors generated by last \"kill\" or \"renice\" command\n\ | |||||
H - toggle the displaying of threads\n\ | |||||
i or I - toggle the displaying of idle processes\n\ | |||||
j - toggle the displaying of jail ID\n\ | |||||
J - display processes for only one jail (+ selects all jails)\n\ | |||||
k - kill processes; send a signal to a list of processes\n\ | |||||
m - toggle the display between 'cpu' and 'io' modes\n\ | |||||
n or # - change number of processes to display\n", stdout); | |||||
#ifdef ORDER | |||||
if (displaymode == DISP_CPU) | |||||
fputs("\ | |||||
o - specify sort order (pri, size, res, cpu, time, threads, jid, pid)\n", | |||||
stdout); | |||||
else | |||||
fputs("\ | |||||
o - specify sort order (vcsw, ivcsw, read, write, fault, total, jid, pid)\n", | |||||
stdout); | |||||
#endif | |||||
fputs("\ | |||||
P - toggle the displaying of per-CPU statistics\n\ | |||||
r - renice a process\n\ | |||||
s - change number of seconds to delay between updates\n\ | |||||
S - toggle the displaying of system processes\n\ | |||||
a - toggle the displaying of process titles\n\ | |||||
t - toggle the display of this process\n\ | |||||
u - display processes for only one user (+ selects all users)\n\ | |||||
z - toggle the displaying of the system idle process\n\ | |||||
\n\ | |||||
\n", stdout); | |||||
} | |||||
} | |||||
/* | /* | ||||
* Utility routines that help with some of the commands. | * err_compar(p1, p2) - comparison routine used by "qsort" | ||||
* for sorting errors. | |||||
*/ | */ | ||||
char *next_field(str) | int | ||||
err_compar(const void *p1, const void *p2) | |||||
register char *str; | { | ||||
register int result; | |||||
if ((result = ((struct errs *)p1)->errnum - | |||||
((struct errs *)p2)->errnum) == 0) | |||||
{ | { | ||||
if ((str = strchr(str, ' ')) == NULL) | return(strcmp(((struct errs *)p1)->arg, | ||||
{ | ((struct errs *)p2)->arg)); | ||||
return(NULL); | |||||
} | } | ||||
*str = '\0'; | return(result); | ||||
while (*++str == ' ') /* loop */; | |||||
/* if there is nothing left of the string, return NULL */ | |||||
/* This fix is dedicated to Greg Earle */ | |||||
return(*str == '\0' ? NULL : str); | |||||
} | } | ||||
/* | |||||
* str_adderr(str, len, err) - add an explanation of error "err" to | |||||
* the string "str" without overflowing length "len". return | |||||
* number of characters remaining in str, or 0 if overflowed. | |||||
*/ | |||||
int | int | ||||
scanint(str, intp) | str_adderr(char *str, int len, int err) | ||||
char *str; | |||||
int *intp; | |||||
{ | { | ||||
register int val = 0; | register char *msg; | ||||
register char ch; | register int msglen; | ||||
/* if there is nothing left of the string, flag it as an error */ | msg = err == 0 ? "Not a number" : errmsg(err); | ||||
/* This fix is dedicated to Greg Earle */ | msglen = strlen(msg) + 2; | ||||
if (*str == '\0') | if (len <= msglen) | ||||
{ | { | ||||
return(-1); | return(0); | ||||
} | } | ||||
(void) strcat(str, ": "); | |||||
(void) strcat(str, msg); | |||||
return(len - msglen); | |||||
} | |||||
while ((ch = *str++) != '\0') | /* | ||||
* str_addarg(str, len, arg, first) - add the string argument "arg" to | |||||
* the string "str" without overflowing length "len". This is the | |||||
* first in the group when "first" is set (indicating that a comma | |||||
* should NOT be added to the front). Return number of characters | |||||
* remaining in str, or 0 if overflowed. | |||||
*/ | |||||
int | |||||
str_addarg(char *str, int len, char *arg, int first) | |||||
{ | { | ||||
if (isdigit(ch)) | register int arglen; | ||||
arglen = strlen(arg); | |||||
if (!first) | |||||
{ | { | ||||
val = val * 10 + (ch - '0'); | arglen += 2; | ||||
} | } | ||||
else if (isspace(ch)) | if (len <= arglen) | ||||
{ | { | ||||
break; | return(0); | ||||
} | } | ||||
else | if (!first) | ||||
{ | { | ||||
return(-1); | (void) strcat(str, ", "); | ||||
} | } | ||||
(void) strcat(str, arg); | |||||
return(len - arglen); | |||||
} | } | ||||
*intp = val; | |||||
return(0); | |||||
} | |||||
/* | /* | ||||
* Some of the commands make system calls that could generate errors. | * void err_string() | ||||
* These errors are collected up in an array of structures for later | * | ||||
* contemplation and display. Such routines return a string containing an | * Use message_error to log errors in the errs array. This function | ||||
* error message, or NULL if no errors occurred. The next few routines are | * will combine identical errors to make the message short, but if | ||||
* for manipulating and displaying these errors. We need an upper limit on | * there is more than one type of error it will call message_error | ||||
* the number of errors, so we arbitrarily choose 20. | * for each one. | ||||
*/ | */ | ||||
#define ERRMAX 20 | |||||
struct errs /* structure for a system-call error */ | |||||
{ | |||||
int errnum; /* value of errno (that is, the actual error) */ | |||||
char *arg; /* argument that caused the error */ | |||||
}; | |||||
static struct errs errs[ERRMAX]; | |||||
static int errcnt; | |||||
static char *err_toomany = " too many errors occurred"; | |||||
static char *err_listem = | |||||
" Many errors occurred. Press `e' to display the list of errors."; | |||||
/* These macros get used to reset and log the errors */ | |||||
#define ERR_RESET errcnt = 0 | |||||
#define ERROR(p, e) if (errcnt >= ERRMAX) \ | |||||
{ \ | |||||
return(err_toomany); \ | |||||
} \ | |||||
else \ | |||||
{ \ | |||||
errs[errcnt].arg = (p); \ | |||||
errs[errcnt++].errnum = (e); \ | |||||
} | |||||
/* | |||||
* err_string() - return an appropriate error string. This is what the | |||||
* command will return for displaying. If no errors were logged, then | |||||
* return NULL. The maximum length of the error string is defined by | |||||
* "STRMAX". | |||||
*/ | |||||
#define STRMAX 80 | #define STRMAX 80 | ||||
char *err_string() | void | ||||
err_string() | |||||
{ | { | ||||
register struct errs *errp; | register struct errs *errp; | ||||
register int cnt = 0; | register int cnt = 0; | ||||
register int first = Yes; | register int first = Yes; | ||||
register int currerr = -1; | register int currerr = -1; | ||||
int stringlen; /* characters still available in "string" */ | int stringlen = 0; /* characters still available in "string" */ | ||||
static char string[STRMAX]; | char string[STRMAX]; | ||||
/* if there are no errors, return NULL */ | /* if there are no errors, our job is easy */ | ||||
if (errcnt == 0) | if (errcnt == 0) | ||||
{ | { | ||||
return(NULL); | return; | ||||
} | } | ||||
/* sort the errors */ | /* sort the errors */ | ||||
qsort((char *)errs, errcnt, sizeof(struct errs), err_compar); | qsort((char *)errs, errcnt, sizeof(struct errs), err_compar); | ||||
/* need a space at the front of the error string */ | /* initialize the buffer (probably not necessary) */ | ||||
string[0] = ' '; | string[0] = '\0'; | ||||
string[1] = '\0'; | stringlen = STRMAX - 1; | ||||
stringlen = STRMAX - 2; | |||||
/* loop thru the sorted list, building an error string */ | /* loop thru the sorted list, logging errors */ | ||||
while (cnt < errcnt) | while (cnt < errcnt) | ||||
{ | { | ||||
/* point to the current error */ | |||||
errp = &(errs[cnt++]); | errp = &(errs[cnt++]); | ||||
/* note that on overflow "stringlen" will become 0 and all | |||||
subsequent calls to str_addarg or str_adderr will return 0 */ | |||||
/* if the error number is different then add the error string */ | |||||
if (errp->errnum != currerr) | if (errp->errnum != currerr) | ||||
{ | { | ||||
if (currerr != -1) | if (currerr != -1) | ||||
{ | { | ||||
if ((stringlen = str_adderr(string, stringlen, currerr)) < 2) | /* add error string and log the error */ | ||||
{ | stringlen = str_adderr(string, stringlen, currerr); | ||||
return(err_listem); | message_error(" %s", string); | ||||
} | } | ||||
(void) strcat(string, "; "); /* we know there's more */ | /* reset the buffer */ | ||||
} | string[0] = '\0'; | ||||
stringlen = STRMAX - 1; | |||||
/* move to next error num */ | |||||
currerr = errp->errnum; | currerr = errp->errnum; | ||||
first = Yes; | first = Yes; | ||||
} | } | ||||
if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0) | |||||
{ | /* add this arg */ | ||||
return(err_listem); | stringlen = str_addarg(string, stringlen, errp->arg, first); | ||||
} | |||||
first = No; | first = No; | ||||
} | } | ||||
/* add final message */ | /* add final message */ | ||||
stringlen = str_adderr(string, stringlen, currerr); | stringlen = str_adderr(string, stringlen, currerr); | ||||
/* return the error string */ | /* write the error string */ | ||||
return(stringlen == 0 ? err_listem : string); | message_error(" %s", string); | ||||
} | } | ||||
/* | /* | ||||
* str_adderr(str, len, err) - add an explanation of error "err" to | * Utility routines that help with some of the commands. | ||||
* the string "str". | |||||
*/ | */ | ||||
static int | char * | ||||
str_adderr(str, len, err) | next_field(char *str) | ||||
char *str; | |||||
int len; | |||||
int err; | |||||
{ | { | ||||
register char *msg; | if ((str = strchr(str, ' ')) == NULL) | ||||
register int msglen; | |||||
msg = err == 0 ? "Not a number" : errmsg(err); | |||||
msglen = strlen(msg) + 2; | |||||
if (len <= msglen) | |||||
{ | { | ||||
return(0); | return(NULL); | ||||
} | } | ||||
(void) strcat(str, ": "); | *str = '\0'; | ||||
(void) strcat(str, msg); | while (*++str == ' ') /* loop */; | ||||
return(len - msglen); | |||||
/* if there is nothing left of the string, return NULL */ | |||||
/* This fix is dedicated to Greg Earle */ | |||||
return(*str == '\0' ? NULL : str); | |||||
} | } | ||||
/* | int | ||||
* str_addarg(str, len, arg, first) - add the string argument "arg" to | scanint(char *str, int *intp) | ||||
* the string "str". This is the first in the group when "first" | |||||
* is set (indicating that a comma should NOT be added to the front). | |||||
*/ | |||||
static int | |||||
str_addarg(str, len, arg, first) | |||||
char *str; | |||||
int len; | |||||
char *arg; | |||||
int first; | |||||
{ | { | ||||
register int arglen; | register int val = 0; | ||||
register int ch; | |||||
arglen = strlen(arg); | /* if there is nothing left of the string, flag it as an error */ | ||||
if (!first) | /* This fix is dedicated to Greg Earle */ | ||||
if (*str == '\0') | |||||
{ | { | ||||
arglen += 2; | return(-1); | ||||
} | } | ||||
if (len <= arglen) | |||||
while ((ch = *str++) != '\0') | |||||
{ | { | ||||
return(0); | if (isdigit(ch)) | ||||
{ | |||||
val = val * 10 + (ch - '0'); | |||||
} | } | ||||
if (!first) | else if (isspace(ch)) | ||||
{ | { | ||||
(void) strcat(str, ", "); | break; | ||||
} | } | ||||
(void) strcat(str, arg); | else | ||||
return(len - arglen); | |||||
} | |||||
/* | |||||
* err_compar(p1, p2) - comparison routine used by "qsort" | |||||
* for sorting errors. | |||||
*/ | |||||
int | |||||
err_compar(p1, p2) | |||||
register struct errs *p1, *p2; | |||||
{ | { | ||||
register int result; | return(-1); | ||||
if ((result = p1->errnum - p2->errnum) == 0) | |||||
{ | |||||
return(strcmp(p1->arg, p2->arg)); | |||||
} | } | ||||
return(result); | |||||
} | } | ||||
*intp = val; | |||||
return(0); | |||||
} | |||||
/* | /* | ||||
* error_count() - return the number of errors currently logged. | * error_count() - return the number of errors currently logged. | ||||
*/ | */ | ||||
int | int | ||||
error_count() | error_count() | ||||
Show All 21 Lines | errp++; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* kill_procs(str) - send signals to processes, much like the "kill" | * kill_procs(str) - send signals to processes, much like the "kill" | ||||
* command does; invoked in response to 'k'. | * command does; invoked in response to 'k'. | ||||
*/ | */ | ||||
char *kill_procs(str) | void | ||||
kill_procs(char *str) | |||||
char *str; | |||||
{ | { | ||||
register char *nptr; | register char *nptr; | ||||
int signum = SIGTERM; /* default */ | int signum = SIGTERM; /* default */ | ||||
int procnum; | int procnum; | ||||
struct sigdesc *sigp; | |||||
int uid; | int uid; | ||||
int owner; | |||||
#ifndef USE_SYS_SIGLIST | |||||
struct sigdesc *sigp; | |||||
#endif | |||||
/* reset error array */ | /* reset error array */ | ||||
ERR_RESET; | ERR_RESET; | ||||
/* remember our uid */ | /* remember our uid */ | ||||
uid = getuid(); | uid = getuid(); | ||||
/* skip over leading white space */ | /* skip over leading white space */ | ||||
while (isspace(*str)) str++; | while (isspace((int)*str)) str++; | ||||
if (str[0] == '-') | if (str[0] == '-') | ||||
{ | { | ||||
/* explicit signal specified */ | /* explicit signal specified */ | ||||
if ((nptr = next_field(str)) == NULL) | if ((nptr = next_field(str)) == NULL) | ||||
{ | { | ||||
return(" kill: no processes specified"); | message_error(" kill: no processes specified"); | ||||
return; | |||||
} | } | ||||
if (isdigit(str[1])) | str++; | ||||
if (isdigit((int)str[0])) | |||||
{ | { | ||||
(void) scanint(str + 1, &signum); | (void) scanint(str, &signum); | ||||
if (signum <= 0 || signum >= NSIG) | if (signum <= 0 || signum >= NSIG) | ||||
{ | { | ||||
return(" invalid signal number"); | message_error(" kill: invalid signal number"); | ||||
return; | |||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
/* translate the name into a number */ | /* translate the name into a number */ | ||||
#ifdef USE_SYS_SIGLIST | |||||
for (signum = 1; signum < NSIG; signum++) | |||||
{ | |||||
if (strcasecmp(sys_signame[signum], str) == 0) | |||||
{ | |||||
break; | |||||
} | |||||
} | |||||
if (signum == NSIG) | |||||
{ | |||||
message_error(" kill: bad signal name"); | |||||
return; | |||||
} | |||||
#else | |||||
for (sigp = sigdesc; sigp->name != NULL; sigp++) | for (sigp = sigdesc; sigp->name != NULL; sigp++) | ||||
{ | { | ||||
if (strcmp(sigp->name, str + 1) == 0) | #ifdef HAVE_STRCASECMP | ||||
if (strcasecmp(sigp->name, str) == 0) | |||||
#else | |||||
if (strcmp(sigp->name, str) == 0) | |||||
#endif | |||||
{ | { | ||||
signum = sigp->number; | signum = sigp->number; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
/* was it ever found */ | /* was it ever found */ | ||||
if (sigp->name == NULL) | if (sigp->name == NULL) | ||||
{ | { | ||||
return(" bad signal name"); | message_error(" kill: bad signal name"); | ||||
return; | |||||
} | } | ||||
#endif | |||||
} | } | ||||
/* put the new pointer in place */ | /* put the new pointer in place */ | ||||
str = nptr; | str = nptr; | ||||
} | } | ||||
/* loop thru the string, killing processes */ | /* loop thru the string, killing processes */ | ||||
do | do | ||||
{ | { | ||||
if (scanint(str, &procnum) == -1) | if (scanint(str, &procnum) == -1) | ||||
{ | { | ||||
ERROR(str, 0); | ERROR(str, 0); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
/* check process owner if we're not root */ | /* check process owner if we're not root */ | ||||
if (uid && (uid != proc_owner(procnum))) | owner = proc_owner(procnum); | ||||
if (uid && (uid != owner)) | |||||
{ | { | ||||
ERROR(str, EACCES); | ERROR(str, owner == -1 ? ESRCH : EACCES); | ||||
} | } | ||||
/* go in for the kill */ | /* go in for the kill */ | ||||
else if (kill(procnum, signum) == -1) | else if (kill(procnum, signum) == -1) | ||||
{ | { | ||||
/* chalk up an error */ | /* chalk up an error */ | ||||
ERROR(str, errno); | ERROR(str, errno); | ||||
} | } | ||||
} | } | ||||
} while ((str = next_field(str)) != NULL); | } while ((str = next_field(str)) != NULL); | ||||
/* return appropriate error string */ | /* process errors */ | ||||
return(err_string()); | err_string(); | ||||
} | } | ||||
/* | /* | ||||
* renice_procs(str) - change the "nice" of processes, much like the | * renice_procs(str) - change the "nice" of processes, much like the | ||||
* "renice" command does; invoked in response to 'r'. | * "renice" command does; invoked in response to 'r'. | ||||
*/ | */ | ||||
char *renice_procs(str) | void | ||||
renice_procs(char *str) | |||||
char *str; | |||||
{ | { | ||||
register char negate; | register char negate; | ||||
int prio; | int prio; | ||||
int procnum; | int procnum; | ||||
int uid; | int uid; | ||||
ERR_RESET; | ERR_RESET; | ||||
uid = getuid(); | uid = getuid(); | ||||
Show All 13 Lines | str++; | ||||
{ | { | ||||
prio = -prio; | prio = -prio; | ||||
} | } | ||||
#if defined(PRIO_MIN) && defined(PRIO_MAX) | #if defined(PRIO_MIN) && defined(PRIO_MAX) | ||||
/* check for validity */ | /* check for validity */ | ||||
if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX) | if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX) | ||||
{ | { | ||||
return(" bad priority value"); | message_error(" renice: bad priority value"); | ||||
} | } | ||||
#endif | #endif | ||||
/* move to the first process number */ | /* move to the first process number */ | ||||
if ((str = next_field(str)) == NULL) | if ((str = next_field(str)) == NULL) | ||||
{ | { | ||||
return(" no processes specified"); | message_error(" remice: no processes specified"); | ||||
} | } | ||||
#ifdef HAVE_SETPRIORITY | |||||
/* loop thru the process numbers, renicing each one */ | /* loop thru the process numbers, renicing each one */ | ||||
do | do | ||||
{ | { | ||||
if (scanint(str, &procnum) == -1) | if (scanint(str, &procnum) == -1) | ||||
{ | { | ||||
ERROR(str, 0); | ERROR(str, 0); | ||||
} | } | ||||
/* check process owner if we're not root */ | /* check process owner if we're not root */ | ||||
else if (uid && (uid != proc_owner(procnum))) | else if (uid && (uid != proc_owner(procnum))) | ||||
{ | { | ||||
ERROR(str, EACCES); | ERROR(str, EACCES); | ||||
} | } | ||||
else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) | else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) | ||||
{ | { | ||||
ERROR(str, errno); | ERROR(str, errno); | ||||
} | } | ||||
} while ((str = next_field(str)) != NULL); | } while ((str = next_field(str)) != NULL); | ||||
err_string(); | |||||
#else | |||||
message_error(" renice operation not supported"); | |||||
#endif | |||||
} | |||||
/* return appropriate error string */ | /* COMMAND ROUTINES */ | ||||
return(err_string()); | |||||
/* | |||||
* Each command routine is called by command_process and is passed a | |||||
* pointer to the current global state. Command routines are free | |||||
* to change anything in the global state, although changes to the | |||||
* statics structure are discouraged. Whatever a command routine | |||||
* returns will be returned by command_process. | |||||
*/ | |||||
void | |||||
cmd_quit(globalstate *gstate) | |||||
{ | |||||
quit(EX_OK); | |||||
/*NOTREACHED*/ | |||||
} | } | ||||
int | |||||
cmd_update(globalstate *gstate) | |||||
{ | |||||
/* go home for visual feedback */ | |||||
screen_home(); | |||||
fflush(stdout); | |||||
message_expire(); | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_redraw(globalstate *gstate) | |||||
{ | |||||
gstate->fulldraw = Yes; | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_color(globalstate *gstate) | |||||
{ | |||||
gstate->use_color = color_activate(-1); | |||||
gstate->fulldraw = Yes; | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_number(globalstate *gstate) | |||||
{ | |||||
int newval; | |||||
char tmpbuf[20]; | |||||
message_prompt("Number of processes to show: "); | |||||
newval = readline(tmpbuf, 8, Yes); | |||||
if (newval > -1) | |||||
{ | |||||
if (newval > gstate->max_topn) | |||||
{ | |||||
message_error(" This terminal can only display %d processes", | |||||
gstate->max_topn); | |||||
} | |||||
if (newval == 0) | |||||
{ | |||||
/* inhibit the header */ | |||||
display_header(No); | |||||
} | |||||
else if (gstate->topn == 0) | |||||
{ | |||||
display_header(Yes); | |||||
} | |||||
gstate->topn = newval; | |||||
} | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_delay(globalstate *gstate) | |||||
{ | |||||
int newval; | |||||
char tmpbuf[20]; | |||||
message_prompt("Seconds to delay: "); | |||||
if ((newval = readline(tmpbuf, 8, Yes)) > -1) | |||||
{ | |||||
if ((gstate->delay = newval) == 0 && getuid() != 0) | |||||
{ | |||||
gstate->delay = 1; | |||||
} | |||||
} | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_idle(globalstate *gstate) | |||||
{ | |||||
gstate->pselect.idle = !gstate->pselect.idle; | |||||
message_error(" %sisplaying idle processes.", | |||||
gstate->pselect.idle ? "D" : "Not d"); | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_displays(globalstate *gstate) | |||||
{ | |||||
int i; | |||||
char tmpbuf[20]; | |||||
message_prompt("Displays to show (currently %s): ", | |||||
gstate->displays == -1 ? "infinite" : | |||||
itoa(gstate->displays)); | |||||
if ((i = readline(tmpbuf, 10, Yes)) > 0) | |||||
{ | |||||
gstate->displays = i; | |||||
} | |||||
else if (i == 0) | |||||
{ | |||||
quit(EX_OK); | |||||
/*NOTREACHED*/ | |||||
} | |||||
return CMD_OK; | |||||
} | |||||
/* Toggle the display of jail IDs. */ | |||||
int | |||||
cmd_jailid(globalstate *gstate) | |||||
{ | |||||
gstate->statics->flags.jids = !gstate->statics->flags.jids; | |||||
/* JID needs to be added or removed from the columns. */ | |||||
calculate_header_columns(gstate->statics); | |||||
message_error(" %sisplaying jail IDs.", | |||||
gstate->statics->flags.jids ? "D" : "Not d"); | |||||
return CMD_REFRESH; | |||||
} | |||||
/* Filter by a particular jail ID. */ | |||||
int | |||||
cmd_filter_jail(globalstate *gstate) | |||||
{ | |||||
char tmpbuf[50]; | |||||
message_prompt("Filter by jail name or ID ('+' to disable): "); | |||||
if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) | |||||
{ | |||||
/* A '+' disables filtering. */ | |||||
if (tmpbuf[0] == '+' && tmpbuf[1] == '\0') | |||||
gstate->pselect.filter_jail = 0; | |||||
/* Otherwise look up the jail ID. */ | |||||
else | |||||
{ | |||||
if ((gstate->pselect.jail = jail_getid(tmpbuf)) == -1) | |||||
message_error(" No such jail."); | |||||
else | |||||
gstate->pselect.filter_jail = 1; | |||||
} | |||||
/* Include jail ID column. */ | |||||
gstate->statics->flags.jids = 1; | |||||
/* JID needs to be added or removed from the columns. */ | |||||
calculate_header_columns(gstate->statics); | |||||
return CMD_REFRESH; | |||||
} | |||||
else return CMD_OK; | |||||
} | |||||
int | |||||
cmd_cmdline(globalstate *gstate) | |||||
{ | |||||
if (gstate->statics->flags.fullcmds) | |||||
{ | |||||
gstate->pselect.fullcmd = !gstate->pselect.fullcmd; | |||||
message_error(" %sisplaying full command lines.", | |||||
gstate->pselect.fullcmd ? "D" : "Not d"); | |||||
return CMD_REFRESH; | |||||
} | |||||
message_error(" Full command display not supported."); | |||||
return CMD_OK; | |||||
} | |||||
int | |||||
cmd_order(globalstate *gstate) | |||||
{ | |||||
char tmpbuf[MAX_COLS]; | |||||
int i; | |||||
if (gstate->statics->order_names != NULL) | |||||
{ | |||||
message_prompt("Column to sort: "); | |||||
if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) | |||||
{ | |||||
if ((i = string_index(tmpbuf, gstate->statics->order_names)) == -1) | |||||
{ | |||||
message_error(" Sort order \"%s\" not recognized", tmpbuf); | |||||
} | |||||
else | |||||
{ | |||||
gstate->order_index = i; | |||||
return CMD_REFRESH; | |||||
} | |||||
} | |||||
} | |||||
return CMD_OK; | |||||
} | |||||
int | |||||
cmd_order_x(globalstate *gstate, char *name, ...) | |||||
{ | |||||
va_list ap; | |||||
char *p; | |||||
char **names; | |||||
int i; | |||||
names = gstate->statics->order_names; | |||||
if (names != NULL) | |||||
{ | |||||
if ((i = string_index(name, names)) == -1) | |||||
{ | |||||
/* check the alternate list */ | |||||
va_start(ap, name); | |||||
p = va_arg(ap, char *); | |||||
while (p != NULL) | |||||
{ | |||||
if ((i = string_index(p, names)) != -1) | |||||
{ | |||||
gstate->order_index = i; | |||||
return CMD_REFRESH; | |||||
} | |||||
p = va_arg(ap, char *); | |||||
} | |||||
message_error(" Sort order not recognized"); | |||||
} | |||||
else | |||||
{ | |||||
gstate->order_index = i; | |||||
return CMD_REFRESH; | |||||
} | |||||
} | |||||
return CMD_OK; | |||||
} | |||||
int | |||||
cmd_order_cpu(globalstate *gstate) | |||||
{ | |||||
return cmd_order_x(gstate, "cpu", NULL); | |||||
} | |||||
int | |||||
cmd_order_pid(globalstate *gstate) | |||||
{ | |||||
return cmd_order_x(gstate, "pid", NULL); | |||||
} | |||||
int | |||||
cmd_order_mem(globalstate *gstate) | |||||
{ | |||||
return cmd_order_x(gstate, "mem", "size", NULL); | |||||
} | |||||
int | |||||
cmd_order_time(globalstate *gstate) | |||||
{ | |||||
return cmd_order_x(gstate, "time"); | |||||
} | |||||
#ifdef ENABLE_KILL | |||||
int | |||||
cmd_kill(globalstate *gstate) | |||||
{ | |||||
char tmpbuf[MAX_COLS]; | |||||
message_prompt_plain("kill "); | |||||
if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) | |||||
{ | |||||
kill_procs(tmpbuf); | |||||
} | |||||
return CMD_OK; | |||||
} | |||||
int | |||||
cmd_renice(globalstate *gstate) | |||||
{ | |||||
char tmpbuf[MAX_COLS]; | |||||
message_prompt_plain("renice "); | |||||
if (readline(tmpbuf, sizeof(tmpbuf), No) > 0) | |||||
{ | |||||
renice_procs(tmpbuf); | |||||
} | |||||
return CMD_OK; | |||||
} | |||||
#endif | |||||
int | |||||
cmd_user(globalstate *gstate) | |||||
{ | |||||
char linebuf[MAX_COLS]; | |||||
int i; | |||||
int ret = CMD_OK; | |||||
message_prompt("Username to show: "); | |||||
if (readline(linebuf, sizeof(linebuf), No) > 0) | |||||
{ | |||||
if (linebuf[0] == '+' && | |||||
linebuf[1] == '\0') | |||||
{ | |||||
gstate->pselect.uid = -1; | |||||
ret = CMD_REFRESH; | |||||
} | |||||
else if ((i = userid(linebuf)) == -1) | |||||
{ | |||||
message_error(" %s: unknown user", linebuf); | |||||
} | |||||
else | |||||
{ | |||||
gstate->pselect.uid = i; | |||||
ret = CMD_REFRESH; | |||||
} | |||||
} | |||||
return ret; | |||||
} | |||||
int | |||||
cmd_command(globalstate *gstate) | |||||
{ | |||||
char linebuf[MAX_COLS]; | |||||
if (gstate->pselect.command != NULL) | |||||
{ | |||||
free(gstate->pselect.command); | |||||
gstate->pselect.command = NULL; | |||||
} | |||||
message_prompt("Command to show: "); | |||||
if (readline(linebuf, sizeof(linebuf), No) > 0) | |||||
{ | |||||
if (linebuf[0] != '\0') | |||||
{ | |||||
gstate->pselect.command = strdup(linebuf); | |||||
} | |||||
} | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_useruid(globalstate *gstate) | |||||
{ | |||||
gstate->pselect.usernames = !gstate->pselect.usernames; | |||||
display_header(2); | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_mode(globalstate *gstate) | |||||
{ | |||||
if (gstate->statics->modemax <= 1) | |||||
{ | |||||
return CMD_NA; | |||||
} | |||||
gstate->pselect.mode = (gstate->pselect.mode + 1) % gstate->statics->modemax; | |||||
display_header(2); | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_system(globalstate *gstate) | |||||
{ | |||||
gstate->pselect.system = !gstate->pselect.system; | |||||
display_header(2); | |||||
return CMD_REFRESH; | |||||
} | |||||
int | |||||
cmd_threads(globalstate *gstate) | |||||
{ | |||||
if (gstate->statics->flags.threads) | |||||
{ | |||||
gstate->pselect.threads = !gstate->pselect.threads; | |||||
display_header(2); | |||||
return CMD_REFRESH; | |||||
} | |||||
return CMD_NA; | |||||
} | |||||
/* forward reference for cmd_help, as it needs to see the command_table */ | |||||
int cmd_help(globalstate *gstate); | |||||
/* command table */ | |||||
command command_table[] = { | |||||
{ '\014', cmd_redraw, "redraw screen" }, | |||||
{ ' ', cmd_update, "update screen" }, | |||||
{ '?', cmd_help, "help; show this text" }, | |||||
{ 'h', cmd_help, NULL }, | |||||
{ 'C', cmd_color, "toggle the use of color" }, | |||||
{ 'H', cmd_threads, "toggle the display of individual threads" }, | |||||
{ 't', cmd_threads, NULL }, | |||||
{ 'M', cmd_order_mem, "sort by memory usage" }, | |||||
{ 'N', cmd_order_pid, "sort by process id" }, | |||||
{ 'P', cmd_order_cpu, "sort by CPU usage" }, | |||||
{ 'S', cmd_system, "toggle the display of system processes" }, | |||||
{ 'T', cmd_order_time, "sort by CPU time" }, | |||||
{ 'U', cmd_useruid, "toggle the display of usernames or uids" }, | |||||
{ 'c', cmd_command, "display processes by command name" }, | |||||
{ 'd', cmd_displays, "change number of displays to show" }, | |||||
{ 'f', cmd_cmdline, "toggle the display of full command paths" }, | |||||
{ 'i', cmd_idle, "toggle the displaying of idle processes" }, | |||||
{ 'I', cmd_idle, NULL }, | |||||
#ifdef ENABLE_KILL | |||||
{ 'k', cmd_kill, "kill processes; send a signal to a list of processes" }, | |||||
#endif | |||||
{ 'm', cmd_mode, "toggle between display modes" }, | |||||
{ 'n', cmd_number, "change number of processes to display" }, | |||||
{ '#', cmd_number, NULL }, | |||||
{ 'o', cmd_order, "specify sort order (see below)" }, | |||||
{ 'q', (int (*)(globalstate *))cmd_quit, "quit" }, | |||||
#ifdef ENABLE_KILL | |||||
{ 'r', cmd_renice, "renice a process" }, | |||||
#endif | |||||
{ 's', cmd_delay, "change number of seconds to delay between updates" }, | |||||
{ 'u', cmd_user, "display processes for only one user (+ selects all users)" }, | |||||
{ 'j', cmd_jailid, "toggle the display of jail IDs" }, | |||||
{ 'J', cmd_filter_jail, "filter by a jail ID" }, | |||||
{ '\0', NULL, NULL }, | |||||
}; | |||||
int | |||||
cmd_help(globalstate *gstate) | |||||
{ | |||||
command *c; | |||||
char buf[12]; | |||||
char *p; | |||||
char *help; | |||||
display_pagerstart(); | |||||
display_pager("Top version %s, %s\n", version_string(), copyright); | |||||
display_pager("Platform module: %s\n\n", MODULE); | |||||
display_pager("A top users display for Unix\n\n"); | |||||
display_pager("These single-character commands are available:\n\n"); | |||||
c = command_table; | |||||
while (c->cmd_func != NULL) | |||||
{ | |||||
/* skip null help strings */ | |||||
if ((help = c->help) == NULL) | |||||
{ | |||||
continue; | |||||
} | |||||
/* translate character in to something readable */ | |||||
if (c->ch < ' ') | |||||
{ | |||||
buf[0] = '^'; | |||||
buf[1] = c->ch + '@'; | |||||
buf[2] = '\0'; | |||||
} | |||||
else if (c->ch == ' ') | |||||
{ | |||||
strcpy(buf, "<sp>"); | |||||
} | |||||
else | |||||
{ | |||||
buf[0] = c->ch; | |||||
buf[1] = '\0'; | |||||
} | |||||
/* if the next command is the same, fold them onto one line */ | |||||
if ((c+1)->cmd_func == c->cmd_func) | |||||
{ | |||||
strcat(buf, " or "); | |||||
p = buf + strlen(buf); | |||||
*p++ = (c+1)->ch; | |||||
*p = '\0'; | |||||
c++; | |||||
} | |||||
display_pager("%-7s - %s\n", buf, help); | |||||
c++; | |||||
} | |||||
display_pager("\nNot all commands are available on all systems.\n\n"); | |||||
display_pager("Available sort orders: %s\n", gstate->order_namelist); | |||||
display_pagerend(); | |||||
gstate->fulldraw = Yes; | |||||
return CMD_REFRESH; | |||||
} | |||||
/* | |||||
* int command_process(globalstate *gstate, int cmd) | |||||
* | |||||
* Process the single-character command "cmd". The global state may | |||||
* be modified by the command to alter the output. Returns CMD_ERROR | |||||
* if there was a serious error that requires an immediate exit, CMD_OK | |||||
* to indicate success, CMD_REFRESH to indicate that the screen needs | |||||
* to be refreshed immediately, CMD_UNKNOWN when the command is not known, | |||||
* and CMD_NA when the command is not available. Error messages for | |||||
* CMD_NA and CMD_UNKNOWN must be handled by the caller. | |||||
*/ | |||||
int | |||||
command_process(globalstate *gstate, int cmd) | |||||
{ | |||||
command *c; | |||||
c = command_table; | |||||
while (c->cmd_func != NULL) | |||||
{ | |||||
if (c->ch == cmd) | |||||
{ | |||||
return (c->cmd_func)(gstate); | |||||
} | |||||
c++; | |||||
} | |||||
return CMD_UNKNOWN; | |||||
} |