1: #if defined(DOSCCS) && !defined(lint)
   2: static char *sccsid = "@(#)sa.c	4.9.2 (2.11BSD GTE) 1997/2/14";
   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 added the -f flag, to force no interactive threshold
  10:  *	compression with the -v flag.
  11:  *
  12:  *	Robert Henry
  13:  *	UC Berkeley
  14:  *	31jan81
  15:  */
  16: 
  17: #include <stdio.h>
  18: #include <ctype.h>
  19: #include <sys/types.h>
  20: #include <sys/acct.h>
  21: #include <signal.h>
  22: #include <utmp.h>
  23: #include <pwd.h>
  24: #include <sysexits.h>
  25: #include <stdlib.h>
  26: 
  27: /* interpret command time accounting */
  28: 
  29: #define NC  sizeof(acctbuf.ac_comm)
  30: 
  31: struct acct acctbuf;
  32: int lflg;
  33: int cflg;
  34: int Dflg;
  35: int dflg;
  36: int iflg;
  37: int jflg;
  38: int Kflg;
  39: int kflg;
  40: int nflg;
  41: int aflg;
  42: int rflg;
  43: int oflg;
  44: int tflg;
  45: int vflg;
  46: int fflg;
  47: int uflg;
  48: int thres;
  49: int sflg;
  50: int bflg;
  51: int mflg;
  52: 
  53: struct  utmp    utmp;
  54: #define NAMELG  (sizeof(utmp.ut_name)+1)
  55: 
  56: struct  Olduser{
  57:     int Us_cnt;
  58:     double  Us_ctime;
  59:     double  Us_io;
  60:     double  Us_imem;
  61: };
  62: 
  63: struct  user {
  64:     char    name[NC];       /* this is <\001><user id><\000> */
  65:     struct  Olduser oldu;
  66:     char    us_name[NAMELG];
  67: };
  68: #define us_cnt      oldu.Us_cnt
  69: #define us_ctime    oldu.Us_ctime
  70: #define us_io       oldu.Us_io
  71: #define us_imem     oldu.Us_imem
  72: 
  73: /*
  74:  *	We protect ourselves from preposterous user id's by looking
  75:  *	through the passwd file for the highest uid allocated, and
  76:  *	then adding 10 to that.
  77:  *	This prevents the user structure from growing too large.
  78:  */
  79: #define USERSLOP    10
  80: uid_t   maxuser;        /* highest uid from /etc/passwd, + 10 for slop*/
  81: 
  82: struct  process {
  83:     char    name[NC];
  84:     int count;
  85:     double  realt;
  86:     double  cput;
  87:     double  syst;
  88:     double  imem;
  89:     double  io;
  90: };
  91: 
  92: union   Tab{
  93:     struct  process p;
  94:     struct  user    u;
  95: };
  96: 
  97: typedef union Tab cell;
  98: 
  99: int (*cmp)();   /* compares 2 cells; set to appropriate func */
 100: cell    *enter();
 101: uid_t   getmaxuid();
 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: double  ncom;
 230: time_t  expand();
 231: 
 232: /*
 233:  *	usracct saves records of type Olduser.
 234:  *	There is one record for every possible uid less than
 235:  *	the largest uid seen in the previous usracct or in savacct.
 236:  *	uid's that had no activity correspond to zero filled slots;
 237:  *	thus one can index the file and get the user record out.
 238:  *	It would be better to save only user information for users
 239:  *	that the system knows about to save space, but that is not
 240:  *	upward compatabile with the old system.
 241:  *
 242:  *	In the old version of sa, uid's greater than 999 were not handled
 243:  *	properly; this system will do that.
 244:  */
 245: 
 246: #ifdef  DEBUG
 247: #define USRACCT "./usracct"
 248: #define SAVACCT "./savacct"
 249: #define ACCT    "./acct"
 250: #else
 251: #define USRACCT "/usr/adm/usracct"
 252: #define SAVACCT "/usr/adm/savacct"
 253: #define ACCT    "/usr/adm/acct"
 254: #endif	DEBUG
 255: 
 256: char *usracct = USRACCT;
 257: char *savacct = SAVACCT;
 258: 
 259: int cellcmp();
 260: cell    *junkp = 0;
 261: int thres = 0;
 262: int htabinstall = 1;
 263: int (*cmp)();
 264: 
 265: /* we assume pagesize is at least 1k */
 266: int pgdiv;
 267: #define pgtok(x)    ((x) / pgdiv)
 268: 
 269: extern  tcmp(), ncmp(), Bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
 270: extern  double sum();
 271: 
 272: main(argc, argv)
 273:     char **argv;
 274: {
 275:     FILE *ff;
 276:     double ft;
 277:     register struct allocbox *allocwalk;
 278:     register cell *tp, *ub;
 279:     char    *acctfn;
 280:     int i, j, size, nchunks, smallest, c;
 281:     struct chunkdesc *chunkvector;
 282: 
 283:     pgdiv = getpagesize() / 1024;
 284:     if (pgdiv == 0)
 285:         pgdiv = 1;
 286:     maxuser = getmaxuid();
 287: 
 288:     tabinit();
 289:     cmp = tcmp;
 290: 
 291:     while   ((c = getopt(argc, argv, "oiblcdDjkKnartsv:fumU:S:")) != EOF)
 292:         {
 293:         switch  (c)
 294:             {
 295:             case    'o':
 296:                 oflg++;
 297:                 break;
 298:             case    'i':
 299:                 iflg++;
 300:                 break;
 301:             case    'b':
 302:                 bflg++;
 303:                 cmp = Bcmp;
 304:                 break;
 305:             case    'l':
 306:                 lflg++;
 307:                 break;
 308:             case    'c':
 309:                 cflg++;
 310:                 break;
 311:             case    'd':
 312:                 dflg++;
 313:                 cmp = dcmp;
 314:                 break;
 315:             case    'D':
 316:                 Dflg++;
 317:                 cmp = Dcmp;
 318:                 break;
 319:             case    'j':
 320:                 jflg++;
 321:                 break;
 322:             case    'k':
 323:                 kflg++;
 324:                 cmp = kcmp;
 325:                 break;
 326:             case    'K':
 327:                 Kflg++;
 328:                 cmp = Kcmp;
 329:                 break;
 330:             case    'n':
 331:                 nflg++;
 332:                 cmp = ncmp;
 333:                 break;
 334:             case    'a':
 335:                 aflg++;
 336:                 break;
 337:             case    'r':
 338:                 rflg++;
 339:                 break;
 340:             case    't':
 341:                 tflg++;
 342:                 break;
 343:             case    's':
 344:                 sflg++;
 345:                 aflg++;
 346:                 break;
 347:             case    'v':
 348:                 vflg++;
 349:                 thres = atoi(optarg);
 350:                 break;
 351:             case    'f':
 352:                 fflg++; /* force v option; no tty interaction */
 353:                 break;
 354:             case    'u':
 355:                 uflg++;
 356:                 break;
 357:             case    'm':
 358:                 mflg++;
 359:                 break;
 360:             case    'U':
 361:                 usracct = optarg;
 362:                 break;
 363:             case    'S':
 364:                 savacct = optarg;
 365:                 break;
 366:             default:
 367:                 (void)usage();
 368:                 /* NOTREACHED */
 369:             }
 370:         }
 371:     switch  (argc - optind)
 372:         {
 373:         case    1:
 374:             acctfn = argv[optind];
 375:             break;
 376:         case    0:
 377:             acctfn = ACCT;
 378:             break;
 379:         default:
 380:             (void)usage();
 381:             /* NOTREACHED */
 382:         }
 383: 
 384:     if  (thres == 0)
 385:         thres = 1;
 386:     if  (iflg==0)
 387:         init();
 388:     doacct(acctfn);
 389:     if  (uflg)
 390:         return;
 391: 
 392: /*
 393:  * cleanup pass
 394:  * put junk together
 395:  */
 396: 
 397:     if (vflg)
 398:         strip();
 399:     if(!aflg)
 400:     PROCESSITERATE(allocwalk, tp, ub){
 401:         for(j=0; j<NC; j++)
 402:             if(tp->p.name[j] == '?')
 403:                 goto yes;
 404:         if(tp->p.count != 1)
 405:             continue;
 406:     yes:
 407:         if(junkp == 0)
 408:             junkp = enter("***other");
 409:         junkp->p.count += tp->p.count;
 410:         junkp->p.realt += tp->p.realt;
 411:         junkp->p.cput += tp->p.cput;
 412:         junkp->p.syst += tp->p.syst;
 413:         junkp->p.imem += tp->p.imem;
 414:         junkp->p.io += tp->p.io;
 415:         tp->p.name[0] = 0;
 416:     }
 417:     if (sflg) {
 418:         signal(SIGINT, SIG_IGN);
 419:         if ((ff = fopen(usracct, "w")) != NULL) {
 420:             static  struct  user ZeroUser = {0};
 421:             struct  user    *up;
 422:             uid_t   uid;
 423:             /*
 424: 			 *	Write out just enough user slots,
 425: 			 *	filling with zero slots for users that
 426: 			 *	weren't found.
 427: 			 *	The file can be indexed directly by uid
 428: 			 *	to get the correct record.
 429: 			 */
 430:             for (uid = 0; uid < maxuser; uid++){
 431:                 if ( (up = wasuser(uid)) != 0)
 432:                     fwrite((char *)&(up->oldu),
 433:                         sizeof(struct Olduser),1,ff);
 434:                 else
 435:                     fwrite((char *)&(ZeroUser.oldu),
 436:                         sizeof(struct Olduser),1,ff);
 437:             }
 438:         }
 439:         if ((ff = fopen(savacct, "w")) == NULL) {
 440:             printf("Can't save\n");
 441:             exit(EX_OK);
 442:         }
 443:         PROCESSITERATE(allocwalk, tp, ub)
 444:             fwrite((char *)&(tp->p), sizeof(struct process), 1, ff);
 445:         fclose(ff);
 446:         signal(SIGINT, SIG_DFL);
 447:     }
 448: /*
 449:  * sort and print
 450:  */
 451:     if (mflg) {
 452:         printmoney();
 453:         exit(EX_OK);
 454:     }
 455:     column(ncom, treal, tcpu, tsys, timem, tio);
 456:     printf("\n");
 457: 
 458:     /*
 459: 	 *	the fragmented table is sorted by sorting each fragment
 460: 	 *	and then merging.
 461: 	 */
 462:     nchunks = 0;
 463:     TABCHUNKS(allocwalk, tp, size){
 464:         qsort(tp, size, sizeof(cell), cellcmp);
 465:         nchunks ++;
 466:     }
 467:     chunkvector = (struct chunkdesc *)calloc(nchunks,
 468:         sizeof(struct chunkdesc));
 469:     nchunks = 0;
 470:     TABCHUNKS(allocwalk, tp, size){
 471:         chunkvector[nchunks].chunk_tp = tp;
 472:         chunkvector[nchunks].chunk_n = size;
 473:         nchunks++;
 474:     }
 475:     for(; nchunks; ){
 476:         /*
 477: 		 *	Find the smallest element at the head of the queues.
 478: 		 */
 479:         smallest = 0;
 480:         for (i = 1; i < nchunks; i++){
 481:             if (cellcmp(chunkvector[i].chunk_tp,
 482:                 chunkvector[smallest].chunk_tp) < 0)
 483:                     smallest = i;
 484:         }
 485:         tp = chunkvector[smallest].chunk_tp++;
 486:         /*
 487: 		 *	If this queue is drained, drop the chunk count,
 488: 		 *	and readjust the queues.
 489: 		 */
 490:         if (--chunkvector[smallest].chunk_n == 0){
 491:             nchunks--;
 492:             for (i = smallest; i < nchunks; i++)
 493:                 chunkvector[i] = chunkvector[i+1];
 494:         }
 495:         if (ISPROCESS(tp)){
 496:             ft = tp->p.count;
 497:             column(ft, tp->p.realt, tp->p.cput,
 498:                 tp->p.syst, tp->p.imem, tp->p.io);
 499:             printf("   %.14s\n", tp->p.name);
 500:         }
 501:     }   /* iterate to merge the lists */
 502: }
 503: 
 504: void
 505: usage()
 506:     {
 507:     fprintf(stderr, "Usage sa [-oiblcdDjkKnartsfum] [-S savacct] [-U usracct] [file]\n");
 508:     exit(EX_USAGE);
 509:     }
 510: 
 511: printmoney()
 512: {
 513:     register uid_t uid;
 514:     register char *cp;
 515:     register    struct user *up;
 516: 
 517:     getnames();     /* fetches all of the names! */
 518:     for (uid = 0; uid < maxuser; uid++) {
 519:         if ( (up = wasuser(uid)) != 0){
 520:             if (up->us_cnt) {
 521:                 if (up->us_name[0])
 522:                     printf("%-8s", up->us_name);
 523:                 else
 524:                     printf("%-8u", uid);
 525:                 printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n",
 526:                     up->us_cnt, up->us_ctime / 60,
 527:                     up->us_io,
 528:                     up->us_imem / AHZ);
 529:             }
 530:         }
 531:     }
 532: }
 533: 
 534: column(n, a, b, c, d, e)
 535:     double n, a, b, c, d, e;
 536: {
 537: 
 538:     printf("%8.0f", n);
 539:     if(cflg) {
 540:         if(n == ncom)
 541:             printf("%9s", ""); else
 542:             printf("%8.2f%%", 100.*n/ncom);
 543:     }
 544:     col(n, a, treal, "re");
 545:     if (oflg)
 546:         col(n, 60*AHZ*(b/(b+c)), tcpu+tsys, "u/s");
 547:     else if(lflg) {
 548:         col(n, b, tcpu, "u");
 549:         col(n, c, tsys, "s");
 550:     } else
 551:         col(n, b+c, tcpu+tsys, "cp");
 552:     if(tflg)
 553:         printf("%8.1fre/cp", a/(b+c));
 554:     if(dflg || !Dflg)
 555:         printf("%10.0favio", e/(n?n:1));
 556:     else
 557:         printf("%10.0ftio", e);
 558:     if (kflg || !Kflg)
 559:         printf("%10.0fk", d/((b+c)!=0.0?(b+c):1.0));
 560:     else
 561:         printf("%10.0fk*sec", d/AHZ);
 562: }
 563: 
 564: col(n, a, m, cp)
 565:     double n, a, m;
 566:     char *cp;
 567: {
 568: 
 569:     if(jflg)
 570:         printf("%11.2f%s", a/(n*(double)AHZ), cp); else
 571:         printf("%11.2f%s", a/(60.*(double)AHZ), cp);
 572:     if(cflg) {
 573:         if(a == m)
 574:             printf("%9s", ""); else
 575:             printf("%8.2f%%", 100.*a/m);
 576:     }
 577: }
 578: 
 579: doacct(f)
 580: char *f;
 581: {
 582:     FILE *ff;
 583:     long x, y, z;
 584:     struct acct fbuf;
 585:     register char *cp;
 586:     register int c;
 587:     register struct user *up;
 588:     register cell *tp;
 589: #ifdef DEBUG
 590:     int nrecords = 0;
 591: #endif DEBUG
 592: 
 593:     if ((ff = fopen(f, "r"))==NULL) {
 594:         fprintf(stderr, "Can't open %s\n", f);
 595:         return;
 596:     }
 597:     while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) {
 598: #ifdef DEBUG
 599:         if (++nrecords % 1000 == 0)
 600:             fprintf(stderr, "Input record from %s number %d\n",
 601:                 f, nrecords);
 602: #endif DEBUG
 603:         for (cp = fbuf.ac_comm; *cp && cp < &fbuf.ac_comm[NC]; cp++)
 604:             if (!isascii(*cp) || iscntrl(*cp))
 605:                 *cp = '?';
 606:         if (cp == fbuf.ac_comm)
 607:             *cp++ = '?';
 608:         if (fbuf.ac_flag&AFORK) {
 609:             if (cp >= &fbuf.ac_comm[NC])
 610:                 cp = &fbuf.ac_comm[NC-1];
 611:             *cp++ = '*';
 612:         }
 613:         if (cp < &fbuf.ac_comm[NC])
 614:             *cp = '\0';
 615:         x = expand(fbuf.ac_utime) + expand(fbuf.ac_stime);
 616:         y = pgtok((u_short)fbuf.ac_mem);
 617:         z = expand(fbuf.ac_io) / AHZ;
 618:         if (uflg) {
 619:             printf("%3u %6.2f cpu %8luk mem %6ld io %.*s\n",
 620:                 fbuf.ac_uid, x/(double)AHZ, y, z, NC, fbuf.ac_comm);
 621:             continue;
 622:         }
 623:         up = finduser(fbuf.ac_uid);
 624:         if (up == 0)
 625:             continue;   /* preposterous user id */
 626:         up->us_cnt++;
 627:         up->us_ctime += x/(double)AHZ;
 628:         up->us_imem += x * y;
 629:         up->us_io += z;
 630:         ncom += 1.0;
 631: 
 632:         tp = enter(fbuf.ac_comm);
 633:         tp->p.imem += x * y;
 634:         timem += x * y;
 635:         tp->p.count++;
 636:         x = expand(fbuf.ac_etime);
 637:         tp->p.realt += x;
 638:         treal += x;
 639:         x = expand(fbuf.ac_utime);
 640:         tp->p.cput += x;
 641:         tcpu += x;
 642:         x = expand(fbuf.ac_stime);
 643:         tp->p.syst += x;
 644:         tsys += x;
 645:         tp->p.io += z;
 646:         tio += z;
 647:     }
 648:     fclose(ff);
 649: }
 650: 
 651: /*
 652:  *	Generalized cell compare routine, to cast out users
 653:  */
 654: cellcmp(p1, p2)
 655:     register cell *p1, *p2;
 656: {
 657:     if (ISPROCESS(p1)){
 658:         if (ISPROCESS(p2))
 659:             return((*cmp)(p1, p2));
 660:         return(-1);
 661:     }
 662:     if (ISPROCESS(p2))
 663:         return(1);
 664:     return(0);
 665: }
 666: 
 667: ncmp(p1, p2)
 668:     register cell *p1, *p2;
 669: {
 670: 
 671:     if(p1->p.count == p2->p.count)
 672:         return(tcmp(p1, p2));
 673:     if(rflg)
 674:         return(p1->p.count - p2->p.count);
 675:     return(p2->p.count - p1->p.count);
 676: }
 677: 
 678: Bcmp(p1, p2)
 679:     cell *p1, *p2;
 680: {
 681:     double f1, f2;
 682:     double sum();
 683: 
 684:     f1 = sum(p1)/p1->p.count;
 685:     f2 = sum(p2)/p2->p.count;
 686:     if(f1 < f2) {
 687:         if(rflg)
 688:             return(-1);
 689:         return(1);
 690:     }
 691:     if(f1 > f2) {
 692:         if(rflg)
 693:             return(1);
 694:         return(-1);
 695:     }
 696:     return(0);
 697: }
 698: 
 699: Kcmp(p1, p2)
 700:     register cell *p1, *p2;
 701: {
 702: 
 703:     if (p1->p.imem < p2->p.imem) {
 704:         if(rflg)
 705:             return(-1);
 706:         return(1);
 707:     }
 708:     if (p1->p.imem > p2->p.imem) {
 709:         if(rflg)
 710:             return(1);
 711:         return(-1);
 712:     }
 713:     return(0);
 714: }
 715: 
 716: kcmp(p1, p2)
 717:     register cell *p1, *p2;
 718: {
 719:     double a1, a2;
 720: 
 721:     a1 = p1->p.imem / ((p1->p.cput+p1->p.syst)?(p1->p.cput+p1->p.syst):1);
 722:     a2 = p2->p.imem / ((p2->p.cput+p2->p.syst)?(p2->p.cput+p2->p.syst):1);
 723:     if (a1 < a2) {
 724:         if(rflg)
 725:             return(-1);
 726:         return(1);
 727:     }
 728:     if (a1 > a2) {
 729:         if(rflg)
 730:             return(1);
 731:         return(-1);
 732:     }
 733:     return(0);
 734: }
 735: 
 736: dcmp(p1, p2)
 737:     register cell *p1, *p2;
 738: {
 739:     double a1, a2;
 740: 
 741:     a1 = p1->p.io / (p1->p.count?p1->p.count:1);
 742:     a2 = p2->p.io / (p2->p.count?p2->p.count:1);
 743:     if (a1 < a2) {
 744:         if(rflg)
 745:             return(-1);
 746:         return(1);
 747:     }
 748:     if (a1 > a2) {
 749:         if(rflg)
 750:             return(1);
 751:         return(-1);
 752:     }
 753:     return(0);
 754: }
 755: 
 756: Dcmp(p1, p2)
 757:     register cell *p1, *p2;
 758: {
 759: 
 760:     if (p1->p.io < p2->p.io) {
 761:         if(rflg)
 762:             return(-1);
 763:         return(1);
 764:     }
 765:     if (p1->p.io > p2->p.io) {
 766:         if(rflg)
 767:             return(1);
 768:         return(-1);
 769:     }
 770:     return(0);
 771: }
 772: 
 773: tcmp(p1, p2)
 774:     cell *p1, *p2;
 775: {
 776:     extern double sum();
 777:     double f1, f2;
 778: 
 779:     f1 = sum(p1);
 780:     f2 = sum(p2);
 781:     if(f1 < f2) {
 782:         if(rflg)
 783:             return(-1);
 784:         return(1);
 785:     }
 786:     if(f1 > f2) {
 787:         if(rflg)
 788:             return(1);
 789:         return(-1);
 790:     }
 791:     return(0);
 792: }
 793: 
 794: double sum(p)
 795:     cell *p;
 796: {
 797: 
 798:     if(p->p.name[0] == 0)
 799:         return(0.0);
 800:     return( p->p.cput + p->p.syst);
 801: }
 802: 
 803: init()
 804: {
 805:     struct user userbuf;
 806:     struct process  tbuf;
 807:     register cell *tp;
 808:     register struct user *up;
 809:     uid_t uid;
 810:     FILE *f;
 811: 
 812:     if ((f = fopen(savacct, "r")) == NULL)
 813:         goto gshm;
 814:     while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) {
 815:         tp = enter(tbuf.name);
 816:         ncom += tbuf.count;
 817:         tp->p.count = tbuf.count;
 818:         treal += tbuf.realt;
 819:         tp->p.realt = tbuf.realt;
 820:         tcpu += tbuf.cput;
 821:         tp->p.cput = tbuf.cput;
 822:         tsys += tbuf.syst;
 823:         tp->p.syst = tbuf.syst;
 824:         tio += tbuf.io;
 825:         tp->p.io = tbuf.io;
 826:         timem += tbuf.imem;
 827:         tp->p.imem = tbuf.imem;
 828:     }
 829:     fclose(f);
 830:  gshm:
 831:     if ((f = fopen(usracct, "r")) == NULL)
 832:         return;
 833:     for(uid = 0;
 834:         fread((char *)&(userbuf.oldu), sizeof(struct Olduser), 1, f) == 1;
 835:         uid++){
 836:         if (userbuf.us_cnt){
 837:             up = finduser(uid);
 838:             if (up == 0)
 839:                 continue;   /* preposterous user id */
 840:             up->oldu = userbuf.oldu;
 841:         }
 842:     }
 843:     fclose(f);
 844: }
 845: 
 846: strip()
 847: {
 848:     int c;
 849:     register struct allocbox *allocwalk;
 850:     register cell *tp, *ub, *junkp;
 851: 
 852:     if (fflg)
 853:         printf("Categorizing commands used %d times or fewer as **junk**\n",
 854:             thres);
 855:     junkp = enter("**junk**");
 856:     PROCESSITERATE(allocwalk, tp, ub){
 857:         if (tp->p.name[0] && tp->p.count <= thres) {
 858:             if (!fflg)
 859:                 printf("%.14s--", tp->p.name);
 860:             if (fflg || ((c=getchar())=='y')) {
 861:                 tp->p.name[0] = '\0';
 862:                 junkp->p.count += tp->p.count;
 863:                 junkp->p.realt += tp->p.realt;
 864:                 junkp->p.cput += tp->p.cput;
 865:                 junkp->p.syst += tp->p.syst;
 866:                 junkp->p.imem += tp->p.imem;
 867:                 junkp->p.io += tp->p.io;
 868:             }
 869:             if (!fflg)
 870:                 while (c && c!='\n')
 871:                     c = getchar();
 872:         }
 873:     }
 874: }
 875: 
 876: time_t
 877: expand(t)
 878:     register unsigned int t;
 879: {
 880:     time_t nt;
 881: 
 882:     nt = t&017777;
 883:     t >>= 13;
 884:     while (t!=0) {
 885:         t--;
 886:         nt <<= 3;
 887:     }
 888:     return(nt);
 889: }
 890: 
 891: static  char UserKey[NAMELG + 2];
 892: 
 893: char *
 894: makekey(uid)
 895:     uid_t uid;
 896: {
 897:     sprintf(UserKey+1, "%04x", uid);
 898:     UserKey[0] = USERKEY;
 899:     return(UserKey);
 900: }
 901: 
 902: struct user *
 903: wasuser(uid)
 904:     uid_t uid;
 905: {
 906:     struct user *tp;
 907: 
 908:     htabinstall = 0;
 909:     tp = finduser(uid);
 910:     htabinstall = 1;
 911:     return(tp);
 912: }
 913: 
 914: /*
 915:  *	Only call this if you really want to insert it in the table!
 916:  */
 917: struct user *
 918: finduser(uid)
 919:     uid_t uid;
 920: {
 921: 
 922:     if (uid > maxuser){
 923:         fprintf(stderr, "Preposterous user id, %u: ignored\n", uid);
 924:         return(0);
 925:     }
 926:     return((struct user*)enter(makekey(uid)));
 927: }
 928: 
 929: /*
 930:  *	Set the names of all users in the password file.
 931:  *	We will later not print those that didn't do anything.
 932:  */
 933: getnames()
 934: {
 935:     register struct user *tp;
 936:     register struct passwd *pw;
 937: 
 938:     setpwent();
 939:     while (pw = getpwent()){
 940:         /* use first name in passwd file for duplicate uid's */
 941:         if ((tp = wasuser(pw->pw_uid)) != 0 && !isalpha(tp->us_name[0]))
 942:             strncpy(tp->us_name, pw->pw_name, NAMELG);
 943:     }
 944:     endpwent();
 945: }
 946: 
 947: uid_t
 948: getmaxuid()
 949: {
 950:     register struct user *tp;
 951:     register struct passwd *pw;
 952:     uid_t maxuid = 0;
 953: 
 954:     setpwent();
 955:     while(pw = getpwent()){
 956:         if (pw->pw_uid > maxuid)
 957:             maxuid = pw->pw_uid;
 958:     }
 959:     endpwent();
 960:     return(maxuid);
 961: }
 962: 
 963: tabinit()
 964: {
 965:     allochead = 0;
 966:     alloctail = 0;
 967:     nexttab = 0;
 968:     tabsleft = 0;
 969:     htab = 0;
 970:     ntabs = 0;
 971:     htaballoc();        /* get the first part of the hash table */
 972: }
 973: 
 974: #define ALLOCQTY    sizeof (struct allocbox)
 975: cell *
 976: taballoc()
 977: {
 978: 
 979:     if (tabsleft == 0){
 980:         newbox = (struct allocbox *)calloc(1, ALLOCQTY);
 981:         tabsleft = TABDALLOP;
 982:         nexttab = &newbox->tabslots[0];
 983:         if (alloctail == 0){
 984:             allochead = alloctail = newbox;
 985:         } else {
 986:             alloctail->nextalloc = newbox;
 987:             alloctail = newbox;
 988:         }
 989:     }
 990:     --tabsleft;
 991:     ++ntabs;
 992: #ifdef DEBUG
 993:     if (ntabs % 100 == 0)
 994:         printf("##Accounting table slot # %d\n", ntabs);
 995: #endif DEBUG
 996:     return(nexttab++);
 997: }
 998: 
 999: htaballoc()
1000: {
1001:     register struct hashdallop *new;
1002: #ifdef DEBUG
1003:     static int ntables = 0;
1004: 
1005:     printf("%%%New hash table chunk allocated, number %d\n", ++ntables);
1006: #endif DEBUG
1007:     new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop));
1008:     if (htab == 0)
1009:         htab = new;
1010:     else {      /* add AFTER the 1st slot */
1011:         new->h_next = htab->h_next;
1012:         htab->h_next = new;
1013:     }
1014: }
1015: 
1016: #define     HASHCLOGGED (NHASH / 2)
1017: /*
1018:  *	Lookup a symbol passed in as the argument.
1019:  *
1020:  *	We take pains to avoid function calls; this function
1021:  *	is called quite frequently, and the calling overhead
1022:  *	contributes significantly to the overall execution speed of sa.
1023:  */
1024: cell *
1025: enter(name)
1026:     char *name;
1027: {
1028:     static int initialprobe;
1029:     register cell **hp;
1030:     register char *from, *to;
1031:     register int len, nprobes;
1032:     static struct hashdallop *hdallop, *emptyhd;
1033:     static cell **emptyslot, **hp_ub;
1034: 
1035:     emptyslot = 0;
1036:     for (nprobes = 0, from = name, len = 0;
1037:          *from && len < NC;
1038:          nprobes <<= 2, nprobes += *from++, len++)
1039:         continue;
1040:     nprobes += from[-1] << 5;
1041:     nprobes %= NHASH;
1042:     if (nprobes < 0)
1043:         nprobes += NHASH;
1044: 
1045:     initialprobe = nprobes;
1046:     for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
1047:         for (hp = &(hdallop->h_tab[initialprobe]),
1048:                 nprobes = 1,
1049:                 hp_ub = &(hdallop->h_tab[NHASH]);
1050:              (*hp) && (nprobes < NHASH);
1051:                 hp += nprobes,
1052:                 hp -= (hp >= hp_ub) ? NHASH:0,
1053:                 nprobes += 2)
1054:         {
1055:             from = name;
1056:             to = (*hp)->p.name;
1057: 
1058:             for (len = 0; (len<NC) && *from; len++)
1059:                 if (*from++ != *to++)
1060:                     goto nextprobe;
1061:             if (len >= NC)      /*both are maximal length*/
1062:                 return(*hp);
1063:             if (*to == 0)       /*assert *from == 0*/
1064:                 return(*hp);
1065:     nextprobe: ;
1066:         }
1067:         if (*hp == 0 && emptyslot == 0 &&
1068:             hdallop->h_nused < HASHCLOGGED) {
1069:             emptyslot = hp;
1070:             emptyhd = hdallop;
1071:         }
1072:     }
1073:     if (emptyslot == 0) {
1074:         htaballoc();
1075:         hdallop = htab->h_next;     /* aren't we smart! */
1076:         hp = &hdallop->h_tab[initialprobe];
1077:     } else {
1078:         hdallop = emptyhd;
1079:         hp = emptyslot;
1080:     }
1081:     if (htabinstall){
1082:         *hp = taballoc();
1083:         hdallop->h_nused++;
1084:         for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++)
1085:             if ((*to++ = *from++) == '\0')
1086:                 break;
1087:         return(*hp);
1088:     }
1089:     return(0);
1090: }

Defined functions

Bcmp defined in line 678; used 2 times
Dcmp defined in line 756; used 2 times
Kcmp defined in line 699; used 2 times
cellcmp defined in line 654; used 3 times
col defined in line 564; used 5 times
column defined in line 534; used 2 times
dcmp defined in line 736; used 2 times
doacct defined in line 579; used 1 times
enter defined in line 1024; used 6 times
expand defined in line 876; used 7 times
finduser defined in line 917; used 4 times
getmaxuid defined in line 947; used 2 times
getnames defined in line 933; used 1 times
htaballoc defined in line 999; used 2 times
init defined in line 803; used 1 times
kcmp defined in line 716; used 2 times
main defined in line 272; never used
makekey defined in line 893; used 1 times
ncmp defined in line 667; used 2 times
printmoney defined in line 511; used 1 times
strip defined in line 846; used 1 times
sum defined in line 794; used 7 times
taballoc defined in line 975; used 1 times
tabinit defined in line 963; used 1 times
tcmp defined in line 773; used 3 times
usage defined in line 504; used 2 times
wasuser defined in line 902; used 4 times

Defined variables

Dflg defined in line 34; used 2 times
Kflg defined in line 38; used 2 times
UserKey defined in line 891; used 3 times
acctbuf defined in line 31; used 1 times
  • in line 29
aflg defined in line 41; used 3 times
allochead defined in line 153; used 4 times
alloctail defined in line 154; used 7 times
bflg defined in line 50; used 1 times
cflg defined in line 33; used 3 times
dflg defined in line 35; used 2 times
fflg defined in line 46; used 5 times
htab defined in line 220; used 7 times
htabinstall defined in line 262; used 3 times
iflg defined in line 36; used 2 times
jflg defined in line 37; used 2 times
junkp defined in line 260; used 16 times
kflg defined in line 39; used 2 times
lflg defined in line 32; used 2 times
maxuser defined in line 80; used 4 times
mflg defined in line 51; used 2 times
ncom defined in line 229; used 5 times
newbox defined in line 155; used 5 times
nexttab defined in line 156; used 5 times
nflg defined in line 40; used 1 times
ntabs defined in line 158; used 4 times
oflg defined in line 43; used 2 times
pgdiv defined in line 266; used 4 times
rflg defined in line 42; used 14 times
savacct defined in line 257; used 3 times
sccsid defined in line 2; never used
sflg defined in line 49; used 2 times
tabsleft defined in line 157; used 4 times
tcpu defined in line 224; used 6 times
tflg defined in line 44; used 2 times
thres defined in line 261; used 5 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 47; used 3 times
usracct defined in line 256; used 3 times
utmp defined in line 53; used 1 times
  • in line 54
vflg defined in line 45; used 2 times

Defined struct's

Olduser defined in line 56; 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 82; used 8 times
user defined in line 63; used 30 times

Defined union's

Tab defined in line 92; used 2 times
  • in line 97(2)

Defined typedef's

cell defined in line 97; used 27 times

Defined macros

ACCT defined in line 253; used 1 times
ALLOCQTY defined in line 974; used 1 times
DECLITERATE defined in line 169; used 2 times
HASHCLOGGED defined in line 1016; used 1 times
ISPROCESS defined in line 144; used 5 times
ISUSER defined in line 145; used 1 times
NAMELG defined in line 54; used 3 times
NC defined in line 29; used 12 times
NHASH defined in line 214; used 7 times
PROCESSITERATE defined in line 194; used 3 times
SAVACCT defined in line 252; 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 79; never used
USRACCT defined in line 251; used 1 times
pgtok defined in line 267; used 1 times
us_cnt defined in line 68; used 4 times
us_ctime defined in line 69; used 2 times
us_imem defined in line 71; used 2 times
us_io defined in line 70; used 2 times
Last modified: 1997-02-16
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 3652
Valid CSS Valid XHTML 1.0 Strict