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