fix silly indirect-through-zero bug
[unix-history] / usr / src / usr.sbin / sa / sa.c
CommitLineData
8f5bfb1c
KB
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * %sccs.include.proprietary.c%
6 */
7
8#ifndef lint
9char copyright[] =
10"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
11 All rights reserved.\n";
12#endif /* not lint */
13
651c99bb 14#ifndef lint
8f5bfb1c
KB
15static char sccsid[] = "@(#)sa.c 4.14 (Berkeley) %G%";
16#endif /* not lint */
32d08ae3
BJ
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 */
2efccdb1 33#include <stdio.h>
181baa37 34#include <ctype.h>
2efccdb1
BJ
35#include <sys/types.h>
36#include <sys/acct.h>
37#include <signal.h>
32d08ae3
BJ
38#include <utmp.h>
39#include <pwd.h>
f76fd03c 40#include "pathnames.h"
2efccdb1
BJ
41
42/* interpret command time accounting */
43
2efccdb1 44#define NC sizeof(acctbuf.ac_comm)
32d08ae3 45
2efccdb1
BJ
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;
32d08ae3 61int fflg;
2efccdb1 62int uflg;
32d08ae3 63int thres;
2efccdb1
BJ
64int sflg;
65int bflg;
66int mflg;
67
32d08ae3
BJ
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
2efccdb1 78struct user {
32d08ae3
BJ
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
2efccdb1 87
32d08ae3
BJ
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 {
2efccdb1
BJ
98 char name[NC];
99 int count;
100 double realt;
101 double cput;
102 double syst;
103 double imem;
104 double io;
32d08ae3
BJ
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 */
2efccdb1
BJ
236
237double treal;
238double tcpu;
239double tsys;
240double tio;
241double timem;
32d08ae3 242cell *junkp;
2efccdb1
BJ
243char *sname;
244double ncom;
245time_t expand();
246char *getname();
247
32d08ae3
BJ
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
f76fd03c
KB
267#define USRACCT _PATH_USRACCT
268#define SAVACCT _PATH_SAVACCT
269#define ACCT _PATH_ACCT
32d08ae3
BJ
270#endif DEBUG
271\f
272
7e0e61a0
MK
273char *usracct = USRACCT;
274char *savacct = SAVACCT;
275
32d08ae3
BJ
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
181baa37
SL
290/* we assume pagesize is at least 1k */
291int pgdiv;
a3b164cb 292#define pgtok(x) ((x) * pgdiv)
181baa37 293
fb3abc15 294extern tcmp(), ncmp(), bflgcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
651c99bb
SL
295extern double sum();
296
2efccdb1 297main(argc, argv)
651c99bb 298 char **argv;
2efccdb1
BJ
299{
300 FILE *ff;
651c99bb
SL
301 double ft;
302 register struct allocbox *allocwalk;
303 register cell *tp, *ub;
304 int i, j, size, nchunks, smallest;
305 struct chunkdesc *chunkvector;
32d08ae3 306
181baa37
SL
307 pgdiv = getpagesize() / 1024;
308 if (pgdiv == 0)
309 pgdiv = 1;
32d08ae3
BJ
310 maxuser = USERSLOP + getmaxuid();
311
312 tabinit();
2efccdb1
BJ
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++;
fb3abc15 331 cmp = bflgcmp;
2efccdb1
BJ
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':
32d08ae3 398 thres = thres * 10 + (argv[0][i]-'0');
2efccdb1
BJ
399 break;
400
401 case 'v':
402 vflg++;
403 break;
404
32d08ae3
BJ
405 case 'f':
406 fflg++; /* force v option; no tty interaction */
407 break;
408
2efccdb1
BJ
409 case 'u':
410 uflg++;
411 break;
412
413 case 'm':
414 mflg++;
415 break;
7e0e61a0
MK
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);
2efccdb1
BJ
429 }
430 }
7e0e61a0
MK
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
32d08ae3
BJ
453 if (thres == 0)
454 thres = 1;
2efccdb1
BJ
455 if (iflg==0)
456 init();
7e0e61a0 457 if (argc<1)
32d08ae3 458 doacct(ACCT);
7e0e61a0
MK
459 else while (argc--)
460 doacct(*argv++);
2efccdb1
BJ
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)
32d08ae3 473 PROCESSITERATE(allocwalk, tp, ub){
2efccdb1 474 for(j=0; j<NC; j++)
32d08ae3 475 if(tp->p.name[j] == '?')
2efccdb1 476 goto yes;
32d08ae3 477 if(tp->p.count != 1)
2efccdb1
BJ
478 continue;
479 yes:
32d08ae3 480 if(junkp == 0)
2efccdb1 481 junkp = enter("***other");
32d08ae3
BJ
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;
2efccdb1
BJ
489 }
490 if (sflg) {
491 signal(SIGINT, SIG_IGN);
7e0e61a0 492 if ((ff = fopen(usracct, "w")) != NULL) {
32d08ae3
BJ
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 }
2efccdb1 511 }
7e0e61a0 512 if ((ff = fopen(savacct, "w")) == NULL) {
2efccdb1
BJ
513 printf("Can't save\n");
514 exit(0);
515 }
32d08ae3
BJ
516 PROCESSITERATE(allocwalk, tp, ub)
517 fwrite((char *)&(tp->p), sizeof(struct process), 1, ff);
2efccdb1 518 fclose(ff);
32d08ae3 519 creat(sname, 0644);
2efccdb1
BJ
520 signal(SIGINT, SIG_DFL);
521 }
522/*
523 * sort and print
524 */
2efccdb1
BJ
525 if (mflg) {
526 printmoney();
527 exit(0);
528 }
2efccdb1
BJ
529 column(ncom, treal, tcpu, tsys, timem, tio);
530 printf("\n");
32d08ae3
BJ
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++;
2efccdb1 548 }
32d08ae3
BJ
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 */
2efccdb1
BJ
576}
577
578printmoney()
579{
580 register i;
2efccdb1 581 register char *cp;
32d08ae3
BJ
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",
651c99bb 593 up->us_cnt, up->us_ctime / 60,
32d08ae3 594 up->us_io,
7e0e61a0 595 up->us_imem / AHZ);
32d08ae3 596 }
2efccdb1
BJ
597 }
598 }
599}
600
601column(n, a, b, c, d, e)
651c99bb 602 double n, a, b, c, d, e;
2efccdb1
BJ
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)
7e0e61a0 613 col(n, 60*AHZ*(b/(b+c)), tcpu+tsys, "u/s");
2efccdb1
BJ
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)
7e0e61a0 620 printf("%8.1fre/cp", a/(b+c));
2efccdb1
BJ
621 if(dflg || !Dflg)
622 printf("%10.0favio", e/(n?n:1));
623 else
624 printf("%10.0ftio", e);
625 if (kflg || !Kflg)
181baa37 626 printf("%10.0fk", d/((b+c)!=0.0?(b+c):1.0));
2efccdb1 627 else
7e0e61a0 628 printf("%10.0fk*sec", d/AHZ);
2efccdb1
BJ
629}
630
631col(n, a, m, cp)
651c99bb
SL
632 double n, a, m;
633 char *cp;
2efccdb1
BJ
634{
635
636 if(jflg)
7e0e61a0
MK
637 printf("%11.2f%s", a/(n*(double)AHZ), cp); else
638 printf("%11.2f%s", a/(60.*(double)AHZ), cp);
2efccdb1
BJ
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{
2efccdb1
BJ
649 FILE *ff;
650 long x, y, z;
651 struct acct fbuf;
652 register char *cp;
653 register int c;
651c99bb
SL
654 register struct user *up;
655 register cell *tp;
32d08ae3
BJ
656#ifdef DEBUG
657 int nrecords = 0;
658#endif DEBUG
2efccdb1
BJ
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) {
32d08ae3
BJ
671#ifdef DEBUG
672 if (++nrecords % 1000 == 0)
673 printf("Input record from %s number %d\n",
674 f, nrecords);
675#endif DEBUG
181baa37
SL
676 for (cp = fbuf.ac_comm; *cp && cp < &fbuf.ac_comm[NC]; cp++)
677 if (!isascii(*cp) || iscntrl(*cp))
2efccdb1 678 *cp = '?';
181baa37
SL
679 if (cp == fbuf.ac_comm)
680 *cp++ = '?';
2efccdb1 681 if (fbuf.ac_flag&AFORK) {
181baa37 682 if (cp >= &fbuf.ac_comm[NC])
a4f89961 683 cp = &fbuf.ac_comm[NC-1];
181baa37 684 *cp++ = '*';
2efccdb1 685 }
181baa37
SL
686 if (cp < &fbuf.ac_comm[NC])
687 *cp = '\0';
2efccdb1 688 x = expand(fbuf.ac_utime) + expand(fbuf.ac_stime);
181baa37 689 y = pgtok((u_short)fbuf.ac_mem);
7e0e61a0 690 z = expand(fbuf.ac_io) / AHZ;
2efccdb1 691 if (uflg) {
7e0e61a0
MK
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);
2efccdb1
BJ
694 continue;
695 }
32d08ae3
BJ
696 up = finduser(fbuf.ac_uid);
697 if (up == 0)
698 continue; /* preposterous user id */
699 up->us_cnt++;
7e0e61a0 700 up->us_ctime += x/(double)AHZ;
32d08ae3
BJ
701 up->us_imem += x * y;
702 up->us_io += z;
2efccdb1 703 ncom += 1.0;
32d08ae3
BJ
704
705 tp = enter(fbuf.ac_comm);
706 tp->p.imem += x * y;
2efccdb1 707 timem += x * y;
32d08ae3 708 tp->p.count++;
651c99bb 709 x = expand(fbuf.ac_etime);
32d08ae3 710 tp->p.realt += x;
2efccdb1
BJ
711 treal += x;
712 x = expand(fbuf.ac_utime);
32d08ae3 713 tp->p.cput += x;
2efccdb1
BJ
714 tcpu += x;
715 x = expand(fbuf.ac_stime);
32d08ae3 716 tp->p.syst += x;
2efccdb1 717 tsys += x;
32d08ae3 718 tp->p.io += z;
2efccdb1
BJ
719 tio += z;
720 }
721 fclose(ff);
722}
723
32d08ae3
BJ
724/*
725 * Generalized cell compare routine, to cast out users
726 */
727cellcmp(p1, p2)
651c99bb 728 cell *p1, *p2;
32d08ae3
BJ
729{
730 if (ISPROCESS(p1)){
731 if (ISPROCESS(p2))
747bddc6 732 return((*cmp)(p1, p2));
32d08ae3
BJ
733 return(-1);
734 }
735 if (ISPROCESS(p2))
736 return(1);
737 return(0);
738}
651c99bb 739
2efccdb1 740ncmp(p1, p2)
651c99bb 741 cell *p1, *p2;
2efccdb1
BJ
742{
743
32d08ae3 744 if(p1->p.count == p2->p.count)
2efccdb1
BJ
745 return(tcmp(p1, p2));
746 if(rflg)
32d08ae3
BJ
747 return(p1->p.count - p2->p.count);
748 return(p2->p.count - p1->p.count);
2efccdb1
BJ
749}
750
fb3abc15 751bflgcmp(p1, p2)
651c99bb 752 cell *p1, *p2;
2efccdb1
BJ
753{
754 double f1, f2;
755 double sum();
756
32d08ae3
BJ
757 f1 = sum(p1)/p1->p.count;
758 f2 = sum(p2)/p2->p.count;
2efccdb1
BJ
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)
651c99bb 773 cell *p1, *p2;
2efccdb1
BJ
774{
775
32d08ae3 776 if (p1->p.imem < p2->p.imem) {
2efccdb1
BJ
777 if(rflg)
778 return(-1);
779 return(1);
780 }
32d08ae3 781 if (p1->p.imem > p2->p.imem) {
2efccdb1
BJ
782 if(rflg)
783 return(1);
784 return(-1);
785 }
786 return(0);
787}
788
789kcmp(p1, p2)
651c99bb 790 cell *p1, *p2;
2efccdb1
BJ
791{
792 double a1, a2;
793
32d08ae3
BJ
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);
2efccdb1
BJ
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)
651c99bb 810 cell *p1, *p2;
2efccdb1
BJ
811{
812 double a1, a2;
813
32d08ae3
BJ
814 a1 = p1->p.io / (p1->p.count?p1->p.count:1);
815 a2 = p2->p.io / (p2->p.count?p2->p.count:1);
2efccdb1
BJ
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)
651c99bb 830 cell *p1, *p2;
2efccdb1
BJ
831{
832
32d08ae3 833 if (p1->p.io < p2->p.io) {
2efccdb1
BJ
834 if(rflg)
835 return(-1);
836 return(1);
837 }
32d08ae3 838 if (p1->p.io > p2->p.io) {
2efccdb1
BJ
839 if(rflg)
840 return(1);
841 return(-1);
842 }
843 return(0);
844}
845
846tcmp(p1, p2)
651c99bb 847 cell *p1, *p2;
2efccdb1
BJ
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)
651c99bb 868 cell *p;
2efccdb1
BJ
869{
870
32d08ae3 871 if(p->p.name[0] == 0)
2efccdb1 872 return(0.0);
32d08ae3 873 return( p->p.cput + p->p.syst);
2efccdb1
BJ
874}
875
876init()
877{
651c99bb
SL
878 struct user userbuf;
879 struct process tbuf;
880 register cell *tp;
881 register struct user *up;
882 int uid;
2efccdb1
BJ
883 FILE *f;
884
7e0e61a0 885 if ((f = fopen(savacct, "r")) == NULL)
2efccdb1 886 goto gshm;
32d08ae3
BJ
887 while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) {
888 tp = enter(tbuf.name);
2efccdb1 889 ncom += tbuf.count;
32d08ae3 890 tp->p.count = tbuf.count;
2efccdb1 891 treal += tbuf.realt;
32d08ae3 892 tp->p.realt = tbuf.realt;
2efccdb1 893 tcpu += tbuf.cput;
32d08ae3 894 tp->p.cput = tbuf.cput;
2efccdb1 895 tsys += tbuf.syst;
32d08ae3 896 tp->p.syst = tbuf.syst;
2efccdb1 897 tio += tbuf.io;
32d08ae3 898 tp->p.io = tbuf.io;
2efccdb1 899 timem += tbuf.imem;
32d08ae3 900 tp->p.imem = tbuf.imem;
2efccdb1
BJ
901 }
902 fclose(f);
903 gshm:
7e0e61a0 904 if ((f = fopen(usracct, "r")) == NULL)
2efccdb1 905 return;
32d08ae3
BJ
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 }
2efccdb1
BJ
916 fclose(f);
917}
918
2efccdb1
BJ
919strip()
920{
651c99bb
SL
921 int c;
922 register struct allocbox *allocwalk;
923 register cell *tp, *ub, *junkp;
32d08ae3
BJ
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;
2efccdb1 941 }
32d08ae3
BJ
942 if (!fflg)
943 while (c && c!='\n')
944 c = getchar();
2efccdb1
BJ
945 }
946 }
947}
948
949time_t
950expand(t)
651c99bb 951 unsigned t;
2efccdb1
BJ
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
651c99bb
SL
964static char UserKey[NAMELG + 2];
965
966char *
967makekey(uid)
968 int uid;
32d08ae3 969{
9bd38ba8 970 (void)sprintf(UserKey+1, "%04x", uid);
32d08ae3
BJ
971 UserKey[0] = USERKEY;
972 return(UserKey);
973}
2efccdb1 974
651c99bb
SL
975struct user *
976wasuser(uid)
977 int uid;
32d08ae3 978{
651c99bb
SL
979 struct user *tp;
980
32d08ae3
BJ
981 htabinstall = 0;
982 tp = finduser(uid);
983 htabinstall = 1;
984 return(tp);
985}
651c99bb 986
32d08ae3
BJ
987/*
988 * Only call this if you really want to insert it in the table!
989 */
651c99bb
SL
990struct user *
991finduser(uid)
992 int uid;
32d08ae3 993{
651c99bb 994
32d08ae3
BJ
995 if (uid > maxuser){
996 fprintf(stderr, "Preposterous user id, %d: ignored\n", uid);
997 return(0);
32d08ae3 998 }
651c99bb 999 return((struct user*)enter(makekey(uid)));
32d08ae3 1000}
2efccdb1 1001
32d08ae3
BJ
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()
2efccdb1 1007{
651c99bb 1008 register struct user *tp;
2efccdb1 1009 register struct passwd *pw;
2efccdb1
BJ
1010 struct passwd *getpwent();
1011
32d08ae3
BJ
1012 setpwent();
1013 while (pw = getpwent()){
7e0e61a0
MK
1014 /* use first name in passwd file for duplicate uid's */
1015 if ((tp = wasuser(pw->pw_uid)) != 0 && !isalpha(tp->us_name[0]))
32d08ae3 1016 strncpy(tp->us_name, pw->pw_name, NAMELG);
2efccdb1 1017 }
2efccdb1 1018 endpwent();
2efccdb1 1019}
32d08ae3 1020
651c99bb
SL
1021int
1022getmaxuid()
32d08ae3 1023{
651c99bb
SL
1024 register struct user *tp;
1025 register struct passwd *pw;
1026 struct passwd *getpwent();
1027 int maxuid = -1;
32d08ae3
BJ
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)
651c99bb
SL
1050cell *
1051taballoc()
32d08ae3 1052{
651c99bb 1053
32d08ae3
BJ
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{
651c99bb 1076 register struct hashdallop *new;
32d08ae3 1077#ifdef DEBUG
651c99bb
SL
1078 static int ntables = 0;
1079
32d08ae3
BJ
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 */
651c99bb
SL
1099cell *
1100enter(name)
1101 char *name;
32d08ae3 1102{
651c99bb
SL
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;
32d08ae3
BJ
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);
651c99bb 1165}