149240Sbostic /*-
2*61875Sbostic * Copyright (c) 1991, 1993
3*61875Sbostic * The Regents of the University of California. All rights reserved.
449240Sbostic *
549240Sbostic * %sccs.include.proprietary.c%
649240Sbostic */
749240Sbostic
811840Ssam #ifndef lint
9*61875Sbostic static char copyright[] =
10*61875Sbostic "@(#) Copyright (c) 1991, 1993\n\
11*61875Sbostic The Regents of the University of California. All rights reserved.\n";
1249240Sbostic #endif /* not lint */
132813Swnj
1449240Sbostic #ifndef lint
15*61875Sbostic static char sccsid[] = "@(#)sa.c 8.1 (Berkeley) 06/06/93";
1649240Sbostic #endif /* not lint */
1749240Sbostic
182813Swnj /*
192813Swnj * Extensive modifications to internal data structures
202813Swnj * to allow arbitrary number of different commands and users added.
212813Swnj *
222813Swnj * Also allowed the digit option on the -v flag (interactive
232813Swnj * threshold compress) to be a digit string, so one can
242813Swnj * set the threshold > 9.
252813Swnj *
262813Swnj * Also added the -f flag, to force no interactive threshold
272813Swnj * compression with the -v flag.
282813Swnj *
292813Swnj * Robert Henry
302813Swnj * UC Berkeley
312813Swnj * 31jan81
322813Swnj */
331087Sbill #include <stdio.h>
3416717Ssam #include <ctype.h>
351087Sbill #include <sys/types.h>
361087Sbill #include <sys/acct.h>
371087Sbill #include <signal.h>
382813Swnj #include <utmp.h>
392813Swnj #include <pwd.h>
4037297Sbostic #include "pathnames.h"
411087Sbill
421087Sbill /* interpret command time accounting */
431087Sbill
441087Sbill #define NC sizeof(acctbuf.ac_comm)
452813Swnj
461087Sbill struct acct acctbuf;
471087Sbill int lflg;
481087Sbill int cflg;
491087Sbill int Dflg;
501087Sbill int dflg;
511087Sbill int iflg;
521087Sbill int jflg;
531087Sbill int Kflg;
541087Sbill int kflg;
551087Sbill int nflg;
561087Sbill int aflg;
571087Sbill int rflg;
581087Sbill int oflg;
591087Sbill int tflg;
601087Sbill int vflg;
612813Swnj int fflg;
621087Sbill int uflg;
632813Swnj int thres;
641087Sbill int sflg;
651087Sbill int bflg;
661087Sbill int mflg;
671087Sbill
682813Swnj struct utmp utmp;
692813Swnj #define NAMELG (sizeof(utmp.ut_name)+1)
702813Swnj
712813Swnj struct Olduser{
722813Swnj int Us_cnt;
732813Swnj double Us_ctime;
742813Swnj double Us_io;
752813Swnj double Us_imem;
762813Swnj };
772813Swnj
781087Sbill struct user {
792813Swnj char name[NC]; /* this is <\001><user id><\000> */
802813Swnj struct Olduser oldu;
812813Swnj char us_name[NAMELG];
822813Swnj };
832813Swnj #define us_cnt oldu.Us_cnt
842813Swnj #define us_ctime oldu.Us_ctime
852813Swnj #define us_io oldu.Us_io
862813Swnj #define us_imem oldu.Us_imem
871087Sbill
882813Swnj /*
892813Swnj * We protect ourselves from preposterous user id's by looking
902813Swnj * through the passwd file for the highest uid allocated, and
912813Swnj * then adding 10 to that.
922813Swnj * This prevents the user structure from growing too large.
932813Swnj */
942813Swnj #define USERSLOP 10
952813Swnj int maxuser; /* highest uid from /etc/passwd, + 10 for slop*/
962813Swnj
972813Swnj struct process {
981087Sbill char name[NC];
991087Sbill int count;
1001087Sbill double realt;
1011087Sbill double cput;
1021087Sbill double syst;
1031087Sbill double imem;
1041087Sbill double io;
1052813Swnj };
1061087Sbill
1072813Swnj union Tab{
1082813Swnj struct process p;
1092813Swnj struct user u;
1102813Swnj };
1112813Swnj
1122813Swnj typedef union Tab cell;
1132813Swnj
1142813Swnj int (*cmp)(); /* compares 2 cells; set to appropriate func */
1152813Swnj cell *enter();
1162813Swnj struct user *finduser();
1172813Swnj struct user *wasuser();
1182813Swnj
1192813Swnj /*
1202813Swnj * Table elements are keyed by the name of the file exec'ed.
1212813Swnj * Because on large systems, many files can be exec'ed,
1222813Swnj * a static table size may grow to be too large.
1232813Swnj *
1242813Swnj * Table elements are allocated in chunks dynamically, linked
1252813Swnj * together so that they may be retrieved sequentially.
1262813Swnj *
1272813Swnj * An index into the table structure is provided by hashing through
1282813Swnj * a seperate hash table.
1292813Swnj * The hash table is segmented, and dynamically extendable.
1302813Swnj * Realize that the hash table and accounting information is kept
1312813Swnj * in different segments!
1322813Swnj *
1332813Swnj * We have a linked list of hash table segments; within each
1342813Swnj * segment we use a quadratic rehash that touches no more than 1/2
1352813Swnj * of the buckets in the hash table when probing.
1362813Swnj * If the probe does not find the desired symbol, it moves to the
1372813Swnj * next segment, or allocates a new segment.
1382813Swnj *
1392813Swnj * Hash table segments are kept on the linked list with the first
1402813Swnj * segment always first (that will probably contain the
1412813Swnj * most frequently executed commands) and
1422813Swnj * the last added segment immediately after the first segment,
1432813Swnj * to hopefully gain something by locality of reference.
1442813Swnj *
1452813Swnj * We store the per user information in the same structure as
1462813Swnj * the per exec'ed file information. This allows us to use the
1472813Swnj * same managers for both, as the number of user id's may be very
1482813Swnj * large.
1492813Swnj * User information is keyed by the first character in the name
1502813Swnj * being a '\001', followed by four bytes of (long extended)
1512813Swnj * user id number, followed by a null byte.
1522813Swnj * The actual user names are kept in a seperate field of the
1532813Swnj * user structure, and is filled in upon demand later.
1542813Swnj * Iteration through all users by low user id to high user id
1552813Swnj * is done by just probing the table, which is gross.
1562813Swnj */
1572813Swnj #define USERKEY '\001'
1582813Swnj #define ISPROCESS(tp) (tp->p.name[0] && (tp->p.name[0] != USERKEY))
1592813Swnj #define ISUSER(tp) (tp->p.name[0] && (tp->p.name[0] == USERKEY))
1602813Swnj
1612813Swnj #define TABDALLOP 500
1622813Swnj struct allocbox{
1632813Swnj struct allocbox *nextalloc;
1642813Swnj cell tabslots[TABDALLOP];
1652813Swnj };
1662813Swnj
1672813Swnj struct allocbox *allochead; /*head of chunk list*/
1682813Swnj struct allocbox *alloctail; /*tail*/
1692813Swnj struct allocbox *newbox; /*for creating a new chunk*/
1702813Swnj cell *nexttab; /*next table element that is free*/
1712813Swnj int tabsleft; /*slots left in current chunk*/
1722813Swnj int ntabs;
1732813Swnj /*
1742813Swnj * Iterate through all symbols in the symbol table in declaration
1752813Swnj * order.
1762813Swnj * struct allocbox *allocwalk;
1772813Swnj * cell *sp, *ub;
1782813Swnj *
1792813Swnj * sp points to the desired item, allocwalk and ub are there
1802813Swnj * to make the iteration go.
1812813Swnj */
1822813Swnj
1832813Swnj #define DECLITERATE(allocwalk, walkpointer, ubpointer) \
1842813Swnj for(allocwalk = allochead; \
1852813Swnj allocwalk != 0; \
1862813Swnj allocwalk = allocwalk->nextalloc) \
1872813Swnj for (walkpointer = &allocwalk->tabslots[0],\
1882813Swnj ubpointer = &allocwalk->tabslots[TABDALLOP], \
1892813Swnj ubpointer = ubpointer > ( (cell *)alloctail) \
1902813Swnj ? nexttab : ubpointer ;\
1912813Swnj walkpointer < ubpointer; \
1922813Swnj walkpointer++ )
1932813Swnj
1942813Swnj #define TABCHUNKS(allocwalk, tabptr, size) \
1952813Swnj for (allocwalk = allochead; \
1962813Swnj allocwalk != 0; \
1972813Swnj allocwalk = allocwalk->nextalloc) \
1982813Swnj if ( \
1992813Swnj (tabptr = &allocwalk->tabslots[0]), \
2002813Swnj (size = \
2012813Swnj ( (&allocwalk->tabslots[TABDALLOP]) \
2022813Swnj > ((cell *)alloctail) \
2032813Swnj ) \
2042813Swnj ? (nexttab - tabptr) : TABDALLOP \
2052813Swnj ), \
2062813Swnj 1 \
2072813Swnj )
2082813Swnj #define PROCESSITERATE(allocwalk, walkpointer, ubpointer) \
2092813Swnj DECLITERATE(allocwalk, walkpointer, ubpointer) \
2102813Swnj if (ISPROCESS(walkpointer))
2112813Swnj
2122813Swnj #define USERITERATE(allocwalk, walkpointer, ubpointer) \
2132813Swnj DECLITERATE(allocwalk, walkpointer, ubpointer) \
2142813Swnj if (ISUSER(walkpointer))
2152813Swnj /*
2162813Swnj * When we have to sort the segmented accounting table, we
2172813Swnj * create a vector of sorted queues that is merged
2182813Swnj * to sort the entire accounting table.
2192813Swnj */
2202813Swnj struct chunkdesc {
2212813Swnj cell *chunk_tp;
2222813Swnj int chunk_n;
2232813Swnj };
2242813Swnj
2252813Swnj /*
2262813Swnj * Hash table segments and manager
2272813Swnj */
2282813Swnj #define NHASH 1103
2292813Swnj struct hashdallop {
2302813Swnj int h_nused;
2312813Swnj struct hashdallop *h_next;
2322813Swnj cell *h_tab[NHASH];
2332813Swnj };
2342813Swnj struct hashdallop *htab; /* head of the list */
2352813Swnj int htabinstall; /* install the symbol */
2362813Swnj
2371087Sbill double treal;
2381087Sbill double tcpu;
2391087Sbill double tsys;
2401087Sbill double tio;
2411087Sbill double timem;
2422813Swnj cell *junkp;
2431087Sbill char *sname;
2441087Sbill double ncom;
2451087Sbill time_t expand();
2461087Sbill char *getname();
2471087Sbill
2482813Swnj /*
2492813Swnj * usracct saves records of type Olduser.
2502813Swnj * There is one record for every possible uid less than
2512813Swnj * the largest uid seen in the previous usracct or in savacct.
2522813Swnj * uid's that had no activity correspond to zero filled slots;
2532813Swnj * thus one can index the file and get the user record out.
2542813Swnj * It would be better to save only user information for users
2552813Swnj * that the system knows about to save space, but that is not
2562813Swnj * upward compatabile with the old system.
2572813Swnj *
2582813Swnj * In the old version of sa, uid's greater than 999 were not handled
2592813Swnj * properly; this system will do that.
2602813Swnj */
2612813Swnj
2622813Swnj #ifdef DEBUG
2632813Swnj #define USRACCT "./usracct"
2642813Swnj #define SAVACCT "./savacct"
2652813Swnj #define ACCT "./acct"
2662813Swnj #else
26737297Sbostic #define USRACCT _PATH_USRACCT
26837297Sbostic #define SAVACCT _PATH_SAVACCT
26937297Sbostic #define ACCT _PATH_ACCT
2702813Swnj #endif DEBUG
2712813Swnj
2722813Swnj
27317504Skarels char *usracct = USRACCT;
27417504Skarels char *savacct = SAVACCT;
27517504Skarels
2762813Swnj int cellcmp();
2772813Swnj cell *junkp = 0;
2782813Swnj /*
2792813Swnj * The threshold is built up from digits in the argv ;
2802813Swnj * eg, -v1s0u1
2812813Swnj * will build a value of thres of 101.
2822813Swnj *
2832813Swnj * If the threshold is zero after processing argv, it is set to 1
2842813Swnj */
2852813Swnj int thres = 0;
2862813Swnj int htabinstall = 1;
2872813Swnj int maxuser = -1;
2882813Swnj int (*cmp)();
2892813Swnj
29016717Ssam /* we assume pagesize is at least 1k */
29116717Ssam int pgdiv;
29245019Smckusick #define pgtok(x) ((x) * pgdiv)
29316717Ssam
29437851Sbostic extern tcmp(), ncmp(), bflgcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
29511840Ssam extern double sum();
29611840Ssam
main(argc,argv)2971087Sbill main(argc, argv)
29811840Ssam char **argv;
2991087Sbill {
3001087Sbill FILE *ff;
30111840Ssam double ft;
30211840Ssam register struct allocbox *allocwalk;
30311840Ssam register cell *tp, *ub;
30411840Ssam int i, j, size, nchunks, smallest;
30511840Ssam struct chunkdesc *chunkvector;
3061087Sbill
30716717Ssam pgdiv = getpagesize() / 1024;
30816717Ssam if (pgdiv == 0)
30916717Ssam pgdiv = 1;
3102813Swnj maxuser = USERSLOP + getmaxuid();
3112813Swnj
3122813Swnj tabinit();
3131087Sbill cmp = tcmp;
3141087Sbill if (argc>1)
3151087Sbill if (argv[1][0]=='-') {
3161087Sbill argv++;
3171087Sbill argc--;
3181087Sbill for(i=1; argv[0][i]; i++)
3191087Sbill switch(argv[0][i]) {
3201087Sbill
3211087Sbill case 'o':
3221087Sbill oflg++;
3231087Sbill break;
3241087Sbill
3251087Sbill case 'i':
3261087Sbill iflg++;
3271087Sbill break;
3281087Sbill
3291087Sbill case 'b':
3301087Sbill bflg++;
33137851Sbostic cmp = bflgcmp;
3321087Sbill break;
3331087Sbill
3341087Sbill case 'l':
3351087Sbill lflg++;
3361087Sbill break;
3371087Sbill
3381087Sbill case 'c':
3391087Sbill cflg++;
3401087Sbill break;
3411087Sbill
3421087Sbill case 'd':
3431087Sbill dflg++;
3441087Sbill cmp = dcmp;
3451087Sbill break;
3461087Sbill
3471087Sbill case 'D':
3481087Sbill Dflg++;
3491087Sbill cmp = Dcmp;
3501087Sbill break;
3511087Sbill
3521087Sbill case 'j':
3531087Sbill jflg++;
3541087Sbill break;
3551087Sbill
3561087Sbill case 'k':
3571087Sbill kflg++;
3581087Sbill cmp = kcmp;
3591087Sbill break;
3601087Sbill
3611087Sbill case 'K':
3621087Sbill Kflg++;
3631087Sbill cmp = Kcmp;
3641087Sbill break;
3651087Sbill
3661087Sbill case 'n':
3671087Sbill nflg++;
3681087Sbill cmp = ncmp;
3691087Sbill break;
3701087Sbill
3711087Sbill case 'a':
3721087Sbill aflg++;
3731087Sbill break;
3741087Sbill
3751087Sbill case 'r':
3761087Sbill rflg++;
3771087Sbill break;
3781087Sbill
3791087Sbill case 't':
3801087Sbill tflg++;
3811087Sbill break;
3821087Sbill
3831087Sbill case 's':
3841087Sbill sflg++;
3851087Sbill aflg++;
3861087Sbill break;
3871087Sbill
3881087Sbill case '0':
3891087Sbill case '1':
3901087Sbill case '2':
3911087Sbill case '3':
3921087Sbill case '4':
3931087Sbill case '5':
3941087Sbill case '6':
3951087Sbill case '7':
3961087Sbill case '8':
3971087Sbill case '9':
3982813Swnj thres = thres * 10 + (argv[0][i]-'0');
3991087Sbill break;
4001087Sbill
4011087Sbill case 'v':
4021087Sbill vflg++;
4031087Sbill break;
4041087Sbill
4052813Swnj case 'f':
4062813Swnj fflg++; /* force v option; no tty interaction */
4072813Swnj break;
4082813Swnj
4091087Sbill case 'u':
4101087Sbill uflg++;
4111087Sbill break;
4121087Sbill
4131087Sbill case 'm':
4141087Sbill mflg++;
4151087Sbill break;
41617504Skarels
41717504Skarels case 'U':
41817504Skarels case 'S':
41917504Skarels if (i != 1 || argv[0][2]) { /* gross! */
42017504Skarels fprintf(stderr, "-U and -S options must be separate\n");
42117504Skarels exit(1);
42217504Skarels }
42317504Skarels argc++, argv--; /* backup - yuk */
42417504Skarels goto doUS;
42517504Skarels
42617504Skarels default:
42717504Skarels fprintf(stderr, "Invalid option %c\n", argv[0][1]);
42817504Skarels exit(1);
4291087Sbill }
4301087Sbill }
43117504Skarels
43217504Skarels #define optfile(f) {if (argc < 2) \
43317504Skarels { fprintf(stderr, "Missing filename\n"); exit(1); } \
43417504Skarels argc--, argv++; f = argv[0]; }
43517504Skarels
43617504Skarels doUS:
43717504Skarels for (argc--, argv++; argc && argv[0][0] == '-'; argc--, argv++) {
43817504Skarels switch(argv[0][1]) {
43917504Skarels case 'U':
44017504Skarels optfile(usracct);
44117504Skarels break;
44217504Skarels
44317504Skarels case 'S':
44417504Skarels optfile(savacct);
44517504Skarels break;
44617504Skarels
44717504Skarels default:
44817504Skarels fprintf(stderr, "Invalid option %c\n", argv[0][1]);
44917504Skarels exit(1);
45017504Skarels }
45117504Skarels }
45217504Skarels
4532813Swnj if (thres == 0)
4542813Swnj thres = 1;
4551087Sbill if (iflg==0)
4561087Sbill init();
45717504Skarels if (argc<1)
4582813Swnj doacct(ACCT);
45917504Skarels else while (argc--)
46017504Skarels doacct(*argv++);
4611087Sbill if (uflg) {
4621087Sbill return;
4631087Sbill }
4641087Sbill
4651087Sbill /*
4661087Sbill * cleanup pass
4671087Sbill * put junk together
4681087Sbill */
4691087Sbill
4701087Sbill if (vflg)
4711087Sbill strip();
4721087Sbill if(!aflg)
4732813Swnj PROCESSITERATE(allocwalk, tp, ub){
4741087Sbill for(j=0; j<NC; j++)
4752813Swnj if(tp->p.name[j] == '?')
4761087Sbill goto yes;
4772813Swnj if(tp->p.count != 1)
4781087Sbill continue;
4791087Sbill yes:
4802813Swnj if(junkp == 0)
4811087Sbill junkp = enter("***other");
4822813Swnj junkp->p.count += tp->p.count;
4832813Swnj junkp->p.realt += tp->p.realt;
4842813Swnj junkp->p.cput += tp->p.cput;
4852813Swnj junkp->p.syst += tp->p.syst;
4862813Swnj junkp->p.imem += tp->p.imem;
4872813Swnj junkp->p.io += tp->p.io;
4882813Swnj tp->p.name[0] = 0;
4891087Sbill }
4901087Sbill if (sflg) {
4911087Sbill signal(SIGINT, SIG_IGN);
49217504Skarels if ((ff = fopen(usracct, "w")) != NULL) {
4932813Swnj static struct user ZeroUser = {0};
4942813Swnj struct user *up;
4952813Swnj int uid;
4962813Swnj /*
4972813Swnj * Write out just enough user slots,
4982813Swnj * filling with zero slots for users that
4992813Swnj * weren't found.
5002813Swnj * The file can be indexed directly by uid
5012813Swnj * to get the correct record.
5022813Swnj */
5032813Swnj for (uid = 0; uid < maxuser; uid++){
5042813Swnj if ( (up = wasuser(uid)) != 0)
5052813Swnj fwrite((char *)&(up->oldu),
5062813Swnj sizeof(struct Olduser),1,ff);
5072813Swnj else
5082813Swnj fwrite((char *)&(ZeroUser.oldu),
5092813Swnj sizeof(struct Olduser),1,ff);
5102813Swnj }
5111087Sbill }
51217504Skarels if ((ff = fopen(savacct, "w")) == NULL) {
5131087Sbill printf("Can't save\n");
5141087Sbill exit(0);
5151087Sbill }
5162813Swnj PROCESSITERATE(allocwalk, tp, ub)
5172813Swnj fwrite((char *)&(tp->p), sizeof(struct process), 1, ff);
5181087Sbill fclose(ff);
5192813Swnj creat(sname, 0644);
5201087Sbill signal(SIGINT, SIG_DFL);
5211087Sbill }
5221087Sbill /*
5231087Sbill * sort and print
5241087Sbill */
5251087Sbill if (mflg) {
5261087Sbill printmoney();
5271087Sbill exit(0);
5281087Sbill }
5291087Sbill column(ncom, treal, tcpu, tsys, timem, tio);
5301087Sbill printf("\n");
5312813Swnj
5322813Swnj /*
5332813Swnj * the fragmented table is sorted by sorting each fragment
5342813Swnj * and then merging.
5352813Swnj */
5362813Swnj nchunks = 0;
5372813Swnj TABCHUNKS(allocwalk, tp, size){
5382813Swnj qsort(tp, size, sizeof(cell), cellcmp);
5392813Swnj nchunks ++;
5401087Sbill }
5412813Swnj chunkvector = (struct chunkdesc *)calloc(nchunks,
5422813Swnj sizeof(struct chunkdesc));
5432813Swnj nchunks = 0;
5442813Swnj TABCHUNKS(allocwalk, tp, size){
5452813Swnj chunkvector[nchunks].chunk_tp = tp;
5462813Swnj chunkvector[nchunks].chunk_n = size;
5472813Swnj nchunks++;
5482813Swnj }
5492813Swnj for(; nchunks; ){
5502813Swnj /*
5512813Swnj * Find the smallest element at the head of the queues.
5522813Swnj */
5532813Swnj smallest = 0;
5542813Swnj for (i = 1; i < nchunks; i++){
5552813Swnj if (cellcmp(chunkvector[i].chunk_tp,
5562813Swnj chunkvector[smallest].chunk_tp) < 0)
5572813Swnj smallest = i;
5582813Swnj }
5592813Swnj tp = chunkvector[smallest].chunk_tp++;
5602813Swnj /*
5612813Swnj * If this queue is drained, drop the chunk count,
5622813Swnj * and readjust the queues.
5632813Swnj */
5642813Swnj if (--chunkvector[smallest].chunk_n == 0){
5652813Swnj nchunks--;
5662813Swnj for (i = smallest; i < nchunks; i++)
5672813Swnj chunkvector[i] = chunkvector[i+1];
5682813Swnj }
5692813Swnj if (ISPROCESS(tp)){
5702813Swnj ft = tp->p.count;
5712813Swnj column(ft, tp->p.realt, tp->p.cput,
5722813Swnj tp->p.syst, tp->p.imem, tp->p.io);
5732813Swnj printf(" %.14s\n", tp->p.name);
5742813Swnj }
5752813Swnj } /* iterate to merge the lists */
5761087Sbill }
5771087Sbill
printmoney()5781087Sbill printmoney()
5791087Sbill {
5801087Sbill register i;
5811087Sbill register char *cp;
5822813Swnj register struct user *up;
5831087Sbill
5842813Swnj getnames(); /* fetches all of the names! */
5852813Swnj for (i = 0; i < maxuser; i++) {
5862813Swnj if ( (up = wasuser(i)) != 0){
5872813Swnj if (up->us_cnt) {
5882813Swnj if (up->us_name[0])
5892813Swnj printf("%-8s", up->us_name);
5902813Swnj else
5912813Swnj printf("%-8d", i);
5922813Swnj printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n",
59311840Ssam up->us_cnt, up->us_ctime / 60,
5942813Swnj up->us_io,
59517504Skarels up->us_imem / AHZ);
5962813Swnj }
5971087Sbill }
5981087Sbill }
5991087Sbill }
6001087Sbill
column(n,a,b,c,d,e)6011087Sbill column(n, a, b, c, d, e)
60211840Ssam double n, a, b, c, d, e;
6031087Sbill {
6041087Sbill
6051087Sbill printf("%8.0f", n);
6061087Sbill if(cflg) {
6071087Sbill if(n == ncom)
6081087Sbill printf("%9s", ""); else
6091087Sbill printf("%8.2f%%", 100.*n/ncom);
6101087Sbill }
6111087Sbill col(n, a, treal, "re");
6121087Sbill if (oflg)
61317504Skarels col(n, 60*AHZ*(b/(b+c)), tcpu+tsys, "u/s");
6141087Sbill else if(lflg) {
6151087Sbill col(n, b, tcpu, "u");
6161087Sbill col(n, c, tsys, "s");
6171087Sbill } else
6181087Sbill col(n, b+c, tcpu+tsys, "cp");
6191087Sbill if(tflg)
62017504Skarels printf("%8.1fre/cp", a/(b+c));
6211087Sbill if(dflg || !Dflg)
6221087Sbill printf("%10.0favio", e/(n?n:1));
6231087Sbill else
6241087Sbill printf("%10.0ftio", e);
6251087Sbill if (kflg || !Kflg)
62616717Ssam printf("%10.0fk", d/((b+c)!=0.0?(b+c):1.0));
6271087Sbill else
62817504Skarels printf("%10.0fk*sec", d/AHZ);
6291087Sbill }
6301087Sbill
col(n,a,m,cp)6311087Sbill col(n, a, m, cp)
63211840Ssam double n, a, m;
63311840Ssam char *cp;
6341087Sbill {
6351087Sbill
6361087Sbill if(jflg)
63717504Skarels printf("%11.2f%s", a/(n*(double)AHZ), cp); else
63817504Skarels printf("%11.2f%s", a/(60.*(double)AHZ), cp);
6391087Sbill if(cflg) {
6401087Sbill if(a == m)
6411087Sbill printf("%9s", ""); else
6421087Sbill printf("%8.2f%%", 100.*a/m);
6431087Sbill }
6441087Sbill }
6451087Sbill
doacct(f)6461087Sbill doacct(f)
6471087Sbill char *f;
6481087Sbill {
6491087Sbill FILE *ff;
6501087Sbill long x, y, z;
6511087Sbill struct acct fbuf;
6521087Sbill register char *cp;
6531087Sbill register int c;
65411840Ssam register struct user *up;
65511840Ssam register cell *tp;
6562813Swnj #ifdef DEBUG
6572813Swnj int nrecords = 0;
6582813Swnj #endif DEBUG
6591087Sbill
6601087Sbill if (sflg && sname) {
6611087Sbill printf("Only 1 file with -s\n");
6621087Sbill exit(0);
6631087Sbill }
6641087Sbill if (sflg)
6651087Sbill sname = f;
6661087Sbill if ((ff = fopen(f, "r"))==NULL) {
6671087Sbill printf("Can't open %s\n", f);
6681087Sbill return;
6691087Sbill }
6701087Sbill while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) {
6712813Swnj #ifdef DEBUG
6722813Swnj if (++nrecords % 1000 == 0)
6732813Swnj printf("Input record from %s number %d\n",
6742813Swnj f, nrecords);
6752813Swnj #endif DEBUG
67616717Ssam for (cp = fbuf.ac_comm; *cp && cp < &fbuf.ac_comm[NC]; cp++)
67716717Ssam if (!isascii(*cp) || iscntrl(*cp))
6781087Sbill *cp = '?';
67916717Ssam if (cp == fbuf.ac_comm)
68016717Ssam *cp++ = '?';
6811087Sbill if (fbuf.ac_flag&AFORK) {
68216717Ssam if (cp >= &fbuf.ac_comm[NC])
68316729Ssam cp = &fbuf.ac_comm[NC-1];
68416717Ssam *cp++ = '*';
6851087Sbill }
68616717Ssam if (cp < &fbuf.ac_comm[NC])
68716717Ssam *cp = '\0';
6881087Sbill x = expand(fbuf.ac_utime) + expand(fbuf.ac_stime);
68916717Ssam y = pgtok((u_short)fbuf.ac_mem);
69017504Skarels z = expand(fbuf.ac_io) / AHZ;
6911087Sbill if (uflg) {
69217504Skarels printf("%3d %6.2f cpu %8luk mem %6ld io %.*s\n",
69317504Skarels fbuf.ac_uid, x/(double)AHZ, y, z, NC, fbuf.ac_comm);
6941087Sbill continue;
6951087Sbill }
6962813Swnj up = finduser(fbuf.ac_uid);
6972813Swnj if (up == 0)
6982813Swnj continue; /* preposterous user id */
6992813Swnj up->us_cnt++;
70017504Skarels up->us_ctime += x/(double)AHZ;
7012813Swnj up->us_imem += x * y;
7022813Swnj up->us_io += z;
7031087Sbill ncom += 1.0;
7042813Swnj
7052813Swnj tp = enter(fbuf.ac_comm);
7062813Swnj tp->p.imem += x * y;
7071087Sbill timem += x * y;
7082813Swnj tp->p.count++;
70911840Ssam x = expand(fbuf.ac_etime);
7102813Swnj tp->p.realt += x;
7111087Sbill treal += x;
7121087Sbill x = expand(fbuf.ac_utime);
7132813Swnj tp->p.cput += x;
7141087Sbill tcpu += x;
7151087Sbill x = expand(fbuf.ac_stime);
7162813Swnj tp->p.syst += x;
7171087Sbill tsys += x;
7182813Swnj tp->p.io += z;
7191087Sbill tio += z;
7201087Sbill }
7211087Sbill fclose(ff);
7221087Sbill }
7231087Sbill
7242813Swnj /*
7252813Swnj * Generalized cell compare routine, to cast out users
7262813Swnj */
cellcmp(p1,p2)7272813Swnj cellcmp(p1, p2)
72811840Ssam cell *p1, *p2;
7292813Swnj {
7302813Swnj if (ISPROCESS(p1)){
7312813Swnj if (ISPROCESS(p2))
73214451Smckusick return((*cmp)(p1, p2));
7332813Swnj return(-1);
7342813Swnj }
7352813Swnj if (ISPROCESS(p2))
7362813Swnj return(1);
7372813Swnj return(0);
7382813Swnj }
73911840Ssam
ncmp(p1,p2)7401087Sbill ncmp(p1, p2)
74111840Ssam cell *p1, *p2;
7421087Sbill {
7431087Sbill
7442813Swnj if(p1->p.count == p2->p.count)
7451087Sbill return(tcmp(p1, p2));
7461087Sbill if(rflg)
7472813Swnj return(p1->p.count - p2->p.count);
7482813Swnj return(p2->p.count - p1->p.count);
7491087Sbill }
7501087Sbill
bflgcmp(p1,p2)75137851Sbostic bflgcmp(p1, p2)
75211840Ssam cell *p1, *p2;
7531087Sbill {
7541087Sbill double f1, f2;
7551087Sbill double sum();
7561087Sbill
7572813Swnj f1 = sum(p1)/p1->p.count;
7582813Swnj f2 = sum(p2)/p2->p.count;
7591087Sbill if(f1 < f2) {
7601087Sbill if(rflg)
7611087Sbill return(-1);
7621087Sbill return(1);
7631087Sbill }
7641087Sbill if(f1 > f2) {
7651087Sbill if(rflg)
7661087Sbill return(1);
7671087Sbill return(-1);
7681087Sbill }
7691087Sbill return(0);
7701087Sbill }
7711087Sbill
Kcmp(p1,p2)7721087Sbill Kcmp(p1, p2)
77311840Ssam cell *p1, *p2;
7741087Sbill {
7751087Sbill
7762813Swnj if (p1->p.imem < p2->p.imem) {
7771087Sbill if(rflg)
7781087Sbill return(-1);
7791087Sbill return(1);
7801087Sbill }
7812813Swnj if (p1->p.imem > p2->p.imem) {
7821087Sbill if(rflg)
7831087Sbill return(1);
7841087Sbill return(-1);
7851087Sbill }
7861087Sbill return(0);
7871087Sbill }
7881087Sbill
kcmp(p1,p2)7891087Sbill kcmp(p1, p2)
79011840Ssam cell *p1, *p2;
7911087Sbill {
7921087Sbill double a1, a2;
7931087Sbill
7942813Swnj a1 = p1->p.imem / ((p1->p.cput+p1->p.syst)?(p1->p.cput+p1->p.syst):1);
7952813Swnj a2 = p2->p.imem / ((p2->p.cput+p2->p.syst)?(p2->p.cput+p2->p.syst):1);
7961087Sbill if (a1 < a2) {
7971087Sbill if(rflg)
7981087Sbill return(-1);
7991087Sbill return(1);
8001087Sbill }
8011087Sbill if (a1 > a2) {
8021087Sbill if(rflg)
8031087Sbill return(1);
8041087Sbill return(-1);
8051087Sbill }
8061087Sbill return(0);
8071087Sbill }
8081087Sbill
dcmp(p1,p2)8091087Sbill dcmp(p1, p2)
81011840Ssam cell *p1, *p2;
8111087Sbill {
8121087Sbill double a1, a2;
8131087Sbill
8142813Swnj a1 = p1->p.io / (p1->p.count?p1->p.count:1);
8152813Swnj a2 = p2->p.io / (p2->p.count?p2->p.count:1);
8161087Sbill if (a1 < a2) {
8171087Sbill if(rflg)
8181087Sbill return(-1);
8191087Sbill return(1);
8201087Sbill }
8211087Sbill if (a1 > a2) {
8221087Sbill if(rflg)
8231087Sbill return(1);
8241087Sbill return(-1);
8251087Sbill }
8261087Sbill return(0);
8271087Sbill }
8281087Sbill
Dcmp(p1,p2)8291087Sbill Dcmp(p1, p2)
83011840Ssam cell *p1, *p2;
8311087Sbill {
8321087Sbill
8332813Swnj if (p1->p.io < p2->p.io) {
8341087Sbill if(rflg)
8351087Sbill return(-1);
8361087Sbill return(1);
8371087Sbill }
8382813Swnj if (p1->p.io > p2->p.io) {
8391087Sbill if(rflg)
8401087Sbill return(1);
8411087Sbill return(-1);
8421087Sbill }
8431087Sbill return(0);
8441087Sbill }
8451087Sbill
tcmp(p1,p2)8461087Sbill tcmp(p1, p2)
84711840Ssam cell *p1, *p2;
8481087Sbill {
8491087Sbill extern double sum();
8501087Sbill double f1, f2;
8511087Sbill
8521087Sbill f1 = sum(p1);
8531087Sbill f2 = sum(p2);
8541087Sbill if(f1 < f2) {
8551087Sbill if(rflg)
8561087Sbill return(-1);
8571087Sbill return(1);
8581087Sbill }
8591087Sbill if(f1 > f2) {
8601087Sbill if(rflg)
8611087Sbill return(1);
8621087Sbill return(-1);
8631087Sbill }
8641087Sbill return(0);
8651087Sbill }
8661087Sbill
sum(p)8671087Sbill double sum(p)
86811840Ssam cell *p;
8691087Sbill {
8701087Sbill
8712813Swnj if(p->p.name[0] == 0)
8721087Sbill return(0.0);
8732813Swnj return( p->p.cput + p->p.syst);
8741087Sbill }
8751087Sbill
init()8761087Sbill init()
8771087Sbill {
87811840Ssam struct user userbuf;
87911840Ssam struct process tbuf;
88011840Ssam register cell *tp;
88111840Ssam register struct user *up;
88211840Ssam int uid;
8831087Sbill FILE *f;
8841087Sbill
88517504Skarels if ((f = fopen(savacct, "r")) == NULL)
8861087Sbill goto gshm;
8872813Swnj while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) {
8882813Swnj tp = enter(tbuf.name);
8891087Sbill ncom += tbuf.count;
8902813Swnj tp->p.count = tbuf.count;
8911087Sbill treal += tbuf.realt;
8922813Swnj tp->p.realt = tbuf.realt;
8931087Sbill tcpu += tbuf.cput;
8942813Swnj tp->p.cput = tbuf.cput;
8951087Sbill tsys += tbuf.syst;
8962813Swnj tp->p.syst = tbuf.syst;
8971087Sbill tio += tbuf.io;
8982813Swnj tp->p.io = tbuf.io;
8991087Sbill timem += tbuf.imem;
9002813Swnj tp->p.imem = tbuf.imem;
9011087Sbill }
9021087Sbill fclose(f);
9031087Sbill gshm:
90417504Skarels if ((f = fopen(usracct, "r")) == NULL)
9051087Sbill return;
9062813Swnj for(uid = 0;
9072813Swnj fread((char *)&(userbuf.oldu), sizeof(struct Olduser), 1, f) == 1;
9082813Swnj uid++){
9092813Swnj if (userbuf.us_cnt){
9102813Swnj up = finduser(uid);
9112813Swnj if (up == 0)
9122813Swnj continue; /* preposterous user id */
9132813Swnj up->oldu = userbuf.oldu;
9142813Swnj }
9152813Swnj }
9161087Sbill fclose(f);
9171087Sbill }
9181087Sbill
strip()9191087Sbill strip()
9201087Sbill {
92111840Ssam int c;
92211840Ssam register struct allocbox *allocwalk;
92311840Ssam register cell *tp, *ub, *junkp;
9241087Sbill
9252813Swnj if (fflg)
9262813Swnj printf("Categorizing commands used %d times or fewer as **junk**\n",
9272813Swnj thres);
9282813Swnj junkp = enter("**junk**");
9292813Swnj PROCESSITERATE(allocwalk, tp, ub){
9302813Swnj if (tp->p.name[0] && tp->p.count <= thres) {
9312813Swnj if (!fflg)
9322813Swnj printf("%.14s--", tp->p.name);
9332813Swnj if (fflg || ((c=getchar())=='y')) {
9342813Swnj tp->p.name[0] = '\0';
9352813Swnj junkp->p.count += tp->p.count;
9362813Swnj junkp->p.realt += tp->p.realt;
9372813Swnj junkp->p.cput += tp->p.cput;
9382813Swnj junkp->p.syst += tp->p.syst;
9392813Swnj junkp->p.imem += tp->p.imem;
9402813Swnj junkp->p.io += tp->p.io;
9411087Sbill }
9422813Swnj if (!fflg)
9432813Swnj while (c && c!='\n')
9442813Swnj c = getchar();
9451087Sbill }
9461087Sbill }
9471087Sbill }
9481087Sbill
9491087Sbill time_t
expand(t)9501087Sbill expand(t)
95111840Ssam unsigned t;
9521087Sbill {
9531087Sbill register time_t nt;
9541087Sbill
9551087Sbill nt = t&017777;
9561087Sbill t >>= 13;
9571087Sbill while (t!=0) {
9581087Sbill t--;
9591087Sbill nt <<= 3;
9601087Sbill }
9611087Sbill return(nt);
9621087Sbill }
9631087Sbill
96411840Ssam static char UserKey[NAMELG + 2];
96511840Ssam
96611840Ssam char *
makekey(uid)96711840Ssam makekey(uid)
96811840Ssam int uid;
9692813Swnj {
97032450Sbostic (void)sprintf(UserKey+1, "%04x", uid);
9712813Swnj UserKey[0] = USERKEY;
9722813Swnj return(UserKey);
9732813Swnj }
9741087Sbill
97511840Ssam struct user *
wasuser(uid)97611840Ssam wasuser(uid)
97711840Ssam int uid;
9782813Swnj {
97911840Ssam struct user *tp;
98011840Ssam
9812813Swnj htabinstall = 0;
9822813Swnj tp = finduser(uid);
9832813Swnj htabinstall = 1;
9842813Swnj return(tp);
9852813Swnj }
98611840Ssam
9872813Swnj /*
9882813Swnj * Only call this if you really want to insert it in the table!
9892813Swnj */
99011840Ssam struct user *
finduser(uid)99111840Ssam finduser(uid)
99211840Ssam int uid;
9932813Swnj {
99411840Ssam
9952813Swnj if (uid > maxuser){
9962813Swnj fprintf(stderr, "Preposterous user id, %d: ignored\n", uid);
9972813Swnj return(0);
9982813Swnj }
99911840Ssam return((struct user*)enter(makekey(uid)));
10002813Swnj }
10011087Sbill
10022813Swnj /*
10032813Swnj * Set the names of all users in the password file.
10042813Swnj * We will later not print those that didn't do anything.
10052813Swnj */
getnames()10062813Swnj getnames()
10071087Sbill {
100811840Ssam register struct user *tp;
10091087Sbill register struct passwd *pw;
10101087Sbill struct passwd *getpwent();
10111087Sbill
10122813Swnj setpwent();
10132813Swnj while (pw = getpwent()){
101417504Skarels /* use first name in passwd file for duplicate uid's */
101517504Skarels if ((tp = wasuser(pw->pw_uid)) != 0 && !isalpha(tp->us_name[0]))
10162813Swnj strncpy(tp->us_name, pw->pw_name, NAMELG);
10171087Sbill }
10181087Sbill endpwent();
10191087Sbill }
10202813Swnj
102111840Ssam int
getmaxuid()102211840Ssam getmaxuid()
10232813Swnj {
102411840Ssam register struct user *tp;
102511840Ssam register struct passwd *pw;
102611840Ssam struct passwd *getpwent();
102711840Ssam int maxuid = -1;
10282813Swnj
10292813Swnj setpwent();
10302813Swnj while(pw = getpwent()){
10312813Swnj if (pw->pw_uid > maxuid)
10322813Swnj maxuid = pw->pw_uid;
10332813Swnj }
10342813Swnj endpwent();
10352813Swnj return(maxuid);
10362813Swnj }
10372813Swnj
tabinit()10382813Swnj tabinit()
10392813Swnj {
10402813Swnj allochead = 0;
10412813Swnj alloctail = 0;
10422813Swnj nexttab = 0;
10432813Swnj tabsleft = 0;
10442813Swnj htab = 0;
10452813Swnj ntabs = 0;
10462813Swnj htaballoc(); /* get the first part of the hash table */
10472813Swnj }
10482813Swnj
10492813Swnj #define ALLOCQTY sizeof (struct allocbox)
105011840Ssam cell *
taballoc()105111840Ssam taballoc()
10522813Swnj {
105311840Ssam
10542813Swnj if (tabsleft == 0){
10552813Swnj newbox = (struct allocbox *)calloc(1, ALLOCQTY);
10562813Swnj tabsleft = TABDALLOP;
10572813Swnj nexttab = &newbox->tabslots[0];
10582813Swnj if (alloctail == 0){
10592813Swnj allochead = alloctail = newbox;
10602813Swnj } else {
10612813Swnj alloctail->nextalloc = newbox;
10622813Swnj alloctail = newbox;
10632813Swnj }
10642813Swnj }
10652813Swnj --tabsleft;
10662813Swnj ++ntabs;
10672813Swnj #ifdef DEBUG
10682813Swnj if (ntabs % 100 == 0)
10692813Swnj printf("##Accounting table slot # %d\n", ntabs);
10702813Swnj #endif DEBUG
10712813Swnj return(nexttab++);
10722813Swnj }
10732813Swnj
htaballoc()10742813Swnj htaballoc()
10752813Swnj {
107611840Ssam register struct hashdallop *new;
10772813Swnj #ifdef DEBUG
107811840Ssam static int ntables = 0;
107911840Ssam
10802813Swnj printf("%%%New hash table chunk allocated, number %d\n", ++ntables);
10812813Swnj #endif DEBUG
10822813Swnj new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop));
10832813Swnj if (htab == 0)
10842813Swnj htab = new;
10852813Swnj else { /* add AFTER the 1st slot */
10862813Swnj new->h_next = htab->h_next;
10872813Swnj htab->h_next = new;
10882813Swnj }
10892813Swnj }
10902813Swnj
10912813Swnj #define HASHCLOGGED (NHASH / 2)
10922813Swnj /*
10932813Swnj * Lookup a symbol passed in as the argument.
10942813Swnj *
10952813Swnj * We take pains to avoid function calls; this function
10962813Swnj * is called quite frequently, and the calling overhead
10972813Swnj * contributes significantly to the overall execution speed of sa.
10982813Swnj */
109911840Ssam cell *
enter(name)110011840Ssam enter(name)
110111840Ssam char *name;
11022813Swnj {
110311840Ssam static int initialprobe;
110411840Ssam register cell **hp;
110511840Ssam register char *from, *to;
110611840Ssam register int len, nprobes;
110711840Ssam static struct hashdallop *hdallop, *emptyhd;
110811840Ssam static cell **emptyslot, **hp_ub;
11092813Swnj
11102813Swnj emptyslot = 0;
11112813Swnj for (nprobes = 0, from = name, len = 0;
11122813Swnj *from && len < NC;
11132813Swnj nprobes <<= 2, nprobes += *from++, len++)
11142813Swnj continue;
11152813Swnj nprobes += from[-1] << 5;
11162813Swnj nprobes %= NHASH;
11172813Swnj if (nprobes < 0)
11182813Swnj nprobes += NHASH;
11192813Swnj
11202813Swnj initialprobe = nprobes;
11212813Swnj for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
11222813Swnj for (hp = &(hdallop->h_tab[initialprobe]),
11232813Swnj nprobes = 1,
11242813Swnj hp_ub = &(hdallop->h_tab[NHASH]);
11252813Swnj (*hp) && (nprobes < NHASH);
11262813Swnj hp += nprobes,
11272813Swnj hp -= (hp >= hp_ub) ? NHASH:0,
11282813Swnj nprobes += 2)
11292813Swnj {
11302813Swnj from = name;
11312813Swnj to = (*hp)->p.name;
11322813Swnj
11332813Swnj for (len = 0; (len<NC) && *from; len++)
11342813Swnj if (*from++ != *to++)
11352813Swnj goto nextprobe;
11362813Swnj if (len >= NC) /*both are maximal length*/
11372813Swnj return(*hp);
11382813Swnj if (*to == 0) /*assert *from == 0*/
11392813Swnj return(*hp);
11402813Swnj nextprobe: ;
11412813Swnj }
11422813Swnj if (*hp == 0 && emptyslot == 0 &&
11432813Swnj hdallop->h_nused < HASHCLOGGED) {
11442813Swnj emptyslot = hp;
11452813Swnj emptyhd = hdallop;
11462813Swnj }
11472813Swnj }
11482813Swnj if (emptyslot == 0) {
11492813Swnj htaballoc();
11502813Swnj hdallop = htab->h_next; /* aren't we smart! */
11512813Swnj hp = &hdallop->h_tab[initialprobe];
11522813Swnj } else {
11532813Swnj hdallop = emptyhd;
11542813Swnj hp = emptyslot;
11552813Swnj }
11562813Swnj if (htabinstall){
11572813Swnj *hp = taballoc();
11582813Swnj hdallop->h_nused++;
11592813Swnj for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++)
11602813Swnj if ((*to++ = *from++) == '\0')
11612813Swnj break;
11622813Swnj return(*hp);
11632813Swnj }
11642813Swnj return(0);
116511840Ssam }
1166