#include "AccountUpdateThread.h"
#include "DaemonConfigs.h"

#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <errno.h>

#ifdef __FreeBSD__
	#include <sys/param.h>
	#include <sys/ucred.h>
	#include <sys/mount.h>
#else	// Linux
	#include <mntent.h>
	#include <sys/vfs.h>
#endif


using namespace std;

#define DEFAULT_QUERY_BUFFER_SIZE           1024
#define QUERY_LIMIT_COUNT    2000

static void string_lower2(char *buf)
{
	int i;

	for (i = 0; *(buf + i) != '\0'; i++) {
		if (*(buf + i) >= 'A' && *(buf + i) <= 'Z') {
			*(buf + i) = *(buf + i) + ('a' - 'A');
		}
	}
}

CAccountUpdateThread::CAccountUpdateThread()
{
	m_pPgSQL = NULL;

	// 멤버 변수 초기화
	m_threadHandle = 0;
}

CAccountUpdateThread::~CAccountUpdateThread()
{
	DbClose();
}

/// @brief DB 연결 함수.
bool CAccountUpdateThread::DbConnect()
{
	if( m_pPgSQL != NULL )
	{
		DbClose();
	}

	m_pPgSQL = new DataBase;
	if( m_pPgSQL == NULL )
	{
		LOG(LERR, "Creating new Database has failed.");
		return false;
	}
	if( m_pPgSQL->PgOpenDB(m_szHost, m_nPort, m_szDBName, m_szAcct, m_szPasswd) == NULL )
	{
		LOG(LERR, "Connecting to Database has failed.");
		DbClose();
		return false;
	}
	return true;
}

/// @breif DB close 함수.
void CAccountUpdateThread::DbClose()
{
	if( m_pPgSQL != NULL )
	{
		m_pPgSQL->PgClear();
		delete m_pPgSQL;
		m_pPgSQL = NULL;
	}
}

bool CAccountUpdateThread::ThreadInit(const sig_atomic_t *sighandle, CSharedMem* pShmemAccount) 
{
	m_sighandle = sighandle;
	m_pShmemAccount = pShmemAccount;

	m_szHost = CDeamonConfig::GetInstance()->GetDBInfo().m_szRcdbIp;
	m_nPort = CDeamonConfig::GetInstance()->GetDBInfo().m_nRcdbPort;
	m_szDBName = CDeamonConfig::GetInstance()->GetDBInfo().m_szRcdbName;
	m_szAcct = CDeamonConfig::GetInstance()->GetDBInfo().m_szRcdbAcct;
	m_szPasswd = CDeamonConfig::GetInstance()->GetDBInfo().m_szRcdbAcctPw;

	m_szStorageRoot = CDeamonConfig::GetInstance()->GetFileStorageRoot();
	// 최초 초기화 시에는 DB연결 정보들이 제대로 되는것인지에 대한 
	// 확인만 처리 한다.
	if (DbConnect() == false )
	{
		LOG(LERR, "Connecting to Database has failed. Check your configuration or RCDB.");
		return false;
	}

	// 접속 성공한 경우...
	LOG( LINF, "RCDB connection ok..." );
	// DB연결 test 후 정리 한다.
	DbClose();
	return true;
}

void CAccountUpdateThread::printShredMemory()
{
	struct account* pSharedHeader = (struct account*)m_pShmemAccount->GetData();
	int nMaxAccout = CDeamonConfig::GetInstance()->GetMaxAccount();
	for(int i = 0; i < nMaxAccout; i++ )
	{
		// shared memory의 끝이면 종료...
		if( (pSharedHeader + i)->id[0] == '\0' )
		{
			LOG(LDBG, "Account Shared Memory End. [index: %d, %d]", i, (int)m_mapAccount.size() );
			break;
		}
		struct account* pCurAccount = (pSharedHeader + i);
		printac(pCurAccount);
	}
}
void CAccountUpdateThread::printac(struct account *pAc)
{
	LOG(LDBG, "xxxxx Account Shared Memory xxxxx");
	_LOG(LDBG, "	seq: %s", pAc->seq);
	_LOG(LDBG, "	id: %s", pAc->id);
	_LOG(LDBG, "	svc_id: %s", pAc->svc_id);
	_LOG(LDBG, "	pass: %s", pAc->pass);
	_LOG(LDBG, "	tran_id: %s", pAc->tran_id);
	_LOG(LDBG, "	iv: %s", pAc->iv);
	_LOG(LDBG, "	pk : %s", pAc->pk);
	_LOG(LDBG, "	tc_percentx: %d", pAc->tc_percent);
	_LOG(LDBG, "	tc_session: %d", pAc->tc_session);
	_LOG(LDBG, "	nFlag: %d", pAc->nFlag);
}

bool CAccountUpdateThread::GetData()
{
	char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
	bool is_user_auth = false, is_referer_chk = false;

	m_mapAccount.clear();

	if( m_pPgSQL == NULL )
	{
		LOG(LWAR, "PgSQL is not initialized.");		
		return false;
	}

	snprintf(szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
			"SELECT a.sp_user_seq, b.sp_svc_tran_id, a.sp_user_password, b.initial_vector, b.private_key, "
			"a.sp_user_id, b.validate_sp_session_id_yn, b.bandwidth_ctrl_yn, b.referer_chk_yn, b.sp_svc_id, b.uri_ignore_case, " 
			"c.auth_server, c.auth_path, c.auth_port, c.bill_server, c.bill_path, c.bill_port, c.auth_timeout, "
			"c.bill_timeout, c.mode, c.referer_chk_url, c.referer_chk_filetype "
			"FROM t_sms_sp_user AS a "
			"INNER JOIN t_sms_sp_svc_product AS b "
			"ON a.sp_user_seq = b.sp_user_seq "
			"LEFT JOIN t_sms_sp_svc_product_val c "
			"ON  b.sp_svc_tran_id = c.sp_svc_tran_id "
			);

	// Query 실행 
	if( m_pPgSQL->PgDoExec(szQuery) < 0)
	{
		// 재연결 되도록 조치 해야한다.
		LOG( LERR, "RCDB error.[%s][%s]", m_pPgSQL->GetErrorMessage().c_str(), szQuery );
		return false;
	}

	// RCDB로부터 얻은 정보들을 Map에 등록한다.
	for( int i = 0; i < m_pPgSQL->GetNoTuples(); i++ )
	{
		struct account ac;
		memset(&ac, 0x00, sizeof(ac));
		
		strncpy(ac.seq, m_pPgSQL->GetValue(i, 0), MaxSizeOfUserSeq - 1);
		strncpy(ac.tran_id, m_pPgSQL->GetValue(i, 1), MaxSizeOfUserTranID - 1);
		strncpy(ac.pass, m_pPgSQL->GetValue(i, 2), MaxSizeOfUserPassword - 1);
		string_lower2(ac.pass);
		strncpy(ac.iv, m_pPgSQL->GetValue(i, 3), MaxSizeOfUserIV - 1);
		strncpy(ac.pk, m_pPgSQL->GetValue(i, 4), MaxSizeOfUserPK - 1);
		strncpy(ac.id, m_pPgSQL->GetValue(i, 5), MaxSizeOfUserID - 1);
		is_user_auth = (*((char *)m_pPgSQL->GetValue(i, 6)) == 'Y');
        //is_band_ctrl = (*((char *)m_pPgSQL->GetValue(i, 7)) == 'Y');
		is_referer_chk = (*((char *)m_pPgSQL->GetValue(i, 8)) == 'Y');
		strncpy(ac.svc_id, (char *)m_pPgSQL->GetValue(i, 9), MaxSizeOfSvcID - 1);
		ac.is_ignorecase = (*((char *)m_pPgSQL->GetValue(i, 10)) == 'Y');

		if (is_user_auth)
		{
			strncpy(ac.auth_addr, m_pPgSQL->GetValue(i, 11),
				MaxSizeOfUserAuthAddr - 1);
			ac.auth_addr[MaxSizeOfUserAuthAddr - 1] = '\0';

			strncpy(ac.auth_uri, m_pPgSQL->GetValue(i, 12),
				MaxSizeOfUserAuthUri - 1);
			ac.auth_uri[MaxSizeOfUserAuthUri - 1] = '\0';

			ac.auth_port = atoi(m_pPgSQL->GetValue(i, 13));

			strncpy(ac.bill_addr, m_pPgSQL->GetValue(i, 14),
				MaxSizeOfUserAuthAddr - 1);
			ac.bill_addr[MaxSizeOfUserAuthAddr - 1] = '\0';
			strncpy(ac.bill_uri, m_pPgSQL->GetValue(i, 15),
				MaxSizeOfUserAuthUri - 1);

			ac.bill_uri[MaxSizeOfUserAuthUri - 1] = '\0';
			ac.bill_port = atoi(m_pPgSQL->GetValue(i, 16));

			ac.auth_timeout = atoi(m_pPgSQL->GetValue(i, 17));
			ac.bill_timeout = atoi(m_pPgSQL->GetValue(i, 18));

			strncpy(ac.mode, m_pPgSQL->GetValue(i, 19),
				MaxSizeOfUserAuthMode - 1);
			ac.mode[MaxSizeOfUserAuthMode - 1] = '\0';

			if (is_referer_chk) {
				strncpy(ac.permitted_referer,
					m_pPgSQL->GetValue(i, 20),
					MaxSizeOfPermittedReferer - 1);
				ac.permitted_referer[MaxSizeOfPermittedReferer - 1] = '\0';

				strncpy(ac.permitted_extend,
					m_pPgSQL->GetValue(i, 21),
					MaxSizeOfPermittedReferer - 1);
				ac.permitted_extend[MaxSizeOfPermittedReferer - 1] = '\0';
			}
		}
		// 모든 데이타를 수집했으므로... 
		// Map에 등록 처리 한다.

		ac.nFlag = 0;
		string szSeq = ac.seq;
		string szTranId = ac.tran_id;
		string szMapKey = szSeq + szTranId;
		m_mapAccount.insert( std::pair<string, struct account>( szMapKey, ac) );
	}

	m_pPgSQL->PgClear();

	std::map<string, struct account>::iterator it;
	for (it=m_mapAccount.begin(); it!=m_mapAccount.end(); ++it)
	{
		struct account *pAc = &(it->second);

		m_pPgSQL->PgClear();
		snprintf(szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
				"SELECT sb.volume_bandwidth_control_current, "
				"sb.session_bandwidth_limit, s.bandwidth_ctrl_yn "
				"FROM t_sms_sp_svc_product_band AS sb "
				" LEFT JOIN t_sms_sp_svc_product AS s ON s.sp_svc_tran_id = sb.sp_svc_tran_id "
				"WHERE sb.sp_svc_tran_id = %s ", pAc->tran_id );

		// Query 실행 
		if( m_pPgSQL->PgDoExec(szQuery) < 0)
		{
			// 재연결 되도록 조치 해야한다.
			LOG( LERR, "RCDB error.[%s][%s]", m_pPgSQL->GetErrorMessage().c_str(), szQuery );
			return false;
		}

		if( m_pPgSQL->GetNoTuples() > 1)
		{
			// 두 개이상의 tuple이 있어서는 안된다.
			LOG( LWAR, "Check your t_sms_sp_svc_product_band table.[TranID: %s]", pAc->tran_id);
		}
		else if( m_pPgSQL->GetNoTuples() == 1 )
		{
			if( *((char *)m_pPgSQL->GetValue(0, 2)) == 'N')
			{
				pAc->tc_percent = pAc->tc_session = 0;
			}
			else
			{
				pAc->tc_percent = atoi( m_pPgSQL->GetValue(0, 0) );
				pAc->tc_session = atoi( m_pPgSQL->GetValue(0, 1) ) / 8;
			}
		}
		else
		{
			pAc->tc_percent = pAc->tc_session = 0;
		}
	}
	
	m_pPgSQL->PgClear();
	
	return true;
}


bool CAccountUpdateThread::GetSystmeMountInfo()
{
	// 추출된 Mount 정보 저장 객체 초기화.
	std::string szMountName;
	m_vecMount.clear();
	
	// statfs 구조체 정보.
	//  struct statfs {
	//  char     f_mntonname[88];       /* directory on which mounted */
	//  int64_t  f_bavail;              /* free blocks avail to non-superuser */
	//  uint64_t f_bsize;               /* filesystem fragment size */
	//  ..
	//  }
	//  => uint64_t = unsigned long long => 0 ~ 18,446,744,073,709,551,615 범위 가짐.
	
// FreeBSD 에서만.. getmntinfo 함수를 지원..
// 향후 Linux Migration 을 대비하기 위해 OS 별 코드 추가한다.
#ifdef __FreeBSD__

	// Mount 정보를 추출하기 위한 변수 초기화.
	int nMountCount = 0;
	struct statfs * pStatFsBuf = NULL;

	// Mount 정보 추출...
	// getmntinfo 특성상.. 할당된 메모리를 application 에서 해제시키면 안되므로 주의할 것.
	nMountCount = getmntinfo( &pStatFsBuf, MNT_WAIT );
	if( nMountCount == 0 )
	{
		// Mount 정보 추출 실패시.. 오류 처리한다.
		int errorNum = errno;
		LOG( LERR, "system getmntinfo function return failed. [%d][%s]", errorNum, strerror(errorNum) );

		return false;
	}

	for( int i = 0 ; i < nMountCount ; i++ )
	{
		szMountName = pStatFsBuf[i].f_mntonname;
	
		m_vecMount.push_back( szMountName );


		// 시스템에서 Mount 정보 추출시 유효 용량에 대한 임계치 검사를 수행할 경우....
		// 아래 코드에서 운영자가 설정한 Mount 정보 추출이 실패.. 잘못된 경로상에 Content 저장이 발생할 수 있으므로.. 임계치 검사를 수해하지 않는다.
	}

#else      // Linux 인 경우...
	
	FILE * fp = NULL;
	
	struct mntent   stMnt;
	struct mntent * pMnt;
	char tempBuf[512];

	fp = setmntent( "/etc/mtab", "r");
	if( fp != NULL )
	{
		while( (pMnt = getmntent_r( fp, &stMnt, tempBuf, sizeof(tempBuf))) != NULL )
		{
			struct statfs statFsBuf;

			if( (stMnt.mnt_dir != NULL) && (statfs( stMnt.mnt_dir, &statFsBuf ) == 0 ) )
			{
				szMountName = stMnt.mnt_dir;

				m_vecMount.push_back( szMountName );
				
			}
		}

		// open 된 fp close 처리.
		endmntent( fp );
	}

#endif
	
	// 추출된 결과 확인...
	if( m_vecMount.empty() == true )
	{
		// 추출된 system Mount 정보가 없는 경우..
		LOG( LERR, "system mount info empty. check mount info." );
		return false;
	}

	return true;
}

std::vector<std::string> CAccountUpdateThread::GetMountNodeInfo()
{
	std::vector<std::string> vecTempMount;
	std::vector<std::string>::iterator it;
	for( it = m_vecMount.begin(); it < m_vecMount.end(); it++ )
	{
		std::string szMountName = *it;
		if( strcmp( m_szStorageRoot.c_str(), szMountName.c_str() ) == 0 )
		{
			LOG(LDBG, "Mount Name : %s, %s", szMountName.c_str(), m_szStorageRoot.c_str() );
			vecTempMount.push_back( szMountName );
		}
	}

	if( vecTempMount.empty() == true )
	{
		// 최초 비교시에는 원래 정보를 가지고 부분 비교처리.. (/stg 등으로 설정된 경우를 위해)
		std::string strPreviousPath = m_szStorageRoot;
		std::string strCurrentPath  = m_szStorageRoot;

		do
		{
			// 수정된 경로 정보만을 가지고 부분 비교를 수행....
			for( it = m_vecMount.begin(); it < m_vecMount.end(); it++ )
			{
				std::string szMountName = *it;
				// 부분 비교 처리
				if( strncmp( strCurrentPath.c_str(), szMountName.c_str(), strlen(strCurrentPath.c_str()) ) == 0 )
				{
					LOG(LDEV, "Mount Name : %s", szMountName.c_str() );
					vecTempMount.push_back( szMountName );
				}
			}

			// 위에서 정보 추출이 실패한 경우..상위 Path 정보를 추출한다. ( /user2/dav_storage, /stg/node0/aa/bb 등으로 설정된 경우를 위해 )
			if( vecTempMount.empty() == true )
			{
				GetUpperPath( strPreviousPath, strCurrentPath );
				if( strCurrentPath.empty() == true )
				{
					break;
				}

				strPreviousPath = strCurrentPath;
			}	

		} while( vecTempMount.empty() == true );
	}

	return vecTempMount;
}

// 전달받은 경로로부터.. 상위 Path 정보를 추출하여 전달한다.
// 오류 발생시 [out] 값인 strUpperPath 가 empty 된다.
void CAccountUpdateThread::GetUpperPath( const std::string & strSourcePath, std::string & strUpperPath )
{
	// 인자로 전달되는 strSourcePath 끝에.. '/' 가 이미 제거되었다고 판단하고 작업을 수행한다.
	std::string::size_type pos = strSourcePath.rfind( '/' );
	if( pos == std::string::npos )
	{
		// 찾지 못한 경우...
		// out 값을 empty 처리시킨다.
		LOG( LNOT, "upper path not found. source[%s]", strSourcePath.c_str());
		strUpperPath.clear();
		
		return;
	}

	// 찾은 경우..
	strUpperPath = strSourcePath.substr(0, pos);

	// 추출된 경로가... 최상위 '/' 인 경우.. 유효하지 않으므로 오류로 처리한다.
	if( strcmp( strUpperPath.c_str(), "/") == 0 )
	{
		LOG( LNOT, "upper path not vaild that root path. [%s]", strUpperPath.c_str() );
		strUpperPath.clear();
	}

	return;
}

void CAccountUpdateThread::MakeServiceFolder()
{
	std::vector<std::string>::iterator itM;
	std::map<std::string, struct account>::iterator itT;
	// 수정된 경로 정보만을 가지고 부분 비교를 수행....
	for( itM = m_vecMountNodeInfo.begin(); itM != m_vecMountNodeInfo.end(); itM++ )
	{
		std::string szMountName = *itM;
		for( itT = m_mapAccount.begin(); itT != m_mapAccount.end(); itT++ )
		{
			std::string szServiceID= itT->second.tran_id;
			LOG( LDEV, "ServiceID [%s]", szServiceID.c_str() );

			std::string szServicePath = szMountName + '/' + szServiceID;
			
			if ((mkdir(szServicePath.c_str(), 0777)) == 0 ) 
			{
				_LOG( LINF, "Created service path [%s]", szServicePath.c_str()  );
				chown(szServicePath.c_str(), 65534, 65534);
			}
			else
			{
				// 기존에 존재해서 나는 에러는 제외 하고 ERR로 처리 한다.
				if ( errno != 17 )
				{
					LOG( LERR, "[%s] is make failed. %d, %s", szServicePath.c_str(), errno, strerror(errno)  );
				}
			}
		}
	}

	_LOG( LINF, "Complete service folder creation and verification");

}

void CAccountUpdateThread::UpdateSharedMemory()
{
	struct account* pSharedHeader = (struct account*)m_pShmemAccount->GetData();

	int nIndex = 0;
	
	int nMaxAccout = CDeamonConfig::GetInstance()->GetMaxAccount();
	// 공유 메모리 기준으로 
	// Maxcount는 설정 값에서 갖고 온다.

	_LOG(LINF, "xxxxx Account Shared Memory xxxxx");
	for(int i = 0; i < nMaxAccout; i++ )
	{
		// 끝이면???
		if( (pSharedHeader + i)->id[0] == '\0' )
		{
			LOG(LDBG, "Account Shared Memory End. [index: %d, %d]", i, (int)m_mapAccount.size() );
			// 맵에 데이터가 남을 경우 해당 인덱스부터 추가해 주기 위해 저장한다.
			nIndex = i;
			break;
		}

		struct account* pCurAccount = (pSharedHeader + i);

		// 맵 키 생성
		string szSeq = pCurAccount->seq;
        string szTranId = pCurAccount->tran_id;
        string szMapKey = szSeq + szTranId;

		// 맵에서 해당 데이터를 찾는다.
		std::map<string, struct account>::iterator it;
		it = m_mapAccount.find(szMapKey);

		if( it == m_mapAccount.end() )
		{
			// 맵에서 데이터를 못찾았다면, 삭제된 서비스라고 판단한다.
			pCurAccount->nFlag = 1;
			_LOG(LNOT, "%s@%s is deleted.", pCurAccount->svc_id, pCurAccount->id);
			continue;
		}

		struct account ac = it->second;
		memcpy(pCurAccount, &ac, sizeof(struct account));

		_LOG(LINF, "id: %s, svc_id: %s, tran_id: %s "
							, (pSharedHeader + i)->id, (pSharedHeader + i)->svc_id, (pSharedHeader + i)->tran_id );
		_LOG(LINF, "	tc_percentx: %d, tc_session: %d, nFlag: %d", (pSharedHeader + i)->tc_percent, (pSharedHeader + i)->tc_session, (pSharedHeader + i)->nFlag);

		// 맵에서 삭제
		m_mapAccount.erase(it);
	}

	// 맵에 데이터가 남았는지 체크 한다.
	if( m_mapAccount.size() <= 0 )
		return;

	
	// 맵에 데이터가 남았다면... 신규로 개통된 서비스가 있다는 뜻이므로 이를 shared 메모리에 등록한다.
	// shared 메모리의 맨 마지막 인덱스(nIndex)부터 사용한다.
	std::map<string, struct account>::iterator it;
	for (it=m_mapAccount.begin(); it!=m_mapAccount.end(); ++it, ++nIndex)
	{
		if( nIndex >= nMaxAccout )
		{
			LOG(LWAR, "Account shared memory is full.[MaxCount: %d]", nMaxAccout);
			break;
		}
		struct account ac = it->second;

		_LOG(LINF, "Account Add  [Index: %d] [id: %s][svc: %s]", nIndex, ac.id, ac.svc_id);
		memcpy((pSharedHeader + nIndex), &ac, sizeof(struct account));
	}
	m_mapAccount.clear();
}

///@brief 이 함수에서 컨트롤 로직이 표현된다.
void CAccountUpdateThread::Execute()
{	
	// 최초라서 실패 하더라도 아래 로직에서 reconnect가 진행 됨.
	DbConnect();
	// 최초 구동시 한번은 갱신 되도록 하기위한 변수
	bool bFirst = true;

	time_t currentTime;        // 현재 시간 정보를 저장하기 위한 변수..

	unsigned long nowTimestamp;
	unsigned long lastUpdateTimestamp = 0;  // shard memory 마지막 갱신 timestamp 값 (5분단위)

	int nRetryCount = 0;
	while (*m_sighandle == 0)
	{

		// 현재 시간 정보 추출..
		currentTime = time(0);

		// 현재 timestamp 값을 계산한다...
		//    (20초마다 갱신위해)19 sec 보정 처리하여 5분 단위 timestamp 값을 추출한다.
		nowTimestamp = (currentTime - 19 + 299) / 300;
				
		// 시간 동기화 이상으로 reset 처리된 경우... lastSendTimestamp = 0 
		if (!bFirst && lastUpdateTimestamp == 0)
		{
			lastUpdateTimestamp = nowTimestamp;
			sleep(1);
			continue;
		}
		
		// 마지막 timestamp 값과 현재 timestamp 계산 값이 같은 경우...
		//    이미 갱신 것으로 판단하고.. 쉰다.
		if (nowTimestamp == lastUpdateTimestamp)
		{
			sleep(1);
			continue;
		}

		// 만약 lastSendTimestamp > nowTimestamp 인 경우...
		//  - 시간 동기화 이상으로 이전에 계산된 timestamp 값이 잘못된 경우....  보정처리..
		if (nowTimestamp < lastUpdateTimestamp)
		{
			_LOG(LWAR, "Account Update Thread : Last timestamp not valid. skip and reset. now[%lu] last[%lu]", nowTimestamp, lastUpdateTimestamp);
			lastUpdateTimestamp = 0;

			sleep(10);
			continue;
		}

		bFirst = false;

		if ( GetData() )
		{
			do
			{
				// 마운트 정보를 정상적으로 가져온 경우만 서비스 폴더 생성
				if (GetSystmeMountInfo() == false)
				{
					break;
				}
				m_vecMountNodeInfo = GetMountNodeInfo();
				if (m_vecMountNodeInfo.empty() == true)
				{
					LOG(LERR, "Not found file storage root. conf path[%s]", m_szStorageRoot.c_str());
					break;
				}
				MakeServiceFolder();
			} while (0);
			UpdateSharedMemory();
			lastUpdateTimestamp = nowTimestamp;

			printShredMemory();
		}
		else
		{
			DbConnect();

			// 3회 재시도...
			nRetryCount++;
			if (nRetryCount >= 3)
			{
				lastUpdateTimestamp = nowTimestamp;
				nRetryCount = 0;
			}
			// 5초 후 재시도
			if (*m_sighandle == 0)
				sleep(5);
		}

		nRetryCount = 0;
	}

	// 쓰레드 종료 될 때 DB 세션 정리 한다.
	DbClose();
}

void* CAccountUpdateThread::EntryPoint(void* arg)
{
	CAccountUpdateThread* pObject = reinterpret_cast<CAccountUpdateThread *>(arg);
	pthread_detach( pthread_self() );

	pObject->Execute();
	
	// Thread 종료시 m_threadHandle 값을 초기화 처리.
	// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
	pObject->m_threadHandle = 0;

	return 0;
}

bool CAccountUpdateThread::Start()
{
	int nRet = ::pthread_create(&m_threadHandle, 0, CAccountUpdateThread::EntryPoint, this);

	if( nRet )
	{
		LOG(LERR, "Account thread create failed.: errno:%d errmsg:%s", errno, strerror(errno));
		return false;
	}
	LOG(LDBG, "Account thread create succeed");
	sleep(0);
	return true;
}
