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