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 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 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 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 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 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 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 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 * 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 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 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 682 fatal(char *s1, char *s2) 683 { 684 fprintf(stderr,"acctcom: %s %s\n", s1, (s2 ? s2 : "")); 685 exit(1); 686 } 687 688 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