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