/* **********************************************************************
 * *                                                                    *
 * *   Send generated RAW product to IRIS                               *
 * *                                                                    *
 * **********************************************************************
 * File: utils/tdwr/iris_send_task.C
 *
 *                        COPYRIGHT (c) 2006  BY
 *              VAISALA INC., WESTFORD MASSACHUSETTS, U.S.A.
 * 
 * THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND  COPIED
 * ONLY  IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE
 * INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE  OR  ANY OTHER
 * COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
 * OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE  IS  HEREBY
 * TRANSFERED.
 */

#include <errno.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>  /* Required on linux 7.X */
#include <sys/stat.h>
#include <unistd.h>

#include "sigtypes.h"
#include "user_lib.h"

struct config_file
{
  struct sockaddr_in servaddr_in ;
  char iris_name[100];
  short  iris_port_num;
  char iris_dir[200];
};

static void ProcessArgs
(int argc, char ** argv, struct config_file *pConfig_a );
static void sighup_handler (int signal_a);
static void sigio_handler (int signal_a);

int iris_s;
int connected_iris = 0, pending_iris = 0;
static void Lprintf(const char *log_str);
int logging = 0;
static char log_str[200];
FILE *log_file;

int main (int argc, char ** argv)
{
  int    answer;
  int    iflag, iaddrsize;
  time_t last_time;
  char   tmp_str[200], iris_file_name[50];
  struct sockaddr *pservaddr;
  struct stat stat_buf;
  FILE   *iris_file;
  struct sigaction action;
  struct config_file Config;

  action.sa_handler = sighup_handler;
  sigemptyset( &action.sa_mask );
  action.sa_flags = 0;
  answer = sigaction( SIGHUP, &action, NULL );

  sleep(5);

  last_time = 0;  

  ProcessArgs( argc, argv, &Config );
  pservaddr = (struct sockaddr *)(void*)&Config.servaddr_in;
  iaddrsize = sizeof( Config.servaddr_in );

  sprintf(log_str, "Using IRIS server and port: %s %d at dir: %s", Config.iris_name, 
	  Config.iris_port_num, Config.iris_dir);
  Lprintf(log_str);

  sprintf(tmp_str, "rm -rf /usr/iris_data/raw/*");
  system(tmp_str);

  /****
     Enter a while forever loop.  This loop runs at a frequency of once per second.
     In this loop we first try to connect to an IRIS server if we are not already
     connected.  Then we check to see if any new iris raw files have been posted.
     If so, we read it, turn it into an IRIS user product and send it to the IRIS
     server.
  ****/

  while (1) 
  {
    /****
       One second pause to pace the loop
    ****/
    struct timeval tv1;
    tv1.tv_sec = 1;
    tv1.tv_usec = 0;
    answer = select(32, NULL, NULL, NULL, &tv1);

    /****
       If we are not connected to the IRIS server and the connection is not
       in the pending state, then try to establish the connection.  Declare 
       the connection to be pending.  We will later check to see if it is
       connected, failed or blocking.
    ****/
    if (!connected_iris && ! pending_iris)
    {
      iris_s = socket(AF_INET, SOCK_STREAM, 0);
      if (iris_s != -1)
      {
        /* Set up so that i/o is nonblocking */
        iflag = 1;
        answer = ioctl( iris_s, FIONBIO, &iflag );
        answer = connect(iris_s, pservaddr, iaddrsize);
        pending_iris = 1;
        sprintf(log_str, "Trying to connect to IRIS server %s", Config.iris_name);
        Lprintf(log_str);
      }
      else
      {
	Lprintf("Socket open failed to IRIS server.");
      }
    }

    /****
       If the pending connection has been blocked for more than 30 seconds, 
       then disconnect it.
    ****/
    if (pending_iris >= 30)
    {
      sprintf(log_str, "Connection to IRIS server %s failed after several attempts.  Will try fresh.", Config.iris_name);
      pending_iris = 0;
      connected_iris = 0;
      close(iris_s);
      iris_s = 0;
    }

    /****
       If the connection is in the pending state, then call "connect".  If connect either
       succeeds or fails with reason already connected, then the connection is good.  At 
       this point, change state to good, and setup the signal handler.  If the connection fails
       but reports in progress (currently block), then stay in state.  If it reports failed,
       then changed state from pending to down.
    ****/
    if (pending_iris)
    {
      struct timeval tv0;
      fd_set afds, bfds;

      FD_ZERO(&afds);
      FD_ZERO(&bfds);
      FD_SET(iris_s, &bfds);
      tv0.tv_sec = 0;
      tv0.tv_usec = 0;

      answer = select(32, NULL, &bfds, NULL, &tv0);
      switch (answer)
      {
        case -1: /* error */	
          sprintf(log_str, "select() returned error (%d)", errno);
          Lprintf(log_str);
        break;
        default: /* got something */
          if (FD_ISSET(iris_s, &bfds))
          {
            answer = connect(iris_s, pservaddr, iaddrsize);
            if ((answer >= 0) || (answer < 0 && errno == EISCONN))
            {
              /* Set up a signal handler for the SIGIO signal */
              action.sa_handler = sigio_handler;
              sigemptyset( &action.sa_mask );
              action.sa_flags = 0;
              answer = sigaction( SIGIO, &action, NULL );
              /* Set up so that i/o causes the SIGIO signal */
              iflag = getpid();
              answer  = ioctl( iris_s, SIOCSPGRP, &iflag );
              iflag = 1;
              answer = ioctl( iris_s, FIOASYNC, &iflag );
              iflag = 1;
              answer = ioctl( iris_s, FIONBIO, &iflag );
	      connected_iris = 1;
              pending_iris = 0;
	      sprintf(log_str, "Connected successfully to IRIS server %s.", Config.iris_name);
              Lprintf(log_str);
            }
	    else if (answer <= 0 && errno == EINPROGRESS)
	    {
              sprintf(log_str, "Still trying connection to IRIS server %s.", Config.iris_name);
              Lprintf(log_str);
	      pending_iris++;
	    }
            else
            {
              sprintf(log_str, "Connection failed to IRIS server %s with errno = %d.", 
		      Config.iris_name, errno); 
              Lprintf(log_str);
              pending_iris = 0;
              close(iris_s);
              iris_s = 0;
            }
          }
	  else
	  {
            sprintf(log_str, "Still trying connection to IRIS server %s", Config.iris_name);
            Lprintf(log_str);
	    pending_iris++;
	  }
        break;
      }
    } /* end if pending_iris */

    /****
      Using a stat call, check to see if a new raw product file exists in the "raw"
      directory.  If so, then rcp it to the IRIS server, and send the socket notification
      message.
    ****/
   
    answer = stat("/usr/iris_data/raw/", &stat_buf);
    if (stat_buf.st_mtime > last_time)
    {
      tv1.tv_sec = 1;
      tv1.tv_usec = 0;
      select(32, NULL, NULL, NULL, &tv1);
      last_time = stat_buf.st_mtime;
      sprintf(tmp_str, "ls -1 /usr/iris_data/raw/ > /tmp/iris.dat");
      system(tmp_str);
      if ((iris_file = fopen("/tmp/iris.dat", "r")) != NULL)
      {
        while (fscanf(iris_file, "%s", iris_file_name) != EOF)
        {
	  if (connected_iris)
	  {
            sprintf(tmp_str, "rcp /usr/iris_data/raw/%s operator@%s:%s",
	    	          iris_file_name, Config.iris_name, Config.iris_dir);
	    system(tmp_str);
	    sprintf(tmp_str, "FILE %s", iris_file_name);
	    send(iris_s, tmp_str, strlen(tmp_str)+1, 0);
	  }
	  sprintf(tmp_str, "rm -f /usr/iris_data/raw/%s", iris_file_name);
	  system(tmp_str);

        } /* end while fscanf produces a new line from iris file */
	fclose(iris_file);

      } /* end if fopen of iris file succeeded */

    } /* end if stat says new file */

  } /* end while(1) */
}

static void sighup_handler(int signal_a)
{
  struct sigaction action;
  action.sa_handler = sighup_handler;
  sigemptyset( &action.sa_mask );
  action.sa_flags = 0;
  sigaction( SIGHUP, &action, NULL );
}

static void sigio_handler( int signal_a )
{
  struct sigaction action;
  char data[100];
  int isize;

  isize = recv( iris_s, data, 99, 0 );
  if (isize > 0)
  {
    /* We got some valid data here.  Add terminating NULL, then process */
    data[isize] = 0;
    if (0 == strcmp( data, "NOP" ))
    {
    }
    else if (0 == strcmp( data, "EXIT" ))
    {
    }
    else
    {
      sprintf(log_str, "recv read unknown message: '%s' from IRIS server.", data );
      Lprintf(log_str);
    }
  }
  else if (isize < 0)
  {
    if (errno != EWOULDBLOCK)
    {
      sprintf(log_str, "recv error errno = %d from IRIS server.", errno);
      Lprintf(log_str);
    }
  }
  else
  {
    /* Zero size indicates that the socket has been closed at the other end */
    Lprintf ("Link broken to IRIS server - closing connection.");
    action.sa_handler = SIG_IGN;
    sigemptyset( &action.sa_mask );
    action.sa_flags = 0;
    sigaction( SIGIO, &action, NULL );
    connected_iris = 0;
    pending_iris = 0;
    close(iris_s);
    iris_s = 0;
  }
  return;
}

static void Lprintf(const char *log_str_a)
{
  struct full_time_spec Time;
  char sTimeBuf[TIMENAME_SIZE];
  if (!logging) return;

  GetTimeFull( &Time, TIME_FLG_LOCAL );

  shhmmssddmonyyyy_r( &Time.Ymds, sTimeBuf );
  fprintf(log_file, "%s %s: %s\n", sTimeBuf, Time.sRecordTZName, log_str_a);
  fflush(log_file);
  return;
}

/* ===================================================
 * Read the command line and config file
 */
static void ProcessArgs
(int argc, char ** argv, struct config_file *pConfig_a )
{
  FILE *config_file;
  char tmp_str[100], iris_port[20];
  char   tdwr_interface[10], tdwr_port[10];
  struct hostent *hostptr;
  int  answer;

  memset( pConfig_a, 0, sizeof(*pConfig_a) );
  /*
   * Process the configuration file
   */
  if (argc != 2)
    {
      printf("usage: iris_send_task <config file name>\n");
      exit(0);
    }
  
  if ((config_file = fopen(argv[1], "r")) == NULL)
    {
      printf("Can not open config file: %s\n", argv[1]);
      exit(-1);
    }
  
  answer = fscanf(config_file, "%s%s", tmp_str, log_str);
  if (answer <= 0 || strcmp(tmp_str, "LOG"))
    {
      printf("No valid LOG line!\n");
      exit(-2);
    }
  if (log_str[0] == '1') 
    {
      char sLogPath[PATHNAME_SIZE];
      strcpy( sLogPath, "/tmp/iris_send_task.log" );
      if ((log_file = fopen( sLogPath, "w")) == NULL)
	{
	  printf("Can not open log file: %s!\n", sLogPath );
	  exit(-2);
	}
      else 
	{
	  logging = 1;
	  printf("Logging to file: %s.\n", sLogPath );
	}
    }
  
  answer = fscanf(config_file, "%s%s%s", tmp_str, tdwr_interface, tdwr_port);
  if (answer <= 0 || strcmp(tmp_str, "TDWR"))
    {
      Lprintf("No valid TDWR / interface / port listed.");
      exit(-2);
    }
  
  answer = fscanf(config_file, "%s%s%s%s", tmp_str, pConfig_a->iris_name, 
		  iris_port, pConfig_a->iris_dir);
  if (answer <= 0 || strcmp(tmp_str, "IRIS"))
    {
      Lprintf("No valid IRIS server / port / dir listed.");
      exit(-2);
    }
  
  hostptr = gethostbyname(pConfig_a->iris_name);
  if (hostptr != NULL)
    {
      struct in_addr *paddress;
      pConfig_a->servaddr_in.sin_family = AF_INET;
      paddress = (struct in_addr *)hostptr->h_addr_list[0];
      pConfig_a->servaddr_in.sin_addr = *paddress;
      pConfig_a->iris_port_num = atoi(iris_port);
      pConfig_a->servaddr_in.sin_port = htons(pConfig_a->iris_port_num);
    }
  else
    {
      printf("Error getting hostname: %s\n", pConfig_a->iris_name);
      sprintf(log_str, "Error getting hostname: %s", pConfig_a->iris_name);
      Lprintf(log_str);
      exit(1);
    }
  return;
}
