// CHG 2020-11-09 huibong FreeBSD 12.2 컴파일시 오류 발생 관련 header 추가 (#33297) // - unlink(), sleep() 사용 컴파일 오류 발생 관련 #include 구문 추가 #include #include #include #include #include #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(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; }