Annotation of src/usr.bin/top/commands.c, Revision 1.6
1.6 ! deraadt 1: /* $OpenBSD: commands.c,v 1.5 2002/05/29 18:33:40 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.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.4 millert 56: static char *next_field(char *);
57: static int scanint(char *, int *);
58: static char *err_string(void);
59: static int str_adderr(char *, int, int);
60: static int str_addarg(char *, int, 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: */
67:
1.2 downsj 68: void
1.1 downsj 69: show_help()
70:
71: {
72: printf("Top version %s, %s\n", version_string(), copyright);
73: fputs("\n\n\
74: A top users display for Unix\n\
75: \n\
76: These single-character commands are available:\n\
77: \n\
78: ^L - redraw screen\n\
79: q - quit\n\
80: h or ? - help; show this text\n", stdout);
81:
82: /* not all commands are availalbe with overstrike terminals */
83: if (overstrike)
84: {
85: fputs("\n\
86: Other commands are also available, but this terminal is not\n\
87: sophisticated enough to handle those commands gracefully.\n\n", stdout);
88: }
89: else
90: {
91: fputs("\
92: d - change number of displays to show\n\
93: e - list errors generated by last \"kill\" or \"renice\" command\n\
94: i - toggle the displaying of idle processes\n\
95: I - same as 'i'\n\
96: k - kill processes; send a signal to a list of processes\n\
97: n or # - change number of processes to display\n", stdout);
98: #ifdef ORDER
99: fputs("\
100: o - specify sort order (size, res, cpu, time)\n", stdout);
101: #endif
102: fputs("\
103: r - renice a process\n\
104: s - change number of seconds to delay between updates\n\
105: u - display processes for only one user (+ selects all users)\n\
106: \n\
107: \n", stdout);
108: }
109: }
110:
111: /*
112: * Utility routines that help with some of the commands.
113: */
114:
1.2 downsj 115: static char *next_field(str)
1.1 downsj 116:
1.3 mpech 117: char *str;
1.1 downsj 118:
119: {
120: if ((str = strchr(str, ' ')) == NULL)
121: {
122: return(NULL);
123: }
124: *str = '\0';
125: while (*++str == ' ') /* loop */;
126:
127: /* if there is nothing left of the string, return NULL */
128: /* This fix is dedicated to Greg Earle */
129: return(*str == '\0' ? NULL : str);
130: }
131:
1.2 downsj 132: static int scanint(str, intp)
1.1 downsj 133:
134: char *str;
135: int *intp;
136:
137: {
1.3 mpech 138: int val = 0;
139: char ch;
1.1 downsj 140:
141: /* if there is nothing left of the string, flag it as an error */
142: /* This fix is dedicated to Greg Earle */
143: if (*str == '\0')
144: {
145: return(-1);
146: }
147:
148: while ((ch = *str++) != '\0')
149: {
150: if (isdigit(ch))
151: {
152: val = val * 10 + (ch - '0');
153: }
154: else if (isspace(ch))
155: {
156: break;
157: }
158: else
159: {
160: return(-1);
161: }
162: }
163: *intp = val;
164: return(0);
165: }
166:
167: /*
168: * Some of the commands make system calls that could generate errors.
169: * These errors are collected up in an array of structures for later
170: * contemplation and display. Such routines return a string containing an
171: * error message, or NULL if no errors occurred. The next few routines are
172: * for manipulating and displaying these errors. We need an upper limit on
173: * the number of errors, so we arbitrarily choose 20.
174: */
175:
176: #define ERRMAX 20
177:
178: struct errs /* structure for a system-call error */
179: {
180: int errno; /* value of errno (that is, the actual error) */
181: char *arg; /* argument that caused the error */
182: };
183:
184: static struct errs errs[ERRMAX];
185: static int errcnt;
186: static char *err_toomany = " too many errors occurred";
187: static char *err_listem =
188: " Many errors occurred. Press `e' to display the list of errors.";
189:
190: /* These macros get used to reset and log the errors */
191: #define ERR_RESET errcnt = 0
192: #define ERROR(p, e) if (errcnt >= ERRMAX) \
193: { \
194: return(err_toomany); \
195: } \
196: else \
197: { \
198: errs[errcnt].arg = (p); \
199: errs[errcnt++].errno = (e); \
200: }
201:
202: /*
203: * err_string() - return an appropriate error string. This is what the
204: * command will return for displaying. If no errors were logged, then
205: * return NULL. The maximum length of the error string is defined by
206: * "STRMAX".
207: */
208:
209: #define STRMAX 80
210:
1.2 downsj 211: static char *err_string()
1.1 downsj 212:
213: {
1.3 mpech 214: struct errs *errp;
215: int cnt = 0;
216: int first = Yes;
217: int currerr = -1;
1.1 downsj 218: int stringlen; /* characters still available in "string" */
219: static char string[STRMAX];
220:
221: /* if there are no errors, return NULL */
222: if (errcnt == 0)
223: {
224: return(NULL);
225: }
226:
227: /* sort the errors */
228: qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
229:
230: /* need a space at the front of the error string */
231: string[0] = ' ';
232: string[1] = '\0';
233: stringlen = STRMAX - 2;
234:
235: /* loop thru the sorted list, building an error string */
236: while (cnt < errcnt)
237: {
238: errp = &(errs[cnt++]);
239: if (errp->errno != currerr)
240: {
241: if (currerr != -1)
242: {
243: if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
244: {
245: return(err_listem);
246: }
1.5 deraadt 247: /* we know there's more */
248: (void) strlcat(string, "; ", sizeof string);
1.1 downsj 249: }
250: currerr = errp->errno;
251: first = Yes;
252: }
253: if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
254: {
255: return(err_listem);
256: }
257: first = No;
258: }
259:
260: /* add final message */
261: stringlen = str_adderr(string, stringlen, currerr);
262:
263: /* return the error string */
264: return(stringlen == 0 ? err_listem : string);
265: }
266:
267: /*
268: * str_adderr(str, len, err) - add an explanation of error "err" to
269: * the string "str".
270: */
271:
1.2 downsj 272: static int str_adderr(str, len, err)
1.1 downsj 273:
274: char *str;
275: int len;
276: int err;
277:
278: {
1.3 mpech 279: char *msg;
280: int msglen;
1.1 downsj 281:
1.2 downsj 282: msg = err == 0 ? "Not a number" : strerror(err);
1.1 downsj 283: msglen = strlen(msg) + 2;
284: if (len <= msglen)
285: {
286: return(0);
287: }
288: (void) strcat(str, ": ");
289: (void) strcat(str, msg);
290: return(len - msglen);
291: }
292:
293: /*
294: * str_addarg(str, len, arg, first) - add the string argument "arg" to
295: * the string "str". This is the first in the group when "first"
296: * is set (indicating that a comma should NOT be added to the front).
297: */
298:
1.2 downsj 299: static int str_addarg(str, len, arg, first)
1.1 downsj 300:
301: char *str;
302: int len;
303: char *arg;
304: int first;
305:
306: {
1.3 mpech 307: int arglen;
1.1 downsj 308:
309: arglen = strlen(arg);
310: if (!first)
311: {
312: arglen += 2;
313: }
314: if (len <= arglen)
315: {
316: return(0);
317: }
318: if (!first)
319: {
320: (void) strcat(str, ", ");
321: }
322: (void) strcat(str, arg);
323: return(len - arglen);
324: }
325:
326: /*
327: * err_compar(p1, p2) - comparison routine used by "qsort"
328: * for sorting errors.
329: */
330:
1.2 downsj 331: static int err_compar(e1, e2)
1.1 downsj 332:
1.2 downsj 333: const void *e1, *e2;
1.1 downsj 334:
335: {
1.3 mpech 336: const struct errs *p1 = (struct errs *)e1;
337: const struct errs *p2 = (struct errs *)e2;
338: int result;
1.1 downsj 339:
340: if ((result = p1->errno - p2->errno) == 0)
341: {
342: return(strcmp(p1->arg, p2->arg));
343: }
344: return(result);
345: }
346:
347: /*
348: * error_count() - return the number of errors currently logged.
349: */
350:
1.2 downsj 351: int error_count()
1.1 downsj 352:
353: {
354: return(errcnt);
355: }
356:
357: /*
358: * show_errors() - display on stdout the current log of errors.
359: */
360:
1.2 downsj 361: void show_errors()
1.1 downsj 362:
363: {
1.3 mpech 364: int cnt = 0;
365: struct errs *errp = errs;
1.1 downsj 366:
367: printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
368: while (cnt++ < errcnt)
369: {
370: printf("%5s: %s\n", errp->arg,
1.2 downsj 371: errp->errno == 0 ? "Not a number" : strerror(errp->errno));
1.1 downsj 372: errp++;
373: }
374: }
375:
376: /*
377: * kill_procs(str) - send signals to processes, much like the "kill"
378: * command does; invoked in response to 'k'.
379: */
380:
381: char *kill_procs(str)
382:
383: char *str;
384:
385: {
1.3 mpech 386: char *nptr;
1.1 downsj 387: int signum = SIGTERM; /* default */
388: int procnum;
389: struct sigdesc *sigp;
390: int uid;
391:
392: /* reset error array */
393: ERR_RESET;
394:
395: /* remember our uid */
396: uid = getuid();
397:
398: /* skip over leading white space */
399: while (isspace(*str)) str++;
400:
401: if (str[0] == '-')
402: {
403: /* explicit signal specified */
404: if ((nptr = next_field(str)) == NULL)
405: {
406: return(" kill: no processes specified");
407: }
408:
409: if (isdigit(str[1]))
410: {
411: (void) scanint(str + 1, &signum);
412: if (signum <= 0 || signum >= NSIG)
413: {
414: return(" invalid signal number");
415: }
416: }
417: else
418: {
419: /* translate the name into a number */
420: for (sigp = sigdesc; sigp->name != NULL; sigp++)
421: {
422: if (strcmp(sigp->name, str + 1) == 0)
423: {
424: signum = sigp->number;
425: break;
426: }
427: }
428:
429: /* was it ever found */
430: if (sigp->name == NULL)
431: {
432: return(" bad signal name");
433: }
434: }
435: /* put the new pointer in place */
436: str = nptr;
437: }
438:
439: /* loop thru the string, killing processes */
440: do
441: {
442: if (scanint(str, &procnum) == -1)
443: {
444: ERROR(str, 0);
445: }
446: else
447: {
448: /* check process owner if we're not root */
449: if (uid && (uid != proc_owner(procnum)))
450: {
451: ERROR(str, EACCES);
452: }
453: /* go in for the kill */
454: else if (kill(procnum, signum) == -1)
455: {
456: /* chalk up an error */
457: ERROR(str, errno);
458: }
459: }
460: } while ((str = next_field(str)) != NULL);
461:
462: /* return appropriate error string */
463: return(err_string());
464: }
465:
466: /*
467: * renice_procs(str) - change the "nice" of processes, much like the
468: * "renice" command does; invoked in response to 'r'.
469: */
470:
471: char *renice_procs(str)
472:
473: char *str;
474:
475: {
1.3 mpech 476: char negate;
1.1 downsj 477: int prio;
478: int procnum;
479: int uid;
480:
481: ERR_RESET;
482: uid = getuid();
483:
484: /* allow for negative priority values */
485: if ((negate = (*str == '-')) != 0)
486: {
487: /* move past the minus sign */
488: str++;
489: }
490:
491: /* use procnum as a temporary holding place and get the number */
492: procnum = scanint(str, &prio);
493:
494: /* negate if necessary */
495: if (negate)
496: {
497: prio = -prio;
498: }
499:
500: #if defined(PRIO_MIN) && defined(PRIO_MAX)
501: /* check for validity */
502: if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
503: {
504: return(" bad priority value");
505: }
506: #endif
507:
508: /* move to the first process number */
509: if ((str = next_field(str)) == NULL)
510: {
511: return(" no processes specified");
512: }
513:
514: /* loop thru the process numbers, renicing each one */
515: do
516: {
517: if (scanint(str, &procnum) == -1)
518: {
519: ERROR(str, 0);
520: }
521:
522: /* check process owner if we're not root */
523: else if (uid && (uid != proc_owner(procnum)))
524: {
525: ERROR(str, EACCES);
526: }
527: else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
528: {
529: ERROR(str, errno);
530: }
531: } while ((str = next_field(str)) != NULL);
532:
533: /* return appropriate error string */
534: return(err_string());
535: }
536: