#include "Worker.h"

#include "ProcessRename.h"
#include "ServiceConfig.h"
#include "Logger.h"

#include "StorageStatus.h"
#include "ProcessStatus.h"
#include "ClientThread.h"

#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>


// Worker Process 의 main 함수
int WorkerMain( int listenSocket )
{
	// Process Rename
	set_ps_display(PROG_NAME": Worker [initialize process]", false);

	// Set Signal Handler
	SetSignalWorker();
    
	// 로그 출력 순서가 Main 프로세스와 꼬일 수 있어... 1초간 대기 후 진행
	sleep( 1 );



	// 공통 모듈 생성 및 기동
	// Storage 상태 정보 추출 모듈 생성 및 기동
	CStorageStatus storageStatus( CServiceConfig::GetInstance()->GetSaveRootPath() );
	if( storageStatus.Start() == false )
	{
		LOG( LERR, "Worker[%d]: Storage status module start fail. => Worker Exit", getpid() );
		return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지		
	}
	//_LOG( LINF, "Worker[%d]: CStorageStatus module start.", getpid() );
	
	// Process status 모듈 생성 및 기동
	CProcessStatus processStatus;
	if( processStatus.Start() == false )
	{
		LOG( LERR, "Worker[%d]: Process status module start fail. => Worker Exit", getpid() );
		return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지		
	}
	//_LOG( LINF, "Worker[%d]: CProcessStatus module start.", getpid() );
	
	
	
	// Worker 프로세스의 작업 수행.		
	// Listen Socket 에 대한 Accept 대기 수행
	int clientSocket;
	struct sockaddr_in clientSockAddr;
	socklen_t clientSockLen = sizeof( clientSockAddr );
	char szClientIp[INET_ADDRSTRLEN];

	CClientThread * pClientThread;
		

	while( 1 )
	{
		clientSocket = accept( listenSocket, ( struct sockaddr * ) &clientSockAddr, &clientSockLen );
		if( clientSocket == -1 )
		{
			// 오류발생시 해당 내역 로깅처리.
			int errorNum = errno;
			LOG( LERR, "Worker: client accept failed. [%d][%s]", errorNum, strerror( errorNum ) );
		}
		else
		{
			// 정상적인 Client 인 경우...

			// IP 정보 추출.. 실패시 0.0.0.0 으로 설정.
			memset( szClientIp, 0x00, sizeof( szClientIp ) );
			if( inet_ntop( AF_INET, &( clientSockAddr.sin_addr ), szClientIp, sizeof( szClientIp ) ) == NULL )
			{
				// 변환 실패시.. 0.0.0.0 으로 설정 처리.
				snprintf( szClientIp, INET_ADDRSTRLEN - 1, "0.0.0.0" );
			}

			// client 를 처리할 Thread 객체 생성 후 기동 처리.
			// - 해당 객체에서 thread 종료시 자동 delete 처리하므로.. 본 함수에서는 new 생성만 처리한다.
			pClientThread = NULL;
			pClientThread = new CClientThread( &storageStatus, &processStatus, clientSocket, szClientIp );
			if( pClientThread == NULL || pClientThread->Start() == false )
			{
				// Thread 기동 실패시..
				if( pClientThread != NULL )
				{
					LOG( LERR, "client[%s] thread object start function fail.", szClientIp );

					delete pClientThread;
					pClientThread = NULL;
				}
				else
				{
					// Thead 객체 생성 실패시
					LOG( LERR, "client[%s] thread object create fail.", szClientIp );
				}

				close( clientSocket );
			}
					
		}

	} // while( 1 )
	
    
	// Process 종료 처리.
	_LOG(  LINF, "Worker[%d] exit.. Good Bye..", getpid());
        
    //잠시 대기 후 종료처리.

	// CHG 2015-09-16 huibong
	// - #24436 usleep -> nanosleep 으로 변경 처리.
	struct timespec sleep;
	sleep.tv_sec = 0;
	sleep.tv_nsec = 500000000;  // 0.5 sec
	nanosleep( &sleep, NULL );
		
	// fork 를 수행한 함수에서 exit 함수 호출을 통한 종료처리
	return EXIT_SUCCESS;	
}


// Worker Process 종료 Signal 을 전달받은 경우 이를 처리하기 위한 함수.
// @param nSignalNumber [in] 발생한 시그널 Number
// @return void
static void SignalWorkerTerminate(int nSignalNumber)
{
	// Signal Number 에 따른 로깅처리.
	if( nSignalNumber == SIGTERM )
	{
		_LOG( LINF, "Worker[%d] process exit by user signal [SIGTERM]. Good Bye..", getpid() );
	}
	else
	{
		_LOG( LWAR, "Worker[%d] process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
	}

	// CHG 2015-09-16 huibong
	// - #24436 usleep -> nanosleep 으로 변경 처리.
	struct timespec sleep;
	sleep.tv_sec = 0;
	sleep.tv_nsec = 500000000;  // 0.5 sec
	nanosleep( &sleep, NULL );
		
	exit( EXIT_SUCCESS );
}

// Worker 프로세스 signal 처리 설정을 위한 함수	
// @return void	
void SetSignalWorker( void )
{
	sigset_t set;
	struct sigaction act;
	memset(&act, 0x00, sizeof(act));

	sigfillset( &set );
	sigprocmask( SIG_SETMASK, &set, NULL );	
	sigfillset( &act.sa_mask );

	/* 무시할 신호 목록 */
	act.sa_handler = SIG_IGN;
	sigaction( SIGPIPE, &act, NULL);	/* desciptor 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
	sigaction( SIGHUP,  &act, NULL);	/* process를 기동시킨 관리자의 로그아웃시, 또는 config reload 등의 처리 signal*/
	sigaction( SIGINT,  &act, NULL);	/* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
	sigaction( SIGQUIT, &act, NULL);	/* 키보드에 의한  Abort 신호 처리 => ? */
	
	// Worker Controller 의 child process 종료에 대한 처리기 설정. 	
	//act.sa_handler = SIG_DFL;           // 외부 프로세스 수행이 발생할 수 있는 경우 default 처리
	act.sa_handler = SIG_IGN;           // 외부 프로세스 수행이 없는 경우.. SIG_IGN 처리
	sigaction( SIGCHLD, &act, NULL);
	
	/* 각종 에러나 사용자의 종료 신호 처리 */
	act.sa_handler = SignalWorkerTerminate;
	sigaction( SIGTERM, &act, NULL);	/* kill -TERM 에 의한 프로세스 종료시 */

	sigemptyset(&set);                  /* 신호 처리기 처리 설정 위한 블록 해제 */
	sigprocmask(SIG_SETMASK, &set, NULL);
}
