#include "Main.h"

#include "ProcessRename.h"
#include "ProcessConfig.h"
#include "Logger.h"
#include "ShmDefine.h"
#include "ShmControl.h"
#include "ShmManager.h"

#include "WorkerRecvVmasd.h"
#include "WorkerRecvSystem.h"
#include "WorkerRecvCheck.h"
#include "WorkerSendInfo.h"
#include "WorkerControl.h"

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

#include <iostream>


// Shared memory key 생성용 파일.
// 2018-02-21 CHG huibong /tmp 경로 대신 /user/service 하위에 생성되도록 경로 수정 (#31701)
// - /tmp 경로 사용시 자동 clean 처리 모듈 동작으로 삭제되는 상황 발생하여 수정 처리.
#define SHARED_MEMORY_KEY_FILE    "/user/service/"PROG_NAME".shm"


// Accept 대기 Count 수
#define DEFAULT_ACCEPT_WAIT_COUNT	50


// 공유메모리 생성, 삭제 등의 Manager 객체에 대한 포인터.
CShmControl< struct RcInfo > * g_pShmCtl = NULL;

// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
CShmManager * g_pShmManager = NULL;


// Worker 프로세스 중 vmasd 가 전송하는 시스템 정보 수신을 담당할 Worker PID 정보.
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리
pid_t g_pidWorkerRecvVmasd = -1;

// Worker 프로세스 중 csagentd 가 전송하는 FHS system 상태 정보 수신을 담당할 Worker PID 정보.
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리
pid_t g_pidWorkerRecvSystem = -1;

// Worker 프로세스 중 csagentd 가 전송하는 FHS check 상태 정보 수신을 담당할 Worker PID 정보.
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리
pid_t g_pidWorkerRecvCheck = -1;

// Worker 프로세스 중 각 Client 의 요청을 수신하여.. Alive FHS 목록 등의 정보 전달을 담당할 Worker PID 정보
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리
pid_t g_pidWorkerSendInfo = -1;

// Worker 프로세스 중 각 FHS 정보의 GMS 전송, Top FHS 정보의 RCDB 업데이트 등을 수행할 Worker PID 정보.
// 시그널 처리에 의해 재생성 처리 단순화를 위해 전역 변수로 처리
pid_t g_pidWorkerControl = -1;


// FHS 의 vmasd 가 Broadcasting 하는 각 FHS 의 상태 정보를 수신받기 위한 UDP Socket....
// 시그널 처리 및 Worker 에서 처리를 단순화 하기 위해 전역 변수로 처리.
int g_sockVmasdRecv = -1;

// FHS csagentd 가 전송하는 시스템 상태 정보 수신을 위한 TCP Socket
// 시그널 처리 및 Worker 에서 처리를 단순화 하기 위해 전역 변수로 처리.
int g_sockSystemRecv = -1;

// FHS csagentd 가 전송하는 서비스 상태 체크 정보 수신을 위한 TCP Socket
// 시그널 처리 및 Worker 에서 처리를 단순화 하기 위해 전역 변수로 처리.
int g_sockCheckRecv = -1;

// fimngd 등과 같은 외부 모듈과 통신을 통해 정보를 전달하기 위한 TCP Socket 생성.
// 시그널 처리 및 Worker 에서 처리를 단순화 하기 위해 전역 변수로 처리.
int g_sockInfoSend = -1;



int main( int argc, char * argv[] )
{
	// 설정 파일을 저장할 변수.
	std::string strConfigFileName = DEFAULT_CONFIG_FILE;

	// linux 에서 프로세스를 Titile 변경을 지원을 위해서 argv 메모리에 저장
	argv = save_ps_display_args( argc, argv );
	
	// 전달 받은 옵션 여부 확인 및 처리
	if( argc >= 2 )
	{
		int opt;
		while( ( opt = getopt( argc, argv, "hvc:" ) ) != -1 )	// 옵션 끝까지 파싱처리
		{
			switch( opt )
			{
				case 'h':
				case '?':	// 정의되지 않은 문자가 나타날 경우 getopt 에 자동반환. 도움말 표시 처리. 
					PrintUsage();
					return EXIT_SUCCESS;

				case 'v':
					PrintVersion();
					return EXIT_SUCCESS;

				case 'c':
					strConfigFileName = optarg;
					fprintf( stderr, PROG_NAME " start with user define conf[%s]\n", strConfigFileName.c_str() );
					break;

				default:	// 본 조건절은 getopt 특성상 동작하지 않지만...업무 Flow 이해(?)를 위해 유지한다.
					fprintf( stderr, "Unkonwn options[%c] used. program terminated.\n", opt );
					return EXIT_FAILURE;
			}
		}
	}

	// 프로세스 중복 실행 여부 검사 
	if( IsCurrentProcessRun() == true )
	{
		fprintf( stderr, "[warning] Process [" PROG_NAME "] is already running....\n" );
		return EXIT_FAILURE;
	}
	
	std::string szErrorMessage;

	// conf 로딩 및 검사 
	if( CProcessConfig::Init( PROG_NAME, strConfigFileName, szErrorMessage ) == false )
	{
		fprintf( stderr, "[ERR] %s\n\n", szErrorMessage.c_str() );
		return EXIT_FAILURE;
	}

	// Log 객체 생성 및 초기화
	if( CLogger::Init( PROG_NAME, CProcessConfig::GetInstance()->GetLogPath(), LINF ) == false )
	{
		fprintf( stderr, "[ERR] Log module initialize failed.[%s]\n\n", CProcessConfig::GetInstance()->GetLogPath() );
		return EXIT_FAILURE;
	}
	
	// Daemonize...
	if( daemon( 1, 0 ) == -1 ) // nochdir: true(작업 디렉토리 변경 안함), noclose: false (표준 입출력, 에러를 /dev/null 로 리디렉트 처리)
	{
		fprintf( stderr, "[ERR] Process daemonize failed.[%s]\n\n", strerror( errno ) );
		return EXIT_FAILURE;
	}

	// 공유메모리 생성 후 daemonize 처리시 문제
	// - ipcs -ma 에 표시되는 CPID, LPID 에 daemonize 수행으로 인해 종료된 이전 process id 가 기록됨.
	// - 이로 인해 정확한 상태를 판단하기 어려우므로... daemonzie 수행 후 공유메모리 생성, 접근을 수행하도록 한다.
	// - 이로 인해 daemonizs 이후에는 fprintf 처리가 안되므로.. fprintf 구문은 주석 처리.

	// 공유메모리 key 
	// - 다른 프로세스에서 사용하지 않으므로.. 고정키 값을 사용할 필요는 없다.
	// - opendav, csagentd 의 경우.. /tmp 상의 파일 생성 후 ftok() 함수를 이용하여 공유메모리 key 를 생성.
	// - 고정 key 를 사용할 경우.. 다른 프로세스와 key 충돌 발생 우려가 있으므로...
	// - opendav, csagentd 와 동일한 방식으로 key 값을 생성하도록 처리한다.

	// 공유메모리 key 생성 관련 기존 임시 파일 존재시 삭제 처리.
	unlink( SHARED_MEMORY_KEY_FILE );
	
	key_t iShmKey;
	// 공유메모리 key 생성을 위한 임시 파일 생성 처리.
	FILE * pShmFile = fopen( SHARED_MEMORY_KEY_FILE, "a+" );
	if( pShmFile != NULL )
	{
		// Key file 생성이 성공한 경우... 파일만 있으면 되니까.. close 처리한다.
		fclose( pShmFile );
		pShmFile = NULL;
	}
	else
	{
		// Key file 생성에 실패한 경우...		
		int errorNum = errno;
		LOG( LERR, "[Main] Shared memory key file create failed.[%s][%d][%s] => Process exit.", SHARED_MEMORY_KEY_FILE, errorNum, strerror( errorNum ) );

		return EXIT_FAILURE;
	}

	// 임시 파일로 부터 key 값 추출
	iShmKey = ftok( SHARED_MEMORY_KEY_FILE, 1 );
	if( iShmKey == (key_t)-1 )
	{
		// 공유메모리 Key 생성 실패시
		//fprintf( stderr, "[ERR] Shared memory key create failed. temp file[%s]\n\n", FHS_SHARED_MEMORY_FILE );

		LOG( LERR, "[Main] Shared memory key create failed. key file[%s] => Process exit.", SHARED_MEMORY_KEY_FILE );

		ReadyToExit();
		return EXIT_FAILURE;
	}

	// 공유메모리에 생성 담당 class 객체를 생성 및 공유메모리 생성 or attach
	CShmControl< struct RcInfo > shmCtl;
	if( shmCtl.Create( iShmKey, sizeof( struct RcInfo ) ) == false )
	{
		LOG( LERR, "[Main] Shared memory create failed. key[%d] size[%lu] => Process exit.", iShmKey, sizeof( struct RcInfo ) );

		ReadyToExit();
		return EXIT_FAILURE;
	}
	g_pShmCtl = &shmCtl; // 전역 포인터에 대한 포인터 지정.	

	// 공유메모리에 데이터 입출력 처리를 담당할 class 객체 생성	
	CShmManager shmMng( shmCtl.GetShmPtr() );
	if( shmMng.IsValid() == false )
	{
		shmCtl.Remove();
		
		LOG( LERR, "[Main] Shared memory address not valid. => Process exit." );

		ReadyToExit();
		return EXIT_FAILURE;
	}

	g_pShmManager = &shmMng;  // 전역 포인터에 대한 포인터 지정.
	shmMng.ClearAll();        // 공유메모리를 우선 초기화	
	

	// signal 처리 설정
	SetSignalMain();
	
	// Process 기동 관련 정보 기록 -> Log	
	_LOG( LINF, "***********************************************************" );
	_LOG( LINF, " %s Start. Version: %s", PROG_NAME, PROG_VERSION );
	_LOG( LINF, "***********************************************************" );
	_LOG( LINF, "Config                         : %s", strConfigFileName.c_str() );
	_LOG( LINF, "Log                            : %s/%s", CProcessConfig::GetInstance()->GetLogPath(), PROG_NAME );
	_LOG( LINF, "Log level                      : %d", CProcessConfig::GetInstance()->GetLogLevel() );
	_LOG( LINF, "RC ID                          : %s", CProcessConfig::GetInstance()->GetRcId() );
	_LOG( LINF, "RCDB                           : %s %u %s %s", CProcessConfig::GetInstance()->GetRcdbIp()
		                                                      , CProcessConfig::GetInstance()->GetRcdbPort()
		                                                      , CProcessConfig::GetInstance()->GetRcdbName()
														      , CProcessConfig::GetInstance()->GetRcdbAcct() );
	_LOG( LINF, "Service listen port" );
	
	// 본 설정은 생략 가능하므로... 유효할 경우에만 표시.	
	if( CProcessConfig::GetInstance()->GetFhsBroadcastRecvPort() != 0 )
		_LOG( LINF, "  vmasd broadcast recv port  : UDP %u", CProcessConfig::GetInstance()->GetFhsBroadcastRecvPort() );

	_LOG( LINF, "  System stat recv port   : TCP %u", CProcessConfig::GetInstance()->GetPortRecvSystem() );
	_LOG( LINF, "  Service check recv port : TCP %u", CProcessConfig::GetInstance()->GetPortRecvCheck() );	
	_LOG( LINF, "  RC info send port       : TCP %u", CProcessConfig::GetInstance()->GetPortSendInfo() );
	
	_LOG( LINF, "GMS                            : %s:%u (TCP)", CProcessConfig::GetInstance()->GetGmsServer(), CProcessConfig::GetInstance()->GetGmsPortStat() );
	_LOG( LINF, "Storage max usage to disable   : %d (%%)", CProcessConfig::GetInstance()->GetStorageMaxUsageToDisable() );
	_LOG( LINF, "Report FHS memory max usage    : %d (%%)", CProcessConfig::GetInstance()->GetReportMemoryMaxUsage() );
	
	_LOG( LINF, "Shared memory" );
	_LOG( LINF, "  Key                     : %d (0x%08x)", iShmKey, iShmKey );
	_LOG( LINF, "  Size                    : %lu", sizeof( struct RcInfo ) );
	_LOG( LINF, "  Max client slot         : %d", MAX_CLIENT_COUNT );
	
	_LOG( LINF, "***********************************************************" );
	

	// Log Level 재설정. -> conf 설정대로 변경 처리.
#ifdef _DEBUG_
	CLogger::GetInstance()->SetLogLevel( LDBG );
#else
	CLogger::GetInstance()->SetLogLevel( CProcessConfig::GetInstance()->GetLogLevel() );
#endif


	
	// FHS vmasd 로 부터 Broadcast 되는 FHS 상태 정보 수신을 위한 UDP Receive Socket 생성
	// - conf 상에서 해당 설정 생략이 가능하므로 유효하지 않은 설정값인 경우...
	// - 해당 socket 을 생성하지 않는다.
	if( CProcessConfig::GetInstance()->GetFhsBroadcastRecvPort() != 0 )
	{
		if( MakeVmasdRecvSocket() == false )
		{
			LOG( LERR, "vmasd udp receive socket create failed. => Process exit." );

			ReadyToExit();
			return EXIT_FAILURE;
		}
	}

	// FHS csagentd 가 전송하는 시스템 상태 정보 수신을 위한 TCP listen socket 생성
	if( MakeSystemRecvSocket() == false )
	{
		LOG( LERR, "system info receive tcp socket create failed. => Process exit." );

		ReadyToExit();
		return EXIT_FAILURE;
	}

	// FHS csagentd 가 전송하는 서비스 상태 체크 정보 수신을 위한 TCP listen socket 생성	
	if( MakeCheckRecvSocket() == false )
	{
		LOG( LERR, "service check info receive tcp socket create failed. => Process exit." );

		ReadyToExit();
		return EXIT_FAILURE;
	}
	
	// fimngd 등과 같은 외부 모듈과 통신을 통해 정보를 전달하기 위한 TCP listen socket 생성
	if( MakeInfoSendSocket() == false )
	{
		LOG( LERR, "info send tcp socket create failed. => Process exit." );

		ReadyToExit();
		return EXIT_FAILURE;
	}
	
	// Worker Process 생성

	// csagentd 가 전송하는 FHS check 상태 정보 수신을 담당할 Worker 프로세스 생성.
	if( MakeWorkerRecvCheck() == false )
	{
		LOG( LERR, "[Main] RecvCheck worker process create failed. => Process exit." );

		ReadyToExit();
		return EXIT_FAILURE;
	}

	// csagentd 가 전송하는 FHS system 상태 정보 수신을 담당할 Worker 프로세스 생성
	if( MakeWorkerRecvSystem() == false )
	{
		LOG( LERR, "[Main] RecvSystem worker process create failed. => Process exit." );

		ReadyToExit();
		return EXIT_FAILURE;
	}

	// vmasd 가 전송하는 시스템 정보 수신을 담당할 Worker 프로세스 생성.
	// - 해당 프로세스는 conf 생략이 가능하므로... 유효하지 않는 설정값인 경우...
	// - 기동 처리하지 않는다.
	if( CProcessConfig::GetInstance()->GetFhsBroadcastRecvPort() != 0 )
	{
		if( MakeWorkerRecvVmasd() == false )
		{
			LOG( LERR, "[Main] RecvVmasd worker process create failed. => Process exit." );

			ReadyToExit();
			return EXIT_FAILURE;
		}
	}


	// 잠시 대기
	// - 수신 프로세스가 생성된 후 FHS 목록 전송, top fhs 정보 등의 업데이트를 수행이 가능하므로.. 잠시 대기.
	sleep( 2 );
	
	// 각 Client 의 요청을 수신하여.. Alive FHS 목록 등의 정보 전달을 담당할 Worker 프로세스 생성.
	if( MakeWorkerSendInfo() == false )
	{
		LOG( LERR, "[Main] SendInfo worker process create failed. => Process exit." );

		ReadyToExit();
		return EXIT_FAILURE;
	}

	// 각 FHS 정보의 GMS 전송, Top FHS 정보의 RCDB 업데이트 등을 수행할 Worker 프로세스 생성.
	if( MakeWorkerControl() == false )
	{
		LOG( LERR, "[Main] Control worker process create failed. => Process exit." );

		ReadyToExit();
		return EXIT_FAILURE;
	}
	

	// Process Rename..
	set_ps_display( PROG_NAME": Main", false );
	


	// Main Process : 그냥 대기
	while( 1 )
	{
		// Main Process 는 할 일이 없다. 
		// => Worker 프로세스 종료시 SIGCHLD 신호로 인해 신호처리기에서 Worker Process 재성성 수행함.
		// => 따라서 그냥 시그널 대기
		pause();
	}

	// 다음의 코드는 Daemon 으로 동작하기 때문에 수행되지 않는다.	
	ReadyToExit();
	return EXIT_SUCCESS;
}


// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExit()
{
	// open 된 socket 관련 Close 처리
	if( g_sockVmasdRecv != -1 )
		close( g_sockVmasdRecv );
	if( g_sockSystemRecv != -1 )
		close( g_sockSystemRecv );
	if( g_sockCheckRecv != -1 )
		close( g_sockCheckRecv );
	if( g_sockInfoSend != -1 )
		close( g_sockInfoSend );
	

	// 공유메모리 data 초기화.
	// - 다른 모니터링용 프로세스가 attach 되어 있는 경우 대비 목적.
	if( g_pShmManager != NULL )
		g_pShmManager->ClearAll();
	
	// 공유메모리 key 생성 관련 기존 임시 파일 존재시 삭제 처리.
	unlink( SHARED_MEMORY_KEY_FILE );
	
	// 공유메모리 제거.	
	// - 다른 프로세스와 공유사용 하는 것이 아니므로.. 종료시 삭제 처리한다.
	if( g_pShmCtl != NULL )
		g_pShmCtl->Remove();
		

	// Logger 객체 종료 처리.
	CLogger::Exit();

	return;
}


// 사용방법 표시
void PrintUsage()
{
	fprintf( stderr, "\n" );
	fprintf( stderr, "Usage: " PROG_NAME " [-h] [-v] [-c {file}]            \n" );
	fprintf( stderr, "Options:                                              \n" );
	fprintf( stderr, "  -h                       Display help information   \n" );
	fprintf( stderr, "  -v                       Display version            \n" );
	fprintf( stderr, "  -c {file}                Use {file} as config file  \n" );

	fprintf( stderr, "\n" );
	fprintf( stderr, PROG_NAME " is Solbox Cloud Storage module.\n" );
	fprintf( stderr, "    - collect FHS system, service module status from FHS.\n" );
	fprintf( stderr, "    - send RC status to FHS and update RCDB.\n" );
	fprintf( stderr, "    - trnasfer periodically RC status to GMS.\n\n" );

	return;
}


// 버전 정보 표시
void PrintVersion()
{
	fprintf( stderr, "\n" );
	fprintf( stderr, PROG_NAME " version: " PROG_VERSION "\n\n" );

	return;
}


/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
bool IsCurrentProcessRun( )
{
	char tempBuffer[512];
	FILE * fd = NULL;
	bool bRun = false;

	snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -x %s | sort", PROG_NAME );

	fd = popen( tempBuffer, "r" );
	if( fd == NULL )
	{
		std::cerr << "[error] Process duplication check failed.[popen error][" << strerror( errno ) << "]" << std::endl;

		// 오류 발생시 true 반환하여 프로세스 실행 방지처리
		return true;
	}
	else
	{
		memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
		while( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
		{
			std::string tempPid( tempBuffer );
			Trim( tempPid );

			if( atoi( tempPid.c_str() ) != getpid() )
			{
				std::cout << "[info] Process duplication found. pid[" << atoi( tempPid.c_str() ) << "]" << std::endl;

				bRun = true;
				break;
			}
		}

		pclose( fd );
		return bRun;
	}
}

/// @brief std 상에 trim 함수가 없어서 직접 구현. 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
/// @return void
void Trim( std::string & str )
{
	if( str.length() == 0 )
		return;

	// 문자열 뒤의 공백, TAB, CR 등의 문자 제거처리.
	std::string::size_type pos = str.find_last_not_of( " \a\b\f\n\r\t\v" );
	if( pos != std::string::npos )
		str.erase( pos + 1 );

	// 문자열 앞의 공백, TAB, CR 등의 문자 제거처리.
	pos = str.find_first_not_of( " \a\b\f\n\r\t\v" );
	if( pos != std::string::npos )
		str.erase( 0, pos );
}

// Worker 프로세스 종료 신호 수신시
static void SignalWorkerDead( int nSignalNumber )
{
	pid_t deadPid;
	int nDeadStatus;

	while( ( deadPid = waitpid( -1, &nDeadStatus, WNOHANG ) ) > 0 )
	{
		// Worker 프로세스가 signal 에 의해 종료되었는지 검사.
		// - 종료 사유 로깅 및
		// - Worker 의 초기화 실패로 인한 종료시... Main 프로세스가 종료되도록 처리.
		if( WIFSIGNALED( nDeadStatus ) )
		{
			// signal 에 의해 종료된 경우...
			// - 관련 내역 로깅
			if( deadPid == g_pidWorkerRecvVmasd )
			{
				_LOG( LWAR, "[Main] RecvVmasd worker process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
			}
			else if( deadPid == g_pidWorkerRecvSystem )
			{
				_LOG( LWAR, "[Main] RecvSystem worker process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
			}
			else if( deadPid == g_pidWorkerRecvCheck )
			{
				_LOG( LWAR, "[Main] RecvCheck worker process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
			}
			else if( deadPid == g_pidWorkerSendInfo )
			{
				_LOG( LWAR, "[Main] SendInfo worker process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
			}
			else if( deadPid == g_pidWorkerControl )
			{
				_LOG( LWAR, "[Main] Control worker process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
			}
			else
			{
				_LOG( LWAR, "[Main] Unknown child process[%d] killed by signal[%d]", deadPid, WTERMSIG( nDeadStatus ) );
			}
		}
		else
		{
			// signal 에 종료된 상황이 아닌 경우...
			// - 각 Worker 의 초기화 실패 발생시... return 구문에 의해 종료됨.
			// - 기타 등등.

			// 관련 내역 로깅.
			if( deadPid == g_pidWorkerRecvVmasd )
			{
				_LOG( LWAR, "[Main] RecvVmasd worker process[%d] killed. Not signal.", deadPid );
			}
			else if( deadPid == g_pidWorkerRecvSystem )
			{
				_LOG( LWAR, "[Main] RecvSystem worker process[%d] killed. Not signal.", deadPid );
			}
			else if( deadPid == g_pidWorkerRecvCheck )
			{
				_LOG( LWAR, "[Main] RecvCheck worker process[%d] killed. Not signal", deadPid );
			}
			else if( deadPid == g_pidWorkerSendInfo )
			{
				_LOG( LWAR, "[Main] SendInfo worker process[%d] killed. Not signal", deadPid );
			}
			else if( deadPid == g_pidWorkerControl )
			{
				_LOG( LWAR, "[Main] Control worker process[%d] killed. Not signal", deadPid );
			}
			else
			{
				_LOG( LWAR, "[Main] Unknown child process[%d] killed. Not signal", deadPid );
			}

			// 만약 Worker 프로세스들이 초기화 실패로 인해 EXIT_FAILURE 반환되는 상황이라면...
			// 자식 프로세스를 재생성하지 않고.. 프로세스를 종료 처리한다.
			if( WIFEXITED( nDeadStatus ) )
			{
				if( WEXITSTATUS( nDeadStatus ) == EXIT_FAILURE )
				{
					if( deadPid == g_pidWorkerRecvVmasd )
					{
						_LOG( LWAR, "[Main] RecvVmasd worker process[%d] initialize failed.", deadPid );
					}
					else if( deadPid == g_pidWorkerRecvSystem )
					{
						_LOG( LWAR, "[Main] RecvSystem worker process[%d] initialize failed.", deadPid );
					}
					else if( deadPid == g_pidWorkerRecvCheck )
					{
						_LOG( LWAR, "[Main] RecvCheck worker process[%d] initialize failed.", deadPid );
					}
					else if( deadPid == g_pidWorkerSendInfo )
					{
						_LOG( LWAR, "[Main] SendInfo worker process[%d] initialize failed.", deadPid );
					}
					else if( deadPid == g_pidWorkerControl )
					{
						_LOG( LWAR, "[Main] Control worker process[%d] initialize failed.", deadPid );
					}
					else
					{
						_LOG( LWAR, "[Main] Unknown child process[%d] exit with fail.", deadPid );
					}
					
					_LOG( LERR, "[Main] Program abnormally exited because Worker initialize faild.." );

					// Main 종료 시그널 발생
					raise( SIGTERM );
					continue;
				}
			}
		}

		// 위에서 로깅 및 초기화 실패로 인한 Main 종료 처리 작업을 수행했으므로...
		// 죽은 Worker 프로세스를 재생성 처리한다.		
		if( deadPid == g_pidWorkerRecvVmasd )
		{
			if( MakeWorkerRecvVmasd() == false )
			{
				_LOG( LERR, "[Main] RecvVmasd worker process recreate failed.=> Main process exit." );
				raise( SIGTERM );
			}
			else
			{
				_LOG( LWAR, "[Main] RecvVmasd worker process recreate success." );
			}
		}
		else if( deadPid == g_pidWorkerRecvSystem )
		{
			if( MakeWorkerRecvSystem() == false )
			{
				_LOG( LERR, "[Main] RecvSystem worker process recreate failed.=> Main process exit." );
				raise( SIGTERM );
			}
			else
			{
				_LOG( LWAR, "[Main] RecvSystem worker process recreate success." );
			}
		}
		else if( deadPid == g_pidWorkerRecvCheck )
		{
			if( MakeWorkerRecvCheck() == false )
			{
				_LOG( LERR, "[Main] RecvCheck worker process recreate failed.=> Main process exit." );
				raise( SIGTERM );
			}
			else
			{
				_LOG( LWAR, "[Main] RecvCheck worker process recreate success." );
			}
		}
		else if( deadPid == g_pidWorkerSendInfo )
		{
			if( MakeWorkerSendInfo() == false )
			{
				_LOG( LERR, "[Main] SendInfo worker process recreate failed.=> Main process exit." );
				raise( SIGTERM );
			}
			else
			{
				_LOG( LWAR, "[Main] SendInfo worker process recreate success." );
			}
		}
		else if( deadPid == g_pidWorkerControl )
		{
			if( MakeWorkerControl() == false )
			{
				_LOG( LERR, "[Main] Control worker process recreate failed.=> Main process exit." );
				raise( SIGTERM );
			}
			else
			{
				_LOG( LWAR, "[Main] Control worker process recreate success." );
			}
		}		
	}

	// 오류 발생시 해당 내역 로깅
	if( deadPid < 0 )
	{
		int errorNum = errno;
		LOG( LERR, "[Main] SIG_CHLD receive but waitpid return error[%d][%s]", errorNum, strerror( errorNum ) );
	}
	
	return;
}


// Main 프로세스에 대한 종료 처리 수신시
static void SignalMainTerminate( int nSignalNumber )
{
	// 자식 프로세스를 종료 처리한다.	
	if( g_pidWorkerRecvVmasd != -1 )
	{
		kill( g_pidWorkerRecvVmasd, SIGTERM );
	}
	if( g_pidWorkerRecvSystem != -1 )
	{
		kill( g_pidWorkerRecvSystem, SIGTERM );
	}
	if( g_pidWorkerRecvCheck != -1 )
	{
		kill( g_pidWorkerRecvCheck, SIGTERM );
	}
	if( g_pidWorkerSendInfo != -1 )
	{
		kill( g_pidWorkerSendInfo, SIGTERM );
	}
	if( g_pidWorkerControl != -1 )
	{
		kill( g_pidWorkerControl, SIGTERM );
	}

	// Signal Number 에 따른 로깅처리.
	if( nSignalNumber == SIGTERM )
	{
		_LOG( LINF, "[Main:%d] Process exit by SIGTERM signal. Good Bye..", getpid());		
	}
	else
	{
		_LOG( LWAR, "[Main:%d] Process exit by abnormal signal[%d]. Good Bye..", getpid(), nSignalNumber );		
	}
	
	// 잠시 대기
	sleep( 1 );
	
	// 기타 모듈들의 종료 처리 수행
	ReadyToExit();
	

	// 종료전 잠시 대기
	struct timespec sleep;
	sleep.tv_sec = 0;
	sleep.tv_nsec = 500000000;  // 0.5 sec
	nanosleep( &sleep, NULL );
		
	exit( EXIT_SUCCESS );
}


// Main Process 의 signal 처리기...
// fork 된 worker 프로세스 역시 기본적으로 본 signal 처리 action 을 상속받는다.
void SetSignalMain()
{
	sigset_t set;
	struct sigaction act;

	memset( &act, 0x00, sizeof( act ) );
	sigfillset( &set );
	sigprocmask( SIG_SETMASK, &set, NULL );
	sigfillset( &act.sa_mask );

	// 무시 처리 signal
	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 child process 종료에 대한 처리기 설정.
	act.sa_handler = SignalWorkerDead;
	sigaction( SIGCHLD, &act, NULL );
	
	// 사용자의 종료 또는 에러 관련 신호 처리.
	act.sa_handler = SignalMainTerminate;
	sigaction( SIGTERM, &act, NULL );	/* kill -TERM 에 의한 프로세스 종료시 */

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


// vmasd 가 전송하는 시스템 정보 수신을 담당할 Worker 프로세스 생성.
bool MakeWorkerRecvVmasd()
{
	// fork() 결과를 RecvVmasd Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장	
	g_pidWorkerRecvVmasd = fork();

	if( g_pidWorkerRecvVmasd == 0 )
	{
		// 생성된 Worker 프로세스인 경우...
		int iResult;

		// Worker RecvVmasd 프로세스의 Main 함수 호출
		iResult = WorkerRecvVmasdMain();

		// Worker SystemCollect 프로세스의 Main 함수 종료시 종료 처리
		_LOG( LINF, "[Main] RecvVmasd worker process exit. PID[%d] return[%d]", getpid(), iResult );

		ReadyToExit();
		exit( iResult );
	}
	else if( g_pidWorkerRecvVmasd < 0 )
	{
		// fork 실패시
		int errorNum = errno;
		LOG( LERR, "[Main] RecvVmasd worker process create failed. [%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}
	else
	{
		// 현재의 Main 프로세스
		_LOG( LINF, "[Main] RecvVmasd worker process created.. PID[%d]", g_pidWorkerRecvVmasd );

		// 잠시 대기
		struct timespec sleep;
		sleep.tv_sec = 0;
		sleep.tv_nsec = 200000000;  // 0.2 sec
		nanosleep( &sleep, NULL );
	}

	return true;
}

// csagentd 가 전송하는 FHS system 상태 정보 수신을 담당할 Worker 프로세스 생성.
bool MakeWorkerRecvSystem()
{
	// fork() 결과를 RecvSystem Worker 프로세스의 PID 정보 저장 목적 전역 변수에 저장.	
	g_pidWorkerRecvSystem = fork();
	
	if( g_pidWorkerRecvSystem == 0 )
	{
		// 생성된 Worker 프로세스인 경우...
		int iResult;

		// Worker RecvSystem 프로세스의 Main 함수 호출
		iResult = WorkerRecvSystemMain();

		// Worker RecvSystem 프로세스의 Main 함수 종료시 종료 처리
		_LOG( LINF, "[Main] RecvSystem worker process exit. PID[%d] return[%d]", getpid(), iResult );

		ReadyToExit();
		exit( iResult );
	}
	else if( g_pidWorkerRecvSystem < 0 )
	{
		// fork 실패시
		int errorNum = errno;
		LOG( LERR, "[Main] RecvSystem worker process create failed. [%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}
	else
	{
		// 현재의 Main 프로세스
		_LOG( LINF, "[Main] RecvSystem worker process created.. PID[%d]", g_pidWorkerRecvSystem );
				
		// 잠시 대기
		struct timespec sleep;
		sleep.tv_sec = 0;
		sleep.tv_nsec = 200000000;  // 0.2 sec
		nanosleep( &sleep, NULL );
	}
	
	return true;	
}


// csagentd 가 전송하는 FHS check 상태 정보 수신을 담당할 Worker 프로세스 생성.
bool MakeWorkerRecvCheck()
{
	// fork() 결과를 RecvCheck Worker 프로세스의 PID 정보 저장 목적 전역변수에 저장.
	g_pidWorkerRecvCheck = fork();

	if( g_pidWorkerRecvCheck == 0 )
	{
		// 생성된 Worker 프로세스인 경우...
		int iResult;

		// Worker RecvCheck 프로세스의 Main 함수 호출
		iResult = WorkerRecvCheckMain();

		// Worker RecvCheck 프로세스의 Main 함수 종료시 종료 처리
		_LOG( LINF, "[Main] RecvCheck worker process exit. PID[%d] return[%d]", getpid(), iResult );

		ReadyToExit();
		exit( iResult );
	}
	else if( g_pidWorkerRecvCheck < 0 )
	{
		// fork 실패시
		int errorNum = errno;
		LOG( LERR, "[Main] RecvCheck worker process create failed. [%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}
	else
	{
		// 현재의 Main 프로세스
		_LOG( LINF, "[Main] RecvCheck worker process created.. PID[%d]", g_pidWorkerRecvCheck );

		// 잠시 대기
		struct timespec sleep;
		sleep.tv_sec = 0;
		sleep.tv_nsec = 200000000;  // 0.2 sec
		nanosleep( &sleep, NULL );
	}
	
	return true;
}

// 각 Client 의 요청을 수신하여.. Alive FHS 목록 등의 정보 전달을 담당할 Worker 프로세스 생성.
bool MakeWorkerSendInfo()
{
	// fork() 결과를 SendInfo Worker 프로세스의 PID 정보 저장 목적 전역변수에 저장.
	g_pidWorkerSendInfo = fork();

	if( g_pidWorkerSendInfo == 0 )
	{
		// 생성된 Worker 프로세스인 경우...
		int iResult;

		// Worker SendInfo 프로세스의 Main 함수 호출
		iResult = WorkerSendInfoMain();

		// Worker SendInfo 프로세스의 Main 함수 종료시 종료 처리
		_LOG( LINF, "[Main] SendInfo worker process exit. PID[%d] return[%d]", getpid(), iResult );

		ReadyToExit();
		exit( iResult );
	}
	else if( g_pidWorkerSendInfo < 0 )
	{
		// fork 실패시
		int errorNum = errno;
		LOG( LERR, "[Main] SendInfo worker process create failed. [%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}
	else
	{
		// 현재의 Main 프로세스
		_LOG( LINF, "[Main] SendInfo worker process created.. PID[%d]", g_pidWorkerSendInfo );

		// 잠시 대기
		struct timespec sleep;
		sleep.tv_sec = 0;
		sleep.tv_nsec = 200000000;  // 0.2 sec
		nanosleep( &sleep, NULL );
	}

	return true;
}

// 각 FHS 정보의 GMS 전송, Top FHS 정보의 RCDB 업데이트 등을 수행할 Worker 프로세스 생성.
bool MakeWorkerControl()
{
	// fork() 결과를 Control Worker 프로세스의 PID 정보 저장 목적 전역변수에 저장.
	g_pidWorkerControl = fork();

	if( g_pidWorkerControl == 0 )
	{
		// 생성된 Worker 프로세스인 경우...
		int iResult;

		// Worker Control 프로세스의 Main 함수 호출
		iResult = WorkerControlMain();

		// Worker Control 프로세스의 Main 함수 종료시 종료 처리
		_LOG( LINF, "[Main] Control worker process exit. PID[%d] return[%d]", getpid(), iResult );

		ReadyToExit();
		exit( iResult );
	}
	else if( g_pidWorkerControl < 0 )
	{
		// fork 실패시
		int errorNum = errno;
		LOG( LERR, "[Main] Control worker process create failed. [%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}
	else
	{
		// 현재의 Main 프로세스
		_LOG( LINF, "[Main] Control worker process created.. PID[%d]", g_pidWorkerControl );
				
		// 잠시 대기
		struct timespec sleep;
		sleep.tv_sec = 0;
		sleep.tv_nsec = 200000000;  // 0.2 sec
		nanosleep( &sleep, NULL );
	}

	return true;
}

// FHS vmasd 로 부터 Broadcast 되는 FHS 상태 정보 수신을 위한 UDP Receive Socket 생성
bool MakeVmasdRecvSocket()
{
	// 기존 Socket close 처리.
	if( g_sockVmasdRecv != -1 )
	{
		close( g_sockVmasdRecv );
		g_sockVmasdRecv = -1;
	}

	// UDP Socket 생성 처리.
	g_sockVmasdRecv = socket( AF_INET, SOCK_DGRAM, 0 );
	if( g_sockVmasdRecv < 0 )
	{
		int errorNum = errno;
		LOG( LERR, "vmasd udp broadcast receive socket create failed.[%d][%s]", errorNum, strerror( errorNum ) );
		return false;
	}

	// Socket option 설정 처리.
	int result = 0;
	int opt = 1;

	// socket port reuse option set
	opt = 1;
	result = setsockopt( g_sockVmasdRecv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof( opt ) );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "vmasd udp broadcast receive socket option [SO_REUSEADDR] set failed. [%d][%s]", errorNum, strerror( errorNum ) );

		close( g_sockVmasdRecv );
		g_sockVmasdRecv = -1;

		return false;
	}

	// vmasd broadcasting 통신을 수신해야 하는 g_sockUdpRecvVmasd 에는 SO_BROADCAST 옵션을 설정한다.
	opt = 1;
	result = setsockopt( g_sockVmasdRecv, SOL_SOCKET, SO_BROADCAST, &opt, sizeof( opt ) );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "vmasd udp broadcast receive socket option [SO_BROADCAST] set failed. [%d][%s]", errorNum, strerror( errorNum ) );

		close( g_sockVmasdRecv );
		g_sockVmasdRecv = -1;

		return false;
	}

	// bind 처리를 위한 설정
	struct sockaddr_in recvSocketAddr;
	socklen_t socketAddrLen = 0;

	memset( &recvSocketAddr, 0x00, sizeof( recvSocketAddr ) );
	socketAddrLen = sizeof( recvSocketAddr );

	recvSocketAddr.sin_family = AF_INET;
	recvSocketAddr.sin_port = htons( CProcessConfig::GetInstance()->GetFhsBroadcastRecvPort() );
	recvSocketAddr.sin_addr.s_addr = htonl( INADDR_ANY );

	// bind 처리.
	result = bind( g_sockVmasdRecv, ( struct sockaddr * )&recvSocketAddr, socketAddrLen );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "vmasd udp broadcast receive socket bind[%u] failed.[%d][%s]"
			 , CProcessConfig::GetInstance()->GetFhsBroadcastRecvPort(), errorNum, strerror( errorNum ) );

		close( g_sockVmasdRecv );
		g_sockVmasdRecv = -1;
		return false;
	}

	// logging 처리
	_LOG( LINF, "vmasd udp broadcast receive socket create success. udp port[%u]", CProcessConfig::GetInstance()->GetFhsBroadcastRecvPort() );


	return true;
}

// FHS csagentd 가 전송하는 시스템 상태 정보 수신을 위한 TCP listen socket 생성
bool MakeSystemRecvSocket()
{
	// 기존 Socket close 처리.
	if( g_sockSystemRecv != -1 )
	{
		close( g_sockSystemRecv );
		g_sockSystemRecv = -1;
	}

	// TCP Socket 생성 처리.
	g_sockSystemRecv = socket( AF_INET, SOCK_STREAM, 0 );
	if( g_sockSystemRecv < 0 )
	{
		int errorNum = errno;
		LOG( LERR, "system info receive tcp socket create failed.[%d][%s]", errorNum, strerror( errorNum ) );
		return false;
	}
	
	// Socket option 설정 처리.
	int result = 0;
	int opt = 1;

	// socket port reuse option set
	opt = 1;
	result = setsockopt( g_sockSystemRecv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof( opt ) );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "system info receive tcp socket option [SO_REUSEADDR] set failed. [%d][%s]", errorNum, strerror( errorNum ) );

		close( g_sockSystemRecv );
		g_sockSystemRecv = -1;

		return false;
	}
	
	// Keep Alive Option set
	opt = 1;
	result = setsockopt( g_sockSystemRecv, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "system info receive tcp socket option[SO_KEEPALIVE] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
		return false;
	}
	
	// bind 처리를 위한 설정
	struct sockaddr_in listenSocketAddr;
	socklen_t socketAddrLen = 0;

	memset( &listenSocketAddr, 0x00, sizeof( listenSocketAddr ) );
	socketAddrLen = sizeof( listenSocketAddr );

	listenSocketAddr.sin_family = AF_INET;
	listenSocketAddr.sin_port = htons( CProcessConfig::GetInstance()->GetPortRecvSystem() );
	listenSocketAddr.sin_addr.s_addr = htonl( INADDR_ANY );

	// bind 처리.
	result = bind( g_sockSystemRecv, ( struct sockaddr * )&listenSocketAddr, socketAddrLen );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "system info receive tcp socket bind[%u] failed.[%d][%s]"
			 , CProcessConfig::GetInstance()->GetPortRecvSystem(), errorNum, strerror( errorNum ) );

		close( g_sockSystemRecv );
		g_sockSystemRecv = -1;
		return false;
	}

	// Socket Listen
	result = listen( g_sockSystemRecv, DEFAULT_ACCEPT_WAIT_COUNT );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "system info receive tcp socket listen failed.[%d][%s]"
			 , errorNum, strerror( errorNum ) );

		close( g_sockSystemRecv );
		g_sockSystemRecv = -1;
		return false;
	}

	// logging 처리
	_LOG( LINF, "system info receive tcp socket create success. tcp port[%u]", CProcessConfig::GetInstance()->GetPortRecvSystem() );
	
	return true;
}

// FHS csagentd 가 전송하는 서비스 상태 체크 정보 수신을 위한 TCP listen socket 생성
bool MakeCheckRecvSocket()
{
	// 기존 Socket close 처리.
	if( g_sockCheckRecv != -1 )
	{
		close( g_sockCheckRecv );
		g_sockCheckRecv = -1;
	}

	// TCP Socket 생성 처리.
	g_sockCheckRecv = socket( AF_INET, SOCK_STREAM, 0 );
	if( g_sockCheckRecv < 0 )
	{
		int errorNum = errno;
		LOG( LERR, "service check info receive tcp socket create failed.[%d][%s]", errorNum, strerror( errorNum ) );
		return false;
	}

	// Socket option 설정 처리.
	int result = 0;
	int opt = 1;

	// socket port reuse option set
	opt = 1;
	result = setsockopt( g_sockCheckRecv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof( opt ) );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "service check info receive tcp socket option [SO_REUSEADDR] set failed. [%d][%s]", errorNum, strerror( errorNum ) );

		close( g_sockCheckRecv );
		g_sockCheckRecv = -1;

		return false;
	}

	// Keep Alive Option set
	opt = 1;
	result = setsockopt( g_sockCheckRecv, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "service check info receive tcp socket option[SO_KEEPALIVE] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
		return false;
	}

	// bind 처리를 위한 설정
	struct sockaddr_in listenSocketAddr;
	socklen_t socketAddrLen = 0;

	memset( &listenSocketAddr, 0x00, sizeof( listenSocketAddr ) );
	socketAddrLen = sizeof( listenSocketAddr );

	listenSocketAddr.sin_family = AF_INET;
	listenSocketAddr.sin_port = htons( CProcessConfig::GetInstance()->GetPortRecvCheck() );
	listenSocketAddr.sin_addr.s_addr = htonl( INADDR_ANY );

	// bind 처리.
	result = bind( g_sockCheckRecv, ( struct sockaddr * )&listenSocketAddr, socketAddrLen );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "service check info receive tcp socket bind[%u] failed.[%d][%s]"
			 , CProcessConfig::GetInstance()->GetPortRecvCheck(), errorNum, strerror( errorNum ) );

		close( g_sockCheckRecv );
		g_sockCheckRecv = -1;
		return false;
	}

	// Socket Listen
	result = listen( g_sockCheckRecv, DEFAULT_ACCEPT_WAIT_COUNT );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "service check info receive tcp socket listen failed.[%d][%s]"
			 , errorNum, strerror( errorNum ) );

		close( g_sockCheckRecv );
		g_sockCheckRecv = -1;
		return false;
	}

	// logging 처리
	_LOG( LINF, "service check info receive tcp socket create success. tcp port[%u]", CProcessConfig::GetInstance()->GetPortRecvCheck() );

	return true;
}

// fimngd 등과 같은 외부 모듈과 통신을 통해 정보를 전달하기 위한 TCP listen socket 생성
bool MakeInfoSendSocket()
{
	// 기존 Socket close 처리.
	if( g_sockInfoSend != -1 )
	{
		close( g_sockInfoSend );
		g_sockInfoSend = -1;
	}

	// TCP Socket 생성 처리.
	g_sockInfoSend = socket( AF_INET, SOCK_STREAM, 0 );
	if( g_sockInfoSend < 0 )
	{
		int errorNum = errno;
		LOG( LERR, "info send tcp socket create failed.[%d][%s]", errorNum, strerror( errorNum ) );
		return false;
	}

	// Socket option 설정 처리.
	int result = 0;
	int opt = 1;

	// socket port reuse option set
	opt = 1;
	result = setsockopt( g_sockInfoSend, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof( opt ) );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "info send tcp socket option [SO_REUSEADDR] set failed. [%d][%s]", errorNum, strerror( errorNum ) );

		close( g_sockInfoSend );
		g_sockInfoSend = -1;

		return false;
	}

	// Keep Alive Option set
	opt = 1;
	result = setsockopt( g_sockInfoSend, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "info send tcp socket option[SO_KEEPALIVE] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
		return false;
	}

	// bind 처리를 위한 설정
	struct sockaddr_in listenSocketAddr;
	socklen_t socketAddrLen = 0;

	memset( &listenSocketAddr, 0x00, sizeof( listenSocketAddr ) );
	socketAddrLen = sizeof( listenSocketAddr );

	listenSocketAddr.sin_family = AF_INET;
	listenSocketAddr.sin_port = htons( CProcessConfig::GetInstance()->GetPortSendInfo() );
	listenSocketAddr.sin_addr.s_addr = htonl( INADDR_ANY );

	// bind 처리.
	result = bind( g_sockInfoSend, ( struct sockaddr * )&listenSocketAddr, socketAddrLen );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "info send tcp socket bind[%u] failed.[%d][%s]"
			 , CProcessConfig::GetInstance()->GetPortSendInfo(), errorNum, strerror( errorNum ) );

		close( g_sockInfoSend );
		g_sockInfoSend = -1;
		return false;
	}

	// Socket Listen
	result = listen( g_sockInfoSend, DEFAULT_ACCEPT_WAIT_COUNT );
	if( result != 0 )
	{
		int errorNum = errno;
		LOG( LERR, "info send tcp socket listen failed.[%d][%s]"
			 , errorNum, strerror( errorNum ) );

		close( g_sockInfoSend );
		g_sockInfoSend = -1;
		return false;
	}

	// logging 처리
	_LOG( LINF, "info send tcp socket create success. tcp port[%u]", CProcessConfig::GetInstance()->GetPortSendInfo() );

	return true;
}
