Annotation of src/usr.bin/top/commands.c, Revision 1.33
1.33 ! kn 1: /* $OpenBSD: commands.c,v 1.32 2017/03/15 04:24:14 deraadt 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.29 lum 40: #include <err.h>
1.1 downsj 41: #include <ctype.h>
1.2 downsj 42: #include <errno.h>
43: #include <stdlib.h>
44: #include <string.h>
1.1 downsj 45: #include <signal.h>
1.2 downsj 46: #include <unistd.h>
1.1 downsj 47: #include <sys/time.h>
48: #include <sys/resource.h>
1.33 ! kn 49: #include <stdbool.h>
1.1 downsj 50:
1.2 downsj 51: #include "top.h"
52:
1.1 downsj 53: #include "utils.h"
1.2 downsj 54: #include "machine.h"
1.1 downsj 55:
1.9 deraadt 56: static char *next_field(char *);
1.29 lum 57: static int scan_arg(char *, int *, char *);
1.9 deraadt 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: * Utility routines that help with some of the commands.
65: */
1.8 pvalchev 66: static char *
67: next_field(char *str)
1.1 downsj 68: {
1.30 lum 69: char *spaces = " \t";
70: size_t span;
71:
72: span = strcspn(str, spaces);
1.32 deraadt 73: if (span == strlen(str))
1.30 lum 74: return (NULL);
75:
76: str += span;
77: *str++ = '\0';
78:
1.32 deraadt 79: while (strcspn(str, spaces) == 0)
1.30 lum 80: str++;
81:
82: if (*str == '\0')
1.9 deraadt 83: return (NULL);
84:
1.30 lum 85: return(str);
1.1 downsj 86: }
87:
1.29 lum 88: /*
89: * Scan the renice or kill interactive arguments for data and/or errors.
90: */
1.14 deraadt 91: static int
1.29 lum 92: scan_arg(char *str, int *intp, char *nptr)
1.1 downsj 93: {
1.29 lum 94: int val = 0, bad_flag = 0;
1.9 deraadt 95: char ch;
1.1 downsj 96:
1.29 lum 97: *nptr = '\0';
98:
1.9 deraadt 99: if (*str == '\0')
100: return (-1);
101:
102: while ((ch = *str++) != '\0') {
1.31 deraadt 103: if (isspace((unsigned char)ch))
1.9 deraadt 104: break;
1.31 deraadt 105: else if (!isdigit((unsigned char)ch))
1.29 lum 106: bad_flag = 1;
1.9 deraadt 107: else
1.29 lum 108: val = val * 10 + (ch - '0');
109:
110: *(nptr++) = ch;
1.1 downsj 111: }
1.29 lum 112: *nptr = '\0';
113:
114: if (bad_flag == 1)
115: return(-1);
116:
1.9 deraadt 117: *intp = val;
118: return (0);
1.1 downsj 119: }
120:
121: /*
122: * Some of the commands make system calls that could generate errors.
123: * These errors are collected up in an array of structures for later
124: * contemplation and display. Such routines return a string containing an
125: * error message, or NULL if no errors occurred. The next few routines are
126: * for manipulating and displaying these errors. We need an upper limit on
127: * the number of errors, so we arbitrarily choose 20.
128: */
129:
130: #define ERRMAX 20
131:
1.28 otto 132: struct errs errs[ERRMAX];
133: int errcnt;
1.9 deraadt 134: static char *err_toomany = " too many errors occurred";
135: static char *err_listem =
1.1 downsj 136: " Many errors occurred. Press `e' to display the list of errors.";
137:
138: /* These macros get used to reset and log the errors */
139: #define ERR_RESET errcnt = 0
1.9 deraadt 140: #define ERROR(p, e) \
141: if (errcnt >= ERRMAX) { \
142: return(err_toomany); \
143: } else { \
1.29 lum 144: free(errs[errcnt].arg); \
145: if ((errs[errcnt].arg = strdup(p)) == NULL) \
146: err(1, "strdup"); \
1.15 marc 147: errs[errcnt++].err = (e); \
1.9 deraadt 148: }
149:
150: #define STRMAX 80
1.1 downsj 151:
152: /*
153: * err_string() - return an appropriate error string. This is what the
154: * command will return for displaying. If no errors were logged, then
155: * return NULL. The maximum length of the error string is defined by
156: * "STRMAX".
157: */
1.8 pvalchev 158: static char *
159: err_string(void)
1.1 downsj 160: {
1.33 ! kn 161: int cnt = 0, first = true, currerr = -1;
1.9 deraadt 162: static char string[STRMAX];
163: struct errs *errp;
164:
165: /* if there are no errors, return NULL */
166: if (errcnt == 0)
167: return (NULL);
168:
169: /* sort the errors */
1.12 deraadt 170: qsort(errs, errcnt, sizeof(struct errs), err_compar);
1.9 deraadt 171:
172: /* need a space at the front of the error string */
173: string[0] = ' ';
174: string[1] = '\0';
175:
176: /* loop thru the sorted list, building an error string */
177: while (cnt < errcnt) {
178: errp = &(errs[cnt++]);
1.15 marc 179: if (errp->err != currerr) {
1.9 deraadt 180: if (currerr != -1) {
181: if (str_adderr(string, sizeof string, currerr) >
182: sizeof string - 2)
183: return (err_listem);
184:
185: /* we know there's more */
186: (void) strlcat(string, "; ", sizeof string);
187: }
1.15 marc 188: currerr = errp->err;
1.33 ! kn 189: first = true;
1.1 downsj 190: }
1.9 deraadt 191: if (str_addarg(string, sizeof string, errp->arg, first) >=
192: sizeof string)
193: return (err_listem);
194:
1.33 ! kn 195: first = false;
1.1 downsj 196: }
197:
1.9 deraadt 198: /* add final message */
199: if (str_adderr(string, sizeof string, currerr) >= sizeof string)
200: return (err_listem);
1.1 downsj 201:
1.9 deraadt 202: /* return the error string */
203: return (string);
1.1 downsj 204: }
205:
206: /*
207: * str_adderr(str, len, err) - add an explanation of error "err" to
208: * the string "str".
209: */
1.8 pvalchev 210: static size_t
211: str_adderr(char *str, size_t len, int err)
1.1 downsj 212: {
1.9 deraadt 213: size_t msglen;
214: char *msg;
1.1 downsj 215:
1.9 deraadt 216: msg = err == 0 ? "Not a number" : strerror(err);
1.8 pvalchev 217:
1.9 deraadt 218: if ((msglen = strlcat(str, ": ", len)) >= len)
219: return (msglen);
1.8 pvalchev 220:
1.9 deraadt 221: return (strlcat(str, msg, len));
1.1 downsj 222: }
223:
224: /*
225: * str_addarg(str, len, arg, first) - add the string argument "arg" to
226: * the string "str". This is the first in the group when "first"
227: * is set (indicating that a comma should NOT be added to the front).
228: */
1.8 pvalchev 229: static size_t
230: str_addarg(char *str, size_t len, char *arg, int first)
1.1 downsj 231: {
1.9 deraadt 232: size_t msglen;
1.1 downsj 233:
1.9 deraadt 234: if (!first) {
235: if ((msglen = strlcat(str, ", ", len)) >= len)
236: return (msglen);
237: }
238: return (strlcat(str, arg, len));
1.1 downsj 239: }
240:
241: /*
242: * err_compar(p1, p2) - comparison routine used by "qsort"
243: * for sorting errors.
244: */
1.8 pvalchev 245: static int
246: err_compar(const void *e1, const void *e2)
1.1 downsj 247: {
1.28 otto 248: const struct errs *p1 = (const struct errs *) e1;
249: const struct errs *p2 = (const struct errs *) e2;
1.9 deraadt 250: int result;
251:
1.15 marc 252: if ((result = p1->err - p2->err) == 0)
1.9 deraadt 253: return (strcmp(p1->arg, p2->arg));
254: return (result);
1.1 downsj 255: }
256:
257: /*
258: * error_count() - return the number of errors currently logged.
259: */
1.8 pvalchev 260: int
261: error_count(void)
1.1 downsj 262: {
1.9 deraadt 263: return (errcnt);
1.1 downsj 264: }
265:
266: /*
267: * kill_procs(str) - send signals to processes, much like the "kill"
268: * command does; invoked in response to 'k'.
269: */
1.8 pvalchev 270: char *
271: kill_procs(char *str)
1.1 downsj 272: {
1.10 millert 273: int signum = SIGTERM, procnum;
1.11 jfb 274: uid_t uid, puid;
1.29 lum 275: char tempbuf[TEMPBUFSIZE];
1.30 lum 276: char *nptr, *tmp;
277:
278: tmp = tempbuf;
1.9 deraadt 279:
280: /* reset error array */
281: ERR_RESET;
282:
283: /* remember our uid */
284: uid = getuid();
285:
286: /* skip over leading white space */
1.31 deraadt 287: while (isspace((unsigned char)*str))
1.9 deraadt 288: str++;
289:
1.30 lum 290: if (*str == '-') {
291: str++;
292:
1.9 deraadt 293: /* explicit signal specified */
294: if ((nptr = next_field(str)) == NULL)
295: return (" kill: no processes specified");
296:
1.31 deraadt 297: if (isdigit((unsigned char)*str)) {
1.30 lum 298: (void) scan_arg(str, &signum, tmp);
1.9 deraadt 299: if (signum <= 0 || signum >= NSIG)
300: return (" invalid signal number");
301: } else {
302: /* translate the name into a number */
1.27 millert 303: for (signum = 0; signum < NSIG; signum++) {
1.32 deraadt 304: if (strcasecmp(sys_signame[signum],
1.30 lum 305: str) == 0)
1.9 deraadt 306: break;
307: }
308:
309: /* was it ever found */
1.27 millert 310: if (signum == NSIG)
1.9 deraadt 311: return (" bad signal name");
312: }
313: /* put the new pointer in place */
314: str = nptr;
1.1 downsj 315: }
1.29 lum 316: nptr = tempbuf;
1.9 deraadt 317: /* loop thru the string, killing processes */
318: do {
1.29 lum 319: if (scan_arg(str, &procnum, nptr) == -1) {
320: ERROR(nptr, 0);
1.9 deraadt 321: } else {
322: /* check process owner if we're not root */
1.11 jfb 323: puid = proc_owner(procnum);
324: if (puid == (uid_t)(-1)) {
1.29 lum 325: ERROR(nptr, ESRCH);
1.11 jfb 326: } else if (uid && (uid != puid)) {
1.29 lum 327: ERROR(nptr, EACCES);
1.9 deraadt 328: } else if (kill(procnum, signum) == -1) {
1.29 lum 329: ERROR(nptr, errno);
1.9 deraadt 330: }
1.1 downsj 331: }
1.9 deraadt 332: } while ((str = next_field(str)) != NULL);
1.1 downsj 333:
1.9 deraadt 334: /* return appropriate error string */
335: return (err_string());
1.1 downsj 336: }
337:
338: /*
339: * renice_procs(str) - change the "nice" of processes, much like the
340: * "renice" command does; invoked in response to 'r'.
341: */
1.8 pvalchev 342: char *
343: renice_procs(char *str)
1.1 downsj 344: {
1.10 millert 345: uid_t uid;
1.9 deraadt 346: char negate;
1.10 millert 347: int prio, procnum;
1.29 lum 348: char tempbuf[TEMPBUFSIZE];
349: char *nptr;
1.9 deraadt 350:
351: ERR_RESET;
352: uid = getuid();
353:
1.29 lum 354: /* skip over leading white space */
1.31 deraadt 355: while (isspace((unsigned char)*str))
1.29 lum 356: str++;
357:
1.9 deraadt 358: /* allow for negative priority values */
359: if ((negate = (*str == '-')) != 0) {
360: /* move past the minus sign */
361: str++;
362: }
1.29 lum 363:
364: nptr = tempbuf;
1.9 deraadt 365: /* use procnum as a temporary holding place and get the number */
1.29 lum 366: procnum = scan_arg(str, &prio, nptr);
1.9 deraadt 367:
368: /* negate if necessary */
369: if (negate)
370: prio = -prio;
1.1 downsj 371:
372: #if defined(PRIO_MIN) && defined(PRIO_MAX)
1.9 deraadt 373: /* check for validity */
374: if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
375: return (" bad priority value");
1.1 downsj 376: #endif
377:
1.9 deraadt 378: /* move to the first process number */
379: if ((str = next_field(str)) == NULL)
380: return (" no processes specified");
381:
382: /* loop thru the process numbers, renicing each one */
383: do {
1.29 lum 384: if (scan_arg(str, &procnum, nptr) == -1) {
385: ERROR(nptr, 0);
1.9 deraadt 386: }
387: /* check process owner if we're not root */
388: else if (uid && (uid != proc_owner(procnum))) {
1.29 lum 389: ERROR(nptr, EACCES);
1.9 deraadt 390: } else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) {
1.29 lum 391: ERROR(nptr, errno);
1.9 deraadt 392: }
393: } while ((str = next_field(str)) != NULL);
1.1 downsj 394:
1.9 deraadt 395: /* return appropriate error string */
396: return (err_string());
1.1 downsj 397: }