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