|
|
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.