/*
UNIX Daemon Server program for monitoring conntrack logging 
processes. 
Usage:		./vyatta-conntrack-logging 
                  -p <proto-name> -e <events> [-s <proto-state>]
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <error.h>
#include <syslog.h>


#define RUNNING_DIR	"/var/run/vyatta"
#define LOCK_FILE	"connlogd.lock"
#define MAX_PROCESS 17
 
// Global variables
pid_t pids[MAX_PROCESS];
char *cmds[MAX_PROCESS];
long int nbuffer[MAX_PROCESS];
int pcounter=0;
long int netlink_buffer_size = 2097152;
long int netlink_buffer_maxsize= 8388608;

// Initialise the nbuffer to 2MB
void init_nbuffer() 
{
  int i;  
  for (i=0;i<MAX_PROCESS;i++) {
    nbuffer[i]=netlink_buffer_size;
  }
  return;
}

//Signal handler for SIGHUP and SIGTERM
void signal_handler(sig)
{
  switch(sig) {
    case SIGHUP:
      openlog("log-conntrack", LOG_PID, LOG_USER);
      syslog(LOG_ALERT,"STOPPING CONNTRACK DAEMON");
      closelog();
      int i;
      for(i=0;i<pcounter;i++) {
        kill(pids[i], SIGKILL); 
      }
      exit(0);
      break;
    case SIGTERM:
      exit(0);
      break;
  }
}

//Create child process to start conntrack logger
void start_child(char *cmd, int index) 
{
  pid_t pid;
  int ret;
  
  pid=fork();
  if (pid<0) { 
    perror("Conntrack logging error:");   
    exit(1); /* fork error */
  }
  if (pid==0) {
    pids[index]=getpid();
    openlog("log-conntrack", LOG_PID, LOG_USER);
    syslog(LOG_ALERT, "STARTED PROCESS: %s", cmd);
    closelog();
    ret=system(cmd);
    if (WIFSIGNALED(ret) &&
    (WTERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT)) {
      exit(0);
    }
    else {
      exit(0);
    }   
  }
  else {
    pids[index]=pid;
  } 
}

//Daemonize the process to run in the background
void daemonize()
{
  int p,i,fptr;
  char str[10];
 
  p=fork();
  if (p<0) {
    perror("Conntrack logging error:"); 
    exit(1);
  }
  if (p>0) {
    exit(0);
  }
  /* child (daemon) continues */
  openlog("log-conntrack", LOG_PID, LOG_USER);
  syslog(LOG_ALERT,"STARTING CONNTRACK DAEMON");
  closelog();
  setsid();
  for (i=getdtablesize();i>=0;--i) 
    close(i); 
  i=open("/dev/null",O_RDWR); dup(i); dup(i);
  umask(027);
  chdir(RUNNING_DIR);
  fptr=open(LOCK_FILE,O_RDWR|O_CREAT,0640);
  if (fptr<0) 
    exit(1);
  if (lockf(fptr,F_TLOCK,0)<0)
    exit(0);
  sprintf(str,"%d\n",getpid());
  write(fptr,str,strlen(str));

  signal(SIGHUP,signal_handler);
  signal(SIGTERM,signal_handler);
}

int main(int argc, char *argv[])
{
  int other=0;
  int i;
  char *conn="conntrack -E";
  char *logger="logger -t log-conntrack -p daemon.notice";
  char *fother="grep -vE 'tcp|udp|icmp'"; 
  char cmd[1024];
  char cmd_to_run[1024]; 
  int length = 0;
  char * temp_cmd = cmd;
       
  for (i=1; i<argc; i++) {
    switch(argv[i][1]) {
      case 'p':
        if (i+1 < argc && argv[i+1][0] != '-') {
          if (strncmp(argv[i+1], "other",
          strlen(argv[i+1])) == 0) {
            other=1;
            snprintf(cmd, sizeof (cmd), "%s", conn);
            length = strlen (cmd); 
            temp_cmd = cmd + length;
            i++;
          } else if ((strncmp(argv[i+1], "tcp",
            strlen(argv[i+1])) == 0) ||
            (strncmp(argv[i+1], "udp",
            strlen(argv[i+1])) == 0) ||
            (strncmp(argv[i+1], "icmp",
            strlen(argv[i+1])) == 0)) {
              snprintf(cmd, sizeof (cmd), "%s%s%s", conn, " -p ", argv[i+1]);
              other=0; 
              length = strlen (cmd);
              temp_cmd = cmd + length;
              i++;
            }
        }
        break;
      case 'e':  
        if (i+1 < argc && argv[i+1][0] != '-') {
          if ((strncmp(argv[i+1], "NEW",
          strlen(argv[i+1])) == 0) ||
          (strncmp(argv[i+1], "UPDATES",
          strlen(argv[i+1])) == 0) ||
          (strncmp(argv[i+1], "DESTROY",
          strlen(argv[i+1])) == 0)) {
            if (other == 1) {
              snprintf(temp_cmd, sizeof (cmd) - length, "%s%s%s%s%s%s%s%s", " -e ", 
              argv[i+1], " -o id", " -b %d", " | ", fother, " | ", logger);
              cmds[pcounter] = malloc(strlen(cmd)+1);
              strcpy(cmds[pcounter],cmd); 
              pcounter++;
            } else if ((strncmp(argv[i-1], "tcp",strlen(argv[i-1]))==0) &&
              (strncmp(argv[i+1], "UPDATES",strlen(argv[i+1])) == 0)){
                snprintf(temp_cmd, sizeof (cmd) - length, "%s%s", " -e ", argv[i+1]);
            } else {
                snprintf(temp_cmd, sizeof (cmd) - length, "%s%s%s%s%s%s", " -e ", 
                argv[i+1], " -o id", " -b %d", " | ", logger);
                cmds[pcounter] = malloc(strlen(cmd)+1); 
                strcpy(cmds[pcounter],cmd);
                pcounter++;
            }
            length = strlen (cmd);
            temp_cmd = cmd + length;
            i++;
          }
        }
        break;
      case 's': 
        if (i+1 < argc && argv[i+1][0] != '-') {
          if ((strncmp(argv[i+1], "SYN_RECV",
          strlen(argv[i+1])) == 0) ||
          (strncmp(argv[i+1], "ESTABLISHED",
          strlen(argv[i+1])) == 0) ||
          (strncmp(argv[i+1], "FIN_WAIT",
          strlen(argv[i+1])) == 0) ||
          (strncmp(argv[i+1], "CLOSE_WAIT",
          strlen(argv[i+1])) == 0) ||
          (strncmp(argv[i+1], "LAST_ACK",
          strlen(argv[i+1])) == 0) ||
          (strncmp(argv[i+1], "TIME_WAIT",
          strlen(argv[i+1])) == 0)) {
            snprintf(temp_cmd, sizeof (cmd) - length, "%s%s%s%s%s%s", " --state ",
            argv[i+1], " -o id", " -b %d", " | ", logger);
            cmds[pcounter] = malloc(strlen(cmd)+1); 
            strcpy(cmds[pcounter],cmd);
            pcounter++;
            length = strlen (cmd);
            temp_cmd = cmd + length;
            i++;
          }
        }
        break;
    }
  }
    // Daemonize the connlog process. 
    daemonize();
        
    // Call to init_nbuffer  
    init_nbuffer();
 
    //Start the conntrack logging processes 
    for(i=0;i<pcounter;i++) {  
      sprintf(cmd_to_run, cmds[i], nbuffer[i]);
      start_child(cmd_to_run,i);
    }
    pid_t dead_child;
    int status; 
    while( (dead_child=wait(&status)) != -1) {
      for(i=0;i<pcounter;i++) {
        if (pids[i]==dead_child) { 
          sprintf(cmd_to_run, cmds[i], nbuffer[i]);
          openlog("log-conntrack", LOG_PID, LOG_USER);
          syslog(LOG_ALERT, "PROCESS EXITED: %s ", cmd_to_run);
          nbuffer[i] += netlink_buffer_size;
          if (nbuffer[i] <= netlink_buffer_maxsize) {
            sprintf(cmd_to_run, cmds[i], nbuffer[i]);
          } else { 
            nbuffer[i] -= netlink_buffer_size; 
            sprintf(cmd_to_run, cmds[i], nbuffer[i]);
          }
          syslog(LOG_ALERT,"RESTARTING PROCESS (Increase netlink buffer to %ld bytes)", nbuffer[i]); 
          closelog(); 
          start_child(cmd_to_run,i);
        }
      }
    }
    return 0;
}

/* EOF */