/****************************************************************************
                cc_statd Main ( main.cpp )
                -----------------------------------------

    begin      : 2013/05/28
    copyright  : (C) 2013 Solbox Inc.
    author     : Development 1 Team
					- 2013/05/28 - 1st dadamin
    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 "cc_statd.h"
#include "ArgParser.h"
#include "Configs.h"
#include "Signals.h"
#include "Worker.h"
#include "Logger.h"
 
static bool _isExit = false;
static bool _isDaemon = true;

// print Version
static void Version()
{
	cerr << "Version: " PROG_NAME " " PROG_VERSION << endl;
}

// print usage 
static void Usage()
{
	cerr << "Usage: " << PROG_NAME " [-c file]" << endl;
	cerr << "                [-v] [-h] [-D]" << endl;
	cerr << "Options: "  << endl;
	cerr << "  -v		: show version number" << endl;
	cerr << "  -h		: list available command line options (this page)" << endl;
	cerr << "  -D		: run console mode" << endl;
	cerr << "  -c file	: process directive reading config file" << endl;
	cerr << endl << endl;
	cerr << PROG_NAME <<" is a daemon to be stored in the CCDB the statistics of RC." << endl;
}

// signal function
static void SigTermMain( int nSignalNumber )
{
	if(_isExit)	return;
	_isExit = true;

	ostringstream msg;
	// Signal Number 에 따른 로깅처리.
	if( nSignalNumber == SIGTERM )
	{
		msg << "Main Process [" << getpid() << "] exit job start by user signal [SIGTERM]";
	}
	else
	{
		msg << "Main Process [" << getpid() << "] exit job start by user signal ["<< nSignalNumber <<"]";
	}

	cout << msg.str() << endl;
	_LOG(LWAR, msg.str().c_str());
	
	// KILL - Child
	kill(0, SIGTERM);

	// waitpid
	while(waitpid(-1, NULL, WNOHANG) > 0);
}

static void SigChldMain( int nSignalNumber )
{
	pid_t killPid;
	int nKillStatus;

	if(_isExit)
		return;

	while( ( killPid = waitpid( -1, &nKillStatus, WNOHANG ) ) > 0 )
	{
		
		if(WIFEXITED(nKillStatus))
		{
			// 자식 프로세스가 정상적으로 종료되었는지 검사.
			LOG( LWAR, "Worker process[%d] killed by signal[SIGTERM]", killPid);
		}
		else if( WIFSIGNALED( nKillStatus ) )
		{
			// 자식 프로세스가 Signal 에 의해 종료되었는지 검사.
			LOG( LWAR, "Worker process[%d] killed by signal[%d]", killPid, WTERMSIG( nKillStatus ) );
		}
		else
		{
			LOG( LWAR, "Worker process[%d] killed. Not signal", killPid );
		}

		// Worker Process 재생성 처리.
		if( ReMakeProcessWorker(_isDaemon, killPid) == false )
		{
			// Worker Process 재성성 실패시 => 그냥 로깅
			LOG( LERR, "Worker process recreate failed.");
		}
		else
		{
			LOG( LWAR, "Worker process recreate success by SIGCHLD");
		}
	}

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

// check running Process
static 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 )
	{
		cerr << "[error] Process duplication check failed.[popen error][" << strerror(errno) << "]" << endl;
		// 오류 발생시 true 반환하여 프로세스 실행 방지처리
		return true;
	}
	else
	{
		memset( tempBuffer, 0x00, sizeof(tempBuffer) );
		while( fgets( tempBuffer, sizeof(tempBuffer)-1, fd) != NULL )
		{
			string tempPid( tempBuffer );
			//Trim(tempPid);
			if( atoi( tempPid.c_str() ) != getpid() )
			{
				cout << "[info] Process duplication found. pid[" << atoi( tempPid.c_str() ) << "]" << endl;
				bRun = true;
				break;
			}
		}
		pclose( fd );
		return bRun;
	}
}

// main function
int main( int argc, char * argv[] )
{
	string strConfPath = DEFAULT_CONFIG_FILE;
	
	// parse Input argument
	CArgParser argparser(argc, argv);

	if( argparser.checkvalue("-v") )
	{
		Version();
		return EXIT_SUCCESS;

	}

	if( argparser.checkvalue("-h") )
	{
		Usage();
		return EXIT_FAILURE;		
	}

	if ( argparser.checkvalue("-D") ) 
	{
		_isDaemon = false;
	}

	string val;
	if ( argparser.checkvalue("-c", &val) )
	{
#ifdef _DEBUG
		cout << "Change conf path " << strConfPath << " to " << val << endl;
#endif // _DEBUG
		strConfPath = val;
	}

	if(!argparser.empty())
	{
		cerr<< "[error] can't understand argument."  << endl;
		Usage();
		return EXIT_FAILURE;
	}

	// Check the program will duplicate
	if( IsCurrentProcessRun() )
	{
		cerr << "[warning] Process [" << PROG_NAME << "] is already running...." << endl;
		return EXIT_FAILURE;
	}
		
	//initialized Config object
	if(CMyConfig::Init( PROG_NAME, strConfPath ) == false)
	{
		cerr << "[error] Failed to initialize the config object." << endl;
		return EXIT_FAILURE;
	}

	// load config
	if( CMyConfig::GetInstance()->LoadConf() == false )
	{
		cerr << "[error] Config load error." << CMyConfig::GetInstance()->GetErrMessage() << endl;
		return EXIT_FAILURE;
	}

	if (CMyConfig::GetInstance()->CheckValue() == false)
	{
		cerr << "[error] Config load error." << CMyConfig::GetInstance()->GetErrMessage() << endl;
		return EXIT_FAILURE;
	}

	// initialized Log object
	if( CLogger::Init( PROG_NAME, CMyConfig::GetInstance()->GetAppLogRoot(), 
		CMyConfig::GetInstance()->GetAppLogLevel() ) == false )
	{
		cerr << "[error] Failed to initialize the log object." << endl;
		return EXIT_FAILURE;
	}	

	// set fail log path
	string faillog = CLogger::GetInstance()->GetLogDir() + "/fail";
	CMyConfig::GetInstance()->SetFailLogPath( faillog );

	cout << PROG_NAME << (_isDaemon ? " Daemonize" : " Console Mode") 
		<< "......." << endl; 

	// daemonize
	if(_isDaemon && daemon(1,0) == -1 )
	{
		cerr << "[error] Failed Daemonize.(errno : " << errno << ")" << endl;
		return EXIT_FAILURE;
	}
	_LOG( LINF, "Main Process [%d] Starting...", getpid());

	// set up signal handlers, now that we've daemonized/forked.
	SetIgnoreSignal(_isDaemon);
	SetSIGCHLD(SigChldMain);
	SetSIGTERM(_isDaemon, SigTermMain);
	
	// run work
	if( MakeProcessWorker( _isDaemon )  == false )
	{
		_isExit = true;
	}

	// signal wait
	while( _isExit == false )
	{
		pause();
	}

	// end
	_LOG( LINF, "Main Process [%d] exit job end. Good Bye..", getpid());
	CLogger::Exit();
	CMyConfig::Exit();
	cout << PROG_NAME << " End." << endl;

	return EXIT_SUCCESS;
}
