This commit is contained in:
biosvos
2026-08-07 17:38:18 +09:00
commit 873193a243
9613 changed files with 2755992 additions and 0 deletions
+317
View File
@@ -0,0 +1,317 @@
#include "ClientThread.h"
#include <errno.h>
#include <string.h>
#include "Logger.h"
#include "RcSyncdRequestData.h"
#include "ModeMaster.h"
#include "ModeSlave.h"
#include "ModeSync.h"
// 생성자
CClientThread::CClientThread( CProcessStatus * pStatus, int clientSocket, char * pszClientAddress )
: m_pProcessStatus( pStatus )
, m_socketClientDesc( clientSocket )
, m_client( clientSocket )
, m_strClientIp( pszClientAddress )
{
// 멤버 변수 초기화
m_threadHandle = 0;
}
// 소멸자
CClientThread:: ~CClientThread()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
// client 와 연결된 socket 종료 처리
m_client.Close();
}
// thread 를 생성하여 처리 업무 flow start
bool CClientThread::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CClientThread::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// 스레드 함수
void* CClientThread::threadFunc( void* arg )
{
CClientThread* pObject = reinterpret_cast<CClientThread *>( arg );
pthread_detach( pthread_self() );
// Client Thread Count 1 증가 처리
pObject->m_pProcessStatus->PlusClientCount();
// 실제 작업 수행.
pObject->Execute();
// Client Thread Count 1 감소 처리
pObject->m_pProcessStatus->MinusClientCount();
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
// 객체 자동 delete 처리.
delete pObject;
return NULL;
}
// client request 요청을 수신하여 이에 따른 job 을 수행
void CClientThread::Execute()
{
_LOG( LINF, "client thread start [%s]", m_strClientIp.c_str() );
// Packet Header 정보를 이용한 client 요청 분석
int nRead;
bool bRequest;
bool bFromClient;
char command;
while( 1 )
{
// socket 연결을 아래 job 등에서 일부로 끊은 경우를 처리하기 위한 코드.
// - Server socket 이지만.. 통신 관련 오류시.. server 가 socket 을 끊을 수 있다.
// - 이는 file 전송 등으로 인해 불필요한 data 가 통신상에 남아 있을 수 있기 때문임.
if( m_client.IsValidSocket() == false )
break;
// Header 수신
nRead = m_client.GetHeaderInfo( bRequest, bFromClient, command );
if( nRead == 0 )
{
// timeout 발생시 - 잠시 대기 후 계속 수신 대기
sleep( 2 );
continue;
}
else if( nRead < 0 )
{
// Socket 접속 종료 또는 오류 발생시
// - 관련 로그는 해당 모듈에서 다 찍었으므로.. 여기서는 루프 종료 처리.
break;
}
else
{
// data 수신시.
// 정의된 packet 에만 동작.. 그외에는 무시.
if( bRequest == true && bFromClient == true )
{
// 외부 tool (cmove) 또는 scheduler 에서 보낸 요청인 경우.
switch( command )
{
case CLIENT_COMMAND_SYNC: // Client 에서 동기화 수행 요청 수신시
_LOG( LINF, "client[%s] request sync.", m_strClientIp.c_str() );
ClientRequestSync();
break;
default: // 미정의 Type 수신시.. client 와 연결 해제 처리
LOG( LERR, "client[%s] request unknown. check request[%02x]", m_strClientIp.c_str(), command );
m_client.PrintHeaderToLog();
m_client.Close();
break;
}
}
else if( bRequest == true && bFromClient == false )
{
// Master rc_syncd 에서 보낸 요청인 경우.
switch( command )
{
case PROCESS_CONTENT_LIST: // Master 로 부터 Content 정보 목록 요청 수신시
_LOG( LINF, "master[%s] request content list.", m_strClientIp.c_str() );
ProcessContentList();
break;
case PROCESS_CONTENT_SYNC: // Master 로 부터 동기화 수행 작업 요청 수신시.
_LOG( LINF, "master[%s] request sync job.", m_strClientIp.c_str() );
ProcessContentSync();
break;
default: // 미정의 Type 수신시.. 연결 해제 처리
LOG( LERR, "master[%s] request unknown. check request[%02x]", m_strClientIp.c_str(), command );
m_client.PrintHeaderToLog();
m_client.Close();
break;
}
}
else
{
// 그 외.. 정의 되지 않은 요청인 경우. => 연결 해제 처리.
LOG( LERR, "Not defined packet header. client[%s]", m_strClientIp.c_str() );
m_client.PrintHeaderToLog();
m_client.Close();
}
}
} // while (1)
// client 와 연결 종료 처리.
_LOG( LINF, "client connection closed.[%s]", m_strClientIp.c_str() );
m_client.Close();
}
// Client로 부터 Sync 요청 수신시 이를 처리하기 위한 함수.
void CClientThread::ClientRequestSync()
{
m_pProcessStatus->PlusMasterModeCount();
// 수신된 Data 를 저장할 객체 생성
CReqMasterData requestData;
if( m_client.RecvBodySyncFromClient( requestData ) == false )
{
// Data 수신 실패시
LOG( LERR, "client[%s] request sync. data receive fail.", m_strClientIp.c_str() );
m_client.Close();
}
else
{
// Request Data 수신 성공시.
LOG( LDBG, "client[%s] request sync. type[%d] time[%s]~[%s] svc[%s]"
, m_strClientIp.c_str()
, requestData.sync_type, requestData.start_time.c_str(), requestData.end_time.c_str()
, ( requestData.one_service.empty() == true ? "ALL" : requestData.one_service.c_str())
);
// Master Mode 처리를 위한 객체 생성
CMasterMode modeMaster( &m_client );
if( modeMaster.CreateSyncList( requestData ) == true )
{
// 초기화 작업이 정상적으로 완료된 경우...
// 업무 Flow 수행함수 실행.
modeMaster.Execute();
}
else
{
// 초기화 작업 실패시
std::string strErrorMessage = "sync list initialize fail.";
if( m_client.SendResultSyncToClient( false, strErrorMessage ) == false )
{
LOG( LERR, "client[%s] sync result[error] send fail.", m_strClientIp.c_str() );
}
}
}
m_pProcessStatus->MinusMasterModeCount();
return;
}
// Content 목록 정보 요청 ( Slave Mode ) 인 경우 이를 처리하기 위한 함수.
void CClientThread::ProcessContentList()
{
m_pProcessStatus->PlusSlaveModeCount();
// 수신된 Data 를 저장할 객체 생성
CReqSlaveData requestData;
if( m_client.RecvBodyContentListFromMaster( requestData ) == false )
{
// Data 수신 실패시
LOG( LERR, "master[%s] request content_list. but data receive fail.", m_strClientIp.c_str() );
m_client.Close();
}
else
{
// Request Data 수신 성공시.
LOG( LDBG, "master[%s] request content list. type[%d] time[%s]~[%s] master[%s] slave[%s]"
, m_strClientIp.c_str()
, requestData.sync_type, requestData.start_time.c_str(), requestData.end_time.c_str()
, requestData.master.c_str(), requestData.slave.c_str()
);
// Slave Mode 처리를 위한 객체 생성
CSlaveMode modeSlave( &m_client );
if( modeSlave.CreateSyncList( requestData ) == true )
{
// 초기화 작업이 정상적으로 완료된 경우...
// 업무 Flow 수행함수 실행.
modeSlave.Execute();
}
else
{
// 초기화 작업 실패시
std::string strErrorMessage = "sync list initialize fail.";
if( m_client.SendErrorContentListToMaster( strErrorMessage ) == false )
{
LOG( LERR, "master[%s] content_list result[error] send fail.", m_strClientIp.c_str() );
}
}
}
m_pProcessStatus->MinusSlaveModeCount();
return;
}
// Content 동기화 요청 ( Sync Mode ) 인 경우 이를 처리하기 위한 함수.
void CClientThread::ProcessContentSync()
{
m_pProcessStatus->PlusSyncModeCount();
// 수신된 Data 를 저장할 객체 생성
CReqSyncData requestData;
if( m_client.RecvBodyExecuteSyncFromMaster( requestData ) == false )
{
// Data 수신 실패시
LOG( LERR, "master[%s] request execute sync. but data receive fail.", m_strClientIp.c_str() );
m_client.Close();
}
else
{
// Request Data 수신 성공시.
LOG( LDBG, "master[%s] request execute sync. type[%d] time[%s]~[%s] master[%s] slave[%s]"
, m_strClientIp.c_str()
, requestData.sync_type, requestData.start_time.c_str(), requestData.end_time.c_str()
, requestData.master.c_str(), requestData.slave.c_str()
);
// Sync Mode 처리를 위한 객체 생성
CSyncMode modeSync( &m_client );
if( modeSync.CreateSyncList( requestData ) == true )
{
// modeSync 객체 초기화 성공시...
// socket 에서 sync 파일 정보를 수신받아 저장처리한다.
// 하지만.. 파일명 정보가 CSyncMode 내부에 저장되어 있으므로...
// 이에 대한 수신 처리 및 응답 처리는 CSyncMode 내의 Execute 함수 내에서 처리한다.
// 초기화 작업이 정상적으로 완료된 경우...
// 업무 Flow 수행함수 실행.
modeSync.Execute();
}
else
{
// 초기화 작업 실패시
std::string strErrorMessage = "sync list initialize fail.";
if( m_client.SendResultExecuteSyncToMaster( false, strErrorMessage ) == false )
{
LOG( LERR, "master[%s] execute sync result[error] send fail.", m_strClientIp.c_str() );
}
}
}
m_pProcessStatus->MinusSyncModeCount();
return;
}
+85
View File
@@ -0,0 +1,85 @@
/***************************************************************************
접속 요청 Client 에 대한 처리를 담당하는 Thread class
-----------------------------------------
begin : 2014/09/12
copyright : (C) 2005 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.4
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.
***************************************************************************/
#ifndef __CLIENT_THREAD_H__
#define __CLIENT_THREAD_H__
#include <pthread.h>
#include <string>
#include "ProcessStatus.h"
#include "ProcessSocketControl.h"
// CClientThread
// rc_syncd 에서 accept 처리한 각 client 세션을 처리하기 위한 Thread class
class CClientThread
{
public:
// 생성자
CClientThread( CProcessStatus * pStatus, int clientSocket, char * pszClientAddress );
// 소멸자
~CClientThread();
// thread 를 생성하여 처리 업무 flow start
bool Start();
private:
// 스레드 함수
static void* threadFunc( void* arg );
// client request 요청을 수신하여 이에 따른 job 을 수행
void Execute();
// Client로 부터 Sync 요청 수신시 이를 처리하기 위한 함수.
void ClientRequestSync();
// Content 목록 정보 요청 ( Slave Mode ) 인 경우 이를 처리하기 위한 함수.
void ProcessContentList();
// Content 동기화 요청 ( Sync Mode ) 인 경우 이를 처리하기 위한 함수.
void ProcessContentSync();
private:
// 쓰레드 핸들
pthread_t m_threadHandle;
// 프로세스 상태를 관리하는 CProcessStatus 객체에 대한 포인터
// - 생성자를 통해 전달받음
CProcessStatus * m_pProcessStatus;
// Client Socket discriptor
// - 생성자를 통해 전달받음.
int m_socketClientDesc;
// client 와 통신 관련 처리를 수행하기 위한 control 객체
CProcessSocketControl m_client;
// Client IP Address
// - 생성자를 통해 전달받음
std::string m_strClientIp;
};
#endif /* __CLIENT_THREAD_H__ */
+545
View File
@@ -0,0 +1,545 @@
/***************************************************************************
Database Connection Pool
-----------------------------------------
begin : 2012/05/13
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.1.0
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include "DBConnPool.h"
#include "Database.h"
#include "Logger.h"
#include "Util.h"
#include <unistd.h>
#define GET_SLEEP 1000 // microsecond
CMasterDBPool *CMasterDBPool::m_inst = NULL;
CSlaveDBPool *CSlaveDBPool::m_inst = NULL;
CDBConnPool::CDBConnPool(CDataBaseInfo info)
: m_dbinfo(info), m_exitpool(false), m_lastset(NULL), m_alivetime(-1), m_tabletype(-1)
{
pthread_mutex_init(&m_mutex, NULL);
pthread_mutex_init(&m_alivemutex, NULL);
pthread_cond_init(&m_alivecond, NULL);
}
CDBConnPool::~CDBConnPool()
{
pthread_mutex_destroy(&m_mutex);
pthread_mutex_destroy(&m_alivemutex);
pthread_cond_destroy(&m_alivecond);
}
int CDBConnPool::ReCreatePool(int poolcnt /* = 4 */)
{
// pool
for (int i = 0; i < poolcnt; i++)
{
DataBase * tmp = new DataBase();
if (tmp->PgOpenDB(m_dbinfo.m_hostaddr, m_dbinfo.m_port, m_dbinfo.m_dbname, m_dbinfo.m_user, m_dbinfo.m_pw) == NULL)
{
#ifdef _DEBUG
cerr << "Database connect error : " << tmp->GetErrorMessage() << endl;
#endif // _DEBUG
LOG(LERR, "Database connect error.[%s]", tmp->GetErrorMessage().c_str());
delete tmp;
continue;
}
// 2014.06.06 dadamin
// Pool 생성 시 현재 생성된 DB의 형상을 체크한다.
if (m_tabletype < 0)
{
if (SetTargetTableType(tmp) == false)
{
delete tmp;
break;
}
}
m_poolmap.insert(make_pair(tmp, CDBConnPool::POOL_FREE));
}
LOG(LDBG, "DB Pool Recreated.[CNT=%zu]", m_poolmap.size());
return m_poolmap.size();
}
int CDBConnPool::CreatePool( int poolcnt /* = 4 */ )
{
// keep alive thread
int nRet = pthread_create(&m_keepalive, 0, CDBConnPool::KeepPoolAlive, this);
if( nRet )
{
cerr << "Thread create failed.: errno: " << errno << endl;
return -1;
}
// pool
m_poolsize = ReCreatePool(poolcnt);
if( m_poolmap.size() == 0 )
{
// alive thread end
m_exitpool = true;
pthread_cond_signal(&m_alivecond);
pthread_join(m_keepalive, NULL);
m_exitpool = false;
return -1;
}
return m_poolsize;
}
int CDBConnPool::DestroyPool()
{
m_exitpool = true;
map<DataBase*, short>::iterator iter;
pthread_mutex_lock(&m_mutex);
while(m_poolmap.size() > 0 )
{
iter = m_poolmap.begin();
if(iter->second == CDBConnPool::POOL_FREE || iter->second == CDBConnPool::POOL_ERR)
{
DataBase *data = static_cast<DataBase *>(iter->first);
delete (DataBase *) data;
m_poolmap.erase(iter);
}
else
sleep(1);
}
pthread_mutex_unlock(&m_mutex);
//
pthread_cond_signal(&m_alivecond);
pthread_join(m_keepalive, NULL);
/*
for( iter = m_poolmap.begin(); !m_poolmap.empty()&& iter != m_poolmap.end(); iter++ )
{
if(iter->second == CDBConnPool::POOL_FREE)
{
DataBase *data = static_cast<DataBase *>(iter->first);
delete (DataBase *) data;
m_poolmap.f
m_poolmap.erase(iter);
}
}
*/
m_poolsize = m_poolmap.size();
return m_poolsize;
}
DataBase * CDBConnPool::GetConnFromPool(int timeout)
{
ostringstream msg;
DataBase * r = NULL;
int64_t usetime = 0;
int64_t out = timeout*1000*1000;
time_t t = time(NULL);
pthread_mutex_lock(&m_mutex);
map<DataBase*, short>::iterator iter = m_poolmap.begin();
if(m_lastset)
{
iter = m_poolmap.find(m_lastset);
if( iter != m_poolmap.end() )
{
++iter;
}
}
do
{
if( m_poolmap.empty() )
{
if (ReCreatePool(m_poolsize) == 0)
{
msg << "DB Pool empty.";
LOG(LERR, msg.str().c_str());
break;
}
}
if( iter == m_poolmap.end() )
{
iter = m_poolmap.begin();
}
for( ; !m_poolmap.empty()&& iter != m_poolmap.end(); )
{
if(iter->second == CDBConnPool::POOL_FREE)
{
iter->second = CDBConnPool::POOL_USE;
r = iter->first;
m_lastset = r;
break;
}
else if (iter->second == CDBConnPool::POOL_ERR)
{
DataBase * d = iter->first;
m_poolmap.erase(iter++);
if(d == m_lastset)
m_lastset = NULL;
delete d;
}
else
iter++;
}
if( r == NULL)
{
solusleep(GET_SLEEP);
if( usetime > 10*1000*1000)
{
msg << "GetConnFromPool have waited longer than 10 seconds.";
LOG(LDEV1, msg.str().c_str());
}
if( out > 0 )
{
usetime += GET_SLEEP;
if(usetime > out)
{
msg << "GetConnFromPool Timeout." << usetime << "," << out <<
"," << time(NULL) -t;
LOG(LERR, msg.str().c_str());
break;
}
}
}
} while (r == NULL);
pthread_mutex_unlock(&m_mutex);
return r;
}
void CDBConnPool::ReleaseConnToPool( DataBase * t, bool success )
{
//pthread_mutex_lock(&m_mutex);
map<DataBase*, short>::iterator iter = m_poolmap.find(t);
if( iter != m_poolmap.end() )
{
if(iter->second==CDBConnPool::POOL_USE)
{
if (success)
{
iter->second=CDBConnPool::POOL_FREE;
}
else
{
ostringstream msg;
iter->second=CDBConnPool::POOL_ERR;
msg << "DB Pool used failed.";
LOG(LERR, msg.str().c_str());
}
}
}
//pthread_mutex_unlock(&m_mutex);
}
bool CDBConnPool::IsTimeoutAlive()
{
bool r = false;
pthread_mutex_lock(&m_alivemutex);
struct timespec to;
if(m_alivetime > 0 )
to.tv_sec = time(NULL) + m_alivetime;
else
to.tv_sec = time(NULL) + 5;
to.tv_nsec = 0;
int err = pthread_cond_timedwait(&m_alivecond, &m_alivemutex, &to);
if (err == ETIMEDOUT)
{
if( m_alivetime > 0 )
r = true;
}
else if ( err == 0 )
{
#ifdef _DEBUG
cout << "Set Keep alive Timeout : " << m_alivetime << endl;
#endif //_DEBUG
r = false;
}
else
{
/* nothing */
}
pthread_mutex_unlock(&m_alivemutex);
return r;
}
bool CDBConnPool::SendAliveMsg()
{
ostringstream msg;
ostringstream sql;
pthread_mutex_lock(&m_mutex);
map<DataBase*, short>::iterator iter = m_poolmap.begin();
map<DataBase*, short>::iterator enditer = m_poolmap.end();
sql << "SELECT sp_user_seq, sp_svc_tran_id FROM t_sms_sp_svc_product LIMIT 1";
while(iter != enditer)
{
DataBase * d = iter->first;
if(iter->second == CDBConnPool::POOL_FREE)
{
iter->second = CDBConnPool::POOL_USE;
d->PgDoExec( const_cast<char*> (sql.str().c_str()) );
if( d->PgResult(DataBase::CLEAR) < 0 )
{
msg << "Send alive : message failed : " << iter->first->GetErrorMessage();
LOG(LERR, msg.str().c_str());
iter->second=CDBConnPool::POOL_ERR;
iter++;
//m_poolmap.erase(iter++);
//delete d;
}
else
{
iter->second = CDBConnPool::POOL_FREE;
msg << "Send alive : message success.";
LOG(LDEV1, msg.str().c_str());
++iter;
}
}
else if (iter->second == CDBConnPool::POOL_ERR)
{
msg << "Send alive : Error Pool remove.";
LOG(LDBG, msg.str().c_str());
m_poolmap.erase(iter++);
delete d;
}
else
++iter;
}
pthread_mutex_unlock(&m_mutex);
return true;
}
void* CDBConnPool::KeepPoolAlive( void* pdata )
{
CDBConnPool* pObject = reinterpret_cast<CDBConnPool *>(pdata);
ostringstream msg;
while( pObject->IsExit() == false )
{
bool b = pObject->IsTimeoutAlive();
msg << "KeepPoolAlive Timeout - run : " << b;
LOG(LDEV1, msg.str().c_str());
if( pObject->IsExit() == false && b )
{
// send keep alive message
pObject->SendAliveMsg();
}
}
return 0;
}
int CDBConnPool::SetKeepAliveTimeout(int sec)
{
m_alivetime = sec;
pthread_cond_signal(&m_alivecond);
return m_alivetime;
}
int CDBConnPool::GetFreePoolCnt()
{
int r = 0;
map<DataBase*, short>::iterator iter;
// pthread_mutex_lock(&m_mutex);
for( iter = m_poolmap.begin(); !m_poolmap.empty()&& iter != m_poolmap.end(); iter++ )
{
if(iter->second == CDBConnPool::POOL_FREE)
{
r++;
}
}
// pthread_mutex_unlock(&m_mutex);
return r;
}
// 2014.06.06 dadamin
// 현재 DB 형상 체크
bool CDBConnPool::SetTargetTableType(DataBase * d)
{
ostringstream msg;
if (d == NULL)
return false;
string sql = "SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource'";
d->PgDoExec(sql);
if (d->PgResult(DataBase::NOT_CLEAR) < 0)
{
d->PgClear();
msg << "SetTargetTableType failed : " << d->GetErrorMessage();
LOG(LERR, msg.str().c_str());
return false;
}
if (d->GetNoTuples() > 0)
{
m_tabletype = 0;
}
else
{
m_tabletype = 1;
}
d->PgClear();
msg << "Set Target Table Type : " << m_tabletype;
LOG(LDBG, msg.str().c_str());
return true;
}
// 작업할 메타 테이블명
string CDBConnPool::GetMetaTableName(const char * tranid)
{
string r;
switch (m_tabletype)
{
case 1:
r = "t_meta_";
r += tranid;
break;
case 0:
default:
r = "t_dav_resource";
break;
}
LOG(LDBG, "Working Table Name %s", r.c_str());
return r;
}
void CDBConnPool::printstatus(string prefixed)
{
int f = 0, u = 0;
map<DataBase*, short>::iterator iter;
if(m_exitpool == true ) return;
// pthread_mutex_lock(&m_mutex);
for( iter = m_poolmap.begin(); !m_poolmap.empty()&& iter != m_poolmap.end(); iter++ )
{
if(iter->second == CDBConnPool::POOL_FREE)
{
f++;
}
else
{
u++;
}
}
ostringstream msg;
if( prefixed.empty() == false )
msg << "["<< prefixed <<"]";
msg <<"DB connection Pool - " <<"total : " << m_poolmap.size() << "(" << u <<
"/" << f << ")";
LOG(LINF, msg.str().c_str());
}
// master db pool
CMasterDBPool::CMasterDBPool(CDataBaseInfo info)
: CDBConnPool(info)
{
}
CMasterDBPool::~CMasterDBPool()
{
release();
}
void CMasterDBPool::init(CDataBaseInfo info)
{
if( CMasterDBPool::m_inst == NULL )
{
CMasterDBPool::m_inst = new CMasterDBPool(info);
}
}
CMasterDBPool* CMasterDBPool::getInstance()
{
return CMasterDBPool::m_inst;
}
int CMasterDBPool::release()
{
int r = -1;
if( CMasterDBPool::m_inst != NULL )
{
r = CMasterDBPool::m_inst->DestroyPool();
delete CMasterDBPool::m_inst;
CMasterDBPool::m_inst = NULL;
}
return r;
}
// slave db pool
CSlaveDBPool::CSlaveDBPool(CDataBaseInfo info)
: CDBConnPool(info)
{
}
CSlaveDBPool::~CSlaveDBPool()
{
release();
}
void CSlaveDBPool::init(CDataBaseInfo info)
{
if( CSlaveDBPool::m_inst == NULL )
{
CSlaveDBPool::m_inst = new CSlaveDBPool(info);
}
}
CSlaveDBPool* CSlaveDBPool::getInstance()
{
return CSlaveDBPool::m_inst;
}
int CSlaveDBPool::release()
{
int r = -1;
if(CSlaveDBPool::m_inst != NULL )
{
r = CSlaveDBPool::m_inst->DestroyPool();
delete CSlaveDBPool::m_inst;
CSlaveDBPool::m_inst = NULL;
}
return r;
}
+134
View File
@@ -0,0 +1,134 @@
/***************************************************************************
Database Connection Pool
-----------------------------------------
begin : 2012/05/13
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.1.0
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __DATABASE_CONNECTION_POOL__
#define __DATABASE_CONNECTION_POOL__
#include <errno.h>
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <list>
#include <algorithm>
using namespace std;
class DataBase;
class CDataBaseInfo
{
public:
CDataBaseInfo() {}
~CDataBaseInfo() {}
public:
string m_hostaddr;
int m_port;
string m_dbname;
string m_user;
string m_pw;
};
class CDBConnPool
{
public:
enum POOL_STATUSE
{
POOL_ERR = -1,
POOL_FREE = 0,
POOL_USE = 1
};
public:
CDBConnPool(CDataBaseInfo info);
virtual ~CDBConnPool();
int CreatePool( int poolcnt = 4 );
int ReCreatePool( int poolcnt = 4 );
int DestroyPool();
DataBase* GetConnFromPool(int timeout = 10);
void ReleaseConnToPool(DataBase * t, bool success = true);
int SetKeepAliveTimeout(int sec);
void printstatus(string prefixed = "");
int GetFreePoolCnt();
bool IsExit() { return m_exitpool; }
bool IsTimeoutAlive();
bool SendAliveMsg();
void SetCDataBaseInfo(CDataBaseInfo info) { m_dbinfo = info; }
CDataBaseInfo GetCDataBaseInfo() { return m_dbinfo; }
size_t GetPoolSize() { return m_poolmap.size(); }
// 2014.06.06 dadamin
// 현재 DB 형상 체크
bool SetTargetTableType(DataBase * d);
// 작업할 메타 테이블명
string GetMetaTableName(const char * tranid);
// display_name 컬럼 사용 유무
bool UseDisplayname() {return (m_tabletype == 1); }
private:
static void* KeepPoolAlive(void*);
private:
CDataBaseInfo m_dbinfo;
map<DataBase*, short> m_poolmap;
pthread_mutex_t m_mutex;
bool m_exitpool;
DataBase* m_lastset;
int m_alivetime;
pthread_t m_keepalive;
pthread_cond_t m_alivecond;
pthread_mutex_t m_alivemutex;
// 2014.06.04 dadamin
// -1: unset, 0: t_dav_resource, 1: t_meat_[sp_svc_tran_id]
int m_tabletype;
int m_poolsize;
};
class CMasterDBPool : public CDBConnPool
{
public:
static void init(CDataBaseInfo info);
static CMasterDBPool* getInstance();
static int release();
private:
CMasterDBPool(CDataBaseInfo info);
~CMasterDBPool();
private:
static CMasterDBPool* m_inst;
};
class CSlaveDBPool : public CDBConnPool
{
public:
static void init(CDataBaseInfo info);
static CSlaveDBPool* getInstance();
static int release();
private:
CSlaveDBPool(CDataBaseInfo info);
~CSlaveDBPool();
private:
static CSlaveDBPool* m_inst;
};
#endif // __DATABASE_CONNECTION_POOL__
+183
View File
@@ -0,0 +1,183 @@
/***************************************************************************
CDataFile
-----------------------------------------
begin : 2011/10/28
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include "DataFile.h"
#include "Logger.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
// 임시 버퍼 최대 크기
#define MAX_BUFFER_SIZE 2048
CDataFile::CDataFile()
{
}
CDataFile::~CDataFile()
{
}
bool CDataFile::Init(const char* path, bool chkdir /*= true*/)
{
ostringstream msg;
// 변수 유효성 검사.
if( path == NULL || strlen( path ) == 0 )
{
msg << "Wrong path info(" << path << ")";
LOG(LERR, msg.str().c_str());
return false;
}
// 변수 정보 Setup
if (strlen(path) > 0)
{
m_strFilePath.clear();
m_strFileName.clear();
string strFullPath(path);
int nFind = strFullPath.rfind("/") + 1;
m_strFilePath = strFullPath.substr(0, nFind-1);
m_strFileName = strFullPath.substr(nFind, strFullPath.size() - nFind);
}
LOG(LDBG, "Data File %s, %s", m_strFilePath.c_str(), m_strFileName.c_str());
if(m_strFilePath.size() <= 0 && m_strFileName.size() <= 0)
{
msg << "Wrong path info(" << path << ")";
LOG(LERR, msg.str().c_str());
return false;
}
if (chkdir)
{
// FLOW상 중복파일이 발생할 수 없지만.. 혹시 생기면 전체 데이터에 영향을 줄 수 있기때문에 방어 코드 삽입
Delete();
struct stat dirStat;
// 디렉토리 정보 검사
if (lstat(m_strFilePath.c_str(), &dirStat) != 0)
{
// Directory 가 존재하지 않는 경우 Directory 생성 시도
if (mkdir(m_strFilePath.c_str(), 0755) != 0)
{
if (errno != EEXIST)
{
msg << "DataFile directory create failed : [" << path << "]";
LOG(LERR, msg.str().c_str());
return false;
}
}
}
}
LOG(LDBG, "check CDataFile::Init");
return true;
}
bool CDataFile::Write(const char* fmt, ...)
{
if(m_strFilePath.size() <= 0 || m_strFileName.size() <= 0)
return false;
// DataFile file open
FILE* pFile = NULL;
string strFullName = m_strFilePath + "/" +m_strFileName;
pFile = fopen( strFullName.c_str(), "a+" );
if( pFile == NULL )
{
ostringstream msg;
msg << "DataFile file open fail : [" << strFullName << "]";
LOG(LERR, msg.str().c_str());
return false;
}
// 가변 인자 처리
va_list args;
char buffer[MAX_BUFFER_SIZE];
va_start( args, fmt );
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
{
va_end( args );
fclose( pFile );
return false;
}
va_end(args);
// Write to DataFile file
if(strlen(buffer) > 0)
fprintf( pFile, "%s\n", buffer );
fflush( pFile );
// 종료 처리.
fclose( pFile );
return true;
}
bool CDataFile::Delete()
{
if(m_strFilePath.size() <= 0 || m_strFileName.size() <= 0)
return false;
string strFullName = m_strFilePath + m_strFileName;
bool bReturn = Delete(strFullName.c_str());
if(bReturn)
{
m_strFilePath.clear();
m_strFileName.clear();
}
return bReturn;
}
bool CDataFile::Delete(const char* path)
{
// 변수 유효성 검사.
if(path == NULL || strlen(path) == 0)
return false;
struct stat dirStat;
// 디렉토리 정보 검사
if(lstat(path, &dirStat ) == 0)
{
remove(path);
ostringstream msg;
msg << "DataFile file delete success : [" << path << "]";
LOG(LDBG, msg.str().c_str());
}
else
{
ostringstream msg;
msg << "DataFile file delete failed : [" << path << "]";
LOG(LDBG, msg.str().c_str());
return false;
}
return true;
}
+47
View File
@@ -0,0 +1,47 @@
/***************************************************************************
CDataFile
-----------------------------------------
begin : 2011/10/28
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __LIBRARY_DATAFILE_H__
#define __LIBRARY_DATAFILE_H__
#include <string>
#include <list>
#include <ostream>
#include <sstream>
using namespace std;
class CDataFile
{
private:
string m_strFilePath;
string m_strFileName;
public:
CDataFile();
~CDataFile();
bool Init(const char* path, bool chkdir = true);
bool Write(const char* fmt, ...);
bool Delete();
bool Delete(const char* path);
inline string GetFilePath()
{ return m_strFilePath; }
inline string GetFileName()
{ return m_strFileName; }
};
#endif /* __LIBRARY_DATAFILE_H__ */
+180
View File
@@ -0,0 +1,180 @@
/***************************************************************************
Database Class
-----------------------------------------
begin : 2010/03/09
copyright : (C) 2010 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include <string.h>
#include <stdlib.h>
#include <sstream>
#include "Database.h"
#include "Logger.h"
DataBase::~DataBase()
{
if(m_PGconn != NULL) {
PgCloseDB();
}
}
PGconn *DataBase::PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout)
{
ostringstream conninfo;
conninfo << "host=" << strHost << " port=" << Port << " dbname=" << strDBName <<
" user=" << strAcct << " password=" << strPasswd << " connect_timeout=" << timeout <<
" application_name=" << PROG_NAME;
#ifdef _USE_LIBPQ_KEEPALIVE
// This option is supported by at least 9.1.2. and Currently supports Linux systems.
conninfo <<" keepalives=1";
#endif // _USE_LIBPQ_KEEPALIVE
//m_PGconn = PQsetdbLogin(strHost.c_str(), szPort, NULL, NULL, strDBName.c_str(), strAcct.c_str(), strPasswd.c_str());
m_PGconn = PQconnectdb(conninfo.str().c_str());
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
return NULL;
}
return m_PGconn;
}
PGconn *DataBase::PgOpenDB(const char *pszDBName)
{
m_PGconn = PQsetdb(NULL, NULL, NULL, NULL, pszDBName);
if(PQstatus(m_PGconn) == CONNECTION_BAD) {
return NULL;
}
return m_PGconn;
}
void DataBase::PgCloseDB()
{
if(m_PGconn != NULL)
{
PQfinish(m_PGconn);
m_PGconn = NULL;
}
}
int DataBase::PgResult(CFLAG flag)
{
int fRet = 0;
int Result;
Result = PQresultStatus(m_pRes);
switch(Result) {
case PGRES_EMPTY_QUERY :
fRet = -1;
break;
case PGRES_BAD_RESPONSE :
fRet = -2;
break;
case PGRES_NONFATAL_ERROR :
fRet = -3;
break;
case PGRES_FATAL_ERROR :
fRet = -4;
break;
case PGRES_TUPLES_OK :
fRet = 1;
break;
case PGRES_COMMAND_OK :
fRet = 2;
break;
}
if(fRet < 0) {
m_ErrorMessage = PQresultErrorMessage(m_pRes);
}
m_ResultCode = Result;
if(flag == CLEAR && m_pRes) {
PQclear(m_pRes);
m_pRes = NULL;
}
return fRet;
}
string &DataBase::GetErrorMessage()
{
return m_ErrorMessage;
}
int DataBase::GetCmdTuples()
{
//fprintf(stderr, "DataBase::GetCmdTuples PQcmdTuples %s\n", PQcmdTuples(m_pRes));
return (int)atoi(PQcmdTuples(m_pRes));
}
int DataBase::GetNoTuples()
{
return PQntuples(m_pRes);
}
int DataBase::GetNoFields()
{
return PQnfields(m_pRes);
}
PGresult *DataBase::GetRes()
{
return m_pRes;
}
void DataBase::PgClear()
{
if(m_pRes)
{
PQclear(m_pRes);
m_pRes = NULL;
}
}
char *DataBase::GetValue(int tuple, int field)
{
return PQgetvalue(m_pRes, tuple, field);
}
int DataBase::PgDoExec(char *pszQuery)
{
return this->PgDoExec(pszQuery, NOT_CLEAR);
}
int DataBase::PgDoExec(string &strQuery)
{
return this->PgDoExec((char *)strQuery.c_str(), NOT_CLEAR);
}
int DataBase::PgDoExec(char *pszQuery, CFLAG flag)
{
if(m_PGconn == NULL) return -1;
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
m_pRes = PQexec(m_PGconn, pszQuery);
int r = PgResult(flag);
return r;
}
int DataBase::PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag)
{
if(m_PGconn == NULL) return -1;
if(PQstatus(m_PGconn) != CONNECTION_OK) return -2;
m_pRes = PQexecParams(m_PGconn, pszQuery, nParamCnt, NULL, paramValues, NULL, NULL, 0);
int r = PgResult(flag);
return r;
}
int DataBase::PgEscapeString(char *to, const char *from, size_t length)
{
int retval = 0;
//PQescapeStringConn(m_PGconn, to, from, length, &retval);
PQescapeString(to, from, length);
return retval;
}
+56
View File
@@ -0,0 +1,56 @@
/***************************************************************************
Database Class
-----------------------------------------
begin : 2010/03/11
copyright : (C) 2010 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __DATABASE_H__
#define __DATABASE_H__
#include <iostream>
#include <string>
#include "libpq-fe.h"
#define MaxSizeOfDBQuery 1024*9
using namespace std;
class DataBase
{
public:
enum CFLAG { CLEAR = 1, NOT_CLEAR };
DataBase() { m_PGconn = NULL; m_pRes = NULL;}
~DataBase();
PGconn *PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout = 10);
PGconn *PgOpenDB(const char *pszDBName);
void PgCloseDB();
int PgResult(CFLAG flag);
PGconn *GetPgConn(){ return m_PGconn;}
PGresult *GetRes();
void SetRes(PGresult * v) {m_pRes = v;}
int GetCmdTuples();
int GetNoTuples();
int GetNoFields();
int GetResultCode() { return m_ResultCode; }
char *GetValue(int tuple, int field);
void PgClear();
int PgDoExec(string &strQuery);
int PgDoExec(char *pszQuery);
int PgDoExec(char *pszQuery, CFLAG flag);
int PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag = NOT_CLEAR);
int PgEscapeString(char *to, const char *from, size_t length);
string &GetErrorMessage();
private:
int m_ResultCode;
PGconn *m_PGconn;
PGresult *m_pRes;
string m_ErrorMessage;
};
#endif // ~__DATABASE_H__
+238
View File
@@ -0,0 +1,238 @@
#include "ExecCall.h"
#include "ServiceConfig.h"
#include "Logger.h"
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <spawn.h>
static int getCommand(string cmd, string & out)
{
string data;
FILE *stream;
int MAX_BUFFER = 256;
char buffer[MAX_BUFFER];
cmd.append(" 2>&1");
LOG(LDEV1, "[POPNE] OPEN %s ", cmd.c_str());
stream = popen(cmd.c_str(), "r");
if (!stream)
{
LOG(LERR, "[POPNE] popen error. [%s] ", cmd.c_str());
return -1;
}
LOG(LDEV1, "[POPNE] READ %s ", cmd.c_str());
memset(buffer, 0, MAX_BUFFER);
while (fgets(buffer, MAX_BUFFER, stream))
{
LOG(LDEV1, "[POPNE] READING.. %s|%s ", cmd.c_str(), buffer);
data.append(buffer);
memset(buffer, 0, MAX_BUFFER);
}
LOG(LDEV1, "[POPNE] READ END %s ", cmd.c_str());
if (ferror(stream))
{
// Handle error.
LOG(LERR, "[POPNE] Handle error. [%s] ", cmd.c_str());
return -2;
}
out = data;
pclose(stream);
//if (status == -1)
//{
// LOG(LERR, "[POPNE] popen run error(%s). [%s] ", data.c_str(),cmd.c_str());
// return -3;
//}
return 0;
}
static int callAnother(char *argv[])
{
//pid_t processID;
//int status = -1;
//status = posix_spawn(&processID, argv[0], NULL, NULL, argv, environ);
//if (status == 0)
// LOG(LDBG, "%s Launching Application", argv[0]);
//else
// LOG(LDBG, "%s Launching application Failed", argv[0]);
///*
pid_t pid = fork();
// 실해된 프로세스 좀비 프로스세 막기 위해서
signal(SIGCHLD, SIG_IGN);
switch (pid)
{
case -1:
LOG(LERR, "Command fork failed.[%s]", argv[0]);
return -1;
break;
case 0:
{
// exec ... call
// close-exec
int flags;
for (int i = getdtablesize(); i-- > 3;) {
if ((flags = fcntl(i, F_GETFD)) != -1)
fcntl(i, F_SETFD, flags | FD_CLOEXEC);
}
execvp(argv[0], const_cast<char**>(argv));
LOG(LERR, "Command exec failed.[%s]", argv[0]); // //check for error in execl
exit(EXIT_FAILURE);
return -2;
}
break;
default:
// wait((int*)0);// child 종료 될 때까지 기다림; block 됨
LOG(LDBG, "%s completed", argv[0]);
return 0;
break;
}
//*/
return 0;
}
CExecCall::CExecCall(CSyncInfo & info)
: m_info(info)
{
}
CExecCall::~CExecCall()
{
}
int CExecCall::Call(Call_TYPE t, bool called /*= true*/)
{
int r = -1 ;
switch (t)
{
case CExecCall::DIFF_SCRIPT:
r = DIFF(called);
break;
case CExecCall::RC_CMOVE:
r = RCCMove(called);
break;
default:
r = -1;
break;
}
return r;
}
int CExecCall::GetFileLine(const char * filename)
{
int64_t r = -1;
string out;
ostringstream cmd, msg;
cmd << "cat " << filename << " | wc -l";
if(getCommand(cmd.str(), out) == 0)
{
r = atoll(out.c_str());
}
msg << "file lines : " << filename << " => " << r;
_LOG(LDBG, msg.str().c_str());
return r;
}
int CExecCall::DIFF(bool called /*= true*/)
{
int r = 0;
// /user/service/etc/svcsync_diff.sh [master data] [slave data] [output file]
string strCmd = CServiceConfig::GetInstance()->GetDiffCommand();
struct stat fileStat;
if (stat(strCmd.c_str(), &fileStat) < 0)
{
LOG(LERR, "Diff Commnad not found.[%s]", strCmd.c_str());
if (getppid() > 1)
{
kill(getppid(), SIGTERM);
}
return -101;
}
strCmd = strCmd + " " + m_info.file_master_name + " " + m_info.file_slave_name + " " + m_info.file_diff_name;
if (called)
{
string out;
r = getCommand(strCmd, out);
if (r != 0)
LOG(LERR, "Fialed diff script.[%s]", strCmd.c_str());
}
else
{
r = -100;
LOG(LNOT, "Only block run diff script.[%s]", strCmd.c_str());
}
return r;
}
int CExecCall::RCCMove(bool called /*= true*/)
{
int r = 0;
// /user/service/bin/rc_cmove -m copy -f [sync file]
string strCmd = CServiceConfig::GetInstance()->GetRCCMoveCommand();
struct stat fileStat;
if (stat(strCmd.c_str(), &fileStat) < 0)
{
LOG(LERR, "RC MOVE Commnad not found.[%s]", strCmd.c_str());
if (getppid() > 1)
{
kill(getppid(), SIGTERM);
}
return -101;
}
if (called)
{
strCmd = strCmd + " -m copy -f " + m_info.file_sync_name;
string out;
r = getCommand(strCmd, out);
}
else
{
char *argv[8];
argv[0] = (char *)strCmd.c_str();
argv[1] = (char *)"-m";
argv[2] = (char *)"copy";
argv[3] = (char *)"-f";
argv[4] = (char *)m_info.file_sync_name.c_str();
argv[5] = (char *)"2>&1";
argv[6] = (char *)"&";
argv[7] = NULL;
r = callAnother(argv);
}
if (r != 0)
LOG(LERR, "Fialed rc_cmove.[%s]", strCmd.c_str());
return 0;
}
+50
View File
@@ -0,0 +1,50 @@
/****************************************************************************
exec system Wrapper Class
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __EXEC_CALL_H__
#define __EXEC_CALL_H__
#include "SyncList.h"
class CExecCall
{
public:
enum Call_TYPE
{
DIFF_SCRIPT = 0,
RC_CMOVE = 1
};
CExecCall(CSyncInfo & info);
~CExecCall();
// 외부 명령어 수행 함수
// @param t : 수행 명령어 종류(Call_TYPE 참조)
// called : 수행 명령어 완료 대기(true: 수행 명령어 종료 대기, flase : 명령만 수행)
// @return 0 : 정상 수행, 0 이외 : 비정상 수행됨
int Call(Call_TYPE t, bool called = true);
// 외부 명령인 wc -l 수행 함수
// @return 0 <= : filename 대한 wc 수행 결과 , 0 > : 비정상 수행됨
int GetFileLine(const char * filename);
private:
int DIFF(bool called = true);
int RCCMove(bool called = true);
CSyncInfo m_info;
};
#endif /* __EXEC_CALL_H__ */
+216
View File
@@ -0,0 +1,216 @@
#include "GetSource.h"
#include "Logger.h"
#include "ProcessSocketControl.h"
#include "ServiceConfig.h"
#include "RcSyncdRequestData.h"
#define REQUEST_TIME_OUT_DIFF (120*60)
#define REQUEST_TIME_OUT (5)
CGetSource::CGetSource(CSyncInfo &info)
: m_syncinfo(info)
{
}
CGetSource::~CGetSource()
{
}
bool CGetSource::GetRemote()
{
int nResult;
// Slave rc_syncd 접속 및 content 정보 요청
CProcessSocketControl slave;
if( slave.ConnectTarget(m_syncinfo.sync_rcts, CServiceConfig::GetInstance()->GetOperationPort()) == false )
{
_LOG( LERR, "Connect failed to slave, %s", m_syncinfo.sync_rcts.c_str() );
}
CReqSlaveData request;
request.sync_type = m_syncinfo.sync_type;
request.start_time = m_syncinfo.start_time;
request.end_time = m_syncinfo.end_time;
request.master = m_syncinfo.sync_master;
request.slave = m_syncinfo.sync_slave;
if( slave.SendRequestContentListToSlave( request ) == false )
{
// 전송 실패시
_LOG( LERR, "ContentList request send fail.(=> slave)");
slave.Close();
return false;
}
// check request result
// timeout check
time_t timeStart;
time_t timeNow;
timeStart = time(&timeStart);
// 루프를 돌면서 요청에 대한 응답을 대기
while(1)
{
bool bResultSuccess = false; // 처리 결과의 성공/실패 여부를 저장하기 위한 변수.
std::string strMessage; // 수신 메시지 저장 변수.
// diff time 120분
if ( difftime(time(&timeNow), timeStart) > REQUEST_TIME_OUT_DIFF )
{ // REQUEST_TIME_OUT_DIFF
LOG(LERR, "Content List Request timeout from slave. [Master svcid:%s]", request.master.c_str());
slave.Close();
return false;
}
// nTimeout 에 지정된 시간동안 응답대기
nResult = slave.RecvResultContentListFromSlave( REQUEST_TIME_OUT, bResultSuccess, m_syncinfo.file_slave_name, strMessage);
if( nResult == -1 )
{
// Socket 통신 관련 오류 또는 접속 종료가 발생한 경우.
// 해당 내역 로깅 및 루프 종료
_LOG( LERR, "Response wait fail by socket" );
slave.Close();
return false;
}
else if( nResult == 0 )
{
// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우 => Alive Check 패킷 한번 쏘고 다시 Loop 로
if( slave.SendAliveCheck() == false )
{
// Alive 전송 실패시 => Socket 종료 및 오류가 발생한 경우임.
_LOG( LERR, "Response wait fail by socket2");
slave.Close();
return false;
}
LOG( LDBG, "Response wait");
// 정상적인 경우 다시 응답대기.
continue;
}
else if( nResult == 1 )
{
// 요청에 대한 처리결과 정보가 수신된 경우.
// 해당 정보 로깅처리.
if( bResultSuccess == true )
{
// 요청에 대한 처리가 정상적으로 처리된 경우
_LOG( LINF, "Sync result SUCCESS [%s]", strMessage.c_str() );
}
else
{
// 오류 발생시
_LOG( LINF, "Sync result ERROR [%s]", strMessage.c_str() );
slave.Close();
return false;
}
break; // 응답을 받았으니 응답 대기 루프 종료
}
else
{
// Replication 요청에 대한 응답패킷이 아닌 경우.
// 해당 패킷은 무시하고 다시 Loop 로
continue;
}
}
slave.Close();
return true;
}
bool CGetSource::GetSelf(bool isMaster, CSourceData & self)
{
if (self.Init(isMaster, m_syncinfo) == false)
{
LOG(LERR, "Failed Source date initialization.");
return false;
}
//// data 추출 시작
if (self.Execute() == false)
{
LOG(LERR, "Failed Source date execute.");
return false;
}
return true;
}
bool CGetSource::GetSource(GET_TYPE t)
{
switch (t)
{
case CGetSource::BOTH:
case CGetSource::SELF_MASTER_ONLY:
{
int nRetry = 0;
bool bRemoteSuccess= false;
// master source 데이터 자기 자신
CSourceData master;
if (GetSelf(true, master) == false)
{
LOG(LERR, "Failed GetSource(Slave).");
return false;
}
if (t == CGetSource::BOTH)
{
// Slave에 동기화 대상 파일 요청
do
{
bRemoteSuccess = GetRemote();
if( bRemoteSuccess == true)
{
break;
}
++nRetry;
}while(nRetry > 3); // 재시도 3회
}
//// data 추출 완료 대기
pthread_join(*master.GetThreadHandle(), NULL);
if( bRemoteSuccess == false )
{
return false;
}
//// 작업 성공 유무 체크
string errmsg;
if (master.isError(errmsg) == true)
{
LOG(LERR, "Failed Master Source date [%s].", errmsg.c_str());
return false;
}
}
break;
case CGetSource::SELF_SLAVE_ONLYE:
{
CSourceData slave;
if (GetSelf(false, slave) == false)
{
LOG(LERR, "Failed GetSource(Slave).");
return false;
}
//// data 추출 완료 대기
pthread_join(*slave.GetThreadHandle(), NULL);
//// 작업 성공 유무 체크
string errmsg;
if (slave.isError(errmsg) == true)
{
LOG(LERR, "Failed Source date [%s].", errmsg.c_str());
return false;
}
}
break;
default:
LOG(LERR, "Unknown GetSouce Type.");
return false;
break;
}
return true;
}
+44
View File
@@ -0,0 +1,44 @@
/****************************************************************************
Get Source Class Header
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __GET_SOURCE_H__
#define __GET_SOURCE_H__
#include "SyncList.h"
#include "SourceData.h"
class CGetSource
{
public:
enum GET_TYPE
{
BOTH = 0, // (local Master, remove : slave)
SELF_MASTER_ONLY = 1,
SELF_SLAVE_ONLYE = 2
};
CGetSource(CSyncInfo &info);
~CGetSource();
bool GetSource(GET_TYPE t);
private:
bool GetRemote();
bool GetSelf(bool isMaster, CSourceData & self);
CSyncInfo m_syncinfo;
};
#endif /* __GET_SOURCE_H__ */
+487
View File
@@ -0,0 +1,487 @@
#include "Main.h"
#include "ProcessRename.h"
#include "ServiceConfig.h"
#include "Logger.h"
#include "Worker.h"
#include "Util.h"
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
// Accept 대기 Count 수
#define DEFAULT_ACCEPT_WAIT_COUNT 50
// 전역 객체 정보
// 명령 수신, Data 송수신을 담당할 TCP Listen Socket
int g_listenSocket = -1;
// 사용방법 표시
void PrintUsage()
{
fprintf(stderr, "\n");
fprintf(stderr, "Usage: " PROG_NAME " [-h] [-v] [-c {file}] \n");
fprintf(stderr, "Options: \n");
fprintf(stderr, " -h Display help information \n");
fprintf(stderr, " -v Display version \n");
fprintf(stderr, " -c {file} Use {file} as config file \n");
fprintf(stderr, "\n");
fprintf(stderr, PROG_NAME " is Solbox Cloud Storage module that service - service content sync and check .\n\n");
return;
}
// 버전 정보 표시
void PrintVersion()
{
fprintf(stderr, "\n");
fprintf(stderr, PROG_NAME " version: " PROG_VERSION "\n\n");
return;
}
int main( int argc, char * argv[] )
{
// 설정 파일을 저장할 변수.
std::string strConfigFileName = DEFAULT_CONFIG_FILE;
// linux 에서 프로세스를 Titile 변경을 지원을 위해서 argv 메모리에 저장
argv = save_ps_display_args(argc, argv);
// 전달 받은 옵션 여부 확인 및 처리
if( argc >= 2)
{
int opt;
while((opt = getopt(argc, argv, "hvc:")) != -1) // 옵션 끝까지 파싱처리
{
switch(opt)
{
case 'h':
case '?': // 정의되지 않은 문자가 나타날 경우 getopt 에 자동반환. 도움말 표시 처리.
PrintUsage();
return EXIT_SUCCESS;
case 'v':
PrintVersion();
return EXIT_SUCCESS;
case 'c':
strConfigFileName = optarg;
fprintf(stderr, PROG_NAME " start with user define conf[%s]\n", strConfigFileName.c_str());
break;
default: // 본 조건절은 getopt 특성상 동작하지 않지만...업무 Flow 이해(?)를 위해 유지한다.
fprintf(stderr, "Unkonwn options[%c] used. program terminated.\n", opt);
return EXIT_FAILURE;
}
}
}
// 프로그램 중복 실행 체크
if( IsCurrentProcessRun() == true )
{
fprintf(stderr, "[warning] Process[" PROG_NAME "] is already running...\n");
return EXIT_FAILURE;
}
// conf 로딩 및 검사
std::string szErrorMessage;
if( CServiceConfig::Init( PROG_NAME, strConfigFileName, szErrorMessage ) == false)
{
fprintf(stderr, "[ERR] %s\n\n", szErrorMessage.c_str());
return EXIT_FAILURE;
}
// Log 객체 생성 및 초기화
if (CLogger::Init(PROG_NAME, CServiceConfig::GetInstance()->GetLogPath(), LINF) == false)
{
fprintf(stderr, "[ERR] Log module initialize failed.[%s]\n\n", CServiceConfig::GetInstance()->GetLogPath());
return EXIT_FAILURE;
}
// Daemonize...
// - nochdir: true(작업 디렉토리 변경 안함)
// - noclose: false (표준 입출력, 에러를 /dev/null 로 리디렉트 처리)
if( daemon(1, 0) == -1)
{
fprintf(stderr, "[ERR] Process daemonize failed.[%s]\n\n", strerror(errno) );
ReadyToExit();
return EXIT_FAILURE;
}
// Signal 처리기 설정
SetSignalMain();
// Process 기동 관련 정보 로깅
std::vector<std::string> vecTemp;
std::vector< std::string >::const_iterator it;
_LOG( LINF, "***********************************************************");
_LOG( LINF, " %s Start. Version: %s", PROG_NAME, PROG_VERSION);
_LOG( LINF, "***********************************************************");
_LOG( LINF, "Config : %s", strConfigFileName.c_str());
_LOG( LINF, "Log : %s/%s", CServiceConfig::GetInstance()->GetLogPath(), PROG_NAME);
_LOG( LINF, "Log level : %d", CServiceConfig::GetInstance()->GetLogLevel());
_LOG( LINF, "RC ID : %s", CServiceConfig::GetInstance()->GetRcId());
_LOG( LINF, "RCDB : %s %u %s %s", CServiceConfig::GetInstance()->GetRcdbIp()
, CServiceConfig::GetInstance()->GetRcdbPort()
, CServiceConfig::GetInstance()->GetRcdbName()
, CServiceConfig::GetInstance()->GetRcdbAcct());
_LOG( LINF, "Skip keyword" );
vecTemp = CServiceConfig::GetInstance()->GetSkipWord();
for( it = vecTemp.begin(); it != vecTemp.end(); it++ )
{
_LOG( LINF, " : %s", it->c_str() );
}
_LOG( LINF, "Operation tcp port : %u", CServiceConfig::GetInstance()->GetOperationPort() );
_LOG( LINF, "DB pool count : %u", CServiceConfig::GetInstance()->GetDbPoolCount() );
_LOG( LINF, "Work pool count : %u", CServiceConfig::GetInstance()->GetWorkPoolCount() );
_LOG( LINF, "Safety factor : %u (sec)", CServiceConfig::GetInstance()->GetSafetyFactor() );
_LOG( LINF, "Once Size : %u (day)", CServiceConfig::GetInstance()->GetOnceSize() );
_LOG( LINF, "Check" );
_LOG( LINF, " small : %u (sec)", CServiceConfig::GetInstance()->GetChkSmall() );
_LOG( LINF, " middle : %u (sec)", CServiceConfig::GetInstance()->GetChkMiddle() );
_LOG( LINF, " once : %u (hour)", CServiceConfig::GetInstance()->GetChkOnce() );
_LOG( LINF, "Diff command : %s", CServiceConfig::GetInstance()->GetDiffCommand() );
_LOG( LINF, "***********************************************************");
// Log Level 재설정. -> conf 설정대로 변경 처리.
#ifdef _DEBUG_
CLogger::GetInstance()->SetLogLevel(LDBG);
#else
CLogger::GetInstance()->SetLogLevel(CServiceConfig::GetInstance()->GetLogLevel());
#endif
// Command 및 Data 송수신 처리 관련 Listen Socket 생성
if( MakeListenSocket( g_listenSocket ) == false )
{
LOG( LERR, "Listen socket create failed. => process exit." );
return EXIT_FAILURE;
}
// Worker Process 생성
if( MakeProcessWorker( g_listenSocket ) == false )
{
LOG( LERR, "Worker process make failed. => process exit.");
ReadyToExit();
return EXIT_FAILURE;
}
// Process Rename
set_ps_display(PROG_NAME": Main [monitor worker process]", false);
// Main Process : 그냥 대기
while( 1 )
{
// Main Process 는 할 일이 없다.
// => Worker 프로세스 종료시 SIGCHLD 신호로 인해 신호처리기에서 Worker Process 재성성 수행함.
// => 따라서 그냥 시그널 대기
pause();
}
// 다음의 코드는 Daemon 으로 동작하기 때문에 수행되지 않는다.
ReadyToExit();
return EXIT_SUCCESS;
}
// Command, Data 수신을 위한 TCP Listen socket 생성
bool MakeListenSocket( int & listenSocket )
{
listenSocket = socket( AF_INET, SOCK_STREAM, 0 );
if( listenSocket == -1 )
{
int errorNum = errno;
LOG( LERR, "Listen socket create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
struct sockaddr_in listenSockAddr;
socklen_t listenSockLen = 0;
memset( &listenSockAddr, 0x00, sizeof( listenSockAddr ) );
listenSockAddr.sin_family = AF_INET;
listenSockAddr.sin_port = htons( CServiceConfig::GetInstance()->GetOperationPort() );
listenSockAddr.sin_addr.s_addr = htonl( INADDR_ANY );
listenSockLen = sizeof( listenSockAddr );
int result = 0;
// Socket Port Reuse Option Set
int opt = 1;
result = setsockopt( listenSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof( opt ) );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "Listen socket option[SO_REUSEADDR] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
// Keep Alive Option set
opt = 1;
result = setsockopt( listenSocket, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof( opt ) );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "Listen socket option[SO_KEEPALIVE] set failed. [%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
// Socket Bind
result = bind( listenSocket, ( struct sockaddr * )&listenSockAddr, listenSockLen );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "Listen socket bind failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
// Socket Listen
result = listen( listenSocket, DEFAULT_ACCEPT_WAIT_COUNT );
if( result != 0 )
{
int errorNum = errno;
LOG( LERR, "Listen socket listen failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// Worker Process 생성(fork) 처리 함수
// @return 생성 성공시 true, 실패시에는 false 를 반환.
bool MakeProcessWorker( int listenSocket )
{
// Worker 프로세스 fork
pid_t processId = fork();
if( processId < 0 ) // fork fail
{
int errorNum = errno;
LOG( LERR, "Worker process create failed. [%d][%s]", errorNum, strerror(errorNum) );
return false;
}
else if( processId == 0 ) // Child Process => Worker Process
{
// Worker Process Main 함수 호출 및 종료처리.
int iResult;
iResult = WorkerMain( listenSocket );
ReadyToExit();
// WorkerMain() 함수 return 값으로 종료처리되도록 한다.
exit( iResult );
}
else
{
// Parent Process => Logging
_LOG(LINF, "Worker[%d] create success." , processId);
// 잠시 대기
solusleep(100000);
}
return true;
}
// std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
// @return void
void Trim( std::string & str)
{
if( str.length() == 0 )
return;
// 문자열 뒤의 공백, TAB, CR 등의 문자 제거처리.
std::string::size_type pos = str.find_last_not_of(" \a\b\f\n\r\t\v");
if( pos != std::string::npos )
str.erase(pos + 1);
// 문자열 앞의 공백, TAB, CR 등의 문자 제거처리.
pos = str.find_first_not_of(" \a\b\f\n\r\t\v");
if( pos != std::string::npos )
str.erase(0, pos);
}
// 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
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 )
{
// 오류 발생시 true 반환하여 프로세스 실행 방지처리
fprintf(stderr, "[ERR] Process duplication check failed.[popen error][%s]\n", strerror(errno));
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() )
{
fprintf(stderr, "[info] Process duplication found. pid[%d]\n", atoi(tempPid.c_str()));
bRun = true;
break;
}
}
pclose( fd );
return bRun;
}
}
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
void ReadyToExit()
{
if( g_listenSocket != -1 )
{
close( g_listenSocket );
g_listenSocket = -1;
}
// Logger 객체 종료 처리.
CLogger::Exit();
return;
}
// Main Process 가 종료 Signal 을 전달받은 경우 이를 처리하기 위한 함수.
// @param nSignalNumber [in] 발생한 시그널 Number
// @return void
static void SignalMainTerminate(int nSignalNumber)
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "Main[%d] process exit by user signal[SIGTERM]. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "Main[%d] process exit by abnormal signal[%d]. Good Bye..", getpid(), nSignalNumber );
}
// KILL - Child
kill(0, SIGTERM);
ReadyToExit();
solusleep(500000);
exit( EXIT_SUCCESS );
}
// Main Process 에서 자식 프로세스인 Worker Process 가 종료된 경우 시그널 처리를 위한 함수
// @param nSignalNumber [in] 발생한 시그널 Number
// @return void
static void SignalWorkerDead(int nSignalNumber)
{
pid_t deadPid;
int nDeadStatus;
while( (deadPid = waitpid(-1, &nDeadStatus, WNOHANG)) > 0 )
{
// Worker 프로세스가 Signal 에 의해 종료되었는가?
if( WIFSIGNALED( nDeadStatus ) )
{
_LOG( LWAR, "Worker[%d] killed by signal[%d].", deadPid, WTERMSIG(nDeadStatus));
}
else
{
_LOG( LWAR, "Worker[%d] killed. Not signal.", deadPid);
// Worker 프로세스가 EXIT_FAILURE 반환 ( 초기화 실패시 )
// 해당 내역을 화면 및 로그 상에 출력하고
// Worker 프로세스를 재생성 처리하지 않는다.
if( WIFEXITED( nDeadStatus ) )
{
if( WEXITSTATUS( nDeadStatus ) == EXIT_FAILURE )
{
_LOG( LERR, "Worker[%d] initilaize failed.", deadPid);
_LOG( LERR, "Main[%d] abnormally exit by worker initialize failed. Good Bye..", getpid());
ReadyToExit();
solusleep(500000);
exit( EXIT_SUCCESS );
}
}
}
// Worker Process 재생성 처리.
if( MakeProcessWorker( g_listenSocket ) == false )
{
// Worker Process 재성성 실패시
// 로깅 처리 후 Main 프로세스 종료 시그널 발생
_LOG( LERR, "Worker process recreate failed. => Main process exit.");
raise( SIGTERM );
}
else
{
_LOG( LWAR, "Worker process recreate success.");
}
}
// 오류 발생시 해당 내역 로깅
if( deadPid < 0 )
{
int errorNum = errno;
LOG( LERR, "Main process error: SIG_CHLD receive but waitpid return error[%d][%s]", errorNum, strerror(errorNum));
}
return;
}
/// @brief Main 프로세스 signal 처리 설정을 위한 함수
/// @return void
void SetSignalMain()
{
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 신호 처리 => ? */
// Child Process 인 Worker 프로세스 종료에 대한 처리기 설정.
act.sa_handler = SignalWorkerDead;
sigaction( SIGCHLD, &act, NULL);
// 사용자 종료 또는 에러 관련 신호 처리.
act.sa_handler = SignalMainTerminate;
sigaction( SIGTERM, &act, NULL); /* kill -TERM 에 의한 프로세스 종료시 */
sigemptyset(&set); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask(SIG_SETMASK, &set, NULL);
}
+64
View File
@@ -0,0 +1,64 @@
/***************************************************************************
rc_syncd Main Header ( Main.h )
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
***************************************************************************/
#ifndef __RC_SYNCD_MAIN_H__
#define __RC_SYNCD_MAIN_H__
#include <string>
#ifdef __cplusplus
extern "C" {
#endif
// 사용 방법을 화면에 표시하기 위한 함수.
void PrintUsage(void);
// Version 정보를 화면에 표시하기 위한 함수
void PrintVersion(void);
// Command, Data 수신을 위한 TCP Listen socket 생성
bool MakeListenSocket( int & listenSocket );
// Worker Process 생성(fork) 처리 함수
// @return 생성 성공시 true, 실패시에는 false 를 반환.
bool MakeProcessWorker( int listenSocket );
// std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
// @param str [in/out] Trim 할 문자열을 저장한 String 참조변수.
// @return void
void Trim( std::string & str );
// 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
bool IsCurrentProcessRun( void );
// 프로세스 종료에 따른 반복적인 종료 관련 작업을 수행하기 위한 함수.
void ReadyToExit(void);
// Main 프로세스 signal 처리 설정을 위한 함수
// @return void
void SetSignalMain( void );
/* Signal 처리를 위한 각 Signal Handler 함수는
* 다른 Code 에서 Include 처리시 Static 관련 문제로 인해
* 본 Header 에서 선언처리 하지 않음. cpp 에만 존재
*/
#ifdef __cplusplus
}
#endif
#endif /* __RC_SYNCD_MAIN_H__ */
+133
View File
@@ -0,0 +1,133 @@
#include "MakeSyncFile.h"
#include "ExecCall.h"
#include "WorkPool.h"
#include "Logger.h"
#include "Util.h"
#include <fstream>
#define WORK_WAIT 1000000 // microsecond
static int worknotifyfn(void *object, short success)
{
CMakeSyncFile* o = reinterpret_cast<CMakeSyncFile *>(object);
return o->WorkNotify(success);
}
CMakeSyncFile::CMakeSyncFile(CSyncInfo &info)
: m_info(info), m_stop(false)
{
pthread_mutex_init(&m_notifymutex, NULL);
}
CMakeSyncFile::~CMakeSyncFile()
{
m_stop = true;
pthread_mutex_destroy(&m_notifymutex);
}
bool CMakeSyncFile::MakeSyncFile()
{
CExecCall diff(m_info);
// diff
if (diff.Call(CExecCall::DIFF_SCRIPT) != 0)
{
LOG(LERR, "diff data file make failed.[%s]", m_info.file_diff_name.c_str());
return false;
}
// read diff file
m_status.m_diff = diff.GetFileLine(m_info.file_diff_name.c_str());
if (m_status.m_diff < 0)
{
LOG(LERR, "Failed diff command call");
return false;
}
ifstream difffile;
try {
difffile.open(m_info.file_diff_name.c_str());
if (!difffile.is_open())
{
LOG(LWAR, "No difference.[%s=>%s:%s~%s]",
m_info.sync_master.c_str(), m_info.sync_slave.c_str(),
m_info.start_time.c_str(), m_info.end_time.c_str());
return true;
}
}
catch (ios_base::failure& e)
{
LOG(LERR, "diff data file open error.[%s]", m_info.file_diff_name.c_str());
return false;
}
string line;
while (!difffile.eof())
{
getline(difffile, line);
if (line.empty() == true || line == "\r")
{
#ifdef _DEBUG
cout << "diff data read : empty or linefeed" << line << endl;
#endif // _DEBUG
continue;
}
CWork *w = CWorkPool::getInstance()->GetWorkPool();
if (w != NULL)
{
++m_status.m_work;
// validation
w->run(m_info, line, worknotifyfn, this);
}
else
{
++m_status.m_launcherfail;
LOG(LERR, "Get Work Pool error.[%s]", line.c_str());
}
}
difffile.close();
// 마지막 라인에 캐리지 리턴 없는 경우 파일을 실제 읽었을 때보다
// 한라인 작은 수를 wc -l 에서 출력되어 작업이 완료되지 않는 것을
// 막기 위해서 work가 diff 값보다 클 때 diff 값을 work 값으로 보정
if (m_status.m_diff < m_status.m_work)
m_status.m_diff = m_status.m_work;
// end of validation
do
{
if (m_status.m_diff ==
(m_status.m_error + m_status.m_success + m_status.m_launcherfail))
{
LOG(LDEV1, "work end of validation");
m_stop = true;
break;
}
LOG(LDEV1, "working of validation.........");
solusleep(WORK_WAIT);
} while (!m_stop);
return true;
}
int CMakeSyncFile::WorkNotify(short success)
{
pthread_mutex_lock(&m_notifymutex);
if (success == 0)
{
++m_status.m_success;
}
else
{
++m_status.m_error;
}
pthread_mutex_unlock(&m_notifymutex);
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
/****************************************************************************
Make Sync File Class Header
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __MAKE_SYNC_FILE_H__
#define __MAKE_SYNC_FILE_H__
#include <sys/types.h>
#include "SyncList.h"
class CMakeSyncFile
{
public:
CMakeSyncFile(CSyncInfo &info);
~CMakeSyncFile();
bool MakeSyncFile();
int WorkNotify(short success);
private:
class CStatus
{
public:
CStatus()
: m_diff(0), m_work(0), m_launcherfail(0), m_success(0), m_error(0) {}
public:
int64_t m_diff;
int64_t m_work;
int64_t m_launcherfail;
int64_t m_success;
int64_t m_error;
};
CSyncInfo m_info;
CStatus m_status;
bool m_stop;
pthread_mutex_t m_notifymutex;
};
#endif /* __MAKE_SYNC_FILE_H__ */
+81
View File
@@ -0,0 +1,81 @@
#****************************************************************************
# Makefile for rc_syncd
# -----------------------------------------
#
# begin : 2014/09/02
# copyright : (C) 2005 Solbox Inc.
# author : storage dev team
# email : storage.sd@solbox.com
# version : 3.4
#
# 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.
#*****************************************************************************
# Program info
PROG_NAME = rc_syncd
REVISION = 1422
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
DEFAULT_CONFIG_FILE = /user/service/etc/rcts.conf
INSTALL_BIN = /user/service/bin
INSTALL_CONF = /user/service/etc
# Compiler info
CC = /usr/bin/g++
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized\
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
-fno-rtti -D_REENTRANT
LFLAGS =
DFLAGS = -DPROG_NAME=\"$(PROG_NAME)\" -DPROG_VERSION=\"$(PROG_VERSION)\" -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\"
# Application Enviroment
APP = $(PROG_NAME)
DIR_INCLUDE = -I./. -I../lib -I /user/db/pgsql/include -I../
DIR_LIB = -L../lib
OBJ = ServiceConfig.o ProcessStatus.o RcdbInfo.o Util.o Database.o RcSyncdClientSocket.o \
Running.o Scheduler.o WorkPool.o DBConnPool.o SyncList.o DataFile.o SourceData.o \
ModeMaster.o ModeSlave.o ModeSync.o RcSyncdRequest.o MasterJobThread.o\
GetSource.o Validation.o ExecCall.o MakeSyncFile.o Synchronization.o Synchronization.o \
ProcessSocketControl.o ClientThread.o \
Worker.o Main.o
LIBS = ../lib/libInterCommon.a -lpthread -lcrypt /user/db/pgsql/lib/libpq.a
############################
all:$(APP)
sync
%.o: %.cpp
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
$(PROG_NAME): $(OBJ)
$(CC) $(LFLAGS) -o $@ $^ $(DFLAGS) $(DIR_LIB) $(LIBS)
clean:
-rm -f *.o core.* *.out *.log
-rm -f $(APP)
sync
install : $(APP)
-cp $(APP) $(INSTALL_BIN)/$(APP)
sync
# End of Makefile
+117
View File
@@ -0,0 +1,117 @@
#include <errno.h>
#include <string.h>
#include "MasterJobThread.h"
#include "GetSource.h"
#include "MakeSyncFile.h"
#include "Synchronization.h"
CMasterJobThread::CMasterJobThread(CSyncInfo info, CWorkResult* pResult)
: m_info(info)
,m_pResult(pResult)
{
m_threadHandle = 0;
}
CMasterJobThread::~CMasterJobThread()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로 업무 Flow 수정시 주의할 것
//if( m_threadHandle != 0 )
//pthread_cancel( m_threadHandle );
}
// thread 를 생성하여 처리 업무 flow start
bool CMasterJobThread::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CMasterJobThread::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
// 스레드 함수
void* CMasterJobThread::threadFunc( void* arg )
{
CMasterJobThread* pObject = reinterpret_cast<CMasterJobThread *>( arg );
//pthread_detach( pthread_self() );
// 실제 작업 수행.
pObject->Execute();
// 생성된 파일 삭제 처리
pObject->m_info.UnlinkFile();
// 객체 자동 delete 처리.
//delete pObject;
return NULL;
}
// 실제 업무 flow를 정의 한다.
void CMasterJobThread::Execute()
{
string strMessage;
// 동기화 대상 파일 수집
CGetSource objGetSource(m_info);
if( objGetSource.GetSource(CGetSource::BOTH) == false )
{
LOG( LERR, "Get Source file failed.");
m_pResult->bSuccess = false;
m_pResult->strMasterID = m_info.sync_master;
m_pResult->strRcts = m_info.sync_rcts;
m_pResult->strMessage = "Get Source file failed.";
m_pResult->strSyncFileName = "";
return;
}
// sync file 생성..
CMakeSyncFile objMakeSyncfile(m_info);
if( objMakeSyncfile.MakeSyncFile() == false )
{
LOG( LERR, "Sync file make failed.");
m_pResult->bSuccess = false;
m_pResult->strMasterID = m_info.sync_master;
m_pResult->strRcts = m_info.sync_rcts;
m_pResult->strMessage = "Sync file make failed.";
m_pResult->strSyncFileName = "";
return;
}
// sync 요청 처리.
CSynchronization objSynchronization(m_info);
int r = objSynchronization.ExcuteSync();
if (r < 0 )
{
LOG( LERR, "Synchronization request failed.");
m_pResult->bSuccess = false;
m_pResult->strMasterID = m_info.sync_master;
m_pResult->strRcts = m_info.sync_rcts;
m_pResult->strMessage = "Synchronization request failed.";
m_pResult->strSyncFileName = "";
return;
}
if (m_info.sync_type > SYNC_TYPE::NORMA_N_ONLY)
{
m_pResult->strMessage = "Get sync file Success.";
// 파일명 추가
m_pResult->strSyncFileName = r == 0 ? "" : m_info.file_sync_name;
}
else
{
m_pResult->strMessage = "Synchronization request Success.";
m_pResult->strSyncFileName = "";
}
m_pResult->bSuccess = true;
m_pResult->strMasterID = m_info.sync_master;
m_pResult->strRcts = m_info.sync_rcts;
LOG(LDBG, "%s", m_pResult->strMessage.c_str());
}
+52
View File
@@ -0,0 +1,52 @@
/****************************************************************************
Synchronization Class Header
-----------------------------------------
begin : 2014/09/17
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __MASTERJOBTHREAD_H__
#define __MASTERJOBTHREAD_H__
#include "ModeMaster.h"
#include "SyncList.h"
class CMasterJobThread
{
public:
CMasterJobThread(CSyncInfo info, CWorkResult* pResult);
~CMasterJobThread();
bool Start(); //Thread 시작
// 호출측에서 Thread의 종료 여부등을 판단하기 위함
inline pthread_t GetThreadHandle()
{ return m_threadHandle; }
private:
// 스레드 함수
static void* threadFunc( void* arg );
// 쓰레드 실행 함수
void Execute();
private:
CSyncInfo m_info;
CWorkResult* m_pResult;
// 쓰레드 핸들
pthread_t m_threadHandle;
// client 와 통신 관련 처리를 수행하기 위한 control 객체
CProcessSocketControl m_client;
};
#endif /* __MASTERJOBTHREAD_H__ */
+41
View File
@@ -0,0 +1,41 @@
/****************************************************************************
Service Mode Base Class Header
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __SERVICE_MODE_BASE_H__
#define __SERVICE_MODE_BASE_H__
#include "SyncList.h"
#include "ProcessSocketControl.h"
// class CServiceMode : Service Mode Base class
class CServiceMode
{
public:
CServiceMode(CProcessSocketControl * pclient) : m_pclient(pclient) {};
virtual ~CServiceMode(){};
// °¢ mode ÀÛ¾÷ ¼öÇà
virtual void Execute() = 0;
protected:
CProcessSocketControl * m_pclient;
CSyncList m_synclist;
inline bool IsEmpytSycnList() { return m_synclist.Empty(); }
private:
};
#endif /* __SERVICE_MODE_BASE_H__ */
+129
View File
@@ -0,0 +1,129 @@
#include "ModeMaster.h"
#include "SourceData.h"
#include "Logger.h"
#include "SyncList.h"
#include "MasterJobThread.h"
// class CServiceMode : Service Master Mode class
CMasterMode::CMasterMode(CProcessSocketControl * pclient)
: CServiceMode(pclient)
{
}
CMasterMode::~CMasterMode()
{
}
bool CMasterMode::CreateSyncList(CReqMasterData &d)
{
return m_synclist.CreateList(d);
}
void CMasterMode::Execute()
{
std::string message = "";
list<CMasterJobThread*> listThread;
list<CWorkResult*> listWorkResult;
bool bResult = true;
if (IsEmpytSycnList())
{
LOG(LERR, "Empty Master Mode Sync Data");
message = "Empty Master Mode Sync Data";
// return 처리전에 꼭 client 에 결과 전송처리할 것
// 결과 전송하지 않으면.. 무한 대기함.
m_pclient->SendResultSyncToClient( true, message );
return;
}
CMasterJobThread *pMasterJobThread = NULL;
while( IsEmpytSycnList() == false )
{
// Master job work thread 수행 결과를 받기 위한 변수
CWorkResult* pResult = new CWorkResult;
CSyncInfo objSyncInfo = m_synclist.Pop();
LOG( LDBG, "Master[%s] Slave[%s] RCTS [%s]", objSyncInfo.sync_master.c_str() , objSyncInfo.sync_slave.c_str(), objSyncInfo.sync_rcts.c_str());
pMasterJobThread = new CMasterJobThread(objSyncInfo, pResult);
listWorkResult.push_back(pResult);
// thread 구동.....
if( pMasterJobThread->Start() == true )
{
listThread.push_back(pMasterJobThread);
LOG(LDBG, "MasterJobThread start ok.");
}
else
{
LOG(LINF, "MasterJobThread start failed.");
// 다른 sync 작업은 성공적으로 쓰레드 생성 될 수 있므로 해당 sync item만 에러 설정함
//message = "MasterJobThread create start failed.";
//m_pclient->SendResultSyncToClient( bResult, message );
pResult->bSuccess = false;
pResult->strMasterID = objSyncInfo.sync_master;
pResult->strMessage = "MasterJob Thread create start failed.";
if( pMasterJobThread != NULL )
{
delete pMasterJobThread;
}
}
}
// 전체 쓰레드가 종료될때까지 대기 하기 위해서 thread join 처리
while (!listThread.empty())
{
list<CMasterJobThread *>::iterator list_iter = listThread.begin();
CMasterJobThread * pthread = *list_iter;
pthread_t handle = pthread->GetThreadHandle();
LOG(LDBG, "Thread Join [%lu]", (unsigned long)handle);
pthread_join(handle, NULL);
LOG(LDBG, "Thread Join realese [%lu]", (unsigned long)handle);
listThread.erase(list_iter);
delete pthread;
}
// 성공 실패 여부를 확인 하는 작업 필요.
while (!listWorkResult.empty())
{
list<CWorkResult*>::iterator iter = listWorkResult.begin();
CWorkResult* pResult = *iter;
// 최종 성공 여부 전달 판단
if( pResult->bSuccess == false)
{
bResult = false;
}
// " MasterTranid;메세지|MasterTranid;메세지|MasterTranid;메세지|MasterTranid;메세지"
// return message 제작
if( message.size() != 0 )
{
message += "|";
}
message += pResult->strMasterID;
message += ";";
message += pResult->bSuccess ? "T":"F";
message += ";";
message += pResult->strRcts;
message += ";";
message += pResult->strMessage;
if(pResult->strSyncFileName.size() != 0)
{
message += ";";
message += pResult->strSyncFileName;
}
listWorkResult.erase(iter);
delete pResult;
}
// 연결된 client로 수행 작업 결과들 리턴
m_pclient->SendResultSyncToClient( bResult, message );
LOG(LINF, "Result : %s", message.c_str());
return;
}
+52
View File
@@ -0,0 +1,52 @@
/****************************************************************************
Service Master Mode Class Header
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __SERVICE_MASTER_MODE_H__
#define __SERVICE_MASTER_MODE_H__
#include "ModeBase.h"
class CWorkResult
{
public:
CWorkResult(){};
~CWorkResult(){};
public:
bool bSuccess; // 성공 여부
std::string strMasterID; // sync_master
std::string strMessage; // error message
std::string strRcts; // rcts info
std::string strSyncFileName; // sync file name
};
// class CServiceMode : Service Master Mode class
class CMasterMode : public CServiceMode
{
public:
CMasterMode(CProcessSocketControl * pclient);
virtual ~CMasterMode();
// 요청 받은 데이터(CReqMasterData) 대한 Sync 정보 만듦
// 함수 수행 성공일 때 Execute() 함수 수행됨
bool CreateSyncList(CReqMasterData &d);
// master mode 작업 수행
virtual void Execute();
private:
};
#endif /* __SERVICE_MASTER_MODE_H__ */
+126
View File
@@ -0,0 +1,126 @@
#include "ModeSlave.h"
#include "GetSource.h"
#include "Logger.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
// class CServiceMode : Service Master Mode class
CSlaveMode::CSlaveMode(CProcessSocketControl * pclient)
: CServiceMode(pclient)
{
}
CSlaveMode::~CSlaveMode()
{
if (!IsEmpytSycnList())
{
m_synclist.GetFirst().UnlinkFile();
}
}
bool CSlaveMode::CreateSyncList(CReqSlaveData &d)
{
return m_synclist.CreateList(d);
}
void CSlaveMode::Execute()
{
std::string strErrorMessage;
if (IsEmpytSycnList())
{
strErrorMessage = "Empty Slave Mode Sync Data";
// 오류 결과 전송
m_pclient->SendErrorContentListToMaster( strErrorMessage );
LOG(LERR, "%s", strErrorMessage.c_str());
return;
}
// 수행 작업
// 요청 받은 정보에 대한 source data 추출
CGetSource slave(m_synclist.GetFirst());
if (slave.GetSource(CGetSource::SELF_SLAVE_ONLYE) == false)
{
strErrorMessage = "Failed Get Source Slave.";
// 오류 결과 전송
m_pclient->SendErrorContentListToMaster( strErrorMessage );
LOG( LERR, "%s", strErrorMessage.c_str() );
return;
}
// Send data : 추출된 source data ( 파일명은 file_slave_name 에 저장된 상태 )
// - 대상 파일 size 확인...
// - fd open 후
// - 전송 처리.
struct stat resultFileStat;
if( stat( m_synclist.GetFirst().file_slave_name.c_str(), &resultFileStat ) != 0 )
{
// 해당 파일에 대한 stat 정보 추출 실패시.
int errorNum = errno;
char tempBuffer[1024];
snprintf( tempBuffer, sizeof( tempBuffer ) - 1, "slave mode result file[%s] stat check fail [%d][%s]"
, m_synclist.GetFirst().file_slave_name.c_str()
, errorNum, strerror( errorNum ) );
strErrorMessage = tempBuffer;
// 오류 결과 전송
m_pclient->SendErrorContentListToMaster( strErrorMessage );
LOG( LERR, "%s", strErrorMessage.c_str() );
return;
}
// 파일 size 추출.
unsigned long long nFileSize = resultFileStat.st_size;
// fd open
int resultFd = open( m_synclist.GetFirst().file_slave_name.c_str(), O_RDONLY );
if( resultFd == -1 )
{
int errorNum = errno;
char tempBuffer[1024];
snprintf( tempBuffer, sizeof( tempBuffer ) - 1, "slave mode result file[%s] open fail [%d][%s]"
, m_synclist.GetFirst().file_slave_name.c_str()
, errorNum, strerror( errorNum ));
strErrorMessage = tempBuffer;
// 오류 결과 전송
m_pclient->SendErrorContentListToMaster( strErrorMessage );
LOG( LERR, "%s", strErrorMessage.c_str() );
return;
}
// 파일 전송 처리.
if( m_pclient->SendResultContentListToMaster( resultFd, nFileSize ) == false )
{
// 전송 처리 실패시..
// 상대편에서 재시도 등의 처리를 수행토록 socket 을 close 처리한다.
LOG( LERR, "slave mode result file[%s] send fail. socket close.", m_synclist.GetFirst().file_slave_name.c_str() );
m_pclient->Close();
}
else
{
_LOG( LDBG, "slave mode result file[%s] send OK.", m_synclist.GetFirst().file_slave_name.c_str() );
}
close( resultFd );
return;
}
+38
View File
@@ -0,0 +1,38 @@
/****************************************************************************
Service Slave Mode Class Header
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __SERVICE_SLAVE_MODE_H__
#define __SERVICE_SLAVE_MODE_H__
#include "ModeBase.h"
// class CServiceMode : Service Slave Mode class
class CSlaveMode : public CServiceMode
{
public:
CSlaveMode(CProcessSocketControl * pclient);
virtual ~CSlaveMode();
// 요청 받은 데이터(CReqSlaveData) 대한 Sync 정보 만듦
// 함수 수행 성공일 때 Execute() 함수 수행됨
bool CreateSyncList(CReqSlaveData &d);
// slave mode 작업 수행
virtual void Execute();
private:
};
#endif /* __SERVICE_SLAVE_MODE_H__ */
+75
View File
@@ -0,0 +1,75 @@
#include "ModeSync.h"
#include "ExecCall.h"
#include "Logger.h"
// class CSyncMode : Sync Mode class
CSyncMode::CSyncMode(CProcessSocketControl * pclient)
: CServiceMode(pclient)
{
}
CSyncMode::~CSyncMode()
{
// rc_cmove 해당 파일 사용하기 때문에 정상적으로 처리되었으면 삭제되면 안됨
//if (!IsEmpytSycnList())
//{
// m_synclist.GetFirst().UnlinkFile();
//}
}
bool CSyncMode::CreateSyncList(CReqSyncData &d)
{
return m_synclist.CreateList(d);
}
void CSyncMode::Execute()
{
// Socket 을 통해 Master 로부터 sync 수행을 위한 파일을 수신 처리한다.
std::string strMessage;
if( m_pclient->RecvFileExecuteSyncFromMaster( m_synclist.GetFirst().file_sync_name, strMessage ) == false )
{
// sync 파일 수신 실패시...
// - 여러 가지 상황이 발생 가능하므로...
// - 에러 로깅 후 세션을 종료 처리한다. ( 그래야.. client 에서 재시도하므로.. )
LOG( LERR, "Sync request file[%s] receive failed [%s]. session close."
, m_synclist.GetFirst().file_sync_name.c_str()
, strMessage.c_str() );
m_pclient->Close();
return;
}
// Client 가 응답대기 중이므로.. rc_cmove 를 실제 수행시키기 전에 응답을 해줘야 한다.
if (IsEmpytSycnList())
{
strMessage = "Empty Sync Mode Sync Data.";
// 오류 결과 전송
m_pclient->SendResultExecuteSyncToMaster( false, strMessage );
m_synclist.GetFirst().UnlinkFile();
LOG( LERR, "%s", strMessage.c_str() );
return;
}
// 실제 rc_move 실행전에 Master 에게 정상 처리관련 응답 전송.
// - 응답 전송 실패시.. Master 가 재시도를 하므로... 작업을 진행하지 말것.
strMessage = "Sync job start.";
if( m_pclient->SendResultExecuteSyncToMaster( true, strMessage ) == false )
{
LOG( LERR, "Send reponse (job start) fail to Master. job cancel." );
m_synclist.GetFirst().UnlinkFile();
return;
}
// 받은 sync file을 인자로 rc_move 수행
CExecCall rcmove(m_synclist.GetFirst());
/// 명령 수행함 시킴
if (rcmove.Call(CExecCall::RC_CMOVE, false) != 0)
{
LOG(LERR, "Failed rc_cmove call");
return;
}
return;
}
+39
View File
@@ -0,0 +1,39 @@
/****************************************************************************
Service Sync Mode Class Header
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __SERVICE_SYNC_MODE_H__
#define __SERVICE_SYNC_MODE_H__
#include "ModeBase.h"
// class CSyncMode : Sync Mode class
class CSyncMode : public CServiceMode
{
public:
CSyncMode(CProcessSocketControl * pclient);
virtual ~CSyncMode();
// 요청 받은 데이터(CReqSyncData) 대한 Sync 정보 만듦
// 함수 수행 성공일 때 Execute() 함수 수행됨
bool CreateSyncList(CReqSyncData &d);
// sync mode 작업 수행
virtual void Execute();
private:
};
#endif /* __SERVICE_SYNC_MODE_H__ */
File diff suppressed because it is too large Load Diff
+224
View File
@@ -0,0 +1,224 @@
/***************************************************************************
rc_syncd - rc_syncd 간의 통신 처리 담당 class
-----------------------------------------
begin : 2014/09/05
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
***************************************************************************/
#ifndef __PROCESS_SOCKET_CONTROL_H__
#define __PROCESS_SOCKET_CONTROL_H__
#include "BaseSocket.h"
#include "RcSyncdProtocol.h"
#include "RcSyncdRequestData.h"
#include <string>
///< BYTE 타입 정의
#ifndef _BYTE_DEFINED
#define _BYTE_DEFINED
typedef unsigned char BYTE;
#endif // _BYTE_DEFINED
#define DEFAULT_SOCKET_TEMP_BUFFER_SIZE 1024 // Socket 관련 data 송수신시 사용할 임시버퍼 크기.
#define FILE_DATA_BUFFER_SIZE 16384 // 16 Kbyte, 파일 송수신시 사용할 임시 버퍼의 크기.
class CProcessSocketControl : public CBaseSocket
{
public:
// 생성자.
// socket [in] 처리할 socket descriptor
CProcessSocketControl();
CProcessSocketControl( const int & socket );
// 소멸자.
~CProcessSocketControl();
/////// 각 업무 Flow 모듈 제공용 Interface 함수 ///////////////////
// 전달받은 Target 으로 Socket 접속을 수행
// @param szTarget [in] 접속 대상 Host name 또는 IP
// @param nPort [in] 접속 Port
// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
bool ConnectTarget( const std::string& szTarget, int nPort );
// Alive Check 를 위한 패킷 전송 => 해당 패킷에 대한 응답은 필요없으므로 수신시 무시할 것.
// @return 전송 성공시 true, 실패시 false 반환.
bool SendAliveCheck(void);
// [Master] Client 에게 Sync 요청에 대한 결과 정보 전송 처리
// @param bSuccess [in] 성공 여부
// @param strMessage [in] Client 로 전달할 메시지
// @return 전송 성공시 true, 전송 실패시 false
bool SendResultSyncToClient( bool bSuccess, std::string& strMessage );
// [Master] Slave rc_syncd 로 content 목록 요청.
// @param [IN] data slave rc_syncd 로 전송할 data 객체
// @return 전송 성공시 true, 전송 실패시 false
bool SendRequestContentListToSlave( CReqSlaveData& data );
// [Master] Slave rc_syncd 에서 전송한 content 목록 정보 처리 결과를 수신하기 위한 함수.
// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )
// 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
// @param bSuccess [out] 성공 여부
// 실패(false) 인 경우.. strErrorMessage 변수에 오류 내역이 저장됨
// @param strSaveFileName [in] 성공(bSuccess==true) 인 경우... 수신된 Data 를 저장하기 위한 파일명 (Full 경로)
// @param strErrorMessage [out] 실패(bSuccess==false) 인 경우... 수신된 오류 메시지를 저장하기 위한 변수
// @return
// -1 : slave rc_syncd 와 통신이 끊어진 경우.
// 0 : slave rc_syncd 로 부터 응답을 아직 수신하지 못한 경우..
// => AliveCheck 등의 작업 후 다시 응답 대기하면 된다.
// 1 : 정상 응답을 수신한 경우
// 2 : 수신된 응답이 다른 요청에 대한 응답인 경우..
// => 무시 처리하고 계속 응답을 대기하면 된다.
int RecvResultContentListFromSlave( int nTimeout, bool& bSuccess, std::string& strSaveFileName, std::string& strErrorMessage );
// [Master] Slave rc_syncd 에 sync 작업 수행을 요청한 후 그 결과를 수신.
// - 본 함수는 data 와 파일을 원격 rc_syncd 에 전달하여 원격 rc_syncd 가 SYNC MODE 로 동작하도록 한다.
// 그리고.. 원격 rc_syncd 의 execute 전까지의 처리 결과를 수신하여 [out] 항목에 그 결과를 저장처리한다.
// data [in] sync mode 수행을 위해 전달해야하는 data 객체.
// fd [in] sync 수행 대상 content 정보를 저장한 파일에 대한 descriptor
// nFileSize [in] sync 파일 크기
// strMessage [out] 원격지의 sync mode 수행 관련 수신된 메시지( 정상, 실패 모두 가능)
// return
// true : sync 수행 요청이 정상적으로 전달되었고... 원격지 rc_syncd 가 정상 동작한 경우..
// false : 그외 모든 오류 사항.
bool SendRequestExecuteSyncToSlave( CReqSyncData& data, int fd, unsigned long long nFileSize, std::string& strMessage );
// [Slave] Master rc_syncd 로 추출된 Content 정보 목록을 전달한다.
// @param [IN] fd 전송할 file descripter
// @param [IN] nFileSize 전송할 file size
// @return 전송 성공시 true, 전송 실패시 false
bool SendResultContentListToMaster( int fd, unsigned long long nFileSize );
// [Slave] Content 정보 목록 추출 실패시 Master rc_syncd 로 관련 오류 내역을 전달한다.
// @param strMessage [IN] master rc_syncd 로 전달할 메시지
// @return 전송 성공시 true, 전송 실패시 false
bool SendErrorContentListToMaster( std::string& strMessage );
// [Sync] Master 에게 Sync Execute 요청에 대한 응답을 전송 처리
// @param bSuccess [in] 성공 여부
// @param strMessage [in] 전달할 메시지
// @return 전송 성공시 true, 전송 실패시 false
bool SendResultExecuteSyncToMaster( bool bSuccess, std::string& strMessage );
/////// 통신 관련 Client Thread 내부용 Interface 함수 ///////////////////
// Client 로 부터 수신된 Sync 요청 Data body 수신 처리.
// @param request [out] 수신된 정보를 저장하기 위한 객체
// @return 수신 성공시 true, 그 외 false
bool RecvBodySyncFromClient( CReqMasterData& request );
// Master rc_syncd 에서 전송한 Content 목록 요청의 Data body 수신 처리.
bool RecvBodyContentListFromMaster( CReqSlaveData& request );
// Master rc_syncd 에서 전송한 sync 실행 요청 관련 data body 수신 처리.
// - 파일 관련 부분은 수신하지 않고.. 아래 함수(RecvFileExecuteSyncFromMaster)를 통해 따로 수신 처리해야 함.
// @param request [in] 수신된 CReqSyncData 정보를 저장하기위한 객체
// @return 성공시 true, 실패시 false
bool RecvBodyExecuteSyncFromMaster( CReqSyncData& request );
// Master rc_syncd 에서 전송한 sync 실행 요청 관련 sync file 관련 부분을 수신 처리.
// @param strSaveFileName [in] 저장 파일명
// @param strErrorMessage [out] return 값이 false 일 경우. 오류 메시지 저장.
bool RecvFileExecuteSyncFromMaster( std::string& strSaveFileName, std::string& strErrorMessage );
// Packet 의 Header 정보를 수신하여 각 정보를 전달받은 참조변수로 반환.
// @param bRequest [out] Request 요청인지 여부
// @param bFromClient [out] Client 로 부터 전달된 Command 인지 여부
// @param command [out] Command 정보
// @return 1 : Data 수신 성공시
// 0 : Timeout 발생시
// -1 : 접속 종료시
int GetHeaderInfo( bool& bRequest, bool& bFromClient, char& command );
// Packet 의 Header 정보 중 현재 전달받은 Header 의 Result 값을 반환
// @param result [out] result 4 Byte 부분의 값을 저장하기 위한 참조변수
// @return result 4 Byte 부분 중 첫번째 Byte 값을 반환.
char GetHeaderResult( void );
void GetHeaderResult( std::vector<char>& result );
// Packet Header 정보를 Log 파일에 Logging 처리
void PrintHeaderToLog( void );
private:
// Packet Header 변수
struct RcSyncdPacketHeader m_packetHeader;
// m_packetHeader 구조체의 크기를 저장하기 위한 상수
const int m_nPacketHeaderLen;
// Packet Header 에 저장된 Data 부분의 길이 정보값.
unsigned int m_nPacketDataLen;
// Packet Data 부분의 수신처리시 임시로 사용할 버퍼.
BYTE m_tempBuffer[DEFAULT_SOCKET_TEMP_BUFFER_SIZE];
protected:
// Packet Header 부분의 수신 처리를 위한 함수.
// @param timeout [in] 대기시간.
// @retrun
// 1 : PACKET_HEADER_RECEIVE_SUCCESS : 성공시
// 0 : PACKET_HEADER_RECEIVE_TIMEOUT : Timeout 발생시
// -1 : PACKET_HEADER_RECEIVE_SOCK_CLOSED : socket 종료된 경우
// -2 : PACKET_HEADER_RECEIVE_INVALID_PROTOCOL : 잘못된 Packet 형식인 경우
// -3 : PACKET_HEADER_RECEIVE_READ_ERROR : 기타 오류
int GetPacketHeader(int timeout = DEFAULT_DATA_RECEIVE_TIMEOUT);
// socket 에서 지정된 크기만큼의 데이터를 읽어 출력변수에 저장처리.
// @param size [in] read 할 데이터 크기
// @param value [out] 읽은 데이터를 저장할 string 변수
// @return On success return true, otherwise return false.
bool GetPacketData( unsigned int& size, std::string& value );
// socket 에서 지정된 크기만큼의 데이터를 읽어 내부 임시버퍼인 m_tempBuffer 에 저장처리.
// @param size [in] read 할 데이터 크기
// @return On success return true, otherwise return false.
bool GetPacketData( unsigned int size );
// pValue 에 저장된 데이터를 unsigned int 형으로 변환처리 및 Endian 변환
unsigned int GetDataToUInt( BYTE * pValue, bool bConvertEndian = true );
// pValue 에 저장된 데이터를 unsigned long long (64Byte) 형으로 변환처리.
unsigned long long GetDataToUInt64( BYTE * pValue );
// 전달받은 Directory 정보를 확인하여 존재하지 않을 경우 생성처리.
bool MakeDirectory( const std::string& szFullPath );
// Socket 으로 부터 수신받은 데이터를 파일에 Write 처리한다.
// 만약 File Write 처리시 오류가 발생하는 경우 Socket Data 의 정상적인 처리를 위해
// Socket 데이터 수신만 처리하고 오류를 반환한다.
// @returnFile Write 가 오류 없이 정상적으로 처리된 경우 true, 오류발생시 false 및 오류메시지 반환
bool ReceiveToFile( FILE * pFile, unsigned long long nFileSize, std::string& errorMessage );
// Packet 정보 중 String 정보를 전송하기 위한 함수.
bool SendString( const std::string& strData );
};
#endif /* __PROCESS_SOCKET_CONTROL_H__ */
+180
View File
@@ -0,0 +1,180 @@
#include "ProcessStatus.h"
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "Logger.h"
#include "ProcessRename.h"
// Process 정보 update 주기 (sec)
#define PROCESS_STATUS_UPDATE_TIME 3
// 생성자
CProcessStatus::CProcessStatus()
{
m_threadHandle = 0;
m_nCurrentClientCount = 0;
m_nCurrentMasterCount = 0;
m_nCurrentSlaveCount = 0;
m_nCurrentSyncCount = 0;
// Lock 초기화
pthread_mutex_init( &m_mxClientThread, NULL );
pthread_mutex_init( &m_mxModeMaster, NULL );
pthread_mutex_init( &m_mxModeSlave, NULL );
pthread_mutex_init( &m_mxModeSync, NULL );
}
// 소멸자
CProcessStatus::~CProcessStatus()
{
// Thread 동작 정지 처리
// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로
// 업무 Flow 수정시 주의할 것
if( m_threadHandle != 0 )
pthread_cancel( m_threadHandle );
pthread_mutex_destroy( &m_mxClientThread );
pthread_mutex_destroy( &m_mxModeMaster );
pthread_mutex_destroy( &m_mxModeSlave );
pthread_mutex_destroy( &m_mxModeSync );
}
// thread를 생성하여 관련 작업을 수행.
bool CProcessStatus::Start()
{
int nRet = ::pthread_create( &m_threadHandle, NULL, CProcessStatus::threadFunc, this );
if( nRet != 0 )
{
// Thread 생성 실패시
int errorNum = errno;
LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );
return false;
}
return true;
}
void* CProcessStatus::threadFunc( void* arg )
{
CProcessStatus* pObject = reinterpret_cast<CProcessStatus *>( arg );
pthread_detach( pthread_self() );
while( 1 )
{
pObject->Execute();
sleep( PROCESS_STATUS_UPDATE_TIME );
}
// Thread 종료시 m_threadHandle 값을 초기화 처리.
// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
pObject->m_threadHandle = 0;
return NULL;
}
// 현재 프로세스 표시 정보 업데이트
void CProcessStatus::Execute( void )
{
// C: client count
// M: master job count
// S: slave job count
// Y: sync job count
#if defined(__FreeBSD__)
setproctitle("[C%d M%d S%d Y%d]",
m_nCurrentClientCount,
m_nCurrentMasterCount,
m_nCurrentSlaveCount,
m_nCurrentSyncCount );
#else
char szTemp[100] = {0};
sprintf(szTemp, "%s: Worker [C%d M%d S%d Y%d]",
PROG_NAME,
m_nCurrentClientCount,
m_nCurrentMasterCount,
m_nCurrentSlaveCount,
m_nCurrentSyncCount );
set_ps_display(szTemp, false);
#endif
return ;
}
// Client 수를 1 증가 처리
void CProcessStatus::PlusClientCount()
{
pthread_mutex_lock( &m_mxClientThread );
++m_nCurrentClientCount;
pthread_mutex_unlock( &m_mxClientThread );
}
// Client 수를 1 감소 처리
// - 정확히 동작하는지 확인을 위해 일부로 (-) 값 보정 처리 안함.
void CProcessStatus::MinusClientCount()
{
pthread_mutex_lock( &m_mxClientThread );
--m_nCurrentClientCount;
pthread_mutex_unlock( &m_mxClientThread );
}
// Master Mode Job 을 1 증가 처리
void CProcessStatus::PlusMasterModeCount()
{
pthread_mutex_lock( &m_mxModeMaster );
++m_nCurrentMasterCount;
pthread_mutex_unlock( &m_mxModeMaster );
}
// Master Mode Job 을 1 감소 처리
// - 정확히 동작하는지 확인을 위해 일부로 (-) 값 보정 처리 안함.
void CProcessStatus::MinusMasterModeCount()
{
pthread_mutex_lock( &m_mxModeMaster );
--m_nCurrentMasterCount;
pthread_mutex_unlock( &m_mxModeMaster );
}
// Slave Mode Job 을 1 증가 처리
void CProcessStatus::PlusSlaveModeCount()
{
pthread_mutex_lock( &m_mxModeSlave );
++m_nCurrentSlaveCount;
pthread_mutex_unlock( &m_mxModeSlave );
}
// Slave Mode Job 을 1 감소 처리
// - 정확히 동작하는지 확인을 위해 일부로 (-) 값 보정 처리 안함.
void CProcessStatus::MinusSlaveModeCount()
{
pthread_mutex_lock( &m_mxModeSlave );
--m_nCurrentSlaveCount;
pthread_mutex_unlock( &m_mxModeSlave );
}
// Sync Mode Job 을 1 증가 처리
void CProcessStatus::PlusSyncModeCount()
{
pthread_mutex_lock( &m_mxModeSync );
++m_nCurrentSyncCount;
pthread_mutex_unlock( &m_mxModeSync );
}
// Sync Mode Job 을 1 감소 처리
// - 정확히 동작하는지 확인을 위해 일부로 (-) 값 보정 처리 안함.
void CProcessStatus::MinusSyncModeCount()
{
pthread_mutex_lock( &m_mxModeSync );
--m_nCurrentSyncCount;
pthread_mutex_unlock( &m_mxModeSync );
}
+96
View File
@@ -0,0 +1,96 @@
/***************************************************************************
Process 상태 정보 저장 및 console 출력 처리 class
-----------------------------------------
begin : 2014/09/12
copyright : (C) 2005 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.4
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.
***************************************************************************/
#ifndef __PROCESS_STATUS_H__
#define __PROCESS_STATUS_H__
#include <pthread.h>
// CProcessStatus class
// - 프로세스 의 Client 수, 각 Mode 별 수행 수 정보를 저장
// - 해당 정보를 console 상에 표시하는 역활을 수행한다.
class CProcessStatus
{
public :
// 생성자
CProcessStatus();
// 소멸자
~CProcessStatus();
// thread를 생성하여 프로세스 출력 상태 갱신 작업을 수행.
bool Start();
// Client 수를 1 증가 처리
void PlusClientCount(void);
// Client 수를 1 감소 처리
void MinusClientCount(void);
// Master Mode Job 을 1 증가 처리
void PlusMasterModeCount( void );
// Master Mode Job 을 1 감소 처리
void MinusMasterModeCount( void );
// Slave Mode Job 을 1 증가 처리
void PlusSlaveModeCount( void );
// Slave Mode Job 을 1 감소 처리
void MinusSlaveModeCount( void );
// Sync Mode Job 을 1 증가 처리
void PlusSyncModeCount( void );
// Sync Mode Job 을 1 감소 처리
void MinusSyncModeCount( void );
private :
// 스레드 함수
static void* threadFunc( void* arg );
// 현재 프로세스 표시 정보 업데이트
void Execute( void );
private:
// 쓰레드 핸들
pthread_t m_threadHandle;
// Lock 처리 관련
pthread_mutex_t m_mxClientThread;
pthread_mutex_t m_mxModeMaster;
pthread_mutex_t m_mxModeSlave;
pthread_mutex_t m_mxModeSync;
////// 프로세스 title 로 모니터 대상 정보 /////////////
// 접속 Client 수
int m_nCurrentClientCount;
// Master Mode job 수
int m_nCurrentMasterCount;
// Slave Mode job 수
int m_nCurrentSlaveCount;
// Sync Mode job 수
int m_nCurrentSyncCount;
};
#endif //__PROCESS_STATUS_H__
+320
View File
@@ -0,0 +1,320 @@
#include "RcSyncdClientSocket.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
// 생성자.
CRcSyncdClientSocket::CRcSyncdClientSocket()
: CBaseSocket( SOCKET_NOT_VALID )
, m_nPacketHeaderLen ( sizeof(m_packetHeader))
, m_nPacketDataLen( 0 )
{
}
// 소멸자
CRcSyncdClientSocket::~CRcSyncdClientSocket()
{
// 소멸자 Socket 명시적 Close 처리.
Close();
}
// 전달받은 Target 으로 Socket 접속을 수행
// @param szTarget [in] 접속 대상 Host name 또는 IP
// @param nPort [in] 접속 Port
// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
bool CRcSyncdClientSocket::ConnectTarget( const std::string& szTarget, int nPort )
{
return Connect( szTarget, nPort );
}
// rc_syncd 로 sync 명령 전달.
// - CReqMasterData class 멤버변수 수정시 본 함수 수정 필요함.
bool CRcSyncdClientSocket::SendSyncRequest( CReqMasterData& request )
{
if( IsValidSocket() == false )
return false;
// 정보 요청을 위한 Packet Header 생성.
struct RcSyncdPacketHeader stHeader;
memset( &stHeader, 0x00, sizeof( struct RcSyncdPacketHeader ) );
stHeader.stx = HEADER_STX_CODE;
stHeader.type = HEADER_TYPE_REQUEST;
stHeader.command[0] = CLIENT_COMMAND_SYNC;
// Data 부분의 길이를 계산한다.
unsigned int nTemp = 0;
nTemp = 4; // sync_type
nTemp += ( 4 + request.start_time.size() ); // start_time
nTemp += ( 4 + request.end_time.size() ); // end_time
nTemp += ( 4 + request.one_service.size() ); // one_service
stHeader.data_length = htonl( nTemp );
// Packet Header 정보 전송
if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
{
LOG( LERR, "request[CLIENT_COMMAND_SYNC] send fail to rc_syncd. " );
return false;
}
else
{
// Data 부분 전송
// 1. sync_type
nTemp = htonl( request.sync_type );
WriteN( &nTemp, 4 );
// 2. start_time
SendString( request.start_time );
// 3. end_time
SendString( request.end_time );
// 4. one_service
SendString( request.one_service );
}
LOG( LDBG, "request[CLIENT_COMMAND_SYNC] send OK. type[%d] time[%s]~[%s] svc[%s]"
, request.sync_type, request.start_time.c_str(), request.end_time.c_str()
, (request.one_service.empty() == true ? "ALL" : request.one_service.c_str()) );
return true;
}
// rc_syncd 로 부터 sync 요청에 대한 결과 수신.
int CRcSyncdClientSocket::GetSyncResult( int nTimeout, bool& bSuccess, std::string& strMessage )
{
if( IsValidSocket() == false )
return -1;
int nRead;
bool isAlivePacket;
do
{
isAlivePacket = false;
// Socket 으로 부터 Packet Header 부분 수신.
// 수신된 정보는 멤버변수에 저장처리.
nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout );
// nRead 0: Socket Closed
// -1: error
// -2: Timeout 이므로
if( nRead == -2 )
return 0; // Timeout 반환.
else if( nRead == 0 || nRead == -1 )
return -1; // Socket Close 또는 오류 발생시
// STX code 검사.
if( m_packetHeader.stx != HEADER_STX_CODE )
{
LOG( LERR, "Not valid stx code." );
return -1;
}
// Data Length 부분 값을 멤버 변수에 저장처리.
m_nPacketDataLen = ntohl( m_packetHeader.data_length );
// Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다.
if( m_packetHeader.type == HEADER_TYPE_REQUEST
&& m_packetHeader.command[0] == COMMON_ALIVE_CHECK
&& m_nPacketDataLen == 0 )
{
isAlivePacket = true;
}
} while( isAlivePacket == true );
// Alive Packet 이 아닌 경우 해당 Packet 에 대한 응답패킷인지 검사.
if( m_packetHeader.type != HEADER_TYPE_RESPONSE
|| m_packetHeader.command[0] != CLIENT_COMMAND_SYNC )
{
// 다른 패킷이 들어온 경우
// ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만
// 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로
// 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다.
GetPacketData( m_nPacketDataLen );
return 2;
}
// Sync 요청에 대한 응답패킷인 경우.
// 1. 성공, 실패 여부 판단.
if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS )
bSuccess = false;
else
bSuccess = true;
// 2. Data 부분에 대한 처리
// - Message 부분이 존재할 수 있으므로..
// 성공/실패 구분 없이 모두 Data 부분 수신 처리한다.
if( m_nPacketDataLen > 0 )
{
if( GetPacketData( m_nPacketDataLen, strMessage ) == false )
{
LOG( LERR, "data body receive failed." );
return -1;
}
}
return 1;
}
// Alive Check 를 위한 패킷 전송 => 해당 패킷에 대한 응답은 필요없으므로 수신시 자동 무시처리됨.
// @return 전송 성공시 true, 실패시 false 반환.
bool CRcSyncdClientSocket::SendAliveCheck()
{
if( IsValidSocket() == false )
return false;
// 정보 요청을 위한 Packet Header 생성.
struct RcSyncdPacketHeader stHeader;
memset( &stHeader, 0x00, sizeof( struct RcSyncdPacketHeader ) );
stHeader.stx = HEADER_STX_CODE;
stHeader.type = HEADER_TYPE_REQUEST;
stHeader.command[0] = COMMON_ALIVE_CHECK;
// Packet Header 정보 전송
if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
return false;
return true;
}
// Packet Header 정보를 Log 파일에 Logging 처리
void CRcSyncdClientSocket::PrintHeaderToLog()
{
_LOG( LINF, "------------------------------------" );
_LOG( LINF, "stx [%02x]", m_packetHeader.stx );
_LOG( LINF, "type [%02x]", m_packetHeader.type );
_LOG( LINF, "command [%02x][%02x][%02x][%02x]", m_packetHeader.command[0], m_packetHeader.command[1], m_packetHeader.command[2], m_packetHeader.command[3] );
_LOG( LINF, "result [%02x][%02x][%02x][%02x]", m_packetHeader.result[0], m_packetHeader.result[1], m_packetHeader.result[2], m_packetHeader.result[3] );
_LOG( LINF, "data_length [%u]", m_nPacketDataLen );
_LOG( LINF, "extend_code [%02x][%02x]", m_packetHeader.extend_code[0], m_packetHeader.extend_code[1] );
_LOG( LINF, "------------------------------------" );
}
//////////////////////////////////////////////////////////////////////////////////
// Packet 정보 중 String 정보를 전송하기 위한 함수.
bool CRcSyncdClientSocket::SendString( const std::string& strData )
{
if( IsValidSocket() == false )
return false;
// 크기값을 저장하기 위한 임시 변수.
unsigned int nSize = 0;
// String Data 전송
nSize = htonl( strData.size() );
WriteN( &nSize, 4 );
if( strData.size() > 0 )
WriteN( strData.c_str(), strData.size() );
return true;
}
// socket 에서 지정된 크기만큼의 데이터를 읽어 출력변수에 저장처리.
// @param size [in] read 할 데이터 크기
// @param value [out] 읽은 데이터를 저장할 string 변수
// @return On success return true, otherwise return false.
bool CRcSyncdClientSocket::GetPacketData( unsigned int& size, std::string& value )
{
// 1. 수신할 크기가 임시버퍼보다 큰 경우 새로운 버퍼를 생성한다.
BYTE * pBuffer = NULL;
bool bNewBufferCreated = false;
if( size > DEFAULT_SOCKET_TEMP_BUFFER_SIZE )
{
pBuffer = new BYTE[size]; // 신규 메모리 할당.
bNewBufferCreated = true;
}
else
pBuffer = m_tempBuffer;
// 2. 데이터 수신 처리.
int nRead = ReadNTimeout( pBuffer, size );
// 오류 발생시
if( nRead <= 0 )
{
// 신규로 생성된 버퍼인 경우 메모리 해제 처리.
if( bNewBufferCreated == true )
delete[] pBuffer;
return false;
}
// 3. 수신 데이터 저장 처리 - 기존 데이터 뒤에 붙여 준다=> 이게 사용하기 편함.
value.append( (char *)pBuffer, size );
// 신규로 생성된 버퍼인 경우 메모리 해제 처리.
if( bNewBufferCreated == true )
delete[] pBuffer;
return true;
}
// socket 에서 지정된 크기만큼의 데이터를 읽어 내부 임시버퍼인 m_tempBuffer 에 저장처리.
// @param size [in] read 할 데이터 크기
// @return On success return true, otherwise return false.
bool CRcSyncdClientSocket::GetPacketData( unsigned int size )
{
// 1. 수신할 크기가 임시버퍼보다 큰 경우 오류 반환.
if( size > DEFAULT_SOCKET_TEMP_BUFFER_SIZE )
return false;
// 2. 데이터 수신 처리.
int nRead = ReadNTimeout( m_tempBuffer, size );
// 오류 발생시
if( nRead <= 0 )
return false;
// 3. 정상인 경우는 ReadNTimeout 함수가 무조건 요청 크기만큼 read 처리하므로 정상수신임.
return true;
}
// 전달받은 데이터를 unsigned int 형으로 변환처리 및 Endian 변환
unsigned int CRcSyncdClientSocket::GetDataToUInt( BYTE * pValue, bool bConvertEndian )
{
BYTE tempBuffer[4];
unsigned int result;
memcpy(tempBuffer, pValue, 4);
unsigned int * pInt = (unsigned int *)tempBuffer;
// Endian 변환처리.
if( bConvertEndian == true )
{
result = ntohl( *pInt );
}
else
result = *pInt;
return result;
}
// pValue 에 저장된 데이터를 unsigned long long (64Byte) 형으로 변환처리.
unsigned long long CRcSyncdClientSocket::GetDataToUInt64( BYTE * pValue )
{
unsigned long long * pResult;
BYTE tempBuffer[8];
memcpy(tempBuffer, pValue, 8);
pResult = (unsigned long long *)tempBuffer;
return *pResult;
}
+132
View File
@@ -0,0 +1,132 @@
/***************************************************************************
client 에서 rc_syncd 와의 통신을 수행하기 위한 class
-----------------------------------------
begin : 2014/09/15
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
***************************************************************************/
#ifndef __RC_SYNCD_CLIENT_SOCKET_H__
#define __RC_SYNCD_CLIENT_SOCKET_H__
#include "BaseSocket.h"
#include "RcSyncdProtocol.h"
#include "RcSyncdRequestData.h"
#include <string>
///< BYTE 타입 정의
#ifndef _BYTE_DEFINED
#define _BYTE_DEFINED
typedef unsigned char BYTE;
#endif // _BYTE_DEFINED
#define DEFAULT_SOCKET_TEMP_BUFFER_SIZE 1024 // Socket 관련 data 송수신시 사용할 임시버퍼 크기.
// CRcSyncdClientSocket
// 내부 또는 외부 모듈에서 rc_syncd 와 통신 수행시...
// 함수 수준의 단순 Interface 지원 목적을 위한 통신 관련 처리 class
class CRcSyncdClientSocket : public CBaseSocket
{
public:
// 생성자
CRcSyncdClientSocket();
// 소멸자
~CRcSyncdClientSocket();
// 전달받은 Target 으로 Socket 접속을 수행
// @param szTarget [in] 접속 대상 Host name 또는 IP
// @param nPort [in] 접속 Port
// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
bool ConnectTarget( const std::string& szTarget, int nPort );
// rc_syncd 로 sync 명령 전달.
// - CReqMasterData class 멤버변수 수정시 본 함수 수정 필요함.
// @param request [in] 요청 정보 저장 객체
// @return true 요청 성공시
// fasle 유효하지 않은 인자값 또는 전송 실패시
bool SendSyncRequest( CReqMasterData& request );
// rc_syncd 로 부터 sync 요청에 대한 결과 수신.
// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )
// 지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
// @param bSuccess [out] 성공 여부
// @param strMessage [out] rc_syncd 로부터 처리 관련 수신된 메시지
// @return
// -1 : rc_syncd 와 통신이 끊어진 경우.
// 0 : rc_syncd 로 부터 응답을 아직 수신하지 못한 경우..
// - 함수 blocking 방지 목적
// => AliveCheck 등의 작업 후 다시 응답 대기하면 된다.
// 1 : Sync 요청에 대한 응답을 수신한 경우
// 2 : Sync 요청에 대한 응답이 아닌 경우..
// => 무시 처리하고 계속 응답을 대기하면 된다.
int GetSyncResult( int nTimeout, bool& bSuccess, std::string& strMessage );
// Alive Check 를 위한 패킷 전송 => 해당 패킷에 대한 응답은 필요없으므로 수신시 자동 무시처리됨.
// @return 전송 성공시 true, 실패시 false 반환.
bool SendAliveCheck( void );
// Packet Header 정보를 Log 파일에 Logging 처리
// - 통신 오류 발생시 로깅을 통한 디버깅 지원 목적
void PrintHeaderToLog( void );
private:
// Packet Header 변수
struct RcSyncdPacketHeader m_packetHeader;
// m_packetHeader 구조체의 크기를 저장하기 위한 상수
const int m_nPacketHeaderLen;
// Packet Header 에 저장된 Data 부분의 길이 정보값.
unsigned int m_nPacketDataLen;
// Packet Data 부분의 수신처리시 임시로 사용할 버퍼.
BYTE m_tempBuffer[DEFAULT_SOCKET_TEMP_BUFFER_SIZE];
protected:
// Packet 정보 중 String 정보를 전송하기 위한 함수.
bool SendString( const std::string& strData );
// socket 에서 지정된 크기만큼의 데이터를 읽어 출력변수에 저장처리.
// @param size [in] read 할 데이터 크기
// @param value [out] 읽은 데이터를 저장할 string 변수
// @return On success return true, otherwise return false.
bool GetPacketData( unsigned int& size, std::string& value );
// socket 에서 지정된 크기만큼의 데이터를 읽어 내부 임시버퍼인 m_tempBuffer 에 저장처리.
// @param size [in] read 할 데이터 크기
// @return On success return true, otherwise return false.
bool GetPacketData( unsigned int size );
// pValue 에 저장된 데이터를 unsigned int 형으로 변환처리 및 Endian 변환
unsigned int GetDataToUInt( BYTE * pValue, bool bConvertEndian = true );
// pValue 에 저장된 데이터를 unsigned long long (64Byte) 형으로 변환처리.
unsigned long long GetDataToUInt64( BYTE * pValue );
};
#endif /* __RC_SYNCD_CLIENT_SOCKET_H__ */
+67
View File
@@ -0,0 +1,67 @@
/***************************************************************************
rc_syncd 통신 관련 protocol header
-----------------------------------------
begin : 2014/09/04
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
***************************************************************************/
#ifndef __RC_SYNCD_PROTOCOL_H__
#define __RC_SYNCD_PROTOCOL_H__
// rc_syncd 에서 사용할 통신 Header 구조체
// - 계산상 크기는 16Byte 이지만...
// - 통신상 sizeof() : 20Byte ( 64Bit OS )
struct RcSyncdPacketHeader {
char stx; // Packet 유효성 관리 코드
char type; // Request or Response 여부 ( 0x00: Request, 0x01: Response )
char command[4]; // Command Code ( 4 Byte) : 0th client-rc_syncd, 1th rc_syncd - rc_syncd 간 사용.
char result[4]; // Result Code ( 4 Byte )
unsigned int data_length; // Packet Data 부분의 길이값 ( Network Byte Order 사용)
char extend_code[2]; // 확장 및 Padding bits ( 2 Byte )
};
// Packet Header stx 코드
#define HEADER_STX_CODE 0x03
// Packet Header type 구분코드
#define HEADER_TYPE_REQUEST 0x00
#define HEADER_TYPE_RESPONSE 0x01
// Packet command 공통.
#define COMMON_ALIVE_CHECK 0x7F
// Packet command
// client <-> rc_syncd 통신 (command 0th byte 만 사용)
#define NOT_CLIENT_COMMAND 0x00 // Client 에서 전송한 요청이 아닌 경우.
#define CLIENT_COMMAND_SYNC 0x01 // Client 에서 동기화 수행 요청
// Packet command
// rc_syncd <-> rc_syncd 간의 내부 통신 (command 1st byte 만 사용)
#define PROCESS_CONTENT_LIST 0x01 // Content 정보 목록 요청 => RCDB 에서 Content 정보 추출 후 전달
#define PROCESS_CONTENT_SYNC 0x02 // 동기화 수행 요청 (동기화 대상 목록 전달) => 동기화 수행 후 결과 전달.
// Packet Result
// => type이 Response 인 경우에만 세팅됨.( 0th Byte 만 사용시 )
#define HEADER_RESULT_SUCCESS 0x00
#define HEADER_RESULT_ERROR 0x01
// 기타 정보
#define LENGTH_FIELD_SIZE 4 // 가변데이터 형식 사용시 Length 필드의 메모리 크기 (unsigned int)
#endif /* __RC_SYNCD_PROTOCOL_H__ */
+110
View File
@@ -0,0 +1,110 @@
#include "RcSyncdRequest.h"
#include "ServiceConfig.h"
#include "Logger.h"
#include "RcSyncdClientSocket.h" // rc_syncd 와 통신을 처리할 client socket 객체 생성 및 접속
#define RC_SYNCD_HOST "127.0.0.1" // rc_syncd host
CRcSyncdRequest::CRcSyncdRequest()
{
}
CRcSyncdRequest::~CRcSyncdRequest()
{
}
void CRcSyncdRequest::SendSyncRequest(int nSyncType, const std::string& strStartTime, const std::string& strEndTime)
{
SendSyncRequest(nSyncType, strStartTime, strEndTime, "");
}
void CRcSyncdRequest::SendSyncRequest(int nSyncType, const std::string& strStartTime, const std::string& strEndTime, const std::string& strService)
{
bool bResultSuccess = false; // 처리 결과의 성공/실패 여부를 저장하기 위한 변수.
std::string strMessage; // 수신 메시지 저장 변수.
int nResult = 0;
int nTimeout = 5; // 5sec에 한번씩 Alive Check하기 위함
// 요청 데이타 생성
CReqMasterData request;
request.sync_type = (SYNC_TYPE::SYNC_TYPE)nSyncType;
request.start_time = strStartTime;
request.end_time = strEndTime;
request.one_service = strService;
// local rc_syncd와 통신을 처리할 client socket 객체 생성 및 접속
CRcSyncdClientSocket client;
if( client.ConnectTarget( RC_SYNCD_HOST, CServiceConfig::GetInstance()->GetOperationPort() ) == false )
{
// local rc_syncd 으로 접속 실패시
_LOG( LERR, "rc_syncd connect fail. [%s][%d]", RC_SYNCD_HOST, CServiceConfig::GetInstance()->GetOperationPort());
return ;
}
// rc_syncd로 sync 요청 전달
if( client.SendSyncRequest( request ) == false )
{
// 전송 실패시
_LOG( LERR, "Sync request send fail");
return ;
}
// 루프를 돌면서 요청에 대한 응답을 대기
while(1)
{
// nTimeout 에 지정된 시간동안 응답대기
nResult = client.GetSyncResult( nTimeout, bResultSuccess, strMessage);
if( nResult == -1 )
{
// Socket 통신 관련 오류 또는 접속 종료가 발생한 경우.
// 해당 내역 로깅 및 루프 종료
_LOG( LERR, "Response wait fail by socket" );
break;
}
else if( nResult == 0 )
{
// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우 => Alive Check 패킷 한번 쏘고 다시 Loop 로
if( client.SendAliveCheck() == false )
{
// Alive 전송 실패시 => Socket 종료 및 오류가 발생한 경우임.
_LOG( LERR, "Response wait fail by socket2");
break;
}
LOG( LDBG, "Response wait");
// 정상적인 경우 다시 응답대기.
continue;
}
else if( nResult == 1 )
{
// 요청에 대한 처리결과 정보가 수신된 경우.
// 해당 정보 로깅처리.
if( bResultSuccess == true )
{
// 요청에 대한 처리가 정상적으로 처리된 경우
_LOG( LINF, "Sync result SUCCESS [%s]", strMessage.c_str() );
}
else
{
// 오류 발생시
_LOG( LINF, "Sync result ERROR [%s]", strMessage.c_str() );
}
break; // 응답을 받았으니 응답 대기 루프 종료
}
else
{
// Replication 요청에 대한 응답패킷이 아닌 경우.
// 해당 패킷은 무시하고 다시 Loop 로
continue;
}
}
sleep(1);
client.Close();
}
+32
View File
@@ -0,0 +1,32 @@
/****************************************************************************
Synchronization Class Header
-----------------------------------------
begin : 2014/09/17
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __RCSYNCDREQUEST_H__
#define __RCSYNCDREQUEST_H__
#include "RcSyncdRequestData.h"
class CRcSyncdRequest
{
public:
CRcSyncdRequest();
~CRcSyncdRequest();
void SendSyncRequest(int nSyncType, const std::string& strStartTime, const std::string& strEndTime);
void SendSyncRequest(int nSyncType, const std::string& strStartTime, const std::string& strEndTime, const std::string& strService);
private:
};
#endif /* __RCSYNCDREQUEST_H__ */
+93
View File
@@ -0,0 +1,93 @@
/***************************************************************************
rc_syncd Request Data Header ( RcSyncdRequestData.h )
- used : rc_cmove, rc_syncd(Scheduler)
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
***************************************************************************/
#ifndef __RC_SYNCD_REQUEST_DATA_H__
#define __RC_SYNCD_REQUEST_DATA_H__
#include <list>
#include <string>
using namespace std;
namespace SYNC_TYPE
{
enum SYNC_TYPE
{
NORMAL = 0, // 동기화 요청(O), 모든 데이터 (복제, 캐쉬 제외)
NORMA_N_ONLY = 1, // 동기화 요청(O), deleted_yn = N
CHK_ONLY = 3, // 동기화 요청(X), 모든 데이터 (복제, 캐쉬 제외)
CHK_ONLY_N_ONLY = 4 // 동기화 요청(X), deleted_yn = N
};
}
// Requeset Data
/// base Data
class CReqServiceData
{
public:
CReqServiceData(){};
~CReqServiceData(){};
public:
SYNC_TYPE::SYNC_TYPE sync_type;
string start_time;
string end_time;
};
// admin(scheduler) => rc_syncd(Master)
class CReqMasterData : public CReqServiceData
{
public:
CReqMasterData(){};
~CReqMasterData(){};
public:
// one_service 값이 없을 수도 있음
// Scheduler : 해당 정보를 보내지 않음(RCDB 작업 회피를 위함)
// 운영자(rc_cmove) : 특정 고객만 수행 시킬 수 있으므로 해당 값은 유동적임
string one_service;
};
// rc_syncd(Master) => rc_syncd(slave)
class CReqSlaveData : public CReqServiceData
{
public:
CReqSlaveData(){};
~CReqSlaveData(){};
public:
string master;
string slave;
};
// rc_syncd(Master) => rc_syncd(slave)
class CReqSyncData : public CReqServiceData
{
public:
CReqSyncData(){};
~CReqSyncData(){};
public:
string master;
string slave;
};
#endif /* __RC_SYNCD_REQUEST_DATA_H__ */
+38
View File
@@ -0,0 +1,38 @@
#include "RcdbInfo.h"
#include "ServiceConfig.h"
// 생성자..
CRcdbInfo::CRcdbInfo()
{
}
// 소멸자..
CRcdbInfo::~CRcdbInfo()
{
}
// CServiceConfig Class 에 저장된 RCDB 접속 정보를 가져와 멤버 변수에 저장한다.
void CRcdbInfo::Load()
{
// CServiceConfig Class 에서 기본적인 설정값에 대해 검사를 수행하므로...
// 본 Class 에서 다시 유효성 검사를 할 필요는 없다.. ( 어차피 유효성 검사 방법이 동일하므로.. )
// RCDB IP
m_strRcdbIp = CServiceConfig::GetInstance()->GetRcdbIp();
// RCDB Port
m_nRcdbPort = CServiceConfig::GetInstance()->GetRcdbPort();
// RCDB DB Name
m_strRcdbName = CServiceConfig::GetInstance()->GetRcdbName();
// RCDB 접근 계정
m_strRcdbAcct = CServiceConfig::GetInstance()->GetRcdbAcct();
// RCDB 접근 Password.
m_strRcdbAcctPw = CServiceConfig::GetInstance()->GetRcdbAcctPw();
return;
}
+52
View File
@@ -0,0 +1,52 @@
/****************************************************************************
RCDB 접속 정보 저장 처리 Class
-----------------------------------------
begin : 2013/11/13
copyright : (C) 2005 Solbox Inc.
author : Dev 1 Team
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.
*****************************************************************************/
#ifndef __RCDB_INFO_H__
#define __RCDB_INFO_H__
#include <unistd.h>
#include <string>
/// @brief CRcdbInfo
/// 본 클래스는 conf 파일에 저장된 RCDB 접속 관련 정보를 저장하기 위한 공용 Class...
/// 본 클래스를 사용하지 않고... CServiceConfig class 의 Get 함수를 이용하여 RCDB 접속 정보를 처리해도 된다..
/// 하지만... RCDB 접속 처리 Class 함수의 string type 변수 사용으로 인해... 여러 모듈에서 반복적인 동일 코드 처리 작업을 수행해야 하므로...
/// 본 Class 를 통해 코드를 단순화 시킬 목적으로 사용한다.
class CRcdbInfo
{
public:
// 생성 및 소멸자.
CRcdbInfo();
~CRcdbInfo();
// CServiceConfig class 에 저장된 RCDB 접속 정보를 멤버 변수에 저장처리한다.
void Load( void );
public:
// 멤버 변수들은 DB 접속을 처리하는 객체에서 직접 Access 해서 사용할 수 있도록 public 으로 구성. ( 편의성 제공 목적)
std::string m_strRcdbIp; // RCDB IP
unsigned int m_nRcdbPort; // RCDB Port
std::string m_strRcdbName; // RCDB DB Name
std::string m_strRcdbAcct; // RCDB 접근 계정
std::string m_strRcdbAcctPw; // RCDB 접근 Password.
};
#endif /* __RCDB_INFO_H__ */
+351
View File
@@ -0,0 +1,351 @@
/***************************************************************************
Running
-----------------------------------------
begin : 2011/11/01
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include "Running.h"
#include "Scheduler.h"
#include "ServiceConfig.h"
#include "Logger.h"
#include "Util.h"
#include "RcSyncdClientSocket.h" // rc_syncd 와 통신을 처리할 client socket 객체 생성 및 접속
#include "RcSyncdRequest.h"
// runlist
CRunlist *CRunlist::m_inst = NULL;
CRunlist::CRunlist()
{
pthread_mutex_init(&m_findmutex, NULL);
}
CRunlist::~CRunlist()
{
pthread_mutex_destroy(&m_findmutex);
}
void CRunlist::init()
{
if (CRunlist::m_inst == NULL)
{
CRunlist::m_inst = new CRunlist();
}
}
CRunlist* CRunlist::getInstance()
{
return CRunlist::m_inst;
}
void CRunlist::release()
{
if (CRunlist::m_inst != NULL)
{
while(m_inst->m_list.size() > 0 )
{
sleep(1);
}
delete CRunlist::m_inst;
CRunlist::m_inst = NULL;
}
}
bool CRunlist::addlist(CRunning *run)
{
bool r = true;
pthread_mutex_lock(&m_findmutex);
stringstream msg;
time_t newstart = 0;
time_t newend = 0;
if( findnewtime(run->getstart(), run->getend(), newstart, newend) == false)
{
msg << "[running:" << pthread_self() << "] input values used.";
LOG(LDBG, msg.str().c_str());
newstart = run->getstart();
newend = run->getend();
}
else
{
msg << "[running:" << pthread_self() << "] changed values used.";
LOG(LDBG, msg.str().c_str());
}
msg.str("");
if( newstart == newend)
{
r = false;
msg << "[running:" << pthread_self() << "]running start and end are the same.";
cout << msg.str() << endl;
LOG(LWAR, msg.str().c_str());
}
else if(newstart > newend)
{
r = false;
msg << "[running:" << pthread_self() << "]running error[Start time is greater than the end of time]." << endl;
msg << " Start Time : " << time_t2string(run->getstart()) << " => " << time_t2string(run->getstart()) << endl;
msg << " End Time : " << time_t2string(run->getend()) << " => " << time_t2string(newend) << endl;
cerr << msg.str() << endl;
LOG(LERR, msg.str().c_str());
}
else
{
run->setstart(newstart);
run->setend(newend);
}
if(r == true)
m_list.push_back(run);
pthread_mutex_unlock(&m_findmutex);
return r;
}
void CRunlist::remove(CRunning *run)
{
pthread_mutex_lock(&m_findmutex);
list<CRunning *>::iterator itor;
itor = find(m_list.begin(), m_list.end(), run);
if (itor != m_list.end())
{
m_list.erase(itor);
}
pthread_mutex_unlock(&m_findmutex);
}
bool CRunlist::findnewtime(time_t s, time_t e, time_t &ns, time_t &ne)
{
stringstream msg;
bool r = false;
ns = s;
ne = e;
msg << "[running:"<< pthread_self() << "] Run list count : " << m_list.size() <<
",input time : " << time_t2string(ns) << "," << time_t2string(ne);
#ifdef _DEBUG
cout << msg.str() << endl;
#endif //_DEBUG
LOG(LDBG, msg.str().c_str());
list<CRunning *>::iterator it;
for ( it=m_list.begin() ; it != m_list.end(); it++ )
{
CRunning *pdata = static_cast<CRunning *>(*it);
msg.str("");
msg << "[running:"<< pthread_self() << "] get running :" <<
time_t2string(pdata->getstart()) << "," << time_t2string(pdata->getend());
#ifdef _DEBUG
cout << msg.str() << endl;
#endif //_DEBUG
LOG(LDBG, msg.str().c_str());
if( ne > pdata->getend() )
{
if(ns >= pdata->getend() )
{
LOG(LDBG, "ne > pdata->getend(), ns >= pdata->getend()");
continue;
}
else
{
r = true;
if( ns <= pdata->getstart() )
{
LOG(LDBG, "ne > pdata->getend(), ns < pdata->getend(), ns <= pdata->getstart() : ne = pdata->getstart()");
ne = pdata->getstart();
}
else
{
LOG(LDBG, "ne > pdata->getend(), ns < pdata->getend(), ns > pdata->getstart() : new ne, ns");
ne = pdata->getstart();
ns = ne - (e - s);
}
}
}
else if( ne == pdata->getend() )
{
r = true;
LOG(LDBG, "ne == pdata->getend(), ne = pdata->getstart();");
ne = pdata->getstart();
}
else // ne < pdata->getend()
{
if(ne <= pdata->getstart() )
{
LOG(LDBG, "ne < pdata->getend(), ne <= pdata->getstart()");
continue;
}
else // ne > pdata->getstart()
{
r = true;
if(ns <= pdata->getstart() )
{
LOG(LDBG, "ne < pdata->getend(), ne > pdata->getstart(), ns <= pdata->getstart() : ne = pdata->getstart()");
ne = pdata->getstart();
}
else
{
LOG(LDBG, "ne < pdata->getend(), ne > pdata->getstart(), ns > pdata->getstart() : new(2) ne ns");
ne = pdata->getstart();
ns = ne - (e - s);
}
}
}
}
msg.str("");
msg << "[running:"<< pthread_self() << "] Find Time." <<
time_t2string(ns) << "," << time_t2string(ne);
#ifdef _DEBUG
cout << msg.str() << endl;
#endif // _DEBUG
LOG(LDBG, msg.str().c_str());
return r;
}
time_t CRunlist::findnewtime(int findtype, time_t find)
{
time_t r = 0;
//pthread_mutex_lock(&m_findmutex);
list<CRunning *>::iterator it;
for ( it=m_list.begin() ; it != m_list.end(); it++ )
{
CRunning *pdata = static_cast<CRunning *>(*it);
switch(findtype)
{
case CRunlist::START_TIME:
r = pdata->includestart(find);
break;
case CRunlist::EDN_TIME:
r = pdata->includeend(find);
break;
}
}
//pthread_mutex_unlock(&m_findmutex);
return r;
}
// running
CRunning::CRunning(CScheduler* sch, int task, time_t r)
{
m_scheduler = sch;
m_taskindex = task;
m_runed = r;
m_starttime = 0;
m_endtime = 0;
}
CRunning::~CRunning()
{
}
void* CRunning::runfn(void* pdata)
{
CRunning* pObject = reinterpret_cast<CRunning *>(pdata);
pthread_detach( pthread_self() );
ostringstream msg;
do
{
msg << "[running:" << pthread_self() << "] Task Index : "<<
pObject->m_taskindex << " START.";
LOG(LDBG, msg.str().c_str());
msg.str("");
bool b = CRunlist::getInstance()->addlist(pObject);
if(b == false)
{
msg << "[running:" << pthread_self() <<"]Inserted into the run list failed.";
cerr << msg.str() << endl;
LOG(LERR, msg.str().c_str());
break;
}
pObject->m_scheduler->updatelastendtime(pObject->m_taskindex,
pObject->m_endtime);
string cmd;
cmd = cmd + " " + time_t2string(pObject->m_starttime) + " " +
time_t2string(pObject->m_endtime);
msg << "call sync : " << cmd;
// sync 요청 처리
// scheduler -> rc_syncd (local host)
// 성공 실패 여부는 objRcSyncdRequest 객체 내부에서 로깅 처리 함.
CRcSyncdRequest objRcSyncdRequest;
objRcSyncdRequest.SendSyncRequest( SYNC_TYPE::NORMAL, time_t2string(pObject->m_starttime), time_t2string(pObject->m_endtime));
msg << "...Job End" ;
LOG(LINF, msg.str().c_str());
CRunlist::getInstance()->remove(pObject);
}while( false );
#ifdef _DEBUG
cout << " RUNNING END index - "<< pObject->m_taskindex << ",now (" <<
time_t2string(pObject->m_runed) << ")"<< endl;
#endif //_DEBUG
msg.str("");
msg << "[running:" << pthread_self() << "] END.";
LOG(LDBG, msg.str().c_str());
msg.str("");
delete pObject;
return NULL;
}
time_t CRunning::includestart(time_t s)
{
time_t t = 0;
return t;
}
time_t CRunning::includeend(time_t e)
{
time_t t = 0;
return t;
}
bool CRunning::run(time_t s, time_t e)
{
m_starttime = s;
m_endtime = e;
pthread_t thread;
int nRet = pthread_create(&thread, 0, CRunning::runfn, this);
if( nRet )
{
ostringstream msg;
msg << "Running Thread create failed.[" << errno <<"]";
cerr << msg.str() << endl;
LOG(LERR, msg.str().c_str());
return false;
}
return true;
}
+93
View File
@@ -0,0 +1,93 @@
/***************************************************************************
Running
-----------------------------------------
begin : 2011/11/01
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __RUNNING_H__
#define __RUNNING_H__
#include <sys/time.h>
#include <errno.h>
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <list>
#include <algorithm>
using namespace std;
class CScheduler;
class CRunning;
class CRunlist
{
public:
enum RUNLIST_FIND
{
START_TIME = 1,
EDN_TIME = 2
};
public:
static CRunlist* getInstance();
static void release();
static void init();
bool addlist(CRunning *run);
void remove(CRunning *run);
private:
CRunlist();
~CRunlist();
time_t findnewtime(int findtype, time_t find);
bool findnewtime(time_t s, time_t e, time_t &ns, time_t &ne);
private:
static CRunlist * m_inst;
list<CRunning*> m_list;
pthread_mutex_t m_findmutex;
};
class CRunning
{
public:
CRunning(CScheduler* scheduler, int task, time_t r);
~CRunning();
bool run(time_t s, time_t e);
time_t includestart(time_t s);
time_t includeend(time_t e);
void setstart(time_t t) { m_starttime = t; }
void setend(time_t t) { m_endtime = t; }
time_t getstart() { return m_starttime; }
time_t getend() { return m_endtime; }
private:
static void* runfn( void* pdata );
private:
time_t m_starttime;
time_t m_endtime;
time_t m_runed;
int m_taskindex;
CScheduler* m_scheduler;
};
#endif // __RUNNING_H__
+352
View File
@@ -0,0 +1,352 @@
/***************************************************************************
Scheduler
-----------------------------------------
begin : 2011/11/01
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include "Scheduler.h"
#include "ServiceConfig.h"
#include "Logger.h"
#include "Util.h"
#include "Running.h"
#define SHC_SLEEP_TIME (1000*1000)// microseconds
#define DAY_SECOND (60*60*24) //
CScheduler::CScheduler()
: m_exit(false)
{
pthread_mutex_init(&m_timermutex, NULL);
pthread_cond_init(&m_timercond, NULL);
pthread_mutex_init(&m_workmutex, NULL);
pthread_cond_init(&m_workcond, NULL);
pthread_mutex_init(&m_updatemutex, NULL);
}
CScheduler::~CScheduler()
{
setexit(true);
pthread_mutex_destroy(&m_timermutex);
pthread_cond_destroy(&m_timercond);
pthread_mutex_destroy(&m_workmutex);
pthread_cond_destroy(&m_workcond);
pthread_mutex_destroy(&m_updatemutex);
}
void* CScheduler::WorkFn(void* pdata)
{
CScheduler* pObject = reinterpret_cast<CScheduler *>(pdata);
ostringstream msg;
pthread_detach( pthread_self() );
while( pObject->isexit() == false )
{
msg.str("");
//pObject->whetherdetach();
pthread_mutex_lock(&pObject->m_workmutex);
int err = pthread_cond_wait(&pObject->m_workcond, &pObject->m_workmutex);
if ( err == 0 && pObject->isexit() == false)
{
pObject->whetherdetach();
}
else
{
if(pObject->isexit())
{
msg << "Timer Work Thread exit.";
cerr << msg.str() << endl;
LOG(LERR, msg.str().c_str());
}
}
pthread_mutex_unlock(&pObject->m_workmutex);
}
return NULL;
}
void* CScheduler::TimerFn(void* pdata)
{
CScheduler* pObject = reinterpret_cast<CScheduler *>(pdata);
struct timeval nowtime;
struct timespec timeout;
ostringstream msg;
#ifdef _DEBUG
time_t t = time(NULL);
#endif // _DEBUG
while( pObject->m_exit == false )
{
msg.str("");
#ifdef _DEBUG
cout << "scheduler::TimerFn..." << strtime_now() <<"("<< time(NULL)-t <<")" << endl;
t = time(NULL);
#endif // _DEBUG
int rc = pthread_mutex_lock(&pObject->m_timermutex);
if(rc)
{
msg << "Timer pthread_mutex_lock error.[" << errno << "]. Timer exit.";
cerr << msg.str() << endl;
LOG(LCRT, msg.str().c_str());
pthread_exit(NULL);
}
gettimeofday(&nowtime, NULL);
timeout.tv_sec = nowtime.tv_sec + (SHC_SLEEP_TIME/1000000);
timeout.tv_nsec = nowtime.tv_usec*1000 + (SHC_SLEEP_TIME%1000000*1000);
rc = pthread_cond_timedwait(&pObject->m_timercond, &pObject->m_timermutex, &timeout);
switch(rc)
{
case ETIMEDOUT:
msg << "Timer wake up. Thread signal workfn.";
#ifdef _DEBUG
cout << " " << msg.str() <<endl;
#endif // _DEBUG
LOG(LDEV2, msg.str().c_str());
pthread_mutex_lock(&pObject->m_workmutex);
pthread_cond_signal(&pObject->m_workcond);
pthread_mutex_unlock(&pObject->m_workmutex);
break;
case 0:
msg << "Timer Kill signaled.[m_exit :" << pObject->m_exit << "]";
cerr << msg.str() << endl;
LOG(LCRT, msg.str().c_str());
break;
default:
msg << "Timer some error occurred.";
cerr << msg.str() << endl;
LOG(LALT, msg.str().c_str());
break;
}
pthread_mutex_unlock(&pObject->m_timermutex);
}
return 0;
}
int CScheduler::run()
{
ostringstream msg;
int nRet = pthread_create(&m_htimerthread, 0, CScheduler::TimerFn, this);
if( nRet )
{
msg << "Timer Thread create failed.[ " << errno <<"]";
cerr << msg << endl;
LOG(LCRT, msg.str().c_str());
return -1;
}
nRet = pthread_create(&m_hworkthread, 0, CScheduler::WorkFn, this);
if( nRet )
{
msg << "Timer Work Thread create failed.[ " << errno <<"]";
cerr << msg << endl;
LOG(LCRT, msg.str().c_str());
return -1;
}
return 0;
}
bool CScheduler::addtask(short tasktype, int val)
{
task_info info;
info.tasktype = tasktype;
info.timeval.interval = val;
int i = m_taskmap.size();
m_taskmap.insert(make_pair(i,info));
return true;
}
void CScheduler::printtasklist()
{
map<int, task_info>::iterator iter;
for( iter = m_taskmap.begin(); !m_taskmap.empty()&& iter != m_taskmap.end(); iter++ )
{
cout << "Task List index : "<< iter->first << ", Taks info : " << iter->second.tasktype << "," <<
iter->second.timeval.interval << "," << iter->second.lastendtime << "," << iter->second.lastruntime << endl;
}
}
bool CScheduler::whetherdetach()
{
ostringstream msg;
time_t nowtime;
time ( &nowtime );
map<int, task_info>::iterator iter;
for( iter = m_taskmap.begin(); !m_taskmap.empty()&& iter != m_taskmap.end(); iter++ )
{
msg.str("");
task_info &task = iter->second;
task.spendtime += SHC_SLEEP_TIME;
if( task.lastruntime - nowtime > 0 )
{
msg << "Task last runtime correction. " <<
time_t2string(task.lastruntime) << " => " << time_t2string(nowtime);
#ifdef _DEBUG
cout << msg.str() << endl;
#endif // _DEBUG
LOG(LNOT, msg.str().c_str());
task.lastruntime = nowtime;
}
#ifdef _DEBUG
cout << " now time - " << time_t2string(nowtime) << ", task index - " << iter->first <<
", spend time - " << task.spendtime << ",(" << nowtime- task.lastruntime << ")"
", last run time - " << time_t2string(task.lastruntime) << ", time value - " <<
(task.tasktype == CScheduler::TIME_INTERVAL ? "interval" : "once") << "," <<
(task.tasktype == CScheduler::TIME_INTERVAL ? task.timeval.interval : task.timeval.hour) << endl;
#endif // _DEBUG
bool detach = false;
switch( task.tasktype )
{
case CScheduler::TIME_INTERVAL:
{
int spendtime = nowtime - task.lastruntime;
//int64_t spendtime = task.spendtime/1000000;
if( (task.lastruntime == 0) || ( spendtime >= task.timeval.interval ) )
{
// 첫번째 등록된 작업이 먼저 수행될 수 있도록 sleep 수행함
solusleep(1000);
task.spendtime = 0;
//task.spendtime = SHC_SLEEP_TIME;
task.lastruntime = nowtime;
detach = true;
}
}
break;
case CScheduler::TIME_ONCE:
{
string nowstr = time_t2string(nowtime);
int hours = atoi(nowstr.substr(8,2).c_str());
string nowhour = nowstr.substr(0,8);
string lastrunhour = time_t2string(task.lastruntime).substr(0,8);
if( hours == task.timeval.hour && nowhour > lastrunhour)
{
task.spendtime = 0;
task.lastruntime = nowtime;
detach = true;
}
}
break;
default:
msg.str("");
msg << "Unknown Task info.";
cerr << msg.str() << endl;
LOG(LWAR, msg.str().c_str());
break;
}
if( detach == true )
{
msg.str("");
msg << "Task index : " << iter->first << ", now time - "<< nowtime <<
"," << iter->second.lastruntime <<", Running detach....";
#ifdef _DEBUG
cout << msg.str() << endl;
#endif // _DEBUG
LOG(LDBG, msg.str().c_str());
time_t start, end;
CRunning * prun = new CRunning(this, iter->first, task.lastruntime);
pthread_mutex_lock(&m_updatemutex);
end = nowtime - CServiceConfig::GetInstance()->GetSafetyFactor();
if(task.lastendtime == 0)
{
if (task.tasktype == CScheduler::TIME_ONCE)
{
start = end - (CServiceConfig::GetInstance()->GetOnceSize() * DAY_SECOND);
}
else
{
start = end - task.timeval.interval;
}
}
else
{
start = task.lastendtime;
if (task.tasktype == CScheduler::TIME_ONCE)
{
time_t newstart = end - (CServiceConfig::GetInstance()->GetOnceSize() * DAY_SECOND);
if( start < newstart )
start = newstart;
}
}
if( prun->run(start, end) == false)
{
}
pthread_mutex_unlock(&m_updatemutex);
}
}
return true;
}
void CScheduler::setexit(bool v)
{
LOG(LINF, "setexit %d", v);
m_exit = v;
// kill timer
pthread_mutex_lock(&m_timermutex);
pthread_cond_signal(&m_timercond);
pthread_mutex_unlock(&m_timermutex);
// kill work
pthread_mutex_lock(&m_workmutex);
pthread_cond_signal(&m_workcond);
pthread_mutex_unlock(&m_workmutex);
}
bool CScheduler::updatelastendtime(int taskindex, time_t newtime)
{
pthread_mutex_lock(&m_updatemutex);
map<int, task_info>::iterator iter = m_taskmap.find(taskindex);
if( iter != m_taskmap.end() )
{
LOG(LDBG, "Taks index %d,%s => %s", taskindex,
time_t2string(iter->second.lastendtime).c_str(),
time_t2string(newtime).c_str());
if( iter->second.lastendtime < newtime )
{
iter->second.lastendtime = newtime;
}
else
{
#ifdef _DEBUG
cout << "The current setting(Task index - ("<< taskindex <<
"), last endtime) is less than new endtime." << endl;
#endif // _DEBUG
}
}
pthread_mutex_unlock(&m_updatemutex);
return true;
}
+91
View File
@@ -0,0 +1,91 @@
/***************************************************************************
Scheduler
-----------------------------------------
begin : 2011/11/01
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __SCHEDULER_H__
#define __SCHEDULER_H__
#include <sys/time.h>
#include <errno.h>
#include <iostream>
#include <sstream>
#include <vector>
#include <map>
#include <list>
#include <algorithm>
using namespace std;
struct task_info
{
short tasktype;
int64_t spendtime;
union
{
int interval;
int hour;
}timeval;
time_t lastendtime;
time_t lastruntime;
task_info()
: tasktype(-1), spendtime(0), lastendtime(0), lastruntime(0) {}
} ;
typedef struct task_info task_info;
class CScheduler
{
public:
enum TASK_TYPE
{
TIME_UNKNOWN = -1,
TIME_ONCE = 0,
TIME_INTERVAL = 1
};
public:
CScheduler();
~CScheduler();
int run();
bool addtask(short tasktype, int val);
pthread_t gettimerhandle() {return m_htimerthread;}
bool whetherdetach();
bool updatelastendtime(int taskindex, time_t newtime);
void printtasklist();
void setexit(bool v);
bool isexit() { return m_exit; }
private:
static void* TimerFn(void* pdata);
static void* WorkFn(void* pdata);
private:
bool m_exit;
map<int, task_info> m_taskmap;
pthread_t m_htimerthread;
pthread_cond_t m_timercond;
pthread_mutex_t m_timermutex;
pthread_t m_hworkthread;
pthread_cond_t m_workcond;
pthread_mutex_t m_workmutex;
pthread_mutex_t m_updatemutex;
};
#endif // __SCHEDULER_H__
+448
View File
@@ -0,0 +1,448 @@
#include "ServiceConfig.h"
#include <stdlib.h>
#include "Logger.h"
// static 변수 초기화
CServiceConfig * CServiceConfig::m_pInstance = NULL;
// 생성자
CServiceConfig::CServiceConfig()
{
// default 값 설정.
// 지정된 conf 파일에 해당 설정이 없어도 동작되도록 처리할 값에 대해서만 default 값을 정의한다.
m_nChkSmall = 60;
m_nChkMiddle = 3600;
m_strDiffCommand = "/user/service/etc/diff_com.sh";
m_strRcCMove = "/user/service/bin/rc_cmove";
m_vecFHSMount.push_back("/stg/node0");
m_vecFHSMount.push_back("/stg/node1");
m_vecFHSMount.push_back("/stg/node2");
}
// 소멸자
CServiceConfig::~CServiceConfig()
{
// 객체 소멸시...
// 만약 static GetInstance() 함수가 가르키는 객체가 자기 자신이라면...
// 소멸 처리에 의해 문제가 발생할 수 있으므로... 이에 대한 처리를 해 준다.
if( CServiceConfig::GetInstance() == this )
{
CServiceConfig::m_pInstance = NULL;
}
}
bool CServiceConfig::Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
{
// 1. 임시 처리용 Instance 를 생성한다.
CServiceConfig * pTempInstance = new CServiceConfig();
// 2. 전달 받은 Config 정보를 임시 Instance 로 로딩한다.
// - 만약 로딩이 실패할 경우... 임시 Instance 객체를 삭제 처리한다.
if( pTempInstance->Load( strProgramName, strConfFileName, strErrorMessage ) == false )
{
delete pTempInstance;
pTempInstance = NULL;
return false; // 생성 실패 사유는 Error String 관련 함수를 사용하여 확인.
}
// 3. 전달받은 Conf 정보 로딩에 성공한 경우...
// - 기존 Instance 객체가 존재하는 경우.. 교체 처리..
// - 기존 Instance 객체가 없는 경우는 신규 Instance 사용토록 처리.
if( CServiceConfig::GetInstance() == NULL )
{
CServiceConfig::m_pInstance = pTempInstance;
}
else
{
// Instance replace...
CServiceConfig * pPreviosInstance = CServiceConfig::m_pInstance;
CServiceConfig::m_pInstance = pTempInstance;
delete pPreviosInstance;
}
return true;
}
CServiceConfig * CServiceConfig::GetInstance( )
{
return CServiceConfig::m_pInstance;
}
// 각 항목별 제약값은 기존 service_sync 모듈 소스를 참고하여 정의함.
bool CServiceConfig::Load( const std::string & strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
{
// 변수 유효성 확인
if( strProgramName.size() <= 0 )
{
strErrorMessage = "Config Program Name[" + strProgramName + "] not valid.";
return false;
}
// Config class (lib 하위) 를 이용하여 config 파일 상의 정보를 로딩 처리
Config conf;
if( conf.Open( strConfFileName ) == false )
{
strErrorMessage = "Config file[" + strConfFileName + "] open failed.";
return false;
}
// Config 모듈에 로딩된 정보 중 필요한 정보만 가져온다...
// - 만약 오류가 발생할 경우... 해당 내역은 Error String 에 저장 처리...
std::string strValue;
int nValue;
std::vector< std::string > vecValue;
// Log path
if( GetConfigValue( conf, strProgramName, "DEFAULT_LOG_DIR", strValue, strErrorMessage ) == false )
return false;
else
{
m_strLogPath = strValue;
strValue.clear();
}
// Log Level
if( GetConfigValue( conf, strProgramName, "LOG_LEVEL", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 || nValue > MAX_LOG_LEVEL )
{
strErrorMessage = "Config [LOG_LEVEL] value[" + strValue + "] is not valid";
return false;
}
else
m_nLogLevel = nValue;
strValue.clear();
}
// RC ID
if( GetConfigValue( conf, strProgramName, "RC_ID", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcId = strValue;
strValue.clear();
}
// RCDB IP
if( GetConfigValue( conf, strProgramName, "RCDB_IP", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbIp = strValue;
strValue.clear();
}
// RCDB PORT
if( GetConfigValue( conf, strProgramName, "RCDB_PORT", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue <= 0 || nValue > 65535 )
{
strErrorMessage = "Config [RCDB_PORT] value[" + strValue + "] is not valid";
return false;
}
else
m_nRcdbPort = nValue;
strValue.clear();
}
// RCDB DB NAME
if( GetConfigValue( conf, strProgramName, "RCDB_DB_NAME", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbName = strValue;
strValue.clear();
}
// RCDB ACCOUNT
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbAcct = strValue;
strValue.clear();
}
// RCDB ACCOUNT PASSWORD
if( GetConfigValue( conf, strProgramName, "RCDB_ACCT_PW", strValue, strErrorMessage ) == false )
return false;
else
{
m_strRcdbAcctPw = strValue;
strValue.clear();
}
// Skip keyword : h|path (head) or m|path(middle) or t|path(tail)
// - 본 설정갑은 없어도 동작하도록 처리한다.
if( conf.GetConfig( strProgramName, "SKIP_KEYWORD", vecValue ) == true )
{
// 해당 값이 존재할 경우..
// Multi value 이므로 갯수 검사
if( vecValue.size() > 0 )
{
m_vecSkipWord = vecValue;
}
vecValue.clear();
}
else
{
// program section 에 존재하지 않을 경우... COMMON 세션 확인
if( conf.GetConfig( "COMMON", "SKIP_KEYWORD", vecValue ) == true )
{
// 해당 값이 존재할 경우..
// Multi value 이므로 갯수 검사
if( vecValue.size() > 0 )
{
m_vecSkipWord = vecValue;
}
vecValue.clear();
}
}
// Operator Command Port, OP_PORT = 14002
if( GetConfigValue( conf, strProgramName, "OP_PORT", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue <= 0 || nValue > 65535 )
{
strErrorMessage = "Config [OP_PORT] value[" + strValue + "] is not valid";
return false;
}
else
m_nOperationPort = nValue;
strValue.clear();
}
// Database connection pool count, DB_POOL = 4
if( GetConfigValue( conf, strProgramName, "DB_POOL", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 )
{
strErrorMessage = "Config [DB_POOL] value[" + strValue + "] is not valid";
return false;
}
else
m_nDbPoolCount = nValue;
strValue.clear();
}
// Work pool count, WORK_POOL = 20
if( GetConfigValue( conf, strProgramName, "WORK_POOL", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 )
{
strErrorMessage = "Config [WORK_POOL] value[" + strValue + "] is not valid";
return false;
}
else
m_nWorkPoolCount = nValue;
strValue.clear();
}
// When endtime to obtain coefficient that are subtracted. (sec), SAFETY_FACTOR = 180
if( GetConfigValue( conf, strProgramName, "SAFETY_FACTOR", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 )
{
strErrorMessage = "Config [SAFETY_FACTOR] value[" + strValue + "] is not valid";
return false;
}
else
m_nSafetyFactor = nValue;
strValue.clear();
}
// Get the set amount of time at a time. (Day), ONCE_SIZE = 1
// 1 이상이어야 한다.
if( GetConfigValue( conf, strProgramName, "ONCE_SIZE", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue <= 0 )
{
strErrorMessage = "Config [ONCE_SIZE] value[" + strValue + "] is not valid";
return false;
}
else
m_nOnceSize = nValue;
strValue.clear();
}
// CHK_SMALL = 60 , range : 30 ~CHK_MIDDLE (second)
// Default 값 존재, 최소 30 이상어야 한다.
if( GetConfigValue( conf, strProgramName, "CHK_SMALL", strValue, strErrorMessage ) == true )
{
nValue = atoi(strValue.c_str());
if( nValue < 30 )
{
strErrorMessage = "Config [CHK_SMALL] value[" + strValue + "] is not valid";
return false;
}
else
m_nChkSmall = nValue;
strValue.clear();
}
// CHK_MIDDLE = 3600, range : CHK_SMALL ~(second),
// Default 값 존재, CHK_SMALL 값보다 커야 한다.
if( GetConfigValue( conf, strProgramName, "CHK_MIDDLE", strValue, strErrorMessage ) == true )
{
nValue = atoi(strValue.c_str());
if( nValue < 0 )
{
strErrorMessage = "Config [CHK_MIDDLE] value[" + strValue + "] is not valid";
return false;
}
else if( nValue <= (int) m_nChkSmall )
{
strErrorMessage = "Config [CHK_MIDDLE] value[" + strValue + "] less then [CHK_SMALL][";
strErrorMessage += m_nChkSmall;
strErrorMessage += "]";
return false;
}
else
m_nChkMiddle = nValue;
strValue.clear();
}
// CHK_ONCE = 7, range : 1Days, hour( 0 - 23 )
// 0 ~ 23 구간내 값이어야 한다.
if( GetConfigValue( conf, strProgramName, "CHK_ONCE", strValue, strErrorMessage ) == false )
return false;
else
{
nValue = atoi(strValue.c_str());
if( nValue < 0 || nValue > 23)
{
strErrorMessage = "Config [CHK_ONCE] value[" + strValue + "] is not valid";
return false;
}
else
m_nChkOnce = nValue;
strValue.clear();
}
// DIFF_COMMAND=/user/service/etc/diff_com.sh
// Default 값 존재
if( GetConfigValue( conf, strProgramName, "DIFF_COMMAND", strValue, strErrorMessage ) == true )
{
m_strDiffCommand = strValue;
strValue.clear();
}
// RC_CMOVE=/user/service/bin/rc_cmove
// Default 값 존재
if (GetConfigValue(conf, strProgramName, "RC_CMOVE", strValue, strErrorMessage) == true)
{
m_strRcCMove = strValue;
strValue.clear();
}
// FHS_STORAGE_MOUNT = /stg/node0,/stg/node1,/stg/node2
// Default 값 존재
if (conf.GetConfig(strProgramName, "FHS_STORAGE_MOUNT", vecValue) == true)
{
// 해당 값이 존재할 경우..
// Multi value 이므로 갯수 검사
if (vecValue.size() > 0)
{
m_vecFHSMount = vecValue;
}
vecValue.clear();
}
else
{
// program section 에 존재하지 않을 경우... COMMON 세션 확인
if (conf.GetConfig("COMMON", "FHS_STORAGE_MOUNT", vecValue) == true)
{
// 해당 값이 존재할 경우..
// Multi value 이므로 갯수 검사
if (vecValue.size() > 0)
{
m_vecFHSMount = vecValue;
}
vecValue.clear();
}
}
// conf 정보 로딩이 모두 정상적으로 완료된 경우...
// 마지막으로 전달받은 config 명, section 정보를 저장한다.
m_strConfigFileName = strConfFileName;
m_strProgramName = strProgramName;
return true;
}
bool CServiceConfig::GetConfigValue( Config & conf, const std::string & strProgramName, const std::string strKey, std::string & strValue, std::string & strErrorMessage )
{
// conf get value
if( conf.GetConfig( strProgramName, strKey, strValue) == false )
{
if( conf.GetConfig( "COMMON", strKey, strValue) == false )
{
strErrorMessage = "Config [" + strKey + "] value is not exist";
return false;
}
}
// value check.
if( strValue.empty() == true )
{
strErrorMessage = "Config [" + strKey + "] value is not valid";
return false;
}
return true;
}
+148
View File
@@ -0,0 +1,148 @@
/****************************************************************************
rc_syncd's service config module
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __SERVICE_CONFIG_H__
#define __SERVICE_CONFIG_H__
#include <string>
#include <vector>
#include "Config.h"
// 본 모듈은 conf 파일의 정보를 read 하여... 본 프로세스 관련 설정 정보를 저장한다.
// 본 모듈은 singleton 으로 동작시켜 Main 프로세스 및 Worker 프로세스에서도 접근이 가능토록 한다.
// conf 파일에 대한 파싱 및 설정 정보 추출은 lib 하위의 Config 클래스를 이용한다.
// 본 객체는 singleton 객체이므로 상속받아 사용하지 않도록 한다. ( 상속받아 사용할 수 없도록 private 처리함 )
class CServiceConfig
{
private:
// sigleton 객체 생성을 위해 생성자를 public 으로 처리하지 않음.
CServiceConfig();
// 소멸자
virtual ~CServiceConfig();
static CServiceConfig * m_pInstance;
public:
// 객체 초기화 처리용 Function.
// 본 함수 호출시 Instance 생성 후 입력 받은 Config 파일 load.
// 따라서 본 함수를 호출하지 않을 경우.. GetInstance() 함수는 NULL 을 반환
// 본 함수를 객체가 생성된 상태에서 재 호출할 경우
// - 주어진 Conf 파일 정보가 정확하다면.. conf 정보를 다시 load 함.
// - 주어진 conf 파일 정보가 부정확하다면... 이전 conf 를 계속 사용함.
static bool Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage );
// Singleton 객체 접근 메소드
static CServiceConfig * GetInstance();
private:
// 전달받은 conf 파일에서 해당 config 정보를 로드하여 멤버 변수에 저장한다.
// 만약 반환값이 false 인 경우 해당 오류 관련 내역은 strErrorMessage 변수에 저장된다.
bool Load( const std::string & strProgramName, const std::string & strConfFileName, std::string & strErrorMessage );
// Config 객체로 부터 지정된 strKey 정보를 가져와 유효성 여부를 판단.
// 반환값이 false 인 경우 strErrorMessage 상에 오류 내역이 저장된다.
bool GetConfigValue( Config & conf, const std::string & strProgramName, const std::string strKey, std::string & strValue, std::string & strErrorMessage );
private:
// 객체 초기화시 전달받은 정보
std::string m_strConfigFileName; // 설정 파일 정보
std::string m_strProgramName; // 프로그램 명 (conf 상에서 해당 프로그램의 설정 정보를 가져올 때 사용)
// 기본 설정 정보.
std::string m_strLogPath; // 로그 저장 Path
int m_nLogLevel; // 로그 기록 Level
std::string m_strRcId; // RC ID
std::string m_strRcdbIp; // RCDB IP
unsigned int m_nRcdbPort; // RCDB Port
std::string m_strRcdbName; // RCDB DB Name
std::string m_strRcdbAcct; // RCDB 접근 계정
std::string m_strRcdbAcctPw; // RCDB 접근 Password.
std::vector<std::string> m_vecSkipWord; // 작업 대상에서 제외할 content 문자열 정보 저장.
// 설정값이 존재하지 않을 수 있음.
// Process
unsigned int m_nOperationPort; // Operation Command TCP Listen port, OP_PORT = 14002
unsigned int m_nDbPoolCount; // Database connection pool count, DB_POOL = 4
unsigned int m_nWorkPoolCount; // Work pool count, WORK_POOL = 20
unsigned int m_nSafetyFactor; // When endtime to obtain coefficient that are subtracted. (sec), SAFETY_FACTOR = 180
unsigned int m_nOnceSize; // Get the set amount of time at a time. (Day), ONCE_SIZE = 1
// Time between sync runs & target interval
unsigned int m_nChkSmall; // range : 30 ~CHK_MIDDLE( second ), CHK_SMALL = 60
// default : 60
unsigned int m_nChkMiddle; // range : CHK_SMALL ~(second), CHK_MIDDLE = 3600
// default : 3600
unsigned int m_nChkOnce; // range : 1Days, hour( 0 - 23 ), CHK_ONCE = 7
std::string m_strDiffCommand; // diff commnad, DIFF_COMMAND=/user/service/etc/diff_com.sh
// default : /user/service/etc/diff_com.sh
std::string m_strRcCMove; // rc_cmove path, RC_CMOVE=/user/service/bin/rc_cmove
// default : /user/service/bin/rc_cmove
std::vector<std::string> m_vecFHSMount; // FHS storage mount info multi set enable, FHS_STORAGE_MOUNT = /stg/node0,/stg/node1,/stg/node2
// default : /stg/node0,/stg/node1,/stg/node2
public: // 멤버 변수에 대한 Get 함수 선언 및 정의
const char * GetConfigFileName() { return m_strConfigFileName.c_str(); }
const char * GetProgramName() { return m_strProgramName.c_str(); }
const char * GetLogPath() { return m_strLogPath.c_str(); }
int GetLogLevel() { return m_nLogLevel; }
const char * GetRcId() { return m_strRcId.c_str(); }
const char * GetRcdbIp() { return m_strRcdbIp.c_str(); }
unsigned int GetRcdbPort() { return m_nRcdbPort; }
const char * GetRcdbName() { return m_strRcdbName.c_str(); }
const char * GetRcdbAcct() { return m_strRcdbAcct.c_str(); }
const char * GetRcdbAcctPw() { return m_strRcdbAcctPw.c_str(); }
std::vector<std::string> GetSkipWord() { return m_vecSkipWord; }
unsigned int GetOperationPort() { return m_nOperationPort; }
unsigned int GetDbPoolCount() { return m_nDbPoolCount; }
unsigned int GetWorkPoolCount() { return m_nWorkPoolCount; }
unsigned int GetSafetyFactor() { return m_nSafetyFactor; }
unsigned int GetOnceSize() { return m_nOnceSize; }
unsigned int GetChkSmall() { return m_nChkSmall; }
unsigned int GetChkMiddle() { return m_nChkMiddle; }
unsigned int GetChkOnce() { return m_nChkOnce; }
const char * GetDiffCommand() { return m_strDiffCommand.c_str(); }
const char * GetRCCMoveCommand() { return m_strRcCMove.c_str(); }
std::vector<std::string> GetFHSMount() { return m_vecFHSMount; }
};
#endif /* __SERVICE_CONFIG_H__ */
+477
View File
@@ -0,0 +1,477 @@
/***************************************************************************
Source Data
-----------------------------------------
begin : 2011/10/27
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include "SourceData.h"
#include "Database.h"
#include "DataFile.h"
#include "SyncList.h"
#include "ServiceConfig.h"
#include "RcdbInfo.h"
#include "Util.h"
#include "Logger.h"
#include <errno.h>
#include <string.h>
// 전체 자료량 구하지 못했을 때 해당 값 사용
#define MAX_LIMIT_OFFSET_COUNT 1000000
// 쿼리문에 사용할 임시 버퍼 Size
#define DEFAULT_BUFFER_SIZE 1024
CSourceData::CSourceData()
: m_bDELETED_YN(false)
, m_usedisplay(false)
, m_ismaster(false)
{
}
CSourceData::~CSourceData()
{
}
bool CSourceData::Init(bool master, CSyncInfo sourceParam)
{
bool r = false;
m_ismaster = master;
m_sourceParam = sourceParam;
if ( m_sourceParam.sync_type == SYNC_TYPE::NORMA_N_ONLY ||
m_sourceParam.sync_type == SYNC_TYPE::CHK_ONLY_N_ONLY )
m_bDELETED_YN = true;
else
m_bDELETED_YN = false;
m_strErrorMessage.clear();
if (m_ismaster)
r = m_dataFile.Init(m_sourceParam.file_master_name.c_str());
else
r = m_dataFile.Init(m_sourceParam.file_slave_name.c_str());
if (r == false)
{
m_strErrorMessage = "Failed Create source file.";
}
return r;
}
bool CSourceData::Execute()
{
string errmsg;
if (isError(errmsg) == true)
{
LOG(LERR, "Failed Initialization.");
return false;
}
ostringstream msg;
int nRet = ::pthread_create(&m_handleThread, NULL, CSourceData::Run, this);
if(nRet != 0)
{
msg << "Thread create failed.: errno: " << errno;
SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
return false;
}
return true;
}
void* CSourceData::Run(void* pVoid)
{
ostringstream msg;
CSourceData* pSourceData = reinterpret_cast<CSourceData*>(pVoid);
// pthread_detach( pthread_self() ); // 호출한 위치에서 pthread_join을 사용한다.
// 1. RCDB t_sms_sp_svc_product 정보를 조회하여 각 고객사별 svcsync count 정보를 가져와 저장처리한다.
// RCDB 접속 처리를 위한 객체 생성
DataBase* pPgSQL = new DataBase;
if( pPgSQL == NULL )
{
msg << "Cannot create DataBase object";
pSourceData->SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
return NULL;
}
CRcdbInfo info;
info.Load();
// RCDB 접속 시도 및 접속 실패시
if (pPgSQL->PgOpenDB(info.m_strRcdbIp, info.m_nRcdbPort, info.m_strRcdbName, info.m_strRcdbAcct, info.m_strRcdbAcctPw) == NULL)
{
if (pPgSQL != NULL)
{
msg << "RCDB Connect failed [" << pPgSQL->GetErrorMessage() << "]";
pSourceData->SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
delete pPgSQL;
pPgSQL = NULL;
}
return NULL;
}
// 2014.06.07 dadamin
// 작업 대상 테이블명 가져오기
string tblname = pSourceData->GetTargetTable(pPgSQL);
if ( tblname.size() <= 0 )
{
if (pPgSQL != NULL)
{
string tranid = pSourceData->m_sourceParam.sync_slave;
if (pSourceData->m_ismaster) tranid = pSourceData->m_sourceParam.sync_master;
msg << "Failed to fetch Target table.[" << tranid << "]";
pSourceData->SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
delete pPgSQL;
pPgSQL = NULL;
}
return NULL;
}
// 2014.08.06 dadamin
// 해당테이블 전체 자료량에 10% 값을 limit 값으로 사용한다.
int64_t total = pSourceData->GetRows(pPgSQL, tblname.c_str());
int64_t getlimit = (total > 0 ? int64_t(total*0.1) : MAX_LIMIT_OFFSET_COUNT);
getlimit = (getlimit == 0 ? MAX_LIMIT_OFFSET_COUNT : getlimit);
int nResult = 0;
// Query Resultset Clear.
pPgSQL->PgClear();
///////////////////////////////////////////////////////////////////////////////////////////////////
int nNowResult = 0;
string strResultUri, strResultType, strResultLength;
string strResultLastDate, strResultDeletedYN, strResultFilenameHash;
char* szResultUriTmp1 = NULL;
char* szResultHasNameTmp1 = NULL;
// Slave의 경우 파일이 없는 경우가 발생할 수 있으므로 미리 빈파일을 생성해 둔다.
pSourceData->m_dataFile.Write("");
do
{
ostringstream sql;
pPgSQL->PgClear();
// 2014.06.13 dadamin
// 신규 형상(9.3) 경우 display_name 사용하여 대소문자 기능을 지원하며,
// 기존 형상의 uri와 동일한 값을 가지고 있으므로 해당 형상인 경우
// uri 대신 display_name으로 소스 데이터를 가져오게 한다.
if(pSourceData->m_usedisplay)
sql << "SELECT display_name, resource_type, get_content_length, file_lastmodified, deleted_yn, filename_hash ";
else
sql << "SELECT uri, resource_type, get_content_length, file_lastmodified, deleted_yn, filename_hash ";
sql << "FROM " << tblname << " ";
sql << "WHERE get_lastmodified >= $1 AND get_lastmodified < $2 ";
sql << "AND depth >= 0 AND depth <= 1000 ";
sql << "AND uri LIKE '/' || $3 || '/%' ";
if( pSourceData->m_bDELETED_YN )
sql << "AND deleted_yn = 'N' ";
sql << "AND is_cache = 'N' ";
if(pSourceData->m_usedisplay)
sql << "ORDER BY display_name, get_lastmodified ";
else
sql << "ORDER BY uri, get_lastmodified ";
sql << "LIMIT $4 OFFSET $5 ";
#ifdef _DEBUG
if(nNowResult == 0)
cout << szQuery << endl;
#endif // _DEBUG
//
//MAX_LIMIT_OFFSET_COUNT
//nNowResult
const char *paramValues[5];
ostringstream tmp;
string strTime1, strTime2;
string strLimit, strOffSet;
tmp << str2longtime(pSourceData->m_sourceParam.start_time);
strTime1 = tmp.str(); tmp.str("");
tmp << str2longtime(pSourceData->m_sourceParam.end_time);
strTime2 = tmp.str(); tmp.str("");
tmp << getlimit;
strLimit = tmp.str(); tmp.str("");
tmp << nNowResult;
strOffSet = tmp.str(); tmp.str("");
paramValues[0] = strTime1.c_str();
paramValues[1] = strTime2.c_str();
if (pSourceData->m_ismaster)
paramValues[2] = pSourceData->m_sourceParam.sync_master.c_str();
else
paramValues[2] = pSourceData->m_sourceParam.sync_slave.c_str();
paramValues[3] = strLimit.c_str();
paramValues[4] = strOffSet.c_str();
nNowResult += getlimit;
// Query 실행
//pPgSQL->PgDoExec(szQuery);
pPgSQL->PgDoExecParams((char*) sql.str().c_str(), 5, paramValues);
// Query 결과를 가져온다.
nResult = pPgSQL->PgResult(DataBase::NOT_CLEAR);
if(nResult < 0)
{
pPgSQL->PgClear();
// RCDB t_dav_resource 테이블 조회 실패
msg << "RCDB Meta row get failed.[" << nResult << "][" << pPgSQL->GetErrorMessage() << "]";
pSourceData->SetErrorMessage(msg.str());
LOG(LERR, msg.str().c_str());
break;
}
// 결과 Row 수 확인
nResult = pPgSQL->GetNoTuples();
// 조회 결과가 존재하는 경우 조회된 결과를 파일에 저장
for (int n = 0; n < nResult; n++)
{
szResultUriTmp1 = NULL;
szResultUriTmp1 = strchr(pPgSQL->GetValue(n, 0)+1, '/'); // URI (앞의 tran_id를 삭제한다.)
// skip 키워드 포함되어 있는 검사한다.
if(pSourceData->IsSkipKeyword(szResultUriTmp1) == true)
{
msg << "SKIP Source data." << szResultUriTmp1;
_LOG(LNOT, "%s", msg.str().c_str());
msg.str("");
//REPORT_SUCCESS("[%s] [SKIP] %s (source data has skip keyword.)",
// pSourceData->m_sourceParam.m_tranid.c_str(), szResultUriTmp1);
continue;
}
strResultUri = szResultUriTmp1;
strResultType = pPgSQL->GetValue(n, 1); // resource_type
strResultLength = pPgSQL->GetValue(n, 2); // get_content_length
strResultLastDate = pPgSQL->GetValue(n, 3); // file_lastmodified
strResultDeletedYN = pPgSQL->GetValue(n, 4); // deleted_yn
// 디렉토리 경우 hash가 없는 경우도 있으므로 파일만 hash 사용
szResultHasNameTmp1 = NULL;
if ( strResultType.compare("0") == 0 )
szResultHasNameTmp1 = strrchr(pPgSQL->GetValue(n, 5) + 1, '/'); // filename_hash ( 볼륨 정보 삭제함)
if (szResultHasNameTmp1)
strResultFilenameHash = szResultHasNameTmp1;
else
strResultFilenameHash = "";
if(strResultUri.size() <= 0) // URI가 없는 경우 저장 안함
continue;
if(strResultType.size() <= 0) // 파일 타입값이 NULL 일 경우 0으로 기본 셋팅
strResultType = "0";
if (strResultLastDate.size() <= 0) // 파일 크기값이 NULL 일 경우 0으로 기본 셋팅
strResultLastDate = "0";
// 해당 정보를 찾은 경우
pSourceData->m_dataFile.Write("%s|%s|%s|%s|%s|%s",
strResultUri.c_str(),
strResultFilenameHash.c_str(),
strResultType.c_str(),
strResultLength.c_str(),
strResultLastDate.c_str(),
strResultDeletedYN.c_str());
}
#ifdef _DEBUG
cout << "Generate tmp file : " << pSourceData->m_dataFile.GetFilePath()
<< "," << pSourceData->m_dataFile.GetFileName()
<< ", run count=" << nResult
<< " / " << nNowResult - MAX_LIMIT_OFFSET_COUNT
<< endl;
#endif // _DEBUG
} while (getlimit == nResult); // do while()
//////////////////////////////////////////////////////////////////////////////////////////////////
// DB Object 삭제 처리.
if( pPgSQL != NULL )
{
pPgSQL->PgClear();
pPgSQL->PgCloseDB();
delete pPgSQL;
pPgSQL = NULL;
}
return NULL;
}
bool CSourceData::IsSkipKeyword(string key)
{
bool r = false;
vector<string> skipwords = CServiceConfig::GetInstance()->GetSkipWord();
int keycnt = skipwords.size();
for( int i=0; i< keycnt; ++i )
{
string k = skipwords[i];
vector<string> v;
StringSplit(k, "|", v);
string::size_type pos;
pos = key.find(v[1]);
if ( pos != string::npos )
{
switch (v[0][0])
{
case 'h':
if (pos == 0)
{
if (v[1] == key || key.at(v[1].size()) == '/')
r = true;
}
break;
case 'm':
if(pos > 0 && key.size() > pos + v[1].size())
r = true;
break;
case 't':
if(pos > 0 && key.size() == pos + v[1].size())
r = true;
break;
default:
break;
}
}
}
return r;
}
// 2014.06.07 dadamin
// 작업 대상 테이블
string CSourceData::GetTargetTable(DataBase *d)
{
string r;
ostringstream msg;
if (d == NULL)
return "";
string sql = "SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource'";
d->PgDoExec(sql);
if (d->PgResult(DataBase::NOT_CLEAR) < 0)
{
d->PgClear();
msg << "SetTargetTableType failed : " << d->GetErrorMessage();
LOG(LERR, msg.str().c_str());
return "";
}
if (d->GetNoTuples() > 0)
{
m_usedisplay = false;
r = "t_dav_resource";
}
else
{
m_usedisplay = true;
if (m_ismaster)
r = "t_meta_" + m_sourceParam.sync_master;
else
r = "t_meta_" + m_sourceParam.sync_slave;
}
d->PgClear();
msg << "Set Target Table : " << r;
LOG(LDBG, msg.str().c_str());
return r;
}
static int64_t convert(string &s)
{
if(s.size() == 0 )
return 0;
int64_t v = 0;
size_t i=0;
char sign = (s[0] == '-' || s[0] == '+') ? (++i, s[0]) : '+';
for( ; i < s.size(); ++i)
{
if ( s[i] < '0' || s[i] > '9' )
return 0;
v = v * 10 + s[i] - '0';
}
return sign == '-' ? -v : v;
}
// 해당 테이블 전체 rows
int64_t CSourceData::GetRows(DataBase *d, const char* tblname)
{
int64_t r = 0;
ostringstream msg;
// SELECT relname, reltuples from pg_class where relname ='t_meta_346';
if (d == NULL)
return 0;
ostringstream sql;
sql << "SELECT relname, reltuples::bigint from pg_class where relname = ";
sql << "'" << tblname << "'";
d->PgDoExec((char*)sql.str().c_str());
if (d->PgResult(DataBase::NOT_CLEAR) < 0)
{
d->PgClear();
msg << "Get Table Rows failed : " << d->GetErrorMessage();
LOG(LERR, msg.str().c_str());
return 0;
}
if (d->GetNoTuples() > 0)
{
string t = d->GetValue(0, 1);
r = convert(t);
}
msg << "Table : " << tblname << ", Rows : "<< r;
LOG(LDBG, msg.str().c_str());
d->PgClear();
return r;
}
+79
View File
@@ -0,0 +1,79 @@
/***************************************************************************
Source Data
-----------------------------------------
begin : 2011/10/27
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __SOURCE_DATA_H__
#define __SOURCE_DATA_H__
#include <sys/types.h>
#include <string>
#include "DataFile.h"
#include "SyncList.h"
using namespace std;
class DataBase;
class CSourceData
{
private:
pthread_t m_handleThread; // 스레드 핸들
CSyncInfo m_sourceParam; // sync info
CDataFile m_dataFile; // 파일저장시 사용할 객체
bool m_bDELETED_YN; // bDELETED_YN값이 true일때 deleted_yn = 'N' 조회
bool m_usedisplay; // 신규형상(9.3)에서는 display_name 사용하며, 기존 uri 값과 동일한 값
bool m_ismaster; // master / slave 여부
// 에러 메시지를 담는다.
// 주의 : 호출한 함수에서 isError() 를 통해 에러 처리를 따로하므로, 에러가 아닌 경우에 변수에 값을 담으면 안된다.
string m_strErrorMessage;
public:
CSourceData();
~CSourceData();
protected:
static void* Run(void* arg); // 스레드 시작
bool IsSkipKeyword(string key); // uri에 skip keyword 포함 유무 체크
string GetTargetTable(DataBase *d);
int64_t GetRows(DataBase *d, const char* tblname);
inline void SetErrorMessage(const string strErrorMessage)
{ m_strErrorMessage = strErrorMessage; }
public:
// thread 초기화
bool Init(bool master, CSyncInfo sourceParam);
// thread 실행
bool Execute();
// 호출측에서 Thread의 종료 여부등을 판단하기 위함
inline pthread_t* GetThreadHandle()
{ return &m_handleThread; }
inline string GetMetaFilePath()
{ return m_dataFile.GetFilePath(); }
inline bool isError(string & errmsg )
{
errmsg = m_strErrorMessage; return bool(m_strErrorMessage.size() > 0);
}
};
#endif // __SOURCE_DATA_H__
+215
View File
@@ -0,0 +1,215 @@
#include "SyncList.h"
#include "ServiceConfig.h"
#include "Database.h"
#include "DBConnPool.h"
#include "Logger.h"
#include "ReportLog.h"
#include <unistd.h>
#include <string.h>
#define SYNC_FILE_PATH "/user/service/_sync"
// CSyncInfo class
CSyncInfo::CSyncInfo()
{
}
CSyncInfo::~CSyncInfo()
{
}
void CSyncInfo::UnlinkFile()
{
unlink(file_master_name.c_str());
unlink(file_slave_name.c_str());
unlink(file_diff_name.c_str());
if (sync_type < SYNC_TYPE::CHK_ONLY )
unlink(file_sync_name.c_str());
}
// CSyncLinst class
CSyncList::CSyncList()
{
}
CSyncList::~CSyncList()
{
m_list.clear();
}
bool CSyncList::CreateList(CReqMasterData & d)
{
// Sync Info from RCDB
DataBase *db = CMasterDBPool::getInstance()->GetConnFromPool();
if (!db)
{
LOG(LERR, "Failed GetDB.");
return false;
}
ostringstream sql;
sql << "SELECT";
sql << " S.sp_svc_tran_id, S.master_yn, S.linked_sp_svc_tran_id, S.linked_rcts, P.sp_svc_tran_id, P.uri_ignore_case ";
sql << " FROM t_sms_sp_svc_product_sync S ";
sql << " LEFT JOIN t_sms_sp_svc_product P ON(S.sp_svc_tran_id = P.sp_svc_tran_id) ";
if (d.one_service.empty())//d.service_list.empty())
{
// All
}
else
{
string s = d.one_service;
// part
/*
for (list<string>::iterator list_iter = d.service_list.begin();
list_iter != d.service_list.end(); list_iter++)
{
if (list_iter != d.service_list.begin())
s += ",";
s += *list_iter;
}
*/
sql << " WHERE P.sp_svc_tran_id IN (" << s << ")";
}
db->PgDoExec((char*)sql.str().c_str());
if (db->PgResult(DataBase::NOT_CLEAR) < 0)
{
LOG(LERR, " Failed Get Sync List. %s", db->GetErrorMessage().c_str());
db->PgClear();
CMasterDBPool::getInstance()->ReleaseConnToPool(db, false);
return false;
}
for (int i = 0; i < db->GetNoTuples(); ++i)
{
CSyncInfo t;
if (strlen(db->GetValue(i, 4)) <= 0)
{
//LOG(LNOT, "Not Found Service.[tran_id :%s]", db->GetValue(i, 0));
CReportLog rlog;
if (rlog.Init(CServiceConfig::GetInstance()->GetLogPath()))
{
rlog.Write("NOT", "[%s] [Not Found Sync Service in Product Table.[tran_id :%s]]", PROG_NAME, db->GetValue(i, 0));
}
continue;
}
// master
if (strcmp(db->GetValue(i, 1), "Y") == 0)
{
t.uri_ignore_case = db->GetValue(i, 5);
t.start_time = d.start_time;
t.end_time = d.end_time;
t.sync_type = d.sync_type;
t.sync_master = db->GetValue(i, 0);
t.sync_slave = db->GetValue(i, 2);
t.sync_rcts = db->GetValue(i, 3);
GenFileName(t);
m_list.push_back(t);
}
}
db->PgClear();
CMasterDBPool::getInstance()->ReleaseConnToPool(db);
if (m_list.empty())
{
LOG(LWAR, "Empty Sync List.");
return false;
}
return true;
}
bool CSyncList::CreateList(CReqSlaveData & d)
{
CSyncInfo i;
CreateList((CReqServiceData*)&d, i);
i.sync_master = d.master;
i.sync_slave = d.slave;
GenFileName(i);
m_list.push_back(i);
return true;
}
bool CSyncList::CreateList(CReqSyncData & d)
{
CSyncInfo i;
CreateList((CReqServiceData*)&d, i);
i.sync_master = d.master;
i.sync_slave = d.slave;
GenFileName(i);
m_list.push_back(i);
return true;
}
CSyncInfo CSyncList::Pop()
{
CSyncInfo r = m_list.front();
m_list.pop_front();
return r;
}
bool CSyncList::CreateList(const CReqServiceData * d, CSyncInfo & i)
{
i.start_time = d->start_time;
i.end_time = d->end_time;
i.sync_type = d->sync_type;
return true;
}
void CSyncList::GenFileName(CSyncInfo & i)
{
ostringstream tmp;
//Source file
// - diff 대상 파일
// - source_(master)tran_id_starttime_endtime_TreadID.(master / slave)
tmp.str("");
tmp << SYNC_FILE_PATH << "/source_" << i.sync_master << "_" << i.sync_slave << i.sync_rcts << "_";
tmp << i.start_time << "_" << i.end_time << "_" << pthread_self() << ".master";
i.file_master_name = tmp.str();
tmp.str("");
tmp << SYNC_FILE_PATH << "/source_" << i.sync_master << "_" << i.sync_slave << i.sync_rcts << "_";
tmp << i.start_time << "_" << i.end_time << "_" << pthread_self() << ".slave";
i.file_slave_name = tmp.str();
//Diff file
// - Source data 비교한 결과
// - diff_(master)tran_id_starttime_endtime_TreadID
tmp.str("");
tmp << SYNC_FILE_PATH << "/diff_" << i.sync_master << "_" << i.sync_slave << i.sync_rcts << "_";
tmp << i.start_time << "_" << i.end_time << "_" << pthread_self() ;
i.file_diff_name = tmp.str();
//Sync file
//- 동기(이관) 대상 파일
//- sync_(master)tran_id_starttime_endtime_TreadID
tmp.str("");
tmp << SYNC_FILE_PATH << "/sync_" << i.sync_master << "_" << i.sync_slave << i.sync_rcts << "_";
tmp << i.start_time << "_" << i.end_time << "_" << pthread_self();
i.file_sync_name = tmp.str();
}
+91
View File
@@ -0,0 +1,91 @@
/****************************************************************************
Sync Information List
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __SYNC_LIST_H__
#define __SYNC_LIST_H__
#include <string>
#include <list>
#include <ostream>
#include <sstream>
#include "RcSyncdRequestData.h"
using namespace std;
// CSyncList 는 내부 다른 모듈에서 사용할 sync info 를 Item 으로 가지는 리스트임
// Item 구성은 요청 받은 (RcSyncdRequestData.h 참조) 데이터로 sync info를 생성하여
// 리스트 구성한다
// CSyncInfo class
class CSyncInfo
{
public:
CSyncInfo();
~CSyncInfo();
public:
// 생성된 파일 삭제 처리
void UnlinkFile();
// 대소문자 구문 여부(master/slave 동일 설정이라고 가정함)
string uri_ignore_case;
// sync 대상 정보
string sync_master;
string sync_slave;
string sync_rcts;
// 동기화 방식 및 추출 대상
SYNC_TYPE::SYNC_TYPE sync_type;
// 동기화 구간
string start_time;
string end_time;
// 사용할 파일명
// // Full 경로 포함. (실제 파일 존재 여부는 확인 필요)
string file_master_name; // master source file name;
string file_slave_name; // slave source file name(slave mode 요청도 사용)
string file_diff_name; // diff file name
string file_sync_name; // sync filen(sync mode 요청도 사용 )
};
// CSyncInfo class
class CSyncList
{
public:
CSyncList();
~CSyncList();
bool CreateList(CReqMasterData & d);
bool CreateList(CReqSlaveData & d);
bool CreateList(CReqSyncData & d);
CSyncInfo Pop();
inline bool Empty() { return m_list.empty(); }
inline CSyncInfo & GetFirst() { return m_list.front(); }
inline CSyncInfo & GetLast() { return m_list.back(); }
inline list<CSyncInfo>& GetList() { return m_list; }
private:
void GenFileName(CSyncInfo & i);
bool CreateList(const CReqServiceData * d, CSyncInfo & i);
private:
list<CSyncInfo> m_list;
};
#endif /* ___SYNC_LIST_H__*/
+100
View File
@@ -0,0 +1,100 @@
#include "Synchronization.h"
#include "Logger.h"
#include "RcSyncdRequestData.h"
#include "ProcessSocketControl.h"
#include "ServiceConfig.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
CSynchronization::CSynchronization(CSyncInfo &info)
: m_syncinfo(info)
{
}
CSynchronization::~CSynchronization()
{
}
int CSynchronization::ExcuteSync()
{
std::string strMessage;
std::string strErrorMessage;
CReqSyncData request;
request.sync_type = m_syncinfo.sync_type;
request.start_time = m_syncinfo.start_time;
request.end_time = m_syncinfo.end_time;
request.master = m_syncinfo.sync_master;
request.slave = m_syncinfo.sync_slave;
// get file size
struct stat resultFileStat;
if( stat( m_syncinfo.file_sync_name.c_str(), &resultFileStat ) != 0 )
{
// 해당 파일에 대한 stat 정보 추출 실패시.
int errorNum = errno;
char tempBuffer[1024];
if( errno != ENOENT )
{
snprintf( tempBuffer, sizeof( tempBuffer ) - 1, "sync file mode file[%s] stat check fail [%d][%s]"
, m_syncinfo.file_sync_name.c_str()
, errorNum, strerror( errorNum ) );
strErrorMessage = tempBuffer;
LOG( LERR, "%s", strErrorMessage.c_str() );
return -1;
}
else
{
// 파일이 존재 하지 않는 경우는 싱크 대상 목록이 없다는 것이기 때문에 성공으로 간주한다.
LOG( LINF, "Not exist sync file.[%s]", strErrorMessage.c_str() );
return 0;
}
}
// check 모드 인 경우 단순 리턴함
if (m_syncinfo.sync_type > SYNC_TYPE::NORMA_N_ONLY)
return 1;
// 파일 size 추출.
unsigned long long nFileSize = resultFileStat.st_size;
// fd open
int resultFd = open( m_syncinfo.file_sync_name.c_str(), O_RDONLY );
if( resultFd == -1 )
{
int errorNum = errno;
char tempBuffer[1024];
snprintf( tempBuffer, sizeof( tempBuffer ) - 1, "sync file mode result file[%s] open fail [%d][%s]"
, m_syncinfo.file_sync_name.c_str()
, errorNum, strerror( errorNum ));
strErrorMessage = tempBuffer;
LOG( LERR, "%s", strErrorMessage.c_str() );
return -2;
}
// Slave rc_syncd 접속 및 content 정보 요청
CProcessSocketControl slave;
slave.ConnectTarget(m_syncinfo.sync_rcts, CServiceConfig::GetInstance()->GetOperationPort());
if( slave.SendRequestExecuteSyncToSlave( request, resultFd, nFileSize, strMessage ) == false )
{
// 전송 실패시
_LOG( LERR, "%s", strMessage.c_str());
close(resultFd);
slave.Close();
return -3;
}
_LOG( LDBG, "%s", strMessage.c_str());
close(resultFd);
slave.Close();
return 1;
}
+33
View File
@@ -0,0 +1,33 @@
/****************************************************************************
Synchronization Class Header
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __SYNCHRONIZATION_H__
#define __SYNCHRONIZATION_H__
#include "SyncList.h"
class CSynchronization
{
public:
CSynchronization(CSyncInfo &info);
~CSynchronization();
// sync ±¸µ¿ ¿äû
int ExcuteSync();
private:
CSyncInfo m_syncinfo;
};
#endif /* __SYNCHRONIZATION_H__ */
+130
View File
@@ -0,0 +1,130 @@
#include "Util.h"
#include <sys/time.h>
#define SEC_TO_MICROSEC (1000000LL)
/// @brief 현재 시간 정보를 micro-second 단위로 계산하여 반환
/// @return 1970년 1월 1일 이후부터의 현재시간을 micro-second(1/1,000,000) 단위로 반환처리, 오류발생시에는 -1 값을 반환.
long long longtime_now()
{
struct timeval tv;
if( gettimeofday(&tv, NULL) == 0 )
{
// 성공시 micro-second 단위의 값을 반환
return tv.tv_sec * SEC_TO_MICROSEC + tv.tv_usec;
}
else
{
// 실패시 -1 값을 반환.
return -1;
}
}
/// @brief source string에 주어진 search string을 replacement로 변환 화여 반환
std::string stringreplace(const std::string& source, const std::string search, const std::string replacement)
{
std::string r = source;
std::string::size_type pos = 0;
while ( (pos = r.find(search, pos)) != std::string::npos )
{
r.replace( pos, search.size(), replacement );
pos = pos + replacement.size();
}
return r;
}
std::string strtime_now()
{
time_t rawtime;
struct tm * timeinfo;
char buffer[16] = { 0 };
time(&rawtime);
timeinfo = localtime(&rawtime);
if (strftime(buffer, sizeof buffer, "%Y%m%d%H%M%S", timeinfo) <= 0)
return "";
return std::string(buffer);
}
time_t str2time_t(std::string strtime)
{
struct tm tm = { 0 };
if (strtime == "0")
return 0;
if (strptime(strtime.c_str(), "%Y%m%d%H%M%S", &tm) == NULL)
return -1;
return mktime(&tm);
}
std::string time_t2string(time_t rawtime)
{
struct tm * timeinfo;
char buffer[16] = { 0 };
timeinfo = localtime(&rawtime);
if (strftime(buffer, sizeof buffer, "%Y%m%d%H%M%S", timeinfo) <= 0)
return "";
return std::string(buffer);
}
long long str2longtime(std::string strtime)
{
return str2time_t(strtime) * SEC_TO_MICROSEC;
}
void StringSplit(std::string str, std::string delim, std::vector<std::string> &results, bool bUseEmpty /*= false*/)
{
const std::string strEmpty("");
std::string::size_type cutAt;
while ((cutAt = str.find_first_of(delim)) != str.npos)
{
if (cutAt > 0)
{
results.push_back(str.substr(0, cutAt));
}
else
{
if (bUseEmpty && cutAt == 0)
results.push_back(strEmpty);
}
str = str.substr(cutAt + 1);
}
if (str.length() > 0)
{
results.push_back(str);
}
}
void solusleep(unsigned long usec)
{
// usleep 사용시 multi thread 환경에서 block 발생 가능성이 존재
// 이에 nanosleep 함수를 사용토록 수정 처리함.
struct timespec sleep;
if (usec >= 1000000)
{
sleep.tv_sec = (int)(usec / 1000000);
sleep.tv_nsec = (usec - ((long)sleep.tv_sec * 1000000)) * 1000;
}
else
{
sleep.tv_sec = 0;
sleep.tv_nsec = usec * 1000;
}
nanosleep(&sleep, NULL);
}
+58
View File
@@ -0,0 +1,58 @@
/***************************************************************************
Util functions
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : storage dev team
email : storage.sd@solbox.com
version : 3.4
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.
***************************************************************************/
#ifndef __UTIL_H__
#define __UTIL_H__
#include <time.h>
#include <string>
#include <vector>
#ifdef __cplusplus
extern "C" {
#endif
/// @brief 현재 시간 정보를 micro-second 단위로 계산하여 반환.
/// @return 1970년 1월 1일 이후부터의 현재시간을 micro-second(1/1,000,000) 단위로 반환처리, 오류발생시에는 -1 값을 반환.
long long longtime_now(void);
/// @brief source string에 주어진 search string을 replacement로 변환 하여 결과 반환.
std::string stringreplace(const std::string& source, const std::string search, const std::string replacement);
// string now time
std::string strtime_now();
// time_t to stirng
std::string time_t2string(time_t rawtime);
// string to time_t
time_t str2time_t(std::string strtime);
// string to microsecond
long long str2longtime(std::string strtime);
// Split string function
// bUseEmpty값이 true면 delimiter의 갯수에 따라 results.size()의 숫자도 맞추기 위한 옵션
void StringSplit(std::string str, std::string delim, std::vector<std::string> &results, bool bUseEmpty = false);
void solusleep(unsigned long usec);
#ifdef __cplusplus
}
#endif
#endif // __UTIL_H__
+548
View File
@@ -0,0 +1,548 @@
#include "Validation.h"
#include "DataFile.h"
#include "DBConnPool.h"
#include "Database.h"
#include "Util.h"
#include "Logger.h"
#include "ReportLog.h"
#include "ServiceConfig.h"
#include <sys/types.h>
#include <string.h>
#define FLAG_SYNC "SYNC"
#define FLAG_DEL "DEL"
#define FLAG_NSYNC "NSYNC"
#define FLAG_COPY "COPY"
#define FLAG_MOVE "MOVE"
// uri|filename_hash|resource_type|get_content_length|file_lastmodified|deleted_yn
class DIFFFILE_FIELD
{
public:
enum FIELD
{
uri = 0, filename_hash, resource_type, get_content_length,
file_lastmodified, deleted_yn,
MAX_FILED
};
};
// meta table filed
class CURRENT_FIELD
{
public:
enum FIELD
{
uri = 0, filename_hash, host_name, resource_type,
depth, creation_date, display_name,
get_content_length, get_content_type,
get_lastmodified, source, deleted_yn,
is_cache, file_lastmodified,
MAX_FILED
};
};
// flag|uri|filename_hash|resource_type|get_content_length|file_lastmodified|deleted_yn|host_name|creation_date|get_content_type|src_uri|slave_tran_id
class SYNCFILE_FIELD
{
public:
// SYNCFILE_FIELD::flag
// - SYCN : 동기화 필요
// - DEL : 삭제 필요
// - COPY : 복사된 파일임
// - MOVE : 이동된 파일임
enum FIELD
{
flag = 0, uri, filename_hash, resource_type, get_content_length,
file_lastmodified, deleted_yn, host_name, creation_date, get_content_type,
src_uri, slave_tran_id, uri_ignore_case,
// copy 속성을 찾기 위해서 추가(sync file 경우 filename_hash 필드에 해당 값 사용)
src_filename_hash,
MAX_FILED
};
};
static int StringCount(string str, string delim)
{
int nCount = 0;
string::size_type cutAt;
while ((cutAt = str.find_first_of(delim)) != str.npos)
{
if (cutAt > 0)
{
nCount++;
}
str = str.substr(cutAt + 1);
}
if (str.length() > 0)
nCount++;
return nCount;
}
static int64_t convert(string &s)
{
if (s.size() == 0)
return 0;
int64_t v = 0;
size_t i = 0;
char sign = (s[0] == '-' || s[0] == '+') ? (++i, s[0]) : '+';
for (; i < s.size(); ++i)
{
if (s[i] < '0' || s[i] > '9')
return 0;
v = v * 10 + s[i] - '0';
}
return sign == '-' ? -v : v;
}
CValidation::CValidation(CSyncInfo& info, string data)
: m_syncinfo(info), m_diffdata(data)
{
}
CValidation::~CValidation()
{
}
bool CValidation::CheckValidation(bool is_master /*= true*/)
{
CMasterDBPool* pool = CMasterDBPool::getInstance();
if (pool == NULL)
{
LOG(LERR, "DB Coon Pool is NULL");
return false;
}
// uri|filename_hash|resource_type|get_content_length|file_lastmodified|deleted_yn
vector<string> vectdiff;
StringSplit(m_diffdata, "|", vectdiff, true);
if (vectdiff.size() != DIFFFILE_FIELD::MAX_FILED)
{
LOG(LERR, "validation sync file string split caution :[%s]", m_diffdata.c_str());
return false;
}
DataBase *db = pool->GetConnFromPool();
if (!db)
{
LOG(LERR, "Failed GetDB.");
return false;
}
string tran_id = (is_master ? m_syncinfo.sync_master : m_syncinfo.sync_slave);
string tblname = pool->GetMetaTableName(tran_id.c_str());
bool useDisyplay = pool->UseDisplayname();
// check 1 : diffdata에 대한 RCDB 최신 상태 비교
/// Get Current Meta
if (GetCurrentMeta(db, vectdiff, tblname, tran_id, useDisyplay) == false)
{
pool->ReleaseConnToPool(db, false);
return false;
}
/// Compare input & current
vector<string>vectOut = CompareData(vectdiff, tran_id);
if (vectOut.empty())
{
pool->ReleaseConnToPool(db);
LOG(LERR, "Failed CompareData.[%s]", m_diffdata.c_str())
return false;
}
// write sync file formate
if (WriteSyncFile(vectOut) == false)
{
pool->ReleaseConnToPool(db);
LOG(LERR, "Failed Write Sync File.[%s]", m_diffdata.c_str())
return false;
}
pool->ReleaseConnToPool(db);
return true;
}
string CValidation::GetFullFilenameHash(vector<string> & vectdiff, string &tran_id)
{
string r;
std::vector<std::string> vecMount = CServiceConfig::GetInstance()->GetFHSMount();
for (vector<string>::size_type i = 0; i < vecMount.size(); ++i)
{
string hash = "/" + tran_id + vectdiff[DIFFFILE_FIELD::filename_hash];
if (i > 0)
r += ",";
r += vecMount[i] + hash;
}
// 2014.06.07 dadamin
// ANY 함수 사용을 {} 포함된 경우 array 로 인되는 경우 방지
r = stringreplace(r, "{", "\\{");
r = stringreplace(r, "}", "\\}");
r = "{" + r + "}";
return r;
}
bool CValidation::GetCurrentMeta(DataBase* d, vector<string> & vectdiff, string &tblname, string &tran_id, bool useDisyplay)
{
ostringstream sql;
ostringstream t;
const char *paramValues[5];
int index = 0;
// check 1 : diffdata에 대한 RCDB 최신 상태
sql << "SELECT ";
// 메타 변경에 다른 참조할 uri 컬럼 값이 다름
// 기존 uri 동일한 값은 display_name 임
if (useDisyplay)
sql << "display_name,";
else
sql << "uri,";
sql << "filename_hash, host_name, resource_type, ";
sql << "depth, creation_date, display_name, ";
sql << "get_content_length, get_content_type, ";
sql << "get_lastmodified, source, deleted_yn, ";
sql << "is_cache, file_lastmodified ";
sql << "FROM " << tblname << " ";
sql << "WHERE ";
string strdepth, strfulluri, strfullhash;
if (vectdiff[DIFFFILE_FIELD::resource_type] == "0")
{
// file
sql << "filename_hash = ANY ($1) ";
sql << "AND resource_type = $2 ";
sql << "AND get_content_length = $3 ";
strfullhash = GetFullFilenameHash(vectdiff, tran_id);
// filename_hash
paramValues[0] = strfullhash.c_str();
// resource_type
paramValues[1] = vectdiff[DIFFFILE_FIELD::resource_type].c_str();
// get_content_length
paramValues[2] = vectdiff[DIFFFILE_FIELD::get_content_length].c_str();
index = 3;
LOG(LDBG, "Value [Get Current Info(file)] => %s,%s,%s",
paramValues[0], paramValues[1], paramValues[2]);
}
else
{
sql << "depth = $1 ";
if (useDisyplay && m_syncinfo.uri_ignore_case == "Y")
{
sql << "AND uri = lower($2) ";
}
else
{
sql << "AND uri = $2 ";
}
// dir
sql << "AND resource_type = $3 ";
sql << "AND get_content_length = $4 ";
// depth 값 구하기.
int nDEPTH = StringCount(vectdiff[DIFFFILE_FIELD::uri], "/");
t << nDEPTH;
strdepth = t.str(); t.str("");
// Full URI
strfulluri = "/" + tran_id + vectdiff[DIFFFILE_FIELD::uri];
// depth
paramValues[0] = strdepth.c_str();
// uri
paramValues[1] = strfulluri.c_str();
// resource_type
paramValues[2] = vectdiff[DIFFFILE_FIELD::resource_type].c_str();
// get_content_length
paramValues[3] = vectdiff[DIFFFILE_FIELD::get_content_length].c_str();
index = 4;
LOG(LDBG, "Value [Get Current Info(dir)] => %s,%s,%s,%s",
paramValues[0], paramValues[1], paramValues[2], paramValues[3]);
}
sql << "AND deleted_yn = 'N' AND is_cache = 'N';";
LOG(LDBG, "SQL[Get Current Info] => %s", sql.str().c_str());
d->PgDoExecParams((char*)sql.str().c_str(), index, paramValues);
// Query 결과를 가져온다.
if (d->PgResult(DataBase::NOT_CLEAR) < 0)
{
LOG(LERR, "RCDB Meta failed.[%s][%s]",
vectdiff[DIFFFILE_FIELD::uri].c_str(), d->GetErrorMessage().c_str());
return false;
}
for (int i = 0; i < d->GetNoTuples(); ++i)
{
vector<string> vectcurr(CURRENT_FIELD::MAX_FILED);
string struri = d->GetValue(i, CURRENT_FIELD::uri);
size_t found = struri.find_first_of("/", 1);
if (found != string::npos)
{
vectcurr[CURRENT_FIELD::uri] = struri.substr(found);
}
else
{
LOG(LNOT, "Use the uri as in RCDB.[%s]", struri.c_str());
vectcurr[CURRENT_FIELD::uri] = struri;
}
if (vectdiff[DIFFFILE_FIELD::resource_type] == "0")
vectcurr[CURRENT_FIELD::filename_hash] = d->GetValue(i, CURRENT_FIELD::filename_hash);
vectcurr[CURRENT_FIELD::host_name] = d->GetValue(i, CURRENT_FIELD::host_name);
vectcurr[CURRENT_FIELD::resource_type] = d->GetValue(i, CURRENT_FIELD::resource_type);
vectcurr[CURRENT_FIELD::depth] = d->GetValue(i, CURRENT_FIELD::depth);
vectcurr[CURRENT_FIELD::creation_date] = d->GetValue(i, CURRENT_FIELD::creation_date);
vectcurr[CURRENT_FIELD::display_name] = d->GetValue(i, CURRENT_FIELD::display_name);
vectcurr[CURRENT_FIELD::get_content_length] = d->GetValue(i, CURRENT_FIELD::get_content_length);
vectcurr[CURRENT_FIELD::get_content_type] = d->GetValue(i, CURRENT_FIELD::get_content_type);
vectcurr[CURRENT_FIELD::get_lastmodified] = d->GetValue(i, CURRENT_FIELD::get_lastmodified);
vectcurr[CURRENT_FIELD::source] = d->GetValue(i, CURRENT_FIELD::source);
vectcurr[CURRENT_FIELD::deleted_yn] = d->GetValue(i, CURRENT_FIELD::deleted_yn);
vectcurr[CURRENT_FIELD::is_cache] = d->GetValue(i, CURRENT_FIELD::is_cache);
vectcurr[CURRENT_FIELD::file_lastmodified] = d->GetValue(i, CURRENT_FIELD::file_lastmodified);
m_current.insert(CCurretMap::value_type(struri, vectcurr));
}
d->PgClear();
return true;
}
vector<string> CValidation::CompareData(vector<string> & vectdiff, string &tran_id)
{
// flag|uri|filename_hash|resource_type|get_content_length|file_lastmodified|deleted_yn|host_name|src_uri|slave tran_id
vector<string> r(SYNCFILE_FIELD::MAX_FILED);
r[SYNCFILE_FIELD::uri] = vectdiff[DIFFFILE_FIELD::uri];
r[SYNCFILE_FIELD::filename_hash] = vectdiff[DIFFFILE_FIELD::filename_hash];
r[SYNCFILE_FIELD::resource_type] = vectdiff[DIFFFILE_FIELD::resource_type];
r[SYNCFILE_FIELD::get_content_length] = vectdiff[DIFFFILE_FIELD::get_content_length];
r[SYNCFILE_FIELD::file_lastmodified] = vectdiff[DIFFFILE_FIELD::file_lastmodified];
r[SYNCFILE_FIELD::deleted_yn] = vectdiff[DIFFFILE_FIELD::deleted_yn];
r[SYNCFILE_FIELD::slave_tran_id] = m_syncinfo.sync_slave;
r[SYNCFILE_FIELD::uri_ignore_case] = m_syncinfo.uri_ignore_case;
do
{
if (m_current.empty())
{
// m_diffdata에 포함된 uir, filename_hash 해당 하는 값이 존재 하지 않으므로 삭제 처리
r[SYNCFILE_FIELD::flag] = FLAG_DEL;
break;
}
string struri = "/" + tran_id + vectdiff[DIFFFILE_FIELD::uri];
int eqcnt = m_current.count(struri);
if (eqcnt > 1)
{
// 디렉토리 경우 생성함
if (vectdiff[DIFFFILE_FIELD::resource_type] == "1")
{
CCurretMap::iterator curr = m_current.find(struri);
r[SYNCFILE_FIELD::creation_date] = (*curr).second[CURRENT_FIELD::creation_date];
r[SYNCFILE_FIELD::file_lastmodified] = (*curr).second[CURRENT_FIELD::file_lastmodified];
r[SYNCFILE_FIELD::get_content_type] = (*curr).second[CURRENT_FIELD::get_content_type];
r[SYNCFILE_FIELD::flag] = FLAG_NSYNC;
break;
}
// 해당 파일 싱크 하지 않음(report log 기록)
CReportLog rlog;
if (rlog.Init(CServiceConfig::GetInstance()->GetLogPath()))
{
rlog.Write("WAR", "[%s] [The duplicate files.[uri :%s]]", PROG_NAME, struri.c_str());
}
break;
}
// diff된 uri == RCDB : 1개
if (eqcnt == 1 )
{
if (m_current.size() == 1)
{
CCurretMap::iterator curr = m_current.find(struri);
r[SYNCFILE_FIELD::creation_date] = (*curr).second[CURRENT_FIELD::creation_date];
r[SYNCFILE_FIELD::get_content_type] = (*curr).second[CURRENT_FIELD::get_content_type];
r[SYNCFILE_FIELD::file_lastmodified] = (*curr).second[CURRENT_FIELD::file_lastmodified];
if ((*curr).second[CURRENT_FIELD::resource_type] == "1" ||
(*curr).second[CURRENT_FIELD::file_lastmodified] != vectdiff[DIFFFILE_FIELD::file_lastmodified])
{
r[SYNCFILE_FIELD::flag] = FLAG_NSYNC;
break;
}
r[SYNCFILE_FIELD::flag] = FLAG_SYNC;
r[SYNCFILE_FIELD::host_name] = (*curr).second[CURRENT_FIELD::host_name];
//r[SYNCFILE_FIELD::src_uri] = current[CURR_FIELD::uri];
r[SYNCFILE_FIELD::src_filename_hash] = (*curr).second[CURRENT_FIELD::filename_hash];
}
else
{
// copy
vector<string> src, self;
int64_t o = 0, n = 0;
CCurretMap::iterator i = m_current.begin();
while (i != m_current.end()) // Loop until end is reached.
{
//cout << (*i).first << " -> " << (*i).second << "\n";
if ((*i).first != struri)
{
// get_lastmodified 작은 값을 원본 소스 취급
n = convert((*i).second[CURRENT_FIELD::get_lastmodified]);
if (o > n || o == 0)
{
o = n;
src = (*i).second;
}
}
else
{
self = (*i).second;;
}
++i;
}
if (src.empty())
{
CReportLog rlog;
if (rlog.Init(CServiceConfig::GetInstance()->GetLogPath()))
{
rlog.Write("WAR", "[%s] [Not Found Source File.[uri :?? => %s]]", PROG_NAME, struri.c_str());
}
r.clear();
break;
}
if (n < convert(self[CURRENT_FIELD::get_lastmodified]))
{
r[SYNCFILE_FIELD::flag] = FLAG_COPY;
r[SYNCFILE_FIELD::src_uri] = src[CURRENT_FIELD::uri];
r[SYNCFILE_FIELD::host_name] = src[CURRENT_FIELD::host_name];
r[SYNCFILE_FIELD::src_filename_hash] = src[CURRENT_FIELD::filename_hash];
r[SYNCFILE_FIELD::creation_date] = self[CURRENT_FIELD::creation_date];
r[SYNCFILE_FIELD::get_content_type] = self[CURRENT_FIELD::get_content_type];
}
else
{
//r.clear();
r[SYNCFILE_FIELD::flag] = FLAG_COPY;
r[SYNCFILE_FIELD::host_name] = src[CURRENT_FIELD::host_name];
r[SYNCFILE_FIELD::src_filename_hash] = src[CURRENT_FIELD::filename_hash];
r[SYNCFILE_FIELD::creation_date] = self[CURRENT_FIELD::creation_date];
r[SYNCFILE_FIELD::get_content_type] = self[CURRENT_FIELD::get_content_type];
}
}
break;
}
if (eqcnt == 0)
{
// move
vector<string> dest;
CCurretMap::iterator i = m_current.begin();
if (m_current.size() == 1)
{
r[SYNCFILE_FIELD::flag] = FLAG_MOVE;
dest = (*i).second;
r[SYNCFILE_FIELD::src_uri] = r[SYNCFILE_FIELD::uri];
r[SYNCFILE_FIELD::uri] = dest[CURRENT_FIELD::uri];
r[SYNCFILE_FIELD::host_name] = dest[CURRENT_FIELD::host_name];
r[SYNCFILE_FIELD::file_lastmodified] = dest[CURRENT_FIELD::file_lastmodified];
r[SYNCFILE_FIELD::src_filename_hash] = dest[CURRENT_FIELD::filename_hash];
r[SYNCFILE_FIELD::creation_date] = dest[CURRENT_FIELD::creation_date];
r[SYNCFILE_FIELD::get_content_type] = dest[CURRENT_FIELD::get_content_type];
}
else
{
// 2개 이상 존재 어떤 것으로 이동된지 알수 없으므로 해당 파일 삭제 처리
r[SYNCFILE_FIELD::flag] = FLAG_DEL;
}
break;
}
} while (false);
return r;
}
bool CValidation::WriteSyncFile(vector<string>& outdata)
{
// flag|uri|filename_hash|resource_type|get_content_length|file_lastmodified|deleted_yn|host_name|creation_date|get_content_type|src_uri|slave_tran_id|uri_ignore_case
CDataFile syncfile;
syncfile.Init(m_syncinfo.file_sync_name.c_str(), false);
vector<string>::size_type n = SYNCFILE_FIELD::MAX_FILED - (SYNCFILE_FIELD::MAX_FILED - SYNCFILE_FIELD::src_filename_hash);
string w;
for (vector<string>::size_type i = 0; i < n; ++i)
{
if (i> 0)
w += "|";
if( i == SYNCFILE_FIELD::filename_hash)
{
if (outdata[SYNCFILE_FIELD::src_filename_hash].size())
{
w += outdata[SYNCFILE_FIELD::src_filename_hash];
continue;
}
}
w += outdata[i];
}
bool r = syncfile.Write("%s", w.c_str());
return r;
}
+76
View File
@@ -0,0 +1,76 @@
/****************************************************************************
Validation Class
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
*****************************************************************************/
#ifndef __VALIDATION_H__
#define __VALIDATION_H__
#include <vector>
# if __GNUC__ >= 3
# include <ext/hash_map>
using namespace __gnu_cxx;
# else
# include <hash_map>
# endif
#include "SyncList.h"
#include "Database.h"
struct hash_key2
{
size_t operator() (const string &k) const
{
return __stl_hash_string(k.c_str());
}
};
struct compare_key2
{
bool operator () (const string s1, const string &s2) const
{
return s1 == s2;
}
};
typedef hash_multimap<string, vector<string>, hash_key2, compare_key2> CCurretMap;
class CValidation
{
public:
CValidation(CSyncInfo& info, string data);
~CValidation();
// diff 된 데이터 검증
bool CheckValidation(bool is_master = true);
private:
// 현재 meta에서 input된 uri, hash에 대한 최신 상태 가져오기
bool GetCurrentMeta(DataBase* db, vector<string> & vectdiff, string &tblname, string &tran_id, bool useDisyplay);
// input 된 데이터와 현재 meta 데이터 비교
// sync file로 저장될 데이터를 리턴한다.
vector<string> CompareData(vector<string> & vectdiff, string &tran_id);
// outdata 를 sync file formate 맞게 쓰기함
bool WriteSyncFile(vector<string>& outdata);
// filename_hash 리턴
string GetFullFilenameHash(vector<string> & vectdiff, string &tran_id);
CSyncInfo m_syncinfo;
string m_diffdata;
CCurretMap m_current;
};
#endif /* __VALIDATION_H__ */
+470
View File
@@ -0,0 +1,470 @@
/***************************************************************************
Work Pool
-----------------------------------------
begin : 2011/10/27
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#include "WorkPool.h"
#include "Validation.h"
#include "Logger.h"
#include "Util.h"
#include <unistd.h>
#include <errno.h>
#include <iostream>
#define DIFF_SPLIT_CNT 6 // diff file format split count
#define SLEEP_TIME 1000 //microsecond
#define _LOGMSG(level, msg) \
_LOG(level, "%s", msg.str().c_str());\
msg.str("");
CWorkPool *CWorkPool::m_inst = NULL;
// work
CWork::CWork()
: m_step(-5), m_exit(false), m_enablesync("0000")
{
m_nofi = NULL;
pthread_mutex_init(&m_workmutex, NULL);
pthread_cond_init(&m_workcond, NULL);
}
CWork::~CWork()
{
pthread_mutex_destroy(&m_workmutex);
pthread_cond_destroy(&m_workcond);
}
void* CWork::working(void * pdata)
{
ostringstream msg;
CWork* pObject = reinterpret_cast<CWork *>(pdata);
//pthread_detach( pthread_self() );
#ifdef _DEBUG
cout << "work::working start - " << pthread_self() << endl;
#endif // _DEBUG
while(pObject->m_exit == false)
{
pthread_mutex_lock(&pObject->m_workmutex);
pObject->m_step = -1;
int err = pthread_cond_wait(&pObject->m_workcond, &pObject->m_workmutex);
pObject->m_step = 0;
#ifdef _DEBUG
cerr << "work::working - signal : " << pthread_self() << "," << pObject->m_exit << endl;
#endif // _DEBUG
if ( err == 0 && pObject->m_exit == false)
{
pObject->m_step = 1;
short success = 0;
msg << "Valid Start : " << pObject->m_rundata;
_LOGMSG(LDBG, msg);
do
{
// add uri
pObject->m_step = 2;
if (CWorkPool::getInstance()->addworkuri(pObject->m_syncinfo.sync_master,
pObject->m_rundata,
pObject->m_workuri, true)
== false)
{
LOG(LERR, "Failed work uri map add [%s].", pObject->m_rundata.c_str());
success = -1;
break;
}
// validation
pObject->m_step = 3;
CValidation v(pObject->m_syncinfo, pObject->m_rundata);
if (v.CheckValidation() == false)
success = -2;
pObject->m_step = 4;
} while (false);
if(pObject->m_nofi)
{
pObject->m_step = 6;
#ifdef _DEBUG
cerr << "Work Thread Notify Call."<< endl;
#endif //_DEBUG
pObject->m_nofi(pObject->m_parm, success);
}
pObject->m_step = 7;
msg << "Valid End : " << pObject->m_rundata;
_LOGMSG(LDBG, msg);
if(pObject->m_workuri.size())
{
CWorkPool::getInstance()->delworkuri(pObject->m_workuri);
}
CWorkPool::getInstance()->ReleaseWork(pObject);
}
else
{
pObject->m_step = -3;
if(pObject->m_exit == false)
{
pObject->m_step = -4;
msg << "Work Thread Error.";
//LOGACERR(LCRT, msg);
LOG(LCRT, msg.str().c_str());
}
else
{
pObject->m_step = -5;
/* nothing -- signal exit */
}
}
pObject->m_step = -6;
pthread_mutex_unlock(&pObject->m_workmutex);
}
#ifdef _DEBUG
cerr << "work::working end - " << pthread_self() << endl;
#endif // _DEBUG
return NULL;
}
bool CWork::init()
{
int nRet = pthread_create(&m_thread, 0, CWork::working, this);
if( nRet )
{
cerr << "Thread create failed.: errno: " << errno << endl;
return false;
}
sleep(0);
return true;
}
void CWork::setexit(bool v)
{
m_exit = v;
pthread_mutex_lock(&m_workmutex);
pthread_cond_signal(&m_workcond);
pthread_mutex_unlock(&m_workmutex);
}
bool CWork::run(CSyncInfo info, string rundata, work_notifyfn nofi /* = NULL */, void * parm /* = NULL */)
{
m_syncinfo = info;
m_rundata = rundata;
m_parm = parm;
m_nofi = nofi;
ostringstream msg;
//msg << "Work Run : TranID(" <<m_master.m_tranid <<"=>" << m_slave.m_tranid <<
// "), UserSeq(" << m_master.m_userseq << "=>" << m_slave.m_userseq << ")" <<
// ", Sync Data - " << m_rundata;
//_LOGACOUT(LDBG, msg);
//spin lock
int64_t looptime = 0;
do
{
solusleep(SLEEP_TIME);
looptime += SLEEP_TIME;
if( looptime > (10 *1000*1000) )
{
msg << "Work Loop.... 10 sec over :" << m_rundata;
LOG(LNOT, "%s", msg.str().c_str());
break;
}
} while (m_step != -1);
pthread_cond_signal(&m_workcond);
return true;
}
// work pool
CWorkPool::CWorkPool()
{
pthread_mutex_init(&m_mutex, NULL);
pthread_mutex_init(&m_mutexuri, NULL);
pthread_mutex_init(&m_mutextry, NULL);
}
CWorkPool::~CWorkPool()
{
pthread_mutex_destroy(&m_mutex);
pthread_mutex_destroy(&m_mutexuri);
pthread_mutex_destroy(&m_mutextry);
}
CWorkPool* CWorkPool::getInstance()
{
if(CWorkPool::m_inst == NULL)
{
CWorkPool::m_inst = new CWorkPool();
}
return CWorkPool::m_inst;
}
void CWorkPool::release()
{
if( CWorkPool::m_inst != NULL)
{
m_inst->DestroyPool();
delete CWorkPool::m_inst;
CWorkPool::m_inst = NULL;
}
}
int CWorkPool::CreatePool( int poolcnt /* = 10 */ )
{
// pool
for(int i=0 ; i < poolcnt ; i++)
{
srand ( time(NULL) );
CWork * w = new CWork();
if( w->init() == false)
{
delete w;
break;
}
//m_pool.insert(make_pair(w, CWorkPool::NOTWORK));
m_pool.insert(pair<CWork *, short>(w, CWorkPool::NOTWORK));
}
if( m_pool.size() < static_cast<unsigned int>(poolcnt) )
{
DestroyPool();
return -1;
}
return 0;
}
int CWorkPool::DestroyPool()
{
map<CWork*, short>::iterator iter;
while(m_pool.size() > 0 )
//for( iter = m_pool.begin(); !m_pool.empty()&& iter != m_pool.end(); iter++ )
{
iter = m_pool.begin();
if(iter->second == CWorkPool::NOTWORK)
{
CWork *data = static_cast<CWork *>(iter->first);
data->setexit(true);
pthread_join(*data->getworkhandle(), NULL);
m_pool.erase(iter);
delete (CWork *)data;
}
//sleep(1);
}
//m_pool.clear();
return m_pool.size();
}
CWork* CWorkPool::GetWorkPool(int timeout)
{
CWork * r = NULL;
int64_t usetime = 0;
int64_t out = timeout * 1000 * 1000;
do
{
pthread_mutex_lock(&m_mutex);
map<CWork*, short>::iterator iter;
for( iter = m_pool.begin(); !m_pool.empty()&& iter != m_pool.end(); iter++ )
{
if(iter->second == CWorkPool::NOTWORK)
{
iter->second = CWorkPool::WORKING;
r = iter->first;
break;
}
}
if( r == NULL )
{
solusleep(SLEEP_TIME);
if( out > 0 )
{
usetime += SLEEP_TIME;
if( usetime > out)
{
ostringstream msg;
msg << "Get Work Pool Timeout.";
//LOGACERR(LERR, msg);
LOG(LERR, msg.str().c_str());
pthread_mutex_unlock(&m_mutex);
break;
}
}
}
pthread_mutex_unlock(&m_mutex);
} while (r == NULL);
return r;
}
void CWorkPool::ReleaseWork(CWork *w)
{
//pthread_mutex_lock(&m_mutex);
map<CWork*, short>::iterator iter = m_pool.find(w);
if( iter != m_pool.end() )
{
if(iter->second==CWorkPool::WORKING)
iter->second=CWorkPool::NOTWORK;
}
else
{
ostringstream msg;
msg << "unknown work pool.";
//LOGACERR(LWAR, msg);
LOG(LWAR, msg.str().c_str());
}
//pthread_mutex_unlock(&m_mutex);
}
void CWorkPool::printstatus(string prefixed)
{
int w = 0, n = 0;
map<CWork*, short>::iterator iter;
for( iter = m_pool.begin(); !m_pool.empty()&& iter != m_pool.end(); iter++ )
{
if(iter->second == CWorkPool::NOTWORK)
{
++n;
}
else
{
++w;
}
}
ostringstream msg;
if( prefixed.empty() == false )
msg << "[" << prefixed << "]";
msg << "Work Pool - " <<"total : " << m_pool.size() << "(" << w <<
"/" << n << ")";
//_LOGACOUT(LINF, msg);
_LOG(LINF, msg.str().c_str());
}
void CWorkPool::printstatusex(string prefixed)
{
ostringstream msg;
map<CWork*, short>::iterator iter;
for( iter = m_pool.begin(); !m_pool.empty()&& iter != m_pool.end(); iter++ )
{
if(iter->second == CWorkPool::NOTWORK)
{
msg << "Work Pool(NOTWORK) - " << iter->first->getrundata() << "," <<
iter->first->getsetp();
}
else
{
msg << "Work Pool(WORKING) - " << iter->first->getrundata() << "," <<
iter->first->getsetp();
}
if( prefixed.empty() == false )
msg << "[" << prefixed << "]";
//_LOGACOUT(LINF, msg);
_LOG(LINF, "%s", msg.str().c_str());
}
}
bool CWorkPool::addworkuri(string tranid, string uri, string &workuri,
bool sourcedata, int timeout)
{
bool r = true;
string key;
ostringstream msg;
if(sourcedata)
{
vector<string> strSplit;
StringSplit(uri, "|", strSplit, true);
if(strSplit.size() != DIFF_SPLIT_CNT)
{
pthread_mutex_unlock(&m_mutexuri);
msg << "addworkuri sync file string split caution : [" << uri << "]";
//LOGACERR(LERR, msg);
LOG(LERR, "%s", msg.str().c_str());
return false;
}
key = "/" + tranid + strSplit[0] + "|[Size=" + strSplit[2] + "]";
}
else
key = "/"+ tranid + uri;
int64_t out = 0, usetime = 0;
out = timeout * 1000 * 1000;
TRY_ADD_WORK_URI:
pthread_mutex_lock(&m_mutexuri);
workurimap::iterator find = m_workurimap.find(key);
if (find != m_workurimap.end())
{
pthread_mutex_unlock(&m_mutexuri);
msg << "Work uri duplication occurred and to add work uri try again." << key;
_LOGMSG(LDBG, msg);
// try
solusleep(SLEEP_TIME);
usetime += SLEEP_TIME;
if(timeout > 0 && usetime > out)
{
r = false;
msg << "Work adduri Timeout." << key;
//LOGACERR(LERR, msg);
LOG(LERR, "%s", msg.str().c_str());
pthread_mutex_unlock(&m_mutexuri);
return false;
}
goto TRY_ADD_WORK_URI;
}
else
{
// add
workuri = key;
m_workurimap[key] = 1;
//pthread_mutex_unlock(&m_mutexuri);
}
pthread_mutex_unlock(&m_mutexuri);
return r;
}
bool CWorkPool::delworkuri(string key)
{
pthread_mutex_lock(&m_mutexuri);
// deleted
m_workurimap.erase(key);
pthread_mutex_unlock(&m_mutexuri);
return true;
}
+128
View File
@@ -0,0 +1,128 @@
/***************************************************************************
Work Pool
-----------------------------------------
begin : 2011/10/27
copyright : (C) 2011 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 3.0.1
CopyRight(C) 2011 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __WORK_POOL_H__
#define __WORK_POOL_H__
#include <string>
#include <map>
using namespace std;
# if __GNUC__ >= 3
# include <ext/hash_map>
using namespace __gnu_cxx;
# else
# include <hash_map>
# endif
typedef int (*work_notifyfn)(void *object, short success);
struct hash_key
{
size_t operator() (const string &k) const
{
return __stl_hash_string(k.c_str());
}
};
struct compare_key
{
bool operator () (const string s1, const string &s2) const
{
return s1 == s2;
}
};
typedef hash_map<string, short, hash_key, compare_key> workurimap;
#include "SyncList.h"
class CWork
{
public:
CWork();
~CWork();
bool init();
bool run(CSyncInfo info, string rundata, work_notifyfn nofi = NULL, void * parm = NULL);
void setexit(bool v);
void setenablesync(const char* szOn) {m_enablesync = szOn;}
pthread_t* getworkhandle() {return &m_thread;}
const char* getrundata() { return m_rundata.c_str(); }
int getsetp() { return m_step; }
private:
static void* working(void * pdata);
private:
int m_step;
bool m_exit;
string m_rundata;
CSyncInfo m_syncinfo;
string m_workuri;
work_notifyfn m_nofi;
void* m_parm;
string m_enablesync;
pthread_t m_thread;
pthread_cond_t m_workcond;
pthread_mutex_t m_workmutex;
};
class CWorkPool
{
public:
enum WORK_SATAUSE
{
WORKING = 0,
NOTWORK = 1
};
public:
static void init();
static CWorkPool* getInstance();
static void release();
int CreatePool( int poolcnt = 10 );
int DestroyPool();
bool addworkuri(string tranid, string uri, string &workuri,
bool sourcedata = false, int timeout = 5);
bool delworkuri(string key);
void printstatus(string prefixed = "");
void printstatusex(string prefixed = "");
CWork* GetWorkPool(int timeout = 0);
void ReleaseWork(CWork *w);
private:
CWorkPool();
~CWorkPool();
private:
static CWorkPool* m_inst;
pthread_mutex_t m_mutex;
pthread_mutex_t m_mutexuri;
pthread_mutex_t m_mutextry;
workurimap m_workurimap;
map<CWork*, short> m_pool;
};
#endif // __WORK_POOL_H__
+211
View File
@@ -0,0 +1,211 @@
#include "Worker.h"
#include "ProcessRename.h"
#include "ServiceConfig.h"
#include "Logger.h"
#include "ProcessStatus.h"
#include "Running.h"
#include "Scheduler.h"
#include "WorkPool.h"
#include "DBConnPool.h"
#include "ClientThread.h"
#include "Util.h"
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// Worker Process 의 main 함수
int WorkerMain( int listenSocket )
{
// Process Rename
set_ps_display(PROG_NAME": Worker [initialize process]", false);
// Set Signal Handler
SetSignalWorker();
// 로그 출력 순서가 Main 프로세스와 꼬일 수 있어... 1초간 대기 후 진행
sleep( 1 );
////////////////////////////////////////////////////////////////////
// 1. 각 모듈 생성 및 초기화.
// - 초기화 실패시 EXIT_FAILURE 를 return 하여 프로세스가 종료되도록 한다.
// 1.1 Run List 생성
// 스케줄링을 통해서 실행 시 시간 중복 구간을 방지 위해서 실행 중인 작업을 list로 관리하고 있음
CRunlist::init();
// 1.2 스케줄링 생성 및 구동
CScheduler scheduler;
scheduler.addtask(CScheduler::TIME_INTERVAL, CServiceConfig::GetInstance()->GetChkSmall());
scheduler.addtask(CScheduler::TIME_INTERVAL, CServiceConfig::GetInstance()->GetChkMiddle());
scheduler.addtask(CScheduler::TIME_ONCE, CServiceConfig::GetInstance()->GetChkOnce());
if (scheduler.run() != 0)
{
LOG( LERR, "Worker[%d] scheduler module run() failed.", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// 1.3 Work Pool 생성
if( CWorkPool::getInstance()->CreatePool( CServiceConfig::GetInstance()->GetWorkPoolCount() ) != 0 )
{
LOG(LERR, "Worker[%d] Work Pool Create() failed.", getpid());
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
// 1.4 DB Connection Pool 생성
CDataBaseInfo info;
info.m_hostaddr = CServiceConfig::GetInstance()->GetRcdbIp();
info.m_port = CServiceConfig::GetInstance()->GetRcdbPort();
info.m_dbname = CServiceConfig::GetInstance()->GetRcdbName();
info.m_user = CServiceConfig::GetInstance()->GetRcdbAcct();
info.m_pw = CServiceConfig::GetInstance()->GetRcdbAcctPw();
CMasterDBPool::init(info);
if (CMasterDBPool::getInstance()->CreatePool(CServiceConfig::GetInstance()->GetDbPoolCount()) <= 0)
{
LOG(LERR, "Worker[%d] DB Connection Pool module Create() failed.", getpid());
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CMasterDBPool module start.", getpid() );
// 1.5 Process status 모듈 생성 및 기동
CProcessStatus processStatus;
if( processStatus.Start() == false )
{
LOG( LERR, "Worker[%d]: Process status module start fail. => Worker Exit", getpid() );
return EXIT_FAILURE; // EXIT_FAILURE 를 반환하여 재생성 처리 방지
}
_LOG( LINF, "Worker[%d]: CProcessStatus module start.", getpid() );
// 모듈 초기화 및 기동 처리 완료.
/////////////////////////////////////////////////////////////////////
// Worker 프로세스의 작업 수행.
// Listen Socket 에 대한 Accept 대기 수행
int clientSocket;
struct sockaddr_in clientSockAddr;
socklen_t clientSockLen = sizeof( clientSockAddr );
char szClientIp[INET_ADDRSTRLEN];
CClientThread * pClientThread;
while( 1 )
{
clientSocket = accept( listenSocket, ( struct sockaddr * ) &clientSockAddr, &clientSockLen );
if( clientSocket == -1 )
{
// 오류발생시 해당 내역 로깅처리.
int errorNum = errno;
LOG( LERR, "Worker: client accept failed. [%d][%s]", errorNum, strerror( errorNum ) );
}
else
{
// 정상적인 Client 인 경우...
memset( szClientIp, 0x00, sizeof( szClientIp ) );
if( inet_ntop( AF_INET, &( clientSockAddr.sin_addr ), szClientIp, sizeof( szClientIp ) ) == NULL )
{
// 변환 실패시.. 0.0.0.0 으로 설정 처리.
snprintf( szClientIp, INET_ADDRSTRLEN - 1, "0.0.0.0" );
}
// client 를 처리할 Thread 객체 생성 후 기동 처리.
// - 해당 객체에서 thread 종료시 자동 delete 처리하므로.. 본 함수에서는 new 생성만 처리한다.
pClientThread = NULL;
pClientThread = new CClientThread( &processStatus, clientSocket, szClientIp );
if( pClientThread == NULL || pClientThread->Start() == false )
{
// Thread 기동 실패시..
if( pClientThread != NULL )
{
LOG( LERR, "client[%s] thread object start function fail.", szClientIp );
delete pClientThread;
pClientThread = NULL;
}
else
{
// Thead 객체 생성 실패시
LOG( LERR, "client[%s] thread object create fail.", szClientIp );
}
close( clientSocket );
}
}
} // while( 1 )
// Process 종료 처리.
_LOG( LINF, "Worker[%d] exit.. Good Bye..", getpid());
//잠시 대기 후 종료처리.
solusleep(500000);
// fork 를 수행한 함수에서 exit 함수 호출을 통한 종료처리
return EXIT_SUCCESS;
}
// Worker Process 종료 Signal 을 전달받은 경우 이를 처리하기 위한 함수.
// @param nSignalNumber [in] 발생한 시그널 Number
// @return void
static void SignalWorkerTerminate(int nSignalNumber)
{
// Signal Number 에 따른 로깅처리.
if( nSignalNumber == SIGTERM )
{
_LOG( LINF, "Worker[%d] process exit by user signal [SIGTERM]. Good Bye..", getpid() );
}
else
{
_LOG( LWAR, "Worker[%d] process exit by abnormal signal[%d], Good Bye..", getpid(), nSignalNumber );
}
solusleep(500000);
exit( EXIT_SUCCESS );
}
// Worker 프로세스 signal 처리 설정을 위한 함수
// @return void
void SetSignalWorker( void )
{
sigset_t set;
struct sigaction act;
memset(&act, 0x00, sizeof(act));
sigfillset( &set );
sigprocmask( SIG_SETMASK, &set, NULL );
sigfillset( &act.sa_mask );
/* 무시할 신호 목록 */
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 Controller 의 child process 종료에 대한 처리기 설정.
act.sa_handler = SIG_DFL; // 외부 프로세스 수행이 발생할 수 있는 경우 default 처리
//act.sa_handler = SIG_IGN; // 외부 프로세스 수행이 없는 경우.. SIG_IGN 처리
sigaction( SIGCHLD, &act, NULL);
/* 각종 에러나 사용자의 종료 신호 처리 */
act.sa_handler = SignalWorkerTerminate;
sigaction( SIGTERM, &act, NULL); /* kill -TERM 에 의한 프로세스 종료시 */
sigemptyset(&set); /* 신호 처리기 처리 설정 위한 블록 해제 */
sigprocmask(SIG_SETMASK, &set, NULL);
}
+45
View File
@@ -0,0 +1,45 @@
/***************************************************************************
rc_syncd Worker Header ( Worker.h )
-----------------------------------------
begin : 2014/09/02
copyright : (C) 2005 Solbox Inc.
author : Storage Dev Team
email : storage.sd@solbox.com
version : 3.4
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.
***************************************************************************/
#ifndef __RC_SYNCD_WORKER_H__
#define __RC_SYNCD_WORKER_H__
#ifdef __cplusplus
extern "C" {
#endif
// Worker Process 의 main 함수
// @param listenSocket command, data 송수신을 위한 TCP Listen socket
// @return EXIT_FAILURE 모듈 초기화 실패시
// EXIT_SUCCESS 그 외 정상 종료
int WorkerMain( int listenSocket );
// Worker 프로세스 signal 처리 설정을 위한 함수
// @return void
void SetSignalWorker( void );
/* Signal 처리를 위한 각 Signal Handler 함수는
* 다른 Code 에서 Include 처리시 Static 관련 문제로 인해
* 본 Header 에서 선언처리 하지 않음. cpp 에만 존재
*/
#ifdef __cplusplus
}
#endif
#endif /* __RC_SYNCD_WORKER_H__ */
+8
View File
@@ -0,0 +1,8 @@
#!/bin/sh
if [ $# -lt 3 ]; then
echo "$0 [master data] [slave data] [output file]"
exit 1
fi
diff $1 $2 | grep -iE "^>|^<" | sed -e 's|^> ||g' | sed -e 's|^< ||g' > $3
+66
View File
@@ -0,0 +1,66 @@
#****************************************************************************
# Makefile for rc_syncd test client
# -----------------------------------------
#
# begin : 2014/09/15
# copyright : (C) 2005 Solbox Inc.
# author : storage dev team
# email : storage.sd@solbox.com
# version : 3.4
#
# 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.
#*****************************************************************************
# Program info
PROG_NAME = test_client
# Compiler info
CC = /usr/bin/g++
CFLAGS = -Wall -O3 -g -Wimplicit -Wreturn-type -Wunused -Wuninitialized\
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
-fno-rtti -D_REENTRANT
LFLAGS =
DFLAGS = -D_DEBUG_ -DPROG_NAME=\"$(PROG_NAME)\"
# Enviroment
DIR_INCLUDE = -I./. -I./.. -I./../../lib
DIR_LIB =
OBJ = ../../lib/Logger.o ../../lib/BaseSocket.o ../RcSyncdClientSocket.o main_client.o
LIBS =
APP = $(PROG_NAME)
############################
all:$(APP)
sync
%.o: %.cpp
$(CC) $(CFLAGS) -o $@ -c $^ $(DFLAGS) $(DIR_INCLUDE)
$(PROG_NAME): $(OBJ)
$(CC) $(LFLAGS) -o $@ $^ $(DFLAGS) $(DIR_LIB) $(LIBS)
clean:
-rm -f *.o core *.out *.log ./../*.o
-rm -f $(APP)
sync
install : $(APP)
sync
# End of Makefile
@@ -0,0 +1,140 @@
#include <stdlib.h>
#include <string>
#include <iostream>
#include "Logger.h"
#include "RcSyncdClientSocket.h"
#define LOG_PATH "/user/service/logs"
#define RC_SYNCD_HOST "127.0.0.1" // rc_syncd host
#define RC_SYNCD_LISTEN_PORT 14002 // rc_syncd 의 Listen Port
int main()
{
// Test 용 코드 정의
CReqMasterData request;
request.sync_type = SYNC_TYPE::NORMAL;
request.start_time = "20140101125900";
request.end_time = "20140915000000";
//request.one_service = "501";
////////////// Test 코드 //////////////
// Log 처리용 객체 생성 및 초기화.
if( CLogger::Init( PROG_NAME, LOG_PATH, LDBG ) == false )
{
std::cout << "[error] Logger object created failed.." << std::endl;
return 0;
}
// Start logging
_LOG( LINF, "rc_syncd test client Start");
// rc_syncd 와 통신을 처리할 client socket 객체 생성 및 접속
CRcSyncdClientSocket client;
if( client.ConnectTarget( RC_SYNCD_HOST, RC_SYNCD_LISTEN_PORT ) == false )
{
// Source ftsd 으로 접속 실패시
_LOG( LERR, "rc_syncd connect fail. [%s][%d]", RC_SYNCD_HOST, RC_SYNCD_LISTEN_PORT );
return EXIT_FAILURE;
}
// rc_syncd 로 sync 요청 전달
if( client.SendSyncRequest( request ) == false )
{
// 전송 실패시
_LOG( LERR, "Sync request send fail");
return EXIT_FAILURE;
}
// 요청에 대한 응답 대기
int nTimeout = 5; // 응답 대기 시간 ( sec )
int nResult = 0;
bool bResultSuccess; // 처리 결과의 성공/실패 여부를 저장하기 위한 변수.
std::string strMessage; // 수신 메시지 저장 변수.
// 루프를 돌면서 요청에 대한 응답을 대기
while(1)
{
// nTimeout 에 지정된 시간동안 응답대기
nResult = client.GetSyncResult( nTimeout, bResultSuccess, strMessage);
if( nResult == -1 )
{
// Socket 통신 관련 오류 또는 접속 종료가 발생한 경우.
// 해당 내역 로깅 및 루프 종료
_LOG( LERR, "Response wait fail by socket" );
break;
}
else if( nResult == 0 )
{
// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우 => Allive Check 패킷 한번 쏘고 다시 Loop 로
if( client.SendAliveCheck() == false )
{
// Alive 전송 실패시 => Socket 종료 및 오류가 발생한 경우임.
_LOG( LERR, "Response wait fail by socket2");
break;
}
// 정상적인 경우 다시 응답대기.
continue;
}
else if( nResult == 1 )
{
// 요청에 대한 처리결과 정보가 수신된 경우.
// 해당 정보 로깅처리.
if( bResultSuccess == true )
{
// 요청에 대한 처리가 정상적으로 처리된 경우
_LOG( LINF, "Sync result SUCCESS.");
}
else
{
// 오류 발생시
_LOG( LINF, "Sync result ERROR [%s]", strMessage.c_str() );
}
break; // 응답을 받았으니 응답 대기 루프 종료
}
else
{
// Replication 요청에 대한 응답패킷이 아닌 경우.
// 해당 패킷은 무시하고 다시 Loop 로
continue;
}
}
sleep(1);
client.Close();
// End logging
_LOG( LINF, "test client end");
_LOG( LINF, "-------------------------------------------------------------------------------" );
CLogger::Exit();
return EXIT_SUCCESS;
}
@@ -0,0 +1,12 @@
#!/bin/sh
CMD="$1"
SLEEP="$2"
while [ 1 ]; do
date
eval $CMD
echo " "
sleep $SLEEP
done