Annotation of src/usr.bin/top/commands.c, Revision 1.9
1.9 ! deraadt 1: /* $OpenBSD: commands.c,v 1.8 2003/06/12 22:30:23 pvalchev Exp $ */
1.1 downsj 2:
3: /*
4: * Top users/processes display for Unix
5: * Version 3
6: *
1.6 deraadt 7: * Copyright (c) 1984, 1989, William LeFebvre, Rice University
8: * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
1.1 downsj 9: *
1.6 deraadt 10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: *
19: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22: * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT,
23: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28: * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 downsj 29: */
30:
31: /*
32: * This file contains the routines that implement some of the interactive
33: * mode commands. Note that some of the commands are implemented in-line
34: * in "main". This is necessary because they change the global state of
35: * "top" (i.e.: changing the number of processes to display).
36: */
37:
1.2 downsj 38: #include <sys/types.h>
39: #include <stdio.h>
1.1 downsj 40: #include <ctype.h>
1.2 downsj 41: #include <errno.h>
42: #include <stdlib.h>
43: #include <string.h>
1.1 downsj 44: #include <signal.h>
1.2 downsj 45: #include <unistd.h>
1.1 downsj 46: #include <sys/time.h>
47: #include <sys/resource.h>
48:
1.2 downsj 49: #include "top.h"
50:
1.1 downsj 51: #include "sigdesc.h" /* generated automatically */
52: #include "boolean.h"
53: #include "utils.h"
1.2 downsj 54: #include "machine.h"
1.1 downsj 55:
1.9 ! deraadt 56: static char *next_field(char *);
! 57: static int scanint(char *, int *);
! 58: static char *err_string(void);
! 59: static size_t str_adderr(char *, size_t, int);
! 60: static size_t str_addarg(char *, size_t, char *, int);
! 61: static int err_compar(const void *, const void *);
1.1 downsj 62:
63: /*
64: * show_help() - display the help screen; invoked in response to
65: * either 'h' or '?'.
66: */
1.2 downsj 67: void
1.8 pvalchev 68: show_help(void)
1.1 downsj 69: {
1.9 ! deraadt 70: printf("Top version %s, %s\n", version_string(), copyright);
! 71: fputs("\n\n"
! 72: "A top users display for Unix\n"
! 73: "\n"
! 74: "These single-character commands are available:\n"
! 75: "\n"
! 76: "^L - redraw screen\n"
! 77: "q - quit\n"
! 78: "h or ? - help; show this text\n", stdout);
! 79:
! 80: /* not all commands are available with overstrike terminals */
! 81: if (overstrike) {
! 82: fputs("\n"
! 83: "Other commands are also available, but this terminal is not\n"
! 84: "sophisticated enough to handle those commands gracefully.\n\n",
! 85: stdout);
! 86: } else {
! 87: fputs(
! 88: "d - change number of displays to show\n"
! 89: "e - list errors generated by last \"kill\" or \"renice\" command\n"
! 90: "i - toggle the displaying of idle processes\n"
! 91: "I - same as 'i'\n"
! 92: "k - kill processes; send a signal to a list of processes\n"
! 93: "n or # - change number of processes to display\n", stdout);
1.1 downsj 94: #ifdef ORDER
1.9 ! deraadt 95: fputs(
! 96: "o - specify sort order (size, res, cpu, time)\n",
! 97: stdout);
1.1 downsj 98: #endif
1.9 ! deraadt 99: fputs(
! 100: "r - renice a process\n"
! 101: "s - change number of seconds to delay between updates\n"
! 102: "u - display processes for only one user (+ selects all users)\n"
! 103: "\n\n", stdout);
! 104: }
1.1 downsj 105: }
106:
107: /*
108: * Utility routines that help with some of the commands.
109: */
1.8 pvalchev 110: static char *
111: next_field(char *str)
1.1 downsj 112: {
1.9 ! deraadt 113: if ((str = strchr(str, ' ')) == NULL)
! 114: return (NULL);
! 115:
! 116: *str = '\0';
! 117: while (*++str == ' ') /* loop */
! 118: ;
! 119:
! 120: /* if there is nothing left of the string, return NULL */
! 121: /* This fix is dedicated to Greg Earle */
! 122: return (*str == '\0' ? NULL : str);
1.1 downsj 123: }
124:
1.9 ! deraadt 125: static int
! 126: scanint(char *str, int *intp)
1.1 downsj 127: {
1.9 ! deraadt 128: int val = 0;
! 129: char ch;
1.1 downsj 130:
1.9 ! deraadt 131: /* if there is nothing left of the string, flag it as an error */
! 132: /* This fix is dedicated to Greg Earle */
! 133: if (*str == '\0')
! 134: return (-1);
! 135:
! 136: while ((ch = *str++) != '\0') {
! 137: if (isdigit(ch))
! 138: val = val * 10 + (ch - '0');
! 139: else if (isspace(ch))
! 140: break;
! 141: else
! 142: return (-1);
1.1 downsj 143: }
1.9 ! deraadt 144: *intp = val;
! 145: return (0);
1.1 downsj 146: }
147:
148: /*
149: * Some of the commands make system calls that could generate errors.
150: * These errors are collected up in an array of structures for later
151: * contemplation and display. Such routines return a string containing an
152: * error message, or NULL if no errors occurred. The next few routines are
153: * for manipulating and displaying these errors. We need an upper limit on
154: * the number of errors, so we arbitrarily choose 20.
155: */
156:
157: #define ERRMAX 20
158:
1.9 ! deraadt 159: struct errs { /* structure for a system-call error */
! 160: int errno; /* value of errno (that is, the actual error) */
! 161: char *arg; /* argument that caused the error */
1.1 downsj 162: };
163:
164: static struct errs errs[ERRMAX];
1.9 ! deraadt 165: static int errcnt;
! 166: static char *err_toomany = " too many errors occurred";
! 167: static char *err_listem =
1.1 downsj 168: " Many errors occurred. Press `e' to display the list of errors.";
169:
170: /* These macros get used to reset and log the errors */
171: #define ERR_RESET errcnt = 0
1.9 ! deraadt 172: #define ERROR(p, e) \
! 173: if (errcnt >= ERRMAX) { \
! 174: return(err_toomany); \
! 175: } else { \
! 176: errs[errcnt].arg = (p); \
! 177: errs[errcnt++].errno = (e); \
! 178: }
! 179:
! 180: #define STRMAX 80
1.1 downsj 181:
182: /*
183: * err_string() - return an appropriate error string. This is what the
184: * command will return for displaying. If no errors were logged, then
185: * return NULL. The maximum length of the error string is defined by
186: * "STRMAX".
187: */
1.8 pvalchev 188: static char *
189: err_string(void)
1.1 downsj 190: {
1.9 ! deraadt 191: int cnt = 0, first = Yes, currerr = -1;
! 192: static char string[STRMAX];
! 193: struct errs *errp;
! 194:
! 195: /* if there are no errors, return NULL */
! 196: if (errcnt == 0)
! 197: return (NULL);
! 198:
! 199: /* sort the errors */
! 200: qsort((char *) errs, errcnt, sizeof(struct errs), err_compar);
! 201:
! 202: /* need a space at the front of the error string */
! 203: string[0] = ' ';
! 204: string[1] = '\0';
! 205:
! 206: /* loop thru the sorted list, building an error string */
! 207: while (cnt < errcnt) {
! 208: errp = &(errs[cnt++]);
! 209: if (errp->errno != currerr) {
! 210: if (currerr != -1) {
! 211: if (str_adderr(string, sizeof string, currerr) >
! 212: sizeof string - 2)
! 213: return (err_listem);
! 214:
! 215: /* we know there's more */
! 216: (void) strlcat(string, "; ", sizeof string);
! 217: }
! 218: currerr = errp->errno;
! 219: first = Yes;
1.1 downsj 220: }
1.9 ! deraadt 221: if (str_addarg(string, sizeof string, errp->arg, first) >=
! 222: sizeof string)
! 223: return (err_listem);
! 224:
! 225: first = No;
1.1 downsj 226: }
227:
1.9 ! deraadt 228: /* add final message */
! 229: if (str_adderr(string, sizeof string, currerr) >= sizeof string)
! 230: return (err_listem);
1.1 downsj 231:
1.9 ! deraadt 232: /* return the error string */
! 233: return (string);
1.1 downsj 234: }
235:
236: /*
237: * str_adderr(str, len, err) - add an explanation of error "err" to
238: * the string "str".
239: */
1.8 pvalchev 240: static size_t
241: str_adderr(char *str, size_t len, int err)
1.1 downsj 242: {
1.9 ! deraadt 243: size_t msglen;
! 244: char *msg;
1.1 downsj 245:
1.9 ! deraadt 246: msg = err == 0 ? "Not a number" : strerror(err);
1.8 pvalchev 247:
1.9 ! deraadt 248: if ((msglen = strlcat(str, ": ", len)) >= len)
! 249: return (msglen);
1.8 pvalchev 250:
1.9 ! deraadt 251: return (strlcat(str, msg, len));
1.1 downsj 252: }
253:
254: /*
255: * str_addarg(str, len, arg, first) - add the string argument "arg" to
256: * the string "str". This is the first in the group when "first"
257: * is set (indicating that a comma should NOT be added to the front).
258: */
1.8 pvalchev 259: static size_t
260: str_addarg(char *str, size_t len, char *arg, int first)
1.1 downsj 261: {
1.9 ! deraadt 262: size_t msglen;
1.1 downsj 263:
1.9 ! deraadt 264: if (!first) {
! 265: if ((msglen = strlcat(str, ", ", len)) >= len)
! 266: return (msglen);
! 267: }
! 268: return (strlcat(str, arg, len));
1.1 downsj 269: }
270:
271: /*
272: * err_compar(p1, p2) - comparison routine used by "qsort"
273: * for sorting errors.
274: */
1.8 pvalchev 275: static int
276: err_compar(const void *e1, const void *e2)
1.1 downsj 277: {
1.9 ! deraadt 278: const struct errs *p1 = (struct errs *) e1;
! 279: const struct errs *p2 = (struct errs *) e2;
! 280: int result;
! 281:
! 282: if ((result = p1->errno - p2->errno) == 0)
! 283: return (strcmp(p1->arg, p2->arg));
! 284: return (result);
1.1 downsj 285: }
286:
287: /*
288: * error_count() - return the number of errors currently logged.
289: */
1.8 pvalchev 290: int
291: error_count(void)
1.1 downsj 292: {
1.9 ! deraadt 293: return (errcnt);
1.1 downsj 294: }
295:
296: /*
297: * show_errors() - display on stdout the current log of errors.
298: */
1.8 pvalchev 299: void
300: show_errors(void)
1.1 downsj 301: {
1.9 ! deraadt 302: struct errs *errp = errs;
! 303: int cnt = 0;
1.1 downsj 304:
1.9 ! deraadt 305: printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
! 306: while (cnt++ < errcnt) {
! 307: printf("%5s: %s\n", errp->arg,
! 308: errp->errno == 0 ? "Not a number" : strerror(errp->errno));
! 309: errp++;
! 310: }
1.1 downsj 311: }
312:
313: /*
314: * kill_procs(str) - send signals to processes, much like the "kill"
315: * command does; invoked in response to 'k'.
316: */
1.8 pvalchev 317: char *
318: kill_procs(char *str)
1.1 downsj 319: {
1.9 ! deraadt 320: int signum = SIGTERM, procnum, uid;
! 321: struct sigdesc *sigp;
! 322: char *nptr;
! 323:
! 324: /* reset error array */
! 325: ERR_RESET;
! 326:
! 327: /* remember our uid */
! 328: uid = getuid();
! 329:
! 330: /* skip over leading white space */
! 331: while (isspace(*str))
! 332: str++;
! 333:
! 334: if (str[0] == '-') {
! 335: /* explicit signal specified */
! 336: if ((nptr = next_field(str)) == NULL)
! 337: return (" kill: no processes specified");
! 338:
! 339: if (isdigit(str[1])) {
! 340: (void) scanint(str + 1, &signum);
! 341: if (signum <= 0 || signum >= NSIG)
! 342: return (" invalid signal number");
! 343: } else {
! 344: /* translate the name into a number */
! 345: for (sigp = sigdesc; sigp->name != NULL; sigp++) {
! 346: if (strcmp(sigp->name, str + 1) == 0) {
! 347: signum = sigp->number;
! 348: break;
! 349: }
! 350: }
! 351:
! 352: /* was it ever found */
! 353: if (sigp->name == NULL)
! 354: return (" bad signal name");
! 355: }
! 356: /* put the new pointer in place */
! 357: str = nptr;
1.1 downsj 358: }
1.9 ! deraadt 359: /* loop thru the string, killing processes */
! 360: do {
! 361: if (scanint(str, &procnum) == -1) {
! 362: ERROR(str, 0);
! 363: } else {
! 364: /* check process owner if we're not root */
! 365: if (uid && (uid != proc_owner(procnum))) {
! 366: ERROR(str, EACCES);
! 367: } else if (kill(procnum, signum) == -1) {
! 368: ERROR(str, errno);
! 369: }
1.1 downsj 370: }
1.9 ! deraadt 371: } while ((str = next_field(str)) != NULL);
1.1 downsj 372:
1.9 ! deraadt 373: /* return appropriate error string */
! 374: return (err_string());
1.1 downsj 375: }
376:
377: /*
378: * renice_procs(str) - change the "nice" of processes, much like the
379: * "renice" command does; invoked in response to 'r'.
380: */
1.8 pvalchev 381: char *
382: renice_procs(char *str)
1.1 downsj 383: {
1.9 ! deraadt 384: int prio, procnum, uid;
! 385: char negate;
! 386:
! 387: ERR_RESET;
! 388: uid = getuid();
! 389:
! 390: /* allow for negative priority values */
! 391: if ((negate = (*str == '-')) != 0) {
! 392: /* move past the minus sign */
! 393: str++;
! 394: }
! 395: /* use procnum as a temporary holding place and get the number */
! 396: procnum = scanint(str, &prio);
! 397:
! 398: /* negate if necessary */
! 399: if (negate)
! 400: prio = -prio;
1.1 downsj 401:
402: #if defined(PRIO_MIN) && defined(PRIO_MAX)
1.9 ! deraadt 403: /* check for validity */
! 404: if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
! 405: return (" bad priority value");
1.1 downsj 406: #endif
407:
1.9 ! deraadt 408: /* move to the first process number */
! 409: if ((str = next_field(str)) == NULL)
! 410: return (" no processes specified");
! 411:
! 412: /* loop thru the process numbers, renicing each one */
! 413: do {
! 414: if (scanint(str, &procnum) == -1) {
! 415: ERROR(str, 0);
! 416: }
! 417: /* check process owner if we're not root */
! 418: else if (uid && (uid != proc_owner(procnum))) {
! 419: ERROR(str, EACCES);
! 420: } else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) {
! 421: ERROR(str, errno);
! 422: }
! 423: } while ((str = next_field(str)) != NULL);
1.1 downsj 424:
1.9 ! deraadt 425: /* return appropriate error string */
! 426: return (err_string());
1.1 downsj 427: }