/***************************************************************************
				Worker (cc_tsd)
                -----------------------------------------

	begin      : 2013/09/16
	copyright  : (C) 2005 Solbox Inc.
	author     : Development 1 Team
					- 2013/09/16
	email      : dev1@solbox.com
	version    : 3.2.0

	CopyRight(C) 2005 Solbox Inc. All Rights reserved.
	Redistribution and use in source and binary forms, with or with out
	modification, are not permitted in outside of Solbox Inc.
***************************************************************************/

#include "Worker.h"

#include <signal.h>
#include <stdlib.h>

#include "Logger.h"
#include "Configs.h"
#include "LoginInfoInserter.h"
#include "ServiceInfoUpdater.h"
#include "DataQueue.h"
#include "InterfaceServer.h"

extern CInterfaceServer _iServer;

/// @brief Worker Process 의 main 함수
int WorkerMain( )
{
	// Set Signal Handler
	SetSignalWorker();
    
#if defined(__FreeBSD__)
	setproctitle( "Worker [initialize process]");
#endif
    
	CQueue<CLoginInfo> queueLoginInfo;

    // 1. 서비스 정보를 주기적으로 가져오는 클래스를 생성한다.
    // Global     
	CServiceInfoUpdater objServiceInfoUpdater;
	if ( objServiceInfoUpdater.Init(CMyConfig::GetInstance()->GetDBIP(),
							CMyConfig::GetInstance()->GetDBPort(),
							CMyConfig::GetInstance()->GetDBName(),
							CMyConfig::GetInstance()->GetDBAccount(),
							CMyConfig::GetInstance()->GetDBPwd()) == false)
	{
		LOG(LERR, "Worker[%d]: CLoginInfoInserter init failed => Worker Exit", getpid() );
		exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
	}
	objServiceInfoUpdater.Start();
    
	CLoginInfoInserter objLoginInfoInserter(&queueLoginInfo);
	// LOGIN_INFO_INSERT 기능이 on인 경우만 CLoginInfoInserter thread가 구동되도록한다.
	if( CMyConfig::GetInstance()->GetLoginInfo() == true)
	{
		// 2. Login Info 정보를 주기적으로 insert할 클래스를 생성한다.
		//-- 각 객체에 대해 초기화 및 시작 할 수 있는지 먼저 확인한다.
		if ( objLoginInfoInserter.Init(CMyConfig::GetInstance()->GetDBIP(),
								CMyConfig::GetInstance()->GetDBPort(),
								CMyConfig::GetInstance()->GetDBName(),
								CMyConfig::GetInstance()->GetDBAccount(),
								CMyConfig::GetInstance()->GetDBPwd()) == false)
		{
			LOG(LERR, "Worker[%d]: CLoginInfoInserter init failed => Worker Exit", getpid() );
			exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
		}
		objLoginInfoInserter.Start();
	}
	
	// 3. socket 생성 후 accept가 되면 Job Thread를 생성하여 socket을 이관한다.
	_iServer.Init(&queueLoginInfo, &objServiceInfoUpdater);
	_iServer.start();

	
	// 생성된 Thread 의 Join 처리 ???
	while( 1 )
	{
		// 그냥 시그널 대기
		pause();
	}
    
	// Process 종료 처리.
	LOG( LWAR, "Worker Process[%d] exit.. Good Bye..", getpid());
    
    //잠시 대기 후 종료처리.
	usleep(500000);
	// exit( EXIT_SUCCESS );
	return EXIT_SUCCESS;	// fork 를 수행한 함수에서 exit 함수 호출을 통한 종료처리 (Guard.cpp)
}

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

/// @brief Worker Process 에서 Core 생성 관련 Signal 을 받은 경우 이를 처리하기 위한 함수.
/// @param nSignalNumber [in] 발생한 시그널 Number
/// @return void
static void SigCoreWorker( int nSignalNumber )
{
	LOG( LERR, "Worker Process[%d] abnoraml exit. Check Core file. Receive signal [%d]", getpid(), nSignalNumber );
	LOG( LERR, "Core file path : [%s/%s.core]", CMyConfig::GetInstance()->GetAppLogRoot(), PROG_NAME );

	// Process 종료관련 작업 추가
    CLogger::Exit();
    CMyConfig::Exit();
    
	// Log Directory 상에 Core 파일 생성처리.
	chdir( CMyConfig::GetInstance()->GetAppLogRoot() );

	signal( nSignalNumber, SIG_DFL );

	// Core dump 생성을 위한 신호 발생처리.=> ?
	raise( nSignalNumber );
}

/// @brief Worker 프로세스 signal 처리 설정을 위한 함수	
/// @return void	
void SetSignalWorker( 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 키를 누른 경우 받는 신호 => daemon 으로 기동되기 땜시 이 신호 못받음 */
	sigaction( SIGQUIT, &act, NULL);	/* 키보드에 의한  Abort 신호 처리 => ? */
	
	// Worker 프로세스는 자식 프로세스가 존재하지 않으므로 그냥 무시처리함.
	//act.sa_handler = SigChldWorker;
	sigaction( SIGCHLD, &act, NULL);    
	
	/* 각종 에러나 사용자의 종료 신호 처리 */
	act.sa_handler = SigTermWorker;
	sigaction( SIGTERM, &act, NULL);	/* kill -TERM 에 의한 프로세스 종료시 */

	/* Core 관련 signal 처리 : 오류 처리 및 Debug(core dump) 목적 */
	act.sa_handler = SigCoreWorker;
	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);
}
