"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 }