360 lines
8.0 KiB
C++
360 lines
8.0 KiB
C++
|
|
// CHG 2020-11-09 huibong FreeBSD 12.2 컴파일시 오류 발생 관련 header 추가 (#33297)
|
|
// - unlink(), sleep() 사용 컴파일 오류 발생 관련 #include <unistd.h> 구문 추가
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include "LogFileProcessThread.h"
|
|
#include "HostInfo.h"
|
|
|
|
/// 테스트를 위해 값을 4,7에서 0, 23로 변경했음.
|
|
#define TIME_SCOPE_FIRST_BEGIN 0
|
|
#define TIME_SCOPE_FIRST_END 23
|
|
#define TIME_SCOPE_BEGIN 4
|
|
#define TIME_SCOPE_END 7
|
|
|
|
using namespace std;
|
|
|
|
CLogFileProcessThread::CLogFileProcessThread(string myname)
|
|
{
|
|
// 멤버 변수 초기화
|
|
m_threadHandle = 0;
|
|
|
|
m_szMyName = myname;
|
|
m_nToday = GetToday();
|
|
m_TimeScope.begin = TIME_SCOPE_FIRST_BEGIN;
|
|
m_TimeScope.end = TIME_SCOPE_FIRST_END;
|
|
SetStatus( LFP_READY );
|
|
bFirst = true;
|
|
}
|
|
|
|
CLogFileProcessThread::~CLogFileProcessThread()
|
|
{
|
|
|
|
}
|
|
|
|
bool CLogFileProcessThread::ThreadInit(const sig_atomic_t *sighandle)
|
|
{
|
|
m_sighandle = sighandle;
|
|
|
|
return true;
|
|
}
|
|
|
|
int CLogFileProcessThread::Rand( uint32_t max, uint32_t key )
|
|
{
|
|
srand( time(NULL) + key );
|
|
return ( rand() % max );
|
|
}
|
|
|
|
bool CLogFileProcessThread::IsBeginWork( struct tm& now )
|
|
{
|
|
|
|
|
|
int status = GetStatus();
|
|
|
|
switch( status )
|
|
{
|
|
/// READY 상태는 현재 시간이 작업 구간에 있는지를 확인 할 수 있는 상태이다.
|
|
case LFP_READY:
|
|
return IsInWorkTimeScope( now.tm_hour );
|
|
|
|
/// DONE 상태는 오늘 이미 작업을 마쳤기 때문에 날자가 변경 되었는지를 확인해서
|
|
/// 날자가 변경 되었으면 상태를 READY로 변경해서 작업 시간에 작업을 다시 할 수 있도록 한다.
|
|
case LFP_DONE:
|
|
if( IsChangedDay( now.tm_mday ) == true )
|
|
{
|
|
LOG( LDEV1, "call SetStatus( LFP_READY )" );
|
|
SetStatus( LFP_READY );
|
|
m_nToday = now.tm_mday;
|
|
}
|
|
break;
|
|
|
|
/// SLEEP, WORKING 상태를 이미 작업이 시작된 상태로 IsBeginWork() 함수가 외부에 노출 되지 않았기 때문에
|
|
/// 정상적인 경우에는 SLEEP, WORKING 상태에서 IsBeginWork() 함수가 호출 될 수 없다.
|
|
case LFP_SLEEP:
|
|
case LFP_WORKING:
|
|
default:
|
|
LOG( LWAR, "%s is invalid status.", GetStrStatus( GetStatus() ).c_str() );
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CLogFileProcessThread::IsInWorkTimeScope( int hour )
|
|
{
|
|
|
|
|
|
if( hour >= m_TimeScope.begin && hour < m_TimeScope.end )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
void CLogFileProcessThread::SetStatus( LogFileProcessStatus status )
|
|
{
|
|
switch( status )
|
|
{
|
|
case LFP_READY:
|
|
case LFP_SLEEP:
|
|
case LFP_WORKING:
|
|
case LFP_DONE:
|
|
_LOG( LINF, "change status of %s %s to %s.", m_szMyName.c_str(), GetStrStatus( m_nStatus ).c_str(), GetStrStatus( status ).c_str() );
|
|
m_nStatus = status;
|
|
break;
|
|
|
|
default:
|
|
_LOG( LERR, "can't set status of CLogFileProcess because of invalid status(%d)", status );
|
|
}
|
|
}
|
|
|
|
string CLogFileProcessThread::GetStrStatus( LogFileProcessStatus status )
|
|
{
|
|
string res = "";
|
|
|
|
switch( status )
|
|
{
|
|
case LFP_READY:
|
|
res = "READY";
|
|
break;
|
|
|
|
case LFP_SLEEP:
|
|
res = "SLEEP";
|
|
break;
|
|
|
|
case LFP_WORKING:
|
|
res = "WORKING";
|
|
break;
|
|
|
|
case LFP_DONE:
|
|
res = "DONE";
|
|
|
|
default:
|
|
res = "UNKNOWN";
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
bool CLogFileProcessThread::IsChangedDay( int day )
|
|
{
|
|
|
|
|
|
bool res = ( m_nToday != day ? true : false );
|
|
|
|
|
|
|
|
return res;
|
|
}
|
|
|
|
bool CLogFileProcessThread::IsDirectory( string path )
|
|
{
|
|
|
|
|
|
struct stat dirStat;
|
|
|
|
if( lstat ( path.c_str(), &dirStat ) != 0 )
|
|
{
|
|
LOG( LERR, "lstat failed. check path(%s).", path.c_str() );
|
|
return false;
|
|
}
|
|
|
|
// 해당 정보가 Directory 가 아닌 경우
|
|
if( S_ISDIR( dirStat.st_mode ) == false )
|
|
{
|
|
LOG( LDBG, "%s is not directory.", path.c_str() );
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CLogFileProcessThread::Unlink( string filename )
|
|
{
|
|
|
|
|
|
if( IsDirectory( filename ) == true )
|
|
{
|
|
LOG( LWAR, "can't unlink file(%s) because %s is direcotry.", filename.c_str(), filename.c_str() );
|
|
|
|
return false;
|
|
}
|
|
|
|
int error = unlink( filename.c_str() );
|
|
|
|
if( error == -1 )
|
|
{
|
|
LOG( LWAR, "can't unlink file(%s). errno=%d, error=%s",
|
|
filename.c_str(), error, strerror( error ) );
|
|
|
|
return false;
|
|
}
|
|
|
|
_LOG( LINF, "unlinke file: %s", filename.c_str() );
|
|
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
int CLogFileProcessThread::GetMaxSleepSecond( struct tm& now )
|
|
{
|
|
if( IsInWorkTimeScope( now.tm_hour ) == false )
|
|
{
|
|
LOG( LWAR, "can't get sleep time because of invalid hour(%d).", now.tm_hour );
|
|
return -1;
|
|
}
|
|
|
|
/// (현재 시간 + 1분)에서 (m_TimeScope.end - 1 )시 59분까지의 남은 분을 계산한다. 초 단위 생략.
|
|
/// (남은 분 * 60)으로 초 단위로 환산한 범위에서 random 한 값을 반환 한다.
|
|
|
|
// 우선 시간을 빼고 분만 가지고 계산.
|
|
// 58분 경우 59로 계산 되므로 해당 값이 '0'되는 것을 방지 하기 위해서 59 => 60으로 변경 한다.
|
|
int min = 60 - ( now.tm_min + 1 );
|
|
if( min < 0 )
|
|
{
|
|
LOG( LERR, "can't get sleep time because invalid time[%02d:%02d:%02d].",
|
|
now.tm_hour, now.tm_min, now.tm_sec );
|
|
return -1;
|
|
|
|
}
|
|
|
|
/// 시간을 계산해서 분에 더한다.
|
|
int hour = ( m_TimeScope.end - 1 ) - now.tm_hour;
|
|
if( hour < 0 )
|
|
{
|
|
LOG( LERR, "can't get sleep time because invalid time[%02d:%02d:%02d].",
|
|
now.tm_hour, now.tm_min, now.tm_sec );
|
|
return -1;
|
|
}
|
|
|
|
min += (hour * 60);
|
|
|
|
/// 분을 초로 환산한다.
|
|
int sec = min * 60;
|
|
|
|
return sec;
|
|
}
|
|
|
|
|
|
int CLogFileProcessThread::GetToday()
|
|
{
|
|
time_t timestamp = time( NULL );
|
|
struct tm now;
|
|
localtime_r( ×tamp, &now );
|
|
|
|
return now.tm_mday;
|
|
}
|
|
|
|
|
|
///@brief 이 함수에서 컨트롤 로직이 표현된다.
|
|
void CLogFileProcessThread::Execute()
|
|
{
|
|
time_t timestamp;
|
|
struct tm now;
|
|
|
|
while (*m_sighandle == 0)
|
|
{
|
|
/// 현재 시각 확인
|
|
timestamp = time( NULL );
|
|
localtime_r( ×tamp, &now );
|
|
|
|
/// 작업 시작 여부 확인.
|
|
if( IsBeginWork( now ) == false )
|
|
{
|
|
LOG( LDEV1, "I'm sleeping now for 1 minute." );
|
|
|
|
/// 작업을 하지 않아야 하면 1분 대기.
|
|
sleep( 60 ); /// 60초
|
|
continue;
|
|
}
|
|
|
|
/// 여기서 부터 작업 시작.
|
|
|
|
/// 현재 시간부터 TIME_SCOPE_END까지 최대 대기 할 수 있는 시간을 초 단위로 가지고 온다.
|
|
int maxSleepSecond = GetMaxSleepSecond( now );
|
|
if( maxSleepSecond < 0 )
|
|
{
|
|
LOG( LERR, "invalid max sleep time. current time = [%02d:%02d:%02d].",
|
|
now.tm_hour, now.tm_min, now.tm_sec );
|
|
continue;
|
|
}
|
|
|
|
/// 0 ~ maxSleepSecond 사이의 랜덤한 값을 가지고 온다.
|
|
CHostInfo hostInfo;
|
|
int sleeptime = Rand( maxSleepSecond, hostInfo.GetHostNumber() );
|
|
|
|
_LOG( LINF, "CLogFileProcess will begin work after %d seconds.", sleeptime );
|
|
|
|
if ( bFirst == true )
|
|
sleeptime = 5;
|
|
|
|
/// 위에서 계산한 sleeptime 만큼 대기한 후에 실제 작업 시작.
|
|
LOG( LDEV1, "call SetStatus( LFP_SLEEP )" );
|
|
SetStatus( LFP_SLEEP );
|
|
sleep( sleeptime );
|
|
|
|
/// 여기서 부터 실제 작업 시작.
|
|
/// 실제 작업 구현은 CLogFileProcess를 상속받은 자식 클래스에서 구현한다.
|
|
LOG( LDEV1, "call SetStatus( LFP_WORKING )" );
|
|
SetStatus( LFP_WORKING );
|
|
if( DoWork() == false )
|
|
{
|
|
LOG( LWAR, "log file process failed." );
|
|
}
|
|
|
|
// DoWork가 한번 이상 수행 되면 아래이 시간 되어야 한다.
|
|
bFirst = false;
|
|
|
|
// 새벽 4~7시 사이...
|
|
m_TimeScope.begin = TIME_SCOPE_BEGIN;
|
|
m_TimeScope.end = TIME_SCOPE_END;
|
|
/// 작업이 끝나면 작업의 성공 여부에 상관 없이 상태를 LFP_DONE로 변경한다.
|
|
LOG( LDEV1, "call SetStatus( LFP_DONE )" );
|
|
SetStatus( LFP_DONE );
|
|
}
|
|
|
|
LOG(LERR, "Thread Terminated...");
|
|
}
|
|
|
|
void* CLogFileProcessThread::EntryPoint(void* arg)
|
|
{
|
|
CLogFileProcessThread* pObject = reinterpret_cast<CLogFileProcessThread *>(arg);
|
|
pthread_detach( pthread_self() );
|
|
|
|
pObject->Execute();
|
|
|
|
pObject->m_threadHandle = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool CLogFileProcessThread::Start()
|
|
{
|
|
int nRet = ::pthread_create(&m_threadHandle, 0, CLogFileProcessThread::EntryPoint, this);
|
|
|
|
if( nRet )
|
|
{
|
|
LOG(LERR, "Thread create failed.: errno:%d errmsg:%s", errno, strerror(errno));
|
|
return false;
|
|
}
|
|
LOG(LDBG, "Thread create succeed");
|
|
sleep(0);
|
|
return true;
|
|
}
|
|
|