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