
//
// Trust Design LLC : SECS/HSMS Communication library
//
// (c) Copyright Trust Design LLC.  2010-2021.  All rights reserved.
//

// =============================================================================
//
// ExTraceAP : Sample of external trace output AP
//
//  By setting TRCTHOST and TRCTPORT in Sample.ini, communication trace string
//  sent by TDS via TCP/IP is acquired and displayed on standard output.
// 
// Starting method
//
//   ExTraceAP [port#]
//   ~~~~~~~~~~~~~~~~~
//   port# : TCP/IP Port# used to communicate external trace output
//           By default, 6000 (PORT defined below) is used.
//
//
//  In order to use this AP, it is necessary to set following parameters of .ini
//  file (Sample.ini) to execute TDS.
//
//  TRCHOST = "127.0.0.1"	// IP-Address of host on which this AP operates
//  TRCPORT = 6000		// TCP/IP Port# used for communication
//
//  Normally, after starting this AP, BasicIo, CallbackIo, etc. are started.
//  (Processing is possible even after starting this AP after starting TDS
//  using AP)
//  This AP ends with input of CTRL-C.
//  This AP omits error processing.
//
//
// =============================================================================
//
// ExTraceAP : 外部トレース出力ＡＰのサンプル
//
//  Sample.ini の TRCTHOST、TRCTPORT を設定することにより TDS から TCP/IP で
//  送出される通信トレース文字列を取得し、標準出力に表示する。
// 
// 起動方法
//
//   ExTraceAP [port#]
//   ~~~~~~~~~~~~~~~~~
//   port# : 外部トレース出力の通信に使用する TCP/IP Port#
//           省略時は、6000 (以下に定義する PORT) を使用する。
//
//
//  本 AP を使用するには、TDS を実行するための .ini ファイル (Sample.ini) の
//  以下のパラメータを設定する必要がある。
//
//  TRCHOST = "127.0.0.1"	// 本 AP が動作するホストの IP-Address
//  TRCPORT = 6000		// 通信に使用する TCP/IP の Port#
//
//  通常は、本ＡＰを起動した後、BasicIo、CallbackIo 等を起動する。
//  (TDS 使用ＡＰの起動後、本ＡＰを起動しても処理は可能)
//  本ＡＰは、CTRL-C の入力で終了する。
//  本ＡＰはエラー処理を省略している。
//
// =============================================================================

#include		<stdio.h>
#include		<stdlib.h>
#include		<signal.h>

#if defined		WINDOWS
#include		<winsock2.h>
#include		<process.h>
#define			SOPTYPE		(const char*)
#define			SHUT_RDWR	SD_BOTH
#define			Errno		WSAGetLastError()
#if !defined		EINTR
#define			EINTR		WSAEINTR
#endif
#if !defined		EAGAIN
#define			EAGAIN		WSAEWOULDBLOCK
#endif
#else
#include		<memory.h>
#include		<unistd.h>
#include		<fcntl.h>
#include		<errno.h>
#include		<pthread.h>
#include		<sys/socket.h>
#include		<netinet/in.h>
#include		<arpa/inet.h>
#define			SOPTYPE		(const void*)
#define			Errno		errno
#define			closesocket(x)	close(x)
#endif


#define			PORT		6000

#define			PO		fprintf(stdout
#define			PE		fprintf(stderr


// -----------------------------------------------------------------------------

static void		Usage		();

int			Port=PORT;
int			Break=0,Cd=0;


// =============================================================================
// Common functions ------------------------------------------------------------

#if !defined	WINDOWS
static void*
_beginthread(
void*			(*func)(void*),
int			stacksz,
void			*par)
{
  pthread_t		th;
  pthread_attr_t	ta;
  int			rtn;

  if(pthread_attr_init(&ta)			==0){
    pthread_attr_setdetachstate(&ta,PTHREAD_CREATE_DETACHED);
    if(stacksz>0) pthread_attr_setstacksize(&ta,stacksz);
    pthread_attr_destroy(&ta);
  }
  if((rtn=pthread_create(&th,&ta,func,par))	==0)	return( 0);
  else							return((void*)(-1));
}



static void
Sleep(
int			msec)
{
  struct timespec	itimer,otimer;

  itimer.tv_sec =(msec/1000);
  itimer.tv_nsec=(msec%1000)*1000*1000;
  nanosleep(&itimer,&otimer);
}
#endif


// =============================================================================
// Signal handlers -------------------------------------------------------------

static void
ServerStop(
int			vec)
{
  Break=1;
  closesocket(Cd);
  exit(0);
}



// =============================================================================
// Trace message server function thread ----------------------------------------

#if defined	WINDOWS
static void
#else
static void*
#endif
TraceThread(
void			*param)
{
  char			host[64],str[4096];
  int			seq,sd,len,xlen,alen,p,s;

  sscanf((char*)param,"%d,%d,%s",&seq,&sd,host);	
  xlen=sizeof(str)-1;		// After that 'param' will be disabled
  memset(str,'\0',sizeof(str));	// 以降 param は使用不可となる

  for(alen=0;;){
    if(Break						!=0)	break;
    if((len=recv(sd,&str[alen],xlen-alen,0))		<=0)	break;
    alen+=len;			// Update 'alen'	// alen を更新

    for(s=p=0;p<alen;p++){
      if(str[p]=='\n'){	
	str [p]= '\0';		// Determine the end of string
				// 文字列終端を決定して
	PO,"[%2d.%3d] %s\n",seq,sd,&str[s]);
				// Display on standard output
				// 標準出力に表示
	s=p+1;			// Update next string start position
	    			// 次の文字列開始位置を更新
    } }

    if((alen=alen-s)	> 0){
      if(s> 0)	memcpy(str,&str[s],alen);
				// Move the rest after '\n' to the beginning of
				// str and prepare for next recv()
				// '\n' の後ろの残りを str の先頭に移動して、
				// 次の recv() に備える
      if(alen==xlen){		// If 'str' is full, display 'str' and clear 'alen'
				// str 満杯の場合は、表示して alen をクリア
	PO,"[%2d.%3d] %s\n",seq,sd,str);
	alen=0;
  } } }

  closesocket(sd);
}



// =============================================================================
// Main function ---------------------------------------------------------------

int
main(
int			argc,
char			*argv[])
{
  struct sockaddr_in	ladd,radd;
  char			runs[256],*add;
  unsigned long		pnobio=0;
  int			sd,seq,len,ru=1,i;

  for(i=1;i<argc;i++){
    if      (argv[i][0]=='-'){
      switch(argv[i][1]){
	case 'h': Usage();					break;
	case 'p': sscanf(argv[++i],"%d",&Port);			break;
  } } }

  signal(SIGTERM,ServerStop);
  signal(SIGINT ,ServerStop);

  memset(&ladd,0,sizeof(ladd));
  ladd.sin_family=AF_INET;
  ladd.sin_addr.s_addr=INADDR_ANY;
  ladd.sin_port=htons((unsigned short)Port);

  #if defined		WINDOWS
  { WSADATA		wsadata;
    WSAStartup(MAKEWORD(2,2),&wsadata);
  }
  #endif

  if((Cd=(int)socket(AF_INET,SOCK_STREAM,0))		< 0)	goto Exit;
  setsockopt(Cd,SOL_SOCKET,SO_REUSEADDR,SOPTYPE(&ru),sizeof(ru));
  if(bind   (Cd,(struct sockaddr*)(&ladd),sizeof(ladd))	< 0)	goto Exit;
  if(listen (Cd,4)					< 0)	goto Exit;
  PO,"INITIALIZED Port=%d Cd=%d\n",Port,Cd);

  for(seq=0;;seq++){
    memset(&radd,0,sizeof(radd));	len=sizeof(radd);
    sd=(int)accept(Cd,(struct sockaddr*)(&radd),&len);
    if(sd				< 0){
      if     (Errno			==EINTR)		continue;
      else if(Errno		 	==EAGAIN)		continue;
      #if defined	WINDOWS
      else if(Errno			==WSAEINTR)		continue;
      else if(Errno			==WSAEWOULDBLOCK)	continue;
      #endif
      else							goto Exit;
    }
    #if defined		WINDOWS
    ioctlsocket	(sd,FIONBIO,&pnobio);
    #else
    fcntl	(sd,F_SETFL,0);
    #endif
    add=inet_ntoa(radd.sin_addr);

    PO,"CONNECTED form %s [%d]\n",add,sd);
    sprintf(runs,"%d,%d,%s",seq,sd,add);
    PO,"RUN Thread parameter=%s\n",runs);
    _beginthread(TraceThread,0,runs);
    Sleep(300);			// Need time for thread to parse 'runs'
				// It's a little scamped work.
				// スレッド側で runs を解析するための時間が必要
  }				// 少々手抜きです。

Exit:
  PO,"TERMINATED Port=%d Cd=%d\n",Port,Cd);
  if(Cd>0) closesocket(Cd);
  return(0);
}



// -----------------------------------------------------------------------------

static void
Usage()
{
  PO,"\n");
  PO,"Usage: ExTraceAP [options...]\n");
  PO,"\n");
  PO,"  options  : Optional parameter\n");
  PO,"    -p port#   : TCP Port#                         [%d]\n"	,Port);
  PO,"\n");
  exit(22);
}
