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