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

Defined functions

Dcmp defined in line 815; used 2 times
Kcmp defined in line 758; used 2 times
bcmp defined in line 737; used 4 times
cellcmp defined in line 713; used 3 times
col defined in line 617; used 5 times
column defined in line 587; used 2 times
dcmp defined in line 795; used 2 times
doacct defined in line 632; used 2 times
enter defined in line 1085; used 6 times
expand defined in line 935; used 7 times
finduser defined in line 976; used 4 times
getmaxuid defined in line 1007; used 1 times
getnames defined in line 992; used 1 times
htaballoc defined in line 1060; used 2 times
init defined in line 862; used 1 times
kcmp defined in line 775; used 2 times
main defined in line 283; never used
makekey defined in line 952; used 1 times
ncmp defined in line 726; used 2 times
printmoney defined in line 564; used 1 times
strip defined in line 905; used 1 times
sum defined in line 853; used 7 times
taballoc defined in line 1036; used 1 times
tabinit defined in line 1024; used 1 times
tcmp defined in line 832; used 3 times
wasuser defined in line 961; used 4 times

Defined variables

Dflg defined in line 35; used 2 times
Kflg defined in line 39; used 2 times
UserKey defined in line 950; used 3 times
acctbuf defined in line 32; used 1 times
  • in line 30
aflg defined in line 42; used 3 times
allochead defined in line 153; used 4 times
alloctail defined in line 154; used 7 times
bflg defined in line 51; used 1 times
cflg defined in line 34; used 3 times
dflg defined in line 36; used 2 times
fflg defined in line 47; used 5 times
htab defined in line 220; used 7 times
htabinstall defined in line 272; used 3 times
iflg defined in line 37; used 2 times
jflg defined in line 38; used 2 times
junkp defined in line 263; used 16 times
kflg defined in line 40; used 2 times
lflg defined in line 33; used 2 times
maxuser defined in line 273; used 4 times
mflg defined in line 52; used 2 times
ncom defined in line 230; used 5 times
newbox defined in line 155; used 5 times
nexttab defined in line 156; used 5 times
nflg defined in line 41; used 1 times
ntabs defined in line 158; used 4 times
oflg defined in line 44; used 2 times
pgdiv defined in line 277; used 4 times
rflg defined in line 43; used 14 times
savacct defined in line 260; used 3 times
sccsid defined in line 2; never used
sflg defined in line 50; used 4 times
sname defined in line 229; used 3 times
tabsleft defined in line 157; used 4 times
tcpu defined in line 224; used 6 times
tflg defined in line 45; used 2 times
thres defined in line 271; used 6 times
timem defined in line 227; used 3 times
tio defined in line 226; used 3 times
treal defined in line 223; used 4 times
tsys defined in line 225; used 6 times
uflg defined in line 48; used 3 times
usracct defined in line 259; used 3 times
utmp defined in line 54; used 1 times
  • in line 55
vflg defined in line 46; used 2 times

Defined struct's

Olduser defined in line 57; used 8 times
allocbox defined in line 148; used 14 times
chunkdesc defined in line 206; used 6 times
hashdallop defined in line 215; used 12 times
process defined in line 83; used 8 times
user defined in line 64; used 30 times

Defined union's

Tab defined in line 93; used 2 times
  • in line 98(2)

Defined typedef's

cell defined in line 98; used 27 times

Defined macros

ACCT defined in line 255; used 1 times
ALLOCQTY defined in line 1035; used 1 times
DECLITERATE defined in line 169; used 2 times
HASHCLOGGED defined in line 1077; used 1 times
ISPROCESS defined in line 144; used 5 times
ISUSER defined in line 145; used 1 times
NAMELG defined in line 55; used 3 times
NC defined in line 30; used 12 times
NHASH defined in line 214; used 7 times
PROCESSITERATE defined in line 194; used 3 times
SAVACCT defined in line 254; used 1 times
TABCHUNKS defined in line 180; used 2 times
TABDALLOP defined in line 147; used 5 times
USERITERATE defined in line 198; never used
USERKEY defined in line 143; used 3 times
USERSLOP defined in line 80; used 1 times
USRACCT defined in line 253; used 1 times
optfile defined in line 418; used 2 times
pgtok defined in line 278; used 1 times
us_cnt defined in line 69; used 4 times
us_ctime defined in line 70; used 2 times
us_imem defined in line 72; used 2 times
us_io defined in line 71; used 2 times
Last modified: 1984-12-12
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3147
Valid CSS Valid XHTML 1.0 Strict