/* *******************************************************************
 * *                                                                 *
 * *        Antenna Library Network Reception Pipe Process           *
 * *                                                                 *
 * ******************************************************************* 
 * File: libs/antenna/ant_netrcv.c
 *
 *                    COPYRIGHT (c) 2004, 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 <sys/socket.h>   /* Needed on old Linux, and other platforms */
#define INCLUDE_NETINET_IN_PROTOTYPES 1
#include <netinet/in.h>
#define INCLUDE_STDIO_PROTOTYPES 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "sigtypes.h"
#include "signetwork.h"
#include "user_lib.h"
#include "antenna_lib.h"
#include "antenna_int.h"

/* #define DEBUG 1 */

static UINT1 QualifyPacket
( const char *sBuffer_a, 
  char cFilter_a,
  in_addr_t LocalAddress_a );

/* ------------------------------
 * The antenna network receive process reads incoming network packets
 * and produces a virtual serial antenna/BITE stream on the requested
 * file descriptor.  It runs until getting an external request to
 * quit, or encountering a fatal internal error.
 */
MESSAGE AntNetRcvProcess
( const struct ant_net_spec *pSpec_a,
  int iFD_a,
  volatile const UINT1 *lQuitFlag_a,
  void (*AntLock_a)(int) ) /* Optional antenna library lock function */
{
  int iSocketFD ; MESSAGE istatus = SS_NORMAL ;
  struct in_addr LocalAddress ; char sLogText[120] ;

  antWriteLog( "NETRCV: Process Starting" ) ;

  iSocketFD = OpenUdpReader( pSpec_a->iPort, pSpec_a->sAddress, pSpec_a->sInterface,
			     &LocalAddress, UDP_FLG_MULTICAST ) ;
  sprintf( sLogText, "NETRCV: OpenUdpReader returned descriptor:%d", iSocketFD ) ;
  antWriteLog( sLogText ) ;

  if( iSocketFD == -1 ) {
    istatus = errno_to_message_va( errno, 1, "AntNetRcvProcess" ) ;
  }
  else {
    char sBuffer[UDP_DATA_SIZE], cFilter = 0 ; SINT4 iCount = 0 ;

    if( pSpec_a->iFlags & ANTNET_FLG_RCV ) cFilter = 'R' ;
    if( pSpec_a->iFlags & ANTNET_FLG_XMT ) cFilter = 'X' ;

    sprintf( sLogText, "NETRCV: Filter set to:%c", cFilter ) ;
    antWriteLog( sLogText ) ;

    while( ! *lQuitFlag_a ) {
      if( lReadAvailFD( iSocketFD, 50 ) ) {
	int iNetSize;
#ifdef LINUX
	ssize_t iSize;
	socklen_t iFromSize ;
#else
	int iSize, iFromSize ;
#endif  /* #ifdef LINUX */
	char sFrom[24];
 
	iFromSize = 24;
	iSize = recvfrom( iSocketFD, sBuffer, UDP_DATA_SIZE, 0,
			  (struct sockaddr*)sFrom, &iFromSize ) ;
	/* iSize = read( iSocketFD, sBuffer, UDP_DATA_SIZE ) ; */
	if( iSize > 0 ) {
	  iCount++ ;
#ifdef DEBUG
	  {
#define PRINT_SIZE (16)
	    char sPrint[PRINT_SIZE+1];
	    int iPrintSize = MIN(PRINT_SIZE,iSize);
	    strncpy_safe( sPrint, sBuffer, iPrintSize );
	    sprintf( sLogText, "NETRCV: Got:'%s'", sPrint ) ;
	    antWriteLog( sLogText ) ;
	  }
#endif  /* #ifdef DEBUG */

	  if( iSize > 16 ) {
	    char *pEnd ;
	    iNetSize = strtol( sBuffer, &pEnd, 10 ) ;
	    if( iNetSize == (iSize-16) ){
	      UINT1 lOk = QualifyPacket( sBuffer, cFilter, LocalAddress.s_addr ) ;
	      if( lOk )
		{
		  UINT4 iFromAddress;
		  write( iFD_a, sBuffer+16, iSize-16 ) ;
		  switch ( ((struct sockaddr*)sFrom)->sa_family )
		    {
#ifdef AF_INET6
		    case AF_INET6:
		      memcpy( &iFromAddress,
			      ((struct sockaddr_in6*)sFrom)->sin6_addr.s6_addr, 4 );
		      break;
#endif  /* #ifdef AF_INET6 */
		    default:
		      iFromAddress = ((struct sockaddr_in*)sFrom)->sin_addr.s_addr;
		      break;
		    }
		  if (AntLock_a) AntLock_a('l');
		  AntRegisterNetHost( iFromAddress );
		  if (AntLock_a) AntLock_a('u');
		}
	    }
	    else {
	      sprintf( sLogText, "NETRCV: Size mismatch: read:%d, header:%d, Count:%d",
		       iSize, iNetSize, iCount ) ;
	      antWriteLog( sLogText ) ;
	    }
	  } else {
	    sprintf( sLogText, "NETRCV: Small Size:%d", iSize ) ;
	    antWriteLog( sLogText ) ;
	  }
	} else {
	  char *sMessage = strerror( errno ) ;
	  sprintf( sLogText, "NETRCV: Read error '%s', Count:%d", sMessage, iCount ) ;
	  antWriteLog( sLogText ) ;
	  istatus = ANT_ERR_NETRCV ; break ;
	}
      }
    }
    close( iSocketFD ) ;
  }

  sprintf( sLogText, "NETRCV: Process Exiting, status=%d",  istatus ) ;
  antWriteLog( sLogText ) ;

  return( istatus ) ;
}

/* ------------------------------
 * Check the packet to see if it is one we want to process.
 */
static UINT1 QualifyPacket
( const char *sBuffer_a, 
  char cFilter_a,
  in_addr_t LocalAddress_a )
{
  UINT4 iTargetAddress;
  if ( sBuffer_a[4] != cFilter_a ) return FALSE;
  memcpy( &iTargetAddress, sBuffer_a+12, 4 );
  if (iTargetAddress == htonl(INADDR_ANY)) return TRUE;
 
  return (iTargetAddress == LocalAddress_a );
}

/* ------------------------------
 * Create a running subprocess that is connected to the calling parent
 * process via a single readable file descriptor.  The child process
 * ID is also returned so that it can be cleaned up later.  The parent
 * must put a kill of this PID in it's exit handler.
 */
int AntNetRcvOpen
( const struct ant_net_spec *pSpec_a, 
  SINT4 *pPID_a )
{
  int fdPair[2], iFD = -1, iPID = -1 ;
  if( 0 == pipe( fdPair ) ) {
    iPID = fork();
    if (iPID < 0 )       /* Fork error case */
      {
	close( fdPair[0] ) ; close( fdPair[1] ) ;
      } 
    else if( iPID == 0 ) /* Fork child case */
      {
	MESSAGE istatus ; UINT1 lFalse = FALSE ; close( fdPair[0] ) ;
	istatus = AntNetRcvProcess( pSpec_a, fdPair[1], &lFalse, NULL ) ;

	if( istatus == SS_NORMAL ) exit( EXIT_SUCCESS ) ;
	else                       exit( EXIT_FAILURE ) ;
      }
    else                 /* Fork parent case */
      {
	close( fdPair[1] ) ; iFD = fdPair[0] ;
      }
  }
  *pPID_a = iPID ; return( iFD ) ;
}

