Commit | Line | Data |
---|---|---|
651c99bb | 1 | #ifndef lint |
f76fd03c | 2 | static 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 |
33 | struct acct acctbuf; |
34 | int lflg; | |
35 | int cflg; | |
36 | int Dflg; | |
37 | int dflg; | |
38 | int iflg; | |
39 | int jflg; | |
40 | int Kflg; | |
41 | int kflg; | |
42 | int nflg; | |
43 | int aflg; | |
44 | int rflg; | |
45 | int oflg; | |
46 | int tflg; | |
47 | int vflg; | |
32d08ae3 | 48 | int fflg; |
2efccdb1 | 49 | int uflg; |
32d08ae3 | 50 | int thres; |
2efccdb1 BJ |
51 | int sflg; |
52 | int bflg; | |
53 | int mflg; | |
54 | ||
32d08ae3 BJ |
55 | struct utmp utmp; |
56 | #define NAMELG (sizeof(utmp.ut_name)+1) | |
57 | ||
58 | struct Olduser{ | |
59 | int Us_cnt; | |
60 | double Us_ctime; | |
61 | double Us_io; | |
62 | double Us_imem; | |
63 | }; | |
64 | ||
2efccdb1 | 65 | struct 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 | |
82 | int maxuser; /* highest uid from /etc/passwd, + 10 for slop*/ | |
83 | ||
84 | struct 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 | ||
94 | union Tab{ | |
95 | struct process p; | |
96 | struct user u; | |
97 | }; | |
98 | ||
99 | typedef union Tab cell; | |
100 | ||
101 | int (*cmp)(); /* compares 2 cells; set to appropriate func */ | |
102 | cell *enter(); | |
103 | struct user *finduser(); | |
104 | struct 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 | |
149 | struct allocbox{ | |
150 | struct allocbox *nextalloc; | |
151 | cell tabslots[TABDALLOP]; | |
152 | }; | |
153 | ||
154 | struct allocbox *allochead; /*head of chunk list*/ | |
155 | struct allocbox *alloctail; /*tail*/ | |
156 | struct allocbox *newbox; /*for creating a new chunk*/ | |
157 | cell *nexttab; /*next table element that is free*/ | |
158 | int tabsleft; /*slots left in current chunk*/ | |
159 | int 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 | */ | |
207 | struct chunkdesc { | |
208 | cell *chunk_tp; | |
209 | int chunk_n; | |
210 | }; | |
211 | ||
212 | /* | |
213 | * Hash table segments and manager | |
214 | */ | |
215 | #define NHASH 1103 | |
216 | struct hashdallop { | |
217 | int h_nused; | |
218 | struct hashdallop *h_next; | |
219 | cell *h_tab[NHASH]; | |
220 | }; | |
221 | struct hashdallop *htab; /* head of the list */ | |
222 | int htabinstall; /* install the symbol */ | |
2efccdb1 BJ |
223 | |
224 | double treal; | |
225 | double tcpu; | |
226 | double tsys; | |
227 | double tio; | |
228 | double timem; | |
32d08ae3 | 229 | cell *junkp; |
2efccdb1 BJ |
230 | char *sname; |
231 | double ncom; | |
232 | time_t expand(); | |
233 | char *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 |
260 | char *usracct = USRACCT; |
261 | char *savacct = SAVACCT; | |
262 | ||
32d08ae3 BJ |
263 | int cellcmp(); |
264 | cell *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 | */ | |
272 | int thres = 0; | |
273 | int htabinstall = 1; | |
274 | int maxuser = -1; | |
275 | int (*cmp)(); | |
276 | ||
181baa37 SL |
277 | /* we assume pagesize is at least 1k */ |
278 | int pgdiv; | |
279 | #define pgtok(x) ((x) / pgdiv) | |
280 | ||
651c99bb SL |
281 | extern tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp(); |
282 | extern double sum(); | |
283 | ||
2efccdb1 | 284 | main(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 | ||
423 | doUS: | |
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 | ||
565 | printmoney() | |
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 | ||
588 | column(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 | ||
618 | col(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 | ||
633 | doacct(f) | |
634 | char *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 | */ | |
714 | cellcmp(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 | 727 | ncmp(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 | ||
738 | bcmp(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 | ||
759 | Kcmp(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 | ||
776 | kcmp(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 | ||
796 | dcmp(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 | ||
816 | Dcmp(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 | ||
833 | tcmp(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 | ||
854 | double 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 | ||
863 | init() | |
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 |
906 | strip() |
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 | ||
936 | time_t | |
937 | expand(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 |
951 | static char UserKey[NAMELG + 2]; |
952 | ||
953 | char * | |
954 | makekey(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 |
962 | struct user * |
963 | wasuser(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 |
977 | struct user * |
978 | finduser(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 | */ | |
993 | getnames() | |
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 |
1008 | int |
1009 | getmaxuid() | |
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 | ||
1025 | tabinit() | |
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 |
1037 | cell * |
1038 | taballoc() | |
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 | ||
1061 | htaballoc() | |
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 |
1086 | cell * |
1087 | enter(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 | } |