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