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