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