"Fossies" - the Fresh Open Source Software Archive 
Member "atop-2.8.1/acctproc.c" (7 Jan 2023, 25806 Bytes) of package /linux/misc/atop-2.8.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "acctproc.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
2.7.1_vs_2.8.0.
1 /*
2 ** ATOP - System & Process Monitor
3 **
4 ** The program 'atop' offers the possibility to view the activity of
5 ** the system on system-level as well as process-level.
6 **
7 ** This source-file contains functions to manipulate with process-accounting
8 ** features (switch it on/off and read the process-accounting records).
9 ** ================================================================
10 ** Author: Gerlof Langeveld
11 ** E-mail: gerlof.langeveld@atoptool.nl
12 ** Date: November 1996
13 ** LINUX-port: June 2000
14 **
15 ** This program is free software; you can redistribute it and/or modify it
16 ** under the terms of the GNU General Public License as published by the
17 ** Free Software Foundation; either version 2, or (at your option) any
18 ** later version.
19 **
20 ** This program is distributed in the hope that it will be useful, but
21 ** WITHOUT ANY WARRANTY; without even the implied warranty of
22 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23 ** See the GNU General Public License for more details.
24 */
25
26 #define _FILE_OFFSET_BITS 64
27
28 #define _GNU_SOURCE
29 #include <sys/types.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <sys/time.h>
37 #include <sys/stat.h>
38 #include <sys/wait.h>
39 #include <sys/ipc.h>
40 #include <sys/sem.h>
41
42 #include "atop.h"
43 #include "photoproc.h"
44 #include "acctproc.h"
45 #include "atopacctd.h"
46
47 #define ACCTDIR "/var/cache/atop.d"
48 #define ACCTFILE "atop.acct"
49 #define ACCTENV "ATOPACCT"
50
51 static char acctatop; /* boolean: atop's own accounting busy ? */
52 static off_t acctsize; /* previous size of account file */
53 static int acctrecsz; /* size of account record */
54 static int acctversion; /* version of account record layout */
55 static int acctfd = -1; /* fd of account file (-1 = not open) */
56
57 static long curshadowseq; /* current shadow file sequence number */
58 static long maxshadowrec; /* number of records per shadow file */
59
60 static char *pacctdir = PACCTDIR;
61
62 static count_t acctexp (comp_t ct);
63 static int acctvers(int);
64 static void acctrestarttrial();
65 static void switchshadow(void);
66
67 /*
68 ** possible process accounting files used by (ps)acct package
69 */
70 struct pacctadm {
71 char *name;
72 struct stat stat;
73 } pacctadm[] = {
74 { "/var/log/pacct", {0, }, },
75 { "/var/account/pacct", {0, }, },
76 { "/var/log/account/pacct", {0, }, }
77 };
78
79 struct pacctadm *pacctcur; // pointer to entry in use
80
81 /*
82 ** Semaphore-handling
83 **
84 ** A semaphore-group with two semaphores is created
85 **
86 ** 0 - Binary semaphore (mutex) to get access to active atop-counter
87 **
88 ** 1 - Active atop-counter (inverted).
89 ** This semaphore is initialized at some high value and is
90 ** decremented by every atop-incarnation which uses the private
91 ** accounting-file and incremented as soon as such atop stops again.
92 ** If an atop-incarnation stops and it appears to be the last one
93 ** using the private accounting-file, accounting is stopped
94 ** and the file removed
95 ** (Yes, I know: it's not proper usage of the semaphore-principle).
96 */
97 #define ATOPACCTKEY 3121959
98 #define ATOPACCTTOT 100
99
100 struct sembuf semclaim = {0, -1, SEM_UNDO},
101 semrelse = {0, +1, SEM_UNDO},
102 semdecre = {1, -1, SEM_UNDO},
103 semincre = {1, +1, SEM_UNDO};
104
105 struct sembuf semreglock[] = {{0, -1, SEM_UNDO|IPC_NOWAIT},
106 {1, -1, SEM_UNDO|IPC_NOWAIT}},
107 semunlock = {1, +1, SEM_UNDO};
108
109 /*
110 ** switch on the process-accounting mechanism
111 **
112 ** return value:
113 ** 0 - activated (success)
114 ** 1 - not activated: unreadable accounting file
115 ** 2 - not activated: empty environment variable ATOPACCT
116 ** 3 - not activated: no access to semaphore group
117 ** 4 - not activated: impossible to create own accounting file
118 ** 5 - not activated: no root privileges
119 */
120 int
121 acctswon(void)
122 {
123 int i, j, sematopid, sempacctpubid;
124 static ushort vals[] = {1, ATOPACCTTOT};
125 union {ushort *array;} arg = {vals};
126 struct stat statbuf;
127 char *ep;
128
129 /*
130 ** when a particular environment variable is present, atop should
131 ** use a specific accounting-file (as defined by the environment
132 ** variable) or should use no process accounting at all (when
133 ** contents of environment variable is empty)
134 */
135 if ( (ep = getenv(ACCTENV)) )
136 {
137 /*
138 ** environment variable exists
139 */
140 if (*ep)
141 {
142 /*
143 ** open active account file with the specified name
144 */
145 if (! droprootprivs() )
146 mcleanstop(42, "failed to drop root privs\n");
147
148 if ( (acctfd = open(ep, O_RDONLY) ) == -1)
149 return 1;
150
151 if ( !acctvers(acctfd) )
152 {
153 (void) close(acctfd);
154 acctfd = -1;
155 return 1;
156 }
157
158 supportflags |= ACCTACTIVE;
159 return 0;
160 }
161 else
162 {
163 /*
164 ** no contents
165 */
166 return 2;
167 }
168 }
169
170 /*
171 ** when the atopacctd daemon is active on this system,
172 ** it should be the preferred way to read the accounting records
173 */
174 if ( (sempacctpubid = semget(PACCTPUBKEY, 2, 0)) != -1)
175 {
176 FILE *cfp;
177 char shadowpath[128];
178 struct flock flock;
179 struct timespec maxsemwait = {3, 0};
180
181 if (! droprootprivs() )
182 mcleanstop(42, "failed to drop root privs\n");
183
184 if (semtimedop(sempacctpubid, semreglock, 2, &maxsemwait) == -1)
185 {
186 acctfd = -1;
187 regainrootprivs();
188 return 3;
189 }
190
191 snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s",
192 pacctdir, PACCTSHADOWD, PACCTSHADOWC);
193
194 if ( (cfp = fopen(shadowpath, "r")) )
195 {
196 if (fscanf(cfp, "%ld/%ld",
197 &curshadowseq, &maxshadowrec) == 2)
198 {
199 fclose(cfp);
200
201 snprintf(shadowpath, sizeof shadowpath,
202 PACCTSHADOWF, pacctdir,
203 PACCTSHADOWD, curshadowseq);
204
205 if ( (acctfd = open(shadowpath, O_RDONLY))!=-1)
206 {
207 if ( !acctvers(acctfd) )
208 {
209 int maxcnt = 40;
210
211 if ( fork() == 0 )
212 exit(0);
213
214 (void) wait((int *) 0);
215
216 while ( !acctvers(acctfd) &&
217 --maxcnt)
218 usleep(50000);
219
220 if (!acctversion)
221 {
222 (void) close(acctfd);
223 acctfd = -1;
224
225 semop(sempacctpubid,
226 &semrelse, 1);
227 semop(sempacctpubid,
228 &semunlock, 1);
229
230 regainrootprivs();
231 return 1;
232 }
233 }
234
235 /*
236 ** set read lock on current shadow file
237 */
238 flock.l_type = F_RDLCK;
239 flock.l_whence = SEEK_SET;
240 flock.l_start = 0;
241 flock.l_len = 1;
242
243 if ( fcntl(acctfd, F_SETLK, &flock)
244 != -1)
245 {
246 supportflags |= ACCTACTIVE;
247 regainrootprivs();
248 semop(sempacctpubid,
249 &semunlock, 1);
250 return 0;
251 }
252
253 (void) close(acctfd);
254 }
255 else
256 {
257 perror("open shadowpath");
258 abort();
259 }
260 }
261 else
262 {
263 fprintf(stderr,
264 "fscanf failed on shadow currency\n");
265 fclose(cfp);
266 maxshadowrec = 0;
267 }
268 }
269
270 (void) semop(sempacctpubid, &semrelse, 1);
271 (void) semop(sempacctpubid, &semunlock, 1);
272 }
273
274 /*
275 ** check if process accounting is already switched on
276 ** for one of the standard accounting-files; in that case we
277 ** open this file and get our info from here ....
278 */
279 for (i=0, j=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++)
280 {
281 if ( stat(pacctadm[i].name, &pacctadm[i].stat) == 0)
282 j++;
283 }
284
285 if (j)
286 {
287 /*
288 ** at least one file is present; check if it is really in use
289 ** at this moment for accounting by forking a child-process
290 ** which immediately finishes to force a new accounting-record
291 */
292 if ( fork() == 0 )
293 exit(0); /* let the child finish */
294
295 (void) wait((int *) 0); /* let the parent wait */
296
297 for (i=0; i < sizeof pacctadm/sizeof pacctadm[0]; i++)
298 {
299 if ( stat(pacctadm[i].name, &statbuf) == 0)
300 {
301 /*
302 ** accounting-file has grown?
303 */
304 if (statbuf.st_size > pacctadm[i].stat.st_size)
305 {
306 /*
307 ** open active account file
308 */
309 if ( (acctfd = open(pacctadm[i].name,
310 O_RDONLY) ) == -1)
311 return 1;
312
313 if ( !acctvers(acctfd) )
314 {
315 (void) close(acctfd);
316 acctfd = -1;
317 return 1;
318 }
319
320 supportflags |= ACCTACTIVE;
321
322 pacctcur = &pacctadm[i]; // register current
323
324 return 0;
325 }
326 }
327 }
328 }
329
330 /*
331 ** process-accounting is not yet switched on in a standard way;
332 ** check if another atop has switched on accounting already
333 **
334 ** first try to create a semaphore group exclusively; if this succeeds,
335 ** this is the first atop-incarnation since boot and the semaphore group
336 ** should be initialized`
337 */
338 if ( (sematopid = semget(ATOPACCTKEY, 2, 0600|IPC_CREAT|IPC_EXCL)) >= 0)
339 (void) semctl(sematopid, 0, SETALL, arg);
340 else
341 sematopid = semget(ATOPACCTKEY, 0, 0);
342
343 /*
344 ** check if we got access to the semaphore group
345 */
346 if (sematopid == -1)
347 return 3;
348
349 /*
350 ** the semaphore group is opened now; claim exclusive rights
351 */
352 (void) semop(sematopid, &semclaim, 1);
353
354 /*
355 ** are we the first to use the accounting-mechanism ?
356 */
357 if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT)
358 {
359 /*
360 ** create a new separate directory below /tmp
361 ** for the accounting file;
362 ** if this directory exists (e.g. previous atop-run killed)
363 ** it will be cleaned and newly created
364 */
365 if ( mkdir(ACCTDIR, 0700) == -1)
366 {
367 if (errno == EEXIST)
368 {
369 (void) acct(0);
370 (void) unlink(ACCTDIR "/" ACCTFILE);
371 (void) rmdir(ACCTDIR);
372 }
373
374 if ( mkdir(ACCTDIR, 0700) == -1)
375 {
376 /*
377 ** persistent failure
378 */
379 (void) semop(sematopid, &semrelse, 1);
380 return 4;
381 }
382 }
383
384 /*
385 ** create accounting file in new directory
386 */
387 (void) close( creat(ACCTDIR "/" ACCTFILE, 0600) );
388
389 /*
390 ** switch on accounting
391 */
392 if ( acct(ACCTDIR "/" ACCTFILE) < 0)
393 {
394 (void) unlink(ACCTDIR "/" ACCTFILE);
395 (void) rmdir(ACCTDIR);
396 (void) semop(sematopid, &semrelse, 1);
397
398 return 5;
399 }
400 }
401
402 /*
403 ** accounting is switched on now;
404 ** open accounting-file
405 */
406 if ( (acctfd = open(ACCTDIR "/" ACCTFILE, O_RDONLY) ) < 0)
407 {
408 (void) acct(0);
409 (void) unlink(ACCTDIR "/" ACCTFILE);
410 (void) rmdir(ACCTDIR);
411
412 (void) semop(sematopid, &semrelse, 1);
413 return 1;
414 }
415
416 /*
417 ** accounting successfully switched on
418 */
419 (void) semop(sematopid, &semdecre, 1);
420 (void) semop(sematopid, &semrelse, 1);
421
422 acctatop = 1;
423
424 /*
425 ** determine version of accounting-record
426 */
427 if (fstat(acctfd, &statbuf) == -1)
428 {
429 (void) acct(0);
430 (void) close(acctfd);
431 (void) unlink(ACCTDIR "/" ACCTFILE);
432 (void) rmdir(ACCTDIR);
433
434 acctfd = -1;
435 return 1;
436
437 }
438
439 if (statbuf.st_size == 0) /* no acct record written yet */
440 {
441 /*
442 ** let's write one record
443 */
444 if ( fork() == 0 )
445 exit(0); /* let the child finish */
446
447 (void) wait((int *) 0); /* let the parent wait */
448 }
449
450 if ( !acctvers(acctfd) )
451 {
452 (void) acct(0);
453 (void) close(acctfd);
454 (void) unlink(ACCTDIR "/" ACCTFILE);
455 (void) rmdir(ACCTDIR);
456
457 acctfd = -1;
458 return 1;
459
460 }
461
462 supportflags |= ACCTACTIVE;
463 return 0;
464 }
465
466 /*
467 ** determine the version of the accounting-record layout/length
468 ** and reposition the seek-pointer to the end of the accounting file
469 */
470 static int
471 acctvers(int fd)
472 {
473 struct acct tmprec;
474
475 /*
476 ** read first record from accounting file to verify
477 ** the second byte (always contains version number)
478 */
479 if ( read(fd, &tmprec, sizeof tmprec) < sizeof tmprec)
480 return 0;
481
482 switch (tmprec.ac_version & 0x0f)
483 {
484 case 2:
485 acctrecsz = sizeof(struct acct);
486 acctversion = 2;
487 break;
488
489 case 3:
490 acctrecsz = sizeof(struct acct_v3);
491 acctversion = 3;
492 break;
493
494 default:
495 mcleanstop(8, "Unknown format of process accounting file\n");
496 }
497
498 /*
499 ** accounting successfully switched on
500 */
501 acctsize = acctprocnt() * acctrecsz;
502
503 /*
504 ** reposition to actual file-size
505 */
506 (void) lseek(fd, acctsize, SEEK_SET);
507
508 return 1;
509 }
510
511 /*
512 ** switch off the process-accounting mechanism
513 */
514 void
515 acctswoff(void)
516 {
517 int sematopid;
518 struct stat before, after;
519
520 /*
521 ** if accounting not supported, skip call
522 */
523 if (acctfd == -1)
524 return;
525
526 /*
527 ** our private accounting-file in use?
528 */
529 if (acctatop)
530 {
531 acctatop = 0;
532
533 /*
534 ** claim the semaphore to get exclusive rights for
535 ** the accounting-manipulation
536 */
537 sematopid = semget(ATOPACCTKEY, 0, 0);
538
539 (void) semop(sematopid, &semclaim, 1);
540 (void) semop(sematopid, &semincre, 1);
541
542 /*
543 ** check if we were the last user of accounting
544 */
545 if (semctl(sematopid, 1, GETVAL, 0) == ATOPACCTTOT)
546 {
547 /*
548 ** switch off private accounting
549 **
550 ** verify if private accounting is still in use to
551 ** avoid that we switch off process accounting that
552 ** has been activated manually in the meantime
553 */
554 (void) fstat(acctfd, &before);
555
556 if ( fork() == 0 ) /* create a child and */
557 exit(0); /* let the child finish */
558
559 (void) wait((int *) 0); /* let the parent wait */
560
561 (void) fstat(acctfd, &after);
562
563 if (after.st_size > before.st_size)
564 {
565 /*
566 ** remove the directory and file
567 */
568 regainrootprivs(); /* get root privs again */
569
570 (void) acct(0);
571 (void) unlink(ACCTDIR "/" ACCTFILE);
572 (void) rmdir(ACCTDIR);
573
574 if (! droprootprivs() )
575 mcleanstop(42,
576 "failed to drop root privs\n");
577 }
578 }
579
580 (void) semop(sematopid, &semrelse, 1);
581 }
582
583 /*
584 ** anyhow close the accounting-file again
585 */
586 (void) close(acctfd); /* close account file again */
587 acctfd = -1;
588
589 supportflags &= ~ACCTACTIVE;
590 }
591
592 /*
593 ** get the number of exited processes written
594 ** in the process-account file since the previous sample
595 */
596 unsigned long
597 acctprocnt(void)
598 {
599 struct stat statacc;
600
601 /*
602 ** if accounting not supported, skip call
603 */
604 if (acctfd == -1)
605 return 0;
606
607 /*
608 ** determine the current size of the accounting file
609 */
610 if (fstat(acctfd, &statacc) == -1)
611 return 0;
612
613 /*
614 ** handle atopacctd-based process accounting on bases of
615 ** fixed-chunk shadow files
616 */
617 if (maxshadowrec)
618 {
619 unsigned long numrecs = 0;
620 long newseq;
621 char shadowpath[128];
622 FILE *cfp;
623
624 /*
625 ** verify how many new processes are added to the current
626 ** shadow file
627 */
628 numrecs = (statacc.st_size - acctsize) / acctrecsz;
629
630 /*
631 ** verify if subsequent shadow files are involved
632 ** (i.e. if the current file is full)
633 */
634 if (statacc.st_size / acctrecsz < maxshadowrec)
635 return numrecs;
636
637 /*
638 ** more shadow files available
639 ** get current shadow file
640 */
641 snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s",
642 pacctdir, PACCTSHADOWD, PACCTSHADOWC);
643
644 if ( (cfp = fopen(shadowpath, "r")) )
645 {
646 if (fscanf(cfp, "%ld", &newseq) == 1)
647 {
648 fclose(cfp);
649 }
650 else
651 {
652 fclose(cfp);
653 return numrecs;
654 }
655 }
656 else
657 {
658 return numrecs;
659 }
660
661 if (newseq == curshadowseq)
662 return numrecs;
663
664 snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
665 pacctdir, PACCTSHADOWD, newseq);
666
667 /*
668 ** determine the size of newest shadow file
669 */
670 if (stat(shadowpath, &statacc) == -1)
671 return numrecs;
672
673 numrecs += ((newseq - curshadowseq - 1) * maxshadowrec) +
674 (statacc.st_size / acctrecsz);
675
676 return numrecs;
677 }
678 else
679 /*
680 ** classic process accounting on bases of directly opened
681 ** process accounting file
682 */
683 {
684 /*
685 ** accounting reset?
686 */
687 if (acctsize > statacc.st_size)
688 {
689 /*
690 ** reposition to start of file
691 */
692 (void) lseek(acctfd, 0, SEEK_SET);
693 acctsize = 0;
694 }
695
696 /*
697 ** using account file managed by (ps)acct package?
698 */
699 if (pacctcur)
700 {
701 /*
702 ** check inode of the current file and compare this
703 ** with the inode of the opened file; if not equal,
704 ** a file rotation has taken place and the size of
705 ** the new file has to be added
706 */
707 if ( stat(pacctcur->name, &(pacctcur->stat)) == 0)
708 {
709 if (statacc.st_ino != pacctcur->stat.st_ino)
710 {
711 return (statacc.st_size - acctsize +
712 pacctcur->stat.st_size) /
713 acctrecsz;
714 }
715 }
716 }
717
718 return (statacc.st_size - acctsize) / acctrecsz;
719 }
720 }
721
722 /*
723 ** reposition the seek offset in the process accounting file to skip
724 ** processes that have not been read
725 */
726 void
727 acctrepos(unsigned int noverflow)
728 {
729 /*
730 ** if accounting not supported, skip call
731 */
732 if (acctfd == -1)
733 return;
734
735 if (maxshadowrec)
736 {
737 int i;
738 off_t virtoffset = acctsize + noverflow * acctrecsz;
739 off_t maxshadowsz = maxshadowrec * acctrecsz;
740 long switches = virtoffset / maxshadowsz;
741 acctsize = virtoffset % maxshadowsz;
742
743 for (i=0; i < switches; i++)
744 switchshadow();
745
746 (void) lseek(acctfd, acctsize, SEEK_SET);
747 }
748 else
749 {
750 /*
751 ** just reposition to skip superfluous records
752 */
753 (void) lseek(acctfd, noverflow * acctrecsz, SEEK_CUR);
754 acctsize += noverflow * acctrecsz;
755
756 /*
757 ** when the new seek pointer is beyond the current file size
758 ** and reading from a process accounting file written by
759 ** the (ps)acct package, a logrotation might have taken place
760 */
761 if (pacctcur)
762 {
763 struct stat statacc;
764
765 /*
766 ** determine the size of the current accounting file
767 */
768 if (fstat(acctfd, &statacc) == -1)
769 return;
770
771 /*
772 ** seek pointer beyond file size and rotated to
773 ** new account file?
774 */
775 if (acctsize > statacc.st_size &&
776 statacc.st_ino != pacctcur->stat.st_ino)
777 {
778 /*
779 ** - close old file
780 ** - open new file
781 ** - adapt acctsize to actual offset in new file
782 ** and seek to that offset
783 */
784 (void) close(acctfd);
785
786 if ( (acctfd = open(pacctcur->name,
787 O_RDONLY) ) == -1)
788 return; // open failed
789
790 acctsize = acctsize - statacc.st_size;
791 (void) lseek(acctfd, acctsize, SEEK_SET);
792
793 if (fstat(acctfd, &statacc) == -1)
794 return; // no new inode info
795 }
796 }
797 }
798 }
799
800
801 /*
802 ** read the process records from the process accounting file,
803 ** that are written since the previous cycle
804 */
805 unsigned long
806 acctphotoproc(struct tstat *accproc, int nrprocs)
807 {
808 register int nrexit;
809 register struct tstat *api;
810 struct acct acctrec;
811 struct acct_v3 acctrec_v3;
812 struct stat statacc;
813
814 /*
815 ** if accounting not supported, skip call
816 */
817 if (acctfd == -1)
818 return 0;
819
820 /*
821 ** determine the size of the (current) account file
822 */
823 if (fstat(acctfd, &statacc) == -1)
824 return 0;
825
826 /*
827 ** check all exited processes in accounting file
828 */
829 for (nrexit=0, api=accproc; nrexit < nrprocs;
830 nrexit++, api++, acctsize += acctrecsz)
831 {
832 /*
833 ** in case of shadow accounting files, we might have to
834 ** switch from the current accounting file to the next
835 */
836 if (maxshadowrec && acctsize >= statacc.st_size)
837 {
838 switchshadow();
839
840 /*
841 ** determine the size of the new (current) account file
842 ** and initialize the current offset within that file
843 */
844 if (fstat(acctfd, &statacc) == -1)
845 return 0;
846
847 acctsize = 0;
848 }
849
850 /*
851 ** in case of account file managed by (ps)acct package,
852 ** be aware that a switch to a newer logfile might have
853 ** to be done
854 */
855 if (pacctcur && acctsize >= statacc.st_size)
856 {
857 /*
858 ** check inode of the current file and compare this
859 ** with the inode of the opened file; if not equal,
860 ** a file rotation has taken place and the file
861 ** has to be reopened
862 */
863 if ( stat(pacctcur->name, &(pacctcur->stat)) == 0)
864 {
865 if (statacc.st_ino != pacctcur->stat.st_ino)
866 {
867 (void) close(acctfd);
868
869 if ( (acctfd = open(pacctcur->name,
870 O_RDONLY) ) == -1)
871 return 0; // open failed
872
873 if (fstat(acctfd, &statacc) == -1)
874 return 0; // no new inode info
875
876 acctsize = 0; // reset size new file
877 }
878 }
879 }
880
881 /*
882 ** read the next record
883 */
884 switch (acctversion)
885 {
886 case 2:
887 if ( read(acctfd, &acctrec, acctrecsz) < acctrecsz )
888 break; /* unexpected end of account file */
889
890 /*
891 ** fill process info from accounting-record
892 */
893 api->gen.state = 'E';
894 api->gen.nthr = 1;
895 api->gen.isproc = 1;
896 api->gen.pid = 0;
897 api->gen.tgid = 0;
898 api->gen.ppid = 0;
899 api->gen.excode = acctrec.ac_exitcode;
900 api->gen.ruid = acctrec.ac_uid16;
901 api->gen.rgid = acctrec.ac_gid16;
902 api->gen.btime = acctrec.ac_btime;
903 api->gen.elaps = acctrec.ac_etime;
904 api->cpu.stime = acctexp(acctrec.ac_stime);
905 api->cpu.utime = acctexp(acctrec.ac_utime);
906 api->mem.minflt = acctexp(acctrec.ac_minflt);
907 api->mem.majflt = acctexp(acctrec.ac_majflt);
908 api->dsk.rio = acctexp(acctrec.ac_rw);
909
910 strncpy(api->gen.name, acctrec.ac_comm, PNAMLEN);
911 api->gen.name[PNAMLEN] = '\0';
912 break;
913
914 case 3:
915 if ( read(acctfd, &acctrec_v3, acctrecsz) < acctrecsz )
916 break; /* unexpected end of account file */
917
918 /*
919 ** fill process info from accounting-record
920 */
921 api->gen.state = 'E';
922 api->gen.pid = acctrec_v3.ac_pid;
923 api->gen.tgid = acctrec_v3.ac_pid;
924 api->gen.ppid = acctrec_v3.ac_ppid;
925 api->gen.nthr = 1;
926 api->gen.isproc = 1;
927 api->gen.excode = acctrec_v3.ac_exitcode;
928 api->gen.ruid = acctrec_v3.ac_uid;
929 api->gen.rgid = acctrec_v3.ac_gid;
930 api->gen.btime = acctrec_v3.ac_btime;
931 api->gen.elaps = acctrec_v3.ac_etime;
932 api->cpu.stime = acctexp(acctrec_v3.ac_stime);
933 api->cpu.utime = acctexp(acctrec_v3.ac_utime);
934 api->mem.minflt = acctexp(acctrec_v3.ac_minflt);
935 api->mem.majflt = acctexp(acctrec_v3.ac_majflt);
936 api->dsk.rio = acctexp(acctrec_v3.ac_rw);
937
938 strncpy(api->gen.name, acctrec_v3.ac_comm, PNAMLEN);
939 api->gen.name[PNAMLEN] = '\0';
940 break;
941 }
942 }
943
944 if (acctsize > ACCTMAXFILESZ && !maxshadowrec)
945 acctrestarttrial();
946
947 return nrexit;
948 }
949
950 /*
951 ** when the size of the private accounting file exceeds a certain limit,
952 ** it might be useful to stop process accounting, truncate the
953 ** process accounting file to zero and start process accounting again
954 **
955 ** this will only be done if this atop process is the only one
956 ** that is currently using the accounting file
957 */
958 static void
959 acctrestarttrial()
960 {
961 struct stat statacc;
962 int sematopid;
963
964 /*
965 ** not private accounting-file in use?
966 */
967 if (!acctatop)
968 return; // do not restart
969
970 /*
971 ** still remaining records in accounting file that are
972 ** written between the moment that the number of exited
973 ** processes was counted and the moment that all processes
974 ** were read
975 */
976 (void) fstat(acctfd, &statacc);
977
978 if (acctsize != statacc.st_size)
979 return; // do not restart
980
981 /*
982 ** claim the semaphore to get exclusive rights for
983 ** the accounting-manipulation
984 */
985 sematopid = semget(ATOPACCTKEY, 0, 0);
986
987 (void) semop(sematopid, &semclaim, 1);
988
989 /*
990 ** check if there are more users of accounting file
991 */
992 if (semctl(sematopid, 1, GETVAL, 0) < ATOPACCTTOT-1)
993 {
994 (void) semop(sematopid, &semrelse, 1);
995 return; // do not restart
996 }
997
998 /*
999 ** restart is possible
1000 **
1001 ** - switch off accounting
1002 ** - truncate the file
1003 ** - switch on accounting
1004 */
1005 regainrootprivs(); // get root privs again
1006
1007 (void) acct(0); // switch off accounting
1008
1009 if ( truncate(ACCTDIR "/" ACCTFILE, 0) == 0)
1010 (void) lseek(acctfd, 0, SEEK_SET);
1011
1012 (void) acct(ACCTDIR "/" ACCTFILE);
1013
1014 if (! droprootprivs() )
1015 mcleanstop(42, "failed to drop root privs\n");
1016
1017 acctsize = 0;
1018
1019 (void) semop(sematopid, &semrelse, 1);
1020 }
1021
1022 /*
1023 ** expand the special compression-methods
1024 */
1025 static count_t
1026 acctexp(comp_t ct)
1027 {
1028 count_t exp;
1029 count_t val;
1030
1031 exp = (ct >> 13) & 0x7; /* obtain 3 bits base-8 exponent */
1032 val = ct & 0x1fff; /* obtain 13 bits mantissa */
1033
1034 while (exp-- > 0)
1035 val <<= 3;
1036
1037 return val;
1038 }
1039
1040 /*
1041 ** switch to the next accounting shadow file
1042 */
1043 static void
1044 switchshadow(void)
1045 {
1046 int tmpfd;
1047 char shadowpath[128];
1048 struct flock flock;
1049
1050 /*
1051 ** determine path name of new shadow file
1052 */
1053 curshadowseq++;
1054
1055 snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
1056 pacctdir, PACCTSHADOWD, curshadowseq);
1057
1058 /*
1059 ** open new shadow file, while the previous is also kept open
1060 ** (to keep the read lock until a read lock is set for the new
1061 ** shadow file)
1062 */
1063 if ( (tmpfd = open(shadowpath, O_RDONLY)) != -1)
1064 {
1065 /*
1066 ** set read lock on new shadow file
1067 */
1068 flock.l_type = F_RDLCK;
1069 flock.l_whence = SEEK_SET;
1070 flock.l_start = 0;
1071 flock.l_len = 1;
1072
1073 if ( fcntl(tmpfd, F_SETLK, &flock) != -1)
1074 {
1075 (void) close(acctfd); // implicit release read lock
1076 acctfd = tmpfd;
1077 return;
1078 }
1079 else
1080 {
1081 (void) close(tmpfd);
1082 }
1083 }
1084 }
1085
1086 /*
1087 ** handle the option 'pacctdir' in the /etc/atoprc file
1088 */
1089 void
1090 do_pacctdir(char *tagname, char *tagvalue)
1091 {
1092 char shadowpath[128];
1093 struct stat dirstat;
1094
1095 /*
1096 ** copy the directory pathname to an own buffer
1097 */
1098 if ( (pacctdir = malloc(strlen(tagvalue)+1)) == NULL )
1099 {
1100 perror("malloc pacctdir");
1101 exit(11);
1102 }
1103
1104 strcpy(pacctdir, tagvalue);
1105
1106 /*
1107 ** verify if the atopacctd daemon is active
1108 */
1109 if ( semget(PACCTPUBKEY, 0, 0) == -1)
1110 {
1111 fprintf(stderr, "Warning: option '%s' specified "
1112 "while atopacctd not running!\n", tagname);
1113 sleep(2);
1114 return;
1115 }
1116
1117 /*
1118 ** verify that the topdirectory exists
1119 */
1120 if ( stat(pacctdir, &dirstat) == -1 )
1121 {
1122 fprintf(stderr, "Warning: option '%s' specified - ", tagname);
1123 perror(pacctdir);
1124 sleep(2);
1125 return;
1126 }
1127
1128 if (! S_ISDIR(dirstat.st_mode) )
1129 {
1130 fprintf(stderr, "Warning: option '%s' specified - ", tagname);
1131 fprintf(stderr, "%s not a directory\n", pacctdir);
1132 sleep(2);
1133 return;
1134 }
1135
1136 /*
1137 ** verify that the topdirectory contains the required subdirectory
1138 */
1139 snprintf(shadowpath, sizeof shadowpath, "%s/%s",
1140 pacctdir, PACCTSHADOWD);
1141
1142 if ( stat(shadowpath, &dirstat) == -1 )
1143 {
1144 fprintf(stderr, "Warning: option '%s' specified - ", tagname);
1145 perror(shadowpath);
1146 sleep(2);
1147 return;
1148 }
1149
1150 if (! S_ISDIR(dirstat.st_mode) )
1151 {
1152 fprintf(stderr, "Warning: option '%s' specified - ", tagname);
1153 fprintf(stderr, "%s not a directory\n", shadowpath);
1154 sleep(2);
1155 return;
1156 }
1157 }