#include "fhs_raidmond.h"
#include "Child.h"
#include "SmsSender.h"
#include "ArecaWrapper.h"
#include "UdpListener.h"
#include "EventDataManager.h"
#include "AutoDisableUpload.h"


#define MAXSIZEOFSTR 128

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

	usleep(500000);
	exit( EXIT_SUCCESS );
}
/// @brief child Process 에서 Core 생성 관련 Signal 을 받은 경우 이를 처리하기 위한 함수.
/// @param nSignalNumber [in] 발생한 시그널 Number
/// @return void
static void SigCoreChild( int nSignalNumber )
{
	_LOG(LERR, "Child Process[%d] abnoraml exit. Check Core file. Receive signal [%d]", getpid(), nSignalNumber );
	//ERROR_LOG( "Core file path : [%s/%s.core]", g_envConfig.szLogDir.c_str(), PROG_NAME );
	// Process 종료관련 작업 추가

	// Log Directory 상에 Core 파일 생성처리.
	chdir( g_envConfig.szLogDir.c_str() );
	signal( nSignalNumber, SIG_DFL );
	// Core dump 생성을 위한 신호 발생처리.=> ?
	raise( nSignalNumber );
}


/// @brief child process signal 처리 설정을 위한 함수	
/// @return void	
static void SetSignalChild( void )
{
	sigset_t set;
	struct sigaction act;
	sigfillset( &set );
	sigprocmask( SIG_SETMASK, &set, NULL );
	memset( &act, 0x00, sizeof(act) );
	sigfillset( &act.sa_mask );
	/* 무시할 신호 목록 */
	act.sa_handler = SIG_IGN;
	sigaction( SIGPIPE, &act, NULL);	/* 파이프 디스크립터 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
	sigaction( SIGHUP , &act, NULL);	/* Process를 기동시킨 관리자의 로그아웃시 발생 시그널 */
	sigaction( SIGINT , &act, NULL);	/* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
	sigaction( SIGQUIT, &act, NULL);	/* 키보드에 의한  Abort 신호 처리 => ? */
	// Child 프로세스는 자식 프로세스가 존재하지 않으므로 그냥 무시처리함.
	sigaction( SIGCHLD, &act, NULL);    
	/* 각종 에러나 사용자의 종료 신호 처리 */
	act.sa_handler = SigTermChild;
	sigaction( SIGTERM, &act, NULL);	/* kill -TERM 에 의한 프로세스 종료시 */
	/* Core 관련 signal 처리 : 오류 처리 및 Debug(core dump) 목적 */
	// dadamin : 팀 기준 적용
	//act.sa_handler = SigCoreChild;
	//sigaction( SIGILL , &act, NULL);	/* Illegal instruction */
	//sigaction( SIGFPE , &act, NULL);	/* Erroneout arithmetic operation */
	//sigaction( SIGBUS , &act, NULL);	/* Access to undefined portion of a memory object */
	//sigaction( SIGSEGV, &act, NULL);	/* Invalid memory reference */
	//sigaction( SIGSYS , &act, NULL);	/* Bad System Call */
	//sigaction( SIGXCPU, &act, NULL);	/* CPU-time limit exceeded */
	//sigaction( SIGXFSZ, &act, NULL);	/* File-size limit exceeded */

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

/// @brief Child Process 의 main 함수
int ChildMain(void) 
{
	bool bRet = false;
	int nFailCnt = 0;
	// set child signal
	SetSignalChild();

#if defined(__FreeBSD__)
	setproctitle( "Child [initialize process]");
#endif


	char szTime[MAXSIZEOFSTR];
	time_t ltime;
	struct tm *pTm = NULL;
	char szHostname[MAXSIZEOFSTR];

	// ** SMS Sender 객체 생성 및 초기화
	CSmsSender objSmsSender(g_envConfig.szSMSHost, g_envConfig.nSMSPort, g_envConfig.szSMSSendfile);
	bRet = objSmsSender.Init();
	if( bRet == false )
	{
		LOG(LERR, "Initializing SmsSender Object has failed.");
		exit(EXIT_FAILURE);
	}

	// ** SMS Sender 객체 생성 및 초기화
	// 2015-06-26 두 번째 파라메터는 더이상 의미가 없다.
	CArecaWrapper objAreca(g_envConfig.nEventLevel);
	if( objAreca.InitArecaControllers() < 0 )
	{
		LOG(LERR, "Initializing Areca RAID Controller has failed.");
		exit(EXIT_FAILURE);
	}

	// ** EventDataManager 객체 생성 및 초기화
	CEventDataManager objDataManager;
	bRet = objDataManager.Init();
	if( bRet == false )
	{
		LOG(LERR, "Initializing SmsSender Object has failed.");
		exit(EXIT_FAILURE);
	}

	// ** Raid Event Monitor 객체 생성 및 초기화

	// ** UDP Listener 객체 생성 및 초기화
	CUdpListener udpObj(&objDataManager);
	// report thread port  : 
	bRet = udpObj.Init(g_envConfig.nReportPort);
			
	if( bRet == false )
	{
		LOG(LERR, "Initializing UdpListener Object has failed.");
		exit(EXIT_FAILURE);
	}
	udpObj.Start();

	// 현재 hostname
	// hostname이 변경되면 반영되지 않는다. 반영이 꼭필요하다면 while구문 안으로 넣으면 된다.
	if( ::gethostname(szHostname, MAXSIZEOFSTR) < 0 )
	{
		_LOG(LWAR, "gethostname failed. : %s", szHostname);
		::snprintf(szHostname, MAXSIZEOFSTR, "%s", "error.hostname.com");
	}
	nFailCnt = 0;
	while( 1 )
	{
		list<CEventData> listRet;

		if( objAreca.GetEvent(GET_EVENT_ALL, listRet) == false )
		{
			if( nFailCnt < 3 )
			{
				nFailCnt++;
				_LOG(LWAR, "Get RAID Event Data Failed.");
			}
			else
			{
				LOG(LERR, "Get RAID Event Data Failed. %d(th). Send SMS.",nFailCnt);
				nFailCnt = 0;

				// 현재 시각 설정 
				time((time_t *)&ltime);
				pTm = ::localtime((time_t *)&ltime);
				::memset(szTime, 0, sizeof(szTime));
				::snprintf(szTime, MAXSIZEOFSTR, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
						pTm->tm_year+1900, pTm->tm_mon +1, pTm->tm_mday,
						pTm->tm_hour,  pTm->tm_min,  pTm->tm_sec);

				objSmsSender.SendToWebServ(szHostname, szTime ,"Can NOT Receive Data From RAID Controller!");
			}
			sleep(g_envConfig.nCheckTerm);
			continue;
		}
		nFailCnt = 0; // 성공하면 실패 카운트를 리셋한다.

		// 받아온 Raid Event 저장
		bRet = objDataManager.SetCurrentData(listRet);
		if( bRet == false )
		{
			LOG(LERR, "SetCurrentData Failed. but it's Impossible. Check ArecaWrapper::GetEvent().");
			sleep(g_envConfig.nCheckTerm);
			continue;
		}
		// SMS 전송 목록 
		listRet.clear();
		bRet = objDataManager.GetSmsEvent(listRet);
		if( bRet == false )
		{
			LOG(LERR, "GetSmsEvent() failed.");
			sleep(g_envConfig.nCheckTerm);
			continue;
		}


		for (list<CEventData>::iterator it=listRet.begin(); it!=listRet.end(); it++)
		{
			// NEW 2019-09-16 huibong 불필요 "Battery Module Failed" event 는 전송하지 않는다. (#32795)
			//                       - "Battery Module Failed" event 는 운영자가 수신해도 할 작업이 없음
			//                       - 하지만 일 10~20 건 정도가 발생하여 운영자에게 불필요 event 가 전달됨
			//                       - 이와 관련 KT 운영팀 윤철희 실장이 해당 event 는 무시하도록 처리해 달라고 해서...
			//                       - 해당 메시지 발생시 운영자에게 event 전송하지 않도록 한다.
			//                       - 실제로 CEventDataManager 모듈에서도 처리가능하지만... 
			//                       - 본 모듈에서 실제 event 전송을 담당하므로.. 본 모듈에서 무시 처리하도록 수정함.
			// BUG 2020-01-10 huibong 불필요 "Battery Module Failed" event 미전송 관련 버그 수정 (#32911)
			//                       - 실제로 Module 단어와 Failed 단어 사이에 공백이 3개 존재
			//                       - 이로 인해 해당 event 가 계속 전송되는 상황 발견됨
			//                       - 이에 Battery Module" check 후 Failed 문자열 check 하도록 수정
			std::string::size_type find_result;
			find_result = it->m_szEventData.find( "Battery Module" );
			if( find_result != std::string::npos )
			{
				find_result = it->m_szEventData.find( "Fail" );
				if( find_result != std::string::npos )
				{
					_LOG( LDBG, "Do not send event with operator requests [%s]", it->m_szEventData.c_str() );
					continue;
				}
			}


			pTm = ::localtime(&(it->m_evtTime));
			::memset(szTime, 0, sizeof(szTime));
			::snprintf(szTime, MAXSIZEOFSTR, "%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d",
					pTm->tm_year+1900, pTm->tm_mon +1, pTm->tm_mday,
					pTm->tm_hour,  pTm->tm_min,  pTm->tm_sec);
			_LOG(LWAR, "SMS Sent.[%s::%s:%s]",szHostname, szTime, it->m_szEventData.c_str());
			//FIXME:호출하면 SMS가 발송된다... :많은사람에게 문자가 수신되므로 디버깅 때는 가급적 자제할것. please........
			objSmsSender.SendToWebServ(szHostname, szTime ,it->m_szEventData.c_str());

			/// 2011.07.26, 수정자 : 장희준
			/// 업로드 자동 방지 
			CAutoDisableUpload autoDisableUpload;
			autoDisableUpload.DoProcess( it->GetEventCategory(), it->GetEventId()  );
		}
		sleep(g_envConfig.nCheckTerm);
	}


	// wait
	while(1)
	{
		// 그냥 시그널 대기
		pause();
	}

	//잠시 대기 후 종료처리.
	usleep(500000);
	// exit( EXIT_SUCCESS );
	return EXIT_SUCCESS;	// fork 를 수행한 함수에서 exit 함수 호출을 통한 종료처리 
}
