# 2.11BSD#431 - /usr/src/libexec/acctd/acctd.c

##### Functions

    checkacctspace    X
    die               X
    doit              X
    errline           X
    hupcatch          X
    main              X
    parseconf         X
    reconfig          X
    reportit          X
    terminate         X
    usage             X

##### Variables

    Acctdcf           X
    Acctfile          X
    Acctparms         X
    Alogfd            X
    Chkfreq           X
    Debug             X
    Disabled          X
    Resume            X
    Suspend           X

##### struct's

    ACCTPARM          X

       1: /*
       2:  * Steven Schultz - sms@moe.2bsd.com
       3:  *
       4:  *	@(#)acctd.c	1.0 (2.11BSD) 1999/2/10
       5:  *
       6:  * acctd - process accounting daemon
       7: */
       8:
       9: #include    <signal.h>
      10: #include    <stdio.h>
      11: #include    <errno.h>
      12: #include    <fcntl.h>
      13: #include    <stdlib.h>
      14: #include    <string.h>
      15: #include    <syslog.h>
      16: #include    <varargs.h>
      17: #include    <sys/types.h>
      18: #include    <sys/param.h>
      19: #include    <sys/acct.h>
      20: #include    <sys/mount.h>
      21: #include    <sys/stat.h>
      22: #include    <sys/time.h>
      23: #include    <sys/resource.h>
      24:
      25:     struct  ACCTPARM
      26:         {
      27:         int suspend;
      28:         int resume;
      29:         int chkfreq;
      30:         char    *acctfile;  /* malloc'd */
      31:         };
      32:
      33:     int Suspend = 2;    /* %free when accounting suspended */
      34:     int Resume = 4; /* %free when accounting to be resumed */
      35:     int Chkfreq = 30;   /* how often (seconds) to check disk space */
      36:     int Debug;
      37:     int Disabled;
      38:     char    *Acctfile;
      39:     int Alogfd;
      40:     struct  ACCTPARM Acctparms;
      41:     FILE    *Acctfp;
      42:     char    *Acctdcf = _PATH_ACCTDCF;
      43:     int checkacctspace(), hupcatch(), terminate();
      44:     void    usage(), errline();
      45:     void    die(), reportit();
      46:
      47: extern  char    *__progname;
      48:
      49: main(argc, argv)
      50:     int argc;
      51:     char    **argv;
      52:     {
      53:     int c, i;
      54:     pid_t   pid;
      55:     register FILE *fp;
      56:     struct  ACCTPARM ajunk;
      57:     sigset_t smask;
      58:     struct  sigaction sa;
      59:
      60:     if  (getuid())
      61:         die("%s", "Only root can run this program");
      62:
      63:     opterr = 0;
      64:     while   ((c = getopt(argc, argv, "d")) != EOF)
      65:         {
      66:         switch  (c)
      67:             {
      68:             case    'd':
      69:                 Debug++;
      70:                 break;
      71:             case    '?':
      72:             default:
      73:                 usage();
      74:                 /* NOTREACHED */
      75:             }
      76:         }
      77:     argc -= optind;
      78:     argv += optind;
      79:     if  (argc != 0)
      80:         {
      81:         usage();
      82:         /* NOTREACHED */
      83:         }
      84: /*
      85:  * Catch the signals of interest and ignore the ones that could get generated
      86:  * from the keyboard.  If additional signals are caught remember to add them
      87:  * to the masks of the other signals!
      88: */
      89:     daemon(0,0);
      90:
      91:     sigemptyset(&smask);
      92:     sigaddset(&smask, SIGTERM);
      93:     sigaddset(&smask, SIGHUP);
      94:     sa.sa_handler = checkacctspace;
      95:     sa.sa_mask = smask;
      96:     sa.sa_flags = SA_RESTART;
      97:     sigaction(SIGALRM, &sa, NULL);
      98:
      99:     sigemptyset(&smask);
     100:     sigaddset(&smask, SIGALRM);
     101:     sigaddset(&smask, SIGHUP);
     102:     sa.sa_handler = terminate;
     103:     sa.sa_mask = smask;
     104:     sa.sa_flags = SA_RESTART;
     105:     sigaction(SIGTERM, &sa, NULL);
     106:
     107:     sigemptyset(&smask);
     108:     sigaddset(&smask, SIGALRM);
     109:     sigaddset(&smask, SIGTERM);
     110:     sa.sa_handler = hupcatch;
     111:     sa.sa_mask = smask;
     112:     sa.sa_flags = SA_RESTART;
     113:     sigaction(SIGHUP, &sa, NULL);
     114:
     115:     signal(SIGQUIT, SIG_IGN);
     116:     signal(SIGTSTP, SIG_IGN);
     117:     signal(SIGINT, SIG_IGN);
     118:
     119:     if  (parseconf(&ajunk) < 0)
     120:         die("%s owner/mode/reading/parsing error", Acctdcf);
     121:     reconfig(&ajunk);
     122: /*
     123:  * The conf file has been opened, parsed/validated and output file created.
     124:  * It's time to open the accounting log device.
     125:  *
     126:  * The open is retried a few times (using usleep which does not involve
     127:  * signals or alarms) because the previous 'acctd' may be in its SIGTERM
     128:  * handling - see the comments in terminate().   Could try longer perhaps.
     129: */
     130:     for (i = 0; i < 4; i++)
     131:         {
     132:         Alogfd = open(_PATH_DEVALOG, O_RDONLY);
     133:         if  (Alogfd > 0)
     134:             break;
     135:         usleep(1100000L);
     136:         }
     137:     if  (Alogfd < 0)
     138:         die("open(%s) errno: %d", _PATH_DEVALOG, errno);
     139: /*
     140:  * Save our pid for 'accton' to use
     141: */
     142:     fp = fopen(_PATH_ACCTDPID, "w");
     143:     pid = getpid();
     144:     if  (!fp)
     145:         die("fopen(%s,w) error %d\n", _PATH_ACCTDPID, errno);
     146:     fprintf(fp, "%d\n", pid);
     147:     fclose(fp);
     148:
     149: /*
     150:  * Raise our priority slightly.  The kernel can buffer quite a bit but
     151:  * if the system gets real busy we might be starved for cpu time and lose
     152:  * accounting events.  We do not run often or for long so this won't impact
     153:  * the system too much.
     154: */
     155:     setpriority(PRIO_PROCESS, pid, -1);
     156:     doit();
     157:     /* NOTREACHED */
     158:     }
     159:
     160: /*
     161:  * The central loop is here.  Try to read 4 accounting records at a time
     162:  * to cut the overhead down some.
     163: */
     164:
     165: doit()
     166:     {
     167:     struct  acct    abuf[4];
     168:     struct  ACCTPARM ajunk;
     169:     sigset_t    smask, omask;
     170:     int len;
     171:
     172:     while   (1)
     173:         {
     174: /*
     175:  * Should a check for 'n' being a multiple of 'sizeof struct acct' be made?
     176:  * No.   The kernel's operations are atomic and we're using SA_RESTART, either
     177:  * we get all that we asked for or we stay suspended.
     178: */
     179:         len = read(Alogfd, abuf, sizeof (abuf));
     180:         if  (len < 0)
     181:             {
     182: /*
     183:  * Shouldn't happen.  If it does then it's best to log the error and die
     184:  * rather than go into an endless loop of retrying the read.  Since SA_RESTART
     185:  * is used on the signals we will not see EINTR.
     186: */
     187:             die("doit read(%d,...): %d\n", Alogfd, errno);
     188:             }
     189: /*
     190:  * If accounting has not been disabled and an accounting file is open
     191:  * write the data out.  Probably should save the current position and
     192:  * truncate the file if the write fails.   Hold off signals so things don't
     193:  * change while writing (this makes it safe for the signal handlers to do
     194:  * more than just set a flag).
     195: */
     196:         sigemptyset(&smask);
     197:         sigaddset(&smask, SIGHUP);
     198:         sigaddset(&smask, SIGTERM);
     199:         sigaddset(&smask, SIGALRM);
     200:         if  (sigprocmask(SIG_BLOCK, &smask, &omask) < 0)
     201:             die("doit() sigprocmask(BLOCK) errno=%d\n", errno);
     202:         if  (!Disabled)
     203:             fwrite(abuf, len, 1, Acctfp);
     204:         sigprocmask(SIG_SETMASK, &omask, NULL);
     205:         }
     206:     }
     207:
     208: checkacctspace()
     209:     {
     210:     struct  statfs  fsb;
     211:     float   suspendfree, totalfree, resumefree;
     212:
     213:     if  (fstatfs(fileno(Acctfp), &fsb) < 0)
     214:         die("checkacctspace(%d) errno: %d\n", fileno(Acctfp), errno);
     215:     totalfree = (float)fsb.f_bfree;
     216:     suspendfree = ((float)fsb.f_blocks * (float)Acctparms.suspend) / 100.0;
     217:
     218:     if  (totalfree <= suspendfree)
     219:         {
     220:         if  (!Disabled)
     221:             reportit("less than %d%% freespace on %s, accounting suspended\n", Acctparms.suspend, fsb.f_mntfromname);
     222:         Disabled = 1;
     223:         return(0);
     224:         }
     225: /*
     226:  * If accounting is not disabled then just return.  If it has been disabled
     227:  * check if enough space is free to resume accounting.
     228: */
     229:     if  (!Disabled)
     230:         return(0);
     231:
     232:     resumefree = ((float)fsb.f_blocks * (float)Acctparms.resume) / 100.0;
     233:     if  (totalfree >= resumefree)
     234:         {
     235:         reportit("more than %d%% freespace on %s, accounting resumed\n",
     236:             Acctparms.resume, fsb.f_mntfromname);
     237:         Disabled = 0;
     238:         }
     239:     return(0);
     240:     }
     241:
     242: /*
     243:  * When a SIGHUP is received parse the config file.  It is safe to do this
     244:  * in the signal handler because other signals are blocked.
     245: */
     246:
     247: hupcatch()
     248:     {
     249:     struct  ACCTPARM ajunk;
     250:
     251: /*
     252:  * What to do if the config file is banged up or has wrong mode/owner...?
     253:  * Safest thing to do is log a message and exit rather than continue with
     254:  * old information or trust corrupted new information.
     255: */
     256:     if  (parseconf(&ajunk) < 0)
     257:         die("%s owner/mode/reading/parsing error", Acctdcf);
     258:     reconfig(&ajunk);
     259:     }
     260:
     261: /*
     262:  * init(8) used to turn off accounting via the old acct(2) syscall when
     263:  * the system went into single user mode on a shutdown.  Since 'acctd' is
     264:  * just another user process as far as init(8) is concerned we receive a
     265:  * SIGTERM when the system is being shutdown.  In order to capture as much
     266:  * data as possible we delay exiting for a few seconds (can't be too long
     267:  * because init(8) will SIGKILL 'hung' processes).
     268:  *
     269:  * Mark the accounting device nonblocking and read data until either
     270:  * nothing is available or we've gone thru the maximum delay.  The same
     271:  * assumption is made here as in doit() - that the reads are atomic, we
     272:  * either get all that we asked for or nothing.
     273: */
     274: terminate()
     275:     {
     276:     register int    i, cnt;
     277:     struct  acct    a;
     278:
     279:     if  (fcntl(Alogfd, F_SETFL, O_NONBLOCK) < 0)
     280:         reportit("fcntl(%d): %d\n", Alogfd, errno);
     281:     for (i = 0; Acctfp && i < 3; i++)
     282:         {
     283:         while   ((cnt = read(Alogfd, &a, sizeof (a)) > 0))
     284:             fwrite(&a, sizeof (a), 1, Acctfp);
     285:         usleep(1000000L);
     286:         }
     287:     if  (Acctfp)
     288:         fclose(Acctfp);
     289:     close(Alogfd);
     290:     exit(0);
     291:     }
     292:
     293: /*
     294:  * Parse the conf file.  The parse is _extremely_ simple minded because
     295:  * only 'accton' should be writing the file.  If manual editing is done
     296:  * be very careful not to add extra whitespace (or comments).  Sanity/range
     297:  * checking of the arguments is performed here.
     298: */
     299: parseconf(ap)
     300:     register struct ACCTPARM *ap;
     301:     {
     302:     int err = 0, count;
     303:     register FILE *fp;
     304:     char    line[256], *cp;
     305:     long    l;
     306:     struct  stat st;
     307:
     308: /*
     309:  * The conf file must be owned by root and not writeable by group or other.
     310:  * This is because the conf file contains a pathname that will be trusted
     311:  * by this program and it is running as root.
     312: */
     313:     fp = fopen(Acctdcf, "r");
     314:     if  (!fp)
     315:         return(-1);
     316:     if  (fstat(fileno(fp), &st) == 0)
     317:         {
     318:         if  ((st.st_uid != 0) || (st.st_mode & (S_IWGRP|S_IWOTH)))
     319:             {
     320:             fclose(fp);
     321:             return(-1);
     322:             }
     323:         }
     324:     bzero(ap, sizeof (*ap));
     325:     for (count = 1; fgets(line, sizeof (line), fp) && !err; count++)
     326:         {
     327:         cp = index(line, '\n');
     328:         if  (cp)
     329:             *cp = '\0';
     330:         if  (bcmp(line, "suspend=", 8) == 0)
     331:             {
     332:             l = strtol(line + 8, &cp, 10);
     333:             if  (l < 0 || l > 99 || (cp && *cp))
     334:                 {
     335:                 errline(count);
     336:                 err = -1;
     337:                 }
     338:             ap->suspend = (int)l;
     339:             }
     340:         else if (bcmp(line, "resume=", 7) == 0)
     341:             {
     342:             l = strtol(line + 7, &cp, 10);
     343:             if  (l < 0 || l > 99 || (cp && *cp))
     344:                 {
     345:                 errline(count);
     346:                 err = -1;
     347:                 }
     348:             ap->resume = (int)l;
     349:             }
     350:         else if (bcmp(line, "chkfreq=", 8) == 0)
     351:             {
     352:             l = strtol(line + 8, &cp, 10);
     353: /*
     354:  * Doesn't make sense to check more often than every 10 seconds.  Put a
     355:  * upper bound of an hour.
     356: */
     357:             if  (l < 10 || l > 3600 || (cp && *cp))
     358:                 {
     359:                 errline(count);
     360:                 err = -1;
     361:                 }
     362:             ap->chkfreq = (int)l;
     363:             }
     364:         else if (bcmp(line, "acctfile=", 9) == 0)
     365:             {
     366:             cp = line + 9;
     367:             if  (ap->acctfile)
     368:                 free(ap->acctfile);
     369:             ap->acctfile = strdup(cp);
     370:             }
     371:         else
     372: /*
     373:  * An unknown string could be the sign of a corrupted file.  Declare an error
     374:  * so we don't trust potential garbage.
     375: */
     376:             {
     377:             errline(count);
     378:             err = -1;
     379:             }
     380:         }
     381:     fclose(fp);
     382:     if  (err)
     383:         {
     384:         if  (ap->acctfile)
     385:             {
     386:             free(ap->acctfile);
     387:             ap->acctfile = NULL;
     388:             }
     389:         return(err);
     390:         }
     391: /*
     392:  * Now see which fields were not filled in and apply the defaults.  The
     393:  * 'accton' program does this but if the conf file was manually edited some
     394:  * fields may have been left out.  Basic range checking has already been done
     395:  * if the fields were present.
     396: */
     397:     if  (ap->suspend == 0)
     398:         ap->suspend = Suspend;
     399:     if  (ap->resume == 0)
     400:         ap->resume = Resume;
     401:     if  (ap->chkfreq == 0)
     402:         ap->chkfreq  = Chkfreq;
     403:     if  (ap->acctfile == NULL)
     404:         ap->acctfile = strdup(_PATH_ACCTFILE);
     405:     return(0);
     406:     }
     407:
     408: void
     409: errline(l)
     410:     {
     411:     reportit("error in line %d of %s\n", l, Acctdcf);
     412:     }
     413:
     414: /*
     415:  * This routine completes the reconfiguration of the accounting daemon.  The
     416:  * parsing and validation has been performed by parseconf() and the results
     417:  * stored in a structure (a pointer to which is passed to this routine).
     418: */
     419:
     420: reconfig(new)
     421:     struct ACCTPARM *new;
     422:     {
     423:     struct  itimerval  itmr;
     424:     int fd;
     425:
     426:     if  (Acctfp)
     427:         fclose(Acctfp);
     428:     if  (Acctparms.acctfile)
     429:         free(Acctparms.acctfile);
     430:     Acctparms = *new;
     431:
     432:     fd = open(Acctparms.acctfile, O_WRONLY | O_APPEND, 644);
     433:     if  (fd < 0)
     434:         die("open(%s,O_WRONLY|O_APPEND): %d\n", Acctparms.acctfile,
     435:             errno);
     436:     Acctfp = fdopen(fd, "a");
     437:     if  (!Acctfp)
     438:         die("fdopen(%d,a): %d\n", fd, errno);
     439:     itmr.it_interval.tv_sec = Acctparms.chkfreq;
     440:     itmr.it_interval.tv_usec = 0;
     441:     itmr.it_value.tv_sec = Acctparms.chkfreq;
     442:     itmr.it_value.tv_usec = 0;
     443:     if  (setitimer(ITIMER_REAL, &itmr, NULL) < 0)
     444:         die("setitmer: %d\n", errno);
     445:     }
     446:
     447: /*
     448:  * The logfile is opened/closed per message to conserve resources
     449:  * (file table and descriptor).  In the case of die() this isn't terribly
     450:  * important since we're about to exit anyhow ;)  For reportit() the
     451:  * messages are of such low frequency that an extra openlog/closelog
     452:  * pair isn't too much extra overhead.
     453: */
     454:
     455: void
     456: die(str, va_alist)
     457:     char    *str;
     458:     va_dcl
     459:     {
     460:     va_list ap;
     461:
     462:     openlog("acctd", LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
     463:     va_start(ap);
     464:     vsyslog(LOG_ERR, str, ap);
     465:     va_end(ap);
     466:     exit(1);
     467:     }
     468:
     469: void
     470: reportit(str, va_alist)
     471:     char    *str;
     472:     va_dcl
     473:     {
     474:     va_list ap;
     475:
     476:     openlog("acctd", LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON);
     477:     va_start(ap);
     478:     vsyslog(LOG_WARNING, str, ap);
     479:     va_end(ap);
     480:     }
     481:
     482: void
     483: usage()
     484:     {
     485:
     486:     die("Usage: %s [-f acctfile] [-s %suspend] [-r %resume] [-t chkfreq] [acctfile]", __progname);
     487:     /* NOTREACHED */
     488:     }

#### Defined functions

checkacctspace defined in line 208; used 2 times

- in line 43, 94

die defined in line 455; used 13 times

- in line 45, 61, 120, 138, 145, 187, 201, 214, 257, 434-438(2), 444, 486

doit defined in line 165; used 1 times

- in line 156

errline defined in line 408; used 5 times

- in line 44, 335, 345, 359, 377

hupcatch defined in line 247; used 2 times

- in line 43, 110

main defined in line 49; never used
parseconf defined in line 299; used 2 times

- in line 119, 256

reconfig defined in line 420; used 2 times

- in line 121, 258

reportit defined in line 469; used 5 times

- in line 45, 221, 235, 280, 411

terminate defined in line 274; used 2 times

- in line 43, 102

usage defined in line 482; used 3 times

- in line 44, 73, 81

#### Defined variables

Acctdcf defined in line 42; used 4 times

- in line 120, 257, 313, 411

Acctfile defined in line 38; never used
Acctparms defined in line 40; used 11 times

- in line 216-221(2), 232-236(2), 428-441(7)

Alogfd defined in line 39; used 9 times

- in line 132-137(3), 179, 187, 279-283(3), 289

Chkfreq defined in line 35; used 1 times

- in line 402

Debug defined in line 36; used 1 times

- in line 69

Disabled defined in line 37; used 5 times

- in line 202, 220-222(2), 229, 237

Resume defined in line 34; used 1 times

- in line 400

Suspend defined in line 33; used 1 times

- in line 398

#### Defined struct's

ACCTPARM defined in line 25; used 12 times

- in line 40(2), 56(2), 168(2), 249(2), 300(2), 421(2)

Last modified: 1999-03-31 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 2298 |