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