1 #ifndef lint 2 static char *sccsid = "@(#)sa.c 4.13 (Berkeley) 08/01/90"; 3 #endif 4 5 /* 6 * Extensive modifications to internal data structures 7 * to allow arbitrary number of different commands and users added. 8 * 9 * Also allowed the digit option on the -v flag (interactive 10 * threshold compress) to be a digit string, so one can 11 * set the threshold > 9. 12 * 13 * Also added the -f flag, to force no interactive threshold 14 * compression with the -v flag. 15 * 16 * Robert Henry 17 * UC Berkeley 18 * 31jan81 19 */ 20 #include <stdio.h> 21 #include <ctype.h> 22 #include <sys/types.h> 23 #include <sys/acct.h> 24 #include <signal.h> 25 #include <utmp.h> 26 #include <pwd.h> 27 #include "pathnames.h" 28 29 /* interpret command time accounting */ 30 31 #define NC sizeof(acctbuf.ac_comm) 32 33 struct acct acctbuf; 34 int lflg; 35 int cflg; 36 int Dflg; 37 int dflg; 38 int iflg; 39 int jflg; 40 int Kflg; 41 int kflg; 42 int nflg; 43 int aflg; 44 int rflg; 45 int oflg; 46 int tflg; 47 int vflg; 48 int fflg; 49 int uflg; 50 int thres; 51 int sflg; 52 int bflg; 53 int mflg; 54 55 struct utmp utmp; 56 #define NAMELG (sizeof(utmp.ut_name)+1) 57 58 struct Olduser{ 59 int Us_cnt; 60 double Us_ctime; 61 double Us_io; 62 double Us_imem; 63 }; 64 65 struct user { 66 char name[NC]; /* this is <\001><user id><\000> */ 67 struct Olduser oldu; 68 char us_name[NAMELG]; 69 }; 70 #define us_cnt oldu.Us_cnt 71 #define us_ctime oldu.Us_ctime 72 #define us_io oldu.Us_io 73 #define us_imem oldu.Us_imem 74 75 /* 76 * We protect ourselves from preposterous user id's by looking 77 * through the passwd file for the highest uid allocated, and 78 * then adding 10 to that. 79 * This prevents the user structure from growing too large. 80 */ 81 #define USERSLOP 10 82 int maxuser; /* highest uid from /etc/passwd, + 10 for slop*/ 83 84 struct process { 85 char name[NC]; 86 int count; 87 double realt; 88 double cput; 89 double syst; 90 double imem; 91 double io; 92 }; 93 94 union Tab{ 95 struct process p; 96 struct user u; 97 }; 98 99 typedef union Tab cell; 100 101 int (*cmp)(); /* compares 2 cells; set to appropriate func */ 102 cell *enter(); 103 struct user *finduser(); 104 struct user *wasuser(); 105 106 /* 107 * Table elements are keyed by the name of the file exec'ed. 108 * Because on large systems, many files can be exec'ed, 109 * a static table size may grow to be too large. 110 * 111 * Table elements are allocated in chunks dynamically, linked 112 * together so that they may be retrieved sequentially. 113 * 114 * An index into the table structure is provided by hashing through 115 * a seperate hash table. 116 * The hash table is segmented, and dynamically extendable. 117 * Realize that the hash table and accounting information is kept 118 * in different segments! 119 * 120 * We have a linked list of hash table segments; within each 121 * segment we use a quadratic rehash that touches no more than 1/2 122 * of the buckets in the hash table when probing. 123 * If the probe does not find the desired symbol, it moves to the 124 * next segment, or allocates a new segment. 125 * 126 * Hash table segments are kept on the linked list with the first 127 * segment always first (that will probably contain the 128 * most frequently executed commands) and 129 * the last added segment immediately after the first segment, 130 * to hopefully gain something by locality of reference. 131 * 132 * We store the per user information in the same structure as 133 * the per exec'ed file information. This allows us to use the 134 * same managers for both, as the number of user id's may be very 135 * large. 136 * User information is keyed by the first character in the name 137 * being a '\001', followed by four bytes of (long extended) 138 * user id number, followed by a null byte. 139 * The actual user names are kept in a seperate field of the 140 * user structure, and is filled in upon demand later. 141 * Iteration through all users by low user id to high user id 142 * is done by just probing the table, which is gross. 143 */ 144 #define USERKEY '\001' 145 #define ISPROCESS(tp) (tp->p.name[0] && (tp->p.name[0] != USERKEY)) 146 #define ISUSER(tp) (tp->p.name[0] && (tp->p.name[0] == USERKEY)) 147 148 #define TABDALLOP 500 149 struct allocbox{ 150 struct allocbox *nextalloc; 151 cell tabslots[TABDALLOP]; 152 }; 153 154 struct allocbox *allochead; /*head of chunk list*/ 155 struct allocbox *alloctail; /*tail*/ 156 struct allocbox *newbox; /*for creating a new chunk*/ 157 cell *nexttab; /*next table element that is free*/ 158 int tabsleft; /*slots left in current chunk*/ 159 int ntabs; 160 /* 161 * Iterate through all symbols in the symbol table in declaration 162 * order. 163 * struct allocbox *allocwalk; 164 * cell *sp, *ub; 165 * 166 * sp points to the desired item, allocwalk and ub are there 167 * to make the iteration go. 168 */ 169 170 #define DECLITERATE(allocwalk, walkpointer, ubpointer) \ 171 for(allocwalk = allochead; \ 172 allocwalk != 0; \ 173 allocwalk = allocwalk->nextalloc) \ 174 for (walkpointer = &allocwalk->tabslots[0],\ 175 ubpointer = &allocwalk->tabslots[TABDALLOP], \ 176 ubpointer = ubpointer > ( (cell *)alloctail) \ 177 ? nexttab : ubpointer ;\ 178 walkpointer < ubpointer; \ 179 walkpointer++ ) 180 181 #define TABCHUNKS(allocwalk, tabptr, size) \ 182 for (allocwalk = allochead; \ 183 allocwalk != 0; \ 184 allocwalk = allocwalk->nextalloc) \ 185 if ( \ 186 (tabptr = &allocwalk->tabslots[0]), \ 187 (size = \ 188 ( (&allocwalk->tabslots[TABDALLOP]) \ 189 > ((cell *)alloctail) \ 190 ) \ 191 ? (nexttab - tabptr) : TABDALLOP \ 192 ), \ 193 1 \ 194 ) 195 #define PROCESSITERATE(allocwalk, walkpointer, ubpointer) \ 196 DECLITERATE(allocwalk, walkpointer, ubpointer) \ 197 if (ISPROCESS(walkpointer)) 198 199 #define USERITERATE(allocwalk, walkpointer, ubpointer) \ 200 DECLITERATE(allocwalk, walkpointer, ubpointer) \ 201 if (ISUSER(walkpointer)) 202 /* 203 * When we have to sort the segmented accounting table, we 204 * create a vector of sorted queues that is merged 205 * to sort the entire accounting table. 206 */ 207 struct chunkdesc { 208 cell *chunk_tp; 209 int chunk_n; 210 }; 211 212 /* 213 * Hash table segments and manager 214 */ 215 #define NHASH 1103 216 struct hashdallop { 217 int h_nused; 218 struct hashdallop *h_next; 219 cell *h_tab[NHASH]; 220 }; 221 struct hashdallop *htab; /* head of the list */ 222 int htabinstall; /* install the symbol */ 223 224 double treal; 225 double tcpu; 226 double tsys; 227 double tio; 228 double timem; 229 cell *junkp; 230 char *sname; 231 double ncom; 232 time_t expand(); 233 char *getname(); 234 235 /* 236 * usracct saves records of type Olduser. 237 * There is one record for every possible uid less than 238 * the largest uid seen in the previous usracct or in savacct. 239 * uid's that had no activity correspond to zero filled slots; 240 * thus one can index the file and get the user record out. 241 * It would be better to save only user information for users 242 * that the system knows about to save space, but that is not 243 * upward compatabile with the old system. 244 * 245 * In the old version of sa, uid's greater than 999 were not handled 246 * properly; this system will do that. 247 */ 248 249 #ifdef DEBUG 250 #define USRACCT "./usracct" 251 #define SAVACCT "./savacct" 252 #define ACCT "./acct" 253 #else 254 #define USRACCT _PATH_USRACCT 255 #define SAVACCT _PATH_SAVACCT 256 #define ACCT _PATH_ACCT 257 #endif DEBUG 258 259 260 char *usracct = USRACCT; 261 char *savacct = SAVACCT; 262 263 int cellcmp(); 264 cell *junkp = 0; 265 /* 266 * The threshold is built up from digits in the argv ; 267 * eg, -v1s0u1 268 * will build a value of thres of 101. 269 * 270 * If the threshold is zero after processing argv, it is set to 1 271 */ 272 int thres = 0; 273 int htabinstall = 1; 274 int maxuser = -1; 275 int (*cmp)(); 276 277 /* we assume pagesize is at least 1k */ 278 int pgdiv; 279 #define pgtok(x) ((x) * pgdiv) 280 281 extern tcmp(), ncmp(), bflgcmp(), dcmp(), Dcmp(), kcmp(), Kcmp(); 282 extern double sum(); 283 284 main(argc, argv) 285 char **argv; 286 { 287 FILE *ff; 288 double ft; 289 register struct allocbox *allocwalk; 290 register cell *tp, *ub; 291 int i, j, size, nchunks, smallest; 292 struct chunkdesc *chunkvector; 293 294 pgdiv = getpagesize() / 1024; 295 if (pgdiv == 0) 296 pgdiv = 1; 297 maxuser = USERSLOP + getmaxuid(); 298 299 tabinit(); 300 cmp = tcmp; 301 if (argc>1) 302 if (argv[1][0]=='-') { 303 argv++; 304 argc--; 305 for(i=1; argv[0][i]; i++) 306 switch(argv[0][i]) { 307 308 case 'o': 309 oflg++; 310 break; 311 312 case 'i': 313 iflg++; 314 break; 315 316 case 'b': 317 bflg++; 318 cmp = bflgcmp; 319 break; 320 321 case 'l': 322 lflg++; 323 break; 324 325 case 'c': 326 cflg++; 327 break; 328 329 case 'd': 330 dflg++; 331 cmp = dcmp; 332 break; 333 334 case 'D': 335 Dflg++; 336 cmp = Dcmp; 337 break; 338 339 case 'j': 340 jflg++; 341 break; 342 343 case 'k': 344 kflg++; 345 cmp = kcmp; 346 break; 347 348 case 'K': 349 Kflg++; 350 cmp = Kcmp; 351 break; 352 353 case 'n': 354 nflg++; 355 cmp = ncmp; 356 break; 357 358 case 'a': 359 aflg++; 360 break; 361 362 case 'r': 363 rflg++; 364 break; 365 366 case 't': 367 tflg++; 368 break; 369 370 case 's': 371 sflg++; 372 aflg++; 373 break; 374 375 case '0': 376 case '1': 377 case '2': 378 case '3': 379 case '4': 380 case '5': 381 case '6': 382 case '7': 383 case '8': 384 case '9': 385 thres = thres * 10 + (argv[0][i]-'0'); 386 break; 387 388 case 'v': 389 vflg++; 390 break; 391 392 case 'f': 393 fflg++; /* force v option; no tty interaction */ 394 break; 395 396 case 'u': 397 uflg++; 398 break; 399 400 case 'm': 401 mflg++; 402 break; 403 404 case 'U': 405 case 'S': 406 if (i != 1 || argv[0][2]) { /* gross! */ 407 fprintf(stderr, "-U and -S options must be separate\n"); 408 exit(1); 409 } 410 argc++, argv--; /* backup - yuk */ 411 goto doUS; 412 413 default: 414 fprintf(stderr, "Invalid option %c\n", argv[0][1]); 415 exit(1); 416 } 417 } 418 419 #define optfile(f) {if (argc < 2) \ 420 { fprintf(stderr, "Missing filename\n"); exit(1); } \ 421 argc--, argv++; f = argv[0]; } 422 423 doUS: 424 for (argc--, argv++; argc && argv[0][0] == '-'; argc--, argv++) { 425 switch(argv[0][1]) { 426 case 'U': 427 optfile(usracct); 428 break; 429 430 case 'S': 431 optfile(savacct); 432 break; 433 434 default: 435 fprintf(stderr, "Invalid option %c\n", argv[0][1]); 436 exit(1); 437 } 438 } 439 440 if (thres == 0) 441 thres = 1; 442 if (iflg==0) 443 init(); 444 if (argc<1) 445 doacct(ACCT); 446 else while (argc--) 447 doacct(*argv++); 448 if (uflg) { 449 return; 450 } 451 452 /* 453 * cleanup pass 454 * put junk together 455 */ 456 457 if (vflg) 458 strip(); 459 if(!aflg) 460 PROCESSITERATE(allocwalk, tp, ub){ 461 for(j=0; j<NC; j++) 462 if(tp->p.name[j] == '?') 463 goto yes; 464 if(tp->p.count != 1) 465 continue; 466 yes: 467 if(junkp == 0) 468 junkp = enter("***other"); 469 junkp->p.count += tp->p.count; 470 junkp->p.realt += tp->p.realt; 471 junkp->p.cput += tp->p.cput; 472 junkp->p.syst += tp->p.syst; 473 junkp->p.imem += tp->p.imem; 474 junkp->p.io += tp->p.io; 475 tp->p.name[0] = 0; 476 } 477 if (sflg) { 478 signal(SIGINT, SIG_IGN); 479 if ((ff = fopen(usracct, "w")) != NULL) { 480 static struct user ZeroUser = {0}; 481 struct user *up; 482 int uid; 483 /* 484 * Write out just enough user slots, 485 * filling with zero slots for users that 486 * weren't found. 487 * The file can be indexed directly by uid 488 * to get the correct record. 489 */ 490 for (uid = 0; uid < maxuser; uid++){ 491 if ( (up = wasuser(uid)) != 0) 492 fwrite((char *)&(up->oldu), 493 sizeof(struct Olduser),1,ff); 494 else 495 fwrite((char *)&(ZeroUser.oldu), 496 sizeof(struct Olduser),1,ff); 497 } 498 } 499 if ((ff = fopen(savacct, "w")) == NULL) { 500 printf("Can't save\n"); 501 exit(0); 502 } 503 PROCESSITERATE(allocwalk, tp, ub) 504 fwrite((char *)&(tp->p), sizeof(struct process), 1, ff); 505 fclose(ff); 506 creat(sname, 0644); 507 signal(SIGINT, SIG_DFL); 508 } 509 /* 510 * sort and print 511 */ 512 if (mflg) { 513 printmoney(); 514 exit(0); 515 } 516 column(ncom, treal, tcpu, tsys, timem, tio); 517 printf("\n"); 518 519 /* 520 * the fragmented table is sorted by sorting each fragment 521 * and then merging. 522 */ 523 nchunks = 0; 524 TABCHUNKS(allocwalk, tp, size){ 525 qsort(tp, size, sizeof(cell), cellcmp); 526 nchunks ++; 527 } 528 chunkvector = (struct chunkdesc *)calloc(nchunks, 529 sizeof(struct chunkdesc)); 530 nchunks = 0; 531 TABCHUNKS(allocwalk, tp, size){ 532 chunkvector[nchunks].chunk_tp = tp; 533 chunkvector[nchunks].chunk_n = size; 534 nchunks++; 535 } 536 for(; nchunks; ){ 537 /* 538 * Find the smallest element at the head of the queues. 539 */ 540 smallest = 0; 541 for (i = 1; i < nchunks; i++){ 542 if (cellcmp(chunkvector[i].chunk_tp, 543 chunkvector[smallest].chunk_tp) < 0) 544 smallest = i; 545 } 546 tp = chunkvector[smallest].chunk_tp++; 547 /* 548 * If this queue is drained, drop the chunk count, 549 * and readjust the queues. 550 */ 551 if (--chunkvector[smallest].chunk_n == 0){ 552 nchunks--; 553 for (i = smallest; i < nchunks; i++) 554 chunkvector[i] = chunkvector[i+1]; 555 } 556 if (ISPROCESS(tp)){ 557 ft = tp->p.count; 558 column(ft, tp->p.realt, tp->p.cput, 559 tp->p.syst, tp->p.imem, tp->p.io); 560 printf(" %.14s\n", tp->p.name); 561 } 562 } /* iterate to merge the lists */ 563 } 564 565 printmoney() 566 { 567 register i; 568 register char *cp; 569 register struct user *up; 570 571 getnames(); /* fetches all of the names! */ 572 for (i = 0; i < maxuser; i++) { 573 if ( (up = wasuser(i)) != 0){ 574 if (up->us_cnt) { 575 if (up->us_name[0]) 576 printf("%-8s", up->us_name); 577 else 578 printf("%-8d", i); 579 printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n", 580 up->us_cnt, up->us_ctime / 60, 581 up->us_io, 582 up->us_imem / AHZ); 583 } 584 } 585 } 586 } 587 588 column(n, a, b, c, d, e) 589 double n, a, b, c, d, e; 590 { 591 592 printf("%8.0f", n); 593 if(cflg) { 594 if(n == ncom) 595 printf("%9s", ""); else 596 printf("%8.2f%%", 100.*n/ncom); 597 } 598 col(n, a, treal, "re"); 599 if (oflg) 600 col(n, 60*AHZ*(b/(b+c)), tcpu+tsys, "u/s"); 601 else if(lflg) { 602 col(n, b, tcpu, "u"); 603 col(n, c, tsys, "s"); 604 } else 605 col(n, b+c, tcpu+tsys, "cp"); 606 if(tflg) 607 printf("%8.1fre/cp", a/(b+c)); 608 if(dflg || !Dflg) 609 printf("%10.0favio", e/(n?n:1)); 610 else 611 printf("%10.0ftio", e); 612 if (kflg || !Kflg) 613 printf("%10.0fk", d/((b+c)!=0.0?(b+c):1.0)); 614 else 615 printf("%10.0fk*sec", d/AHZ); 616 } 617 618 col(n, a, m, cp) 619 double n, a, m; 620 char *cp; 621 { 622 623 if(jflg) 624 printf("%11.2f%s", a/(n*(double)AHZ), cp); else 625 printf("%11.2f%s", a/(60.*(double)AHZ), cp); 626 if(cflg) { 627 if(a == m) 628 printf("%9s", ""); else 629 printf("%8.2f%%", 100.*a/m); 630 } 631 } 632 633 doacct(f) 634 char *f; 635 { 636 FILE *ff; 637 long x, y, z; 638 struct acct fbuf; 639 register char *cp; 640 register int c; 641 register struct user *up; 642 register cell *tp; 643 #ifdef DEBUG 644 int nrecords = 0; 645 #endif DEBUG 646 647 if (sflg && sname) { 648 printf("Only 1 file with -s\n"); 649 exit(0); 650 } 651 if (sflg) 652 sname = f; 653 if ((ff = fopen(f, "r"))==NULL) { 654 printf("Can't open %s\n", f); 655 return; 656 } 657 while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) { 658 #ifdef DEBUG 659 if (++nrecords % 1000 == 0) 660 printf("Input record from %s number %d\n", 661 f, nrecords); 662 #endif DEBUG 663 for (cp = fbuf.ac_comm; *cp && cp < &fbuf.ac_comm[NC]; cp++) 664 if (!isascii(*cp) || iscntrl(*cp)) 665 *cp = '?'; 666 if (cp == fbuf.ac_comm) 667 *cp++ = '?'; 668 if (fbuf.ac_flag&AFORK) { 669 if (cp >= &fbuf.ac_comm[NC]) 670 cp = &fbuf.ac_comm[NC-1]; 671 *cp++ = '*'; 672 } 673 if (cp < &fbuf.ac_comm[NC]) 674 *cp = '\0'; 675 x = expand(fbuf.ac_utime) + expand(fbuf.ac_stime); 676 y = pgtok((u_short)fbuf.ac_mem); 677 z = expand(fbuf.ac_io) / AHZ; 678 if (uflg) { 679 printf("%3d %6.2f cpu %8luk mem %6ld io %.*s\n", 680 fbuf.ac_uid, x/(double)AHZ, y, z, NC, fbuf.ac_comm); 681 continue; 682 } 683 up = finduser(fbuf.ac_uid); 684 if (up == 0) 685 continue; /* preposterous user id */ 686 up->us_cnt++; 687 up->us_ctime += x/(double)AHZ; 688 up->us_imem += x * y; 689 up->us_io += z; 690 ncom += 1.0; 691 692 tp = enter(fbuf.ac_comm); 693 tp->p.imem += x * y; 694 timem += x * y; 695 tp->p.count++; 696 x = expand(fbuf.ac_etime); 697 tp->p.realt += x; 698 treal += x; 699 x = expand(fbuf.ac_utime); 700 tp->p.cput += x; 701 tcpu += x; 702 x = expand(fbuf.ac_stime); 703 tp->p.syst += x; 704 tsys += x; 705 tp->p.io += z; 706 tio += z; 707 } 708 fclose(ff); 709 } 710 711 /* 712 * Generalized cell compare routine, to cast out users 713 */ 714 cellcmp(p1, p2) 715 cell *p1, *p2; 716 { 717 if (ISPROCESS(p1)){ 718 if (ISPROCESS(p2)) 719 return((*cmp)(p1, p2)); 720 return(-1); 721 } 722 if (ISPROCESS(p2)) 723 return(1); 724 return(0); 725 } 726 727 ncmp(p1, p2) 728 cell *p1, *p2; 729 { 730 731 if(p1->p.count == p2->p.count) 732 return(tcmp(p1, p2)); 733 if(rflg) 734 return(p1->p.count - p2->p.count); 735 return(p2->p.count - p1->p.count); 736 } 737 738 bflgcmp(p1, p2) 739 cell *p1, *p2; 740 { 741 double f1, f2; 742 double sum(); 743 744 f1 = sum(p1)/p1->p.count; 745 f2 = sum(p2)/p2->p.count; 746 if(f1 < f2) { 747 if(rflg) 748 return(-1); 749 return(1); 750 } 751 if(f1 > f2) { 752 if(rflg) 753 return(1); 754 return(-1); 755 } 756 return(0); 757 } 758 759 Kcmp(p1, p2) 760 cell *p1, *p2; 761 { 762 763 if (p1->p.imem < p2->p.imem) { 764 if(rflg) 765 return(-1); 766 return(1); 767 } 768 if (p1->p.imem > p2->p.imem) { 769 if(rflg) 770 return(1); 771 return(-1); 772 } 773 return(0); 774 } 775 776 kcmp(p1, p2) 777 cell *p1, *p2; 778 { 779 double a1, a2; 780 781 a1 = p1->p.imem / ((p1->p.cput+p1->p.syst)?(p1->p.cput+p1->p.syst):1); 782 a2 = p2->p.imem / ((p2->p.cput+p2->p.syst)?(p2->p.cput+p2->p.syst):1); 783 if (a1 < a2) { 784 if(rflg) 785 return(-1); 786 return(1); 787 } 788 if (a1 > a2) { 789 if(rflg) 790 return(1); 791 return(-1); 792 } 793 return(0); 794 } 795 796 dcmp(p1, p2) 797 cell *p1, *p2; 798 { 799 double a1, a2; 800 801 a1 = p1->p.io / (p1->p.count?p1->p.count:1); 802 a2 = p2->p.io / (p2->p.count?p2->p.count:1); 803 if (a1 < a2) { 804 if(rflg) 805 return(-1); 806 return(1); 807 } 808 if (a1 > a2) { 809 if(rflg) 810 return(1); 811 return(-1); 812 } 813 return(0); 814 } 815 816 Dcmp(p1, p2) 817 cell *p1, *p2; 818 { 819 820 if (p1->p.io < p2->p.io) { 821 if(rflg) 822 return(-1); 823 return(1); 824 } 825 if (p1->p.io > p2->p.io) { 826 if(rflg) 827 return(1); 828 return(-1); 829 } 830 return(0); 831 } 832 833 tcmp(p1, p2) 834 cell *p1, *p2; 835 { 836 extern double sum(); 837 double f1, f2; 838 839 f1 = sum(p1); 840 f2 = sum(p2); 841 if(f1 < f2) { 842 if(rflg) 843 return(-1); 844 return(1); 845 } 846 if(f1 > f2) { 847 if(rflg) 848 return(1); 849 return(-1); 850 } 851 return(0); 852 } 853 854 double sum(p) 855 cell *p; 856 { 857 858 if(p->p.name[0] == 0) 859 return(0.0); 860 return( p->p.cput + p->p.syst); 861 } 862 863 init() 864 { 865 struct user userbuf; 866 struct process tbuf; 867 register cell *tp; 868 register struct user *up; 869 int uid; 870 FILE *f; 871 872 if ((f = fopen(savacct, "r")) == NULL) 873 goto gshm; 874 while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) { 875 tp = enter(tbuf.name); 876 ncom += tbuf.count; 877 tp->p.count = tbuf.count; 878 treal += tbuf.realt; 879 tp->p.realt = tbuf.realt; 880 tcpu += tbuf.cput; 881 tp->p.cput = tbuf.cput; 882 tsys += tbuf.syst; 883 tp->p.syst = tbuf.syst; 884 tio += tbuf.io; 885 tp->p.io = tbuf.io; 886 timem += tbuf.imem; 887 tp->p.imem = tbuf.imem; 888 } 889 fclose(f); 890 gshm: 891 if ((f = fopen(usracct, "r")) == NULL) 892 return; 893 for(uid = 0; 894 fread((char *)&(userbuf.oldu), sizeof(struct Olduser), 1, f) == 1; 895 uid++){ 896 if (userbuf.us_cnt){ 897 up = finduser(uid); 898 if (up == 0) 899 continue; /* preposterous user id */ 900 up->oldu = userbuf.oldu; 901 } 902 } 903 fclose(f); 904 } 905 906 strip() 907 { 908 int c; 909 register struct allocbox *allocwalk; 910 register cell *tp, *ub, *junkp; 911 912 if (fflg) 913 printf("Categorizing commands used %d times or fewer as **junk**\n", 914 thres); 915 junkp = enter("**junk**"); 916 PROCESSITERATE(allocwalk, tp, ub){ 917 if (tp->p.name[0] && tp->p.count <= thres) { 918 if (!fflg) 919 printf("%.14s--", tp->p.name); 920 if (fflg || ((c=getchar())=='y')) { 921 tp->p.name[0] = '\0'; 922 junkp->p.count += tp->p.count; 923 junkp->p.realt += tp->p.realt; 924 junkp->p.cput += tp->p.cput; 925 junkp->p.syst += tp->p.syst; 926 junkp->p.imem += tp->p.imem; 927 junkp->p.io += tp->p.io; 928 } 929 if (!fflg) 930 while (c && c!='\n') 931 c = getchar(); 932 } 933 } 934 } 935 936 time_t 937 expand(t) 938 unsigned t; 939 { 940 register time_t nt; 941 942 nt = t&017777; 943 t >>= 13; 944 while (t!=0) { 945 t--; 946 nt <<= 3; 947 } 948 return(nt); 949 } 950 951 static char UserKey[NAMELG + 2]; 952 953 char * 954 makekey(uid) 955 int uid; 956 { 957 (void)sprintf(UserKey+1, "%04x", uid); 958 UserKey[0] = USERKEY; 959 return(UserKey); 960 } 961 962 struct user * 963 wasuser(uid) 964 int uid; 965 { 966 struct user *tp; 967 968 htabinstall = 0; 969 tp = finduser(uid); 970 htabinstall = 1; 971 return(tp); 972 } 973 974 /* 975 * Only call this if you really want to insert it in the table! 976 */ 977 struct user * 978 finduser(uid) 979 int uid; 980 { 981 982 if (uid > maxuser){ 983 fprintf(stderr, "Preposterous user id, %d: ignored\n", uid); 984 return(0); 985 } 986 return((struct user*)enter(makekey(uid))); 987 } 988 989 /* 990 * Set the names of all users in the password file. 991 * We will later not print those that didn't do anything. 992 */ 993 getnames() 994 { 995 register struct user *tp; 996 register struct passwd *pw; 997 struct passwd *getpwent(); 998 999 setpwent(); 1000 while (pw = getpwent()){ 1001 /* use first name in passwd file for duplicate uid's */ 1002 if ((tp = wasuser(pw->pw_uid)) != 0 && !isalpha(tp->us_name[0])) 1003 strncpy(tp->us_name, pw->pw_name, NAMELG); 1004 } 1005 endpwent(); 1006 } 1007 1008 int 1009 getmaxuid() 1010 { 1011 register struct user *tp; 1012 register struct passwd *pw; 1013 struct passwd *getpwent(); 1014 int maxuid = -1; 1015 1016 setpwent(); 1017 while(pw = getpwent()){ 1018 if (pw->pw_uid > maxuid) 1019 maxuid = pw->pw_uid; 1020 } 1021 endpwent(); 1022 return(maxuid); 1023 } 1024 1025 tabinit() 1026 { 1027 allochead = 0; 1028 alloctail = 0; 1029 nexttab = 0; 1030 tabsleft = 0; 1031 htab = 0; 1032 ntabs = 0; 1033 htaballoc(); /* get the first part of the hash table */ 1034 } 1035 1036 #define ALLOCQTY sizeof (struct allocbox) 1037 cell * 1038 taballoc() 1039 { 1040 1041 if (tabsleft == 0){ 1042 newbox = (struct allocbox *)calloc(1, ALLOCQTY); 1043 tabsleft = TABDALLOP; 1044 nexttab = &newbox->tabslots[0]; 1045 if (alloctail == 0){ 1046 allochead = alloctail = newbox; 1047 } else { 1048 alloctail->nextalloc = newbox; 1049 alloctail = newbox; 1050 } 1051 } 1052 --tabsleft; 1053 ++ntabs; 1054 #ifdef DEBUG 1055 if (ntabs % 100 == 0) 1056 printf("##Accounting table slot # %d\n", ntabs); 1057 #endif DEBUG 1058 return(nexttab++); 1059 } 1060 1061 htaballoc() 1062 { 1063 register struct hashdallop *new; 1064 #ifdef DEBUG 1065 static int ntables = 0; 1066 1067 printf("%%%New hash table chunk allocated, number %d\n", ++ntables); 1068 #endif DEBUG 1069 new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop)); 1070 if (htab == 0) 1071 htab = new; 1072 else { /* add AFTER the 1st slot */ 1073 new->h_next = htab->h_next; 1074 htab->h_next = new; 1075 } 1076 } 1077 1078 #define HASHCLOGGED (NHASH / 2) 1079 /* 1080 * Lookup a symbol passed in as the argument. 1081 * 1082 * We take pains to avoid function calls; this function 1083 * is called quite frequently, and the calling overhead 1084 * contributes significantly to the overall execution speed of sa. 1085 */ 1086 cell * 1087 enter(name) 1088 char *name; 1089 { 1090 static int initialprobe; 1091 register cell **hp; 1092 register char *from, *to; 1093 register int len, nprobes; 1094 static struct hashdallop *hdallop, *emptyhd; 1095 static cell **emptyslot, **hp_ub; 1096 1097 emptyslot = 0; 1098 for (nprobes = 0, from = name, len = 0; 1099 *from && len < NC; 1100 nprobes <<= 2, nprobes += *from++, len++) 1101 continue; 1102 nprobes += from[-1] << 5; 1103 nprobes %= NHASH; 1104 if (nprobes < 0) 1105 nprobes += NHASH; 1106 1107 initialprobe = nprobes; 1108 for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){ 1109 for (hp = &(hdallop->h_tab[initialprobe]), 1110 nprobes = 1, 1111 hp_ub = &(hdallop->h_tab[NHASH]); 1112 (*hp) && (nprobes < NHASH); 1113 hp += nprobes, 1114 hp -= (hp >= hp_ub) ? NHASH:0, 1115 nprobes += 2) 1116 { 1117 from = name; 1118 to = (*hp)->p.name; 1119 1120 for (len = 0; (len<NC) && *from; len++) 1121 if (*from++ != *to++) 1122 goto nextprobe; 1123 if (len >= NC) /*both are maximal length*/ 1124 return(*hp); 1125 if (*to == 0) /*assert *from == 0*/ 1126 return(*hp); 1127 nextprobe: ; 1128 } 1129 if (*hp == 0 && emptyslot == 0 && 1130 hdallop->h_nused < HASHCLOGGED) { 1131 emptyslot = hp; 1132 emptyhd = hdallop; 1133 } 1134 } 1135 if (emptyslot == 0) { 1136 htaballoc(); 1137 hdallop = htab->h_next; /* aren't we smart! */ 1138 hp = &hdallop->h_tab[initialprobe]; 1139 } else { 1140 hdallop = emptyhd; 1141 hp = emptyslot; 1142 } 1143 if (htabinstall){ 1144 *hp = taballoc(); 1145 hdallop->h_nused++; 1146 for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++) 1147 if ((*to++ = *from++) == '\0') 1148 break; 1149 return(*hp); 1150 } 1151 return(0); 1152 } 1153