say what you mean... kre
[unix-history] / usr / src / usr.sbin / sa / sa.c
CommitLineData
651c99bb 1#ifndef lint
747bddc6 2static char *sccsid = "@(#)sa.c 4.4 (Berkeley) 83/08/11";
651c99bb 3#endif
32d08ae3
BJ
4
5/*
6 * Extensive modifications to internal data structures
7 * to allow arbitrary number of different commands and users added.
8 *
9 * Also allowed the digit option on the -v flag (interactive
10 * threshold compress) to be a digit string, so one can
11 * set the threshold > 9.
12 *
13 * Also added the -f flag, to force no interactive threshold
14 * compression with the -v flag.
15 *
16 * Robert Henry
17 * UC Berkeley
18 * 31jan81
19 */
2efccdb1
BJ
20#include <stdio.h>
21#include <sys/types.h>
22#include <sys/acct.h>
23#include <signal.h>
32d08ae3
BJ
24#include <utmp.h>
25#include <pwd.h>
2efccdb1
BJ
26
27/* interpret command time accounting */
28
2efccdb1 29#define NC sizeof(acctbuf.ac_comm)
32d08ae3 30
2efccdb1
BJ
31struct acct acctbuf;
32int lflg;
33int cflg;
34int Dflg;
35int dflg;
36int iflg;
37int jflg;
38int Kflg;
39int kflg;
40int nflg;
41int aflg;
42int rflg;
43int oflg;
44int tflg;
45int vflg;
32d08ae3 46int fflg;
2efccdb1 47int uflg;
32d08ae3 48int thres;
2efccdb1
BJ
49int sflg;
50int bflg;
51int mflg;
52
32d08ae3
BJ
53struct utmp utmp;
54#define NAMELG (sizeof(utmp.ut_name)+1)
55
56struct Olduser{
57 int Us_cnt;
58 double Us_ctime;
59 double Us_io;
60 double Us_imem;
61};
62
2efccdb1 63struct user {
32d08ae3
BJ
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
2efccdb1 72
32d08ae3
BJ
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
80int maxuser; /* highest uid from /etc/passwd, + 10 for slop*/
81
82struct process {
2efccdb1
BJ
83 char name[NC];
84 int count;
85 double realt;
86 double cput;
87 double syst;
88 double imem;
89 double io;
32d08ae3
BJ
90};
91
92union Tab{
93 struct process p;
94 struct user u;
95};
96
97typedef union Tab cell;
98
99int (*cmp)(); /* compares 2 cells; set to appropriate func */
100cell *enter();
101struct user *finduser();
102struct user *wasuser();
103
104/*
105 * Table elements are keyed by the name of the file exec'ed.
106 * Because on large systems, many files can be exec'ed,
107 * a static table size may grow to be too large.
108 *
109 * Table elements are allocated in chunks dynamically, linked
110 * together so that they may be retrieved sequentially.
111 *
112 * An index into the table structure is provided by hashing through
113 * a seperate hash table.
114 * The hash table is segmented, and dynamically extendable.
115 * Realize that the hash table and accounting information is kept
116 * in different segments!
117 *
118 * We have a linked list of hash table segments; within each
119 * segment we use a quadratic rehash that touches no more than 1/2
120 * of the buckets in the hash table when probing.
121 * If the probe does not find the desired symbol, it moves to the
122 * next segment, or allocates a new segment.
123 *
124 * Hash table segments are kept on the linked list with the first
125 * segment always first (that will probably contain the
126 * most frequently executed commands) and
127 * the last added segment immediately after the first segment,
128 * to hopefully gain something by locality of reference.
129 *
130 * We store the per user information in the same structure as
131 * the per exec'ed file information. This allows us to use the
132 * same managers for both, as the number of user id's may be very
133 * large.
134 * User information is keyed by the first character in the name
135 * being a '\001', followed by four bytes of (long extended)
136 * user id number, followed by a null byte.
137 * The actual user names are kept in a seperate field of the
138 * user structure, and is filled in upon demand later.
139 * Iteration through all users by low user id to high user id
140 * is done by just probing the table, which is gross.
141 */
142#define USERKEY '\001'
143#define ISPROCESS(tp) (tp->p.name[0] && (tp->p.name[0] != USERKEY))
144#define ISUSER(tp) (tp->p.name[0] && (tp->p.name[0] == USERKEY))
145
146#define TABDALLOP 500
147struct allocbox{
148 struct allocbox *nextalloc;
149 cell tabslots[TABDALLOP];
150};
151
152struct allocbox *allochead; /*head of chunk list*/
153struct allocbox *alloctail; /*tail*/
154struct allocbox *newbox; /*for creating a new chunk*/
155cell *nexttab; /*next table element that is free*/
156int tabsleft; /*slots left in current chunk*/
157int ntabs;
158/*
159 * Iterate through all symbols in the symbol table in declaration
160 * order.
161 * struct allocbox *allocwalk;
162 * cell *sp, *ub;
163 *
164 * sp points to the desired item, allocwalk and ub are there
165 * to make the iteration go.
166 */
167
168#define DECLITERATE(allocwalk, walkpointer, ubpointer) \
169 for(allocwalk = allochead; \
170 allocwalk != 0; \
171 allocwalk = allocwalk->nextalloc) \
172 for (walkpointer = &allocwalk->tabslots[0],\
173 ubpointer = &allocwalk->tabslots[TABDALLOP], \
174 ubpointer = ubpointer > ( (cell *)alloctail) \
175 ? nexttab : ubpointer ;\
176 walkpointer < ubpointer; \
177 walkpointer++ )
178
179#define TABCHUNKS(allocwalk, tabptr, size) \
180 for (allocwalk = allochead; \
181 allocwalk != 0; \
182 allocwalk = allocwalk->nextalloc) \
183 if ( \
184 (tabptr = &allocwalk->tabslots[0]), \
185 (size = \
186 ( (&allocwalk->tabslots[TABDALLOP]) \
187 > ((cell *)alloctail) \
188 ) \
189 ? (nexttab - tabptr) : TABDALLOP \
190 ), \
191 1 \
192 )
193#define PROCESSITERATE(allocwalk, walkpointer, ubpointer) \
194 DECLITERATE(allocwalk, walkpointer, ubpointer) \
195 if (ISPROCESS(walkpointer))
196
197#define USERITERATE(allocwalk, walkpointer, ubpointer) \
198 DECLITERATE(allocwalk, walkpointer, ubpointer) \
199 if (ISUSER(walkpointer))
200/*
201 * When we have to sort the segmented accounting table, we
202 * create a vector of sorted queues that is merged
203 * to sort the entire accounting table.
204 */
205struct chunkdesc {
206 cell *chunk_tp;
207 int chunk_n;
208};
209
210/*
211 * Hash table segments and manager
212 */
213#define NHASH 1103
214struct hashdallop {
215 int h_nused;
216 struct hashdallop *h_next;
217 cell *h_tab[NHASH];
218};
219struct hashdallop *htab; /* head of the list */
220int htabinstall; /* install the symbol */
2efccdb1
BJ
221
222double treal;
223double tcpu;
224double tsys;
225double tio;
226double timem;
32d08ae3 227cell *junkp;
2efccdb1
BJ
228char *sname;
229double ncom;
230time_t expand();
231char *getname();
232
32d08ae3
BJ
233/*
234 * usracct saves records of type Olduser.
235 * There is one record for every possible uid less than
236 * the largest uid seen in the previous usracct or in savacct.
237 * uid's that had no activity correspond to zero filled slots;
238 * thus one can index the file and get the user record out.
239 * It would be better to save only user information for users
240 * that the system knows about to save space, but that is not
241 * upward compatabile with the old system.
242 *
243 * In the old version of sa, uid's greater than 999 were not handled
244 * properly; this system will do that.
245 */
246
247#ifdef DEBUG
248#define USRACCT "./usracct"
249#define SAVACCT "./savacct"
250#define ACCT "./acct"
251#else
252#define USRACCT "/usr/adm/usracct"
253#define SAVACCT "/usr/adm/savacct"
254#define ACCT "/usr/adm/acct"
255#endif DEBUG
256\f
257
258int cellcmp();
259cell *junkp = 0;
260/*
261 * The threshold is built up from digits in the argv ;
262 * eg, -v1s0u1
263 * will build a value of thres of 101.
264 *
265 * If the threshold is zero after processing argv, it is set to 1
266 */
267int thres = 0;
268int htabinstall = 1;
269int maxuser = -1;
270int (*cmp)();
271
651c99bb
SL
272extern tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
273extern double sum();
274
2efccdb1 275main(argc, argv)
651c99bb 276 char **argv;
2efccdb1
BJ
277{
278 FILE *ff;
651c99bb
SL
279 double ft;
280 register struct allocbox *allocwalk;
281 register cell *tp, *ub;
282 int i, j, size, nchunks, smallest;
283 struct chunkdesc *chunkvector;
32d08ae3
BJ
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",
651c99bb 533 up->us_cnt, up->us_ctime / 60,
32d08ae3
BJ
534 up->us_io,
535 up->us_imem / (60 * 2));
536 }
2efccdb1
BJ
537 }
538 }
539}
540
541column(n, a, b, c, d, e)
651c99bb 542 double n, a, b, c, d, e;
2efccdb1
BJ
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)
651c99bb
SL
572 double n, a, m;
573 char *cp;
2efccdb1
BJ
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;
651c99bb
SL
594 register struct user *up;
595 register cell *tp;
32d08ae3
BJ
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",
651c99bb 637 fbuf.ac_uid, x, y, z, fbuf.ac_comm);
2efccdb1
BJ
638 continue;
639 }
32d08ae3
BJ
640 up = finduser(fbuf.ac_uid);
641 if (up == 0)
642 continue; /* preposterous user id */
643 up->us_cnt++;
651c99bb 644 up->us_ctime += x;
32d08ae3
BJ
645 up->us_imem += x * y;
646 up->us_io += z;
2efccdb1 647 ncom += 1.0;
32d08ae3
BJ
648
649 tp = enter(fbuf.ac_comm);
650 tp->p.imem += x * y;
2efccdb1 651 timem += x * y;
32d08ae3 652 tp->p.count++;
651c99bb 653 x = expand(fbuf.ac_etime);
32d08ae3 654 tp->p.realt += x;
2efccdb1
BJ
655 treal += x;
656 x = expand(fbuf.ac_utime);
32d08ae3 657 tp->p.cput += x;
2efccdb1
BJ
658 tcpu += x;
659 x = expand(fbuf.ac_stime);
32d08ae3 660 tp->p.syst += x;
2efccdb1 661 tsys += x;
32d08ae3 662 tp->p.io += z;
2efccdb1
BJ
663 tio += z;
664 }
665 fclose(ff);
666}
667
32d08ae3
BJ
668/*
669 * Generalized cell compare routine, to cast out users
670 */
671cellcmp(p1, p2)
651c99bb 672 cell *p1, *p2;
32d08ae3
BJ
673{
674 if (ISPROCESS(p1)){
675 if (ISPROCESS(p2))
747bddc6 676 return((*cmp)(p1, p2));
32d08ae3
BJ
677 return(-1);
678 }
679 if (ISPROCESS(p2))
680 return(1);
681 return(0);
682}
651c99bb 683
2efccdb1 684ncmp(p1, p2)
651c99bb 685 cell *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)
651c99bb 696 cell *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)
651c99bb 717 cell *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)
651c99bb 734 cell *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)
651c99bb 754 cell *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)
651c99bb 774 cell *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)
651c99bb 791 cell *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)
651c99bb 812 cell *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{
651c99bb
SL
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{
651c99bb
SL
865 int c;
866 register struct allocbox *allocwalk;
867 register cell *tp, *ub, *junkp;
32d08ae3
BJ
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)
651c99bb 895 unsigned t;
2efccdb1
BJ
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
651c99bb
SL
908static char UserKey[NAMELG + 2];
909
910char *
911makekey(uid)
912 int uid;
32d08ae3
BJ
913{
914 sprintf(UserKey+1, "%04x", uid);
915 UserKey[0] = USERKEY;
916 return(UserKey);
917}
2efccdb1 918
651c99bb
SL
919struct user *
920wasuser(uid)
921 int uid;
32d08ae3 922{
651c99bb
SL
923 struct user *tp;
924
32d08ae3
BJ
925 htabinstall = 0;
926 tp = finduser(uid);
927 htabinstall = 1;
928 return(tp);
929}
651c99bb 930
32d08ae3
BJ
931/*
932 * Only call this if you really want to insert it in the table!
933 */
651c99bb
SL
934struct user *
935finduser(uid)
936 int uid;
32d08ae3 937{
651c99bb 938
32d08ae3
BJ
939 if (uid > maxuser){
940 fprintf(stderr, "Preposterous user id, %d: ignored\n", uid);
941 return(0);
32d08ae3 942 }
651c99bb 943 return((struct user*)enter(makekey(uid)));
32d08ae3 944}
2efccdb1 945
32d08ae3
BJ
946/*
947 * Set the names of all users in the password file.
948 * We will later not print those that didn't do anything.
949 */
950getnames()
2efccdb1 951{
651c99bb 952 register struct user *tp;
2efccdb1 953 register struct passwd *pw;
2efccdb1
BJ
954 struct passwd *getpwent();
955
32d08ae3
BJ
956 setpwent();
957 while (pw = getpwent()){
958 if ( (tp = wasuser(pw->pw_uid)) != 0)
959 strncpy(tp->us_name, pw->pw_name, NAMELG);
2efccdb1 960 }
2efccdb1 961 endpwent();
2efccdb1 962}
32d08ae3 963
651c99bb
SL
964int
965getmaxuid()
32d08ae3 966{
651c99bb
SL
967 register struct user *tp;
968 register struct passwd *pw;
969 struct passwd *getpwent();
970 int maxuid = -1;
32d08ae3
BJ
971
972 setpwent();
973 while(pw = getpwent()){
974 if (pw->pw_uid > maxuid)
975 maxuid = pw->pw_uid;
976 }
977 endpwent();
978 return(maxuid);
979}
980
981tabinit()
982{
983 allochead = 0;
984 alloctail = 0;
985 nexttab = 0;
986 tabsleft = 0;
987 htab = 0;
988 ntabs = 0;
989 htaballoc(); /* get the first part of the hash table */
990}
991
992#define ALLOCQTY sizeof (struct allocbox)
651c99bb
SL
993cell *
994taballoc()
32d08ae3 995{
651c99bb 996
32d08ae3
BJ
997 if (tabsleft == 0){
998 newbox = (struct allocbox *)calloc(1, ALLOCQTY);
999 tabsleft = TABDALLOP;
1000 nexttab = &newbox->tabslots[0];
1001 if (alloctail == 0){
1002 allochead = alloctail = newbox;
1003 } else {
1004 alloctail->nextalloc = newbox;
1005 alloctail = newbox;
1006 }
1007 }
1008 --tabsleft;
1009 ++ntabs;
1010#ifdef DEBUG
1011 if (ntabs % 100 == 0)
1012 printf("##Accounting table slot # %d\n", ntabs);
1013#endif DEBUG
1014 return(nexttab++);
1015}
1016
1017htaballoc()
1018{
651c99bb 1019 register struct hashdallop *new;
32d08ae3 1020#ifdef DEBUG
651c99bb
SL
1021 static int ntables = 0;
1022
32d08ae3
BJ
1023 printf("%%%New hash table chunk allocated, number %d\n", ++ntables);
1024#endif DEBUG
1025 new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop));
1026 if (htab == 0)
1027 htab = new;
1028 else { /* add AFTER the 1st slot */
1029 new->h_next = htab->h_next;
1030 htab->h_next = new;
1031 }
1032}
1033
1034#define HASHCLOGGED (NHASH / 2)
1035/*
1036 * Lookup a symbol passed in as the argument.
1037 *
1038 * We take pains to avoid function calls; this function
1039 * is called quite frequently, and the calling overhead
1040 * contributes significantly to the overall execution speed of sa.
1041 */
651c99bb
SL
1042cell *
1043enter(name)
1044 char *name;
32d08ae3 1045{
651c99bb
SL
1046 static int initialprobe;
1047 register cell **hp;
1048 register char *from, *to;
1049 register int len, nprobes;
1050 static struct hashdallop *hdallop, *emptyhd;
1051 static cell **emptyslot, **hp_ub;
32d08ae3
BJ
1052
1053 emptyslot = 0;
1054 for (nprobes = 0, from = name, len = 0;
1055 *from && len < NC;
1056 nprobes <<= 2, nprobes += *from++, len++)
1057 continue;
1058 nprobes += from[-1] << 5;
1059 nprobes %= NHASH;
1060 if (nprobes < 0)
1061 nprobes += NHASH;
1062
1063 initialprobe = nprobes;
1064 for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
1065 for (hp = &(hdallop->h_tab[initialprobe]),
1066 nprobes = 1,
1067 hp_ub = &(hdallop->h_tab[NHASH]);
1068 (*hp) && (nprobes < NHASH);
1069 hp += nprobes,
1070 hp -= (hp >= hp_ub) ? NHASH:0,
1071 nprobes += 2)
1072 {
1073 from = name;
1074 to = (*hp)->p.name;
1075
1076 for (len = 0; (len<NC) && *from; len++)
1077 if (*from++ != *to++)
1078 goto nextprobe;
1079 if (len >= NC) /*both are maximal length*/
1080 return(*hp);
1081 if (*to == 0) /*assert *from == 0*/
1082 return(*hp);
1083 nextprobe: ;
1084 }
1085 if (*hp == 0 && emptyslot == 0 &&
1086 hdallop->h_nused < HASHCLOGGED) {
1087 emptyslot = hp;
1088 emptyhd = hdallop;
1089 }
1090 }
1091 if (emptyslot == 0) {
1092 htaballoc();
1093 hdallop = htab->h_next; /* aren't we smart! */
1094 hp = &hdallop->h_tab[initialprobe];
1095 } else {
1096 hdallop = emptyhd;
1097 hp = emptyslot;
1098 }
1099 if (htabinstall){
1100 *hp = taballoc();
1101 hdallop->h_nused++;
1102 for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++)
1103 if ((*to++ = *from++) == '\0')
1104 break;
1105 return(*hp);
1106 }
1107 return(0);
651c99bb 1108}