[BACK]Return to commands.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / top

Annotation of src/usr.bin/top/commands.c, Revision 1.9

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