#include "WorkerControl.h"

#include "Logger.h"
#include "ProcessRename.h"
#include "ProcessConfig.h"
#include "LocalDbClearThread.h"
#include "StorageUsedCalcThread.h"
#include "StorageCorrectThread.h"
#include "TrafficControlThread.h"
#include "NetworkSendThread.h"
#include "StorageSendThread.h"

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


// FHS 의 opendav-listener 로 부터 서비스 관련 통계 (Network, 세션등의 통계 등)정보를 수신하기 위한 UDP socket...
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockDavListenerRecv;

// FHS fimngd 가 전송하는 서비스별 Network 통계 정보 수신을 위한 TCP Socket
// Main.cpp 에 전역변수로 정의 되어 있다.
extern int g_sockServiceStatRecv;


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

	return;
}

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

	ReadyToExitWorkerControl();

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

// Worker::Control Process 의 signal 처리기...
void SetWorkerControlSignalHandler()
{
	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 Control 의 child process 종료에 대한 처리기 설정. 
	// -> Storage 사용량 계산 관련하여.. 외부 프로세스인 rc_du 를 system 명령으로 수행함.
	// -> 이에 따라.. 시그널 처리기를 설정할 경우... 
	//    상황에 따라서.. 시그널 처리기에서 system() 함수에서 발생된 시그널을 먼저 Catch 하여... system() 함수 반환값이 -1이 되는 상황이 발생함.
	// -> 따라서 다음과 같이 default 처리토록 변경한다.	
	act.sa_handler = SIG_DFL;
	sigaction( SIGCHLD, &act, NULL );	

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


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




// Worker::Control 프로세스의 Main 역활을 수행하기 위한 함수.
int WorkerControlMain()
{
	// 전역 변수로 설정된 socket 중 해당 worker 에서 사용하지 않는 socket 은 close 처리한다.
	if( g_sockServiceStatRecv != -1 )
	{
		close( g_sockServiceStatRecv );
		g_sockServiceStatRecv = -1;
	}
	
	// Process Rename..
	set_ps_display( PROG_NAME": Control", false );
	
	// Set Signal Handler
	SetWorkerControlSignalHandler();


	// 사용할 객체 초기화 처리

	// 1. Local sqlite3 DB 를 주기적으로 정리하는 Thread 를 생성후 기동한다.
	CLocalDbClearThread threadClearLocalDb;
	if( threadClearLocalDb.Start() == false )
	{
		_LOG( LWAR, "[Control:%d] Local db clear thread create failed. => Process Exit", getpid() );
		return EXIT_FAILURE;	// EXIT_FAILURE 를 반환하여 재생성 처리 방지...	
	}

	// 2. 서비스별 Storage 사용량 정보 계산 처리를 담당할 Thread 를 생성 후 기동한다.
	CStorageUsedCalcThread threadStorageUsedCalc;
	if( threadStorageUsedCalc.Start() == false )
	{
		_LOG( LWAR, "[Control:%d] Storage used calc thread create failed. => Process Exit", getpid() );
		return EXIT_FAILURE;	// EXIT_FAILURE 를 반환하여 재생성 처리 방지...
	}

	// 3. Storage 통계 Data 에 대한 보정 처리 Thread 를 기동시킨다.
	CStorageCorrectThread threadCorrectStorage;
	if( threadCorrectStorage.Start() == false )
	{
		_LOG( LWAR, "[Control:%d] Storage stat correct thread create failed. => Process Exit", getpid() );
		return EXIT_FAILURE;	// EXIT_FAILURE 를 반환하여 재생성 처리 방지...
	}

	// 4. 서비스별 Network 통계 정보를 바탕으로 트래픽 제어 정보를 주기적으로 재계산하여 RCDB 에 업데이트하는 Thread 를 생성 후 기동한다.
	CTrafficControlThread threadTrafficControl;
	if( threadTrafficControl.Start() == false )
	{
		_LOG( LWAR, "[Control:%d] Traffic control thread create failed. => Process Exit", getpid() );
		return EXIT_FAILURE;	// EXIT_FAILURE 를 반환하여 재생성 처리 방지...
	}

	// 5. 서비스별 Network 통계 정보를 주기적으로 GTS cc_statd 로 전송하기 위한 Thread 를 생성 후 기동한다.
	CNetworkSendThread threadSendNetworkToGts;
	if( threadSendNetworkToGts.Start() == false )
	{
		_LOG( LWAR, "[Control:%d] Network stats send to GTS thread create failed. => Process Exit", getpid() );
		return EXIT_FAILURE;	// EXIT_FAILURE 를 반환하여 재생성 처리 방지...
	}

	// 6. 서비스별 Storage 통계 정보를 GTS 로 전송.
	CStorageSendThread threadSendStorageToGts;
	if( threadSendStorageToGts.Start() == false )
	{
		_LOG( LWAR, "[Control:%d] Storage stats send to GTS thread create failed. = > Process Exit", getpid() );
		return EXIT_FAILURE;	// EXIT_FAILURE 를 반환하여 재생성 처리 방지...
	}
	

	// 대기
	while( 1 )
	{
		// 할일 없어서.. 시그널 수신까지 대기 한다.
		pause();
	}
	

	// 종료시 정리 작업 수행
	ReadyToExitWorkerControl();
	return EXIT_SUCCESS;
}


