Annotation of src/usr.bin/top/commands.c, Revision 1.24
1.24 ! jmc 1: /* $OpenBSD: commands.c,v 1.23 2007/02/06 06:53:13 otto 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);
1.23 otto 71: puts("These single-character commands are available:\n"
1.9 deraadt 72: "\n"
1.24 ! jmc 73: "h | ? - help; show this text\n"
! 74: "^L - redraw screen\n"
! 75: "q - quit");
1.9 deraadt 76:
77: /* not all commands are available with overstrike terminals */
78: if (overstrike) {
1.21 otto 79: puts("\n"
1.9 deraadt 80: "Other commands are also available, but this terminal is not\n"
1.21 otto 81: "sophisticated enough to handle those commands gracefully.\n");
1.9 deraadt 82: } else {
1.21 otto 83: puts(
1.24 ! jmc 84: "+ - reset any g, p, or u filters\n"
! 85: "C - toggle the display of command line arguments\n"
! 86: "d n - show number `n' displays\n"
! 87: "e - list errors generated by last \"kill\" or \"renice\" command\n"
! 88: "g string - filter on command name (g+ selects all commands)\n"
! 89: "I | i - toggle the display of idle processes\n"
! 90: "k - send a signal to a list of processes\n"
! 91: "n|# n - show number `n' processes\n"
! 92: "o field - specify sort order (size, res, cpu, time, pri)\n"
! 93: "p pid - display process by pid (p+ selects all processes)\n"
! 94: "r n pid - renice process `pid' to nice value `n'\n"
! 95: "S - toggle the display of system processes\n"
! 96: "s n - change delay between displays to `n' seconds\n"
! 97: "T - toggle the display of threads\n"
! 98: "u user - display processes for `user' (u+ selects all users)\n"
1.21 otto 99: "\n");
1.9 deraadt 100: }
1.1 downsj 101: }
102:
103: /*
104: * Utility routines that help with some of the commands.
105: */
1.8 pvalchev 106: static char *
107: next_field(char *str)
1.1 downsj 108: {
1.9 deraadt 109: if ((str = strchr(str, ' ')) == NULL)
110: return (NULL);
111:
112: *str = '\0';
113: while (*++str == ' ') /* loop */
114: ;
115:
116: /* if there is nothing left of the string, return NULL */
117: /* This fix is dedicated to Greg Earle */
118: return (*str == '\0' ? NULL : str);
1.1 downsj 119: }
120:
1.14 deraadt 121: static int
1.9 deraadt 122: scanint(char *str, int *intp)
1.1 downsj 123: {
1.9 deraadt 124: int val = 0;
125: char ch;
1.1 downsj 126:
1.9 deraadt 127: /* if there is nothing left of the string, flag it as an error */
128: /* This fix is dedicated to Greg Earle */
129: if (*str == '\0')
130: return (-1);
131:
132: while ((ch = *str++) != '\0') {
133: if (isdigit(ch))
134: val = val * 10 + (ch - '0');
135: else if (isspace(ch))
136: break;
137: else
138: return (-1);
1.1 downsj 139: }
1.9 deraadt 140: *intp = val;
141: return (0);
1.1 downsj 142: }
143:
144: /*
145: * Some of the commands make system calls that could generate errors.
146: * These errors are collected up in an array of structures for later
147: * contemplation and display. Such routines return a string containing an
148: * error message, or NULL if no errors occurred. The next few routines are
149: * for manipulating and displaying these errors. We need an upper limit on
150: * the number of errors, so we arbitrarily choose 20.
151: */
152:
153: #define ERRMAX 20
154:
1.9 deraadt 155: struct errs { /* structure for a system-call error */
1.15 marc 156: int err; /* value of errno (that is, the actual error) */
1.9 deraadt 157: char *arg; /* argument that caused the error */
1.1 downsj 158: };
159:
160: static struct errs errs[ERRMAX];
1.9 deraadt 161: static int errcnt;
162: static char *err_toomany = " too many errors occurred";
163: static char *err_listem =
1.1 downsj 164: " Many errors occurred. Press `e' to display the list of errors.";
165:
166: /* These macros get used to reset and log the errors */
167: #define ERR_RESET errcnt = 0
1.9 deraadt 168: #define ERROR(p, e) \
169: if (errcnt >= ERRMAX) { \
170: return(err_toomany); \
171: } else { \
172: errs[errcnt].arg = (p); \
1.15 marc 173: errs[errcnt++].err = (e); \
1.9 deraadt 174: }
175:
176: #define STRMAX 80
1.1 downsj 177:
178: /*
179: * err_string() - return an appropriate error string. This is what the
180: * command will return for displaying. If no errors were logged, then
181: * return NULL. The maximum length of the error string is defined by
182: * "STRMAX".
183: */
1.8 pvalchev 184: static char *
185: err_string(void)
1.1 downsj 186: {
1.9 deraadt 187: int cnt = 0, first = Yes, currerr = -1;
188: static char string[STRMAX];
189: struct errs *errp;
190:
191: /* if there are no errors, return NULL */
192: if (errcnt == 0)
193: return (NULL);
194:
195: /* sort the errors */
1.12 deraadt 196: qsort(errs, errcnt, sizeof(struct errs), err_compar);
1.9 deraadt 197:
198: /* need a space at the front of the error string */
199: string[0] = ' ';
200: string[1] = '\0';
201:
202: /* loop thru the sorted list, building an error string */
203: while (cnt < errcnt) {
204: errp = &(errs[cnt++]);
1.15 marc 205: if (errp->err != currerr) {
1.9 deraadt 206: if (currerr != -1) {
207: if (str_adderr(string, sizeof string, currerr) >
208: sizeof string - 2)
209: return (err_listem);
210:
211: /* we know there's more */
212: (void) strlcat(string, "; ", sizeof string);
213: }
1.15 marc 214: currerr = errp->err;
1.9 deraadt 215: first = Yes;
1.1 downsj 216: }
1.9 deraadt 217: if (str_addarg(string, sizeof string, errp->arg, first) >=
218: sizeof string)
219: return (err_listem);
220:
221: first = No;
1.1 downsj 222: }
223:
1.9 deraadt 224: /* add final message */
225: if (str_adderr(string, sizeof string, currerr) >= sizeof string)
226: return (err_listem);
1.1 downsj 227:
1.9 deraadt 228: /* return the error string */
229: return (string);
1.1 downsj 230: }
231:
232: /*
233: * str_adderr(str, len, err) - add an explanation of error "err" to
234: * the string "str".
235: */
1.8 pvalchev 236: static size_t
237: str_adderr(char *str, size_t len, int err)
1.1 downsj 238: {
1.9 deraadt 239: size_t msglen;
240: char *msg;
1.1 downsj 241:
1.9 deraadt 242: msg = err == 0 ? "Not a number" : strerror(err);
1.8 pvalchev 243:
1.9 deraadt 244: if ((msglen = strlcat(str, ": ", len)) >= len)
245: return (msglen);
1.8 pvalchev 246:
1.9 deraadt 247: return (strlcat(str, msg, len));
1.1 downsj 248: }
249:
250: /*
251: * str_addarg(str, len, arg, first) - add the string argument "arg" to
252: * the string "str". This is the first in the group when "first"
253: * is set (indicating that a comma should NOT be added to the front).
254: */
1.8 pvalchev 255: static size_t
256: str_addarg(char *str, size_t len, char *arg, int first)
1.1 downsj 257: {
1.9 deraadt 258: size_t msglen;
1.1 downsj 259:
1.9 deraadt 260: if (!first) {
261: if ((msglen = strlcat(str, ", ", len)) >= len)
262: return (msglen);
263: }
264: return (strlcat(str, arg, len));
1.1 downsj 265: }
266:
267: /*
268: * err_compar(p1, p2) - comparison routine used by "qsort"
269: * for sorting errors.
270: */
1.8 pvalchev 271: static int
272: err_compar(const void *e1, const void *e2)
1.1 downsj 273: {
1.9 deraadt 274: const struct errs *p1 = (struct errs *) e1;
275: const struct errs *p2 = (struct errs *) e2;
276: int result;
277:
1.15 marc 278: if ((result = p1->err - p2->err) == 0)
1.9 deraadt 279: return (strcmp(p1->arg, p2->arg));
280: return (result);
1.1 downsj 281: }
282:
283: /*
284: * error_count() - return the number of errors currently logged.
285: */
1.8 pvalchev 286: int
287: error_count(void)
1.1 downsj 288: {
1.9 deraadt 289: return (errcnt);
1.1 downsj 290: }
291:
292: /*
293: * show_errors() - display on stdout the current log of errors.
294: */
1.8 pvalchev 295: void
296: show_errors(void)
1.1 downsj 297: {
1.9 deraadt 298: struct errs *errp = errs;
299: int cnt = 0;
1.1 downsj 300:
1.9 deraadt 301: printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
302: while (cnt++ < errcnt) {
303: printf("%5s: %s\n", errp->arg,
1.15 marc 304: errp->err == 0 ? "Not a number" : strerror(errp->err));
1.9 deraadt 305: errp++;
306: }
1.1 downsj 307: }
308:
309: /*
310: * kill_procs(str) - send signals to processes, much like the "kill"
311: * command does; invoked in response to 'k'.
312: */
1.8 pvalchev 313: char *
314: kill_procs(char *str)
1.1 downsj 315: {
1.10 millert 316: int signum = SIGTERM, procnum;
1.9 deraadt 317: struct sigdesc *sigp;
1.11 jfb 318: uid_t uid, puid;
1.9 deraadt 319: char *nptr;
320:
321: /* reset error array */
322: ERR_RESET;
323:
324: /* remember our uid */
325: uid = getuid();
326:
327: /* skip over leading white space */
328: while (isspace(*str))
329: str++;
330:
331: if (str[0] == '-') {
332: /* explicit signal specified */
333: if ((nptr = next_field(str)) == NULL)
334: return (" kill: no processes specified");
335:
336: if (isdigit(str[1])) {
337: (void) scanint(str + 1, &signum);
338: if (signum <= 0 || signum >= NSIG)
339: return (" invalid signal number");
340: } else {
341: /* translate the name into a number */
342: for (sigp = sigdesc; sigp->name != NULL; sigp++) {
343: if (strcmp(sigp->name, str + 1) == 0) {
344: signum = sigp->number;
345: break;
346: }
347: }
348:
349: /* was it ever found */
350: if (sigp->name == NULL)
351: return (" bad signal name");
352: }
353: /* put the new pointer in place */
354: str = nptr;
1.1 downsj 355: }
1.9 deraadt 356: /* loop thru the string, killing processes */
357: do {
358: if (scanint(str, &procnum) == -1) {
359: ERROR(str, 0);
360: } else {
361: /* check process owner if we're not root */
1.11 jfb 362: puid = proc_owner(procnum);
363: if (puid == (uid_t)(-1)) {
364: ERROR(str, ESRCH);
365: } else if (uid && (uid != puid)) {
1.9 deraadt 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.10 millert 384: uid_t uid;
1.9 deraadt 385: char negate;
1.10 millert 386: int prio, procnum;
1.9 deraadt 387:
388: ERR_RESET;
389: uid = getuid();
390:
391: /* allow for negative priority values */
392: if ((negate = (*str == '-')) != 0) {
393: /* move past the minus sign */
394: str++;
395: }
396: /* use procnum as a temporary holding place and get the number */
397: procnum = scanint(str, &prio);
398:
399: /* negate if necessary */
400: if (negate)
401: prio = -prio;
1.1 downsj 402:
403: #if defined(PRIO_MIN) && defined(PRIO_MAX)
1.9 deraadt 404: /* check for validity */
405: if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
406: return (" bad priority value");
1.1 downsj 407: #endif
408:
1.9 deraadt 409: /* move to the first process number */
410: if ((str = next_field(str)) == NULL)
411: return (" no processes specified");
412:
413: /* loop thru the process numbers, renicing each one */
414: do {
415: if (scanint(str, &procnum) == -1) {
416: ERROR(str, 0);
417: }
418: /* check process owner if we're not root */
419: else if (uid && (uid != proc_owner(procnum))) {
420: ERROR(str, EACCES);
421: } else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) {
422: ERROR(str, errno);
423: }
424: } while ((str = next_field(str)) != NULL);
1.1 downsj 425:
1.9 deraadt 426: /* return appropriate error string */
427: return (err_string());
1.1 downsj 428: }