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