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

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