base
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
|
||||
Revision 1463
|
||||
-------------------
|
||||
수정일 : 2019-08-30
|
||||
수정자 : 유희곤
|
||||
|
||||
- CHG: cls garbage 삭제시 access time check 관련 기능 개선 (#32774)
|
||||
- garbage content 의 안전한 삭제 처리 관련 방어 기간을 기존 7일 -> 2일로 변경 처리
|
||||
- -f 옵션 사용시 access time check 수행없이 modify time 만은 check 하여 unlink 처리토록 수정
|
||||
- batch 관련 옵션을 -b 로 변경 처리
|
||||
- 로깅 관련 가독성 향상 처리
|
||||
|
||||
- CHG: 소스 파일 형식 변경
|
||||
- 장비에서 소스 파일 깨짐을 방지하기 위해 인코딩 형식 변경.
|
||||
- "서명있는 UTF-8" 인코딩으로 소스 파일 변경시 FreeBSD 6.X 의 gcc 3.4.6 컴파일러에서 오류 발생하여 실패
|
||||
- 따라서 모든 소스 파일을 BOM 이 없는 "서명없는 UTF-8" 인코딩으로 변경 처리함.
|
||||
|
||||
|
||||
Revision 1306
|
||||
-------------------
|
||||
수정일 : 2015-12-08
|
||||
수정자 : 유희곤
|
||||
|
||||
- CHG: 0 파일 자동 생성 실패시 작업 완료 결과 통계 로깅 추가 (#25462)
|
||||
- 0 파일 자동 생성 실패시..
|
||||
- 작업 완료 결과 통계에 로깅되지 않아 운영자가 그냥 지나칠 수 있음.
|
||||
- 따라서.. 작업 완료 결과 통계 중 not exist 에 카운팅 되도록 처리한다.
|
||||
|
||||
|
||||
Revision 1286
|
||||
-------------------
|
||||
수정일 : 2015-11-06
|
||||
수정자 : 유희곤
|
||||
|
||||
- NEW: 각 작업 완료시 결과 통계 로깅 (#25000)
|
||||
- Meta Copy 시 memory 사용량은 FreeBSD 6.X 에 배포되는 sqlite 3.4.1 에서 함수 미지원으로 적용 불가.
|
||||
- 정합성 검증 완료 후.. not exist, size mismatch, zero auto create 항목 로깅 추가
|
||||
- garbage clean 완료 후 unlink 처리된 전체 size 로깅 추가.
|
||||
|
||||
|
||||
|
||||
Revision 1277
|
||||
-------------------
|
||||
수정일 : 2015-11-03
|
||||
수정자 : 유희곤
|
||||
|
||||
- NEW: FHS Content 정합성 검증 및 garbage content 정리 목적으로 신규 개발
|
||||
- 세부 내역: #13817 참고
|
||||
- 설계서: svn://svc1svn.solbox.com/document/01 양방향/02 설계서/16 Content 삭제& 정합성 검증/cls 설계서.vsdx
|
||||
|
||||
- NEW: SVN 신규 등록
|
||||
- 버전 관리 정책에 따라 3.5.0 버전으로 신규 등록
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#****************************************************************************
|
||||
# Makefile for cls
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2015/10/06
|
||||
# copyright : (C) 2005 Solbox Inc.
|
||||
# author : Dev Storage team
|
||||
# email : storage.sd@solbox.com
|
||||
# version : 3.5
|
||||
#
|
||||
# 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.
|
||||
#*****************************************************************************
|
||||
|
||||
SUBDIRS = lib src
|
||||
|
||||
.PHONY: all $(SUBDIRS)
|
||||
|
||||
|
||||
all: $(SUBDIRS)
|
||||
sync;
|
||||
|
||||
|
||||
$(SUBDIRS):
|
||||
$(MAKE) all -C $@
|
||||
|
||||
|
||||
install:
|
||||
@for dir in $(SUBDIRS); do (cd $$dir; $(MAKE) install); done
|
||||
|
||||
|
||||
clean:
|
||||
@for dir in $(SUBDIRS); do (cd $$dir; $(MAKE) clean); done
|
||||
|
||||
|
||||
# End of Makefile
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <boost/algorithm/string.hpp> // use boost
|
||||
|
||||
|
||||
Config::Config()
|
||||
: m_keyDelimiter( CONFIG_DEFAULT_KEY_DELIMITER )
|
||||
, m_valueDelimiter( CONFIG_DEFAULT_VALUE_DELIMITER )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// @brief destructor
|
||||
Config::~Config()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
|
||||
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::Open( const string path )
|
||||
{
|
||||
ifstream file;
|
||||
|
||||
// Config file open
|
||||
file.open( path.c_str());
|
||||
if( file.is_open() == false )
|
||||
{
|
||||
cerr << "[error] " << __FILE__<< ":" << __func__ << ": config file open failed.[" << path << "][" << strerror( errno ) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 기존 데이터가 존재하면 삭제 처리.
|
||||
if( m_configData.empty() == false )
|
||||
m_configData.clear();
|
||||
|
||||
// config file parsing
|
||||
string line;
|
||||
string section;
|
||||
vector< string > configs;
|
||||
|
||||
// 루프를 돌면서 Config 파일을 line 단위로 읽어들인당....
|
||||
while( std::getline( file, line ) )
|
||||
{
|
||||
// 앞뒤 공백 제거 처리
|
||||
boost::trim( line );
|
||||
|
||||
// 공백 or 주석처리 라인 검사
|
||||
if( line.empty() == true || boost::starts_with( line, string("#") ) == true || line == "\r" )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Section 여부 검사
|
||||
if( boost::starts_with( line, string("[") ) == true && boost::ends_with( line, string("]") ) == true )
|
||||
{
|
||||
// [] 로 정의된 section 의 문자열 값 추출
|
||||
line.erase( line.begin() );
|
||||
line.erase( line.end() -1 );
|
||||
boost::trim( line );
|
||||
|
||||
if( section.empty() == false && section != line )
|
||||
{
|
||||
// 이전 세션과 다른 신규 세션인 경우
|
||||
// 기존까지 저장했던 데이터를 멤버 변수에 입력 처리 후 내부변수 초기화 처리.
|
||||
|
||||
m_configData.insert( make_pair( section, configs ) );
|
||||
section.clear();
|
||||
configs.clear();
|
||||
}
|
||||
|
||||
// 신규 section 정보 저장처리.
|
||||
section = line;
|
||||
line.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Section 정보가 아닌 경우 => 실제 설정값 .. ^^
|
||||
configs.push_back( line );
|
||||
}
|
||||
}
|
||||
|
||||
// 마지막 세션 처리된 정보가 존재하는 경우 멤버 변수에 저장 처리.
|
||||
if( section.empty() == false )
|
||||
{
|
||||
m_configData.insert( make_pair( section, configs ) );
|
||||
}
|
||||
|
||||
// 종료 처리.
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
|
||||
/// @return void
|
||||
void Config::Clear()
|
||||
{
|
||||
m_configData.clear();
|
||||
}
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::GetConfig( const string section, const string key, string& value )
|
||||
{
|
||||
// Section 에 대한 임시 데이터 저장객체 생성
|
||||
vector< string > configs;
|
||||
|
||||
// 해당 Section 이 존재하지 않는 경우
|
||||
if( Find( section, configs ) == false )
|
||||
return false;
|
||||
|
||||
string line;
|
||||
string result;
|
||||
vector< string >::const_iterator it;
|
||||
|
||||
// 해당 Key 이 존재하는지 검사
|
||||
for( it = configs.begin(); it != configs.end(); it++)
|
||||
{
|
||||
line = *it;
|
||||
|
||||
// Line 상의 주석 제거
|
||||
string::size_type pos = line.find( '#' );
|
||||
if( pos != string::npos)
|
||||
{
|
||||
line = line.substr(0, pos);
|
||||
boost::trim( line );
|
||||
}
|
||||
|
||||
// key = value 에서 key 부분 추출
|
||||
pos = line.find( m_keyDelimiter );
|
||||
if( pos == string::npos )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = line.substr(0, pos);
|
||||
boost::trim( result );
|
||||
}
|
||||
|
||||
if( key == result )
|
||||
{
|
||||
// Key 값이 동일한 경우
|
||||
value = line.substr( pos+1 );
|
||||
boost::trim( value );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::GetConfig( const string section, const string key, vector< string >& value )
|
||||
{
|
||||
string result;
|
||||
vector< string > vec;
|
||||
|
||||
if( GetConfig( section, key, result ) == false )
|
||||
return false;
|
||||
|
||||
// 전달받은 result 값을 value delimiter 를 이용하여 parsing 처리
|
||||
boost::split( vec, result, boost::is_any_of( m_valueDelimiter ));
|
||||
if( vec.empty() == true )
|
||||
return false;
|
||||
|
||||
// 루프를 돌면서 trim 처리 후 결과값 저장처리.
|
||||
vector< string >::const_iterator it;
|
||||
for( it = vec.begin(); it != vec.end(); it++)
|
||||
{
|
||||
result = boost::trim_copy( *it );
|
||||
|
||||
if( result.empty() == false )
|
||||
value.push_back( result );
|
||||
}
|
||||
|
||||
if( value.empty() == true )
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
|
||||
/// @param section [in] section 명
|
||||
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Config::Find( const string& section, vector<string>& configData )
|
||||
{
|
||||
map< string, vector< string > >::iterator it = m_configData.find( section );
|
||||
if( it != m_configData.end() )
|
||||
{
|
||||
// 해당 section 을 찾은 경우
|
||||
configData = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 해당 section 을 찾지 못한 경우
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/***************************************************************************
|
||||
Config File Parser Class
|
||||
-----------------------------------------
|
||||
begin : 2010/02/27
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2005 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_CONFIG_H__
|
||||
#define __LIBRARY_CONFIG_H__
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
#define CONFIG_DEFAULT_KEY_DELIMITER "="
|
||||
#define CONFIG_DEFAULT_VALUE_DELIMITER ","
|
||||
|
||||
|
||||
/// @brief Config class
|
||||
/// 1. config 파일의 내용을 파싱처리하여 각 Section, Key 에 해당하는 값을 반환처리
|
||||
/// 2. config 파일은 [section] 단위로 구분된다.
|
||||
/// 3. config 파일은 key=value 로 구분가능하며 이는 Delimiter 설정을 통해 변경가능하다.
|
||||
/// 4. value 값이 다중으로 존재하는 경우 "," 값을 통해 구분가능, Delimiter 변경시 다른 값도 사용가능함.
|
||||
/// 5. config 파일상에서 "#" 로 시작하는 문자열은 라인 끝까지 주석으로 처리된다.
|
||||
/// 6. 특정 Key 값에 대한 다중 value 값 조회는 vector<string> 을 통해 수행한다.
|
||||
/// 7. 만약 다중 value 값이 존재시 string 으로 반환받을 경우 해당 Row 가 통째로 반환된다.
|
||||
/// 8. 본 객체는 Open , Clear 함수가 호출되기전까지 이전 Config 정보가 저장된다.
|
||||
class Config
|
||||
{
|
||||
private:
|
||||
/// @brief config information saved variable
|
||||
// string : section data
|
||||
// vector<string> : key=value data
|
||||
map< string, vector< string> > m_configData;
|
||||
|
||||
/// @brief Key, Value 구분자 저장 변수
|
||||
string m_keyDelimiter;
|
||||
string m_valueDelimiter;
|
||||
|
||||
public:
|
||||
/// @brief constructor
|
||||
Config();
|
||||
|
||||
/// @brief destructor
|
||||
~Config();
|
||||
|
||||
/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
|
||||
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Open( const string path );
|
||||
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
|
||||
/// @return void
|
||||
void Clear();
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
|
||||
/// @return On success return true, otherwise return false
|
||||
bool GetConfig( const string section, const string key, string& value );
|
||||
|
||||
/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
|
||||
/// @param section [in] 찾고자 하는 Section 값
|
||||
/// @param key [in] 찾고자 하는 key 값
|
||||
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool GetConfig( const string section, const string key, vector< string >& value );
|
||||
|
||||
/// @brief Key, Value Delimiter Get 함수
|
||||
/// @param key [out] key delimiter 값
|
||||
/// @param value [out] value delimiter 값
|
||||
void GetDelimiter( string& key, string& value )
|
||||
{
|
||||
key = m_keyDelimiter;
|
||||
value = m_valueDelimiter;
|
||||
}
|
||||
|
||||
// BUG 2010-12-06 huibong
|
||||
// string& value = CONFIG_DEFAULT_VALUE_DELIMITER 값은 문법상 오류 구문임.
|
||||
// gcc 3.4.6 버전에서는 Compile 되나 gcc 4.4.5 에서는 error 로 처리되어 수정처리함.
|
||||
|
||||
/// @brief Key, Value Delimiter Set 함수
|
||||
/// @param key [out] key delimiter 값
|
||||
/// @param value [out] value delimiter 값, 지정하지 않을 경우 Default 값이 사용됨.
|
||||
void SetDelimiter( string& key, string value = CONFIG_DEFAULT_VALUE_DELIMITER )
|
||||
{
|
||||
m_keyDelimiter = key;
|
||||
m_valueDelimiter = value;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
|
||||
/// @param section [in] section 명
|
||||
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
|
||||
/// @return On success return true, otherwise return false
|
||||
bool Find( const string& section, vector<string>& configData );
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /* __LIBRARY_CONFIG_H__ */
|
||||
@@ -0,0 +1,487 @@
|
||||
/***************************************************************************
|
||||
Logger.cpp
|
||||
-----------------------------------------
|
||||
begin : 2011/10/26
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 1.0
|
||||
|
||||
CopyRight(C) 2005 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 "Logger.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <iostream>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_BUFFER_SIZE 2048 // 임시 버퍼 최대 크기
|
||||
#define DEFAULT_LOG_LEVEL LINF
|
||||
|
||||
using namespace std;
|
||||
|
||||
CLogger* CLogger::m_pInstance = NULL;
|
||||
bool CLogger::m_bIsInitialized = false;
|
||||
pthread_mutex_t CLogger::m_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
|
||||
CLogger::CLogger( string programName, string logDir, int logLevel )
|
||||
{
|
||||
m_szProgramName = programName;
|
||||
m_szLogDir = logDir;
|
||||
|
||||
if( IsValidLogLevel( logLevel ) == true )
|
||||
{
|
||||
m_nLogLevel = logLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_nLogLevel = DEFAULT_LOG_LEVEL;
|
||||
}
|
||||
|
||||
MakeLogLevelString();
|
||||
}
|
||||
|
||||
void CLogger::MakeLogLevelString()
|
||||
{
|
||||
m_vectorLogLevelString.push_back( "EMR" );
|
||||
m_vectorLogLevelString.push_back( "ALT" );
|
||||
m_vectorLogLevelString.push_back( "CRT" );
|
||||
m_vectorLogLevelString.push_back( "ERR" );
|
||||
m_vectorLogLevelString.push_back( "WAR" );
|
||||
m_vectorLogLevelString.push_back( "NOT" );
|
||||
m_vectorLogLevelString.push_back( "INF" );
|
||||
m_vectorLogLevelString.push_back( "DBG" );
|
||||
m_vectorLogLevelString.push_back( "DEV" );
|
||||
m_vectorLogLevelString.push_back( "DEV1" );
|
||||
m_vectorLogLevelString.push_back( "DEV2" );
|
||||
}
|
||||
|
||||
CLogger::~CLogger()
|
||||
{
|
||||
m_vectorLogLevelString.clear();
|
||||
}
|
||||
|
||||
bool CLogger::Init( string programName, string logDir, int logLevel )
|
||||
{
|
||||
if( m_bIsInitialized == true )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
pthread_mutex_lock( &m_mutex );
|
||||
|
||||
// 변수 유효성 검사.
|
||||
if( programName.empty() == true || logDir.empty() == true )
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( MakeLogDir( programName, logDir ) == false )
|
||||
{
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( CLogger::m_pInstance != NULL )
|
||||
{
|
||||
delete CLogger::m_pInstance;
|
||||
CLogger::m_pInstance = NULL;
|
||||
}
|
||||
|
||||
CLogger::m_pInstance = new CLogger( programName, logDir, logLevel );
|
||||
m_bIsInitialized = true;
|
||||
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::IsValidLogLevel( int logLevel )
|
||||
{
|
||||
return ( ( logLevel < 0 || logLevel > MAX_LOG_LEVEL ) ? false : true );
|
||||
}
|
||||
|
||||
bool CLogger::SetLogLevel( int logLevel )
|
||||
{
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_nLogLevel = logLevel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::MakeLogDir( string programName, string logDir )
|
||||
{
|
||||
string path = logDir + "/" + programName;
|
||||
|
||||
struct stat dirStat;
|
||||
|
||||
// 해당 이름을 가진 파일 또는 디렉토리가 존재하고
|
||||
if( lstat( path.c_str(), &dirStat ) == 0 )
|
||||
{
|
||||
// 해당 이름이 디렉토리인 경우
|
||||
if( S_ISDIR( dirStat.st_mode ) == true )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "exist file with the same name as log path(" << path << ")." << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 디렉토리 가 존재하지 않는 경우 디렉토리 생성 시도
|
||||
string cmd = "mkdir -p " + path;
|
||||
system( cmd.c_str() );
|
||||
|
||||
if( IsDirectory( path ) == false )
|
||||
{
|
||||
cerr << "can't make log directory. path=" << path << "." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::IsDirectory( string path )
|
||||
{
|
||||
struct stat dirStat;
|
||||
|
||||
if( lstat ( path.c_str(), &dirStat ) != 0 )
|
||||
{
|
||||
cerr << "Log path not valid. Check path [" << path << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 해당 정보가 Directory 가 아닌 경우
|
||||
if( S_ISDIR( dirStat.st_mode ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CLogger::Exit()
|
||||
{
|
||||
if( m_bIsInitialized == false )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock( &m_mutex );
|
||||
|
||||
if( CLogger::m_pInstance != NULL )
|
||||
{
|
||||
delete CLogger::m_pInstance;
|
||||
CLogger::m_pInstance = NULL;
|
||||
m_bIsInitialized = false;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &m_mutex );
|
||||
}
|
||||
|
||||
CLogger* CLogger::GetInstance()
|
||||
{
|
||||
return CLogger::m_pInstance;
|
||||
}
|
||||
|
||||
string CLogger::GetLogFilename( struct tm &timeNow )
|
||||
{
|
||||
char timeStr[256];
|
||||
snprintf( timeStr, (size_t)256, "%04d%02d%02d.log", timeNow.tm_year+1900, timeNow.tm_mon+1, timeNow.tm_mday);
|
||||
|
||||
// Make File Name
|
||||
string filename = m_szLogDir + "/" + m_szProgramName + "/" + m_szProgramName + "_" + string( timeStr );
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
bool CLogger::Write( int logLevel, const char * fmt, ...)
|
||||
{
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return Write( logLevel, PREFIX_DATE, NULL, 0, buffer );
|
||||
}
|
||||
|
||||
bool CLogger::WriteNoPrefix( int logLevel, const char * fmt, ...)
|
||||
{
|
||||
// 가변 인자 처리
|
||||
va_list args;
|
||||
char buffer[MAX_BUFFER_SIZE];
|
||||
va_start( args, fmt );
|
||||
if( vsnprintf( buffer, MAX_BUFFER_SIZE, fmt, args) < 0 )
|
||||
{
|
||||
va_end( args );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
return Write( logLevel, PREFIX_NONE, NULL, 0, buffer );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// CHG 2012-08-16 huibong
|
||||
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
|
||||
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
|
||||
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
|
||||
// 함수명 : Write -> WriteWithFunc 으로 변경 처리
|
||||
bool CLogger::WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
|
||||
{
|
||||
if( filename == NULL || funcname == NULL || lineNum < 0 || fmt == NULL )
|
||||
{
|
||||
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 );
|
||||
return false;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
|
||||
char className[255];
|
||||
string funcPrefix = GetClassName( className, filename );
|
||||
funcPrefix += "::" + string( funcname ) + "()";
|
||||
|
||||
return Write( logLevel, PREFIX_FUNCTION, funcPrefix.c_str(), lineNum, buffer );
|
||||
}
|
||||
|
||||
const char* CLogger::GetClassName( char* className, const char* filename )
|
||||
{
|
||||
if( className == NULL )
|
||||
{
|
||||
cout << "[ERROR] The input argument 'className' is NULL." << endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( filename == NULL )
|
||||
{
|
||||
cout << "[ERROR] The input argument 'filename' is NULL." << endl;
|
||||
strcpy( className, "NULL" );
|
||||
return className;
|
||||
}
|
||||
|
||||
int filenameSize = strlen( filename );
|
||||
memcpy( className, filename, filenameSize );
|
||||
|
||||
// find end position
|
||||
char endCharacter = '.';
|
||||
int endPos = filenameSize - 1;
|
||||
for( ; endPos > 0; --endPos )
|
||||
{
|
||||
if( className[endPos] == endCharacter )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( endPos == 0 )
|
||||
{
|
||||
cout << "[ERROR] endPos=0" << endl;
|
||||
strcpy( className, "NULL" );
|
||||
return className;
|
||||
}
|
||||
|
||||
className[endPos] = '\0';
|
||||
|
||||
// find start position
|
||||
char startCharacter = '/';
|
||||
int startPos = endPos - 1;
|
||||
for( ; startPos > 0; --startPos )
|
||||
{
|
||||
if( className[startPos] == startCharacter )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( startPos != 0 )
|
||||
{
|
||||
startPos += 1;
|
||||
}
|
||||
|
||||
return (className + startPos );
|
||||
}
|
||||
|
||||
bool CLogger::Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log )
|
||||
{
|
||||
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
|
||||
if( logLevel > m_nLogLevel )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( log == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Current Data & Time
|
||||
time_t now = time( NULL );
|
||||
struct tm timeNow;
|
||||
localtime_r( &now, &timeNow );
|
||||
|
||||
string filename = GetLogFilename( timeNow );
|
||||
|
||||
// Log file open
|
||||
FILE* pFile = NULL;
|
||||
pFile = fopen( filename.c_str(), "a+" );
|
||||
|
||||
if( pFile == NULL )
|
||||
{
|
||||
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// 시간 정보처리
|
||||
char timeStr[256];
|
||||
snprintf( timeStr, (size_t)256, "[%02d:%02d:%02d]"
|
||||
, timeNow.tm_hour, timeNow.tm_min, timeNow.tm_sec );
|
||||
|
||||
// Log Level 문자열 검색
|
||||
string szLevel = m_vectorLogLevelString[ logLevel ];
|
||||
|
||||
switch( logPrefix )
|
||||
{
|
||||
case PREFIX_DATE:
|
||||
/// 형식 예: [15:47:41] [DBG] sample log message.
|
||||
fprintf( pFile, "%s [%-4s] %s\n", timeStr, szLevel.c_str(), log );
|
||||
break;
|
||||
|
||||
case PREFIX_FUNCTION:
|
||||
if( functionName == NULL )
|
||||
{
|
||||
fclose( pFile );
|
||||
return false;
|
||||
}
|
||||
/// 형식 예: [15:47:41] [DBG] sample log message. [SomeClass::SomeFunction():12]
|
||||
fprintf( pFile, "%s [%-4s] %s [%s:%d]\n", timeStr, szLevel.c_str(), log, functionName, lineNum );
|
||||
break;
|
||||
|
||||
case PREFIX_NONE:
|
||||
/// 형식 예: sample log message.
|
||||
fprintf( pFile, "%s\n", log );
|
||||
break;
|
||||
}
|
||||
// Write to log file
|
||||
fflush( pFile );
|
||||
|
||||
// 종료 처리.
|
||||
fclose( pFile );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CLogger::WriteHex( int logLevel, const unsigned char* data, const int size )
|
||||
{
|
||||
/// 유효한 로그 레벨이 아니면 로깅하지 않음.
|
||||
if( IsValidLogLevel( logLevel ) == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Log Level 검사 : 지정된 Level 이상인 경우 로깅하지 않음.
|
||||
if( logLevel > m_nLogLevel )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( data == NULL )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( size <= 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Current Data & Time
|
||||
time_t now = time( NULL );
|
||||
struct tm timeNow;
|
||||
localtime_r( &now, &timeNow );
|
||||
|
||||
string filename = GetLogFilename( timeNow );
|
||||
|
||||
// Log file open
|
||||
FILE* pFile = NULL;
|
||||
pFile = fopen( filename.c_str(), "a+" );
|
||||
|
||||
if( pFile == NULL )
|
||||
{
|
||||
cerr << "Log file open fail.[" << filename.c_str() << "][" << errno << "][" << strerror(errno) << "]" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for( int i = 0; i < size; ++i )
|
||||
{
|
||||
fprintf( pFile, "%02X", data[i] );
|
||||
|
||||
if( i == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( ( (i+1) % 8 ) == 0 )
|
||||
{
|
||||
fprintf( pFile, " " );
|
||||
}
|
||||
|
||||
if( ( (i+1) % 16 ) == 0 )
|
||||
{
|
||||
fprintf( pFile, " " );
|
||||
}
|
||||
|
||||
if( ( (i+1) % 32 ) == 0 )
|
||||
{
|
||||
fprintf( pFile, "\n" );
|
||||
}
|
||||
}
|
||||
fprintf( pFile, "\n" );
|
||||
fflush( pFile );
|
||||
|
||||
// 종료 처리.
|
||||
fclose( pFile );
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/***************************************************************************
|
||||
Logger.h
|
||||
-----------------------------------------
|
||||
begin : 2011/10/26
|
||||
copyright : (C) 2005 SolutionBox Inc.
|
||||
author : Service 1 Team
|
||||
email : svc1@solbox.com
|
||||
version : 3.2.0.805
|
||||
|
||||
CopyRight(C) 2005 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 __LOGGER_H__
|
||||
#define __LOGGER_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <pthread.h>
|
||||
|
||||
///@brief 로그 레벨 정의
|
||||
#define LEMR 0 /* system is or will be unusable if situation is not resolved */
|
||||
#define LALT 1 /* immediate action required */
|
||||
#define LCRT 2 /* critical situations */
|
||||
#define LERR 3 /* error conditions */
|
||||
#define LWAR 4 /* recoverable errors */
|
||||
#define LNOT 5 /* unusual situation that merits investigation */
|
||||
#define LINF 6 /* information messages */
|
||||
#define LDBG 7 /* verbose data for debugging */
|
||||
#define LDEV 8 /* verbose data for developer */
|
||||
#define LDEV1 9 /* start or end the function of application level */
|
||||
#define LDEV2 10 /* start or end the function of application level */
|
||||
|
||||
#define MAX_LOG_LEVEL LDEV2
|
||||
|
||||
|
||||
///@brief Log 정보를 파일로 저장하기 위한 Class.
|
||||
///1. 가변 format 으로 전달된 로그 관련 정보를 Log Level 에 따라 로그 파일에 아래의 4가지 형식으로 저장한다.
|
||||
///
|
||||
/// 1.1 Prefix로 [hh:mm:ss]와 [ClassName::FunctionName]이 추가된 로그
|
||||
/// LOG( level, format, ... ) 매크로 사용.
|
||||
/// 예) [17:43:40] [DBG] log level debug [LoggerTestTestLogger]
|
||||
///
|
||||
/// 1.2 Prefix로 [hh:mm:ss]이 추가된 로그
|
||||
/// _LOG( level, format, ... ) 매크로 사용
|
||||
/// 예) [17:43:40] [EMR] log level emergency
|
||||
///
|
||||
/// 1.3 Prefix가 없는 로그
|
||||
/// _LOG_( level, format, ... ) 매크로 사용
|
||||
/// 예) log level emergency
|
||||
///
|
||||
/// 1.4 Hex 로그.
|
||||
/// LOG_HEX( level, data, size ) 매크로 사용
|
||||
/// 예) 00010203 04050607 08090A0B 0C0D0E0F
|
||||
///
|
||||
///2. Logger 객체 초기화시 전달된 Log Level 정보보다 전달받은 Log Level 정보가 큰 경우 해당 로그는 파일로 저장되지 않는다.
|
||||
///
|
||||
///3. 로그 파일은 매 일단위로 저장파일이 변경된다.
|
||||
///
|
||||
///4. 로그 저장을 위한 program 경로가 존재하지 않는 경우 자동 생성 처리된다.
|
||||
///
|
||||
///5. 로그 저장방식은 매 저장로그마다 open-close 로 처리된다.
|
||||
///
|
||||
///6. 파일로 기록시 Log Level 에 대한 정보도 함께 기록된다.
|
||||
///
|
||||
///7. 싱글톤으로 작성되었고, CLogger::Init(...)시에 쓰레드 안정성을 제공한다.
|
||||
///
|
||||
///8. 동적으로 로그 레벨을 변경할 수 있는 인터페이스를 제공한다.
|
||||
///
|
||||
class CLogger
|
||||
{
|
||||
// Attributes
|
||||
private:
|
||||
///@brief 싱글톤 객체 인트턴스.
|
||||
static CLogger* m_pInstance;
|
||||
|
||||
///@brief 싱글톤 객체 초기화 여부.
|
||||
static bool m_bIsInitialized;
|
||||
|
||||
///@brief 싱글톤 객체 초기화시 스레드 안정성을 위한 뮤텍스.
|
||||
static pthread_mutex_t m_mutex;
|
||||
|
||||
///@brief 프로그램 이름. 로그 파일 경로를 만들 때 사용.
|
||||
std::string m_szProgramName;
|
||||
|
||||
///@brief 공통 로그 디렉토리 이름. 로그 파일 경로를 만들 때 사용.
|
||||
std::string m_szLogDir;
|
||||
|
||||
///@brief 로그 레벨.
|
||||
int m_nLogLevel;
|
||||
|
||||
///@brief 로그 레벨에 대응되는 문자열 정보를 저장.
|
||||
std::vector<std::string> m_vectorLogLevelString;
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
///@brief 로그 형식을 지정.
|
||||
typedef enum
|
||||
{
|
||||
PREFIX_NONE = 0, /// 클래스 설명의 1.3에 해당
|
||||
PREFIX_DATE, /// 클래스 설명의 1.2에 해당
|
||||
PREFIX_FUNCTION, /// 클래스 설명의 1.1에 해당
|
||||
} LOG_PREFIX;
|
||||
|
||||
// Operations
|
||||
private:
|
||||
///@brief 생성자.
|
||||
/// 프로그램 이름, 공통 로그 디렉토리, 로그 레벨을 저장하고
|
||||
/// 로그 레벨에 대응되는 문자열 정보를 만든다.
|
||||
///@param programName [in] 프로그램 이름
|
||||
///@param logDir [in] 공통 로그 디렉토리 경로
|
||||
///@param logLevel [in] 로그 레벨
|
||||
CLogger( std::string programName, std::string logDir, int logLevel );
|
||||
|
||||
///@brief 소멸자.
|
||||
/// 로그 레벨에 대응되는 문자열 정보를 저장하고 있는
|
||||
/// 벡터 m_vectorLogLevelString을 clear 시킴.
|
||||
virtual ~CLogger();
|
||||
|
||||
///@brief 로그 레벨에 대응되는 문자열을 만든다.
|
||||
///@param none.
|
||||
///@return none.
|
||||
void MakeLogLevelString();
|
||||
|
||||
///@brief 로그 파일이 위치할 실제 로그 디렉토리를 생성한다.
|
||||
/// 생성할 디렉토리 경로는 'logDir/programName'이 된다.
|
||||
///@param programName [in] 프로그램 이름
|
||||
///@param logDir [in] 공통 로그 디렉토리 경로
|
||||
///@return 디렉토리가 이미 존재하거나 생성 성공하면 true,
|
||||
/// 해당 경로가 존재하지만 디렉토리가 아니거나, 디렉토리 생성 실패하면 false 반환.
|
||||
static bool MakeLogDir( std::string programName, std::string logDir );
|
||||
|
||||
///@brief 해당 경로가 디렉토리 인지 아닌지 판단.
|
||||
///@param path [in] 디렉토리 인지 아닌지 판단할 경로.
|
||||
///@return 해당 경로가 디렉토이면 true,
|
||||
/// 경로가 존재하지 않거나 디렉토리가 아니면 false 반환.
|
||||
static bool IsDirectory( std::string path );
|
||||
|
||||
///@brief 로그 레벨이 올바른지 판별.
|
||||
///@param logLevel [in]
|
||||
///@return 올바른 로그 레벨이면 true, 그렇지 않으면 false 반환.
|
||||
static bool IsValidLogLevel( int logLevel );
|
||||
|
||||
///@brief 시간 정보를 입력 받아 로그 파일 이름을 만든다.
|
||||
/// 로그 파일 이름 형식 : 프로그램명_YYYYMMDD.log
|
||||
///param timeNow [in] 현재 시간 정보.
|
||||
///return 로그 파일 이름.
|
||||
std::string GetLogFilename( struct tm &timeNow );
|
||||
|
||||
///@brief 로그를 남기는는 클래스가 정의된 파일의 이름에서 클래스명을 추출한다.
|
||||
/// 쓰레드 안정성을 보장한다.
|
||||
///@param className [out] 파일 이름에서 추출된 클래스명
|
||||
///@param filename [in] 파일 이름.
|
||||
///@return 클래스명 추출이 성공하면 클래스명 문자열의 포인터, 추출 실패하면 NULL.
|
||||
const char* GetClassName( char* className, const char* filename );
|
||||
|
||||
///@brief 인자 logPrefix에 따라 적절한 형식으로 로그 파일에 로그를 저장한다.
|
||||
/// 인자 logLevel이 설정된 로그 레벨보다 높으면 로그를 출력하지 않는다.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param logPrefix [in] 로그 프리픽스 종류.
|
||||
///@functionName [in] ClassName::FunctionNmae() 형식의 문자열.
|
||||
///@lineNum [in] 라인 번호.
|
||||
///@log [in] 출력하고자 하는 로그 내용.
|
||||
bool Write( int logLevel, int logPrefix, const char* functionName, int lineNum, const char* log );
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
///@brief 싱글톤 객체 m_pInstance를 생성하고 인자 정보로 로그 디렉토리를 만든다.
|
||||
///@param programName [in] 프로그램 이름
|
||||
///@param logDir [in] 공통 로그 디렉토리 경로
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@return 로그 디렉토리를 만들고 싱글톤 객체를 생성했으면 true,
|
||||
/// 로그 디렉토리를 만들지 못 했거나 인자 값이 올바르지 않으면 false 반환.
|
||||
static bool Init( std::string programName, std::string logDir, int logLevel );
|
||||
|
||||
///@brief 싱글톤 객체 m_pInstance를 delete 한다.
|
||||
///@param none.
|
||||
///@return none.
|
||||
static void Exit();
|
||||
|
||||
///@brief 싱글톤 객체 m_pInstance를 반환한다.
|
||||
///@param none.
|
||||
///@return CLogger 객체의 인스턴스.
|
||||
static CLogger* GetInstance();
|
||||
|
||||
///@brief Prefix로 [hh:mm:ss]이 추가된 형식으로 로그 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param fmt [in] 로그 내용 포맷.
|
||||
///@param __VAR_ARGS__ [in] 가변 인자.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool Write( int logLevel, const char * fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
///@brief Prefix가 없는 로그 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param fmt [in] 로그 내용 포맷.
|
||||
///@param __VAR_ARGS__ [in] 가변 인자.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool WriteNoPrefix( int logLevel, const char * fmt, ...)
|
||||
__attribute__((format(printf, 3, 4)));
|
||||
|
||||
|
||||
// CHG 2012-08-16 huibong
|
||||
// 가변인자를 사용하는 Write 함수 다중 정의로 인해...
|
||||
// Complier 에서 인수 갯수 및 Type 이 동일할 경우 다른 함수를 가르키는 현상이 발견됨.
|
||||
// 이를 해결하기 위해 Write 함수에 대한 다중 정의를 제거토록 함수명을 명확하게 변경처리함.
|
||||
|
||||
///@brief Prefix로 [hh:mm:ss]와 [ClassName::FunctionName:line]이 추가된 형식으로 로그 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param filename [in] 파일 이름
|
||||
///@param funcname [in] 함수 이름
|
||||
///@param lineNum [in] 라인 번호
|
||||
///@param fmt [in] 로그 내용 포맷.
|
||||
///@param __VAR_ARGS__ [in] 가변 인자.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool WriteWithFunc( int logLevel, const char* filename, const char* funcname, int lineNum, const char * fmt, ...)
|
||||
__attribute__((format(printf, 6, 7)));
|
||||
|
||||
///@brief 로그를 hex 형식으로 저장.
|
||||
///@param logLevel [in] 로그 레벨
|
||||
///@param data [in] hex 형식으로 출력할 데이터.
|
||||
///@pram size [in] data의 크기.
|
||||
///@return 로그 파일에 로그를 저장했으면 true, 그렇지 않으면 false 반환
|
||||
bool WriteHex( int logLevel, const unsigned char* data, const int size );
|
||||
|
||||
///@brief 로그을 설정한다. 인자 logLevel이 적절한 값이면 새로운 값으로 변경하고
|
||||
/// 적절한 값이 아니면 로그 레벨을 변경하지 않는다.
|
||||
///@param logLevel [in] 설정할 로그 레벨
|
||||
///@return none.
|
||||
bool SetLogLevel( int logLevel );
|
||||
|
||||
inline int GetLogLevel() { return m_nLogLevel; };
|
||||
inline std::string GetLogDir() { return m_szLogDir + "/" + m_szProgramName; };
|
||||
};
|
||||
|
||||
#define LOG( level, format, ... ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->WriteWithFunc( level, __FILE__, __FUNCTION__, __LINE__, format, ##__VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
|
||||
#define _LOG( level, format, ... ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->Write( level, format, ##__VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
|
||||
#define _LOG_( level, format, ... ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->WriteNoPrefix( level, format, ##__VA_ARGS__ ); \
|
||||
}
|
||||
|
||||
|
||||
#define _LOG_HEX_( level, data, size ) \
|
||||
if( CLogger::GetInstance() != NULL ) \
|
||||
{ \
|
||||
CLogger::GetInstance()->WriteHex( level, (const unsigned char*)data, size ); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define FUNC_BEGIN() LOG( LDEV1, "begin" )
|
||||
#define FUNC_END() LOG( LDEV1, "end" )
|
||||
|
||||
#define FRM_BEGIN() LOG( LDEV2, "begin" )
|
||||
#define FRM_END() LOG( LDEV2, "end" )
|
||||
|
||||
#endif // __LOGGER_H__
|
||||
@@ -0,0 +1,61 @@
|
||||
#****************************************************************************
|
||||
# Makefile for Cloud Storage Common Libaray
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2015/10/06
|
||||
# copyright : (C) 2013 Solbox Inc.
|
||||
# author : Dev Storage Team
|
||||
# email : storage.sd@solbox.com
|
||||
# version : 3.5.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.
|
||||
#*****************************************************************************
|
||||
|
||||
# Library info
|
||||
|
||||
LIB_NAME = InterCommon
|
||||
|
||||
LIB = lib$(LIB_NAME).a
|
||||
|
||||
OBJS = Config.o Logger.o
|
||||
|
||||
# Compiler info
|
||||
CC = /usr/bin/g++
|
||||
AR = /usr/bin/ar
|
||||
|
||||
DIR_INCLUDE = -I/usr/local/include
|
||||
|
||||
CFLAGS = -Wall -O3 -g -Wreturn-type -Wunused -Wuninitialized\
|
||||
-Wparentheses -Wshadow -Wpointer-arith -Woverloaded-virtual\
|
||||
-D_REENTRANT -D_THREAD_SAFE -D_PTHREADS
|
||||
|
||||
|
||||
############################
|
||||
|
||||
all:$(LIB)
|
||||
sync
|
||||
|
||||
%.o: %.cpp
|
||||
$(CC) $(CFLAGS) -o $@ -c $^ $(DIR_INCLUDE)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -o $@ -c $^ $(DIR_INCLUDE)
|
||||
|
||||
|
||||
$(LIB): $(OBJS)
|
||||
$(AR) crsv $@ $^
|
||||
|
||||
|
||||
clean:
|
||||
-rm -f *.o core *.out *.log
|
||||
-rm -f $(LIB)
|
||||
sync
|
||||
|
||||
|
||||
install :
|
||||
sync
|
||||
|
||||
|
||||
# End of Makefile
|
||||
@@ -0,0 +1,184 @@
|
||||
/***************************************************************************
|
||||
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;
|
||||
|
||||
// CHG 2014-10-29 huibong
|
||||
// application_name add to logging RCDB
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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__
|
||||
@@ -0,0 +1,512 @@
|
||||
#include "GarbageClean.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <iterator>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
|
||||
#define SQLITE_DB_LOCK_TIMEOUT 1000 // SQLITE_BUSY 상황 발생 문제 해결을 위해 DB Lock 관련 timeout 설정값. (1 sec)
|
||||
#define SQLITE_DB_LOCK_MAX_RETRY 60 // SQLITE_BUSY 발생시 재시도 최대 횟수
|
||||
|
||||
|
||||
// meta 에 존재하지 않아.. unlink 여부 판단시... 삭제 방지 day 값
|
||||
// - unlink 대상인 경우.. 최종 수정일, 최종 access 시간이 본 설정값 이하이면 삭제되지 않도록 처리.
|
||||
// - 2019-08-29 일감 #32774 에 의해 7일 -> 2일로 변경 처리함.
|
||||
#define UNLINK_PREVENT_MAX_DAY 2
|
||||
|
||||
|
||||
// unlink slow 모드인 경우.. unlink 후 sleep 시간 (sec)
|
||||
// - 2 sec sleep : 일 4만건 unlink
|
||||
// - 1 sec sleep : 일 8만건 unlink
|
||||
#define UNLINK_SLOW_MODE_SLEEP 1
|
||||
|
||||
|
||||
// 생성자
|
||||
// - pLocalDb : sqlite memory db 객체에 대한 포인트
|
||||
CGarbageClean::CGarbageClean( sqlite3 * pLocalDb )
|
||||
: m_pLocalStmt( NULL )
|
||||
{
|
||||
m_pLocalDb = pLocalDb;
|
||||
}
|
||||
|
||||
|
||||
// 소멸자
|
||||
CGarbageClean::~CGarbageClean()
|
||||
{
|
||||
m_vecTargetDirectory.clear();
|
||||
|
||||
// Local DB stamt clear
|
||||
if( m_pLocalStmt != NULL )
|
||||
{
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 객체 초기화 함수
|
||||
bool CGarbageClean::Init()
|
||||
{
|
||||
// 작업대상 폴더 정보를 저장할 변수 초기화.
|
||||
m_vecTargetDirectory.clear();
|
||||
|
||||
// Local sqlite db 관련 변수
|
||||
int nResult = 0;
|
||||
int nRetryCount = 0;
|
||||
|
||||
|
||||
// strQuery = "CREATE TABLE meta( resource_id integer, filename_hash text, get_content_length integer, deleted_yn text )";
|
||||
|
||||
// 조회 처리를 수행할 stmt 객체 생성.
|
||||
// - 결과 추출 오류 발생시 sqlite3_column_int() 함수가 0 을 반환하므로.. 오류 와.. 실제 미존재 상태를 구분하기 위해... +1 처리함.
|
||||
std::string strQuery;
|
||||
strQuery = "SELECT count( resource_id ) +1 FROM meta WHERE filename_hash = ? ;";
|
||||
|
||||
nResult = sqlite3_prepare( m_pLocalDb, strQuery.c_str(), strQuery.size(), &m_pLocalStmt, NULL );
|
||||
|
||||
// sqlite3_prepare() 함수 실행시 SQLITE_BUSY 오류가 발생할 수 있으므로.. 재시도 로직을 추가한다.
|
||||
while( nResult == SQLITE_BUSY && nRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nRetryCount;
|
||||
|
||||
_LOG( LWAR, "Local db prepare sql SQLITE_BUSY. Retry[%d]", nRetryCount );
|
||||
|
||||
nResult = sqlite3_prepare( m_pLocalDb, strQuery.c_str(), strQuery.size(), &m_pLocalStmt, NULL );
|
||||
}
|
||||
|
||||
// 최종 실패시...
|
||||
if( nResult != SQLITE_OK )
|
||||
{
|
||||
LOG( LERR, "sqlite3 prepare sql stmt create failed. [%d][%s]", nResult, sqlite3_errmsg( m_pLocalDb ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// garbage clean 작업 수행.
|
||||
// - bGarbageCleanSlow : unlink 수행 후 sleep 를 호출하는 slow 모드로 동작시킬지 여부.
|
||||
// - bGarbageForceDelete: unlink 처리시 access time 정보를 check 할지 여부
|
||||
bool CGarbageClean::Start( bool bGarbageCleanSlow, bool bGarbageForceDelete )
|
||||
{
|
||||
// 작업 소요 시간 정보 계산을 위한 변수.
|
||||
// - 시간 동기화로 인한 문제를 해결하기 위해 clock_gettime() 함수 사용.
|
||||
struct timespec timeStart;
|
||||
struct timespec timeEnd;
|
||||
double dTimeInterval = 0.0;
|
||||
|
||||
memset( &timeStart, 0x00, sizeof( struct timespec ) );
|
||||
memset( &timeEnd, 0x00, sizeof( struct timespec ) );
|
||||
|
||||
// Meta 에 존재하지 않는 파일 삭제 처리 관련...
|
||||
// 현재 시간 정보 추출하여.. 정의된 보호 시간을 뺀다.
|
||||
time_t unlinkPreventMaxTime;
|
||||
unlinkPreventMaxTime = time( 0 ) - ( UNLINK_PREVENT_MAX_DAY * 24 * 60 * 60 );
|
||||
|
||||
// 작업 시작 로깅 및 시작 시간 정보 추출.
|
||||
_LOG( LINF, "Garbage clean start." );
|
||||
_LOG( LINF, "- unlink speed: %s", ( bGarbageCleanSlow == true ? "slow" : "normal" ) );
|
||||
_LOG( LINF, "- unlink modifiy time max prevent : %d day, %ld time.", UNLINK_PREVENT_MAX_DAY, unlinkPreventMaxTime);
|
||||
if( bGarbageForceDelete == false )
|
||||
{
|
||||
_LOG( LINF, "- unlink access time max prevent : %d day, %ld time.", UNLINK_PREVENT_MAX_DAY, unlinkPreventMaxTime );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LINF, "- unlink access time not check. force unlink." );
|
||||
}
|
||||
|
||||
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeStart );
|
||||
|
||||
// 통계 정보 저장을 위한 변수
|
||||
// 각 폴더별 삭제 count
|
||||
unsigned long long ullDirTotalCount = 0; // 각 폴더별 처리한 count 정보 저장.
|
||||
unsigned long long ullDirUnlinkCount = 0; // 각 폴더별 삭제한 count 정보 저장.
|
||||
|
||||
unsigned long long ullTotalCount = 0; // 누적 처리 count 정보 저장.
|
||||
unsigned long long ullTotalUnlinkCount = 0; // 누적 삭제 count 정보를 저장
|
||||
unsigned long long ullTotalUnlinkSize = 0; // 누적 삭제 Size 정보를 저장
|
||||
|
||||
|
||||
// 1. 설정된 대상 경로를 바탕으로.. 실제 최하위 서비스 폴더 목록 list 를 추출한다.
|
||||
if( GetTargetDir() == false )
|
||||
{
|
||||
// 오류 발생시.. 해당 모듈에서 로깅 처리 했으므로.. 실패로 처리.
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LINF, "Garbage clean [%s] sub service directory count [%lu]", CProcessConfig::GetInstance()->GetJobRoot(), m_vecTargetDirectory.size() );
|
||||
}
|
||||
|
||||
// 2. 추출된 각 경로에 대해.. 파일 정보를 추출하여.. 검증 수행.
|
||||
DIR * pDir = NULL;
|
||||
struct dirent * pDirEntry = NULL;
|
||||
std::vector< std::string >::iterator it;
|
||||
|
||||
// 파일에 대한 정보를 저장할 변수.
|
||||
std::string strFullFileName;
|
||||
int nFullFileNameExistCount;
|
||||
struct stat stFileStat;
|
||||
|
||||
// Local sqlite db 관련 변수
|
||||
int nResult = 0;
|
||||
int nRetryCount = 0;
|
||||
|
||||
|
||||
for( it = m_vecTargetDirectory.begin(); it != m_vecTargetDirectory.end(); ++it )
|
||||
{
|
||||
pDir = opendir( it->c_str() );
|
||||
if( pDir == NULL )
|
||||
{
|
||||
// 폴더 open 실패시...
|
||||
// - 오류 내역 로깅하고 계속 진행.
|
||||
|
||||
LOG( LERR, "directory[%s] open faild. [%d][%s]", it->c_str(), errno, strerror( errno ) );
|
||||
continue;
|
||||
}
|
||||
|
||||
// 개별 폴더 통계 초기화.
|
||||
ullDirTotalCount = 0;
|
||||
ullDirUnlinkCount = 0;
|
||||
|
||||
_LOG( LINF, "Garbage clean [%s] start", it->c_str() );
|
||||
|
||||
while( ( pDirEntry = readdir( pDir ) ) != NULL )
|
||||
{
|
||||
// 만약 하위에 폴더, 링크, FIFO 등이 존재할 수 있으므로...
|
||||
// 이를 제외하고 only 파일만 비교 처리.
|
||||
|
||||
// man 5 dir
|
||||
// d_type 정의 내역.
|
||||
// #define DT_DIR 4
|
||||
// #define DT_REG 8
|
||||
|
||||
if( pDirEntry->d_type == DT_REG )
|
||||
{
|
||||
// '.'으로 시작하는 숨김 파일 등은 제외 처리. ( '.', '..', '..sujournal' 등 )
|
||||
if( pDirEntry->d_name[0] == '.' )
|
||||
continue;
|
||||
|
||||
// 진행 사항 로깅.
|
||||
if( ullDirTotalCount % 10000 == 0 && ullDirTotalCount != 0 )
|
||||
{
|
||||
_LOG( LINF, "Garbage checking. [%s] count[%10llu] unlink[%10llu]"
|
||||
, it->c_str(), ullDirTotalCount, ullDirUnlinkCount );
|
||||
}
|
||||
|
||||
// 통계 정보 증감 처리
|
||||
++ullDirTotalCount;
|
||||
|
||||
// 파일명 전체 경로 정보 조합
|
||||
strFullFileName = it->c_str();
|
||||
strFullFileName.append( "/" );
|
||||
strFullFileName.append( pDirEntry->d_name );
|
||||
|
||||
// 이제 실제로 파일이 존재하는지 local DB 에서 확인한다.
|
||||
// select query 에 변수 bind 처리. ( bind 값을 1부터 시작 )
|
||||
// - bind 처리시.. SQLITE_TRANSIENT 도 사용할 수 있으나.. 내부 copy 로 속도 느림
|
||||
// - query 처리 중간에 변수값이 변경될 가능성 없으므로.. SQLITE_STATIC 사용.
|
||||
nResult = sqlite3_bind_text( m_pLocalStmt, 1, strFullFileName.c_str(), strFullFileName.size(), SQLITE_STATIC );
|
||||
if( nResult != SQLITE_OK )
|
||||
{
|
||||
// bind 처리 실패시....
|
||||
LOG( LERR, "sqlite3 bind error.[%s] [%d][%s]", strFullFileName.c_str(), nResult, sqlite3_errmsg( m_pLocalDb ) );
|
||||
|
||||
// reset 처리 후.. 다음 항목을 진행 처리
|
||||
sqlite3_reset( m_pLocalStmt );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Query 실행.
|
||||
nResult = sqlite3_step( m_pLocalStmt );
|
||||
nRetryCount = 0;
|
||||
|
||||
while( nResult == SQLITE_BUSY && nRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nRetryCount;
|
||||
|
||||
LOG( LWAR, "sqlite3 step SQLITE_BUSY, Retry[%d]", nRetryCount );
|
||||
|
||||
// query 재시도
|
||||
nResult = sqlite3_step( m_pLocalStmt );
|
||||
}
|
||||
|
||||
// SELECT query 수행시....
|
||||
// - 결과 row 가 2 개 이상인 경우....SQLITE_ROW(100) 을 반환하고.. 더 이상 조회 결과가 없는 경우.....SQLITE_DONE (101) 반환.
|
||||
// - 결과가 없거나...결과 row 가 1개 인 경우.. SQLITE_DONE (101) 을 반환한다.
|
||||
// - 따라서 SQLITE_DONE 을 반환한다고 해서.. 결과가 없는 것으로 판단하면 안됨.
|
||||
|
||||
// select query 수행시 오류가 발생한 경우...
|
||||
if( nResult != SQLITE_ROW && nResult != SQLITE_DONE )
|
||||
{
|
||||
LOG( LERR, "sqlite3 step error.[%s] [%d][%s]", strFullFileName.c_str(), nResult, sqlite3_errmsg( m_pLocalDb ) );
|
||||
|
||||
// 만약 오류 코드가 SQLITE_ERROR, SQLITE_INTERNAL, SQLITE_EMPTY, SQLITE_SCHEMA 등이라면.... DB에 문제 있는 것으로 보고.. 작업 중지.
|
||||
if( nResult == SQLITE_ERROR || nResult == SQLITE_INTERNAL || nResult == SQLITE_EMPTY || nResult == SQLITE_SCHEMA )
|
||||
{
|
||||
LOG( LERR, "sqlite3 db not valid. garbage clean stop.");
|
||||
|
||||
closedir( pDir );
|
||||
pDir = NULL;
|
||||
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 그 외 오류에 대해서는 무시하고 계속 진행.
|
||||
}
|
||||
else
|
||||
{
|
||||
// SELECT query 수행시....
|
||||
// - 결과 row 가 2 개 이상인 경우....SQLITE_ROW(100) 을 반환하고.. 더 이상 조회 결과가 없는 경우.....SQLITE_DONE (101) 반환.
|
||||
// - 결과가 없거나...결과 row 가 1개 인 경우.. SQLITE_DONE (101) 을 반환한다.
|
||||
// - 따라서 SQLITE_DONE 을 반환한다고 해서.. 결과가 없는 것으로 판단하면 안됨.
|
||||
|
||||
// strQuery = "SELECT count( resource_id ) +1 FROM meta WHERE filename_hash = ? ;";
|
||||
// - sqlite3_column_int() 호출시 오류가 발생할 경우에 대한 처리를 위해 +1 처리함.
|
||||
|
||||
// 조회 결과를 변수에 저장 처리.
|
||||
nFullFileNameExistCount = sqlite3_column_int( m_pLocalStmt, 0 );
|
||||
|
||||
// nFullFileNameExistCount == 0 : sqlite3_column_int() 함수 오류로 default 값이 반환된 경우...
|
||||
// nFullFileNameExistCount == 1 : Meta 에 해당 파일 정보 미존재시.
|
||||
// 그 이외에는 Meta 에 해당 파일 정보 존재로 처리.
|
||||
|
||||
//_LOG( LINF, "[GARBAGE] Meta SELECT. [%s] count [%d]", strFullFileName.c_str(), nFullFileNameExistCount -1 );
|
||||
|
||||
if( nFullFileNameExistCount == 1 )
|
||||
{
|
||||
// Meta 에 해당 파일 미존재시.
|
||||
|
||||
// 해당 파일 속성 정보 추출.
|
||||
if( stat( strFullFileName.c_str(), &stFileStat ) == 0 )
|
||||
{
|
||||
// 만약 해당 파일이 Meta에 존재하지 않는 경우...
|
||||
// 최종 수정일 정보를 추출하여... UNLINK_PREVENT_MAX_DAY 일이 지났는지 확인 후.
|
||||
// UNLINK_PREVENT_MAX_DAY 일 이상된 content 인 경우.. 삭제 처리한다.
|
||||
|
||||
// 파일의 최종 수정 시간이 UNLINK_PREVENT_MAX_DAY 일 이상이면 삭제 처리
|
||||
// - difftime() 함수를 사용해도 되지만.. CPU 사용률이 거의 100% 수준이라... CPU 부하 낮추기 위해 날코딩 처리.
|
||||
if( stFileStat.st_mtime < unlinkPreventMaxTime )
|
||||
{
|
||||
// access time 을 check 하지 않고.. unlink 처리 수행한다.
|
||||
if( bGarbageForceDelete == true )
|
||||
{
|
||||
// 해당 파일 unlink 처리.
|
||||
unlink( strFullFileName.c_str() );
|
||||
|
||||
++ullDirUnlinkCount;
|
||||
ullTotalUnlinkSize += stFileStat.st_size;
|
||||
|
||||
_LOG( LINF, "[GARBAGE] unlink with force[%s] mtime[%ld] size[%jd]"
|
||||
, strFullFileName.c_str(), stFileStat.st_mtime, stFileStat.st_size );
|
||||
|
||||
// unlink slow 모드인 경우.. unlink 처리 후.. 정의된 시간만큼 sleep 처리한다.
|
||||
if( bGarbageCleanSlow == true )
|
||||
sleep( UNLINK_SLOW_MODE_SLEEP );
|
||||
}
|
||||
else if( stFileStat.st_atime < unlinkPreventMaxTime )
|
||||
{
|
||||
// -f 옵션을 사용하지 않은 상태이고 access time 이 2일 이상인 경우
|
||||
// 삭제 처리를 수행한다.
|
||||
|
||||
// 해당 파일 unlink 처리.
|
||||
unlink( strFullFileName.c_str() );
|
||||
|
||||
++ullDirUnlinkCount;
|
||||
ullTotalUnlinkSize += stFileStat.st_size;
|
||||
|
||||
_LOG( LINF, "[GARBAGE] unlink[%s] atime[%ld] mtime[%ld] size[%jd]"
|
||||
, strFullFileName.c_str(), stFileStat.st_atime, stFileStat.st_mtime, stFileStat.st_size );
|
||||
|
||||
// unlink slow 모드인 경우.. unlink 처리 후.. 정의된 시간만큼 sleep 처리한다.
|
||||
if( bGarbageCleanSlow == true )
|
||||
sleep( UNLINK_SLOW_MODE_SLEEP );
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// 파일의 최종 수정 시간은 UNLINK_PREVENT_MAX_DAY 값을 초과했지만.
|
||||
// force mode 가 아니고, access 시간이 UNLINK_PREVENT_MAX_DAY 일 이내인 경우..
|
||||
// - 로깅만 처리.
|
||||
_LOG( LINF, "[%s] meta not exist, but access occurred within [%d] day. a[%ld] m[%ld] limit[%ld] size[%jd]"
|
||||
, strFullFileName.c_str(), UNLINK_PREVENT_MAX_DAY, stFileStat.st_atime, stFileStat.st_mtime, unlinkPreventMaxTime, stFileStat.st_size );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 파일의 최종 수정 시간이 UNLINK_PREVENT_MAX_DAY 일 이내인 경우..
|
||||
// - 로깅만 처리.
|
||||
_LOG( LINF, "[%s] meta not exist, but modify occurred within [%d] day. a[%ld] m[%ld] limit[%ld] size[%jd]"
|
||||
, strFullFileName.c_str(), UNLINK_PREVENT_MAX_DAY, stFileStat.st_atime, stFileStat.st_mtime, unlinkPreventMaxTime, stFileStat.st_size );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// stat 호출 오류인 경우...
|
||||
// - multi thread 가 아직 1 process 구조라서.. errno 를 그대로 사용....
|
||||
// - 만약 multi thread 구조로 변경할 경우.. errno 그대로 사용하면 안됨.
|
||||
|
||||
// garbage clean 를 very slow 모드로 동작시킬 경우...
|
||||
// - 작업 시간이 오래 걸려...
|
||||
// - 해당 물리 파일이 rc_gcmd 등의 다른 프로세스에 의해 삭제될 가능성이 존재함.
|
||||
// - 이를 경우... 다음과 같은 오류 발생
|
||||
// [17:25:42] [ERR ] [GARBAGE] meta not exist. but file stat call error. name[/stg/node0/250/22a753b521dfc43ae468854c2089e8ef] [2][No such file or directory]
|
||||
// - 따라서.. 다른 프로세스에 의해 삭제된 경우.. 불필요하게 오류 찍을 필요 없다.
|
||||
|
||||
if( errno != ENOENT )
|
||||
{
|
||||
_LOG( LERR, "[GARBAGE] meta not exist. but file stat call error. name[%s] [%d][%s]", strFullFileName.c_str(), errno, strerror( errno ) );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if( nFullFileNameExistCount == 0 )
|
||||
{
|
||||
//sqlite3_column_int() 함수 오류로 default 값이 반환된 경우...
|
||||
LOG( LERR, "sqlite3 column get error.[%s] [%d][%s]", strFullFileName.c_str(), nFullFileNameExistCount, sqlite3_errmsg( m_pLocalDb ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Meta 에 해당 파일 정보 존재로 처리.
|
||||
//_LOG( LINF, "[GARBAGE] Meta OK. [%s] count [%d]", strFullFileName.c_str(), nFullFileNameExistCount - 1 );
|
||||
}
|
||||
}
|
||||
|
||||
// Query 수행된 경우.. prepared 문을 재사용하기 위해 reset 처리한다.
|
||||
sqlite3_reset( m_pLocalStmt );
|
||||
}
|
||||
|
||||
} // while( ( pDirEntry = readdir( pDir ) ) != NULL )
|
||||
|
||||
|
||||
// open 처리된 dir 객체를 close 처리한다.
|
||||
closedir( pDir );
|
||||
pDir = NULL;
|
||||
|
||||
|
||||
// 통계 저장 처리
|
||||
ullTotalCount += ullDirTotalCount;
|
||||
ullTotalUnlinkCount += ullDirUnlinkCount;
|
||||
|
||||
|
||||
_LOG( LINF, "Garbage clean [%s] end. unlink count[ %llu / %llu ] ", it->c_str(), ullDirUnlinkCount, ullDirTotalCount );
|
||||
|
||||
|
||||
} // for( it = m_vecTargetDirectory.begin(); it != m_vecTargetDirectory.end(); ++it )
|
||||
|
||||
|
||||
// 작업 완료 후 stmt 객체 clear
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
// 작업 완료시 시간 정보 추출 후 소요시간 계산.
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeEnd );
|
||||
|
||||
dTimeInterval = timeEnd.tv_sec - timeStart.tv_sec;
|
||||
dTimeInterval += ( timeEnd.tv_nsec - timeStart.tv_nsec ) * 1e-9;
|
||||
|
||||
_LOG( LINF, "Garbage clean complete. path[%s] unlink mode[%s] elapsed time: %.1lf sec."
|
||||
, CProcessConfig::GetInstance()->GetJobRoot()
|
||||
, ( bGarbageCleanSlow == true ? "slow" : "normal" )
|
||||
, dTimeInterval );
|
||||
|
||||
_LOG( LINF, "Garbage clean report: total count[%llu] unlink count[%llu] unlink size[%llu byte]"
|
||||
, ullTotalCount, ullTotalUnlinkCount, ullTotalUnlinkSize );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 설정된 기본 경로를 바탕으로.. 작업대상 폴더명 정보를 추출하여.. 멤버변수에 저장 처리.
|
||||
bool CGarbageClean::GetTargetDir()
|
||||
{
|
||||
// 사용자가 입력했거나.. 기본으로 설정된 경로에서...
|
||||
// 실제 정리 작업을 수행할 서비스 폴더를 추출한다.
|
||||
// - 서비스 폴더가 아닌 경우.. 운영 목적으로 설정한 파일들이 존재하여..
|
||||
// - 삭제될 수 있으므로...
|
||||
// - 서비스 폴더에 대해서만 정리 작업이 수행되도록 한다.
|
||||
|
||||
// 추출 방식
|
||||
// - 직접 코드로 opendir, readdir 함수를 이용하여 추출 가능하지만....
|
||||
// - 불필요하게 시간이 오래 걸리고.. 버그의 가능성 존재.
|
||||
// - 따라서 외부 명령어를 이용하여..
|
||||
// - 하위 폴더 정보를 추출하고.... 서비스 폴더인지 검증 처리.
|
||||
|
||||
// 외부 tool
|
||||
// find /stg/node0 -type d -print
|
||||
// find /stg/node0 -type d -print | grep '/[1-9]*$'
|
||||
|
||||
// 참고사항
|
||||
// main 모듈에서 root 경로에 대한
|
||||
// 맨 끝의 '/' 제거 및
|
||||
// 존재 여부 확인 했으므로... 그냥 사용하면 됨.
|
||||
|
||||
char tempBuffer[2048];
|
||||
std::string strTempPath;
|
||||
FILE * fd = NULL;
|
||||
|
||||
// trim 처리 관련 변수
|
||||
std::string::size_type pos;
|
||||
|
||||
|
||||
// 명령어 조립
|
||||
snprintf( tempBuffer, sizeof( tempBuffer ), "find %s -type d -print | grep '/[0-9]*$'", CProcessConfig::GetInstance()->GetJobRoot() );
|
||||
fd = popen( tempBuffer, "r" );
|
||||
if( fd == NULL )
|
||||
{
|
||||
int errorNum = errno;
|
||||
LOG( LERR, "service directory search error. popen error. [%d][%s]", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 작업 대상 경로를 저장할 멤버 변수 초기화.
|
||||
m_vecTargetDirectory.clear();
|
||||
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
while( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
|
||||
{
|
||||
// 결과 맨 끝에 줄바꿈 문자가 있어.. trim 처리 필요
|
||||
strTempPath = tempBuffer;
|
||||
if( strTempPath.length() == 0 )
|
||||
continue;
|
||||
|
||||
pos = strTempPath.find_last_not_of( " \a\b\f\n\r\t\v" );
|
||||
if( pos != std::string::npos )
|
||||
strTempPath.erase( pos + 1 );
|
||||
|
||||
// 원래는 추출된 전체 경로.. (/stg/node0/290 ) 에서
|
||||
// 마지막 290 정보 추출하여 숫자인지 비교해야 하지만...
|
||||
// 귀찬아서.. 외부 명령어의 grep 으로 비교 처리 넣어버림...
|
||||
|
||||
// 따라서.. 추출된 폴더 정보를 바로 저장 처리하면 됨.
|
||||
|
||||
m_vecTargetDirectory.push_back( strTempPath );
|
||||
}
|
||||
|
||||
pclose( fd );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/****************************************************************************
|
||||
Garbage content unlink module
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2015/10/22
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage Team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
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 __GARBAGE_CONTENT_CLEAN_H__
|
||||
#define __GARBAGE_CONTENT_CLEAN_H__
|
||||
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
// 본 클래스는
|
||||
// 지정된 경로의 content 가 sqlite local db 에 존재하는지 검사하여...
|
||||
// garbage content 를 판별하고 unlink 처리를 수행하는 모듈이다.
|
||||
class CGarbageClean
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
// - pLocalDb : sqlite memory db 객체에 대한 포인트
|
||||
CGarbageClean( sqlite3 * pLocalDb );
|
||||
|
||||
// 소멸자
|
||||
~CGarbageClean();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// 객체 초기화 함수
|
||||
// - local db 와의 연결 생성, 처리 결과를 저장할 table 생성, Query 객체 생성 처리.
|
||||
bool Init();
|
||||
|
||||
// garbage clean 작업 수행.
|
||||
// - bGarbageCleanSlow : unlink 수행 후 sleep 를 호출하는 slow 모드로 동작시킬지 여부.
|
||||
// - bGarbageForceDelete: unlink 처리시 access time 정보를 check 할지 여부
|
||||
bool Start( bool bGarbageCleanSlow, bool bGarbageForceDelete );
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// sqlite3 local db 객체.
|
||||
sqlite3 * m_pLocalDb;
|
||||
sqlite3_stmt * m_pLocalStmt;
|
||||
|
||||
|
||||
// 작업 대상 경로의 최하위 서비스 폴더명 정보를 저장하기 위한 변수
|
||||
std::vector< std::string > m_vecTargetDirectory;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// 설정된 기본 경로를 바탕으로.. 작업대상 폴더명 정보를 추출하여.. 멤버변수에 저장 처리.
|
||||
bool GetTargetDir();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __GARBAGE_CONTENT_CLEAN_H__ */
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
#include "IntegrityCheck.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
#include "Logger.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
|
||||
#define SQLITE_DB_LOCK_TIMEOUT 1000 // SQLITE_BUSY 상황 발생 문제 해결을 위해 DB Lock 관련 timeout 설정값. (1 sec)
|
||||
#define SQLITE_DB_LOCK_MAX_RETRY 60 // SQLITE_BUSY 발생시 재시도 최대 횟수
|
||||
|
||||
|
||||
|
||||
// 생성자
|
||||
// - pLocalDb : sqlite memory db 객체에 대한 포인트
|
||||
CIntegrityCheck::CIntegrityCheck( sqlite3 * pLocalDb )
|
||||
: m_pLocalStmt( NULL )
|
||||
{
|
||||
m_pLocalDb = pLocalDb;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 소멸자
|
||||
CIntegrityCheck::~CIntegrityCheck()
|
||||
{
|
||||
// Local DB 관련 stmt clear 처리.
|
||||
// - local db 객체는 memory db 로서 전역 객체이므로.. close 하지 않는다.
|
||||
if( m_pLocalStmt != NULL )
|
||||
{
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 객체 초기화 함수
|
||||
// - local db 에서 조회 처리를 수행할 stmt 생성
|
||||
bool CIntegrityCheck::Init()
|
||||
{
|
||||
// Local sqlite db 관련 변수
|
||||
int nResult = 0;
|
||||
int nRetryCount = 0;
|
||||
|
||||
// 조회 처리를 수행할 stmt 객체 생성.
|
||||
// strQuery = "CREATE TABLE meta( resource_id integer, filename_hash text, get_content_length integer, deleted_yn text )";
|
||||
std::string strQuery;
|
||||
strQuery = "SELECT resource_id, filename_hash, get_content_length FROM meta where deleted_yn = 'N';";
|
||||
|
||||
nResult = sqlite3_prepare( m_pLocalDb, strQuery.c_str(), strQuery.size(), &m_pLocalStmt, NULL );
|
||||
|
||||
// sqlite3_prepare() 함수 실행시 SQLITE_BUSY 오류가 발생할 수 있으므로.. 재시도 로직을 추가한다.
|
||||
while( nResult == SQLITE_BUSY && nRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nRetryCount;
|
||||
|
||||
_LOG( LWAR, "Local db prepare sql SQLITE_BUSY. Retry[%d]", nRetryCount );
|
||||
|
||||
nResult = sqlite3_prepare( m_pLocalDb, strQuery.c_str(), strQuery.size(), &m_pLocalStmt, NULL );
|
||||
}
|
||||
|
||||
// 최종 실패시...
|
||||
if( nResult != SQLITE_OK )
|
||||
{
|
||||
LOG( LERR, "sqlite3 prepare sql stmt create failed. [%d][%s]", nResult, sqlite3_errmsg( m_pLocalDb ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// integrity check 작업 수행.
|
||||
bool CIntegrityCheck::Start()
|
||||
{
|
||||
// 작업 소요 시간 정보 계산을 위한 변수.
|
||||
// - 시간 동기화로 인한 문제를 해결하기 위해 clock_gettime() 함수 사용.
|
||||
struct timespec timeStart;
|
||||
struct timespec timeEnd;
|
||||
double dTimeInterval = 0.0;
|
||||
|
||||
memset( &timeStart, 0x00, sizeof( struct timespec ) );
|
||||
memset( &timeEnd, 0x00, sizeof( struct timespec ) );
|
||||
|
||||
// 작업 시작 로깅 및 시작 시간 정보 추출.
|
||||
_LOG( LINF, "Integrity check start." );
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeStart );
|
||||
|
||||
// Local sqlite db 관련 변수
|
||||
int nResult = 0;
|
||||
int nRetryCount = 0;
|
||||
|
||||
|
||||
// zero 파일 생성 처리 관련하여....nobody user 에 대한 uid, gid 정보 추출
|
||||
uid_t nobodyUid;
|
||||
gid_t nobodyGid;
|
||||
struct passwd * pStPwNobody;
|
||||
|
||||
pStPwNobody = getpwnam( "nobody" );
|
||||
if( pStPwNobody != NULL )
|
||||
{
|
||||
nobodyUid = pStPwNobody->pw_uid;
|
||||
nobodyGid = pStPwNobody->pw_gid;
|
||||
}
|
||||
else
|
||||
{
|
||||
nobodyUid = 65534;
|
||||
nobodyGid = 65534;
|
||||
}
|
||||
|
||||
// select query 수행.
|
||||
nResult = sqlite3_step( m_pLocalStmt );
|
||||
nRetryCount = 0;
|
||||
|
||||
while( nResult == SQLITE_BUSY && nRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nRetryCount;
|
||||
|
||||
LOG( LWAR, "sqlite3 step SQLITE_BUSY, Retry[%d]", nRetryCount );
|
||||
|
||||
// query 재시도
|
||||
nResult = sqlite3_step( m_pLocalStmt );
|
||||
}
|
||||
|
||||
|
||||
// select 조회 결과가 존재하는 경우... SQLITE_ROW (100 ) 을 반환...
|
||||
// select 조회 결과가 없는 경우.. SQLITE_DONE (101) 을 반환.
|
||||
|
||||
// select query 수행시 오류가 발생한 경우...
|
||||
if( nResult != SQLITE_ROW && nResult != SQLITE_DONE )
|
||||
{
|
||||
LOG( LERR, "sqlite3 step error.[%d][%s]", nResult, sqlite3_errmsg( m_pLocalDb ) );
|
||||
|
||||
// Local DB 오류 발생시.. DB 에 문제가 있는 것으로 보고.. DB 객체를 초기화한다.
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long long ullSelectCount = 0;
|
||||
unsigned long long nResourceId = 0;
|
||||
std::string strFileName;
|
||||
unsigned long long nFileSize = 0;
|
||||
struct stat stFileStat;
|
||||
|
||||
|
||||
// NEW 2015-11-06 huibong
|
||||
// - #25000 에 따른 통계 기능 보강
|
||||
unsigned long long ullTotalCountSizeMismatch = 0;
|
||||
unsigned long long ullTotalCountZeroAutoCreate = 0;
|
||||
unsigned long long ullTotalCountNotExist = 0;
|
||||
|
||||
|
||||
|
||||
// select Query 가 정상적으로 수행된 경우....
|
||||
while( nResult == SQLITE_ROW )
|
||||
{
|
||||
// Query 결과를 변수에 저장 처리.
|
||||
nResourceId = sqlite3_column_int64( m_pLocalStmt, 0 );
|
||||
|
||||
strFileName.clear();
|
||||
strFileName = (char *)sqlite3_column_text( m_pLocalStmt, 1 );
|
||||
|
||||
nFileSize = sqlite3_column_int64( m_pLocalStmt, 2 );
|
||||
|
||||
// 체크 항목.
|
||||
// 파일 존재시
|
||||
// - size mismatch 체크
|
||||
// 파일 존재 안할 경우...
|
||||
// - size 가 0 인 경우.. 0 파일 자동 생성 처리.
|
||||
// - 그 외는 not exist 처리.
|
||||
|
||||
|
||||
// 해당 파일 속성 정보 추출.
|
||||
if( stat( strFileName.c_str(), &stFileStat ) == 0 )
|
||||
{
|
||||
// 해당 파일이 존재하는 경우...
|
||||
// - Size 비교.
|
||||
if( ( unsigned long long )( stFileStat.st_size ) != nFileSize )
|
||||
{
|
||||
// 파일 size 가 서로 다른 경우..
|
||||
// - 해당 파일이 업로드 중인 경우에도 .. meta 정보와 물리 정보가 서로 다를 수 있음.
|
||||
// - 따라서 본 프로세스 기동 전 업로드 방지 처리가 되어있는지 체크 후 동작 함.
|
||||
++ullTotalCountSizeMismatch;
|
||||
|
||||
_LOG( LERR, "[INTEGRITY] File size mismatch. resource_id[%llu] name[%s] size[%llu] local[%jd]", nResourceId, strFileName.c_str(), nFileSize, stFileStat.st_size );
|
||||
}
|
||||
|
||||
// 정규 파일여부 검사
|
||||
// - S_ISREG( stFileStat.st_mode ) 을 사용하여 검사 가능하나...
|
||||
// - 정합성 검증이므로.. 굳이 할 필요 없으며.. 할 경우.. 처리 속도 저하가 발행하므로.. 안한다.
|
||||
}
|
||||
else
|
||||
{
|
||||
// stat 호출 오류인 경우...
|
||||
// - multi thread 가 아직 1 process 구조라서.. errno 를 그대로 사용....
|
||||
// - 만약 multi thread 구조로 변경할 경우.. errno 그대로 사용하면 안됨.
|
||||
|
||||
if( errno == ENOENT )
|
||||
{
|
||||
// 해당 파일이 존재하지 않는 경우....
|
||||
// - 0 파일인 경우.. 자동 생성 처리...
|
||||
// - 그 외에는 로깅 처리.
|
||||
|
||||
if( nFileSize == 0 )
|
||||
{
|
||||
// 해당 파일이 0 파일인 경우.. 자동으로 생성 처리한다.
|
||||
FILE * pFile = fopen( strFileName.c_str(), "w" );
|
||||
if( pFile == NULL )
|
||||
{
|
||||
_LOG( LERR, "[INTEGRITY] Zero File not exist. auto create error. [%llu][%s] [%d][%s]", nResourceId, strFileName.c_str(), errno, strerror( errno ) );
|
||||
|
||||
// ADD 2015-12-08 huibong
|
||||
// - 0 파일 생성 실패시.. not exist count 가 증가되도록 처리. (#25462)
|
||||
++ullTotalCountNotExist;
|
||||
}
|
||||
else
|
||||
{
|
||||
fclose( pFile );
|
||||
|
||||
// 권한을 nobody:nobody 로 변경 처리
|
||||
if( chown( strFileName.c_str(), nobodyUid, nobodyGid ) != 0 )
|
||||
{
|
||||
_LOG( LERR, "[INTEGRITY] Zero File auto create. but chown() to nobody faild. [%llu][%s] [%d][%s]", nResourceId, strFileName.c_str(), errno, strerror( errno ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
++ullTotalCountZeroAutoCreate;
|
||||
_LOG( LINF, "Integrity zero file auto create. resource_id[%llu] name[%s] size[%llu]", nResourceId, strFileName.c_str(), nFileSize );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 0 파일이 아닌 파일이 존재하지 않는 경우..
|
||||
++ullTotalCountNotExist;
|
||||
_LOG( LERR, "[INTEGRITY] File not exist. resource_id[%llu] name[%s] size[%llu]", nResourceId, strFileName.c_str(), nFileSize );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// stat 호출 오류...
|
||||
_LOG( LERR, "[INTEGRITY] file stat call error. resource_id[%llu] name[%s] size[%llu] [%d][%s]", nResourceId, strFileName.c_str(), nFileSize, errno, strerror( errno ) );
|
||||
}
|
||||
}
|
||||
|
||||
// 작업 진행 사항 로깅.
|
||||
++ullSelectCount;
|
||||
if( ullSelectCount % 10000 == 0 && ullSelectCount != 0 )
|
||||
{
|
||||
_LOG( LINF, "Integrity checking. count[%10llu]", ullSelectCount );
|
||||
}
|
||||
|
||||
|
||||
// Next 결과 처리.
|
||||
nResult = sqlite3_step( m_pLocalStmt );
|
||||
nRetryCount = 0;
|
||||
|
||||
while( nResult == SQLITE_BUSY && nRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nRetryCount;
|
||||
|
||||
LOG( LWAR, "sqlite3 step SQLITE_BUSY, Retry[%d]", nRetryCount );
|
||||
|
||||
// query 재시도
|
||||
nResult = sqlite3_step( m_pLocalStmt );
|
||||
}
|
||||
|
||||
if( nResult != SQLITE_ROW && nResult != SQLITE_DONE )
|
||||
{
|
||||
LOG( LERR, "sqlite3 step error.[%d][%s]", nResult, sqlite3_errmsg( m_pLocalDb ) );
|
||||
|
||||
// Local DB 오류 발생시.. stmt 객체 초기화.
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 작업 완료 후 stmt clear
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
// 작업 완료시 시간 정보 추출 후 소요시간 계산.
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeEnd );
|
||||
|
||||
dTimeInterval = timeEnd.tv_sec - timeStart.tv_sec;
|
||||
dTimeInterval += ( timeEnd.tv_nsec - timeStart.tv_nsec ) * 1e-9;
|
||||
|
||||
_LOG( LINF, "Integrity check complete. path[%s] total count[%llu], elapsed time: %.1lf sec"
|
||||
, CProcessConfig::GetInstance()->GetJobRoot(), ullSelectCount, dTimeInterval );
|
||||
|
||||
_LOG( LINF, "Integrity check report: not exist[%llu], size mismatch[%llu], zero auto create[%llu]"
|
||||
, ullTotalCountNotExist, ullTotalCountSizeMismatch, ullTotalCountZeroAutoCreate );
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/****************************************************************************
|
||||
Content integrity check module
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2015/10/20
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage Team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
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 __CONTENT_INTEGRITY_CHECK_H__
|
||||
#define __CONTENT_INTEGRITY_CHECK_H__
|
||||
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
// 본 클래스는
|
||||
// sqlite local db 에 저장된 content 정보를 바탕으로
|
||||
// 정합성 검증( integrity check ) 을 수행하는 모듈이다.
|
||||
class CIntegrityCheck
|
||||
{
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
// - pLocalDb : sqlite memory db 객체에 대한 포인트
|
||||
CIntegrityCheck( sqlite3 * pLocalDb );
|
||||
|
||||
// 소멸자
|
||||
~CIntegrityCheck();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// 객체 초기화 함수
|
||||
// - local db 에서 조회 처리를 수행할 stmt 생성
|
||||
bool Init();
|
||||
|
||||
// integrity check 작업 수행.
|
||||
bool Start();
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// sqlite3 local db 객체.
|
||||
sqlite3 * m_pLocalDb;
|
||||
sqlite3_stmt * m_pLocalStmt;
|
||||
|
||||
};
|
||||
|
||||
#endif /* __CONTENT_INTEGRITY_CHECK_H__ */
|
||||
|
||||
@@ -0,0 +1,814 @@
|
||||
#include "Main.h"
|
||||
#include "ProcessConfig.h"
|
||||
#include "Logger.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "MetaCopy.h"
|
||||
#include "IntegrityCheck.h"
|
||||
#include "GarbageClean.h"
|
||||
|
||||
|
||||
// FHS 의 업로드 방지 상태를 판단하기 위한 값.
|
||||
#define FILE_SERVICE_OUT "/user/service/fhs.out"
|
||||
#define FILE_SERVICE_RONLY "/user/service/fhs.ronly"
|
||||
#define FILE_VMASD_DISABLE "/user/service/vmasd.disable"
|
||||
|
||||
|
||||
// sqlite memory db 사용 관련 전역 객체..
|
||||
// - 시그널 처리 관련 종료시 삭제 처리 목적으로 전역변수 선언.
|
||||
sqlite3 * g_pLocalDb = NULL;
|
||||
|
||||
|
||||
int main( int argc, char * argv[] )
|
||||
{
|
||||
// 기본 변수.
|
||||
std::string strConfigFileName = DEFAULT_CONFIG_FILE; // conf 관련 처리를 위한 변수.
|
||||
std::string szErrorMessage; // 에러 메시지 관련 처리를 위한 변수.
|
||||
|
||||
|
||||
// 1. 전달 받은 옵션 여부 확인 및 처리
|
||||
// - 무조건 옵션으로 동작 방식을 결정하도록 한다. ( 무옵션은 오류로 처리)
|
||||
|
||||
// 인자로 전달받은 정보를 저장하기 위한 변수.
|
||||
bool bRunIntegrityCheck = false;
|
||||
bool bRunGarbageClean = false;
|
||||
bool bGarbageCleanSlow = false;
|
||||
bool bGarbageForceDelete = false;
|
||||
bool bBatchRun = false;
|
||||
|
||||
std::string strWorkPath;
|
||||
strWorkPath.clear();
|
||||
|
||||
if( argc >= 2 )
|
||||
{
|
||||
int opt;
|
||||
while( ( opt = getopt( argc, argv, "hvbficsp:" ) ) != -1 ) // 옵션 끝까지 파싱처리
|
||||
{
|
||||
switch( opt )
|
||||
{
|
||||
case 'h':
|
||||
case '?': // 정의되지 않은 문자가 나타날 경우 getopt 에 자동반환. 도움말 표시 처리.
|
||||
PrintUsage();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
case 'v':
|
||||
PrintVersion();
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
case 's':
|
||||
bGarbageCleanSlow = true;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
bBatchRun = true;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
bGarbageForceDelete = true;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
bRunIntegrityCheck = true;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
bRunGarbageClean = true;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
strWorkPath = optarg;
|
||||
break;
|
||||
|
||||
default: // 본 조건절은 getopt 특성상 동작하지 않지만...업무 Flow 이해(?)를 위해 유지한다.
|
||||
fprintf( stderr, "Unkonwn options[%c] used. program terminated.\n", opt );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 옵션값이 설정되지 않은 경우....
|
||||
fprintf( stderr, PROG_NAME ": not enough option.\n" );
|
||||
PrintUsage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 2. 프로세스 중복 실행 여부 검사
|
||||
// - 검사 대상 경로 처리 관련 복잡성 문제로 인해 중복 실행을 허용하지 않는다.
|
||||
if( IsCurrentProcessRun() == true )
|
||||
{
|
||||
fprintf( stderr, "[warning] Process [" PROG_NAME "] is already running....\n" );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 3. conf 로딩 및 검사
|
||||
// - fhs.conf 가 존재해야 하며....
|
||||
// - 전달받은 옵션에 대한 정합성 검증을 위해... conf 를 미리 로딩 처리한다.
|
||||
if( CProcessConfig::Init( PROG_NAME, strConfigFileName, szErrorMessage ) == false )
|
||||
{
|
||||
fprintf( stderr, PROG_NAME ": conf error [%s]\n\n", szErrorMessage.c_str() );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 4. 전달받은 옵션에 대한 검증 및 conf 저장 처리.
|
||||
// - 1) integrity chek, garbage clean 작업 중 최소 1개 작업 선택 필요 (아무 작업도 선택하지 않은 경우.. 오류 처리 )
|
||||
if( bRunIntegrityCheck == false && bRunGarbageClean == false )
|
||||
{
|
||||
fprintf( stderr, PROG_NAME ": not enough option. select -i or -c \n" );
|
||||
PrintUsage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// conf 객체에 저장 처리한다.
|
||||
CProcessConfig::GetInstance()->SetIntegrityCheck( bRunIntegrityCheck );
|
||||
CProcessConfig::GetInstance()->SetGarbageClean( bRunGarbageClean );
|
||||
CProcessConfig::GetInstance()->SetGarbageCleanSlow( bGarbageCleanSlow );
|
||||
CProcessConfig::GetInstance()->SetGarbageForceDelete( bGarbageForceDelete );
|
||||
}
|
||||
|
||||
// - 2) 작업 대상 경로를 전달받은 경우.. 해당 경로를 저장 처리한다.
|
||||
// 저장 처리 함수에서 유효성 검증을 수행하며.. 오류 발생시 해당 내역을 출력 처리한다.
|
||||
if( strWorkPath.empty() == false )
|
||||
{
|
||||
// conf 객체에 저장 처리한다.
|
||||
if( CProcessConfig::GetInstance()->SetJobRoot( strWorkPath, szErrorMessage ) == false )
|
||||
{
|
||||
// 유효성 검증에서 오류가 발생한 경우...
|
||||
fprintf( stderr, PROG_NAME ": %s \n", szErrorMessage.c_str());
|
||||
PrintUsage();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 장비의 hostname 정보 추출.
|
||||
char tempBuffer[256];
|
||||
if( gethostname( tempBuffer, 255 ) == 0 )
|
||||
{
|
||||
// 정보 추출 성공시
|
||||
if( CProcessConfig::GetInstance()->SetHostname( tempBuffer ) == false )
|
||||
{
|
||||
fprintf( stderr, PROG_NAME ": hostname not valid. [%s]\n", tempBuffer );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int errorNum = errno;
|
||||
fprintf( stderr, PROG_NAME ": hostname not valid. [%d][%s]\n", errorNum, strerror( errorNum ) );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 6. 업로드 방지 상태인지 확인...
|
||||
// - 업로드 방지 상태가 아닌 경우 아래와 같은 문제점 발생.
|
||||
// - 정합성 검증
|
||||
// -- 업로드 중인 파일에 대해 size mismatch 상황 발생 가능.
|
||||
// - garbage clean
|
||||
// -- meta copy 후 local meta 정보로 check 를 수행하므로..신규 업로드 파일에 대해서 copy 된 meta 에 존재하지 않아... 삭제 처리될 가능성 존재.
|
||||
// -- repliation 생성시.. 장비가 복제 완료 후 DB 에 insert 처리하므로... garbage 로 판단될 가능성 존재.
|
||||
// - 따라서 업로드 방지 상태에서 작업 수행 필요.
|
||||
if( IsUploadDisable() == false )
|
||||
{
|
||||
// 해당 함수에서 관련 내역 출력 처리 했으므로... 그냥 종료 처리만 수행.
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// 7. 최종 사용자 확인 처리.
|
||||
// - batch 작업을 위한 -b 옵션을 사용한 경우에는 사용자 확인 없이 그냥 진행 처리.
|
||||
if( bBatchRun == false )
|
||||
{
|
||||
fprintf( stderr, "\nRun with the following options? \n" );
|
||||
fprintf( stderr, " hostname : %s \n", CProcessConfig::GetInstance()->GetHostname() );
|
||||
fprintf( stderr, " content integrity check : %s \n", ( CProcessConfig::GetInstance()->IsRunIntegrityCheck() == true ? "yes" : "no" ) );
|
||||
|
||||
fprintf( stderr, " garbage content clean : %s \n", ( CProcessConfig::GetInstance()->IsRunGarbageClean() == true ? "yes" : "no" ) );
|
||||
if( CProcessConfig::GetInstance()->IsRunGarbageClean() == true )
|
||||
{
|
||||
fprintf( stderr, " - unlink speed : %s \n", ( CProcessConfig::GetInstance()->IsSlowGarbageClean() == true ? "slow" : "normal" ) );
|
||||
fprintf( stderr, " - unlink mode : %s \n", ( CProcessConfig::GetInstance()->IsForceGarbageDelete() == true ? "force" : "normal" ) );
|
||||
}
|
||||
fprintf( stderr, " target directory path : %s %s\n"
|
||||
, CProcessConfig::GetInstance()->GetJobRoot()
|
||||
, ( CProcessConfig::GetInstance()->IsUserDefineJobRoot() == true ? "(user)" : "(default)" ) );
|
||||
|
||||
// garbage clean 작업을 진행할 경우.. disk busy check 하라는 경고 문고 추가.
|
||||
if( CProcessConfig::GetInstance()->IsRunGarbageClean() == true )
|
||||
{
|
||||
fprintf( stderr, "\nWarning : garbage clean job is caused disk busy. Check disk busy before run.\n" );
|
||||
}
|
||||
|
||||
fprintf( stderr, "\nIs ok? [y/N] : " );
|
||||
|
||||
char chUserYN;
|
||||
chUserYN = getchar();
|
||||
|
||||
if( chUserYN != 'y' )
|
||||
{
|
||||
// 중지 시키는 경우.
|
||||
fprintf( stderr, "\n" PROG_NAME " exit. good bye.\n\n" );
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
// Log 객체 생성 및 초기화
|
||||
if( CLogger::Init( PROG_NAME, CProcessConfig::GetInstance()->GetLogPath(), LINF ) == false )
|
||||
{
|
||||
fprintf( stderr, "[ERR] Log module initialize failed.[%s]\n\n", CProcessConfig::GetInstance()->GetLogPath() );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf( stderr, "\n" PROG_NAME " running with daemon. use log file for processing check [%s/%s]\n\n", CProcessConfig::GetInstance()->GetLogPath(), PROG_NAME );
|
||||
}
|
||||
|
||||
// Daemonize...
|
||||
if( daemon( 1, 0 ) == -1 ) // nochdir: true(작업 디렉토리 변경 안함), noclose: false (표준 입출력, 에러를 /dev/null 로 리디렉트 처리)
|
||||
{
|
||||
fprintf( stderr, "[ERR] Process daemonize failed.[%s]\n\n", strerror( errno ) );
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// signal 처리 설정
|
||||
SetSignalMain();
|
||||
|
||||
|
||||
// Process 기동 관련 정보 기록 -> Log
|
||||
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", CProcessConfig::GetInstance()->GetLogPath(), PROG_NAME );
|
||||
_LOG( LINF, "Log level : %d", CProcessConfig::GetInstance()->GetLogLevel() );
|
||||
_LOG( LINF, "RCDB : %s %u %s %s", CProcessConfig::GetInstance()->GetRcdbIp()
|
||||
, CProcessConfig::GetInstance()->GetRcdbPort()
|
||||
, CProcessConfig::GetInstance()->GetRcdbName()
|
||||
, CProcessConfig::GetInstance()->GetRcdbAcct() );
|
||||
|
||||
_LOG( LINF, "Command option");
|
||||
_LOG( LINF, " job root path : %s %s", CProcessConfig::GetInstance()->GetJobRoot()
|
||||
, ( CProcessConfig::GetInstance()->IsUserDefineJobRoot() == true ? "(user)" : "(default)" ) );
|
||||
_LOG( LINF, " integrity check : %s", ( CProcessConfig::GetInstance()->IsRunIntegrityCheck() == true ? "yes" : "no" ) );
|
||||
_LOG( LINF, " garbage clean : %s", ( CProcessConfig::GetInstance()->IsRunGarbageClean() == true ? "yes" : "no" ) );
|
||||
if( CProcessConfig::GetInstance()->IsRunGarbageClean() == true )
|
||||
{
|
||||
_LOG( LINF, " garbage clean speed : %s", ( CProcessConfig::GetInstance()->IsSlowGarbageClean() == true ? "slow" : "normal" ) );
|
||||
_LOG( LINF, " garbage clean mode : %s", ( CProcessConfig::GetInstance()->IsForceGarbageDelete() == true ? "force" : "normal" ) );
|
||||
}
|
||||
_LOG( LINF, " last user confirm : %s", ( bBatchRun == false ? "yes" : "batch mode" ) );
|
||||
_LOG( LINF, "Local" );
|
||||
_LOG( LINF, " hostname : %s", CProcessConfig::GetInstance()->GetHostname());
|
||||
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
|
||||
// Log Level 재설정. -> conf 설정대로 변경 처리.
|
||||
#ifdef _DEBUG_
|
||||
CLogger::GetInstance()->SetLogLevel( LDBG );
|
||||
#else
|
||||
CLogger::GetInstance()->SetLogLevel( CProcessConfig::GetInstance()->GetLogLevel() );
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// 0. sqlite memory db 객체 생성.
|
||||
int nResult = sqlite3_open( ":memory:", &g_pLocalDb );
|
||||
if( nResult != SQLITE_OK )
|
||||
{
|
||||
LOG( LERR, "sqlite3 memory db open failed. [%d][%s]", nResult, sqlite3_errmsg( g_pLocalDb ) );
|
||||
|
||||
_LOG( LERR, "Process exit by sqlite local db create error. Job not completed.. " );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// sqlite memory DB 생성이 정상 수행 된 경우.
|
||||
_LOG( LINF, "sqlite memory db open success.");
|
||||
}
|
||||
|
||||
|
||||
// 1. RCDB 로 부터 Meta 정보 조회
|
||||
// - 해당 객체는 작업 완료 후.. 계속 메모리에 유지할 필요가 없으므로.. 포인터 객체로 처리
|
||||
setproctitle( "meta copy" );
|
||||
|
||||
CMetaCopy * pMeta = new CMetaCopy( g_pLocalDb );
|
||||
unsigned long long ullMetaTotalCount = 0;
|
||||
|
||||
if( pMeta == NULL )
|
||||
{
|
||||
_LOG( LERR, "Process exit by meta copy create error. Job not completed.. " );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if( pMeta->Init() == false )
|
||||
{
|
||||
// 관련 오류 발생 로깅은 해당 모듈에서 했으므로... 생략
|
||||
_LOG( LERR, "Process exit by meta copy init error. Job not completed.. ");
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
delete pMeta;
|
||||
pMeta = NULL;
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if( pMeta->Start() == false )
|
||||
{
|
||||
// 관련 오류 발생 로깅은 해당 모듈에서 했으므로... 생략
|
||||
_LOG( LERR, "Process exit by meta copy start error. Job not completed.. ");
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
delete pMeta;
|
||||
pMeta = NULL;
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
// 조회된 Meta 의 최종 count 정보를 가져와 저장 처리한다.
|
||||
ullMetaTotalCount = pMeta->GetTotalCount();
|
||||
|
||||
|
||||
// Meta copy 작업이 완료된 경우.. 해당 모듈은 더 이상 필요 없으므로.. 삭제 처리.
|
||||
delete pMeta;
|
||||
pMeta = NULL;
|
||||
|
||||
|
||||
|
||||
// 2. Content integrity check 수행
|
||||
if( CProcessConfig::GetInstance()->IsRunIntegrityCheck() == true )
|
||||
{
|
||||
setproctitle( "integrity check" );
|
||||
|
||||
// 앞의 Meta Copy 작업에서... 조회된 Content 가 없는 경우...
|
||||
// - 그냥 처리 완료된 것으로 한다.
|
||||
if( ullMetaTotalCount == 0 )
|
||||
{
|
||||
_LOG( LINF, "[%s] Available content not exist. Integrity check complete.", CProcessConfig::GetInstance()->GetJobRoot() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// 정합성 검증 처리 모듈 생성.
|
||||
CIntegrityCheck * pIntegrityCheck = new CIntegrityCheck( g_pLocalDb );
|
||||
if( pIntegrityCheck == NULL )
|
||||
{
|
||||
_LOG( LERR, "Process exit by integrity check create error. Job not completed.. " );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 정합성 검증 처리 모듈 초기화
|
||||
if( pIntegrityCheck->Init() == false )
|
||||
{
|
||||
// 관련 오류 발생 로깅은 해당 모듈에서 했으므로... 생략
|
||||
_LOG( LERR, "Process exit by integrity check init error. Job not completed.. " );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
delete pIntegrityCheck;
|
||||
pIntegrityCheck = NULL;
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 정합성 검증 처리 시작.
|
||||
if( pIntegrityCheck->Start() == false )
|
||||
{
|
||||
// 관련 오류 발생 로깅은 해당 모듈에서 했으므로... 생략
|
||||
_LOG( LERR, "Process exit by integrity check start error. Job not completed.. " );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
delete pIntegrityCheck;
|
||||
pIntegrityCheck = NULL;
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Integrity Check 작업이 완료된 경우.. 해당 모듈은 더 이상 필요 없으므로.. 삭제 처리.
|
||||
delete pIntegrityCheck;
|
||||
pIntegrityCheck = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3. Garbage Content Clean 수행
|
||||
if( CProcessConfig::GetInstance()->IsRunGarbageClean() == true )
|
||||
{
|
||||
setproctitle( "garbage clean" );
|
||||
|
||||
// 앞의 Meta Copy 작업에서... 조회된 Content 가 없는 경우...
|
||||
// - DB 작업 등으로 인해 깡통 DB 인 상태에서 조회가 수행된 경우.....(예전 파일바다 장애)
|
||||
// => FHS 에 존재하는 모든 content 가 삭제될 수 있어 위험함.
|
||||
// - 실제 RCDB 상에 content 가 없는 경우....
|
||||
// => RCDB 에 최종 확인 작업을 수행할 경우.. RCDB 부하만 높아짐.
|
||||
// => 특정 기간 이상 된 Content만 삭제 처리시.... 남아 있게 되는 content 존재.
|
||||
// 따라서 이럴 경우... 운영자가 직접 확인 후 폴더를 통째로 삭제 처리하는 것이 깔끔하다.
|
||||
|
||||
if( ullMetaTotalCount == 0 )
|
||||
{
|
||||
_LOG( LWAR, "[%s][%s] Available content not exist. Garbage clean not run. Check RCDB and Clean manual."
|
||||
, CProcessConfig::GetInstance()->GetHostname(), CProcessConfig::GetInstance()->GetJobRoot() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Garbage content clean 처리 모듈 생성.
|
||||
CGarbageClean * pGarbageClean = new CGarbageClean( g_pLocalDb );
|
||||
if( pGarbageClean == NULL )
|
||||
{
|
||||
_LOG( LERR, "Process exit by garbage clean create error. Job not completed.. " );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// garbage clean 처리 모듈 초기화
|
||||
if( pGarbageClean->Init() == false )
|
||||
{
|
||||
// 관련 오류 발생 로깅은 해당 모듈에서 했으므로... 생략
|
||||
_LOG( LERR, "Process exit by garbage clean init error. Job not completed.. " );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
delete pGarbageClean;
|
||||
pGarbageClean = NULL;
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// garbage clean 처리 시작.
|
||||
// - unlink slow 모드 인지 여부를 인자로 전달.
|
||||
// - unlink force 옵션을 인자로 전달.
|
||||
bGarbageCleanSlow = CProcessConfig::GetInstance()->IsSlowGarbageClean();
|
||||
bGarbageForceDelete = CProcessConfig::GetInstance()->IsForceGarbageDelete();
|
||||
|
||||
if( pGarbageClean->Start( bGarbageCleanSlow, bGarbageForceDelete ) == false )
|
||||
{
|
||||
// 관련 오류 발생 로깅은 해당 모듈에서 했으므로... 생략
|
||||
_LOG( LERR, "Process exit by garbage clean start error. Job not completed.. " );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
delete pGarbageClean;
|
||||
pGarbageClean = NULL;
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// garbage clean 작업이 완료된 경우.. 해당 모듈은 더 이상 필요 없으므로.. 삭제 처리.
|
||||
delete pGarbageClean;
|
||||
pGarbageClean = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 작업 완료 후 종료시.. 로깅 처리.
|
||||
_LOG( LINF, "Job completed. Good bye...");
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
|
||||
ReadyToExit();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
// 프로세스 종료 전 처리할 종료 관련 각 작업을 일괄로 처리하기 위한 함수.
|
||||
void ReadyToExit()
|
||||
{
|
||||
// sqlite memory db 를 삭제한다.
|
||||
if( g_pLocalDb != NULL )
|
||||
{
|
||||
sqlite3_close( g_pLocalDb );
|
||||
g_pLocalDb = NULL;
|
||||
}
|
||||
|
||||
// Logger 객체 종료 처리.
|
||||
CLogger::Exit();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 사용방법 표시
|
||||
void PrintUsage()
|
||||
{
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, "Usage: " PROG_NAME " [OPTION] \n" );
|
||||
fprintf( stderr, "Options: \n" );
|
||||
|
||||
fprintf( stderr, " -i Execute content integrity check \n" );
|
||||
fprintf( stderr, " -c Execute garbage content clean \n" );
|
||||
fprintf( stderr, " -p {path} Target path set for integrity check or garbage clean (ex: /stg/node0 )\n" );
|
||||
fprintf( stderr, " fhs.conf [FILE_STORAGE_ROOT] value is default.\n" );
|
||||
fprintf( stderr, " -s Very slow unlink when execute garbage clean. but need many time.\n" );
|
||||
fprintf( stderr, " -f Force garbage unlink mode. never access time check before garbage content unlink.\n");
|
||||
fprintf( stderr, " -b Batch run mode. never print confirm prompt before job execute.\n" );
|
||||
fprintf( stderr, " -h Display help information \n" );
|
||||
fprintf( stderr, " -v Display version \n" );
|
||||
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, PROG_NAME " is Solbox Cloud Storage module.\n" );
|
||||
fprintf( stderr, " - FHS saved content integrity check based RCDB.\n" );
|
||||
fprintf( stderr, " - Garbage content find & unlink for storage free capacity.\n\n" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 버전 정보 표시
|
||||
void PrintVersion()
|
||||
{
|
||||
fprintf( stderr, "\n" );
|
||||
fprintf( stderr, PROG_NAME " version: " PROG_VERSION "\n\n" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/// @brief 현재 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 )
|
||||
{
|
||||
std::cerr << "[error] Process duplication check failed.[popen error][" << strerror( errno ) << "]" << std::endl;
|
||||
|
||||
// 오류 발생시 true 반환하여 프로세스 실행 방지처리
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
while( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
|
||||
{
|
||||
std::string tempPid( tempBuffer );
|
||||
Trim( tempPid );
|
||||
|
||||
if( atoi( tempPid.c_str() ) != getpid() )
|
||||
{
|
||||
std::cout << "[info] Process duplication found. pid[" << atoi( tempPid.c_str() ) << "]" << std::endl;
|
||||
|
||||
bRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pclose( fd );
|
||||
return bRun;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 본 FHS 장비가 업로드 방지 상태인지 여부를 반환.
|
||||
// 업로드 방지 상태인 경우 true, 업로드 가능 상태인 경우.. false 를 반환한다.
|
||||
bool IsUploadDisable()
|
||||
{
|
||||
// 업로드 방지 상태 여부 확인.
|
||||
// 1. vmasd 가 동작 중인 경우... /user/service/vmasd.disable 설정되었는지 확인.
|
||||
// 2. vmasd 미동작, csagentd 가 동작 중인 경우... 아래 사항 체크.
|
||||
// - /user/service/fhs.ronly
|
||||
// - /user/service/fhs.out
|
||||
// 3. vmasd, csagentd 모두 미동작인 경우...
|
||||
// - 정상 서비스 상태가 아니므로.... 업로드 방지 상태로 처리.
|
||||
|
||||
char tempBuffer[512];
|
||||
FILE * fd = NULL;
|
||||
bool bRun = false;
|
||||
|
||||
struct stat stFileStat;
|
||||
|
||||
// vmasd 가 동작 중인지 확인.
|
||||
snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -x vmasd | sort");
|
||||
fd = popen( tempBuffer, "r" );
|
||||
if( fd == NULL )
|
||||
{
|
||||
int errorNum = errno;
|
||||
fprintf( stderr, PROG_NAME ": vmasd upload disable check error. popen error. [%d][%s]\n", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
while( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
|
||||
{
|
||||
std::string tempPid( tempBuffer );
|
||||
Trim( tempPid );
|
||||
|
||||
if( tempPid.empty() == false )
|
||||
{
|
||||
bRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pclose( fd );
|
||||
}
|
||||
|
||||
// vmasd 가 동작 중인 경우...
|
||||
if( bRun == true )
|
||||
{
|
||||
// /user/service/vmasd.disable 설정 체크.
|
||||
memset( &stFileStat, 0x00, sizeof( struct stat ) );
|
||||
if( lstat( FILE_VMASD_DISABLE, &stFileStat ) == 0 )
|
||||
{
|
||||
// 해당 파일이 정규 파일인 경우.. ( 폴더, 링크 등은 제외 처리 )
|
||||
if( S_ISREG( stFileStat.st_mode ) != 0 )
|
||||
{
|
||||
// 해당 파일이 존재하는 경우...
|
||||
fprintf( stderr, PROG_NAME ": upload disable check OK. [%s]\n", FILE_VMASD_DISABLE );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// vmasd 가 동작 중인데... 위의 return 경우 외에는 전부 오류로 처리.
|
||||
fprintf( stderr, PROG_NAME ": vmasd run. but upload disable[%s] not set. check and retry. \n", FILE_VMASD_DISABLE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// csagentd 가 동작 중인지 확인.
|
||||
snprintf( tempBuffer, sizeof( tempBuffer ), "pgrep -x csagentd | sort" );
|
||||
fd = popen( tempBuffer, "r" );
|
||||
if( fd == NULL )
|
||||
{
|
||||
int errorNum = errno;
|
||||
fprintf( stderr, PROG_NAME ": csagentd upload disable check error. popen error. [%d][%s]\n", errorNum, strerror( errorNum ) );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset( tempBuffer, 0x00, sizeof( tempBuffer ) );
|
||||
while( fgets( tempBuffer, sizeof( tempBuffer ) - 1, fd ) != NULL )
|
||||
{
|
||||
std::string tempPid( tempBuffer );
|
||||
Trim( tempPid );
|
||||
|
||||
if( tempPid.empty() == false )
|
||||
{
|
||||
bRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pclose( fd );
|
||||
}
|
||||
|
||||
// csagentd 가 동작 중인 경우...
|
||||
if( bRun == true )
|
||||
{
|
||||
// /user/service/fhs.ronly 설정 체크.
|
||||
memset( &stFileStat, 0x00, sizeof( struct stat ) );
|
||||
if( lstat( FILE_SERVICE_RONLY, &stFileStat ) == 0 )
|
||||
{
|
||||
// 해당 파일이 정규 파일인 경우.. ( 폴더, 링크 등은 제외 처리 )
|
||||
if( S_ISREG( stFileStat.st_mode ) != 0 )
|
||||
{
|
||||
// 해당 파일이 존재하는 경우...
|
||||
fprintf( stderr, PROG_NAME ": upload disable check OK. [%s]\n", FILE_SERVICE_RONLY );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// /user/service/fhs.out 설정 체크.
|
||||
memset( &stFileStat, 0x00, sizeof( struct stat ) );
|
||||
if( lstat( FILE_SERVICE_OUT, &stFileStat ) == 0 )
|
||||
{
|
||||
// 해당 파일이 정규 파일인 경우.. ( 폴더, 링크 등은 제외 처리 )
|
||||
if( S_ISREG( stFileStat.st_mode ) != 0 )
|
||||
{
|
||||
// 해당 파일이 존재하는 경우...
|
||||
fprintf( stderr, PROG_NAME ": upload disable check OK. [%s]\n", FILE_SERVICE_OUT );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// csagentd 가 동작 중인데... 위의 return 경우 외에는 전부 오류로 처리.
|
||||
fprintf( stderr, PROG_NAME ": csagentd run. but upload disable[%s] not set. check and retry. \n", FILE_SERVICE_RONLY );
|
||||
return false;
|
||||
}
|
||||
|
||||
// vmasd, csagentd 모두 미기동 상태인 경우....
|
||||
// - 서비스 상태가 아니므로.. 정상 처리.
|
||||
fprintf( stderr, PROG_NAME ": FHS is not service status. upload disable check OK.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// @brief 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 );
|
||||
}
|
||||
|
||||
|
||||
// Main 프로세스에 대한 종료 처리 수신시
|
||||
// - 요청한 모든 작업이 완료된 후 종료 되어야 정상 종료이며...
|
||||
// - 만약 작업 도중 signal 에 의한 종료 요청이 발생하는 경우.. 비정상 종료로 처리해야 한다.
|
||||
static void SignalMainTerminate( int nSignalNumber )
|
||||
{
|
||||
// Signal Number 에 따른 로깅처리.
|
||||
if( nSignalNumber == SIGTERM )
|
||||
{
|
||||
_LOG( LERR, "[Main:%d] Process exit by SIGTERM signal. Job not completed..", getpid() );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LERR, "[Main:%d] Process exit by abnormal signal[%d]. Job not completed..", getpid(), nSignalNumber );
|
||||
_LOG( LINF, "***********************************************************" );
|
||||
}
|
||||
|
||||
// 기타 모듈들의 종료 처리 수행
|
||||
ReadyToExit();
|
||||
|
||||
// 종료전 잠시 대기
|
||||
struct timespec sleep;
|
||||
sleep.tv_sec = 0;
|
||||
sleep.tv_nsec = 500000000; // 0.5 sec
|
||||
nanosleep( &sleep, NULL );
|
||||
|
||||
exit( EXIT_SUCCESS );
|
||||
}
|
||||
|
||||
|
||||
// Main Process 의 signal 처리기...
|
||||
// fork 된 worker 프로세스 역시 기본적으로 본 signal 처리 action 을 상속받는다.
|
||||
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 신호 처리 => ? */
|
||||
|
||||
// Worker child process 종료에 대한 처리기 설정.
|
||||
// - 자식 프로세스가 존재하지 않으므로.. 그냥 무시 처리.
|
||||
// - 만약 system() 함수 등을 이용하여 외부 모듈 수행시.. system() 함수의 정상적 반환값 처리를 위해서는 SIG_DFL 로 설정해야 한다.
|
||||
act.sa_handler = SIG_DFL;
|
||||
sigaction( SIGCHLD, &act, NULL );
|
||||
|
||||
// 사용자의 종료 또는 에러 관련 신호 처리.
|
||||
act.sa_handler = SignalMainTerminate;
|
||||
sigaction( SIGTERM, &act, NULL ); /* kill -TERM 에 의한 프로세스 종료시 */
|
||||
|
||||
// 나머지 신호는 default 처리.
|
||||
|
||||
sigemptyset( &set ); /* 신호 처리기 처리 설정 위한 블록 해제 */
|
||||
sigprocmask( SIG_SETMASK, &set, NULL );
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/****************************************************************************
|
||||
Main.h for cls
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2015/10/06
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage Team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
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 __CLS_MAIN_H__
|
||||
#define __CLS_MAIN_H__
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/// @brief 사용 방법을 화면에 표시하기 위한 함수.
|
||||
void PrintUsage( void );
|
||||
|
||||
/// @brief Version 정보를 화면에 표시하기 위한 함수
|
||||
void PrintVersion( void );
|
||||
|
||||
/// @brief 현재 Process가 기동중인지 여부를 판단하기 위한 함수( 프로세스 중복 실행 체크)
|
||||
/// @return 이미 해당 프로세스가 기동 중인 경우 true 반환, 그렇지 않으면 false 반환.
|
||||
bool IsCurrentProcessRun( void );
|
||||
|
||||
// 본 FHS 장비가 업로드 방지 상태인지 여부를 반환.
|
||||
// 업로드 방지 상태인 경우 true, 업로드 가능 상태인 경우.. false 를 반환한다.
|
||||
bool IsUploadDisable( void );
|
||||
|
||||
/// @brief std 상에 trim 함수가 없어서 직접 구현 아니면 boost/algorithm/string.hpp 상의 boost::trim 함수 사용
|
||||
/// @param str [in/out] Trim 할 문자열을 저장한 String 참조변수.
|
||||
/// @return void
|
||||
void Trim( std::string & str );
|
||||
|
||||
/// @brief 프로세스 종료에 따른 반복적인 종료 관련 작업을 수행하기 위한 함수.
|
||||
void ReadyToExit( void );
|
||||
|
||||
/// @brief Main 프로세스 signal 처리 설정을 위한 함수
|
||||
/// @return void
|
||||
void SetSignalMain( void );
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __CLS_MAIN_H__ */
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#****************************************************************************
|
||||
# Makefile for cls
|
||||
# -----------------------------------------
|
||||
#
|
||||
# begin : 2015/10/06
|
||||
# copyright : (C) 2005 Solbox Inc.
|
||||
# author : Dev Storage Team
|
||||
# email : huibong@solbox.com
|
||||
# version : 3.5
|
||||
#
|
||||
# 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 = cls
|
||||
REVISION = 1463
|
||||
PROG_VERSION = 3.5.0.$(REVISION)-`date +%Y%m%d%H%M%S`
|
||||
|
||||
|
||||
DEFAULT_CONFIG_FILE = /user/service/etc/fhs.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 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
|
||||
LFLAGS =
|
||||
|
||||
|
||||
DFLAGS = -DPROG_NAME=\"$(PROG_NAME)\" -DPROG_VERSION=\"$(PROG_VERSION)\" -DDEFAULT_CONFIG_FILE=\"$(DEFAULT_CONFIG_FILE)\"
|
||||
|
||||
# Application Enviroment
|
||||
APP = $(PROG_NAME)
|
||||
|
||||
DIR_INCLUDE = -I../lib -I/user/db/pgsql/include -I/usr/local/include
|
||||
DIR_LIB = -L../lib -L/usr/local/lib
|
||||
|
||||
|
||||
OBJ = ProcessConfig.o \
|
||||
Database.o RcdbInfo.o MetaCopy.o \
|
||||
IntegrityCheck.o GarbageClean.o \
|
||||
Main.o
|
||||
|
||||
LIBS = ../lib/libInterCommon.a -lc /user/db/pgsql/lib/libpq.a -lsqlite3 -lpthread
|
||||
|
||||
|
||||
#---------------------------------------------------------------------#
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,552 @@
|
||||
#include "MetaCopy.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Logger.h"
|
||||
#include "RcdbInfo.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
|
||||
#define SQLITE_DB_LOCK_TIMEOUT 1000 // SQLITE_BUSY 상황 발생 문제 해결을 위해 DB Lock 관련 timeout 설정값. (1 sec)
|
||||
#define SQLITE_DB_LOCK_MAX_RETRY 60 // SQLITE_BUSY 발생시 재시도 최대 횟수
|
||||
|
||||
|
||||
// Query 문장 저장을 위한 Buffer 크기
|
||||
#define DEFAULT_QUERY_BUFFER_SIZE 2048
|
||||
|
||||
|
||||
// 생성자
|
||||
// - pLocalDb : sqlite memory db 객체에 대한 포인트
|
||||
CMetaCopy::CMetaCopy( sqlite3 * pLocalDb )
|
||||
: m_pLocalStmt(NULL)
|
||||
{
|
||||
m_pLocalDb = pLocalDb;
|
||||
|
||||
m_ullTotalSelectCount = 0;
|
||||
}
|
||||
|
||||
|
||||
// 소멸자
|
||||
CMetaCopy::~CMetaCopy()
|
||||
{
|
||||
// RCDB 와 연결 해제 처리.
|
||||
m_rcdb.PgClear();
|
||||
m_rcdb.PgCloseDB();
|
||||
|
||||
// Local DB stmt clear 처리.
|
||||
// - local db 객체는 memory db 로서 전역 객체이므로.. close 하지 않는다.
|
||||
if( m_pLocalStmt != NULL )
|
||||
{
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 객체 초기화 함수
|
||||
// - RCDB 와 연결 생성
|
||||
// - sqlite local memory db 관련 table, index 생성 및 insert stmt 생성 처리.
|
||||
bool CMetaCopy::Init()
|
||||
{
|
||||
// RCDB 접속 정보 저장 관련 객체 생성 및 초기화...
|
||||
CRcdbInfo rcdbInfo;
|
||||
rcdbInfo.Load();
|
||||
|
||||
// sqlite 를 이용하여 local 장비에 저장 목적 file DB 생성 처리 및 세션 연결.
|
||||
if( CreateLocalDb() == false )
|
||||
return false;
|
||||
|
||||
// RCDB 와 연결 생성.
|
||||
if( m_rcdb.PgOpenDB( rcdbInfo.m_strRcdbIp, rcdbInfo.m_nRcdbPort, rcdbInfo.m_strRcdbName, rcdbInfo.m_strRcdbAcct, rcdbInfo.m_strRcdbAcctPw ) == NULL )
|
||||
{
|
||||
// 연결 실패시...
|
||||
LOG( LERR, "RCDB connection failed." );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LINF, "RCDB connection success." );
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// sqlite 를 이용하여 local db 생성 작업을 수행.
|
||||
bool CMetaCopy::CreateLocalDb()
|
||||
{
|
||||
|
||||
// sqlite local memory db 객체는 Main 모듈에서 생성 했으므로...
|
||||
// 그 외 작업을 수행한다.
|
||||
|
||||
// 1. local db 상에 관련 table 생성 작업 수행 처리.
|
||||
int nResult = 0;
|
||||
std::string strQuery;
|
||||
strQuery = "CREATE TABLE meta( resource_id integer, filename_hash text, get_content_length integer, deleted_yn text )";
|
||||
|
||||
char * pStrDbError;
|
||||
int nRetryCount = 0;
|
||||
|
||||
nResult = sqlite3_exec( m_pLocalDb, strQuery.c_str(), NULL, NULL, &pStrDbError );
|
||||
|
||||
while( nResult == SQLITE_BUSY && nRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_free( pStrDbError );
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nRetryCount;
|
||||
|
||||
_LOG( LWAR, "Local db table create SQLITE_BUSY. Retry[%d]", nRetryCount );
|
||||
|
||||
nResult = sqlite3_exec( m_pLocalDb, strQuery.c_str(), NULL, NULL, &pStrDbError );
|
||||
}
|
||||
|
||||
// Table Create Query 가 최종 실패한 경우....
|
||||
if( nResult != SQLITE_OK )
|
||||
{
|
||||
LOG( LERR, "sqlite3 table create failed. [%d][%s]", nResult, pStrDbError );
|
||||
|
||||
sqlite3_free( pStrDbError );
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LINF, "Local db table create success.");
|
||||
}
|
||||
|
||||
// 2. garbage clean 시 select 속도 향상을 위한 Index 생성
|
||||
if( CProcessConfig::GetInstance()->IsRunGarbageClean() == true )
|
||||
{
|
||||
strQuery = "CREATE INDEX index_filename_hash ON meta( filename_hash ) ;";
|
||||
nResult = sqlite3_exec( m_pLocalDb, strQuery.c_str(), NULL, NULL, &pStrDbError );
|
||||
|
||||
while( nResult == SQLITE_BUSY && nRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_free( pStrDbError );
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nRetryCount;
|
||||
|
||||
_LOG( LWAR, "Local db index create SQLITE_BUSY. Retry[%d]", nRetryCount );
|
||||
|
||||
nResult = sqlite3_exec( m_pLocalDb, strQuery.c_str(), NULL, NULL, &pStrDbError );
|
||||
}
|
||||
|
||||
// Index Create Query 가 최종 실패한 경우....
|
||||
if( nResult != SQLITE_OK )
|
||||
{
|
||||
LOG( LERR, "sqlite3 index create failed. [%d][%s]", nResult, pStrDbError );
|
||||
|
||||
sqlite3_free( pStrDbError );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_LOG( LINF, "Local db index create success." );
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Meta 정보 조회 결과를 local db 에 저장 처리시 사용할 prepared 객체에 대한 초기화 수행.
|
||||
// - prepared sql 을 사용해야... insert 속도가 향상됨.
|
||||
strQuery = "INSERT INTO meta( resource_id, filename_hash, get_content_length, deleted_yn ) VALUES ( ?, ?, ?, ? );";
|
||||
|
||||
nResult = sqlite3_prepare( m_pLocalDb, strQuery.c_str(), strQuery.size(), &m_pLocalStmt, NULL );
|
||||
|
||||
// sqlite3_prepare() 함수 실행시 SQLITE_BUSY 오류가 발생할 수 있으므로.. 재시도 로직을 추가한다.
|
||||
while( nResult == SQLITE_BUSY && nRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nRetryCount;
|
||||
|
||||
_LOG( LWAR, "Local db prepare sql SQLITE_BUSY. Retry[%d]", nRetryCount );
|
||||
|
||||
nResult = sqlite3_prepare( m_pLocalDb, strQuery.c_str(), strQuery.size(), &m_pLocalStmt, NULL );
|
||||
}
|
||||
|
||||
// 최종 실패시...
|
||||
if( nResult != SQLITE_OK )
|
||||
{
|
||||
LOG( LERR, "sqlite3 prepare sql stmt create failed. [%d][%s]", nResult, sqlite3_errmsg(m_pLocalDb) );
|
||||
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CMetaCopy::Start()
|
||||
{
|
||||
// 작업 소요 시간 정보 계산을 위한 변수.
|
||||
// - 시간 동기화로 인한 문제를 해결하기 위해 clock_gettime() 함수 사용.
|
||||
struct timespec timeStart;
|
||||
struct timespec timeEnd;
|
||||
double dTimeInterval = 0.0;
|
||||
|
||||
memset( &timeStart, 0x00, sizeof( struct timespec ) );
|
||||
memset( &timeEnd, 0x00, sizeof( struct timespec ) );
|
||||
|
||||
|
||||
// 작업 시작 로깅 및 시작 시간 정보 추출.
|
||||
_LOG( LINF, "Meta copy start.");
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeStart );
|
||||
|
||||
|
||||
// Query 문장 저장을 위한 버퍼 생성
|
||||
char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
|
||||
|
||||
// RCDB Query 수행 결과값 저장을 위한 변수..
|
||||
int nResult = 0;
|
||||
|
||||
|
||||
// 1. t_sms_sp_svc_product 에서 각 고객사 정보를 가져온다.
|
||||
snprintf( szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
|
||||
"SELECT sp_svc_tran_id FROM t_sms_sp_svc_product ORDER BY sp_svc_tran_id ASC;");
|
||||
|
||||
// Query 실행
|
||||
m_rcdb.PgDoExec( szQuery );
|
||||
nResult = m_rcdb.PgResult( DataBase::NOT_CLEAR );
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// t_sms_sp_svc_product 테이블 조회시 오류가 발생한 경우...
|
||||
// - 해당 table 또는 sp_svc_tran_id 컬럼 존재 안할 경우..
|
||||
LOG( LERR, "RCDB t_sms_sp_svc_product table select failed. [%s][%s]", m_rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
m_rcdb.PgClear();
|
||||
m_rcdb.PgCloseDB();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 조회된 각 고객사 정보 확인
|
||||
nResult = m_rcdb.GetNoTuples();
|
||||
if( nResult <= 0 )
|
||||
{
|
||||
// 작업을 수행할 고객사 정보가 RCDB 에 존재하지 않는 경우...
|
||||
// - 고객사가 존재하지 않는 빈깡통 RCDB 인 경우.. 오류일 가능성이 많으므로.. 오류 처리.
|
||||
LOG( LERR, "RCDB t_sms_sp_svc_product table empty. Not found service. Check." );
|
||||
|
||||
m_rcdb.PgClear();
|
||||
m_rcdb.PgCloseDB();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 루프를 돌면서.. 고객사 정보를 저장 처리.
|
||||
std::vector<std::string> vecService;
|
||||
std::string strTemp;
|
||||
for( int i = 0; i < nResult; i++ )
|
||||
{
|
||||
strTemp = m_rcdb.GetValue( i, 0 );
|
||||
if( strTemp.size() > 0 )
|
||||
vecService.push_back( strTemp );
|
||||
}
|
||||
|
||||
// Query Resultset Clear.
|
||||
m_rcdb.PgClear();
|
||||
|
||||
// 변수에 저장된 서비스 정보의 수를 최종 확인
|
||||
if( vecService.empty() == true )
|
||||
{
|
||||
LOG( LERR, "RCDB t_sms_sp_svc_product table not empty. but valid service not found. Check." );
|
||||
|
||||
m_rcdb.PgCloseDB();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 사용자가 작업대상 경로를 지정한 경우...
|
||||
// /stg/node0
|
||||
// - t_sms_sp_svc_product 테이블을 조회하여 서비스 목록 추출해야 함.
|
||||
// /stg/node0/346
|
||||
// - 서비스 폴더를 지정했으므로.... 다른 서비스 table 까지 불필요하게 조회할 필요 없음.
|
||||
// - 따라서.. t_sms_sp_svc_product 조회 결과에서
|
||||
// - 사용자가 지정한 서비스 seq 가 존재하는 경우.... 해당 table 만 조회하도록 처리.
|
||||
// - 사용자가 지정한 서비스 seq 가 존재하지 않는 경우... 서비스 폴더가 아닐 수 있으므로.. 전체 조회 처리.
|
||||
|
||||
|
||||
std::vector< std::string >::const_iterator it;
|
||||
|
||||
// 사용자가 작업 대상 경로를 지정한 경우...
|
||||
if( CProcessConfig::GetInstance()->IsUserDefineJobRoot() == true )
|
||||
{
|
||||
strTemp = CProcessConfig::GetInstance()->GetJobRoot();
|
||||
|
||||
// 맨 끝의 폴더명만을 추출.
|
||||
string::size_type pos = strTemp.rfind( '/' );
|
||||
if( pos != string::npos )
|
||||
{
|
||||
// 경로의 마지막 문자열 정보 추출.
|
||||
std::string strSvcSeq = strTemp.substr( pos + 1 );
|
||||
|
||||
// vecService 목록에 추출된 정보가 존재하는지 확인.
|
||||
bool bFound = false;
|
||||
for( it = vecService.begin(); it != vecService.end(); ++it )
|
||||
{
|
||||
if( strcmp( it->c_str(), strSvcSeq.c_str() ) == 0 )
|
||||
{
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( bFound == true )
|
||||
{
|
||||
// 기존 RCDB 에서 추출된 정보 삭제
|
||||
vecService.clear();
|
||||
|
||||
// 사용자가 지정한 서비스 seq 만 조회되도록 처리.
|
||||
vecService.push_back( strSvcSeq );
|
||||
|
||||
_LOG( LINF, "meta copy : user path set [%s]. meta select only service [%s]", CProcessConfig::GetInstance()->GetJobRoot(), strSvcSeq.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 2. 사용자 지정 옵션에 따른 조회 구문을 설정 처리한다.
|
||||
char szWhere[DEFAULT_QUERY_BUFFER_SIZE];
|
||||
memset( szWhere, 0x00, sizeof( szWhere ) );
|
||||
|
||||
// directory 는 빼고 파일만 조회.
|
||||
strcat( szWhere, "AND resource_type = 0 " );
|
||||
|
||||
// integrity check 만 수행할 경우...
|
||||
// - 삭제된 content 는 조회하지 않는다.
|
||||
if( CProcessConfig::GetInstance()->IsRunIntegrityCheck() == true && CProcessConfig::GetInstance()->IsRunGarbageClean() == false )
|
||||
{
|
||||
strcat( szWhere, "AND deleted_yn = 'N' " );
|
||||
}
|
||||
|
||||
// 사용자가 작업 대상 경로를 지정한 경우...
|
||||
if( CProcessConfig::GetInstance()->IsUserDefineJobRoot() == true )
|
||||
{
|
||||
char tempBuffer[DEFAULT_QUERY_BUFFER_SIZE];
|
||||
|
||||
snprintf( tempBuffer, DEFAULT_QUERY_BUFFER_SIZE - 1,
|
||||
"AND filename_hash LIKE '%s/%%' "
|
||||
, CProcessConfig::GetInstance()->GetJobRoot() );
|
||||
|
||||
strcat( szWhere, tempBuffer );
|
||||
}
|
||||
|
||||
strcat( szWhere, " ;" );
|
||||
|
||||
// Local sqlite db 관련 변수
|
||||
int nLocalDbResult = 0;
|
||||
int nLocalRetryCount = 0;
|
||||
|
||||
|
||||
// 3. 루프를 돌면서
|
||||
// 각 서비스별 t_meta_xx 테이블에서 content 정보를 조회한다.
|
||||
//std::vector< std::string >::const_iterator it;
|
||||
for( it = vecService.begin(); it != vecService.end(); ++it )
|
||||
{
|
||||
_LOG( LINF, "Service [%s] meta copy start.", it->c_str() );
|
||||
|
||||
snprintf( szQuery, DEFAULT_QUERY_BUFFER_SIZE - 1,
|
||||
"SELECT resource_id, filename_hash, get_content_length, deleted_yn FROM t_meta_%s "
|
||||
"WHERE host_name = '%s' %s"
|
||||
, it->c_str()
|
||||
, CProcessConfig::GetInstance()->GetHostname()
|
||||
, szWhere );
|
||||
|
||||
|
||||
// Query 실행
|
||||
m_rcdb.PgDoExec( szQuery );
|
||||
nResult = m_rcdb.PgResult( DataBase::NOT_CLEAR );
|
||||
if( nResult < 0 )
|
||||
{
|
||||
// t_meta_xxx 테이블 조회시 오류가 발생한 경우...
|
||||
LOG( LERR, "RCDB t_meta_%s table select failed. [%s][%s]", it->c_str(), m_rcdb.GetErrorMessage().c_str(), szQuery );
|
||||
|
||||
// local db stmt clear 처리.
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
// RCDB 연결 해제 처리.
|
||||
m_rcdb.PgClear();
|
||||
m_rcdb.PgCloseDB();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 조회된 row 정보 확인
|
||||
nResult = m_rcdb.GetNoTuples();
|
||||
if( nResult <= 0 )
|
||||
{
|
||||
// 작업 대상 content 가 존재하지 않는 경우....
|
||||
_LOG( LINF, "Service [%s] meta content not exist.", it->c_str() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// sqlite 의 insert 속도 향상을 위해.. begin - commit 구문 사용....
|
||||
// 본 구문을 사용하지 않을 경우.. 초당 180 건 정도 insert 처리됨.
|
||||
nLocalDbResult= sqlite3_exec( m_pLocalDb, "BEGIN;", NULL, NULL, NULL );
|
||||
nLocalRetryCount = 0;
|
||||
|
||||
while( nLocalDbResult == SQLITE_BUSY && nLocalRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nLocalRetryCount;
|
||||
|
||||
_LOG( LWAR, "Local db BEGIN query SQLITE_BUSY. Retry[%d]", nLocalRetryCount );
|
||||
|
||||
nLocalDbResult = sqlite3_exec( m_pLocalDb, "BEGIN;", NULL, NULL, NULL );
|
||||
}
|
||||
|
||||
// 최종 실패시....
|
||||
if( nLocalDbResult != SQLITE_OK )
|
||||
{
|
||||
LOG( LERR, "sqlite3 BEGIN query failed. [%d][%s]", nLocalDbResult, sqlite3_errmsg( m_pLocalDb ) );
|
||||
|
||||
// local db stmt clear 처리.
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
// RCDB 연결 해제 처리.
|
||||
m_rcdb.PgClear();
|
||||
m_rcdb.PgCloseDB();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 루프를 돌면서 조회된 content 를 local db 에 저장 처리.
|
||||
for( int i = 0; i < nResult; i++ )
|
||||
{
|
||||
// local table 생성 Query
|
||||
// strQuery = "CREATE TABLE meta( resource_id integer, filename_hash text, get_content_length integer, deleted_yn text )";
|
||||
|
||||
// 조회 Query
|
||||
// "SELECT resource_id, filename_hash, get_content_length, deleted_yn FROM t_meta_%s "
|
||||
|
||||
// insert query 에 변수 bind 처리. ( bind 값을 1부터 시작 )
|
||||
// resource_id
|
||||
sqlite3_bind_text( m_pLocalStmt, 1, m_rcdb.GetValue( i, 0 ), strlen( m_rcdb.GetValue( i, 0 ) ), SQLITE_STATIC );
|
||||
|
||||
// filename_hash
|
||||
sqlite3_bind_text( m_pLocalStmt, 2, m_rcdb.GetValue( i, 1 ), strlen( m_rcdb.GetValue( i, 1 ) ), SQLITE_STATIC );
|
||||
|
||||
// get_content_length
|
||||
sqlite3_bind_text( m_pLocalStmt, 3, m_rcdb.GetValue( i, 2 ), strlen( m_rcdb.GetValue( i, 2 ) ), SQLITE_STATIC );
|
||||
|
||||
// deleted_yn
|
||||
sqlite3_bind_text( m_pLocalStmt, 4, m_rcdb.GetValue( i, 3 ), strlen( m_rcdb.GetValue( i, 3 ) ), SQLITE_STATIC );
|
||||
|
||||
// insert query 수행.
|
||||
nLocalDbResult = sqlite3_step( m_pLocalStmt );
|
||||
nLocalRetryCount = 0;
|
||||
|
||||
|
||||
// 만약 BUSY 상태로 인해 오류가 발생하면..지정된 횟수 만큼 재시도
|
||||
while( nLocalDbResult == SQLITE_BUSY && nLocalRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nLocalRetryCount;
|
||||
|
||||
_LOG( LWAR, "Meta insert to local db. SQLITE_BUSY. Retry[%d]", nLocalRetryCount );
|
||||
|
||||
// query 재시도
|
||||
nLocalDbResult = sqlite3_step( m_pLocalStmt );
|
||||
}
|
||||
|
||||
|
||||
if( nLocalDbResult != SQLITE_DONE )
|
||||
{
|
||||
// INSERT 실패 발생시...
|
||||
LOG( LERR, "sqlite3 insert step error. [%d][%s]", nLocalDbResult, sqlite3_errmsg( m_pLocalDb ) );
|
||||
|
||||
// local db stmt clear 처리.
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
// RCDB 와의 연결까지 clear 처리한다.
|
||||
m_rcdb.PgClear();
|
||||
m_rcdb.PgCloseDB();
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Query 수행된 경우.. prepared 문을 재사용하기 위해 reset 처리한다.
|
||||
sqlite3_reset( m_pLocalStmt );
|
||||
}
|
||||
|
||||
} // for
|
||||
|
||||
// sqlite 의 insert 속도 향상을 위해.. begin - commit 구문 사용....
|
||||
// 본 구문을 사용하지 않을 경우.. 초당 180 건 정도 insert 처리됨.
|
||||
nLocalDbResult = sqlite3_exec( m_pLocalDb, "COMMIT;", NULL, NULL, NULL );
|
||||
nLocalRetryCount = 0;
|
||||
|
||||
while( nLocalDbResult == SQLITE_BUSY && nLocalRetryCount < SQLITE_DB_LOCK_MAX_RETRY )
|
||||
{
|
||||
sqlite3_busy_timeout( m_pLocalDb, SQLITE_DB_LOCK_TIMEOUT );
|
||||
++nLocalRetryCount;
|
||||
|
||||
_LOG( LWAR, "Local db COMMIT query SQLITE_BUSY. Retry[%d]", nLocalRetryCount );
|
||||
|
||||
nLocalDbResult = sqlite3_exec( m_pLocalDb, "COMMIT;", NULL, NULL, NULL );
|
||||
}
|
||||
|
||||
// 최종 실패시....
|
||||
if( nLocalDbResult != SQLITE_OK )
|
||||
{
|
||||
LOG( LERR, "sqlite3 COMMIT query failed. [%d][%s]", nLocalDbResult, sqlite3_errmsg( m_pLocalDb ) );
|
||||
|
||||
// local db stmt clear 처리.
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
// RCDB 연결 해제 처리.
|
||||
m_rcdb.PgClear();
|
||||
m_rcdb.PgCloseDB();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Query Resultset Clear.
|
||||
m_rcdb.PgClear();
|
||||
|
||||
|
||||
// 최종 통계 처리를 위해 누적 처리.
|
||||
m_ullTotalSelectCount += nResult;
|
||||
|
||||
_LOG( LINF, "Service [%s] meta count [%d] copy end.", it->c_str(), nResult );
|
||||
|
||||
} // for
|
||||
|
||||
|
||||
// 작업 완료 후... RCDB 와의 연결을 종료 처리한다.
|
||||
m_rcdb.PgClear();
|
||||
m_rcdb.PgCloseDB();
|
||||
|
||||
// 작업 완료 후 Local DB stmt 객체 clear 처리한다.
|
||||
sqlite3_finalize( m_pLocalStmt );
|
||||
m_pLocalStmt = NULL;
|
||||
|
||||
// 작업 완료시 시간 정보 추출 후 소요시간 계산.
|
||||
clock_gettime( CLOCK_MONOTONIC, &timeEnd );
|
||||
|
||||
dTimeInterval = timeEnd.tv_sec - timeStart.tv_sec;
|
||||
dTimeInterval += ( timeEnd.tv_nsec - timeStart.tv_nsec ) * 1e-9;
|
||||
|
||||
// memory db 사용으로 인해
|
||||
// sqlite3_memory_used() 함수를 사용하여 memory 사용량을 로깅하려 했으나..
|
||||
// FreeBSD 6.X 에 설치되는 sqlite 3.4.1 버전에서 sqlite3_memory_used, sqlite3_status 등의 함수를 지원 안함.
|
||||
// 이로 인해 적용 불가함.
|
||||
|
||||
_LOG( LINF, "Meta copy complete. path[%s] total count[%llu], elapsed time: %.1lf sec."
|
||||
, CProcessConfig::GetInstance()->GetJobRoot(), m_ullTotalSelectCount, dTimeInterval );
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/****************************************************************************
|
||||
RCDB meta info copy to local sqlite db
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2015/10/15
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage Team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
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 __META_COPY_H__
|
||||
#define __META_COPY_H__
|
||||
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Database.h"
|
||||
|
||||
|
||||
// 본 클래스는
|
||||
// 정합성 검증 또는 Garbage Clean 처리 목적으로 이에 해당하는 content 정보를
|
||||
// RCDB 에서 조회하여... 그 결과를 sqlite local DB 에 저장하는 역활을 담당한다.
|
||||
// - 정합성 검증, garbage clean 의 사용 목적에 따라... 조회 Query 가 달라짐. ( deleted_yn 포함 여부 )
|
||||
class CMetaCopy
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
// 생성자
|
||||
// - pLocalDb : sqlite memory db 객체에 대한 포인트
|
||||
CMetaCopy( sqlite3 * pLocalDb );
|
||||
|
||||
// 소멸자
|
||||
~CMetaCopy();
|
||||
|
||||
|
||||
// 객체 초기화 함수
|
||||
// - RCDB 와 연결 생성
|
||||
// - sqlite 를 이용하여 local 장비에 저장 목적 file DB 생성 처리
|
||||
bool Init();
|
||||
|
||||
// RCDB 상의 meta 정보를 조회하여.. local DB 에 저장 처리 수행 함수
|
||||
bool Start();
|
||||
|
||||
// RCDB 에서 조회된 최종 합계 정보를 반환한다.
|
||||
unsigned long long GetTotalCount() { return m_ullTotalSelectCount; }
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// RCDB 객체
|
||||
DataBase m_rcdb;
|
||||
|
||||
// sqlite3 local db 객체.
|
||||
sqlite3 * m_pLocalDb;
|
||||
sqlite3_stmt * m_pLocalStmt;
|
||||
|
||||
|
||||
// 최종 조회된 total 건수를 저장하기 위한 변수.
|
||||
unsigned long long m_ullTotalSelectCount;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
// sqlite 를 이용하여 local db table, index 등의 생성 작업을 수행.
|
||||
bool CreateLocalDb();
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __META_COPY_H__ */
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
|
||||
// static 변수 초기화
|
||||
CProcessConfig * CProcessConfig::m_pInstance = NULL;
|
||||
|
||||
|
||||
// 생성자
|
||||
CProcessConfig::CProcessConfig()
|
||||
{
|
||||
// default 값 설정.
|
||||
m_bRunIntegrityCheck = false;
|
||||
m_bRunGarbageClean = false;
|
||||
m_bGarbageCleanSlow = false;
|
||||
m_bGarbageForceDelete = false;
|
||||
m_bUserDefineJobRoot = false;
|
||||
|
||||
m_strJobRoot.clear();
|
||||
m_strHostname.clear();
|
||||
}
|
||||
|
||||
// 소멸자
|
||||
CProcessConfig::~CProcessConfig()
|
||||
{
|
||||
// 객체 소멸시...
|
||||
// 만약 static GetInstance() 함수가 가르키는 객체가 자기 자신이라면...
|
||||
// 소멸 처리에 의해 문제가 발생할 수 있으므로... 이에 대한 처리를 해 준다.
|
||||
if( CProcessConfig::GetInstance() == this )
|
||||
{
|
||||
CProcessConfig::m_pInstance = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool CProcessConfig::Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
|
||||
{
|
||||
|
||||
// 1. 임시 처리용 Instance 를 생성한다.
|
||||
CProcessConfig * pTempInstance = new CProcessConfig();
|
||||
|
||||
// 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( CProcessConfig::GetInstance() == NULL )
|
||||
{
|
||||
CProcessConfig::m_pInstance = pTempInstance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Instance replace...
|
||||
CProcessConfig * pPreviosInstance = CProcessConfig::m_pInstance;
|
||||
CProcessConfig::m_pInstance = pTempInstance;
|
||||
|
||||
delete pPreviosInstance;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
CProcessConfig * CProcessConfig::GetInstance()
|
||||
{
|
||||
return CProcessConfig::m_pInstance;
|
||||
}
|
||||
|
||||
|
||||
bool CProcessConfig::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;
|
||||
//std::vector< std::string > vecValue;
|
||||
|
||||
// DEFAULT_LOG_DIR
|
||||
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
|
||||
{
|
||||
m_nLogLevel = atoi(strValue.c_str());
|
||||
if( m_nLogLevel < 0 || m_nLogLevel > MAX_LOG_LEVEL )
|
||||
{
|
||||
strErrorMessage = "Config [LOG_LEVEL] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
strValue.clear();
|
||||
}
|
||||
|
||||
|
||||
// FILE_STORAGE_ROOT
|
||||
// - 작업 대상 경로는 최초 초기화시... conf 의 FILE_STORAGE_ROOT 정보로 설정 처리한다.
|
||||
if( GetConfigValue( conf, strProgramName, "FILE_STORAGE_ROOT", strValue, strErrorMessage ) == false )
|
||||
return false;
|
||||
else
|
||||
{
|
||||
m_strStorageRoot = 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
|
||||
{
|
||||
m_nRcdbPort = atoi( strValue.c_str() );
|
||||
|
||||
if( m_nRcdbPort <= 0 || m_nRcdbPort > 65535 )
|
||||
{
|
||||
strErrorMessage = "Config [RCDB_PORT] value[" + strValue + "] is not valid";
|
||||
return false;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// conf 정보 로딩이 모두 정상적으로 완료된 경우... 마지막으로 전달받은 config 명, section 정보를 저장한다.
|
||||
m_strConfigFileName = strConfFileName;
|
||||
m_strProgramName = strProgramName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CProcessConfig::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;
|
||||
}
|
||||
|
||||
|
||||
// 작업을 수행할 content 저정 경로 정보를 반환한다.
|
||||
// - 사용자가 옵션으로 경로를 지정한 경우.. 해당 정보를 반환.
|
||||
// - 사용자가 옵션으로 경로를 지정하지 않은 경우... conf 에 정의된 FILE_STORAGE_ROOT 값을 반환한다.
|
||||
// - 사용자의 옵션 지정 여부는 IsUserDefineJobRoot() 함수를 이용하여 확인 가능.
|
||||
const char * CProcessConfig::GetJobRoot()
|
||||
{
|
||||
if( m_bUserDefineJobRoot == false )
|
||||
return m_strStorageRoot.c_str();
|
||||
else
|
||||
return m_strJobRoot.c_str();
|
||||
}
|
||||
|
||||
// 사용자가 옵션으로 작업 경로를 지정한 경우.. 해당 정보를 저장 처리
|
||||
// - 지정된 경로는 conf 의 FILE_STORAGE_ROOT 설정값 하위 폴더이어야 하며...
|
||||
// - 오류 발생시.. 해당 내역을 strErrorMessage 에 저장하여 반환 처리.
|
||||
bool CProcessConfig::SetJobRoot( std::string & strPath, std::string & strErrorMessage )
|
||||
{
|
||||
|
||||
if( m_strStorageRoot != strPath )
|
||||
{
|
||||
// conf 에 정의된 FILE_STORAGE_ROOT 설정값과 옵션으로 입력받은 path 값이 서로 다른 경우...
|
||||
|
||||
// 폴더 비교 처리를 위해.. 마지막에. '/' 이 포함되어 있지 않은 경우... 붙여서 비교 처리하도록 한다.
|
||||
std::string strStorageRoot = m_strStorageRoot;
|
||||
if( strStorageRoot.at( strStorageRoot.size() - 1 ) != '/' )
|
||||
strStorageRoot.append( "/" );
|
||||
|
||||
if( strncmp( strStorageRoot.c_str(), strPath.c_str(), strStorageRoot.size() ) != 0 )
|
||||
{
|
||||
// FILE_STORAGE_ROOT 의 하위 폴더가 아닌 경우...
|
||||
strErrorMessage = "user path[" + strPath + "] not valid. set sub path of conf value[" + m_strStorageRoot + "]";
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// FILE_STORAGE_ROOT 의 하위 폴더 인 경우.
|
||||
// - 저장 규칙에 따라.. 전달받은 경로의 맨 끝에 '/' 가 존재하는 경우.. 제거 처리한다.
|
||||
m_strJobRoot = strPath;
|
||||
if( m_strJobRoot.at( m_strJobRoot.size() - 1 ) == '/' )
|
||||
m_strJobRoot.erase( m_strJobRoot.size() - 1 );
|
||||
|
||||
// /stg/ 로 사용자가 정의한 경우.. 최종 검사로 판별하기 위해 다시한번 검사 처리.
|
||||
if( m_strStorageRoot != m_strJobRoot )
|
||||
m_bUserDefineJobRoot = true;
|
||||
else
|
||||
{
|
||||
m_bUserDefineJobRoot = false;
|
||||
m_strJobRoot.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// conf 에 정의된 FILE_STORAGE_ROOT 설정값과 옵션으로 입력받은 path 값이 동일한 경우...
|
||||
m_bUserDefineJobRoot = false;
|
||||
}
|
||||
|
||||
// 최종 결정된 Job Path directory 가 실제 존재하는지 확인한다.
|
||||
std::string strJobRoot = GetJobRoot();
|
||||
struct stat dirStat;
|
||||
|
||||
if( lstat( strJobRoot.c_str(), &dirStat ) != 0 )
|
||||
{
|
||||
strErrorMessage = "path not valid. check path [" + strJobRoot + "] [" + strerror( errno ) + "]";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 해당 정보가 Directory 가 아닌 경우
|
||||
if( S_ISDIR( dirStat.st_mode ) == false )
|
||||
{
|
||||
strErrorMessage = "path is not directory. check path [" + strJobRoot + "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// hostname 처리 관련 함수
|
||||
bool CProcessConfig::SetHostname( const char * name )
|
||||
{
|
||||
if( name == NULL )
|
||||
return false;
|
||||
|
||||
if( strlen( name ) == 0 )
|
||||
return false;
|
||||
|
||||
m_strHostname = name;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char * CProcessConfig::GetHostname()
|
||||
{
|
||||
return m_strHostname.c_str();
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/****************************************************************************
|
||||
cls config processing module
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2015/10/06
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
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_CONFIG_H__
|
||||
#define __PROCESS_CONFIG_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
// 본 모듈은 conf 파일의 정보를 read 하여... 본 프로세스 관련 설정 정보를 저장한다.
|
||||
// 본 모듈은 singleton 으로 동작시켜 Main 프로세스 및 Worker 프로세스에서도 접근이 가능토록 한다.
|
||||
// conf 파일에 대한 파싱 및 설정 정보 추출은 lib 하위의 Config 클래스를 이용한다.
|
||||
// 본 객체는 singleton 객체이므로 상속받아 사용하지 않도록 한다. ( 상속받아 사용할 수 없도록 private 처리함 )
|
||||
|
||||
class CProcessConfig
|
||||
{
|
||||
|
||||
private:
|
||||
|
||||
// sigleton 객체 생성을 위해 생성자를 public 으로 처리하지 않음.
|
||||
CProcessConfig();
|
||||
|
||||
// 소멸자
|
||||
virtual ~CProcessConfig();
|
||||
|
||||
static CProcessConfig * 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 CProcessConfig * 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 (/user/service/logs)
|
||||
int m_nLogLevel; // 로그 기록 Level
|
||||
|
||||
std::string m_strStorageRoot; // content save path
|
||||
// - conf 상의 FILE_STORAGE_ROOT 설정값
|
||||
|
||||
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
|
||||
|
||||
|
||||
// 프로세스 설정 정보
|
||||
bool m_bRunIntegrityCheck; // content integrity check 수행 여부 ( 실행 옵션값을 통해 설정 처리)
|
||||
bool m_bRunGarbageClean; // garbage content clean 수행 여부 ( 실행 옵션값을 통해 설정 처리)
|
||||
|
||||
bool m_bGarbageCleanSlow; // garbage content clean 수행시 slow unlink 처리 여부 ( 실행 옵션값을 통해 설정 처리)
|
||||
|
||||
bool m_bGarbageForceDelete; // garbage content clean 수행시 access time check 여부 (실행 옶션값을 통해 설정 처리)
|
||||
|
||||
|
||||
bool m_bUserDefineJobRoot; // 사용자가 작업 경로를 설정했는지 여부를 판단하기 위한 함수.
|
||||
// - 지정하지 않았을 경우.. 해당 FHS 에 존재하는 content 를 조회하면 되지만..
|
||||
// - 지정시 filename_hash like 구문을 사용하여 조회해야 하므로...
|
||||
// - 이를 구분하기 위해 사용 처리.
|
||||
|
||||
std::string m_strJobRoot; // 작업을 수행할 content save path
|
||||
// 실행 옵션에서 -p 로 경로 지정시.. 검증을 통해.. 최종 작업 대상 경로가 설정됨.
|
||||
// 해당 값은 폴더 경로 정보이지만.. 마지막 '/' 정보는 입력처리 안 되도록 처리한다. ( /stg/ -> /stg )
|
||||
// 본 값이 존재하지 않을 경우... m_strStorageRoot 값을 사용한다.
|
||||
|
||||
|
||||
std::string m_strHostname; // 장비의 hostname 정보를 저장 처리. (main 프로세스 초기화시 추출하여 저장 처리)
|
||||
|
||||
|
||||
|
||||
|
||||
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 * GetStorageRoot() { return m_strStorageRoot.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(); }
|
||||
|
||||
|
||||
// integrity check 수행 여부 설정.
|
||||
void SetIntegrityCheck( bool & bRun ) { m_bRunIntegrityCheck = bRun; return; }
|
||||
// garbage clean job 수행 여부 설정.
|
||||
void SetGarbageClean( bool & bRun ) { m_bRunGarbageClean = bRun; return; }
|
||||
|
||||
// garbage clean job slow unlink 설정 여부
|
||||
void SetGarbageCleanSlow( bool & bRun ) { m_bGarbageCleanSlow = bRun; return; }
|
||||
|
||||
// garbage clean 작업시 access time check 여부
|
||||
void SetGarbageForceDelete(bool& bRun) { m_bGarbageForceDelete = bRun; return; }
|
||||
|
||||
|
||||
// 사용자가 옵션으로 작업 경로를 지정한 경우.. 해당 정보를 저장 처리
|
||||
// - 지정된 경로는 conf 의 FILE_STORAGE_ROOT 설정값 하위 폴더이어야 하며...
|
||||
// - 오류 발생시.. 해당 내역을 strErrorMessage 에 저장하여 반환 처리.
|
||||
bool SetJobRoot( std::string & strPath, std::string & strErrorMessage );
|
||||
|
||||
// 작업을 수행할 content 저정 경로 정보를 반환한다.
|
||||
// - 사용자가 옵션으로 경로를 지정한 경우.. 해당 정보를 반환.
|
||||
// - 사용자가 옵션으로 경로를 지정하지 않은 경우... conf 에 정의된 FILE_STORAGE_ROOT 값을 반환한다.
|
||||
// - 사용자의 옵션 지정 여부는 IsUserDefineJobRoot() 함수를 이용하여 확인 가능.
|
||||
const char * GetJobRoot();
|
||||
|
||||
bool IsRunIntegrityCheck() { return m_bRunIntegrityCheck; }
|
||||
bool IsRunGarbageClean() { return m_bRunGarbageClean; }
|
||||
bool IsSlowGarbageClean() { return m_bGarbageCleanSlow; }
|
||||
bool IsForceGarbageDelete() { return m_bGarbageForceDelete; }
|
||||
bool IsUserDefineJobRoot() { return m_bUserDefineJobRoot; }
|
||||
|
||||
// hostname 처리 관련 함수
|
||||
bool SetHostname( const char * name );
|
||||
const char * GetHostname();
|
||||
|
||||
};
|
||||
|
||||
#endif /* __PROCESS_CONFIG_H__ */
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "RcdbInfo.h"
|
||||
#include "ProcessConfig.h"
|
||||
|
||||
// 생성자..
|
||||
CRcdbInfo::CRcdbInfo()
|
||||
{
|
||||
}
|
||||
|
||||
// 소멸자..
|
||||
CRcdbInfo::~CRcdbInfo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
// CServiceConfig Class 에 저장된 RCDB 접속 정보를 가져와 멤버 변수에 저장한다.
|
||||
void CRcdbInfo::Load()
|
||||
{
|
||||
// CProcessConfig Class 에서 기본적인 설정값에 대해 검사를 수행하므로...
|
||||
// 본 Class 에서 다시 유효성 검사를 할 필요는 없다.. ( 어차피 유효성 검사 방법이 동일하므로.. )
|
||||
|
||||
// RCDB IP
|
||||
m_strRcdbIp = CProcessConfig::GetInstance()->GetRcdbIp();
|
||||
|
||||
// RCDB Port
|
||||
m_nRcdbPort = CProcessConfig::GetInstance()->GetRcdbPort();
|
||||
|
||||
// RCDB DB Name
|
||||
m_strRcdbName = CProcessConfig::GetInstance()->GetRcdbName();
|
||||
|
||||
// RCDB 접근 계정
|
||||
m_strRcdbAcct = CProcessConfig::GetInstance()->GetRcdbAcct();
|
||||
|
||||
// RCDB 접근 Password.
|
||||
m_strRcdbAcctPw = CProcessConfig::GetInstance()->GetRcdbAcctPw();
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
RCDB 접속 정보 저장 처리 Class
|
||||
-----------------------------------------
|
||||
|
||||
begin : 2015/10/15
|
||||
copyright : (C) 2005 Solbox Inc.
|
||||
author : Dev Storage Team
|
||||
email : huibong@solbox.com
|
||||
version : 3.5
|
||||
|
||||
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__ */
|
||||
Reference in New Issue
Block a user