Annotation of coherent/b/bin/sa.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * sa.c
        !             3:  * System accounting of command execution 
        !             4:  * (in conjuction with acct system call.)
        !             5:  */
        !             6: 
        !             7: #include <stdio.h>
        !             8: #include <ctype.h>
        !             9: #include <pwd.h>
        !            10: #include <acct.h>
        !            11: #include <sys/times.h>
        !            12: #include <sys/const.h>
        !            13: #include <dirent.h>
        !            14: 
        !            15: #define        MIN     60              /* Seconds in a minute */
        !            16: #define        MINHZ   (MIN*HZ)        /* HZ in a minute */
        !            17: #define        CPUTIME 0               /* Sort by CPU time */
        !            18: #define        PERCALL 1               /* Sort by CPU time per call */
        !            19: #define        CALLS   2               /* Sort by numbers of calls */
        !            20: 
        !            21: #define        NUSER   100             /* Maximum number of users */
        !            22: #define        NCOMM   500             /* Maximum number of commands */
        !            23: #define        NSORT   NUSER
        !            24: #define        NCNAME  (sizeof(ac.ac_comm))    /* Size of a command name) */
        !            25: 
        !            26: #define DIRSIZ 14
        !            27: 
        !            28: struct acct    ac;
        !            29: 
        !            30: /*
        !            31:  * structure of /usr/adm/usracct
        !            32:  * for per-user type of `sa' information.
        !            33:  */
        !            34: struct svu     {
        !            35:        unsigned svu_count;             /* Number of processes measured */
        !            36:        short   svu_uid;                /* User number */
        !            37:        time_t  svu_stime;              /* Total system time (HZ) */
        !            38:        time_t  svu_utime;              /* Total user time */
        !            39:        time_t  svu_etime;              /* Total elapsed time  (sec.)*/
        !            40: }      svu[NUSER];
        !            41: 
        !            42: /*
        !            43:  * For per command information as
        !            44:  * is stored in /usr/adm/savacct.
        !            45:  */
        !            46: struct svc     {
        !            47:        unsigned svc_count;             /* Number of calls */
        !            48:        char    svc_comm[NCNAME];       /* Command name */
        !            49:        time_t  svc_stime;
        !            50:        time_t  svc_utime;
        !            51:        time_t  svc_etime;
        !            52: }      svc[NCOMM];
        !            53: struct svc     *junkp, *otherp;
        !            54: 
        !            55: /*
        !            56:  * For the final sort.  This is
        !            57:  * the combined information of
        !            58:  * the two structures above.
        !            59:  * I.e. either one or the other.
        !            60:  */
        !            61: struct sort {
        !            62:        unsigned s_count;               /* Number of calls */
        !            63:        char    s_comm[NCNAME];         /* Command name */
        !            64:        short   s_uid;                  /* User number */
        !            65:        time_t  s_key;                  /* Sort key */
        !            66:        time_t  s_stime;                /* System time */
        !            67:        time_t  s_utime;                /* User time */
        !            68:        time_t  s_etime;                /* Elapsed time */
        !            69: }      sort[NSORT];
        !            70: 
        !            71: char   *acctf = "/usr/adm/acct";               /* Raw accounting file */
        !            72: char   *sacctf = "/usr/adm/savacct";           /* Summary file */
        !            73: char   *uacctf = "/usr/adm/usracct";           /* Per-user summary */
        !            74: char   junk[NCNAME] = "**junk**";
        !            75: char   other[NCNAME] = "***other";
        !            76: 
        !            77: int    aflag;                  /* Move unlikely commands to "**other" */
        !            78: int    cflag;                  /* Give percentage of total time */
        !            79: int    jflag;                  /* Give seconds instead of minutes/call */
        !            80: int    lflag;                  /* Separate system & user times */
        !            81: int    mflag;                  /* Number of processes and CPU min. per user */
        !            82: int    rflag;                  /* Reverse sort order */
        !            83: int    sflag;                  /* Merge accounting file when done */
        !            84: int    tflag;                  /* Print ratio of real to CPU time */
        !            85: int    uflag;                  /* Print user-id and command name */
        !            86: int    vflag;                  /* Prompt if used fewer than `n' times */
        !            87: int    sortflag = CPUTIME;     /* Sort mode */
        !            88: 
        !            89: long   ctol();
        !            90: time_t units();
        !            91: struct svc     *make();
        !            92: struct svc     *move();
        !            93: char   *uname();
        !            94: int    compar();
        !            95: 
        !            96: main(argc, argv)
        !            97: char *argv[];
        !            98: {
        !            99:        register char *ap;
        !           100: 
        !           101:        while (argc>1 && *argv[1]=='-') {
        !           102:                for (ap = &argv[1][1]; *ap != '\0'; ap++)
        !           103:                        switch (*ap) {
        !           104:                        case 'a':
        !           105:                                aflag = 1;
        !           106:                                break;
        !           107: 
        !           108:                        case 'b':
        !           109:                                sortflag = PERCALL;
        !           110:                                break;
        !           111: 
        !           112:                        case 'c':
        !           113:                                cflag = 1;
        !           114:                                break;
        !           115: 
        !           116:                        case 'j':
        !           117:                                jflag = 1;
        !           118:                                break;
        !           119: 
        !           120:                        case 'l':
        !           121:                                lflag = 1;
        !           122:                                break;
        !           123: 
        !           124:                        case 'm':
        !           125:                                mflag = 1;
        !           126:                                break;
        !           127: 
        !           128:                        case 'n':
        !           129:                                sortflag = CALLS;
        !           130:                                break;
        !           131: 
        !           132:                        case 'r':
        !           133:                                rflag = 1;
        !           134:                                break;
        !           135: 
        !           136:                        case 's':
        !           137:                                sflag = 1;
        !           138:                                break;
        !           139: 
        !           140:                        case 't':
        !           141:                                tflag = 1;
        !           142:                                break;
        !           143: 
        !           144:                        case 'u':
        !           145:                                uflag = 1;
        !           146:                                break;
        !           147: 
        !           148:                        case 'v':
        !           149:                                if (ap[1]>='0' && ap[1]<='9')
        !           150:                                        vflag = *++ap - '0';
        !           151:                                break;
        !           152: 
        !           153:                        default:
        !           154:                                usage();
        !           155:                        }
        !           156:                argc--;
        !           157:                argv++;
        !           158:        }
        !           159:        if (argc == 2)
        !           160:                acctf = argv[1];
        !           161:        else if (argc > 2)
        !           162:                usage();
        !           163:        if (!uflag)
        !           164:                rsummary();
        !           165:        rraw(acctf);
        !           166:        if (sflag && !uflag)
        !           167:                samerge();
        !           168:        if (!uflag)
        !           169:                saprint();
        !           170:        exit(0);
        !           171: }
        !           172: 
        !           173: /*
        !           174:  * Read the summary file of old accounting information
        !           175:  * from both user and command saved accounting files.
        !           176:  */
        !           177: rsummary()
        !           178: {
        !           179:        register FILE *fp;
        !           180: 
        !           181:        if ((fp = fopen(sacctf, "r")) != NULL) {
        !           182:                register struct svc *svcp;
        !           183: 
        !           184:                for (svcp = svc; svcp < &svc[NCOMM]; svcp++)
        !           185:                        if (fread(svcp, sizeof *svcp, 1, fp) != 1)
        !           186:                                break;
        !           187:                if (svcp >= &svc[NCOMM])
        !           188:                        fprintf(stderr, "%s is too large\n", sacctf);
        !           189:                fclose(fp);
        !           190:        }
        !           191:        if ((fp = fopen(uacctf, "r")) != NULL) {
        !           192:                register struct svu *svup;
        !           193: 
        !           194:                for (svup = svu; svup < &svu[NUSER]; svup++)
        !           195:                        if (fread(svup, sizeof *svup, 1, fp) != 1)
        !           196:                                break;
        !           197:                if (svup >= &svu[NUSER])
        !           198:                        fprintf(stderr, "%s is too large\n", uacctf);
        !           199:                fclose(fp);
        !           200:        }
        !           201: }
        !           202: 
        !           203: /*
        !           204:  * Read the raw accounting.
        !           205:  */
        !           206: rraw(af)
        !           207: char *af;
        !           208: {
        !           209:        FILE *afp;
        !           210:        register struct svc *svcp;
        !           211: 
        !           212:        if ((afp = fopen(af, "r")) == NULL) {
        !           213:                fprintf(stderr, "Cannot open raw accounting file `%s'\n", af);
        !           214:                exit(1);
        !           215:        }
        !           216:        while (fread(&ac, sizeof ac, 1, afp) == 1) {
        !           217:                if (uflag)
        !           218:                        printf("%-*s  %.*s\n", DIRSIZ, uname(ac.ac_uid),
        !           219:                            NCNAME, ac.ac_comm);
        !           220:                else {
        !           221:                        senter(&ac);
        !           222:                        uenter(&ac);
        !           223:                }
        !           224:        }
        !           225:        fclose(afp);
        !           226:        /*
        !           227:         * Create (if not there) junk and other classes.
        !           228:         */
        !           229:        if (aflag) {
        !           230:                otherp = make(other);
        !           231:                for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++) {
        !           232:                        if (svcp == otherp)
        !           233:                                continue;
        !           234:                        if (svcp->svc_count==1 || unprintable(svcp->svc_comm))
        !           235:                                otherp = move(svcp--, otherp);
        !           236:                }
        !           237:        }
        !           238:        if (vflag) {
        !           239:                junkp = make(junk);
        !           240:                for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++) {
        !           241:                        if (svcp == junkp)
        !           242:                                continue;
        !           243:                        if (svcp->svc_count<=vflag && yes(svcp->svc_comm))
        !           244:                                junkp = move(svcp--, junkp);
        !           245:                }
        !           246:        }
        !           247: }
        !           248: 
        !           249: /*
        !           250:  * Merge system accounting info back
        !           251:  * into the two merged files and
        !           252:  * truncate the raw accounting file.
        !           253:  * Accounting probably should be
        !           254:  * turned off when `sa' is called
        !           255:  * if this is to be done.
        !           256:  */
        !           257: samerge()
        !           258: {
        !           259:        register FILE *fp;
        !           260: 
        !           261:        if ((fp = fopen(sacctf, "w")) != NULL) {
        !           262:                register struct svc *svcp;
        !           263: 
        !           264:                for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++)
        !           265:                        if (fwrite(svcp, sizeof *svcp, 1, fp) != 1)
        !           266:                                saerr("%s: write error", sacctf);
        !           267:                fclose(fp);
        !           268:        } else
        !           269:                saerr("cannot rewrite %s", sacctf);
        !           270:        if ((fp = fopen(uacctf, "w")) != NULL) {
        !           271:                register struct svu *svup;
        !           272: 
        !           273:                for (svup = svu; svup<&svu[NUSER] && svup->svu_count; svup++)
        !           274:                        if (fwrite(svup, sizeof *svup, 1, fp) != 1)
        !           275:                                saerr("%s: write error", uacctf);
        !           276:                fclose(fp);
        !           277:        } else
        !           278:                saerr("cannot rewrite %s", uacctf);
        !           279:        if ((fp = fopen(acctf, "w")) == NULL)
        !           280:                saerr("cannot truncate %s", acctf);
        !           281:        fclose(fp);
        !           282: }
        !           283: 
        !           284: /*
        !           285:  * Output the accounting
        !           286:  * information according to
        !           287:  * sorting and printing options.
        !           288:  */
        !           289: saprint()
        !           290: {
        !           291:        register struct sort *sp;
        !           292:        time_t tottime;
        !           293: 
        !           294:        if (mflag)
        !           295:                userenter();
        !           296:        else
        !           297:                commenter();
        !           298:        for (sp = sort; sp<&sort[NSORT] && sp->s_count; sp++)
        !           299:                if (sortflag == CPUTIME)
        !           300:                        sp->s_key = sp->s_stime+sp->s_utime;
        !           301:                else if (sortflag == PERCALL)
        !           302:                        sp->s_key = (sp->s_stime+sp->s_utime)/sp->s_count;
        !           303:                else            /* # calls */
        !           304:                        sp->s_key = sp->s_count;
        !           305:        qsort(sort, sp-sort, sizeof *sp, compar);
        !           306:        if (mflag)
        !           307:                printf("%-*s #PROC", DIRSIZ, "");
        !           308:        else
        !           309:                printf("%-*s #CALL", NCNAME, "");
        !           310:        if (lflag)
        !           311:                printf("  USER  SYS");
        !           312:        else
        !           313:                printf("  CPU");
        !           314:        printf("  REAL");
        !           315:        if (cflag)
        !           316:                printf(" CPU %% ");
        !           317:        if (tflag)
        !           318:                printf(" CPU/REAL %%");
        !           319:        putchar('\n');
        !           320:        if (cflag) {
        !           321:                tottime = 0;
        !           322:                for (sp = sort; sp<&sort[NSORT] && sp->s_count; sp++) {
        !           323:                        tottime += sp->s_stime;
        !           324:                        tottime += sp->s_utime;
        !           325:                }
        !           326:        }
        !           327:        for (sp = sort; sp<&sort[NSORT] && sp->s_count; sp++) {
        !           328:                if (mflag)
        !           329:                        printf("%-*s", DIRSIZ, uname(sp->s_uid));
        !           330:                else
        !           331:                        printf("%-*s", NCNAME, sp->s_comm);
        !           332:                printf(" %5d", sp->s_count);
        !           333:                if (lflag)
        !           334:                        printf(" %5ld %5ld", units(sp->s_utime, sp),
        !           335:                            units(sp->s_stime, sp));
        !           336:                else
        !           337:                        printf(" %5ld", units(sp->s_utime+sp->s_stime, sp));
        !           338:                printf(" %5ld", units(sp->s_etime*HZ, sp));
        !           339:                if (cflag)
        !           340:                        percent(sp->s_utime+sp->s_stime, tottime);
        !           341:                if (tflag) {
        !           342:                        printf("  ");
        !           343:                        percent(sp->s_utime+sp->s_stime, sp->s_etime*HZ);
        !           344:                }
        !           345:                putchar('\n');
        !           346:        }
        !           347: }
        !           348: 
        !           349: /*
        !           350:  * Enter user information for the sort.
        !           351:  */
        !           352: userenter()
        !           353: {
        !           354:        register struct sort *sp;
        !           355:        register struct svu *svup;
        !           356: 
        !           357:        sp = sort;
        !           358:        svup = svu;
        !           359:        while (svup < &svu[NCOMM] && svup->svu_count) {
        !           360:                sp->s_count = svup->svu_count;
        !           361:                sp->s_uid = svup->svu_uid;
        !           362:                sp->s_stime = svup->svu_stime;
        !           363:                sp->s_utime = svup->svu_utime;
        !           364:                sp->s_etime = svup->svu_etime;
        !           365:                sp++;
        !           366:                svup++;
        !           367:        }
        !           368: /*
        !           369:        free(svc);
        !           370:  */
        !           371: }
        !           372: 
        !           373: /*
        !           374:  * Enter the commands into the list for
        !           375:  * sorting.
        !           376:  */
        !           377: commenter()
        !           378: {
        !           379:        register struct sort *sp;
        !           380:        register struct svc *svcp;
        !           381: 
        !           382:        sp = sort;
        !           383:        svcp = svc;
        !           384:        while (svcp < &svc[NCOMM] && svcp->svc_count) {
        !           385:                sp->s_count = svcp->svc_count;
        !           386:                strncpy(sp->s_comm, svcp->svc_comm, NCNAME);
        !           387:                sp->s_stime = svcp->svc_stime;
        !           388:                sp->s_utime = svcp->svc_utime;
        !           389:                sp->s_etime = svcp->svc_etime;
        !           390:                sp++;
        !           391:                svcp++;
        !           392:        }
        !           393: /*
        !           394:        free(svu);
        !           395:  */
        !           396: }
        !           397: 
        !           398: /*
        !           399:  * Enter this accounting entry into
        !           400:  * the command table for savacct.
        !           401:  */
        !           402: senter(ap)
        !           403: register struct acct *ap;
        !           404: {
        !           405:        register struct svc *svcp;
        !           406: 
        !           407:        for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++)
        !           408:                if (strncmp(svcp->svc_comm, ap->ac_comm, NCNAME) == 0)
        !           409:                        break;
        !           410:        if (svcp >= &svc[NCOMM])
        !           411:                saerr("Command table overflow");
        !           412:        if (svcp->svc_count++ == 0)
        !           413:                strncpy(svcp->svc_comm, ap->ac_comm, NCNAME);
        !           414:        svcp->svc_stime += ctol(ap->ac_stime);
        !           415:        svcp->svc_utime += ctol(ap->ac_utime);
        !           416:        svcp->svc_etime += ctol(ap->ac_etime);
        !           417: }
        !           418: 
        !           419: /*
        !           420:  * Enter this accounting entry into
        !           421:  * the user table for usracct.
        !           422:  */
        !           423: uenter(ap)
        !           424: register struct acct *ap;
        !           425: {
        !           426:        register struct svu *svup;
        !           427: 
        !           428:        for (svup = svu; svup<&svu[NUSER] && svup->svu_count; svup++)
        !           429:                if (svup->svu_uid == ap->ac_uid)
        !           430:                        break;
        !           431:        if (svup >= &svu[NUSER])
        !           432:                saerr("User table overflow");
        !           433:        svup->svu_count++;
        !           434:        svup->svu_uid = ap->ac_uid;
        !           435:        svup->svu_stime += ctol(ap->ac_stime);
        !           436:        svup->svu_utime += ctol(ap->ac_utime);
        !           437:        svup->svu_etime += ctol(ap->ac_etime);
        !           438: }
        !           439: 
        !           440: /*
        !           441:  * Make a new zero entry with
        !           442:  * the indicated name.
        !           443:  */
        !           444: struct svc *
        !           445: make(name)
        !           446: register char *name;
        !           447: {
        !           448:        register struct svc *svcp;
        !           449: 
        !           450:        for (svcp = svc; svcp<&svc[NCOMM] && svcp->svc_count; svcp++)
        !           451:                if (strncmp(name, svcp->svc_comm, NCNAME) == 0)
        !           452:                        return (svcp);
        !           453:        if (svcp >= &svc[NCOMM])
        !           454:                saerr("out of room for %s", name);
        !           455:        strncpy(svcp->svc_comm, name, NCNAME);
        !           456:        return (svcp);
        !           457: }
        !           458: 
        !           459: /*
        !           460:  * Move an entry to one of the deprecated
        !           461:  * places.
        !           462:  */
        !           463: struct svc *
        !           464: move(svcp, depp)
        !           465: register struct svc *svcp;
        !           466: struct svc *depp;
        !           467: {
        !           468: 
        !           469:        depp->svc_count += svcp->svc_count;
        !           470:        depp->svc_stime += svcp->svc_stime;
        !           471:        depp->svc_utime += svcp->svc_utime;
        !           472:        depp->svc_etime += svcp->svc_etime;
        !           473:        while (svcp->svc_count) {
        !           474:                if (svcp == depp)
        !           475:                        depp--;
        !           476:                bcopy(svcp+1, svcp, sizeof *svcp);
        !           477:                svcp++;
        !           478:        }
        !           479:        (svcp-1)->svc_count = 0;
        !           480:        return (depp);
        !           481: }
        !           482: 
        !           483: /*
        !           484:  * Return the user-name for
        !           485:  * a user-ID.
        !           486:  */
        !           487: char *
        !           488: uname(uid)
        !           489: short uid;
        !           490: {
        !           491:        register struct passwd *pwp;
        !           492:        static char ubuf[15];
        !           493: 
        !           494:        if ((pwp = getpwuid(uid)) != NULL)
        !           495:                return (pwp->pw_name);
        !           496:        return (sprintf(ubuf, "%d", uid));
        !           497: }
        !           498: 
        !           499: /*
        !           500:  * Block copy of `n' bytes.
        !           501:  */
        !           502: bcopy(f, t, n)
        !           503: register char *f, *t;
        !           504: register unsigned n;
        !           505: {
        !           506:        if (n)
        !           507:                do {
        !           508:                        *t++ = *f++;
        !           509:                } while (--n);
        !           510: }
        !           511: 
        !           512: /*
        !           513:  * Return non-zero if an NCNAME-length string
        !           514:  * is unprintable.
        !           515:  */
        !           516: unprintable(s)
        !           517: register char *s;
        !           518: {
        !           519:        register int n = NCNAME;
        !           520:        register int c;
        !           521: 
        !           522:        do {
        !           523:                if ((c = *s++) == '\0')
        !           524:                        break;
        !           525:                if (!isascii(c) || !isprint(c))
        !           526:                        return (1);
        !           527:        } while (--n);
        !           528:        return (0);
        !           529: }
        !           530: 
        !           531: /*
        !           532:  * Ask whether or not to delete and
        !           533:  * entry.  Return 1 if yes.
        !           534:  */
        !           535: yes(s)
        !           536: register char *s;
        !           537: {
        !           538:        register int c, ans = 0;
        !           539: 
        !           540:        printf("%.*s ? ", NCNAME, s);
        !           541:        if ((c = getchar()) == 'y')
        !           542:                ans = 1;
        !           543:        while (c!='\n' && c!=EOF)
        !           544:                c = getchar();
        !           545:        return (ans);
        !           546: }
        !           547: 
        !           548: /*
        !           549:  * Qsort compare routine.
        !           550:  */
        !           551: compar(sp1, sp2)
        !           552: register struct sort *sp1, *sp2;
        !           553: {
        !           554:        register int rval;
        !           555: 
        !           556:        if (sp1->s_key == sp2->s_key)
        !           557:                return (0);
        !           558:        rval = 1;
        !           559:        if (sp1->s_key > sp2->s_key)
        !           560:                rval = -1;
        !           561:        if (rflag)
        !           562:                return (-rval);
        !           563:        return (rval);
        !           564: }
        !           565: 
        !           566: /*
        !           567:  * Return the number of CPU minutes from CPU HZ.
        !           568:  * or if `-j', return seconds/call.
        !           569:  */
        !           570: time_t
        !           571: units(hz, sp)
        !           572: time_t hz;
        !           573: register struct sort *sp;
        !           574: {
        !           575:        if (jflag)
        !           576:                return ((hz + HZ/2) / (HZ*sp->s_count));
        !           577:        else
        !           578:                return ((hz + MINHZ/2) / MINHZ);
        !           579: }
        !           580: 
        !           581: /*
        !           582:  * Print the percentage of
        !           583:  * `t' out of `total'.
        !           584:  */
        !           585: percent(t, total)
        !           586: time_t t, total;
        !           587: {
        !           588:        if (total == (time_t)0)
        !           589:                t = total = (time_t)1;
        !           590:        t *= 100;
        !           591:        printf("%3ld.", t/total);
        !           592:        t %= total;
        !           593:        if (t < 0)
        !           594:                t = -t;
        !           595:        printf("%1ld ", t*10/total);
        !           596: }
        !           597: 
        !           598: usage()
        !           599: {
        !           600:        fprintf(stderr, "Usage: sa [-abcjlmnrstu] [-v[n]] [file]\n");
        !           601:        exit(1);
        !           602: }
        !           603: 
        !           604: /* VARARGS */
        !           605: saerr(x)
        !           606: {
        !           607:        fprintf(stderr, "sa: %r", &x);
        !           608:        putc('\n', stderr);
        !           609:        exit(1);
        !           610: }
        !           611: 
        !           612: /* end of sa.c */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.