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

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