#include "WorkerSystemCollect.h"

#include "ProcessConfig.h"
#include "Logger.h"
#include "FhsShm.h"
#include "SystemInfo.h"

#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <time.h>


// 시스템 정보 추출 주기 (sec)
#define SYSTEM_COLLECT_INTERVAL     10  


// 공유메모리에 Data Set, Get 을 담당할 객체에 대한 포인터.
// Main 프로세스에서 생성 처리하며... 전역변수로 설정되어 있다.
extern CFhsShm * g_pFhsShm;


// Worker::SystemCollect 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerSystemCollectMain()
{
	setproctitle( "SystemCollect [initialize]" );

	// Set Signal Handler
	SetWorkerSystemCollectSignalHandler();


	// Main Process 에서 생성한 공유메모리 접근 객체 유효성 확인
	// NULL 인 경우... 초기화 실패로 처리한다.
	if( g_pFhsShm == NULL )
	{
		// 객체 생성 실패시
		LOG( LERR, "[SystemCollect:%d] Shared memory object not valid. process exit", getpid() );

		ReadyToExitWorkerSystemCollect();
		return EXIT_FAILURE;
	}

	// system info 수집을 담당할 객체 생성 및 초기화.
	CSystemInfo systemInfo( g_pFhsShm );
	if( systemInfo.Init() == false )
	{
		// 객체 초기화 실패시
		LOG( LERR, "[SystemCollect:%d] system info collect module init fail. process exit", getpid() );

		ReadyToExitWorkerSystemCollect();
		return EXIT_FAILURE;
	}
	
	
	// 전체 프로세스의 생성이 완료될때까지 대기 후 작업 시작
	sleep( 1 );
	setproctitle( "SystemCollect" );
	

	// 최초 기동인지 여부
	bool bFirstLoop = true;

	struct timespec timeStart;	// 소요시간 측정을 위한 멤버변수
	struct timespec timeEnd;	// 소요시간 측정을 위한 멤버변수
	double dElapsedTime;		// 소요시간 측정을 위한 멤버변수


	// 주기적으로 시스템 정보를 추출한다.
	while( 1 )
	{
		// check start time 
		clock_gettime( CLOCK_MONOTONIC, &timeStart );

		// System Check Start time 정보를 공유 메모리에 업데이트 (#31701)
		// - 정보 수집 Hang 발생을 탐지할 수 있도록 expire 처리를 위한 시간값 업데이트 기능 추가
		g_pFhsShm->UpdateTimeSystemCheck();

		// hostname
		// - 추출 실패시.. 업무 규약에 따라 프로세스 종료 처리
		// - shared memory 설정 실패시.. 업무 규약에 따라 프로세스 종료 처리
		if( systemInfo.GetHostname() == false )
		{
			LOG( LERR, "[System] hostname collect fail. => Process exit" );
			ReadyToExitWorkerSystemCollect();
			return EXIT_FAILURE;
		}
		_LOG( LINF, "[System] hostname collect passed." );


		// load average
		systemInfo.GetLoadAverage();
		_LOG( LINF, "[System] load average collect passed." );


		// total memory 
		// - 최초 loop 시에만 정보 수집 ( 오류발생시 프로세스 종료 처리)		
		if( bFirstLoop == true )		
		{
			if( systemInfo.GetMemoryTotal() == false )
			{
				LOG( LERR, "[System] total memory collect fail. => Process exit" );
				ReadyToExitWorkerSystemCollect();
				return EXIT_FAILURE;
			}
			_LOG( LINF, "[System] total memory collect passed." );
		}


		// free memory
		systemInfo.GetMemoryFree();
		_LOG( LINF, "[System] free memory collect passed." );

		
		// swap
		systemInfo.GetSwap();
		_LOG( LINF, "[System] swap memory collect passed." );
		

		// mount
		systemInfo.GetMount( CProcessConfig::GetInstance()->GetStorageRoot() );
		_LOG( LINF, "[System] storage mount & size info collect passed." );


		// disk status
		// - queue length, busy
		systemInfo.GetDiskStatus();
		_LOG( LINF, "[System] disk busy info collect passed." );


		// network status
		systemInfo.GetNetworkStatus();
		_LOG( LINF, "[System] network status collect passed." );


		// tcp session count
		systemInfo.GetTcpSession();
		_LOG( LINF, "[System] tcp session collect passed." );

		// Debug 인 경우에만.. 로그상에 shared memory 내역 출력 처리
		//if( CLogger::GetInstance()->GetLogLevel() == LDBG )
		//	g_pFhsShm->Print();


		/////////////////////////////////////

		// check end time 
		clock_gettime( CLOCK_MONOTONIC, &timeEnd );

		// 작업 소요 시간 계산
		dElapsedTime = timeEnd.tv_sec - timeStart.tv_sec;
		dElapsedTime += ( timeEnd.tv_nsec - timeStart.tv_nsec ) * 1e-9;

		_LOG( LINF, "[System] collect end. total elapsed time: %.3lf sec.", dElapsedTime );


		// 공유메모리 설정 내역을 로그 파일에 기록 처리한다.
		// - 본 루프가 SYSTEM_COLLECT_INTERVAL 간격으로 동작하므로.. 로깅 처리하기에 가장 적당하다고 판단됨.
		g_pFhsShm->Print();

		// loop 1회 실행했으므로.. 최초 loop 플래그를 false 로 변경.
		if( bFirstLoop == true )
			bFirstLoop = false;
		
		// 지정된 시간동안 sleep
		sleep( SYSTEM_COLLECT_INTERVAL );		
	}
	
	// 종료시 정리 작업 수행
	ReadyToExitWorkerSystemCollect();
	return EXIT_SUCCESS;
}

// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExitWorkerSystemCollect()
{
	// Logger 객체 종료 처리.
	CLogger::Exit();

	return;
}

// Worker SystemCollect 프로세스에 대한 대한 종료 처리 수신시.
static void SignalWorkerSystemCollectTerminate( int nSignalNumber )
{
	// Signal Number 에 따른 로깅처리.
	if( nSignalNumber == SIGTERM )
	{
		_LOG( LINF, "[SystemCollect:%d] Process exit by SIGTERM signal. Good Bye..", getpid() );
	}
	else
	{
		_LOG( LWAR, "[SystemCollect:%d] Process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
	}
	
	ReadyToExitWorkerSystemCollect();

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

// Worker::SystemCollect Process 의 signal 처리기...
void SetWorkerSystemCollectSignalHandler()
{
	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 프로세스는 자식 프로세스가 존재하지 않으므로 그냥 무시처리함.
	act.sa_handler = SIG_IGN;
	sigaction( SIGCHLD, &act, NULL );

	/* 각종 에러나 사용자의 종료 신호 처리 */
	act.sa_handler = SignalWorkerSystemCollectTerminate;
	sigaction( SIGTERM, &act, NULL );	/* kill -TERM 에 의한 프로세스 종료시 */

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