Annotation of coherent/b/bin/sa.c, revision 1.1.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.