version 1.8, 2003/06/12 22:30:23 |
version 1.9, 2003/06/13 21:52:24 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
|
|
/* |
/* |
* Top users/processes display for Unix |
* Top users/processes display for Unix |
|
|
#include "utils.h" |
#include "utils.h" |
#include "machine.h" |
#include "machine.h" |
|
|
static char *next_field(char *); |
static char *next_field(char *); |
static int scanint(char *, int *); |
static int scanint(char *, int *); |
static char *err_string(void); |
static char *err_string(void); |
static size_t str_adderr(char *, size_t, int); |
static size_t str_adderr(char *, size_t, int); |
static size_t str_addarg(char *, size_t, char *, int); |
static size_t str_addarg(char *, size_t, char *, int); |
static int err_compar(const void *, const void *); |
static int err_compar(const void *, const void *); |
|
|
/* |
/* |
* show_help() - display the help screen; invoked in response to |
* show_help() - display the help screen; invoked in response to |
* either 'h' or '?'. |
* either 'h' or '?'. |
*/ |
*/ |
|
|
void |
void |
show_help(void) |
show_help(void) |
{ |
{ |
printf("Top version %s, %s\n", version_string(), copyright); |
printf("Top version %s, %s\n", version_string(), copyright); |
fputs("\n\n\ |
fputs("\n\n" |
A top users display for Unix\n\ |
"A top users display for Unix\n" |
\n\ |
"\n" |
These single-character commands are available:\n\ |
"These single-character commands are available:\n" |
\n\ |
"\n" |
^L - redraw screen\n\ |
"^L - redraw screen\n" |
q - quit\n\ |
"q - quit\n" |
h or ? - help; show this text\n", stdout); |
"h or ? - help; show this text\n", stdout); |
|
|
/* not all commands are availalbe with overstrike terminals */ |
/* not all commands are available with overstrike terminals */ |
if (overstrike) |
if (overstrike) { |
{ |
fputs("\n" |
fputs("\n\ |
"Other commands are also available, but this terminal is not\n" |
Other commands are also available, but this terminal is not\n\ |
"sophisticated enough to handle those commands gracefully.\n\n", |
sophisticated enough to handle those commands gracefully.\n\n", stdout); |
stdout); |
} |
} else { |
else |
fputs( |
{ |
"d - change number of displays to show\n" |
fputs("\ |
"e - list errors generated by last \"kill\" or \"renice\" command\n" |
d - change number of displays to show\n\ |
"i - toggle the displaying of idle processes\n" |
e - list errors generated by last \"kill\" or \"renice\" command\n\ |
"I - same as 'i'\n" |
i - toggle the displaying of idle processes\n\ |
"k - kill processes; send a signal to a list of processes\n" |
I - same as 'i'\n\ |
"n or # - change number of processes to display\n", stdout); |
k - kill processes; send a signal to a list of processes\n\ |
|
n or # - change number of processes to display\n", stdout); |
|
#ifdef ORDER |
#ifdef ORDER |
fputs("\ |
fputs( |
o - specify sort order (size, res, cpu, time)\n", stdout); |
"o - specify sort order (size, res, cpu, time)\n", |
|
stdout); |
#endif |
#endif |
fputs("\ |
fputs( |
r - renice a process\n\ |
"r - renice a process\n" |
s - change number of seconds to delay between updates\n\ |
"s - change number of seconds to delay between updates\n" |
u - display processes for only one user (+ selects all users)\n\ |
"u - display processes for only one user (+ selects all users)\n" |
\n\ |
"\n\n", stdout); |
\n", stdout); |
} |
} |
|
} |
} |
|
|
/* |
/* |
* Utility routines that help with some of the commands. |
* Utility routines that help with some of the commands. |
*/ |
*/ |
|
|
static char * |
static char * |
next_field(char *str) |
next_field(char *str) |
{ |
{ |
if ((str = strchr(str, ' ')) == NULL) |
if ((str = strchr(str, ' ')) == NULL) |
{ |
return (NULL); |
return(NULL); |
|
} |
|
*str = '\0'; |
|
while (*++str == ' ') /* loop */; |
|
|
|
/* if there is nothing left of the string, return NULL */ |
*str = '\0'; |
/* This fix is dedicated to Greg Earle */ |
while (*++str == ' ') /* loop */ |
return (*str == '\0' ? NULL : str); |
; |
|
|
|
/* if there is nothing left of the string, return NULL */ |
|
/* This fix is dedicated to Greg Earle */ |
|
return (*str == '\0' ? NULL : str); |
} |
} |
|
|
static int scanint(char *str, int *intp) |
static int |
|
scanint(char *str, int *intp) |
{ |
{ |
int val = 0; |
int val = 0; |
char ch; |
char ch; |
|
|
/* if there is nothing left of the string, flag it as an error */ |
/* if there is nothing left of the string, flag it as an error */ |
/* This fix is dedicated to Greg Earle */ |
/* This fix is dedicated to Greg Earle */ |
if (*str == '\0') |
if (*str == '\0') |
{ |
return (-1); |
return(-1); |
|
} |
|
|
|
while ((ch = *str++) != '\0') |
while ((ch = *str++) != '\0') { |
{ |
if (isdigit(ch)) |
if (isdigit(ch)) |
val = val * 10 + (ch - '0'); |
{ |
else if (isspace(ch)) |
val = val * 10 + (ch - '0'); |
break; |
|
else |
|
return (-1); |
} |
} |
else if (isspace(ch)) |
*intp = val; |
{ |
return (0); |
break; |
|
} |
|
else |
|
{ |
|
return(-1); |
|
} |
|
} |
|
*intp = val; |
|
return(0); |
|
} |
} |
|
|
/* |
/* |
|
|
|
|
#define ERRMAX 20 |
#define ERRMAX 20 |
|
|
struct errs /* structure for a system-call error */ |
struct errs { /* structure for a system-call error */ |
{ |
int errno; /* value of errno (that is, the actual error) */ |
int errno; /* value of errno (that is, the actual error) */ |
char *arg; /* argument that caused the error */ |
char *arg; /* argument that caused the error */ |
|
}; |
}; |
|
|
static struct errs errs[ERRMAX]; |
static struct errs errs[ERRMAX]; |
static int errcnt; |
static int errcnt; |
static char *err_toomany = " too many errors occurred"; |
static char *err_toomany = " too many errors occurred"; |
static char *err_listem = |
static char *err_listem = |
" Many errors occurred. Press `e' to display the list of errors."; |
" Many errors occurred. Press `e' to display the list of errors."; |
|
|
/* These macros get used to reset and log the errors */ |
/* These macros get used to reset and log the errors */ |
#define ERR_RESET errcnt = 0 |
#define ERR_RESET errcnt = 0 |
#define ERROR(p, e) if (errcnt >= ERRMAX) \ |
#define ERROR(p, e) \ |
{ \ |
if (errcnt >= ERRMAX) { \ |
return(err_toomany); \ |
return(err_toomany); \ |
} \ |
} else { \ |
else \ |
errs[errcnt].arg = (p); \ |
{ \ |
errs[errcnt++].errno = (e); \ |
errs[errcnt].arg = (p); \ |
} |
errs[errcnt++].errno = (e); \ |
|
} |
|
|
|
|
#define STRMAX 80 |
|
|
/* |
/* |
* err_string() - return an appropriate error string. This is what the |
* err_string() - return an appropriate error string. This is what the |
* command will return for displaying. If no errors were logged, then |
* command will return for displaying. If no errors were logged, then |
* return NULL. The maximum length of the error string is defined by |
* return NULL. The maximum length of the error string is defined by |
* "STRMAX". |
* "STRMAX". |
*/ |
*/ |
|
|
#define STRMAX 80 |
|
|
|
static char * |
static char * |
err_string(void) |
err_string(void) |
{ |
{ |
struct errs *errp; |
int cnt = 0, first = Yes, currerr = -1; |
int cnt = 0; |
static char string[STRMAX]; |
int first = Yes; |
struct errs *errp; |
int currerr = -1; |
|
static char string[STRMAX]; |
|
|
|
/* if there are no errors, return NULL */ |
/* if there are no errors, return NULL */ |
if (errcnt == 0) |
if (errcnt == 0) |
{ |
return (NULL); |
return(NULL); |
|
} |
|
|
|
/* 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 */ |
/* need a space at the front of the error string */ |
string[0] = ' '; |
string[0] = ' '; |
string[1] = '\0'; |
string[1] = '\0'; |
|
|
/* loop thru the sorted list, building an error string */ |
/* loop thru the sorted list, building an error string */ |
while (cnt < errcnt) |
while (cnt < errcnt) { |
{ |
errp = &(errs[cnt++]); |
errp = &(errs[cnt++]); |
if (errp->errno != currerr) { |
if (errp->errno != currerr) |
if (currerr != -1) { |
{ |
if (str_adderr(string, sizeof string, currerr) > |
if (currerr != -1) |
sizeof string - 2) |
{ |
return (err_listem); |
if (str_adderr(string, sizeof string, currerr) > |
|
(sizeof string - 2)) |
/* we know there's more */ |
{ |
(void) strlcat(string, "; ", sizeof string); |
return(err_listem); |
} |
|
currerr = errp->errno; |
|
first = Yes; |
} |
} |
/* we know there's more */ |
if (str_addarg(string, sizeof string, errp->arg, first) >= |
(void) strlcat(string, "; ", sizeof string); |
sizeof string) |
} |
return (err_listem); |
currerr = errp->errno; |
|
first = Yes; |
first = No; |
} |
} |
if (str_addarg(string, sizeof string, errp->arg, first) >= |
|
sizeof string) |
|
{ |
|
return(err_listem); |
|
} |
|
first = No; |
|
} |
|
|
|
/* add final message */ |
/* add final message */ |
if (str_adderr(string, sizeof string, currerr) >= sizeof string) |
if (str_adderr(string, sizeof string, currerr) >= sizeof string) |
return(err_listem); |
return (err_listem); |
|
|
/* return the error string */ |
/* return the error string */ |
return(string); |
return (string); |
} |
} |
|
|
/* |
/* |
* str_adderr(str, len, err) - add an explanation of error "err" to |
* str_adderr(str, len, err) - add an explanation of error "err" to |
* the string "str". |
* the string "str". |
*/ |
*/ |
|
|
static size_t |
static size_t |
str_adderr(char *str, size_t len, int err) |
str_adderr(char *str, size_t len, int err) |
{ |
{ |
char *msg; |
size_t msglen; |
size_t msglen; |
char *msg; |
|
|
msg = err == 0 ? "Not a number" : strerror(err); |
msg = err == 0 ? "Not a number" : strerror(err); |
|
|
if ((msglen = strlcat(str, ": ", len)) >= len) |
if ((msglen = strlcat(str, ": ", len)) >= len) |
return(msglen); |
return (msglen); |
|
|
return(strlcat(str, msg, len)); |
return (strlcat(str, msg, len)); |
} |
} |
|
|
/* |
/* |
|
|
* the string "str". This is the first in the group when "first" |
* 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). |
* is set (indicating that a comma should NOT be added to the front). |
*/ |
*/ |
|
|
static size_t |
static size_t |
str_addarg(char *str, size_t len, char *arg, int first) |
str_addarg(char *str, size_t len, char *arg, int first) |
{ |
{ |
size_t msglen; |
size_t msglen; |
|
|
if (!first) |
if (!first) { |
{ |
if ((msglen = strlcat(str, ", ", len)) >= len) |
if ((msglen = strlcat(str, ", ", len)) >= len) |
return (msglen); |
return(msglen); |
} |
} |
return (strlcat(str, arg, len)); |
|
|
return (strlcat(str, arg, len)); |
|
} |
} |
|
|
/* |
/* |
* err_compar(p1, p2) - comparison routine used by "qsort" |
* err_compar(p1, p2) - comparison routine used by "qsort" |
* for sorting errors. |
* for sorting errors. |
*/ |
*/ |
|
|
static int |
static int |
err_compar(const void *e1, const void *e2) |
err_compar(const void *e1, const void *e2) |
{ |
{ |
const struct errs *p1 = (struct errs *)e1; |
const struct errs *p1 = (struct errs *) e1; |
const struct errs *p2 = (struct errs *)e2; |
const struct errs *p2 = (struct errs *) e2; |
int result; |
int result; |
|
|
if ((result = p1->errno - p2->errno) == 0) |
if ((result = p1->errno - p2->errno) == 0) |
{ |
return (strcmp(p1->arg, p2->arg)); |
return(strcmp(p1->arg, p2->arg)); |
return (result); |
} |
|
return(result); |
|
} |
} |
|
|
/* |
/* |
* error_count() - return the number of errors currently logged. |
* error_count() - return the number of errors currently logged. |
*/ |
*/ |
|
|
int |
int |
error_count(void) |
error_count(void) |
{ |
{ |
return(errcnt); |
return (errcnt); |
} |
} |
|
|
/* |
/* |
* show_errors() - display on stdout the current log of errors. |
* show_errors() - display on stdout the current log of errors. |
*/ |
*/ |
|
|
void |
void |
show_errors(void) |
show_errors(void) |
{ |
{ |
int cnt = 0; |
struct errs *errp = errs; |
struct errs *errp = errs; |
int cnt = 0; |
|
|
printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); |
printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); |
while (cnt++ < errcnt) |
while (cnt++ < errcnt) { |
{ |
printf("%5s: %s\n", errp->arg, |
printf("%5s: %s\n", errp->arg, |
errp->errno == 0 ? "Not a number" : strerror(errp->errno)); |
errp->errno == 0 ? "Not a number" : strerror(errp->errno)); |
errp++; |
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 * |
char * |
kill_procs(char *str) |
kill_procs(char *str) |
{ |
{ |
char *nptr; |
int signum = SIGTERM, procnum, uid; |
int signum = SIGTERM; /* default */ |
struct sigdesc *sigp; |
int procnum; |
char *nptr; |
struct sigdesc *sigp; |
|
int uid; |
|
|
|
/* 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(*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"); |
{ |
|
return(" kill: no processes specified"); |
|
} |
|
|
|
if (isdigit(str[1])) |
if (isdigit(str[1])) { |
{ |
(void) scanint(str + 1, &signum); |
(void) scanint(str + 1, &signum); |
if (signum <= 0 || signum >= NSIG) |
if (signum <= 0 || signum >= NSIG) |
return (" invalid signal number"); |
{ |
} else { |
return(" invalid signal number"); |
/* translate the name into a number */ |
} |
for (sigp = sigdesc; sigp->name != NULL; sigp++) { |
|
if (strcmp(sigp->name, str + 1) == 0) { |
|
signum = sigp->number; |
|
break; |
|
} |
|
} |
|
|
|
/* was it ever found */ |
|
if (sigp->name == NULL) |
|
return (" bad signal name"); |
|
} |
|
/* put the new pointer in place */ |
|
str = nptr; |
} |
} |
else |
/* loop thru the string, killing processes */ |
{ |
do { |
/* translate the name into a number */ |
if (scanint(str, &procnum) == -1) { |
for (sigp = sigdesc; sigp->name != NULL; sigp++) |
ERROR(str, 0); |
{ |
} else { |
if (strcmp(sigp->name, str + 1) == 0) |
/* check process owner if we're not root */ |
{ |
if (uid && (uid != proc_owner(procnum))) { |
signum = sigp->number; |
ERROR(str, EACCES); |
break; |
} else if (kill(procnum, signum) == -1) { |
|
ERROR(str, errno); |
|
} |
} |
} |
} |
} while ((str = next_field(str)) != NULL); |
|
|
/* was it ever found */ |
/* return appropriate error string */ |
if (sigp->name == NULL) |
return (err_string()); |
{ |
|
return(" bad signal name"); |
|
} |
|
} |
|
/* put the new pointer in place */ |
|
str = nptr; |
|
} |
|
|
|
/* loop thru the string, killing processes */ |
|
do |
|
{ |
|
if (scanint(str, &procnum) == -1) |
|
{ |
|
ERROR(str, 0); |
|
} |
|
else |
|
{ |
|
/* check process owner if we're not root */ |
|
if (uid && (uid != proc_owner(procnum))) |
|
{ |
|
ERROR(str, EACCES); |
|
} |
|
/* go in for the kill */ |
|
else if (kill(procnum, signum) == -1) |
|
{ |
|
/* chalk up an error */ |
|
ERROR(str, errno); |
|
} |
|
} |
|
} while ((str = next_field(str)) != NULL); |
|
|
|
/* return appropriate error string */ |
|
return(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 * |
char * |
renice_procs(char *str) |
renice_procs(char *str) |
{ |
{ |
char negate; |
int prio, procnum, uid; |
int prio; |
char negate; |
int procnum; |
|
int uid; |
|
|
|
ERR_RESET; |
ERR_RESET; |
uid = getuid(); |
uid = getuid(); |
|
|
/* allow for negative priority values */ |
/* allow for negative priority values */ |
if ((negate = (*str == '-')) != 0) |
if ((negate = (*str == '-')) != 0) { |
{ |
/* move past the minus sign */ |
/* move past the minus sign */ |
str++; |
str++; |
} |
} |
/* use procnum as a temporary holding place and get the number */ |
|
procnum = scanint(str, &prio); |
|
|
/* use procnum as a temporary holding place and get the number */ |
/* negate if necessary */ |
procnum = scanint(str, &prio); |
if (negate) |
|
prio = -prio; |
|
|
/* negate if necessary */ |
|
if (negate) |
|
{ |
|
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"); |
return(" 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"); |
return(" no processes specified"); |
|
} |
|
|
|
/* 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 */ |
} |
else if (uid && (uid != proc_owner(procnum))) { |
|
ERROR(str, EACCES); |
|
} else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) { |
|
ERROR(str, errno); |
|
} |
|
} while ((str = next_field(str)) != NULL); |
|
|
/* check process owner if we're not root */ |
/* return appropriate error string */ |
else if (uid && (uid != proc_owner(procnum))) |
return (err_string()); |
{ |
|
ERROR(str, EACCES); |
|
} |
|
else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) |
|
{ |
|
ERROR(str, errno); |
|
} |
|
} while ((str = next_field(str)) != NULL); |
|
|
|
/* return appropriate error string */ |
|
return(err_string()); |
|
} |
} |