Annotation of coherent/d/bin/sa.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

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