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