/* 
logpp (Log PreProcessor) 0.16 - logpp.c
Copyright (C) 2006-2008 Risto Vaarandi

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "common.h"
#include "logpp.h"

/* --- Global variables that are set at command line --- */

/* block size in bytes for read/write operations (defaults to 8192) */
size_t BLOCKSIZE = 8192;

/* "run as daemon" flag (default behavior is "nodaemon") */
char DAEMON = 0;

/* facility for syslog-logging (defaults to LOG_USER) */
int FACILITY = LOG_USER;

/* input buffer size in lines for each input file (defaults to 10) */
size_t IBUFSIZE = 10;

/* logging level for logpp's own syslog messages (defaults to LOG_INFO) */
int LOGLEVEL = LOG_INFO;

/* pointer to pid-file name (default is 0) */
char *PIDFILE = 0;

/* reopen interval for closed input/output files (defaults to "no reopen") */
time_t REOPENINT = 0;

/* sleep interval if no data were read from inputs (defaults to 1 second) */
long SLEEPSEC = 1;
long SLEEPUSEC = 0;

/* tag (or "program name") for syslog-logging (defaults to "logpp") */
char TAG[TAGLEN] = "logpp";

/* --- Other global variables --- */

/* List of configuration files and its size */
char **CFLIST = 0;
int CFLSIZE = 0;

/* List of filter definitions */
struct filter *FILTERLIST = 0;

/* List of output definitions */
struct output *OUTPUTLIST = 0;

/* List of flow definitions */
struct flow *FLOWLIST = 0;

/* List of input definitions */
struct input *INPUTLIST = 0;

/* Buffer that holds input data during matching and its size
   (the buffer needs (BLOCKSIZE+1)*IBUFSIZE bytes) */
char *MATCHBUF = 0;
size_t MBUFSIZE = 0;

/* Buffer that holds output data during writes and its size 
   (BLOCKSIZE+1 bytes should be allocated for the buffer) */
char *OUTBUF = 0;
size_t OBUFSIZE = 0;

/* Signal flags */
char GOT_SIGHUP = 0;
char GOT_SIGTERM = 0;
char GOT_SIGUSR1 = 0;
char GOT_SIGUSR2 = 0;
char GOT_SIGCHLD = 0;

/* --- Main program --- */

int main(int argc, char **argv)
{
  struct timeval timeout;
  int ind, i, flags;

  /* parse command line options */
  ind = get_options(argc, argv); 

  /* open connection to the system logger (LOG_PID is used for including
     the process ID in log messages, and LOG_NOWAIT means that if future
     calls to syslog() involve the creation of child processes, syslog()
     does not have to call wait() for the processes, because the program
     itself will handle all terminated child processes) */
  flags = LOG_PID | LOG_NOWAIT;
  openlog(TAG, flags, FACILITY);

  /* set up the array of config file names */
  CFLSIZE = argc - ind;
  if (!CFLSIZE) {
    log_msg(LOG_CRIT, "No configuration files specified, exiting");
    exit(1);
  }
  CFLIST = (char **) my_malloc(sizeof(char *) * CFLSIZE);
  for (i = 0; i < CFLSIZE; ++i) {
    CFLIST[i] = 
      (char *) my_malloc(sizeof(char) * (strlen(argv[ind + i]) + 1));
    strcpy(CFLIST[i], argv[ind + i]);
  }

  /* if requested, daemonize the process */
  if (DAEMON && !daemonize()) {
    log_msg(LOG_CRIT, "Can't become a daemon, exiting");
    exit(1);
  }

  /* if requested, store the process id to a file 
     (must be done after daemonize()) */
  if (PIDFILE && !store_pid()) {
    log_msg(LOG_CRIT, "Can't create the pid file, exiting");
    exit(1);
  }

  /* set signal handlers (must be done after daemonize() which 
     sets a handler for SIGHUP) */
  if (!set_sighandlers()) {
    log_msg(LOG_CRIT, "Can't set signal handlers, exiting");
    exit(1);
  }

  /* create buffers for input matching and output - the buffers must
     accomodate data block(s) with terminating zero(s) */
  MBUFSIZE = (BLOCKSIZE + 1) * IBUFSIZE;
  MATCHBUF = (char *) my_malloc(sizeof(char) * MBUFSIZE);
  OBUFSIZE = BLOCKSIZE + 1;
  OUTBUF = (char *) my_malloc(sizeof(char) * OBUFSIZE);

  /* load configuration and create config lists */
  create_config_lists();

  /* open inputs and outputs */
  open_inputs();
  open_outputs();

  for (;;) {
    /* read data from inputs and process it, and if no data were acquired, 
       sleep for 'timeout' seconds (select() may change 'timeout', so 
       'timeout' is set before each call to select()) */
    if (!read_and_process()) {
      timeout.tv_sec = SLEEPSEC;
      timeout.tv_usec = SLEEPUSEC;
      select(0, 0, 0, 0, &timeout);
    }
    /* if the process has received SIGHUP, it reinitializes itself */
    if (GOT_SIGHUP) {
      log_msg(LOG_NOTICE, "Got SIGHUP, reinitializing");
      close_inputs();
      close_outputs();
      free_config_lists();
      closelog();
      openlog(TAG, flags, FACILITY);
      create_config_lists();
      open_inputs();
      open_outputs();
      GOT_SIGHUP = 0;
    }
    /* if the process has received SIGUSR1, it dumps config lists */
    if (GOT_SIGUSR1) {
      log_msg(LOG_NOTICE, "Got SIGUSR1, dumping config lists");
      dump_config_lists();
      GOT_SIGUSR1 = 0;
    }
    /* if the process has received SIGUSR2, it reopens its outputs and
       the connection to the system logger */
    if (GOT_SIGUSR2) {
      log_msg(LOG_NOTICE, "Got SIGUSR2, reopening outputs");
      close_outputs();
      closelog();
      openlog(TAG, flags, FACILITY);
      open_outputs();
      GOT_SIGUSR2 = 0;
    }
    /* if the process has received SIGCHLD, it collects the exit statuses
       of terminated child processes (note that on some platforms calls
       to syslog() create child processes that are also handled here) -
       waitpid returns -1 if there are no more such processes and could
       return 0 on some platforms if running child processes exist */
    if (GOT_SIGCHLD) {
      while (waitpid(-1, 0, WNOHANG) > 0);
      GOT_SIGCHLD = 0;
    }
    /* if the process has received SIGTERM, it exits the main loop */
    if (GOT_SIGTERM) {
      log_msg(LOG_NOTICE, "Got SIGTERM, exiting");
      break;
    }
  }

  /* close inputs and outputs */
  close_inputs();
  close_outputs();

  /* free config lists and destroy the array of config file names */
  free_config_lists();
  for (i = 0; i < CFLSIZE; ++i) my_free(CFLIST[i]);
  my_free(CFLIST);

  /* free buffers */
  my_free(OUTBUF);
  my_free(MATCHBUF);

  /* close connection to the system logger and exit */
  closelog();
  exit(0);
}
