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