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

1.32    ! deraadt     1: /* $OpenBSD: commands.c,v 1.31 2013/11/28 18:24:55 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.29      lum        40: #include <err.h>
1.1       downsj     41: #include <ctype.h>
1.2       downsj     42: #include <errno.h>
                     43: #include <stdlib.h>
                     44: #include <string.h>
1.1       downsj     45: #include <signal.h>
1.2       downsj     46: #include <unistd.h>
1.1       downsj     47: #include <sys/time.h>
                     48: #include <sys/resource.h>
                     49:
1.2       downsj     50: #include "top.h"
                     51:
1.1       downsj     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 *);
1.29      lum        57: static int      scan_arg(char *, int *, char *);
1.9       deraadt    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:  *  Utility routines that help with some of the commands.
                     65:  */
1.8       pvalchev   66: static char *
                     67: next_field(char *str)
1.1       downsj     68: {
1.30      lum        69:        char *spaces = " \t";
                     70:        size_t span;
                     71:
                     72:        span = strcspn(str, spaces);
1.32    ! deraadt    73:        if (span == strlen(str))
1.30      lum        74:                return (NULL);
                     75:
                     76:        str += span;
                     77:        *str++  = '\0';
                     78:
1.32    ! deraadt    79:        while (strcspn(str, spaces) == 0)
1.30      lum        80:                str++;
                     81:
                     82:        if (*str == '\0')
1.9       deraadt    83:                return (NULL);
                     84:
1.30      lum        85:        return(str);
1.1       downsj     86: }
                     87:
1.29      lum        88: /*
                     89:  * Scan the renice or kill interactive arguments for data and/or errors.
                     90:  */
1.14      deraadt    91: static int
1.29      lum        92: scan_arg(char *str, int *intp, char *nptr)
1.1       downsj     93: {
1.29      lum        94:        int val = 0, bad_flag = 0;
1.9       deraadt    95:        char ch;
1.1       downsj     96:
1.29      lum        97:        *nptr = '\0';
                     98:
1.9       deraadt    99:        if (*str == '\0')
                    100:                return (-1);
                    101:
                    102:        while ((ch = *str++) != '\0') {
1.31      deraadt   103:                if (isspace((unsigned char)ch))
1.9       deraadt   104:                        break;
1.31      deraadt   105:                else if (!isdigit((unsigned char)ch))
1.29      lum       106:                        bad_flag = 1;
1.9       deraadt   107:                else
1.29      lum       108:                        val = val * 10 + (ch - '0');
                    109:
                    110:                *(nptr++) = ch;
1.1       downsj    111:        }
1.29      lum       112:        *nptr = '\0';
                    113:
                    114:        if (bad_flag == 1)
                    115:                return(-1);
                    116:
1.9       deraadt   117:        *intp = val;
                    118:        return (0);
1.1       downsj    119: }
                    120:
                    121: /*
                    122:  *  Some of the commands make system calls that could generate errors.
                    123:  *  These errors are collected up in an array of structures for later
                    124:  *  contemplation and display.  Such routines return a string containing an
                    125:  *  error message, or NULL if no errors occurred.  The next few routines are
                    126:  *  for manipulating and displaying these errors.  We need an upper limit on
                    127:  *  the number of errors, so we arbitrarily choose 20.
                    128:  */
                    129:
                    130: #define ERRMAX 20
                    131:
1.28      otto      132: struct errs    errs[ERRMAX];
                    133: int            errcnt;
1.9       deraadt   134: static char    *err_toomany = " too many errors occurred";
                    135: static char    *err_listem =
1.1       downsj    136:        " Many errors occurred.  Press `e' to display the list of errors.";
                    137:
                    138: /* These macros get used to reset and log the errors */
                    139: #define ERR_RESET   errcnt = 0
1.9       deraadt   140: #define ERROR(p, e) \
                    141:        if (errcnt >= ERRMAX) { \
                    142:                return(err_toomany); \
                    143:        } else { \
1.29      lum       144:                free(errs[errcnt].arg); \
                    145:                if ((errs[errcnt].arg = strdup(p)) == NULL) \
                    146:                        err(1, "strdup"); \
1.15      marc      147:                errs[errcnt++].err = (e); \
1.9       deraadt   148:        }
                    149:
                    150: #define STRMAX 80
1.1       downsj    151:
                    152: /*
                    153:  *  err_string() - return an appropriate error string.  This is what the
                    154:  *     command will return for displaying.  If no errors were logged, then
                    155:  *     return NULL.  The maximum length of the error string is defined by
                    156:  *     "STRMAX".
                    157:  */
1.8       pvalchev  158: static char *
                    159: err_string(void)
1.1       downsj    160: {
1.9       deraadt   161:        int cnt = 0, first = Yes, currerr = -1;
                    162:        static char string[STRMAX];
                    163:        struct errs *errp;
                    164:
                    165:        /* if there are no errors, return NULL */
                    166:        if (errcnt == 0)
                    167:                return (NULL);
                    168:
                    169:        /* sort the errors */
1.12      deraadt   170:        qsort(errs, errcnt, sizeof(struct errs), err_compar);
1.9       deraadt   171:
                    172:        /* need a space at the front of the error string */
                    173:        string[0] = ' ';
                    174:        string[1] = '\0';
                    175:
                    176:        /* loop thru the sorted list, building an error string */
                    177:        while (cnt < errcnt) {
                    178:                errp = &(errs[cnt++]);
1.15      marc      179:                if (errp->err != currerr) {
1.9       deraadt   180:                        if (currerr != -1) {
                    181:                                if (str_adderr(string, sizeof string, currerr) >
                    182:                                    sizeof string - 2)
                    183:                                        return (err_listem);
                    184:
                    185:                                /* we know there's more */
                    186:                                (void) strlcat(string, "; ", sizeof string);
                    187:                        }
1.15      marc      188:                        currerr = errp->err;
1.9       deraadt   189:                        first = Yes;
1.1       downsj    190:                }
1.9       deraadt   191:                if (str_addarg(string, sizeof string, errp->arg, first) >=
                    192:                    sizeof string)
                    193:                        return (err_listem);
                    194:
                    195:                first = No;
1.1       downsj    196:        }
                    197:
1.9       deraadt   198:        /* add final message */
                    199:        if (str_adderr(string, sizeof string, currerr) >= sizeof string)
                    200:                return (err_listem);
1.1       downsj    201:
1.9       deraadt   202:        /* return the error string */
                    203:        return (string);
1.1       downsj    204: }
                    205:
                    206: /*
                    207:  *  str_adderr(str, len, err) - add an explanation of error "err" to
                    208:  *     the string "str".
                    209:  */
1.8       pvalchev  210: static size_t
                    211: str_adderr(char *str, size_t len, int err)
1.1       downsj    212: {
1.9       deraadt   213:        size_t msglen;
                    214:        char *msg;
1.1       downsj    215:
1.9       deraadt   216:        msg = err == 0 ? "Not a number" : strerror(err);
1.8       pvalchev  217:
1.9       deraadt   218:        if ((msglen = strlcat(str, ": ", len)) >= len)
                    219:                return (msglen);
1.8       pvalchev  220:
1.9       deraadt   221:        return (strlcat(str, msg, len));
1.1       downsj    222: }
                    223:
                    224: /*
                    225:  *  str_addarg(str, len, arg, first) - add the string argument "arg" to
                    226:  *     the string "str".  This is the first in the group when "first"
                    227:  *     is set (indicating that a comma should NOT be added to the front).
                    228:  */
1.8       pvalchev  229: static size_t
                    230: str_addarg(char *str, size_t len, char *arg, int first)
1.1       downsj    231: {
1.9       deraadt   232:        size_t msglen;
1.1       downsj    233:
1.9       deraadt   234:        if (!first) {
                    235:                if ((msglen = strlcat(str, ", ", len)) >= len)
                    236:                        return (msglen);
                    237:        }
                    238:        return (strlcat(str, arg, len));
1.1       downsj    239: }
                    240:
                    241: /*
                    242:  *  err_compar(p1, p2) - comparison routine used by "qsort"
                    243:  *     for sorting errors.
                    244:  */
1.8       pvalchev  245: static int
                    246: err_compar(const void *e1, const void *e2)
1.1       downsj    247: {
1.28      otto      248:        const struct errs *p1 = (const struct errs *) e1;
                    249:        const struct errs *p2 = (const struct errs *) e2;
1.9       deraadt   250:        int result;
                    251:
1.15      marc      252:        if ((result = p1->err - p2->err) == 0)
1.9       deraadt   253:                return (strcmp(p1->arg, p2->arg));
                    254:        return (result);
1.1       downsj    255: }
                    256:
                    257: /*
                    258:  *  error_count() - return the number of errors currently logged.
                    259:  */
1.8       pvalchev  260: int
                    261: error_count(void)
1.1       downsj    262: {
1.9       deraadt   263:        return (errcnt);
1.1       downsj    264: }
                    265:
                    266: /*
                    267:  *  kill_procs(str) - send signals to processes, much like the "kill"
                    268:  *             command does; invoked in response to 'k'.
                    269:  */
1.8       pvalchev  270: char *
                    271: kill_procs(char *str)
1.1       downsj    272: {
1.10      millert   273:        int signum = SIGTERM, procnum;
1.11      jfb       274:        uid_t uid, puid;
1.29      lum       275:        char tempbuf[TEMPBUFSIZE];
1.30      lum       276:        char *nptr, *tmp;
                    277:
                    278:        tmp = tempbuf;
1.9       deraadt   279:
                    280:        /* reset error array */
                    281:        ERR_RESET;
                    282:
                    283:        /* remember our uid */
                    284:        uid = getuid();
                    285:
                    286:        /* skip over leading white space */
1.31      deraadt   287:        while (isspace((unsigned char)*str))
1.9       deraadt   288:                str++;
                    289:
1.30      lum       290:        if (*str == '-') {
                    291:                str++;
                    292:
1.9       deraadt   293:                /* explicit signal specified */
                    294:                if ((nptr = next_field(str)) == NULL)
                    295:                        return (" kill: no processes specified");
                    296:
1.31      deraadt   297:                if (isdigit((unsigned char)*str)) {
1.30      lum       298:                        (void) scan_arg(str, &signum, tmp);
1.9       deraadt   299:                        if (signum <= 0 || signum >= NSIG)
                    300:                                return (" invalid signal number");
                    301:                } else {
                    302:                        /* translate the name into a number */
1.27      millert   303:                        for (signum = 0; signum < NSIG; signum++) {
1.32    ! deraadt   304:                                if (strcasecmp(sys_signame[signum],
1.30      lum       305:                                    str) == 0)
1.9       deraadt   306:                                        break;
                    307:                        }
                    308:
                    309:                        /* was it ever found */
1.27      millert   310:                        if (signum == NSIG)
1.9       deraadt   311:                                return (" bad signal name");
                    312:                }
                    313:                /* put the new pointer in place */
                    314:                str = nptr;
1.1       downsj    315:        }
1.29      lum       316:        nptr = tempbuf;
1.9       deraadt   317:        /* loop thru the string, killing processes */
                    318:        do {
1.29      lum       319:                if (scan_arg(str, &procnum, nptr) == -1) {
                    320:                        ERROR(nptr, 0);
1.9       deraadt   321:                } else {
                    322:                        /* check process owner if we're not root */
1.11      jfb       323:                        puid = proc_owner(procnum);
                    324:                        if (puid == (uid_t)(-1)) {
1.29      lum       325:                                ERROR(nptr, ESRCH);
1.11      jfb       326:                        } else if (uid && (uid != puid)) {
1.29      lum       327:                                ERROR(nptr, EACCES);
1.9       deraadt   328:                        } else if (kill(procnum, signum) == -1) {
1.29      lum       329:                                ERROR(nptr, errno);
1.9       deraadt   330:                        }
1.1       downsj    331:                }
1.9       deraadt   332:        } while ((str = next_field(str)) != NULL);
1.1       downsj    333:
1.9       deraadt   334:        /* return appropriate error string */
                    335:        return (err_string());
1.1       downsj    336: }
                    337:
                    338: /*
                    339:  *  renice_procs(str) - change the "nice" of processes, much like the
                    340:  *             "renice" command does; invoked in response to 'r'.
                    341:  */
1.8       pvalchev  342: char *
                    343: renice_procs(char *str)
1.1       downsj    344: {
1.10      millert   345:        uid_t uid;
1.9       deraadt   346:        char negate;
1.10      millert   347:        int prio, procnum;
1.29      lum       348:        char tempbuf[TEMPBUFSIZE];
                    349:        char *nptr;
1.9       deraadt   350:
                    351:        ERR_RESET;
                    352:        uid = getuid();
                    353:
1.29      lum       354:        /* skip over leading white space */
1.31      deraadt   355:        while (isspace((unsigned char)*str))
1.29      lum       356:                str++;
                    357:
1.9       deraadt   358:        /* allow for negative priority values */
                    359:        if ((negate = (*str == '-')) != 0) {
                    360:                /* move past the minus sign */
                    361:                str++;
                    362:        }
1.29      lum       363:
                    364:        nptr = tempbuf;
1.9       deraadt   365:        /* use procnum as a temporary holding place and get the number */
1.29      lum       366:        procnum = scan_arg(str, &prio, nptr);
1.9       deraadt   367:
                    368:        /* negate if necessary */
                    369:        if (negate)
                    370:                prio = -prio;
1.1       downsj    371:
                    372: #if defined(PRIO_MIN) && defined(PRIO_MAX)
1.9       deraadt   373:        /* check for validity */
                    374:        if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
                    375:                return (" bad priority value");
1.1       downsj    376: #endif
                    377:
1.9       deraadt   378:        /* move to the first process number */
                    379:        if ((str = next_field(str)) == NULL)
                    380:                return (" no processes specified");
                    381:
                    382:        /* loop thru the process numbers, renicing each one */
                    383:        do {
1.29      lum       384:                if (scan_arg(str, &procnum, nptr) == -1) {
                    385:                        ERROR(nptr, 0);
1.9       deraadt   386:                }
                    387:                /* check process owner if we're not root */
                    388:                else if (uid && (uid != proc_owner(procnum))) {
1.29      lum       389:                        ERROR(nptr, EACCES);
1.9       deraadt   390:                } else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) {
1.29      lum       391:                        ERROR(nptr, errno);
1.9       deraadt   392:                }
                    393:        } while ((str = next_field(str)) != NULL);
1.1       downsj    394:
1.9       deraadt   395:        /* return appropriate error string */
                    396:        return (err_string());
1.1       downsj    397: }