This commit is contained in:
biosvos
2026-08-07 17:38:18 +09:00
commit 873193a243
9613 changed files with 2755992 additions and 0 deletions
+53
View File
@@ -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 버전으로 신규 등록
+37
View File
@@ -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
+215
View File
@@ -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;
}
+114
View File
@@ -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__ */
+487
View File
@@ -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;
}
+273
View File
@@ -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__
+61
View File
@@ -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
+184
View File
@@ -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;
}
+56
View File
@@ -0,0 +1,56 @@
/***************************************************************************
Database Class
-----------------------------------------
begin : 2010/03/11
copyright : (C) 2010 SolutionBox Inc.
author : Service 1 Team
email : svc1@solbox.com
version : 1.0
CopyRight(C) 2010 SolutionBox Inc. All Rights reserved.
Redistribution and use in source and binary forms, with or with out
modification, are not permitted in outside of SolutionBox Inc.
***************************************************************************/
#ifndef __DATABASE_H__
#define __DATABASE_H__
#include <iostream>
#include <string>
#include "libpq-fe.h"
#define MaxSizeOfDBQuery 1024*9
using namespace std;
class DataBase
{
public:
enum CFLAG { CLEAR = 1, NOT_CLEAR };
DataBase() { m_PGconn = NULL; m_pRes = NULL;}
~DataBase();
PGconn *PgOpenDB(string &strHost, int Port, string &strDBName, string &strAcct, string &strPasswd, int timeout = 10);
PGconn *PgOpenDB(const char *pszDBName);
void PgCloseDB();
int PgResult(CFLAG flag);
PGconn *GetPgConn(){ return m_PGconn;}
PGresult *GetRes();
void SetRes(PGresult * v) {m_pRes = v;}
int GetCmdTuples();
int GetNoTuples();
int GetNoFields();
int GetResultCode() { return m_ResultCode; }
char *GetValue(int tuple, int field);
void PgClear();
int PgDoExec(string &strQuery);
int PgDoExec(char *pszQuery);
int PgDoExec(char *pszQuery, CFLAG flag);
int PgDoExecParams(char *pszQuery, int nParamCnt, const char * const *paramValues ,CFLAG flag = NOT_CLEAR);
int PgEscapeString(char *to, const char *from, size_t length);
string &GetErrorMessage();
private:
int m_ResultCode;
PGconn *m_PGconn;
PGresult *m_pRes;
string m_ErrorMessage;
};
#endif // ~__DATABASE_H__
+512
View File
@@ -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;
}
+77
View File
@@ -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__ */
+313
View File
@@ -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;
}
+59
View File
@@ -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__ */
+814
View File
@@ -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 );
}
+63
View File
@@ -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__ */
+80
View File
@@ -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
+552
View File
@@ -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;
}
+78
View File
@@ -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__ */
+330
View File
@@ -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();
}
+167
View File
@@ -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__ */
+39
View File
@@ -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;
}
+52
View File
@@ -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__ */