"Fossies" - the Fresh Open Source Software Archive 
Member "atop-2.8.1/atopacctd.c" (7 Jan 2023, 25606 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 "atopacctd.c" see the
Fossies "Dox" file reference documentation and the last
Fossies "Diffs" side-by-side code changes report:
2.5.0_vs_2.6.0.
1 /*
2 ** The atopacctd daemon switches on the process accounting feature
3 ** in the kernel and let the process accounting records be written to
4 ** a file, called the source file. After process accounting is active,
5 ** the atopacctd daemon transfers every process accounting record that
6 ** is available in the source file to a shadow file.
7 ** Client processes (like atop) can read the shadow file instead of
8 ** the original process accounting source file.
9 **
10 ** This approach has the following advantages:
11 **
12 ** - The atopacctd daemon keeps the source file to a limited size
13 ** by truncating it regularly.
14 **
15 ** - The atopacct daemon takes care that a shadow file has a limited size.
16 ** As soon as the current shadow file reaches its maximum size, the
17 ** atopacctd daemon creates a subsequent shadow file. For this reason,
18 ** the name of a shadow file contains a sequence number.
19 ** Shadow files that are not used by client processes any more, are
20 ** automatically removed by the atopacctd daemon.
21 **
22 ** - When no client processes are active (any more), all shadow files
23 ** will be deleted and no records will be transferred to shadow files
24 ** any more. As soon as at least one client is activated again, the
25 ** atopacctd daemon start writing shadow files again.
26 **
27 ** The directory /var/run is used as the default top-directory. An
28 ** alternative top-directory can be specified as command line argument
29 ** (in that case, also modify /etc/atoprc to inform atop as a client).
30 ** Below this top-directory the source file pacct_source is created and
31 ** the subdirectory pacct_shadow.d as a 'container' for the shadow files.
32 ** ----------------------------------------------------------------------
33 ** Copyright (C) 2014 Gerlof Langeveld (gerlof.langeveld@atoptool.nl)
34 **
35 ** This program is free software; you can redistribute it and/or modify
36 ** it under the terms of the GNU General Public License version 2 as
37 ** published by the Free Software Foundation.
38 */
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <stdio.h>
42 #include <syslog.h>
43 #include <string.h>
44 #include <signal.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <fcntl.h>
48 #include <sys/ipc.h>
49 #include <sys/sem.h>
50 #include <errno.h>
51 #include <time.h>
52 #include <sys/resource.h>
53 #include <sys/socket.h>
54 #include <sys/mman.h>
55 #include <sys/statvfs.h>
56 #include <sys/wait.h>
57
58 #include "atop.h"
59 #include "photoproc.h"
60 #include "acctproc.h"
61 #include "version.h"
62 #include "versdate.h"
63 #include "atopacctd.h"
64
65 #define RETRYCNT 10 // # retries to read account record
66 #define RETRYMS 25 // timeout (millisec) to read account record
67 #define NORECINTERVAL 3600 // no-record-available interval (seconds)
68
69 #define PACCTSEC 3 // timeout (sec) to retry switch on pacct
70 #define POLLSEC 1 // timeout (sec) when NETLINK fails
71
72 #define GCINTERVAL 60 // garbage collection interval (seconds)
73
74 /*
75 ** Semaphore-handling
76 **
77 ** Two semaphore groups are created:
78 **
79 ** The private semaphore (group) specifies the number of atopacctd processes
80 ** running (to be sure that only one daemon is active at the time).
81 **
82 ** The public semaphore group contains two semaphores:
83 **
84 ** 0: the number of processes using the process accounting shadow files,
85 ** i.e. the number of (atop) clients. This semaphore starts at a high
86 ** value and should be decremented by every (atop) client that starts
87 ** using the shadow files and incremented again whenever that (atop)
88 ** client terminates.
89 **
90 ** 1: binary semphore that has to be claimed before using/modifying
91 ** semaphore 0 in this group.
92 */
93 static int semprv;
94
95
96 static int sempub;
97 #define SEMTOTAL 100
98 #define NUMCLIENTS (SEMTOTAL - semctl(sempub, 0, GETVAL, 0))
99
100 struct sembuf semlocknowait = {1, -1, SEM_UNDO|IPC_NOWAIT},
101 semunlock = {1, +1, SEM_UNDO};
102
103
104 static char atopacctdversion[] = ATOPVERS;
105 static char atopacctddate[] = ATOPDATE;
106
107 static unsigned long maxshadowrec = MAXSHADOWREC;
108 static char *pacctdir = PACCTDIR;
109
110 static char cleanup_and_go = 0;
111
112 /*
113 ** function prototypes
114 */
115 static int awaitprocterm(int, int, int, char *, int *,
116 unsigned long *, unsigned long *);
117 static int swonpacct(int, char *);
118 static int createshadow(long);
119 static int pass2shadow(int, char *, int);
120 static void gcshadows(unsigned long *, unsigned long);
121 static void setcurrent(long);
122 static int acctsize(struct acct *);
123 static void cleanup(int);
124
125
126 int netlink_open(void); // from netlink.c
127 int netlink_recv(int, int); // from netlink.c
128
129
130 int
131 main(int argc, char *argv[])
132 {
133 int i, nfd, afd, sfd;
134 int parentpid;
135 struct stat dirstat;
136 struct rlimit rlim;
137 FILE *pidf;
138
139 struct sembuf semincr = {0, +1, SEM_UNDO};
140
141 char shadowdir[128], shadowpath[128];
142 char accountpath[128];
143 unsigned long oldshadow = 0, curshadow = 0;
144 int shadowbusy = 0;
145 time_t gclast = time(0);
146
147 struct sigaction sigcleanup;
148 int liResult;
149
150 /*
151 ** argument passed?
152 */
153 if (argc == 2)
154 {
155 /*
156 ** version number required (flag -v or -V)?
157 */
158 if (*argv[1] == '-') // flag?
159 {
160 if ( *(argv[1]+1) == 'v' || *(argv[1]+1) == 'V')
161 {
162 printf("%s <gerlof.langeveld@atoptool.nl>\n",
163 getstrvers());
164 return 0;
165 }
166 else
167 {
168 fprintf(stderr,
169 "Usage: atopacctd [-v|topdirectory]\n"
170 "Default topdirectory: %s\n", PACCTDIR);
171 exit(1);
172 }
173 }
174
175 /*
176 ** if first argument is not a flag, it should be the name
177 ** of an alternative top directory (to be validated later on)
178 */
179 pacctdir = argv[1];
180 }
181 else
182 {
183 if (argc != 1)
184 {
185 fprintf(stderr,
186 "Usage: atopacctd [-v|topdirectory]\n"
187 "Default topdirectory: %s\n", PACCTDIR);
188 exit(1);
189 }
190 }
191
192 /*
193 ** verify if we are running with the right privileges
194 */
195 if (geteuid() != 0)
196 {
197 fprintf(stderr, "Root privileges are needed!\n");
198 exit(1);
199 }
200
201 /*
202 ** verify that the top directory is not world-writable
203 ** and owned by root
204 */
205 if ( stat(pacctdir, &dirstat) == -1 )
206 {
207 perror(pacctdir);
208 fprintf(stderr, "Usage: atopacctd [-v|topdirectory]\n"
209 "Default topdirectory: %s\n", PACCTDIR);
210 exit(2);
211 }
212
213 if (! S_ISDIR(dirstat.st_mode) )
214 {
215 fprintf(stderr, "atopacctd: %s is not a directory\n", pacctdir);
216 exit(2);
217 }
218
219 if (dirstat.st_uid != 0)
220 {
221 fprintf(stderr,
222 "atopacctd: directory %s must be owned by root\n",
223 pacctdir);
224 exit(2);
225 }
226
227 if (dirstat.st_mode & (S_IWGRP|S_IWOTH))
228 {
229 fprintf(stderr,
230 "atopacctd: directory %s may not be writable "
231 "for group/others\n", pacctdir);
232 exit(2);
233 }
234
235 /*
236 ** create the semaphore groups and initialize the semaphores;
237 ** if the private semaphore already exists, verify if another
238 ** atopacctd daemon is already running
239 */
240 if ( (semprv = semget(PACCTPRVKEY, 0, 0)) >= 0) // exists?
241 {
242 if ( semctl(semprv, 0, GETVAL, 0) > 0)
243 {
244 fprintf(stderr, "atopacctd is already running!\n");
245 exit(3);
246 }
247 }
248 else
249 {
250 if ( (semprv = semget(PACCTPRVKEY, 1,
251 0600|IPC_CREAT|IPC_EXCL)) >= 0)
252 {
253 (void) semctl(semprv, 0, SETVAL, 0);
254 }
255 else
256 {
257 perror("cannot create private semaphore");
258 exit(3);
259 }
260 }
261
262 // create new semaphore group
263 //
264 if ( (sempub = semget(PACCTPUBKEY, 0, 0)) != -1) // existing?
265 (void) semctl(sempub, 0, IPC_RMID, 0);
266
267 if ( (sempub = semget(PACCTPUBKEY, 2, 0666|IPC_CREAT|IPC_EXCL)) >= 0)
268 {
269 (void) semctl(sempub, 0, SETVAL, SEMTOTAL);
270 (void) semctl(sempub, 1, SETVAL, 1);
271 }
272 else
273 {
274 perror("cannot create public semaphore");
275 exit(3);
276 }
277
278 /*
279 ** prepare cleanup signal handler
280 */
281 memset(&sigcleanup, 0, sizeof sigcleanup);
282 sigcleanup.sa_handler = cleanup;
283 sigemptyset(&sigcleanup.sa_mask);
284
285 /*
286 ** daemonize this process
287 ** i.e. be sure that the daemon is no session leader (any more)
288 ** and get rid of a possible bad context that might have been
289 ** inherited from ancestors
290 */
291 parentpid = getpid(); // to be killed when initialized
292 (void) sigaction(SIGTERM, &sigcleanup, (struct sigaction *)0);
293
294 if ( fork() ) // implicitly switch to background
295 {
296 /*
297 ** parent after forking first child:
298 ** wait for signal 15 from child before terminating
299 ** because systemd expects parent to terminate whenever
300 ** service is up and running
301 */
302 pause(); // wait for signal from child
303 exit(0); // finish parent
304 }
305
306 setsid(); // become session leader to lose ctty
307
308 if ( fork() ) // finish parent; continue in child
309 exit(0); // --> no session leader, no ctty
310
311 getrlimit(RLIMIT_NOFILE, &rlim);
312
313 for (i=0; i < rlim.rlim_cur; i++) // close all files, but
314 {
315 if (i != 2) // do not close stderr
316 close(i);
317 }
318
319 umask(022);
320
321 liResult = chdir("/tmp"); // go to a safe place
322
323 if(liResult != 0)
324 {
325 char lcMessage[64];
326
327 snprintf(lcMessage, sizeof(lcMessage) - 1,
328 "%s:%d - Error %d changing to tmp dir\n",
329 __FILE__, __LINE__, errno );
330 fprintf(stderr, "%s", lcMessage);
331 }
332
333 /*
334 ** increase semaphore to define that atopacctd is running
335 */
336 if ( semop(semprv, &semincr, 1) == -1)
337 {
338 perror("cannot increment private semaphore");
339 kill(parentpid, SIGTERM);
340 exit(4);
341 }
342
343 /*
344 ** create source accounting file to which the kernel can write
345 ** its records
346 */
347 snprintf(accountpath, sizeof accountpath, "%s/%s", pacctdir, PACCTORIG);
348
349 (void) unlink(accountpath); // in case atopacctd previously killed
350
351 if ( (afd = creat(accountpath, 0600)) == -1)
352 {
353 perror(accountpath);
354 kill(parentpid, SIGTERM);
355 exit(5);
356 }
357
358 (void) close(afd);
359
360 /*
361 ** open the accounting file for read
362 */
363 if ( (afd = open(accountpath, O_RDONLY)) == -1)
364 {
365 perror(accountpath);
366 kill(parentpid, SIGTERM);
367 exit(5);
368 }
369
370 /*
371 ** create directory to store the shadow files
372 ** when atopacctd was previously killed, rename the
373 ** old directory to a unique name
374 */
375 snprintf(shadowdir, sizeof shadowdir, "%s/%s", pacctdir, PACCTSHADOWD);
376
377 if ( stat(shadowdir, &dirstat) == 0 ) // already exists?
378 {
379 if (S_ISDIR(dirstat.st_mode)) // and is directory?
380 {
381 // define new name to save directory
382 char newshadow[256];
383
384 snprintf(newshadow, sizeof newshadow, "%s-old-%d",
385 shadowdir, getpid());
386
387 if ( rename(shadowdir, newshadow) == -1)
388 {
389 perror("could not rename old shadow directory");
390 exit(5);
391 }
392 }
393 }
394
395 if ( mkdir(shadowdir, 0755) == -1)
396 {
397 perror(shadowdir);
398 kill(parentpid, SIGTERM);
399 exit(5);
400 }
401
402 sfd = createshadow(curshadow);
403 setcurrent(curshadow);
404
405 /*
406 ** open syslog interface
407 */
408 openlog("atopacctd", LOG_PID, LOG_DAEMON);
409 syslog(LOG_INFO, "%s <gerlof.langeveld@atoptool.nl>", getstrvers());
410
411 /*
412 ** raise priority (be sure the nice value becomes -20,
413 ** independent of the current nice value)
414 ** this may fail without notice for non-privileged processes
415 */
416 liResult = nice(-39);
417
418 /*
419 ** connect to NETLINK socket of kernel to be triggered
420 ** when processes have finished
421 */
422 if ( (nfd = netlink_open()) == -1)
423 {
424 (void) unlink(accountpath);
425 kill(parentpid, SIGTERM);
426 exit(5);
427 }
428
429 /*
430 ** switch on accounting - inital
431 */
432 if ( swonpacct(afd, accountpath) == -1)
433 {
434 (void) unlink(accountpath);
435 kill(parentpid, SIGTERM);
436 exit(6);
437 }
438
439 syslog(LOG_INFO, "accounting to %s", accountpath);
440
441 /*
442 ** signal handling
443 */
444 (void) signal(SIGHUP, SIG_IGN);
445
446 (void) sigaction(SIGINT, &sigcleanup, (struct sigaction *)0);
447 (void) sigaction(SIGQUIT, &sigcleanup, (struct sigaction *)0);
448 (void) sigaction(SIGTERM, &sigcleanup, (struct sigaction *)0);
449
450 /*
451 ** create PID file
452 */
453 if ( (pidf = fopen(PIDFILE, "w")) )
454 {
455 fprintf(pidf, "%d\n", getpid());
456 fclose(pidf);
457 }
458
459 /*
460 ** terminate parent: service initialized
461 */
462 kill(parentpid, SIGTERM);
463
464 /*
465 ** main loop
466 */
467 while (! cleanup_and_go)
468 {
469 int state;
470 time_t curtime;
471
472 /*
473 ** await termination of (at least) one process and
474 ** copy the process accounting record(s)
475 */
476 state = awaitprocterm(nfd, afd, sfd, accountpath,
477 &shadowbusy, &oldshadow, &curshadow);
478
479 if (state == -1) // irrecoverable error?
480 break;
481
482 /*
483 ** garbage collection (i.e. removal of shadow files that
484 ** are not in use any more) is needed in case:
485 **
486 ** - shadow files are currently maintained because
487 ** at least one atop is running, and
488 ** - shadow files have not been removed for GCINTERVAL
489 ** seconds, or
490 ** - the system clock has been modified (lowered)
491 */
492 if ( shadowbusy &&
493 (time(&curtime) > gclast + GCINTERVAL ||
494 curtime < gclast ) )
495 {
496 gcshadows(&oldshadow, curshadow);
497 gclast = time(0);
498 }
499 }
500
501 /*
502 ** cleanup and terminate
503 */
504 (void) acct((char *) 0); // disable process accounting
505 (void) unlink(accountpath); // remove source file
506
507 for (; oldshadow <= curshadow; oldshadow++) // remove shadow files
508 {
509 /*
510 ** assemble path name of shadow file (starting by oldest)
511 */
512 snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
513 pacctdir, PACCTSHADOWD, oldshadow);
514
515 (void) unlink(shadowpath);
516 }
517
518 snprintf(shadowpath, sizeof shadowpath, "%s/%s/%s",
519 pacctdir, PACCTSHADOWD, PACCTSHADOWC);
520
521 (void) unlink(shadowpath); // remove file 'current'
522 (void) rmdir(shadowdir); // remove shadow.d directory
523
524 if (cleanup_and_go)
525 {
526 syslog(LOG_NOTICE, "Terminated by signal %d\n", cleanup_and_go);
527
528 if (cleanup_and_go == SIGTERM)
529 return 0;
530 else
531 return cleanup_and_go + 128;
532 }
533 else
534 {
535 syslog(LOG_NOTICE, "Terminated!\n");
536 return 13;
537 }
538 }
539
540
541 /*
542 ** wait for at least one process termination and copy process accounting
543 ** record(s) from the source process accounting file to the current
544 ** shadow accounting file
545 **
546 ** return code: 0 - no process accounting record read
547 ** 1 - at least one process accounting record read
548 ** -1 - irrecoverable failure
549 */
550 static int
551 awaitprocterm(int nfd, int afd, int sfd, char *accountpath,
552 int *shadowbusyp, unsigned long *oldshadowp, unsigned long *curshadowp)
553 {
554 static int arecsize, netlinkactive = 1;
555 static unsigned long long atotsize, stotsize, maxshadowsz;
556 static time_t reclast;
557 struct timespec retrytimer = {0, RETRYMS/2*1000000};
558 int retrycount = RETRYCNT;
559 int asz, rv, ssz;
560 char abuf[16000];
561 int partsz, remsz;
562
563 /*
564 ** neutral state:
565 **
566 ** wait for info from NETLINK indicating that at least
567 ** one process has finished; the real contents of the
568 ** NETLINK message is ignored, it is only used as trigger
569 ** that something can be read from the process accounting file
570 **
571 ** unfortunately it is not possible to use inotify() on the
572 ** source file as a trigger that a new accounting record
573 ** has been written (does not work if the kernel itself
574 ** writes to the file)
575 **
576 ** when the NETLINK interface fails due to kernel bug 190711,
577 ** we switch to polling mode:
578 ** wait for timer and verify if process accounting
579 ** records are available (repeatedly); ugly but the only
580 ** thing we can do if we can't use NETLINK
581 */
582 if (netlinkactive)
583 {
584 rv = netlink_recv(nfd, 0);
585
586 if (rv == 0) // EOF?
587 {
588 syslog(LOG_ERR, "unexpected EOF on NETLINK\n");
589 perror("unexpected EOF on NETLINK\n");
590 return -1;
591 }
592
593 if (rv < 0) // failure?
594 {
595 switch (-rv)
596 {
597 // acceptable errors that might indicate that
598 // processes have terminated
599 case EINTR:
600 case ENOMEM:
601 case ENOBUFS:
602 break;
603
604 default:
605 syslog(LOG_ERR,
606 "unexpected error on NETLINK: %s\n",
607 strerror(-rv));
608 fprintf(stderr,
609 "unexpected error on NETLINK: %s\n",
610 strerror(-rv));
611
612 if (-rv == EINVAL)
613 {
614 syslog(LOG_ERR,
615 "(see ATOP README about kernel bug 190711)\n");
616 fprintf(stderr,
617 "(see ATOP README about kernel bug 190711)\n");
618 }
619
620 syslog(LOG_ERR,
621 "switching to polling mode\n");
622 fprintf(stderr,
623 "switching to polling mode\n");
624
625 netlinkactive = 0; // polling mode wanted
626
627 return 0;
628 }
629 }
630
631 /*
632 ** get rid of all other waiting finished processes via netlink
633 ** before handling the process accounting record(s)
634 */
635 while ( netlink_recv(nfd, MSG_DONTWAIT) > 0 );
636 }
637 else // POLLING MODE
638 {
639 sleep(POLLSEC);
640 retrycount = 1;
641 }
642
643 /*
644 ** read new process accounting record(s)
645 ** such record(s) may not immediately be available (timing matter),
646 ** so some retries might be necessary
647 */
648 while ((asz = read(afd, abuf, sizeof abuf)) == 0 && --retrycount)
649 {
650 nanosleep(&retrytimer, (struct timespec *)0);
651 retrytimer.tv_nsec = RETRYMS*1000000;
652 }
653
654 switch (asz)
655 {
656 case 0: // EOF (no records available)?
657 if (time(0) > reclast + NORECINTERVAL && reclast)
658 {
659 syslog(LOG_WARNING, "reactivate process accounting\n");
660
661 if (truncate(accountpath, 0) != -1)
662 {
663 lseek(afd, 0, SEEK_SET);
664 atotsize = swonpacct(afd, accountpath);
665 }
666
667 reclast = time(0);
668 }
669
670 return 0; // wait for NETLINK again
671
672 case -1: // failure?
673 syslog(LOG_ERR, "%s - unexpected read error: %s\n",
674 accountpath, strerror(errno));
675 return -1;
676 }
677
678 reclast = time(0);
679
680 /*
681 ** only once: determine the size of an accounting
682 ** record and calculate the maximum size for each
683 ** shadow file
684 */
685 if (!arecsize)
686 {
687 arecsize = acctsize((struct acct *)abuf);
688
689 if (arecsize)
690 {
691 maxshadowsz = maxshadowrec * arecsize;
692 }
693 else
694 {
695 syslog(LOG_ERR,
696 "cannot determine size of account record\n");
697 return -1;
698 }
699 }
700
701 /*
702 ** truncate process accounting file regularly
703 */
704 atotsize += asz; // maintain current size
705
706 if (atotsize >= MAXORIGSZ)
707 {
708 if (truncate(accountpath, 0) != -1)
709 {
710 lseek(afd, 0, SEEK_SET);
711 atotsize = 0;
712 }
713 }
714
715 /*
716 ** determine if any client is using the shadow
717 ** accounting files; if not, verify if clients
718 ** have been using the shadow files till now and
719 ** cleanup has to be performed
720 */
721 if (semop(sempub, &semlocknowait, 1) == 0) // lock succeeded?
722 {
723 if (NUMCLIENTS == 0)
724 {
725 /*
726 ** did last client just disappear?
727 */
728 if (*shadowbusyp)
729 {
730 /*
731 ** remove all shadow files
732 */
733 gcshadows(oldshadowp, (*curshadowp)+1);
734 *oldshadowp = 0;
735 *curshadowp = 0;
736 stotsize = 0;
737
738 /*
739 ** create new file with sequence 0
740 */
741 (void) close(sfd);
742
743 sfd = createshadow(*curshadowp);
744 setcurrent(*curshadowp);
745
746 *shadowbusyp = 0;
747 }
748
749 (void) semop(sempub, &semunlock, 1);
750 return 1;
751 }
752
753 (void) semop(sempub, &semunlock, 1);
754 }
755
756 *shadowbusyp = 1;
757
758 /*
759 ** transfer process accounting data to shadow file
760 ** but take care to fill a shadow file exactly
761 ** to its maximum and not more...
762 */
763 if (stotsize + asz <= maxshadowsz) // would fit?
764 {
765 /*
766 ** pass all data
767 */
768 if ( (ssz = pass2shadow(sfd, abuf, asz)) > 0)
769 stotsize += ssz;
770
771 remsz = 0;
772 partsz = 0;
773 }
774 else
775 {
776 /*
777 ** calculate the remainder that has to
778 ** be written to the next shadow file
779 */
780 partsz = maxshadowsz - stotsize;
781 remsz = asz - partsz;
782
783 /*
784 ** transfer first part of the data to current
785 ** shadow file
786 */
787 if ( (ssz = pass2shadow(sfd, abuf, partsz)) > 0)
788 stotsize += ssz;
789 }
790
791 /*
792 ** verify if current shadow file has reached its
793 ** maximum size; if so, switch to next sequence number
794 ** and write remaining data (if any)
795 */
796 if (stotsize >= maxshadowsz)
797 {
798 close(sfd);
799
800 sfd = createshadow(++(*curshadowp));
801 setcurrent(*curshadowp);
802
803 stotsize = 0;
804
805 /*
806 ** transfer remaining part of the data
807 */
808 if (remsz)
809 {
810 if ( (ssz = pass2shadow(sfd, abuf+partsz, remsz)) > 0)
811 stotsize += ssz;
812 }
813 }
814
815 return 1;
816 }
817
818
819 /*
820 ** create first shadow file with requested sequence number
821 */
822 static int
823 createshadow(long seq)
824 {
825 int sfd;
826 char shadowpath[128];
827
828 snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
829 pacctdir, PACCTSHADOWD, seq);
830
831 /*
832 ** open the shadow file for write
833 */
834 if ( (sfd = creat(shadowpath, 0644)) == -1)
835 {
836 perror(shadowpath);
837 exit(5);
838 }
839
840 return sfd;
841 }
842
843 /*
844 ** transfer process accounting data to shadow file
845 */
846 static int
847 pass2shadow(int sfd, char *sbuf, int ssz)
848 {
849 static unsigned long long nrskipped;
850 struct statvfs statvfs;
851
852 /*
853 ** check if the filesystem is not filled for more than 95%
854 */
855 if ( fstatvfs(sfd, &statvfs) != -1)
856 {
857 if (statvfs.f_blocks == 0 ||
858 statvfs.f_bfree * 100 / statvfs.f_blocks < 5 )
859 {
860 if (nrskipped == 0) // first skip?
861 {
862 syslog(LOG_WARNING,
863 "Filesystem > 95%% full; "
864 "pacct writing skipped\n");
865 }
866
867 nrskipped++;
868
869 return 0;
870 }
871 }
872
873 /*
874 ** there is enough space in the filesystem (again)
875 ** verify if writing has been suspended due to lack of space (if so,
876 ** write a log message)
877 */
878 if (nrskipped)
879 {
880 syslog(LOG_WARNING, "Pacct writing continued (%llu skipped)\n",
881 nrskipped);
882 nrskipped = 0;
883 }
884
885 /*
886 ** transfer process accounting record(s) to shadow file
887 */
888 if ( write(sfd, sbuf, ssz) == -1 )
889 {
890 syslog(LOG_ERR, "Unexpected write error to shadow file: %s\n",
891 strerror(errno));
892 exit(7);
893 }
894
895 return ssz;
896 }
897
898
899 /*
900 ** switch on the process accounting mechanism
901 ** first parameter: file descriptor of open accounting file
902 ** second parameter: name of accounting file
903 ** return value: -1 in case of permanent failure,
904 ** otherwise number of bytes read from accounting file
905 */
906 static int
907 swonpacct(int afd, char *accountpath)
908 {
909 int n, acctokay = 0;
910 char abuf[4096];
911
912 /*
913 ** due to kernel bug 190271 (process accounting sometimes
914 ** does not work), we verify if process accounting really
915 ** works after switching it on. If not, we keep retrying
916 ** for a while.
917 */
918 while (! acctokay)
919 {
920 int maxcnt = 40;
921
922 /*
923 ** switch on process accounting
924 */
925 if ( acct(accountpath) == -1)
926 {
927 perror("cannot switch on process accounting");
928 return -1;
929 }
930
931 /*
932 ** try if process accounting works by spawning a
933 ** child process that immediately finishes (should
934 ** result in a process accounting record)
935 */
936 if ( fork() == 0 )
937 exit(0);
938
939 (void) wait((int *)0); // wait for child to finish
940
941 while ( (n = read(afd, abuf, sizeof abuf)) <= 0 && --maxcnt)
942 usleep(50000);
943
944 if (n > 0) // process accounting works
945 {
946 acctokay = 1;
947 }
948 else
949 {
950 syslog(LOG_ERR,
951 "Retrying to switch on process accounting\n");
952 syslog(LOG_ERR,
953 "(see ATOP README about kernel bug 190271)\n");
954
955 acct((char *)0); // switch off process accounting
956 sleep(PACCTSEC); // wait a while before retrying
957 }
958 }
959
960 return n;
961 }
962
963
964 /*
965 ** remove old shadow files not being in use any more
966 **
967 ** When a reading process (atop) opens a shadow file,
968 ** it places a read lock on the first byte of that file.
969 ** More then one read lock is allowed on that first byte
970 ** (in case of more atop incarnations).
971 ** If at least one read lock exists, a write lock (to be
972 ** tried here) will fail which means that the file is still
973 ** in use by at least one reader.
974 */
975 static void
976 gcshadows(unsigned long *oldshadowp, unsigned long curshadow)
977 {
978 struct flock flock;
979 int tmpsfd;
980 char shadowpath[128];
981
982 /*
983 ** fill flock structure: write lock on first byte
984 */
985 flock.l_type = F_WRLCK;
986 flock.l_whence = SEEK_SET;
987 flock.l_start = 0;
988 flock.l_len = 1;
989
990 for (; *oldshadowp < curshadow; (*oldshadowp)++)
991 {
992 /*
993 ** assemble path name of shadow file (starting by oldest)
994 */
995 snprintf(shadowpath, sizeof shadowpath, PACCTSHADOWF,
996 pacctdir, PACCTSHADOWD, *oldshadowp);
997
998 /*
999 ** try to open oldest existing file for write
1000 ** and verify if it is in use
1001 */
1002 if ( (tmpsfd = open(shadowpath, O_WRONLY)) == -1)
1003 break;
1004
1005 if ( fcntl(tmpsfd, F_SETLK, &flock) == -1) // no-wait trial
1006 {
1007 close(tmpsfd);
1008 break; // setting lock failed, so still in use
1009 }
1010
1011 /*
1012 ** lock successfully set, so file is unused: remove file;
1013 ** closing the file implicitly removes the succeeded lock
1014 */
1015 (void) close(tmpsfd);
1016 (void) unlink(shadowpath);
1017 }
1018 }
1019
1020 /*
1021 ** write sequence number of current (newest) file
1022 */
1023 static void
1024 setcurrent(long curshadow)
1025 {
1026 static int cfd = -1;
1027 char currentpath[128], currentdata[128];
1028 int len;
1029 int liResult;
1030
1031 /*
1032 ** assemble file name of currency file and open (only once)
1033 */
1034 if (cfd == -1)
1035 {
1036 snprintf(currentpath, sizeof currentpath, "%s/%s/%s",
1037 pacctdir, PACCTSHADOWD, PACCTSHADOWC);
1038
1039 if ( (cfd = creat(currentpath, 0644)) == -1)
1040 {
1041 syslog(LOG_ERR, "Could not create currency file: %s\n",
1042 strerror(errno));
1043 return;
1044 }
1045 }
1046
1047 /*
1048 ** assemble ASCII string to be written: seqnumber/maxrecords
1049 */
1050 len = snprintf(currentdata, sizeof currentdata, "%ld/%lu",
1051 curshadow, maxshadowrec);
1052
1053 /*
1054 ** wipe currency file and write new assembled string
1055 */
1056 liResult = ftruncate(cfd, 0);
1057
1058 if(liResult != 0)
1059 {
1060 char lcMessage[64];
1061
1062 snprintf(lcMessage, sizeof(lcMessage) - 1,
1063 "%s:%d - Error %d ftruncate\n\n", __FILE__, __LINE__,
1064 errno );
1065 fprintf(stderr, "%s", lcMessage);
1066 }
1067
1068 (void) lseek(cfd, 0, SEEK_SET);
1069
1070 liResult = write(cfd, currentdata, len);
1071
1072 if(liResult == -1)
1073 {
1074 char lcMessage[64];
1075
1076 snprintf(lcMessage, sizeof(lcMessage) - 1,
1077 "%s:%d - Error %d writing\n\n", __FILE__, __LINE__,
1078 errno );
1079 fprintf(stderr, "%s", lcMessage);
1080 }
1081 }
1082
1083 /*
1084 ** determine the size of an accounting record
1085 */
1086 static int
1087 acctsize(struct acct *parec)
1088 {
1089 switch (parec->ac_version & 0x0f)
1090 {
1091 case 2:
1092 return sizeof(struct acct);
1093
1094 case 3:
1095 return sizeof(struct acct_v3);
1096
1097 default:
1098 return 0;
1099 }
1100 }
1101
1102 /*
1103 ** generate version number and date
1104 */
1105 char *
1106 getstrvers(void)
1107 {
1108 static char vers[256];
1109
1110 snprintf(vers, sizeof vers, "Version: %s - %s",
1111 atopacctdversion, atopacctddate);
1112
1113 return vers;
1114 }
1115
1116 /*
1117 ** signal catcher:
1118 ** set flag to be verified in main loop to cleanup and terminate
1119 */
1120 void
1121 cleanup(int sig)
1122 {
1123 cleanup_and_go = sig;
1124 }