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