[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.23

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