1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30 #include <time.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <sys/param.h>
35 #include "acctdef.h"
36 #include <grp.h>
37 #include <sys/acct.h>
38 #include <pwd.h>
39 #include <sys/stat.h>
40 #include <locale.h>
41 #include <stdlib.h>
42 #include <libgen.h>
43
44 struct acct ab;
45 char command_name[16];
46 char obuf[BUFSIZ];
47 static char time_buf[50];
48
49 double cpucut,
50 syscut,
51 hogcut,
52 iocut,
53 realtot,
54 cputot,
55 usertot,
56 systot,
57 kcoretot,
58 iotot,
59 rwtot;
60 extern long timezone;
61 extern int daylight; /* daylight savings time if set */
62 long daydiff,
63 offset = -2,
64 cmdcount;
65 ulong_t elapsed,
66 sys,
67 user,
68 cpu,
69 io,
70 rw,
71 mem,
72 etime;
73 time_t tstrt_b,
74 tstrt_a,
75 tend_b,
76 tend_a;
77 int backward,
78 flag_field,
79 average,
80 quiet,
81 option,
82 verbose = 1,
83 uidflag,
84 gidflag,
85 unkid, /*user doesn't have login on this machine*/
86 errflg,
87 su_user,
88 fileout = 0,
89 stdinflg,
90 nfiles;
91 static int eflg = 0,
92 Eflg = 0,
93 sflg = 0,
94 Sflg = 0;
95 #ifdef uts
96 dev_t linedev = 0xffff; /* changed from -1, as dev_t is now ushort */
97 #else
98 dev_t linedev = (dev_t)-1;
99 #endif
100 uid_t uidval;
101 gid_t gidval;
102 char *cname = NULL; /* command name pattern to match*/
103
104 struct passwd *getpwnam(), *getpwuid(), *pw;
105 struct group *getgrnam(),*grp;
106 long convtime();
107
108 #ifdef uts
109 float expand();
110 #else
111 ulong_t expand();
112 #endif
113
114 char *ofile,
115 *devtolin(),
116 *uidtonam();
117 dev_t lintodev();
118
119 void dofile(char *);
120 void doexit(int) __NORETURN;
121 void usage(void);
122 void fatal(char *, char *);
123 void println(void);
124 void printhd(void);
125 char *cmset(char *);
126
127 FILE *ostrm;
128
129 int
main(int argc,char ** argv)130 main(int argc, char **argv)
131 {
132 int c;
133
134 (void)setlocale(LC_ALL, "");
135 setbuf(stdout,obuf);
136 while((c = getopt(argc, argv,
137 "C:E:H:I:O:S:abe:fg:hikl:mn:o:qrs:tu:v")) != EOF) {
138 switch(c) {
139 case 'C':
140 sscanf(optarg,"%lf",&cpucut);
141 continue;
142 case 'O':
143 sscanf(optarg,"%lf",&syscut);
144 continue;
145 case 'H':
146 sscanf(optarg,"%lf",&hogcut);
147 continue;
148 case 'I':
149 sscanf(optarg,"%lf",&iocut);
150 continue;
151 case 'a':
152 average++;
153 continue;
154 case 'b':
155 backward++;
156 continue;
157 case 'g':
158 if(sscanf(optarg,"%ld",&gidval) == 1) {
159 if (getgrgid(gidval) == NULL)
160 fatal("Unknown group", optarg);
161 } else if((grp=getgrnam(optarg)) == NULL)
162 fatal("Unknown group", optarg);
163 else
164 gidval=grp->gr_gid;
165 gidflag++;
166 continue;
167 case 'h':
168 option |= HOGFACTOR;
169 continue;
170 case 'i':
171 option |= IORW;
172 continue;
173 case 'k':
174 option |= KCOREMIN;
175 continue;
176 case 'm':
177 option |= MEANSIZE;
178 continue;
179 case 'n':
180 cname=cmset(optarg);
181 continue;
182 case 't':
183 option |= SEPTIME;
184 continue;
185 case 'r':
186 option |= CPUFACTOR;
187 continue;
188 case 'v':
189 verbose=0;
190 continue;
191 case 'l':
192 linedev = lintodev(optarg);
193 continue;
194 case 'u':
195 if(*optarg == '?') {
196 unkid++;
197 continue;
198 }
199 if(*optarg == '#') {
200 su_user++;
201 uidval = 0;
202 uidflag++;
203 continue;
204 }
205 if((pw = getpwnam(optarg)) == NULL) {
206 uidval = (uid_t)atoi(optarg);
207 /* atoi will return 0 in abnormal situation */
208 if (uidval == 0 && strcmp(optarg, "0") != 0) {
209 fprintf(stderr, "%s: Unknown user %s\n", argv[0], optarg);
210 exit(1);
211 }
212 if ((pw = getpwuid(uidval)) == NULL) {
213 fprintf(stderr, "%s: Unknown user %s\n", argv[0], optarg);
214 exit(1);
215 }
216 uidflag++;
217 } else {
218 uidval = pw->pw_uid;
219 uidflag++;
220 }
221 continue;
222 case 'q':
223 quiet++;
224 verbose=0;
225 average++;
226 continue;
227 case 's':
228 sflg = 1;
229 tend_a = convtime(optarg);
230 continue;
231 case 'S':
232 Sflg = 1;
233 tstrt_a = convtime(optarg);
234 continue;
235 case 'f':
236 flag_field++;
237 continue;
238 case 'e':
239 eflg = 1;
240 tstrt_b = convtime(optarg);
241 continue;
242 case 'E':
243 Eflg = 1;
244 tend_b = convtime(optarg);
245 continue;
246 case 'o':
247 ofile = optarg;
248 fileout++;
249 if((ostrm = fopen(ofile, "w")) == NULL) {
250 perror("open error on output file");
251 errflg++;
252 }
253 continue;
254 case '?':
255 errflg++;
256 continue;
257 }
258 }
259
260 if(errflg) {
261 usage();
262 exit(1);
263 }
264
265
266 argv = &argv[optind];
267 while(optind++ < argc) {
268 dofile(*argv++); /* change from *argv */
269 nfiles++;
270 }
271
272 if(nfiles==0) {
273 if(isatty(0) || isdevnull())
274 dofile(PACCT);
275 else {
276 stdinflg = 1;
277 backward = offset = 0;
278 dofile(NULL);
279 }
280 }
281 doexit(0);
282 /* NOTREACHED */
283 }
284
285 void
dofile(char * fname)286 dofile(char *fname)
287 {
288 struct acct *a = &ab;
289 struct tm *t;
290 time_t curtime;
291 time_t ts_a = 0,
292 ts_b = 0,
293 te_a = 0,
294 te_b = 0;
295 long daystart;
296 long nsize;
297 int ver; /* version of acct structure */
298 int dst_secs; /* number of seconds to adjust
299 for daylight savings time */
300
301 if(fname != NULL)
302 if(freopen(fname, "r", stdin) == NULL) {
303 fprintf(stderr, "acctcom: cannot open %s\n", fname);
304 return;
305 }
306
307 if (fread((char *)&ab, sizeof(struct acct), 1, stdin) != 1)
308 return;
309 else if (ab.ac_flag & AEXPND)
310 ver = 2; /* 4.0 acct structure */
311 else
312 ver = 1; /* 3.x acct structure */
313
314 rewind(stdin);
315
316
317 if(backward) {
318 if (ver == 2)
319 nsize = sizeof(struct acct); /* make sure offset is signed */
320 else
321 nsize = sizeof(struct o_acct); /* make sure offset is signed */
322 fseek(stdin, (long)(-nsize), 2);
323 }
324 tzset();
325 daydiff = a->ac_btime - (a->ac_btime % SECSINDAY);
326 time(&curtime);
327 t = localtime(&curtime);
328 if (daydiff < (curtime - (curtime % SECSINDAY))) {
329 time_t t;
330 /*
331 * it is older than today
332 */
333 t = (time_t)a->ac_btime;
334 cftime(time_buf, DATE_FMT, &t);
335 fprintf(stdout, "\nACCOUNTING RECORDS FROM: %s", time_buf);
336 }
337
338 /* adjust time by one hour for daylight savings time */
339 if (daylight && t->tm_isdst != 0)
340 dst_secs = 3600;
341 else
342 dst_secs = 0;
343 daystart = (a->ac_btime - timezone + dst_secs) -
344 ((a->ac_btime - timezone + dst_secs) % SECSINDAY);
345 if (Sflg) {
346 ts_a = tstrt_a + daystart - dst_secs;
347 cftime(time_buf, DATE_FMT, &ts_a);
348 fprintf(stdout, "START AFT: %s", time_buf);
349 }
350 if (eflg) {
351 ts_b = tstrt_b + daystart - dst_secs;
352 cftime(time_buf, DATE_FMT, &ts_b);
353 fprintf(stdout, "START BEF: %s", time_buf);
354 }
355 if (sflg) {
356 te_a = tend_a + daystart - dst_secs;
357 cftime(time_buf, DATE_FMT, &te_a);
358 fprintf(stdout, "END AFTER: %s", time_buf);
359 }
360 if (Eflg) {
361 te_b = tend_b + daystart - dst_secs;
362 cftime(time_buf, DATE_FMT, &te_b);
363 fprintf(stdout, "END BEFOR: %s", time_buf);
364 }
365 if(ts_a) {
366 if (te_b && ts_a > te_b) te_b += SECSINDAY;
367 }
368
369 while(aread(ver) != 0) {
370 elapsed = expand(a->ac_etime);
371 etime = (ulong_t)a->ac_btime + (ulong_t)SECS(elapsed);
372 if(ts_a || ts_b || te_a || te_b) {
373
374 if(te_a && (etime < te_a)) {
375 if(backward) return;
376 else continue;
377 }
378 if(te_b && (etime > te_b)) {
379 if(backward) continue;
380 else return;
381 }
382 if(ts_a && (a->ac_btime < ts_a))
383 continue;
384 if(ts_b && (a->ac_btime > ts_b))
385 continue;
386 }
387 if(!MYKIND(a->ac_flag))
388 continue;
389 if(su_user && !SU(a->ac_flag))
390 continue;
391 sys = expand(a->ac_stime);
392 user = expand(a->ac_utime);
393 cpu = sys + user;
394 if(cpu == 0)
395 cpu = 1;
396 mem = expand(a->ac_mem);
397 (void) strncpy(command_name, a->ac_comm, 8);
398 io=expand(a->ac_io);
399 rw=expand(a->ac_rw);
400 if(cpucut && cpucut >= SECS(cpu))
401 continue;
402 if(syscut && syscut >= SECS(sys))
403 continue;
404 #ifdef uts
405 if(linedev != 0xffff && a->ac_tty != linedev)
406 continue;
407 #else
408 if(linedev != (dev_t)-1 && a->ac_tty != linedev)
409 continue;
410 #endif
411 if(uidflag && a->ac_uid != uidval)
412 continue;
413 if(gidflag && a->ac_gid != gidval)
414 continue;
415 if(cname && !cmatch(a->ac_comm,cname))
416 continue;
417 if(iocut && iocut > io)
418 continue;
419 if(unkid && uidtonam(a->ac_uid)[0] != '?')
420 continue;
421 if(verbose && (fileout == 0)) {
422 printhd();
423 verbose = 0;
424 }
425 if(elapsed == 0)
426 elapsed++;
427 if(hogcut && hogcut >= (double)cpu/(double)elapsed)
428 continue;
429 if(fileout)
430 fwrite(&ab, sizeof(ab), 1, ostrm);
431 else
432 println();
433 if(average) {
434 cmdcount++;
435 realtot += (double)elapsed;
436 usertot += (double)user;
437 systot += (double)sys;
438 kcoretot += (double)mem;
439 iotot += (double)io;
440 rwtot += (double)rw;
441 };
442 }
443 }
444
445 int
aread(int ver)446 aread(int ver)
447 {
448 static int ok = 1;
449 struct o_acct oab;
450 int ret;
451
452 if (ver != 2) {
453 if ((ret = fread((char *)&oab, sizeof(struct o_acct), 1, stdin)) == 1){
454 /* copy SVR3 acct struct to SVR4 acct struct */
455 ab.ac_flag = oab.ac_flag | AEXPND;
456 ab.ac_stat = oab.ac_stat;
457 ab.ac_uid = (uid_t) oab.ac_uid;
458 ab.ac_gid = (gid_t) oab.ac_gid;
459 ab.ac_tty = (dev_t) oab.ac_tty;
460 ab.ac_btime = oab.ac_btime;
461 ab.ac_utime = oab.ac_utime;
462 ab.ac_stime = oab.ac_stime;
463 ab.ac_mem = oab.ac_mem;
464 ab.ac_io = oab.ac_io;
465 ab.ac_rw = oab.ac_rw;
466 strcpy(ab.ac_comm, oab.ac_comm);
467 }
468 } else
469 ret = fread((char *)&ab, sizeof(struct acct), 1, stdin);
470
471
472 if(backward) {
473 if(ok) {
474 if(fseek(stdin,
475 (long)(offset*(ver == 2 ? sizeof(struct acct) :
476 sizeof(struct o_acct))), 1) != 0) {
477
478 rewind(stdin); /* get 1st record */
479 ok = 0;
480 }
481 } else
482 ret = 0;
483 }
484 return(ret != 1 ? 0 : 1);
485 }
486
487 void
printhd(void)488 printhd(void)
489 {
490 fprintf(stdout, "COMMAND START END REAL");
491 ps("CPU");
492 if(option & SEPTIME)
493 ps("(SECS)");
494 if(option & IORW){
495 ps("CHARS");
496 ps("BLOCKS");
497 }
498 if(option & CPUFACTOR)
499 ps("CPU");
500 if(option & HOGFACTOR)
501 ps("HOG");
502 if(!option || (option & MEANSIZE))
503 ps("MEAN");
504 if(option & KCOREMIN)
505 ps("KCORE");
506 fprintf(stdout, "\n");
507 fprintf(stdout, "NAME USER TTYNAME TIME TIME (SECS)");
508 if(option & SEPTIME) {
509 ps("SYS");
510 ps("USER");
511 } else
512 ps("(SECS)");
513 if(option & IORW) {
514 ps("TRNSFD");
515 ps("READ");
516 }
517 if(option & CPUFACTOR)
518 ps("FACTOR");
519 if(option & HOGFACTOR)
520 ps("FACTOR");
521 if(!option || (option & MEANSIZE))
522 ps("SIZE(K)");
523 if(option & KCOREMIN)
524 ps("MIN");
525 if(flag_field)
526 fprintf(stdout, " F STAT");
527 fprintf(stdout, "\n");
528 fflush(stdout);
529 }
530
531 void
println(void)532 println(void)
533 {
534 char name[32];
535 struct acct *a = &ab;
536 time_t t;
537
538 if(quiet)
539 return;
540 if(!SU(a->ac_flag))
541 strcpy(name,command_name);
542 else {
543 strcpy(name,"#");
544 strcat(name,command_name);
545 }
546 fprintf(stdout, "%-*.*s", (OUTPUT_NSZ + 1),
547 (OUTPUT_NSZ + 1), name);
548 strcpy(name,uidtonam(a->ac_uid));
549 if(*name != '?')
550 fprintf(stdout, " %-*.*s", (OUTPUT_NSZ + 1),
551 (OUTPUT_NSZ + 1), name);
552 else
553 fprintf(stdout, " %-9d",a->ac_uid);
554 #ifdef uts
555 fprintf(stdout, " %-*.*s", OUTPUT_LSZ, OUTPUT_LSZ,
556 a->ac_tty != 0xffff? devtolin(a->ac_tty):"?");
557 #else
558 fprintf(stdout, " %-*.*s", OUTPUT_LSZ, OUTPUT_LSZ,
559 a->ac_tty != (dev_t)-1? devtolin(a->ac_tty):"?");
560 #endif
561 t = a->ac_btime;
562 cftime(time_buf, DATE_FMT1, &t);
563 fprintf(stdout, "%.9s", time_buf);
564 cftime(time_buf, DATE_FMT1, (time_t *)&etime);
565 fprintf(stdout, "%.9s ", time_buf);
566 pf((double)SECS(elapsed));
567 if(option & SEPTIME) {
568 pf((double)sys / HZ);
569 pf((double)user / HZ);
570 } else
571 pf((double)cpu / HZ);
572 if(option & IORW)
573 fprintf(stdout, io < 100000000 ? "%8ld%8ld" : "%12ld%8ld",io,rw);
574 if(option & CPUFACTOR)
575 pf((double)user / cpu);
576 if(option & HOGFACTOR)
577 pf((double)cpu / elapsed);
578 if(!option || (option & MEANSIZE))
579 pf(KCORE(mem / cpu));
580 if(option & KCOREMIN)
581 pf(MINT(KCORE(mem)));
582 if(flag_field)
583 fprintf(stdout, " %1o %3o", (unsigned char) a->ac_flag,
584 (unsigned char) a->ac_stat);
585 fprintf(stdout, "\n");
586 }
587
588 /*
589 * convtime converts time arg to internal value
590 * arg has form hr:min:sec, min or sec are assumed to be 0 if omitted
591 */
592 long
convtime(str)593 convtime(str)
594 char *str;
595 {
596 long hr, min, sec;
597
598 min = sec = 0;
599
600 if(sscanf(str, "%ld:%ld:%ld", &hr, &min, &sec) < 1) {
601 fatal("acctcom: bad time:", str);
602 }
603 tzset();
604 sec += (min*60);
605 sec += (hr*3600);
606 return(sec + timezone);
607 }
608
609 int
cmatch(char * comm,char * cstr)610 cmatch(char *comm, char *cstr)
611 {
612
613 char xcomm[9];
614 int i;
615
616 for(i=0;i<8;i++){
617 if(comm[i]==' '||comm[i]=='\0')
618 break;
619 xcomm[i] = comm[i];
620 }
621 xcomm[i] = '\0';
622
623 return (regex(cstr,xcomm) ? 1 : 0);
624 }
625
626 char *
cmset(char * pattern)627 cmset(char *pattern)
628 {
629
630 if((pattern=(char *)regcmp(pattern,(char *)0))==NULL){
631 fatal("pattern syntax", NULL);
632 }
633
634 return (pattern);
635 }
636
637 void
doexit(int status)638 doexit(int status)
639 {
640 if(!average)
641 exit(status);
642 if(cmdcount) {
643 fprintf(stdout, "cmds=%ld ",cmdcount);
644 fprintf(stdout, "Real=%-6.2f ",SECS(realtot)/cmdcount);
645 cputot = systot + usertot;
646 fprintf(stdout, "CPU=%-6.2f ",SECS(cputot)/cmdcount);
647 fprintf(stdout, "USER=%-6.2f ",SECS(usertot)/cmdcount);
648 fprintf(stdout, "SYS=%-6.2f ",SECS(systot)/cmdcount);
649 fprintf(stdout, "CHAR=%-8.2f ",iotot/cmdcount);
650 fprintf(stdout, "BLK=%-8.2f ",rwtot/cmdcount);
651 fprintf(stdout, "USR/TOT=%-4.2f ",usertot/cputot);
652 fprintf(stdout, "HOG=%-4.2f ",cputot/realtot);
653 fprintf(stdout, "\n");
654 }
655 else
656 fprintf(stdout, "\nNo commands matched\n");
657 exit(status);
658 }
659
660 int
isdevnull(void)661 isdevnull(void)
662 {
663 struct stat filearg;
664 struct stat devnull;
665
666 if(fstat(0,&filearg) == -1) {
667 fprintf(stderr,"acctcom: cannot stat stdin\n");
668 return (0);
669 }
670 if(stat("/dev/null",&devnull) == -1) {
671 fprintf(stderr,"acctcom: cannot stat /dev/null\n");
672 return (0);
673 }
674
675 if (filearg.st_rdev == devnull.st_rdev)
676 return (1);
677 else
678 return (0);
679 }
680
681 void
fatal(char * s1,char * s2)682 fatal(char *s1, char *s2)
683 {
684 fprintf(stderr,"acctcom: %s %s\n", s1, (s2 ? s2 : ""));
685 exit(1);
686 }
687
688 void
usage(void)689 usage(void)
690 {
691 fprintf(stderr, "Usage: acctcom [options] [files]\n");
692 fprintf(stderr, "\nWhere options can be:\n");
693 diag("-b read backwards through file");
694 diag("-f print the fork/exec flag and exit status");
695 diag("-h print hog factor (total-CPU-time/elapsed-time)");
696 diag("-i print I/O counts");
697 diag("-k show total Kcore minutes instead of memory size");
698 diag("-m show mean memory size");
699 diag("-r show CPU factor (user-time/(sys-time + user-time))");
700 diag("-t show separate system and user CPU times");
701 diag("-v don't print column headings");
702 diag("-a print average statistics of selected commands");
703 diag("-q print average statistics only");
704 diag("-l line \tshow processes belonging to terminal /dev/line");
705 diag("-u user \tshow processes belonging to user name or user ID");
706 diag("-u # \tshow processes executed by super-user");
707 diag("-u ? \tshow processes executed by unknown UID's");
708 diag("-g group show processes belonging to group name of group ID");
709 diag("-s time \tshow processes ending after time (hh[:mm[:ss]])");
710 diag("-e time \tshow processes starting before time");
711 diag("-S time \tshow processes starting after time");
712 diag("-E time \tshow processes ending before time");
713 diag("-n regex select commands matching the ed(1) regular expression");
714 diag("-o file \tdo not print, put selected pacct records into file");
715 diag("-H factor show processes that exceed hog factor");
716 diag("-O sec \tshow processes that exceed CPU system time sec");
717 diag("-C sec \tshow processes that exceed total CPU time sec");
718 diag("-I chars show processes that transfer more than char chars");
719 }
720