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