/*
 * ******************************************************
 * *	                                                *
 * *     IRIS Socket Mode Change example program        *
 * *                                                    *
 * ******************************************************
 * File: utils/examples/mode_change.C
 *
 *                 COPYRIGHT (c) 1997, 2002  BY
 *         SIGMET INCORPORATED, 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 <fcntl.h>
#include <locale.h>
#include <netdb.h>
#include <netinet/tcp.h>
#ifndef LINUX
#include <signal.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifndef LINUX
#include <string.h>
#endif
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>

#include "sigtypes.h"
#include "signetwork.h"
#include "user_lib.h"  /* Only required for liris_operator function */


/* Function prototypes used just in this file */
static int init_tcpip
  ( int iport_a );

static void run_tcpip
( const char *smode_a, /* Issue a single command if specified */
  const char *sOnly_a );

static void sigio_handler
( int signal_a );

/* Shared variables */
static char shost_c[MAXHOSTNAMELEN];
static int socket_c;

/* =====================================================
 * This is an example program showing how to send a command to IRIS
 * to change the mode.
 *
 */
int main( int iargc_a, char** sargv_a )
{
  int ierror, iarg;
  unsigned int iport;
#define ONLY_SIZE (24)
  char *sptr, sMode[16], sOnly[ONLY_SIZE];

  /* ------------------------------------------------------------------- */

  setlocale( LC_ALL, "" );

  if (!liris_operator(sig_username()))
    {
      printf( "Sorry, you must be an IRIS operator to run mode_change.\n" );
      exit(0);
    }

  iport  = PORT_NETWORK_RECEIVER;
  ierror = gethostname( shost_c, MAXHOSTNAMELEN );
  if (ierror < 0)
    {
      perror( "gethostname" );
      exit(0);
    }
  sMode[0] = 0; sOnly[0] = 0;

  for (iarg=1; iarg<iargc_a; iarg++)
    {
      if ( 0 == strncmp( sargv_a[iarg], "-h", 2 ) )
	{
	  printf( "mode_change command line options:\n" );
	  printf( "  -help      : help\n" );
	  printf( "  -mode mmmm : specify mode to switch to (default is interactive)\n" );
	  printf( "               note that a mode of '0' means enable mode switching\n" );
	  printf( "  -node hhhh : specify host name to send to (defaults to local host)\n" );
	  printf( "  -only oooo : Only change if current mode matches (pattern matching allowed)\n" );
	  printf( "  -port pppp : specify port number (default is %d)\n", PORT_NETWORK_RECEIVER );
	  exit(0);
	}
      else if ( 0 == strncmp( sargv_a[iarg], "-m", 2 ) )
	{
	  iarg++;
	  if (iarg<iargc_a)
	    {
	      /* Process the mode name */
	      strncpy( sMode, sargv_a[iarg], 16-1 );
	      sMode[16-1] = 0;
	    }
	  else
	    {
	      printf( "Missing mode after '-mode'\n" );
	      exit(1);
	    }
	}
      else if ( 0 == strncmp( sargv_a[iarg], "-n", 2 ) )
	{
	  iarg++;
	  if (iarg<iargc_a)
	    {
	      /* Process the host name */
	      strncpy( shost_c, sargv_a[iarg], MAXHOSTNAMELEN-1 );
	      shost_c[MAXHOSTNAMELEN-1] = 0;
	    }
	  else
	    {
	      printf( "Missing host name after '-node'\n" );
	      exit(1);
	    }
	}
      else if ( 0 == strncmp( sargv_a[iarg], "-o", 2 ) )
	{
	  iarg++;
	  if (iarg<iargc_a)
	    {
	      /* Process the mode name */
	      strncpy( sOnly, sargv_a[iarg], ONLY_SIZE-1 );
	      sOnly[ONLY_SIZE-1] = 0;
	    }
	  else
	    {
	      printf( "Missing mode after '-only'\n" );
	      exit(1);
	    }
	}
      else if ( 0 == strncmp( sargv_a[iarg], "-p", 2 ) )
	{
	  iarg++;
	  if (iarg<iargc_a)
	    {
	      /* Process the port number */
	      iport = strtol( sargv_a[iarg], &sptr, 10 );
	      if ((iport<MIN_USER_PORT) || (iport>65535))
		{
		  printf( "Port number %d invalid.\n", iport );
		  exit(EXIT_FAILURE);
		}
	    }
	  else
	    {
	      printf( "Missing port number after '-port'\n" );
	      exit(EXIT_FAILURE);
	    }
	}
      else
	{
	  printf( "Unrecognized command line argument '%s'\n", sargv_a[iarg] );
	  exit(1);
	}
    }

  init_tcpip( iport );

  run_tcpip( sMode, sOnly );

  exit(EXIT_SUCCESS) ;
}

/* ===========================================================
 * Initialize the socket connection.  Returns -1 if there was an error.
 */
static int init_tcpip
  ( int iport_a )
{
  int ierror, iflag, iaddrsize;
  struct sockaddr_in servaddr_in;
  struct sockaddr *pservaddr;
  struct hostent *hostptr;
  struct in_addr *paddress;


  /* Convert the server host name to an internet address:
   */
  hostptr = gethostbyname(shost_c);
  if (hostptr != NULL)
    {
      memset( &servaddr_in, 0, sizeof(servaddr_in));
      servaddr_in.sin_family = AF_INET;
      paddress = (struct in_addr *)hostptr->h_addr_list[0];
      servaddr_in.sin_addr = *paddress;
      servaddr_in.sin_port = htons(iport_a);
      
      pservaddr = (struct sockaddr *)(void*)&servaddr_in;
      iaddrsize = sizeof( servaddr_in );
    }
  else
    {
      perror( "gethostbyname" );
      return -1;
    }

  socket_c = socket(AF_INET,SOCK_STREAM,0);
  if (socket_c == -1)
    {
      perror( "socket" );
      return -1;
    }
  /* We now have a valid opened socket, it must be closed if we exit.
   * Set the TCP_NODELAY option.
   */
  iflag = TRUE;
  ierror = setsockopt( socket_c, IPPROTO_TCP, TCP_NODELAY, (char *)&iflag,
		      sizeof(iflag) );
  if (ierror < 0) perror( "setsockopt" );

  ierror = connect(socket_c, pservaddr, iaddrsize);
  if (ierror >= 0)
    {
      struct sigaction action;
      /* Set up the socket to be nonblocking asynchronous mode */
      /* Set up a signal handler for the SIGIO signal */
      action.sa_handler = sigio_handler;
      sigemptyset( &action.sa_mask );
      action.sa_flags = SA_RESTART;
      ierror = sigaction( SIGIO, &action, NULL );
      if (ierror == -1) perror( "sigaction" );
      
      /* Set up so that i/o causes the SIGIO signal */
#ifdef LINUX
      {
	pid_t iPid=getpid();
	ierror = fcntl(socket_c, F_SETOWN, iPid );
	if (ierror == -1) perror( "fcntl1" );
      }
      ierror = fcntl(socket_c, F_SETFL, FASYNC);
      if (ierror == -1) perror( "fcntl2" );
#else
      iflag = getpid();
      ierror = ioctl( socket_c, SIOCSPGRP, &iflag );
      if (ierror == -1)	perror( "ioctl1" );
      iflag = 1;
      ierror = ioctl( socket_c, FIOASYNC, &iflag );
      if (ierror == -1) perror( "ioctl2" );
#endif

      /* Set up so that i/o is nonblocking */
      iflag = 1;
      ierror = ioctl( socket_c, FIONBIO, &iflag );
      if (ierror == -1) perror( "ioctl" );
      
      return 0;
    }
  else
    {
      perror( "connect" );
    }
  /* Close the socket, if there was an error before returning */

  ierror = close( socket_c );
  if (ierror == -1) perror( "close" );

  return -1;

}
/* ===========================================
 * Run a loop sending products to the specified machine 
 */
static void run_tcpip
( const char *smode_a,   /* Issue a single command, if specified */
  const char *sOnly_a )  /* Issue a single command, if specified */
{
  char sline[80], smessage[100], *smode;
  int ilength, ierror, last_fgets_was_interrupted;

  if (smode_a[0])
    {
      /* Notify the other end of the file */
      strcpy( smessage, "MODE " );
      /* Interpret a single character "0" to mean goto automatic mode switching */
      strcat( smessage, smode_a );
      if (sOnly_a[0])
	{
	  strcat( smessage, " " );
	  strcat( smessage, sOnly_a );
	}
      ilength = strlen(smessage);
      ierror = send( socket_c, smessage, ilength+1, 0 );
      if (ierror == -1) perror( "send" );
      return;
    }

  last_fgets_was_interrupted = FALSE;
  while (TRUE)
    {
      if (!last_fgets_was_interrupted) printf( "Please enter new mode: " );
      smode = fgets( sline, 80, stdin );
      if (NULL == smode)
	{
	  last_fgets_was_interrupted = TRUE;
	  continue;
	}
      last_fgets_was_interrupted = FALSE;

      /* Trim trailing carriage return from file name */
      ilength = strlen(smode)-1;
      if (ilength == 0) continue;
      smode[ilength] = 0;

      /* Notify the other end of the file */
      strcpy( smessage, "MODE " );
      /* Interpret a single character "0" to mean goto automatic mode switching */
      if ( strcmp( smode, "0" )) strcat( smessage, smode );
      ilength = strlen(smessage);
      ierror = send( socket_c, smessage, ilength+1, 0 );
      if (ierror == -1) perror( "send" );
    }
  
  return;

}
/* ================================================================
 * Signal handler for SIGIO signal.  It just does a read, and processes
 * the result.
 */
static void sigio_handler( int signal_a )
{
  char data[100];
  int isize;

  isize = recv( socket_c, 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" ))
	{
	  printf( "Got 'EXIT' message.\n" );
	  exit(0);
	}
      else
	{
	  printf( "recv read unknown message: '%s'\n", data );
	}


    }

  else if (isize < 0)
    {
      /* We an error here, signal all errors except EWOULDBLOCK */
      if (errno != EWOULDBLOCK)
	{
	  perror( "recv" );
	  exit(0);
	}
    }

  else
    {
      /* Zero size indicates that the socket has been closed at the other end */
      printf( "Link broken\n" );
      exit(0);
    }
  return;

}

