/* -*- c -*-
 *
 * Author:      James Brister <brister@vix.com> -- berkeley-unix --
 * Start Date:  Mon, 15 Jan 1996 17:31:58 +1100
 * Project:     INN -- innfeed
 * File:        main.c
 * RCSId:       $Id: main.c,v 1.25.2.1 2000/06/30 15:15:36 kondou Exp $
 *
 * Copyright:   Copyright (c) 1996 by Internet Software Consortium
 *
 *              Permission to use, copy, modify, and distribute this
 *              software for any purpose with or without fee is hereby
 *              granted, provided that the above copyright notice and this
 *              permission notice appear in all copies.
 *
 *              THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE
 *              CONSORTIUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 *              SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *              MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET
 *              SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
 *              INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *              WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 *              WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 *              TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
 *              USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Description: Main routines for the innfeed program.
 * 
 */

#if ! defined (lint)
static const char *rcsid = "$Id: main.c,v 1.25.2.1 2000/06/30 15:15:36 kondou Exp $" ;
static void use_rcsid (const char *rid) {   /* Never called */
  use_rcsid (rcsid) ; use_rcsid (rid) ;
}
#endif

#include "innfeed.h"
#include "config.h"
#include "clibrary.h"

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <math.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <syslog.h>

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif

#ifdef HAVE_RLIMIT
# include <sys/resource.h>
#endif

#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
#else
# include <time.h>
#endif

#if defined(HAVE_UNIX_DOMAIN_SOCKETS)
# include <sys/un.h>
#endif

#include "libinn.h"
#include "storage.h"

#include "article.h"
#include "buffer.h"
#include "configfile.h"
#include "connection.h"
#include "endpoint.h"
#include "host.h"
#include "innlistener.h"
#include "misc.h"
#include "msgs.h"
#include "tape.h"

#define INHERIT 1
#define NO_INHERIT 0

/* exports */
bool talkToSelf ;
extern int debugWrites ;
bool sigFlag = false ;
const char *InputFile ;
const char *configFile = NULL ;
bool RollInputFile = false ;
const char *pidFile = NULL ;
bool useMMap = false ;
void (*gPrintInfo) (void) ;
char *dflTapeDir;

/* imports */
extern char *versionInfo ;
#if defined (sun)
extern char *optarg ;           /* needed for Solaris */
extern int optind;
#endif
extern bool genHtml ;

extern void openInputFile (void);

/* privates */
static char *logFile ;
static char *newsspool ;

static void sigemt (int sig) ;
static void sigalrm (int sig) ;
static void sigchld (int sig) ;
static void sigint (int sig) ;
static void sigquit (int sig) ;
static void sighup (int sig) ;
static void sigterm (int sig) ;
static void sigusr (int sig) ;
static void usage (int) ;
static void gprintinfo (void) ;
static void openLogFile (void) ;
static void writePidFile (void) ;
static int mainOptionsProcess (void *data) ;
static int mainConfigLoadCbk (void *data) ;
static void mainCleanup (void) ;

static char *bopt = NULL ;
static char *aopt = NULL ;
static char *popt = NULL ;
static bool Mopt = false ;
static bool Zopt = false ;
static bool Dopt = false ;
static int debugLevel = 0 ;
static u_int initialSleep = 2 ;
static char *sopt = NULL ;
static char *lopt = NULL ;
static bool eopt = false ;
static int elimit = 0 ;

int main (int argc, char **argv)
{
  EndPoint ep ;
  InnListener listener ;
  int optVal, fd, rval ;
  const char *subProgram = NULL ;
  bool seenV = false ;
  bool dynamicPeers = false ;
  time_t now = theTime() ;
  char dateString [30] ;
  char *copt = NULL ;
  char *debugFile;
  bool checkConfig = false ;
  struct rlimit rl;
  bool val;


  strcpy (dateString,ctime(&now)) ;
  dateString [24] = '\0' ;

  if ((program = strrchr (argv [0],'/')) == NULL)
    program = argv [0] ;
  else
    program++ ;

  gPrintInfo = gprintinfo ;

  openlog (program,(int)(L_OPENLOG_FLAGS|LOG_PID),LOG_INN_PROG) ;
  if (ReadInnConf() < 0) {
      syslog(LOG_ERR, "cant read inn.conf\n");
      exit(1);
  }

#if defined (HAVE_MMAP)
  useMMap = true ;
#else
  useMMap = false ;
#endif

#define OPT_STRING "a:b:c:Cd:e:hl:mMo:p:S:s:vxyz"

  while ((optVal = getopt (argc,argv,OPT_STRING)) != EOF)
    {
      switch (optVal) 
        {
          case 'a':
            aopt = optarg ;
            break ;

          case 'b':
            if ( !isDirectory (optarg) )
              logAndExit (1,"Not a directory: %s\n",optarg) ;
            bopt = optarg ;
            break ;

          case 'C':
            checkConfig = true ;
            break ;

          case 'c':
            copt = optarg ;
            break ;

          case 'd':
            loggingLevel = atoi (optarg) ;
            debugLevel = loggingLevel ;
            Dopt = true ;
            break ;

          case 'e':
            eopt = true ;
            elimit = atoi (optarg) ;
            if (elimit <= 0)
              {
                fprintf (stderr,"Illegal value for -e option\n") ;
                usage (1) ;
              }
            break ;

          case 'h':
            usage (0) ;

          case 'l':
            lopt = optarg ;
            break ;

          case 'M':
            Mopt = true ;
            useMMap = false ;
            break ;

          case 'm':
            artLogMissingArticles (true) ;
            break ;

          case 'o':
            artSetMaxBytesInUse (atoi (optarg)) ;
            break ;

          case 'p':
            popt = optarg ;
            break ;

          case 's':
            subProgram = optarg ;
            break ;

          case 'S':
            sopt = optarg ;
            break ;

          case 'v':
            seenV = true ;
            break ;

          case 'x':
            talkToSelf = true ;
            break ;

          case 'y':
            dynamicPeers = true ;
            break ;

          case 'z':
            Zopt = true ;
            break ;

          default:
            usage (1) ;	
        }
    }

  argc -= optind;
  argv += optind;

  if (argc > 1)
    usage (1) ;
  else if (argc == 1)
    InputFile = *argv;

  if (seenV)
    {
      warn ("%s version: %s\n",program, versionInfo) ;
      exit (0) ;
    }

  /* make sure we have valid fds 0, 1 & 2 so it is not taken by
    something else, probably openlog().  fd 0 will be freopen()ed on the
    inputFile, the subProgram, or ourself.  fd 1 and fd 2 will
    be freopen()ed on the log file (or will stay pointed at /dev/null).
    
    without doing this, if the descriptors were closed then the
    freopen calls on some systems (like BSDI 2.1) will really close
    whatever has aquired the stdio descriptors, such as the socket
    to syslogd.
    
    XXX possible problems:  what if fd 0 is closed but no inputFile,
    XXX subProgram or talkToSelf is true?  it will not be freopen()ed, so
    XXX innfeed won't have any fresh data (besides, fd 0 is only writable
    XXX here).  perhaps a warning should be issued.
    */
  do
    {
      fd = open("/dev/null", O_WRONLY);
      switch (fd)
        {
          case -1:
            logAndExit (1,"open(\"/dev/null\", O_WRONLY): %s",
                        strerror (errno));
            break;
          case 0:
          case 1:
          case 2:
            /* good, we saved an fd from being trounced */
            break;
          default:
            close(fd);
        }
    } while (fd < 2);

  if ( !checkConfig ) 
    {
      syslog (LOG_NOTICE,STARTING_PROGRAM,versionInfo,dateString) ;
    }

  val = TRUE;
  if (!SMsetup(SM_PREOPEN, (void *)&val)) {
      syslog(LOG_ERR, "cant setup the storage subsystem\n");
      exit(1);
  }
  if (!SMinit()) {
      d_printf(0, "Storage manager initialization failed\n");
      syslog(LOG_ERR, "Storage manager initialization failed\n");
      exit(1);
  }

  if (subProgram == NULL && talkToSelf == false)
    {
      struct stat buf ;

      if (fstat (0,&buf) < 0)
        logAndExit (1,FSTAT_FAILURE,"stdin") ;
      else if (S_ISREG (buf.st_mode))
        InputFile = "";
    }

  /*
   * set up the config file name and then read the file in. Order is important.
   */
  configAddLoadCallback (mainOptionsProcess,(checkConfig ? stderr : NULL)) ;
  configAddLoadCallback (tapeConfigLoadCbk,(checkConfig ? stderr : NULL)) ;

  configAddLoadCallback (endpointConfigLoadCbk,(checkConfig ? stderr : NULL));
  configAddLoadCallback (hostConfigLoadCbk,(checkConfig ? stderr : NULL)) ;
  configAddLoadCallback (cxnConfigLoadCbk,(checkConfig ? stderr : NULL)) ;
  configAddLoadCallback (mainConfigLoadCbk,(checkConfig ? stderr : NULL)) ;
  configAddLoadCallback (listenerConfigLoadCbk,(checkConfig ? stderr : NULL));

  if (copt != NULL && *copt == '\0')
    {
      logOrPrint (LOG_CRIT,(checkConfig ? stderr : NULL),
                  "Empty pathname for ``-c'' option") ;
      exit (1) ;
    }
  configFile = buildFilename(innconf->pathetc, copt ? copt : CONFIG_FILE);
  dflTapeDir = buildFilename(innconf->pathspool, TAPE_DIRECTORY);

  rval = readConfig (configFile,(checkConfig ? stderr : NULL),
                     checkConfig,loggingLevel > 0);

  if (subProgram != NULL && (talkToSelf == true || InputFile))
    {
      d_printf (0,"Cannot specify '-s' with '-x' or an input file\n") ;
      syslog (LOG_ERR,"Incorrect arguments: '-s' with '-x' or an input file\n");
      usage (1) ;
    }

  if (checkConfig)
    {
      if (!rval)
        fprintf (stderr,"config loading failed.\n") ;
      else
        fprintf (stderr,"config loading succeeded.\n") ;
      exit (1) ;
    }
  else if (!rval)
    exit (1) ;

  debugFile = buildFilename (innconf->pathlog, DEBUG_FILE);
  if (loggingLevel == 0 && fileExistsP (debugFile))
    loggingLevel = 1 ;

  if (logFile == NULL && ! isatty (fileno (stderr)))
    logFile = buildFilename (innconf->pathlog, LOG_FILE) ;

  if (logFile)
    openLogFile () ;

  openfds = 4 ;                 /* stdin, stdout, stderr and syslog */

  writePidFile ();

  if (subProgram != NULL)
    {
      int fds [2] ;
      int pid ;

      if (pipe (fds) < 0)
        {
          syslog (LOG_CRIT,PIPE_FAILURE) ;
          exit (1) ;
        }

      if ((pid = fork ()) < 0)
        {
          syslog (LOG_CRIT,FORK_FAILURE) ;
          exit (1) ;
        }
      else if (pid == 0)
        {                       /* child */
          close (fds[0]) ;
          close (0) ;
          close (1) ;
          close (2) ;
          dup2 (fds[1],1) ;
          dup2 (fds[1],2) ;
          execlp ("sh","sh", "-c", subProgram, NULL) ;
          perror ("execlp") ;
          exit (1) ;
        }
      else
        {                       /* parent */
          close (0) ;
          dup2 (fds[0],0) ;
          close (fds[1]) ;
          xsignal(SIGCHLD,sigchld) ;
          openfds++ ;
        }
    }
  else  if (talkToSelf)
    {
        /* We're not really getting information from innd or a subprogram,
           but are just processing backlog files. We set up a pipe to ourself
           that we never write to, to simulate an idle innd. */
      int pipefds [2] ;

      if (pipe (pipefds) != 0)
        {
          syslog (LOG_ERR,PIPE_FAILURE) ;
          exit (1) ;
        }

      close (0) ;
      dup2 (pipefds [0], 0) ;

      openfds++ ;
      openfds++ ;
    }

  if (chdir (newsspool) != 0)
    {
      syslog (LOG_ERR,CD_FAILED,newsspool) ;
      exit (1) ;
    }

    /* hook up the endpoint to the source of new article information (usually
       innd). */
  ep = newEndPoint (0) ;        /* fd 0, i.e. stdin */

    /* now arrange for this endpoint to always be the first one checked for
       possible activity. */
  setMainEndPoint (ep) ;

  listener = newListener (ep, talkToSelf,dynamicPeers) ;
  mainListener = listener ;

  sleep (initialSleep) ;


#if defined(HAVE_RLIMIT) && defined(RLIMIT_NOFILE)
  /* now lower maximum open file limit to match what select(2) can handle. */
  if (innconf->rlimitnofile >= 0) {
    if (getrlimit(RLIMIT_NOFILE,&rl) != 0)
      syslog (LOG_ERR,GETRLIM_FAILED) ;
    else
      {
#if defined (FD_SETSIZE)
	u_int fd_max = FD_SETSIZE ;
#else
	u_int fd_max = sizeof (fd_set) * CHAR_BIT ;
#endif
	if (innconf->rlimitnofile < fd_max)
	  fd_max = innconf->rlimitnofile;

	if (rl.rlim_max > fd_max)
	  {
	    rl.rlim_max = rl.rlim_cur = fd_max ;
	    if (setrlimit (RLIMIT_NOFILE,&rl) != 0)
	      syslog (LOG_ERR,SETRLIM_FAILED,(long)fd_max);
	  }
      }
  }
#endif /* HAVE_RLIMIT && RLIMIT_NOFILE */
      
  
  configHosts (talkToSelf) ;

  if (InputFile && *InputFile) {
    openInputFile () ;
  }

  /* handle signal to shutdown */
  setSigHandler (SIGTERM,sigterm) ;
  setSigHandler (SIGQUIT,sigquit) ;

  /* handle signal to reload config */
  setSigHandler (SIGHUP,sighup) ;

  /* handle signal to print snapshot. */
  setSigHandler (SIGINT,sigint) ;

  /* handle signal to roll input file */
  setSigHandler (SIGALRM,sigalrm) ;

  /* handle signal to flush all the backlog files */
  setSigHandler (SIGCHLD,sigemt) ;

  /* we can increment and decrement logging levels by sending SIGUSR{1,2} */
  setSigHandler (SIGUSR1,sigusr) ;
  setSigHandler (SIGUSR2,sigusr) ;

  atexit (mainCleanup) ;
  
  Run () ;

  exit (0) ;
}

static void usage (int val)
{
  fprintf (stderr,"usage: %s [ options ] [ file ]\n\n",
           program) ;
  fprintf (stderr,"Version: %s\n\n",versionInfo) ;
  fprintf (stderr,"Config file: %s\n",CONFIG_FILE) ;
  fprintf (stderr,"Backlog directory: %s/%s\n", innconf->pathspool, TAPE_DIRECTORY) ;
  fprintf (stderr,"\nLegal options are:\n") ;
  fprintf (stderr,"\t-a dir      Use the given directory as the top of the article spool\n") ;

  fprintf (stderr,"\t-b dir      Use the given directory as the the storage\n");
  fprintf (stderr,"\t            place for backlog files and lock files.\n");

  fprintf (stderr,"\t-c file     Use the given file as the config file instead of the\n");
  fprintf (stderr,"\t            default of %s\n",CONFIG_FILE);

  fprintf (stderr,"\t-C          Check the config file and then exit.\n") ;
  fprintf (stderr,"\t-d num      set the logging level to num (an integer).\n");
  fprintf (stderr,"\t            Larger value means more logging. 0 means no\n");
  fprintf (stderr,"\t            logging. The default is 0\n");

  fprintf (stderr,"\t-e bytes    Keep the output backlog files to no bigger\n");
  fprintf (stderr,"\t            than %.2f times this number\n",LIMIT_FUDGE);

  fprintf (stderr,"\t-h          print this message\n");

  fprintf (stderr,"\t-l file     redirect stderr and stdout to the given file.\n");
  fprintf (stderr,"\t            When run under INN they normally are redirected to\n");
  fprintf (stderr,"\t            /dev/null. This is needed if using '-d'.\n");

  fprintf (stderr,"\t-m          Log information on all missing articles\n");

  fprintf (stderr,"\t-M          Turn *off* use of mmap\n") ;
#if ! defined (HAVE_MMAP)
  fprintf (stderr,"\t            (a no-op as this excutable has been built without mmap support\n") ;
#endif

  fprintf (stderr,"\t-p file     Write the process id to the given file\n") ;
  fprintf (stderr,"\t            instead of the default of %s\n",PID_FILE);
  fprintf (stderr,"\t            A relative path is relative to %s\n", innconf->pathrun) ;

  fprintf (stderr,"\t-s command  run the given command in a subprocess and use\n");
  fprintf (stderr,"\t            its output as article information instead of\n");
  fprintf (stderr,"\t            running under innd\n");

  fprintf (stderr,"\t-S file     Use the give filename instead of innfeed.status\n") ;
  fprintf (stderr,"\t            relative pathnames start from %s\n", innconf->pathlog) ;

  fprintf (stderr,"\t-v          print version information\n");

  fprintf (stderr,"\t-x          Do not read any article information off stdin,\n");
  fprintf (stderr,"\t            but simply process backlog files and then exit\n");
  fprintf (stderr,"\t            when done\n");

  fprintf (stderr,"\t-y          Add peers dynamically. If an unrecognized peername\n");
  fprintf (stderr,"\t            is received from innd, then it is presumed to also\n");
  fprintf (stderr,"\t            be the ip name and a new peer binding is set up\n");

  fprintf (stderr,"\t-z          have each of the connections issue their own stats\n");
  fprintf (stderr,"\t            whenever they close, or whenever their controller\n");
  fprintf (stderr,"\t            issues its own stats\n");

  exit (val) ;
}

static void sigterm (int sig)
{
  (void) sig ;
  
  syslog(LOG_NOTICE, SHUTDOWN_SIGNAL);
  shutDown (mainListener) ;
}

static void sigquit (int sig)
{
  (void) sig ;
  
  sigterm (0) ;
}

static void sigint (int sig)
{
  (void) sig ;

  gprintinfo () ;
}

static void sighup (int sig)
{
  (void) sig ;
  
  syslog(LOG_NOTICE, CONFIG_RELOAD, configFile);

  if (!readConfig (configFile,NULL,false,loggingLevel > 0))
    {
      syslog (LOG_ERR,PARSE_FAILURE) ;
      exit (1) ;
    }

  configHosts (talkToSelf) ;
}

static void sigemt (int sig)
{
  (void) sig ;

  gFlushTapes () ;
}

static void sigalrm (int sig)
{
  (void) sig ;
  
  if (InputFile == NULL)
    syslog (LOG_ERR,IGNORE_SIGALRM) ;
  else 
    {
      RollInputFile = true;
      syslog(LOG_NOTICE, "ME preparing to roll %s", InputFile);
    }
}

static void sigchld (int sig)
{

  (void) sig ;                  /* keep lint happy */

#if 0
  wait (&status) ;              /* we don't care */
#endif

  xsignal (sig,sigchld) ;
}

  /* SIGUSR1 increments logging level. SIGUSR2 decrements. */
static void sigusr (int sig)
{
  if (sig == SIGUSR1) {
    syslog(LOG_NOTICE, INCR_LOGLEVEL, loggingLevel);
    loggingLevel++ ;
  } else if (sig == SIGUSR2 && loggingLevel > 0) {
    syslog(LOG_NOTICE, DECR_LOGLEVEL, loggingLevel);
    loggingLevel-- ;
  }    
}

static void openLogFile (void)
{
  FILE *fpr ;

  if (logFile)
    {
      fpr = freopen (logFile,"a",stdout) ;
      if (fpr != stdout)
        logAndExit (1,"freopen (%s, \"a\", stdout): %s",
                    logFile, strerror (errno)) ;
      
      fpr = freopen (logFile,"a",stderr) ;
      if (fpr != stderr)
        logAndExit (1,"freopen (%s, \"a\", stderr): %s",
                    logFile, strerror (errno)) ;
      
#if defined (HAVE_SETBUFFER)
      setbuffer (stdout, NULL, 0) ;
      setbuffer (stderr, NULL, 0) ;
#else
      setbuf (stdout, NULL) ;
      setbuf (stderr, NULL) ;
#endif
    }
}

static void writePidFile (void)
{
  FILE *F;
  int pid;

  if (pidFile == NULL)
    logAndExit (1,"NULL pidFile\n") ;

  /* Record our PID. */
  pid = getpid();
  if ((F = fopen(pidFile, "w")) == NULL)
    {
      syslog(LOG_ERR, "ME cant fopen %s %m", pidFile);
    }
  else
    {
      if (fprintf(F, "%ld\n", (long)pid) == EOF || ferror(F))
	{
	  syslog(LOG_ERR, "ME cant fprintf %s %m", pidFile);
        }
      if (fclose(F) == EOF)
	{
	  syslog(LOG_ERR, "ME cant fclose %s %m", pidFile);
        }
      if (chmod(pidFile, 0664) < 0)
	{
	  syslog(LOG_ERR, "ME cant chmod %s %m", pidFile);
        }
    }
}

static void gprintinfo (void)
{
  char *snapshotFile;
  FILE *fp;
  time_t now = theTime() ;

  snapshotFile = buildFilename(innconf->pathlog, SNAPSHOT_FILE);
  fp = fopen (snapshotFile,"a") ;
  if (fp == NULL)
    {
      syslog (LOG_ERR,FOPEN_FAILURE,snapshotFile) ;
      free(snapshotFile);
      return ;
    }
  free(snapshotFile);

#if defined (HAVE_SETBUFFER)
  setbuffer (fp, NULL, 0) ;
#else
  setbuf (fp, NULL) ;
#endif

  fprintf (fp,"----------------------------System snaphot taken at: %s\n",
           ctime (&now)) ;
  gPrintListenerInfo (fp,0) ;
  fprintf (fp,"\n\n\n\n") ;
  gPrintHostInfo (fp,0) ;
  fprintf (fp,"\n\n\n\n") ;
  gPrintCxnInfo (fp,0) ;
  fprintf (fp,"\n\n\n\n") ;
  gPrintArticleInfo (fp,0) ;
  fprintf (fp,"\n\n\n\n") ;
  gPrintBufferInfo (fp,0) ;
  fprintf (fp,"\n\n\n\n") ;
  fclose (fp) ;
}

/* called after the config file is loaded and after the config data has
  been updated with command line options. */
static int mainConfigLoadCbk (void *data)
{
  FILE *fp = (FILE *) data ;
  char *p ;
  long ival ;
  int bval ;

  if (getString (topScope,"news-spool", &p,NO_INHERIT))
    {
      if ( !isDirectory (p) && isDirectory (innconf->patharticles) )
        {
          logOrPrint (LOG_WARNING,fp,BADSPOOL_CHANGE,p,innconf->patharticles) ;
          p = strdup (innconf->patharticles) ;
        }
      else if (!isDirectory (p))
        logAndExit (1,"Bad spool directories: %s, %s\n",p,innconf->patharticles) ;
    }
  else if (!isDirectory (innconf->patharticles))
    logAndExit (1,SPOOL_NODEF,innconf->patharticles);
  else
    p = strdup (innconf->patharticles) ;
  newsspool = p ;

  /***************************************************/
  
  if (getString (topScope,"input-file",&p,NO_INHERIT))
    {
      if (*p != '\0')
	InputFile = buildFilename (getTapeDirectory(),p) ;
      else
	InputFile = "" ;
      free (p) ;
    }
  
  if (getString (topScope,"pid-file",&p,NO_INHERIT))
    {
      pidFile = buildFilename (innconf->pathrun,p) ;
      free (p) ;
    }
  else
    pidFile = buildFilename (innconf->pathrun,PID_FILE) ;
  
  if (getInteger (topScope,"debug-level",&ival,NO_INHERIT))
    loggingLevel = (u_int) ival ;
  
  
  if (getInteger (topScope,"initial-sleep",&ival,NO_INHERIT))
    initialSleep = (u_int) ival ;
  
  
  if (getBool (topScope,"use-mmap",&bval,NO_INHERIT))
    useMMap = (bval ? true : false) ;

  
  if (getString (topScope,"log-file",&p,NO_INHERIT))
    {
      logFile = buildFilename (innconf->pathlog,p) ;
      FREE (p) ;
    }
  

  return 1 ;
}

/*
 * called after config file is loaded but before other callbacks, so we
 * can adjust config file values from options. They will be validated in the
 * second callback.
 */
static int mainOptionsProcess (void *data)
{
  value *v ;

  (void) data ;
  
  if (bopt != NULL)
    {
      if ((v = findValue (topScope,"backlog-directory",NO_INHERIT)) != NULL) 
        {
          FREE (v->v.charp_val) ;
          v->v.charp_val = strdup (bopt) ;
        }
      else
        addString (topScope,"backlog-directory",strdup (bopt)) ;
    }

  if (aopt != NULL)
    {
      if ((v = findValue (topScope,"news-spool",NO_INHERIT)) != NULL)
        {
          FREE (v->v.charp_val) ;
          v->v.charp_val = strdup (aopt) ;
        }
      else
        addString (topScope,"news-spool",strdup (aopt)) ;
    }

  if (sopt != NULL)
    {
      if ((v = findValue (topScope,"status-file",NO_INHERIT)) != NULL)
        {
          FREE (v->v.charp_val) ;
          v->v.charp_val = strdup (sopt) ;
        }
      else
        addString (topScope,"status-file",strdup (sopt)) ;
    }


  if (Dopt)
    {
      if ((v = findValue (topScope,"debug-level",NO_INHERIT)) != NULL)
        v->v.int_val = debugLevel ;
      else
        addInteger (topScope,"debug-level",debugLevel) ;
    }

  
  if (eopt || talkToSelf)
    {
      if (talkToSelf)
        elimit = 0 ;
      
      if ((v = findValue (topScope,"backlog-limit",NO_INHERIT)) != NULL)
        v->v.int_val = elimit ;
      else
        addInteger (topScope,"backlog-limit",elimit) ;
    }

  
  if (Mopt)
    {
      if ((v = findValue (topScope,"use-mmap",NO_INHERIT)) != NULL)
        v->v.bool_val = 0 ;
      else
        addBoolean (topScope,"use-mmap",0) ;
    }
  

  if (popt != NULL)
    {
      if ((v = findValue (topScope,"pid-file",NO_INHERIT)) != NULL)
        {
          FREE (v->v.charp_val) ;
          v->v.charp_val = strdup (popt) ;
        }
      else
        addString (topScope,"pid-file",strdup (popt)) ;
    }

  if (Zopt)
    {
      if ((v = findValue (topScope,"connection-stats",NO_INHERIT)) != NULL)
        v->v.bool_val = 1 ;
      else
        addBoolean (topScope,"connection-stats",1) ;
    }

  if (lopt != NULL)
    {
      if ((v = findValue (topScope,"log-file",NO_INHERIT)) != NULL)
        {
          FREE (v->v.charp_val) ;
          v->v.charp_val = strdup (lopt) ;
        }
      else
        addString (topScope,"log-file",strdup (lopt)) ;
    }

  if (InputFile != NULL)
    {
      if ((v = findValue (topScope,"input-file",NO_INHERIT)) != NULL)
        {
          FREE (v->v.charp_val) ;
          v->v.charp_val = strdup (InputFile) ;
        }
      else
        addString (topScope,"input-file",strdup (InputFile)) ;
    }

  return 1 ;
}



static void mainCleanup (void)
{
  FREE (configFile) ;
  FREE (pidFile) ;
  FREE (logFile) ;
  FREE (newsspool) ;
}


void mainLogStatus (FILE *fp)
{
  fprintf (fp,"%sGlobal configuration parameters:%s\n",
           genHtml ? "<H2>" : "", genHtml ? "</H2>" : "") ;
  fprintf (fp,"          Mode: ") ;
  if (InputFile != NULL)
    fprintf (fp,"Funnel file") ;
  else if (talkToSelf)
    fprintf (fp,"Batch") ;
  else
    fprintf (fp,"Channel") ;
  if (InputFile != NULL)
    fprintf (fp,"   (%s)",(*InputFile == '\0' ? "stdin" : InputFile)) ;
  fprintf (fp,"\n") ;
  fprintf (fp,"    News spool: %s\n",newsspool) ;
  fprintf (fp,"      Pid file: %s\n",pidFile) ;
  fprintf (fp,"      Log file: %s\n",(logFile == NULL ? "(none)" : logFile));
  fprintf (fp,"   Debug level: %2ld                Mmap: %s\n",
           (long)loggingLevel,boolToString(useMMap)) ;
  fprintf (fp,"\n") ;
}
