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

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