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