|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.